Files
2020-05-06 19:19:16 +00:00

912 lines
38 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AyaNova.PlugIn;
using GZTW.AyaNova.BLL;
using System.ComponentModel;
using System.Windows.Forms;
using System.Reflection;
using System.Data;
using ofc=Microsoft.Office.Interop;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
namespace AyaNova.Plugin.OutlookSchedule
{
class OutlookSchedule : IAyaNovaPlugin
{
//Keep all the object types we want to deal with in a collection
//so that we can quickly check it when asked
private static List<RootObjectTypes> ObjectsWeCanDealWith = null;
private static bool bLicensed = false;
//Holds the image resources from AyaNova
//so we can display the correct icons in our plugin
System.Resources.ResourceManager resman = null;
//Holds the current logged in user's localized text
//lookup object
LocalizedTextTable LocaleText = null;
#region IAyaNovaPlugin Members
#region interface properties
public string PluginName
{
get { return "OutlookSchedule"; }
}
public string PluginVersion
{
get { return "7.6"; }
}
public string About
{
get
{
return "AyaNova OutlookSchedule plugin";
}
}
public Guid PluginID
{
get { return new Guid("{6891D5FD-10B3-44ac-B138-D9DB4111F3DE}"); }
}
public System.Drawing.Image PluginSmallIcon
{
get { return Resource.OutlookSchedule16; }
}
public System.Drawing.Image PluginLargeIcon
{
get { return Resource.OutlookSchedule32; }
}
public System.Resources.ResourceManager AyaNovaResourceManager
{
set { resman = value; }
get { return resman; }
}
#endregion interface properties
#region Initialization and Close
public bool Initialize(Version AyaNovaVersion, LocalizedTextTable localizedText)
{
//bLicensed = (!string.IsNullOrEmpty(AyaBizUtils.PluginLicensedVersion("OutlookSchedule")));
//case 2094
bLicensed = AyaBizUtils.PluginAllowed("OutlookSchedule", AyaNova.PlugIn.OutlookSchedule.Timestamp.BuildAt);
if (!bLicensed && AyaBizUtils.PluginTooNew("OutlookSchedule", AyaNova.PlugIn.OutlookSchedule.Timestamp.BuildAt))
{
MessageBox.Show(
"NOT LICENSED!\r\n\r\nThis OutlookSchedule plugin was built " +
AyaNova.PlugIn.OutlookSchedule.Timestamp.BuildAt.ToString() + "\r\n" +
"but your license subscription for it ended " +
AyaBizUtils.PluginSubscriptionDate("OutlookSchedule").ToString() + "\r\n" +
"\r\nDowngrade back to your previous version or purchase a subscription to use this plugin.");
return false;
}
if (AyaNovaVersion.Major < 7)
{
MessageBox.Show("This OutlookSchedule plugin requires AyaNova 7 or newer");
return false;
}
LocaleText = localizedText;
ObjectsWeCanDealWith = new List<RootObjectTypes>();
ObjectsWeCanDealWith.Add(RootObjectTypes.Nothing);
return true;
}
public void Close()
{
;
}
#endregion Initialization and close
#region ShowMenu?
public bool SingleObjectMenuShow(RootObjectTypes objectType)
{
return (ObjectsWeCanDealWith.Contains(objectType));
}
public bool MultipleObjectsMenuShow(RootObjectTypes objectType)
{
return false;
}
#endregion show menu?
#region Menu options
public List<AyaNovaPluginMenuItem> SingleObjectMenuOptions(RootObjectTypes objectType, object ayaNovaObject)
{
if (!ObjectsWeCanDealWith.Contains(objectType)) return null;
List<AyaNovaPluginMenuItem> list = new List<AyaNovaPluginMenuItem>();
list.Add(new AyaNovaPluginMenuItem("OutlookSchedule", "Outlook schedule export", null, null));
//list.Add(new AyaNovaPluginMenuItem("OutlookScheduleDiagnostic", "Outlook schedule export (DIAGNOSTIC MODE)", null, null));
return list;
}
public List<AyaNovaPluginMenuItem> MultipleObjectsMenuOptions(RootObjectTypes objectType)
{
return null;
}
#endregion
#region Menu Commands
private void ShowWarning(string swarn, string scaption)
{
MessageBox.Show(swarn, scaption, MessageBoxButtons.OK, MessageBoxIcon.Stop);
}
#region LIST OBJECT COMMAND
/// <summary>
/// LIST OBJECT
/// </summary>
/// <param name="commandKey"></param>
/// <param name="objectType"></param>
/// <param name="objectIDList"></param>
/// <param name="listObject"></param>
/// <returns></returns>
public bool CommandSelectedForList(string commandKey, RootObjectTypes objectType, List<Guid> objectIDList, object listObject)
{
return false;
}
#endregion list object command
#region SINGLE OBJECT COMMAND
/// <summary>
/// SINGLE OBJECT
/// </summary>
/// <param name="commandKey"></param>
/// <param name="objectType"></param>
/// <param name="ayaNovaObject"></param>
public void CommandSelectedForSingleObject(string commandKey, RootObjectTypes objectType, object ayaNovaObject)
{
if (!bLicensed)
{
ShowWarning("An AyaNova OutlookSchedule license is required to use this plugin.", "Not licensed");
return;
}
switch (commandKey)
{
case "OutlookSchedule":
{
Export(false);
}
break;
case "OutlookScheduleDiagnostic":
{
Export(true);
}
break;
}
}
#endregion single object command
#endregion menu commands
#endregion
#region Outlook api globals and util methods
#region Outlook application object
ofc.Outlook.Application moutApp = null;
public ofc.Outlook.Application outApp
{
get
{
if (moutApp == null)
{
if (bdiag) d("Obtaining link to Outlook...");
//todo: need to login here or something if outlook not already running
moutApp = new ofc.Outlook.Application();
if (bdiag) d("..link obtained to Outlook.");
}
return moutApp;
}
}
#endregion outlook app object
#region AyaNova calendar folder
ofc.Outlook.MAPIFolder mAyaCalendar = null;
/// <summary>
/// Get the AyaNova calendar
/// create it if required
/// </summary>
public ofc.Outlook.MAPIFolder AyaCalendar
{
get
{
if (mAyaCalendar == null)
{
if (bdiag) d("Check for existing AyaNovaSchedule calendar...");
//see if there is one already, if not create it
mAyaCalendar = GetFolder(ofc.Outlook.OlDefaultFolders.olFolderCalendar, "AyaNovaSchedule");
if (mAyaCalendar == null)
{
if (bdiag) d("Calendar AyaNovaSchedule not found in Outlook. Creating it...");
//get required objects to do the op
ofc.Outlook.NameSpace ns = outApp.GetNamespace("MAPI");
ofc.Outlook.MAPIFolder fldDefault=ns.GetDefaultFolder(ofc.Outlook.OlDefaultFolders.olFolderCalendar);
ofc.Outlook.Folders fldDefaultFolders = fldDefault.Folders;
//create it
mAyaCalendar = fldDefaultFolders.Add("AyaNovaSchedule", ofc.Outlook.OlDefaultFolders.olFolderCalendar);
mAyaCalendar.Description = "AyaNova OutlookSchedule plugin exported items";
//release required objects
Marshal.ReleaseComObject(fldDefaultFolders);
fldDefaultFolders = null;
Marshal.ReleaseComObject(fldDefault);
fldDefault = null;
Marshal.ReleaseComObject(ns);
ns = null;
}
}
return mAyaCalendar;
}
}
#endregion ayanova calendar folder
#region Remove AyaNova generated appointment from Outlook
public void RemoveOutlookAppointment(TypeAndID tid)
{
// d("Remove outlook appointment top...");
ofc.Outlook.AppointmentItem oaiToDelete = null;
ofc.Outlook.Items ayCalItems = AyaCalendar.Items;
bool bFoundIt = false;
for (int x = 0; x < ayCalItems.Count; x++)
{
//d("fetching outlook appointment for comparison...");
ofc.Outlook.AppointmentItem oai = (ofc.Outlook.AppointmentItem)ayCalItems[x+1];
//d("examining outlook appointment user properties...");
ofc.Outlook.UserProperties props=null;
if(oai.UserProperties!=null)
props = oai.UserProperties;
if (props != null && props.Count > 0)
{
ofc.Outlook.UserProperty prop = props["AyaNovaTypeAndID"];
if (prop != null)
{
//d("prop.value to string:" + prop.Value.ToString());
//d("Tid to string:" + tid.ToString());//bugbug tid is null here
if (prop.Value.ToString() == tid.ToString())
{
//d("Have a match!");
oaiToDelete = oai;
bFoundIt = true;
}
Marshal.ReleaseComObject(prop); prop = null;
}
Marshal.ReleaseComObject(props); props = null;
}
//d("Found? - " + bFoundIt.ToString());
if (bFoundIt)
break;
else
Marshal.ReleaseComObject(oai); oai = null;
}
Marshal.ReleaseComObject(ayCalItems); ayCalItems = null;
//d("Finished in loop");
//Remove from Outlook
if (oaiToDelete != null)
{
//d("Deleteing outlook appointment " + oaiToDelete.Subject + " ...");
oaiToDelete.Delete();
if (oaiToDelete != null)
{
Marshal.ReleaseComObject(oaiToDelete); oaiToDelete = null;
}
}
else
throw new System.ApplicationException("Outlook schedule plugin: Error RemoveOutlookAppointment: appointment matching [" + tid.ToString() + "] not found in Outlook");
//d("Remove outlook appointment bottom.");
}
#endregion
#region Insert Shadow appointment into Outlook
public void InsertShadowAppointment(ShadowAppointment sa)
{
ofc.Outlook.Items ayCalItems = AyaCalendar.Items;
ofc.Outlook.AppointmentItem oa = (ofc.Outlook.AppointmentItem)ayCalItems.Add(ofc.Outlook.OlItemType.olAppointmentItem);
ofc.Outlook.UserProperties props= oa.UserProperties;
ofc.Outlook.UserProperty prop = props.Add("AyaNovaTypeAndID", Microsoft.Office.Interop.Outlook.OlUserPropertyType.olText, false, null);
prop.Value = sa.AyaNovaTypeAndID.ToString();
oa.Subject = sa.Subject;
oa.Location = sa.Location;
oa.Start = sa.Start;
oa.End = sa.End;
oa.Body = sa.BodyText;
if (sa.ShowTimeAsBusy)
oa.BusyStatus = Microsoft.Office.Interop.Outlook.OlBusyStatus.olBusy;
else
oa.BusyStatus = Microsoft.Office.Interop.Outlook.OlBusyStatus.olTentative;
oa.ReminderSet = false;
oa.Save();
Marshal.ReleaseComObject(prop);prop = null;
Marshal.ReleaseComObject(props);props = null;
Marshal.ReleaseComObject(oa);oa = null;
Marshal.ReleaseComObject(ayCalItems); ayCalItems = null;
}
#endregion insert shadow appt.
#region Localized text stuff
string mSchedUserLocalizedText = null;
string mSchedMarkerLocalizedText = null;
public string SchedMarkerLocalizedText
{
get
{
if (mSchedMarkerLocalizedText == null)
mSchedMarkerLocalizedText = LocaleText.GetLocalizedText("O.ScheduleMarker");
return mSchedMarkerLocalizedText;
}
}
public string SchedUserLocalizedText
{
get
{
if (mSchedUserLocalizedText == null)
mSchedUserLocalizedText = LocaleText.GetLocalizedText("O.WorkorderItemScheduledUser");
return mSchedUserLocalizedText;
}
}
#endregion LocalizedTextstuff
#region Client contact cache
Dictionary<Guid, string> clientInfoCache = null;
/// <summary>
/// Get client info
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public string GetClientInfoDisplay(Guid id)
{
//check in cache first
if(clientInfoCache==null)
clientInfoCache = new Dictionary<Guid, string>();
if (!clientInfoCache.ContainsKey(id))
{
//retrieve the client record
Client c = Client.GetItemNoMRU(id);
StringBuilder b = new StringBuilder();
b.Append(c.Name);
b.Append("\r\n");
b.Append(c.GoToAddress.FullAddress);
b.Append("\r\n");
b.Append(c.GetPrimaryContactDefaultContactInfo());
//if (c.TechNotes != "")
//{
// b.Append(LocaleText.GetLocalizedText("Client.Label.TechNotes"));
// b.Append(":\r\n");
// b.Append(c.TechNotes);
//}
clientInfoCache.Add(id, b.ToString());
}
return clientInfoCache[id];
}
#endregion client contact cache
#region utils
#region ShadowAppointmentStuff
/// <summary>
///
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public ShadowAppointment GetShadowAppointment(AppointmentList.AppointmentListInfo i)
{
ShadowAppointment a = new ShadowAppointment();
if (i.SourceObjectType == RootObjectTypes.ScheduleMarker)
{
ScheduleMarker sm=ScheduleMarker.GetItem(i.SourceObjectID);
a.Subject = sm.Name;
a.Location = SchedMarkerLocalizedText;
a.Start = i.StartDateTime;
a.End = i.EndDateTime;
a.ShowTimeAsBusy = false;
string sFollowUpText = "";
if (sm.FollowID != Guid.Empty)
sFollowUpText = NameFetcher.GetItem(new TypeAndID(sm.FollowType, sm.FollowID)).RecordName + "\r\n";
a.BodyText = sFollowUpText + sm.Notes;
a.AyaNovaTypeAndID = new TypeAndID(RootObjectTypes.ScheduleMarker, i.SourceObjectID);
}
else//workorderitemscheduleuser
{
WorkorderServiceList.WorkorderServiceListInfo woinfo= WorkorderServiceList.GetListForSingleItem(i.WorkorderID)[0];
a.Subject = i.ServiceNumber.ToString() + " " + woinfo.LT_O_Client.Display;
a.Location = SchedUserLocalizedText;
a.Start = i.StartDateTime;
a.End = i.EndDateTime;
a.ShowTimeAsBusy = true;
a.BodyText = i.Subject + "\r\n------------------------------------\r\n" + GetClientInfoDisplay(woinfo.LT_O_Client.Value);
a.AyaNovaTypeAndID = new TypeAndID(i.SourceObjectType, i.SourceObjectID);
}
System.Text.StringBuilder sb = new StringBuilder();
sb.Append(a.Subject);
sb.Append(a.Location);
sb.Append(a.Start.ToString("yyyyMMddHHmm"));
sb.Append(a.End.ToString("yyyyMMddHHmm"));
sb.Append(a.BodyText);
a.CheckSum = GetHash(sb.ToString());
//if (i.SourceObjectType == RootObjectTypes.ScheduleMarker)
// d("Shadow appt. checksum:" + a.CheckSum + "\r\nData was:\r\n" + sb.ToString());
return a;
}
#endregion shadowappointmentstuff
#region Outlook folder stuff
/// <summary>
/// Iterates all folders of type specified and returns the one with the name specified
/// Caller MUST release folder returned
/// </summary>
/// <param name="sNameContains"></param>
/// <returns></returns>
public ofc.Outlook.MAPIFolder GetFolder(ofc.Outlook.OlDefaultFolders folderType, string sNameContains)
{
//d("Top of GetFolder...");
//get required objects to do the op
ofc.Outlook.NameSpace ns = outApp.GetNamespace("MAPI");
ofc.Outlook.MAPIFolder f= GetCalendar(ns.GetDefaultFolder(folderType), sNameContains);
// d("Got folder, releasing objects...");
if (ns != null)
{
Marshal.ReleaseComObject(ns);
ns = null;
}
return f;
}
/// <summary>
/// Recursive method to iterate folders and return one that contains the name specified
/// </summary>
/// <param name="mf"></param>
/// <param name="sNameContains"></param>
/// <returns></returns>
public ofc.Outlook.MAPIFolder GetCalendar(ofc.Outlook.MAPIFolder mf, string sNameContains)
{
//d("Top of GetCalendar " + mf.FolderPath + ", " + sNameContains);
if (mf.FolderPath.Contains(sNameContains))
{
//d("GetCalendar: Found it, returning.");
return mf;
}
ofc.Outlook.Folders fldrs = mf.Folders;
for (int x = 0; x < fldrs.Count; x++)
{
//d("GetCalendar: Checking calendar " + x.ToString() + " of " + mf.Folders.Count.ToString());
ofc.Outlook.MAPIFolder f = mf.Folders[x+1];
if (GetCalendar(f, sNameContains) != null)
{
Marshal.ReleaseComObject(fldrs); fldrs = null;
return f;
}
else
{
Marshal.ReleaseComObject(f); f = null;
}
}
//d("GetCalendar: Not here, returning.");
Marshal.ReleaseComObject(fldrs); fldrs = null;
return null;
}
#endregion Outlook folder stuff
#region Checksum stuff
private aptOut GetOutlookAppointmentChecksum(ofc.Outlook.AppointmentItem oai)
{
//d("top of GetOutlookAppointmentChecksum(" + oai.Subject + ")");
aptOut a;
a.CheckSum="";
a.AyaNovaTypeAndID = new TypeAndID(RootObjectTypes.Nothing, Guid.Empty);
if (oai == null) return a;
if (string.IsNullOrEmpty(oai.EntryID)) return a;
ofc.Outlook.UserProperties props = oai.UserProperties;
ofc.Outlook.UserProperty prop = props["AyaNovaTypeAndID"];
a.AyaNovaTypeAndID = TypeAndID.Parse(prop.Value.ToString());
Marshal.ReleaseComObject(prop); prop = null;
Marshal.ReleaseComObject(props); props = null;
System.Text.StringBuilder sb = new StringBuilder();
sb.Append(oai.Subject);
sb.Append(oai.Location);
sb.Append(oai.Start.ToString("yyyyMMddHHmm"));
sb.Append(oai.End.ToString("yyyyMMddHHmm"));
sb.Append(oai.Body);
a.CheckSum = GetHash(sb.ToString());
//if(a.CheckSum.StartsWith("92C"))
// d("Outlook appt. checksum:"+a.CheckSum+"\r\nData was:\r\n" + sb.ToString());
return a;
}
/// <summary>
/// Get a reliable hash value
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
private string GetHash(string s)
{
//TODO: FIPS
SHA256 shaM = new SHA256Managed();
UTF8Encoding enc = new UTF8Encoding();
return BitConverter.ToString(shaM.ComputeHash(enc.GetBytes(s))).Replace("-", "");
}
#endregion checksum stuff
#endregion utils
#endregion outlook api globals and util methods
#region Diagnostic messages
/// <summary>
/// Diagnostics messages
/// </summary>
/// <param name="s"></param>
private void d(string s)
{
if (bdiag)
MessageBox.Show(s);
}
private bool bdiag = false;
#endregion diags
/// <summary>
/// Do the export
/// </summary>
private void Export(bool DiagnosticMode)
{
UserTypes currentUserType = User.CurrentUserType;
if (currentUserType != UserTypes.Schedulable)
{
MessageBox.Show("This plugin can only be used by scheduleable users.\r\n" +
"The current logged in user is an " + currentUserType.ToString() + " type of user.");
return;
}
bdiag = DiagnosticMode;
DateTime dtStarted = DateTime.Now.AddMinutes(-1);
if(bdiag) d("Starting export of items between " + dtStarted.ToString() + " and "+ dtStarted.AddYears(1) + "...");
List<aptOut> lsaptOut = new List<aptOut>();
List<ShadowAppointment> lsShadowApt = new List<ShadowAppointment>();
AppointmentList apl = null;
try
{
#region Build checksum list of appointments in Outlook
if (bdiag) d("Inspecting existing AyaNovaSchedule items in Outlook ...");
ofc.Outlook.Items ayCalItems = AyaCalendar.Items;
// d("Raw AyaCalendar.Items.Count=" + ayCalItems.Count);
for (int x = 0; x < ayCalItems.Count; x++)
{
//d("Fetching existing item " + x.ToString()+" ...");
ofc.Outlook.AppointmentItem oai = (ofc.Outlook.AppointmentItem)ayCalItems[x + 1];
if (oai.Start > dtStarted || oai.End > dtStarted)//appointments that start after now or end after now
{
ofc.Outlook.UserProperties props = oai.UserProperties;
if (props != null && props.Count > 0)
{
ofc.Outlook.UserProperty prop = props["AyaNovaTypeAndID"];
if (prop != null)
{
lsaptOut.Add(GetOutlookAppointmentChecksum(oai));
Marshal.ReleaseComObject(prop); prop = null;
}
Marshal.ReleaseComObject(props); props = null;
}
}
Marshal.ReleaseComObject(oai); oai = null;
}
Marshal.ReleaseComObject(ayCalItems); ayCalItems = null;
if (bdiag) d(lsaptOut.Count.ToString() + " existing relevant AyaNovaSchedule Outlook calendar items found in Outlook.");
#endregion build checksum list
#region Build shadow appointment list
if (bdiag) d("Fetching any existing open AyaNova schedule items in the next year for current logged in user...");
apl = AppointmentList.GetList(dtStarted, dtStarted.AddYears(1), User.CurrentThreadUserID);
UserListScheduleable uls = UserListScheduleable.GetList();
foreach (AppointmentList.AppointmentListInfo i in apl)
{
switch (i.SourceObjectType)
{
case RootObjectTypes.WorkorderItemScheduledUser:
{
lsShadowApt.Add(GetShadowAppointment(i));
}
break;
case RootObjectTypes.ScheduleMarker:
{
#region schedmarkers
////Could be a bunch by region , global , dispatchzone, schedusergroup
////or could be a single by one user ID
switch (i.AppliesToObjectType)
{
case RootObjectTypes.User:
if (i.AppliesToObjectID == User.CurrentThreadUserID)
lsShadowApt.Add(GetShadowAppointment(i));
break;
case RootObjectTypes.Region:
{
//Loop through all active scheduleable users
//if the region ID matches the marker region ID then
//add a marker on the calendar for it
foreach (UserListScheduleable.UserListScheduleableInfo ui in uls)
{
if (ui.ID == User.CurrentThreadUserID)
{
//case 58
if (i.AppliesToObjectID == GZTW.AyaNova.BLL.Region.DefaultRegionID)//SM is for default region? Then applies to everyone
lsShadowApt.Add(GetShadowAppointment(i));
else if (ui.RegionID == GZTW.AyaNova.BLL.Region.DefaultRegionID)//User is in default region? Then applies to them
lsShadowApt.Add(GetShadowAppointment(i));
else if (ui.RegionID == i.AppliesToObjectID)//SM and User in same non default region
lsShadowApt.Add(GetShadowAppointment(i));
break;
}
}
}
break;
case RootObjectTypes.DispatchZone:
{
//Loop through all active scheduleable users
//if the Dispatch zone ID matches the marker zone ID then
//add a marker on the calendar for it
foreach (UserListScheduleable.UserListScheduleableInfo ui in uls)
if (ui.ID == User.CurrentThreadUserID)
{
if (ui.DispatchZoneID == i.AppliesToObjectID)
lsShadowApt.Add(GetShadowAppointment(i));
break;
}
}
break;
case RootObjectTypes.ScheduleableUserGroup:
{
ScheduleableUserGroupUsersList ScheduleMarkerGroup = ScheduleableUserGroupUsersList.GetList(i.AppliesToObjectID);
//Loop through all active scheduleable users
//if they are in the ScheduleableUserGroup then
//add a marker on the calendar for it
foreach (UserListScheduleable.UserListScheduleableInfo ui in uls)
if (ui.ID == User.CurrentThreadUserID)
{
if (ScheduleMarkerGroup.Contains(ui.ID))
lsShadowApt.Add(GetShadowAppointment(i));
break;
}
}
break;
case RootObjectTypes.Global:
{
lsShadowApt.Add(GetShadowAppointment(i));
}
break;
}
#endregion schedmarkers
}
break;
}
}
if (bdiag) d(lsShadowApt.Count.ToString() + " existing AyaNova schedule items found in AyaNova.");
#endregion build shadow appointment list
//Do Export:
#region step 1 remove stale outlook appointments
if (bdiag) d("Removing existing AyaNovaSchedule Outlook calendar items that are changed since last export ...");
//iterate outlook appointments list, for each AyaNova generated one
//that has no matching checksum in shadow, delete it from outlook then from outlist
int nlsaptOutCount = lsaptOut.Count;
int nTotalStaleCount = 0;
#region mismatch Diagnostics stuff
if (bdiag)
{
System.Text.StringBuilder sbdiag = new StringBuilder();
sbdiag.Append("Shadowlist:\r\n");
foreach (ShadowAppointment sa in lsShadowApt)
{
sbdiag.Append(sa.Subject);
sbdiag.Append(" ck:");
sbdiag.Append(sa.CheckSum);
sbdiag.Append("\r\n");
}
d(sbdiag.ToString());
sbdiag.Length = 0;
sbdiag.Append("Outlist:\r\n");
foreach (aptOut sa in lsaptOut)
{
sbdiag.Append(sa.AyaNovaTypeAndID.ToString());
sbdiag.Append(" ck:");
sbdiag.Append(sa.CheckSum);
sbdiag.Append("\r\n");
}
d(sbdiag.ToString());
}
#endregion mismatch diags
//iterate the outlook appointments list
for (int x = 0; x < nlsaptOutCount; x++)
{
//d("x=" + x.ToString() + ", nlsaptOutCount=" + nlsaptOutCount.ToString() + ", lsaptOut.Count=" + lsaptOut.Count.ToString() + ", lsShadowApt.count=" + lsShadowApt.Count.ToString());
bool bExists = false;
//if outlook appointment checksum not in shadow list delete it
aptOut ThisapOut = lsaptOut[x];
foreach (ShadowAppointment sa in lsShadowApt)
{
//System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(sa.CheckSum));
//d("Comparing outlook checksum:\r\n " + ThisapOut.CheckSum + "\r\nTo shadow checksum:\r\n" + sa.CheckSum);
if (sa.CheckSum == ThisapOut.CheckSum)
{
//d("Identical match: " + sa.CheckSum);
bExists = true;
break;
}
}
if (!bExists)
{
//d("Unmatched outlook checksum:\r\n " + ThisapOut.CheckSum );
//remove from outlook
RemoveOutlookAppointment(ThisapOut.AyaNovaTypeAndID);
//remove from the working list
lsaptOut.RemoveAt(x);
x--;
nlsaptOutCount--;
nTotalStaleCount++;
}
}
if (bdiag) d(nTotalStaleCount.ToString() + " stale items removed from Outlook.");
#endregion step 1
#region Step 2 Remove unchanged appointments from shadow appointment list
if (bdiag) d("Identifying AyaNova items that already exist identically in Outlook and do not need to be exported...");
//Iterate through shadow appointments, for each one that has matching checksum in outlook apts remove it from shadow list
int nShadowListCount = lsShadowApt.Count;
int nTotalUnchangedShadowItems = 0;
for (int x = 0; x < nShadowListCount; x++)
{
bool bExists = false;
ShadowAppointment sa = lsShadowApt[x];
foreach (aptOut ao in lsaptOut)
{
if (ao.CheckSum == sa.CheckSum)
{
bExists = true;
break;
}
}
if (bExists)
{
//remove from shadowlist
lsShadowApt.RemoveAt(x);
x--;
nShadowListCount--;
nTotalUnchangedShadowItems++;
}
}
if (bdiag) d(nTotalUnchangedShadowItems.ToString() + " items in AyaNova already exist unchanged in Outlook and won't be exported.");
#endregion Step 2
#region Step 3 Insert remaining shadow appointments in Outlook
if (bdiag)
{
if (lsShadowApt.Count > 0)
d("Exporting " + lsShadowApt.Count.ToString() + " new / updated AyaNova schedule items to Outlook...");
else
d("No items found to export to Outlook.");
}
foreach (ShadowAppointment sa in lsShadowApt)
{
InsertShadowAppointment(sa);
}
#endregion step 3
}
catch (Exception ex)
{
MessageBox.Show("OutlookSchedule plugin error:\r\n"+ex.ToString());
throw (ex);
}
finally
{
#region Step 4 release objects
if (bdiag) d("Releasing calendar object...");
if (mAyaCalendar != null)
{
Marshal.ReleaseComObject(mAyaCalendar); mAyaCalendar = null;
}
if (bdiag) d("Releasing app object...");
if (moutApp != null)
{
Marshal.ReleaseComObject(moutApp); moutApp = null;
}
//clear the client cache
if (clientInfoCache != null)
{
if (bdiag) d("Clearing client cache...");
clientInfoCache.Clear();
clientInfoCache = null;
}
if (bdiag) d("Done!");
#endregion step 4
}
}
}//end of class
#region Appointment and comparison structs
public struct aptOut
{
public TypeAndID AyaNovaTypeAndID;
public string CheckSum;
}
/// <summary>
/// contains all the data that will go into an Outlook appointment
/// </summary>
public struct ShadowAppointment
{
public string Subject;
public string Location;
public DateTime Start;
public DateTime End;
public bool ShowTimeAsBusy;
public string BodyText;
public TypeAndID AyaNovaTypeAndID;
public string CheckSum;
}
#endregion appt comparison structs
}