diff --git a/Management/Controllers/ExchangeAccountController.cs b/Management/Controllers/ExchangeAccountController.cs
index 45e7c75..90d042a 100644
--- a/Management/Controllers/ExchangeAccountController.cs
+++ b/Management/Controllers/ExchangeAccountController.cs
@@ -271,10 +271,11 @@ public ActionResult Edit([Bind(Include = "AccountId,Name,Account,PasswordUnmaske
Match lnk = _emailRgx.Match(ews.Account);
ews.Account = lnk.Success ? lnk.Value : "";
- resolveAccount(ews);
+ // resolveAccount(ews); // Not required since we use MS Graph - but some validation here would probably be sensible
if (ModelState.IsValid)
{
+ ews.UpdatePassword(db);
db.Entry(ews).State = EntityState.Modified;
db.Entry(ews).Property(l => l.Password).IsModified = ews.PasswordSet;
//db.Entry(ews).Property(l => l.Url).IsModified = false;
diff --git a/Management/Views/ExchangeAccount/_Edit.cshtml b/Management/Views/ExchangeAccount/_Edit.cshtml
index 8a2de09..1087a5e 100644
--- a/Management/Views/ExchangeAccount/_Edit.cshtml
+++ b/Management/Views/ExchangeAccount/_Edit.cshtml
@@ -28,18 +28,18 @@
@Html.ValidationMessageFor(model => model.PasswordUnmasked)
-
- @Html.LabelFor(model => model.Url)
-
-
- @Html.EditorFor(model => model.Url)
- @Html.ValidationMessageFor(model => model.Url)
-
-
-
- @Html.LabelFor(model => model.EwsVersion)
-
-
- @Html.DropDownListFor(model => model.EwsVersion, ewsVersions)
- @Html.ValidationMessageFor(model => model.EwsVersion)
-
+ @* *@
+ @* @Html.LabelFor(model => model.Url) *@
+ @*
*@
+ @* *@
+ @* @Html.EditorFor(model => model.Url) *@
+ @* @Html.ValidationMessageFor(model => model.Url) *@
+ @*
*@
+ @* *@
+ @* *@
+ @* @Html.LabelFor(model => model.EwsVersion) *@
+ @*
*@
+ @* *@
+ @* @Html.DropDownListFor(model => model.EwsVersion, ewsVersions) *@
+ @* @Html.ValidationMessageFor(model => model.EwsVersion) *@
+ @*
*@
diff --git a/Presentation/Outlook.cs b/Presentation/Outlook.cs
index 1b1a558..31b3bfc 100644
--- a/Presentation/Outlook.cs
+++ b/Presentation/Outlook.cs
@@ -19,6 +19,7 @@
using Microsoft.Exchange.WebServices.Data;
using System.Web.Script.Serialization;
using DisplayMonkey.Language;
+using Microsoft.Graph.Models;
namespace DisplayMonkey
{
@@ -44,7 +45,7 @@ public class Outlook : Frame
private int ShowAsFlags { get; set; }
- public bool IsShowAsAllowed(LegacyFreeBusyStatus flag)
+ public bool IsShowAsAllowed(FreeBusyStatus flag)
{
return (ShowAsFlags & (1 << (int)flag)) != 0;
}
diff --git a/Presentation/Presentation.csproj b/Presentation/Presentation.csproj
index bc3b1b6..723d1e1 100644
--- a/Presentation/Presentation.csproj
+++ b/Presentation/Presentation.csproj
@@ -13,7 +13,7 @@
Properties
DisplayMonkey
DisplayMonkey.Presentation
- v4.5
+ v4.8
@@ -48,6 +48,15 @@
false
+
+ ..\packages\Azure.Core.1.32.0\lib\net461\Azure.Core.dll
+
+
+ ..\packages\Azure.Identity.1.9.0\lib\netstandard2.0\Azure.Identity.dll
+
+
+ ..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll
+
..\packages\EWS-Api-2.1.1.0.0\lib\net35\Microsoft.Exchange.WebServices.dll
@@ -55,13 +64,136 @@
..\packages\EWS-Api-2.1.1.0.0\lib\net35\Microsoft.Exchange.WebServices.Auth.dll
+
+ ..\packages\Microsoft.Graph.5.14.0\lib\netstandard2.0\Microsoft.Graph.dll
+
+
+ ..\packages\Microsoft.Graph.Core.3.0.7\lib\net462\Microsoft.Graph.Core.dll
+
+
+ ..\packages\Microsoft.Identity.Client.4.49.1\lib\net461\Microsoft.Identity.Client.dll
+
+
+ ..\packages\Microsoft.Identity.Client.Extensions.Msal.2.25.3\lib\net45\Microsoft.Identity.Client.Extensions.Msal.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Abstractions.6.30.1\lib\net472\Microsoft.IdentityModel.Abstractions.dll
+
+
+ ..\packages\Microsoft.IdentityModel.JsonWebTokens.6.30.1\lib\net472\Microsoft.IdentityModel.JsonWebTokens.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Logging.6.30.1\lib\net472\Microsoft.IdentityModel.Logging.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Protocols.6.30.1\lib\net472\Microsoft.IdentityModel.Protocols.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.6.30.1\lib\net472\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
+
+
+ ..\packages\Microsoft.IdentityModel.Tokens.6.30.1\lib\net472\Microsoft.IdentityModel.Tokens.dll
+
+
+ ..\packages\Microsoft.Kiota.Abstractions.1.1.2\lib\netstandard2.0\Microsoft.Kiota.Abstractions.dll
+
+
+ ..\packages\Microsoft.Kiota.Authentication.Azure.1.0.2\lib\netstandard2.0\Microsoft.Kiota.Authentication.Azure.dll
+
+
+ ..\packages\Microsoft.Kiota.Http.HttpClientLibrary.1.0.2\lib\netstandard2.0\Microsoft.Kiota.Http.HttpClientLibrary.dll
+
+
+ ..\packages\Microsoft.Kiota.Serialization.Form.1.0.1\lib\netstandard2.0\Microsoft.Kiota.Serialization.Form.dll
+
+
+ ..\packages\Microsoft.Kiota.Serialization.Json.1.0.6\lib\netstandard2.0\Microsoft.Kiota.Serialization.Json.dll
+
+
+ ..\packages\Microsoft.Kiota.Serialization.Text.1.0.1\lib\netstandard2.0\Microsoft.Kiota.Serialization.Text.dll
+
+
+
+ ..\packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
+
+
+
-
+
+ ..\packages\System.Diagnostics.DiagnosticSource.6.0.0\lib\net461\System.Diagnostics.DiagnosticSource.dll
+
+
+
+ ..\packages\System.IdentityModel.Tokens.Jwt.6.30.1\lib\net472\System.IdentityModel.Tokens.Jwt.dll
+
+
+ ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll
+
+
+ ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll
+
+
+ ..\packages\System.Memory.Data.1.0.2\lib\net461\System.Memory.Data.dll
+
+
+ ..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll
+
+
+ ..\packages\System.Net.Http.WinHttpHandler.7.0.0\lib\net462\System.Net.Http.WinHttpHandler.dll
+
+
+
+ ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
+
+
+ ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
+
+
+
+ ..\packages\System.Security.Claims.4.3.0\lib\net46\System.Security.Claims.dll
+
+
+ ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll
+
+
+ ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
+
+
+ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
+
+
+ ..\packages\System.Security.Cryptography.ProtectedData.4.7.0\lib\net461\System.Security.Cryptography.ProtectedData.dll
+
+
+ ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
+
+
+ ..\packages\System.Text.Encodings.Web.6.0.0\lib\net461\System.Text.Encodings.Web.dll
+
+
+ ..\packages\System.Text.Json.6.0.0\lib\net461\System.Text.Json.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
+
+
+ ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
+
+
@@ -70,6 +202,9 @@
+
+ ..\packages\Tavis.UriTemplates.2.0.0\lib\netstandard2.0\Tavis.UriTemplates.dll
+
@@ -450,6 +585,15 @@
xcopy "$(SolutionDir)Management\Content\Site.css" "$(SolutionDir)Presentation\Styles\Site.css" /Y /U
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.
+
+
+
+
+
-
+
-
+
+
+
+
+
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
-
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Presentation/getOutlook.ashx.cs b/Presentation/getOutlook.ashx.cs
index b5620da..1b26586 100644
--- a/Presentation/getOutlook.ashx.cs
+++ b/Presentation/getOutlook.ashx.cs
@@ -10,23 +10,24 @@
using System;
using System.Collections.Generic;
+using System.Configuration;
using System.Linq;
using System.Web;
-//using System.Web.UI;
-//using System.Web.UI.WebControls;
using System.Web.Script.Serialization;
-using Microsoft.Exchange.WebServices.Data;
-using System.Net;
+using Azure.Identity;
using DisplayMonkey.Language;
+using Microsoft.Graph;
+using Microsoft.Graph.Models;
+using Microsoft.Graph.Models.ODataErrors;
namespace DisplayMonkey
{
public partial class getOutlook : HttpTaskAsyncHandler
{
public override async System.Threading.Tasks.Task ProcessRequestAsync(HttpContext context)
- {
- HttpRequest request = context.Request;
- HttpResponse response = context.Response;
+ {
+ HttpRequest request = context.Request;
+ HttpResponse response = context.Response;
int frameId = request.IntOrZero("frame");
int panelId = request.IntOrZero("panel");
@@ -35,17 +36,17 @@ public override async System.Threading.Tasks.Task ProcessRequestAsync(HttpContex
int reserveMinutes = request.IntOrZero("reserveMinutes");
int trace = request.IntOrZero("trace");
- string json = "";
-
- try
- {
+ string json = "";
+
+ try
+ {
// set culture
Outlook outlook = new Outlook(frameId);
Location location = new Location(displayId);
if (string.IsNullOrWhiteSpace(culture))
culture = location.Culture;
-
+
if (!string.IsNullOrWhiteSpace(culture))
{
System.Globalization.CultureInfo cultureInfo = new System.Globalization.CultureInfo(culture);
@@ -56,7 +57,7 @@ public override async System.Threading.Tasks.Task ProcessRequestAsync(HttpContex
// EWS: get data
OutlookData data = await HttpRuntime.Cache.GetOrAddAbsoluteAsync(
string.Format("outlook_{0}_{1}_{2}", location.LocationId, outlook.FrameId, outlook.Version),
- async (expire) =>
+ async (expire) =>
{
expire.When = DateTime.Now.AddMinutes(outlook.CacheInterval);
return await OutlookData.FromFrameAsync(outlook, location, reserveMinutes);
@@ -70,12 +71,12 @@ public override async System.Threading.Tasks.Task ProcessRequestAsync(HttpContex
//int showEvents = outlook.ShowEvents;
List currentList = data.events
- .Where(e => e.Ends >= locationTime)
- .Take(Math.Max(1, outlook.ShowEvents))
- .ToList()
+ .Where(e => e.Ends >= locationTime)
+ .Take(Math.Max(1, outlook.ShowEvents))
+ .ToList()
;
-
- EventEntry
+
+ EventEntry
currentEvent = null,
firstEvent = currentList.FirstOrDefault()
;
@@ -86,26 +87,29 @@ public override async System.Threading.Tasks.Task ProcessRequestAsync(HttpContex
}
DateTime endTime = locationToday.Add(data.endTime);
- string
+ string
strCurrentEvent = "",
strCurrentStatus = string.Format(
Resources.Outlook_AvailableUntil,
endTime.ToShortTimeString()
- )
+ )
;
TimeSpan availableTime = new TimeSpan(0);
-
+
if (currentEvent != null)
{
DateTime tomorrow = locationToday.AddDays(1);
- strCurrentEvent = string.Format("{0}, {1} - {2}",
+ strCurrentEvent = string.Format("{0}, {1} - {2}",
currentEvent.Subject,
- (currentEvent.Starts >= tomorrow ? currentEvent.Starts.ToShortDateString() + " " : "") + currentEvent.Starts.ToShortTimeString(),
- (currentEvent.Ends >= tomorrow ? currentEvent.Ends.ToShortDateString() + " " : "") + currentEvent.Ends.ToShortTimeString()
- );
+ (currentEvent.Starts >= tomorrow ? currentEvent.Starts.ToShortDateString() + " " : "") +
+ currentEvent.Starts.ToShortTimeString(),
+ (currentEvent.Ends >= tomorrow ? currentEvent.Ends.ToShortDateString() + " " : "") +
+ currentEvent.Ends.ToShortTimeString()
+ );
TimeSpan gap = currentEvent.Ends.Subtract(locationTime);
if (gap.Hours > 0)
- strCurrentStatus = string.Format(Resources.Outlook_EndsInHrsMin, (int)gap.TotalHours, gap.Minutes);
+ strCurrentStatus = string.Format(Resources.Outlook_EndsInHrsMin, (int)gap.TotalHours,
+ gap.Minutes);
else
strCurrentStatus = string.Format(Resources.Outlook_EndsInMin, gap.Minutes);
}
@@ -113,7 +117,8 @@ public override async System.Threading.Tasks.Task ProcessRequestAsync(HttpContex
{
availableTime = firstEvent.Starts.Subtract(locationTime);
if (availableTime.Hours > 0)
- strCurrentStatus = string.Format(Resources.Outlook_AvailableForHrsMin, (int)availableTime.TotalHours, availableTime.Minutes);
+ strCurrentStatus = string.Format(Resources.Outlook_AvailableForHrsMin,
+ (int)availableTime.TotalHours, availableTime.Minutes);
else
strCurrentStatus = string.Format(Resources.Outlook_AvailableForMin, availableTime.Minutes);
}
@@ -165,23 +170,23 @@ public override async System.Threading.Tasks.Task ProcessRequestAsync(HttpContex
{
items = currentList,
},
- labels = new[]
+ labels = new[]
{
- new {key = "subject", value = Resources.Outlook_Event},
- new {key = "starts", value = Resources.Outlook_Starts},
- new {key = "ends", value = Resources.Outlook_Ends},
- new {key = "duration", value = Resources.Outlook_Duration},
- new {key = "sensitivity", value = Resources.Outlook_Sensitivity},
- new {key = "showAs", value = Resources.OutlookShowAs},
- new {key = "noEvents", value = Resources.Outlook_NoEventsToday},
- new {key = "bookingImpossible", value = Resources.Outlook_BookingImpossible},
- new {key = "bookingSent", value = Resources.Outlook_BookingSent},
+ new { key = "subject", value = Resources.Outlook_Event },
+ new { key = "starts", value = Resources.Outlook_Starts },
+ new { key = "ends", value = Resources.Outlook_Ends },
+ new { key = "duration", value = Resources.Outlook_Duration },
+ new { key = "sensitivity", value = Resources.Outlook_Sensitivity },
+ new { key = "showAs", value = Resources.OutlookShowAs },
+ new { key = "noEvents", value = Resources.Outlook_NoEventsToday },
+ new { key = "bookingImpossible", value = Resources.Outlook_BookingImpossible },
+ new { key = "bookingSent", value = Resources.Outlook_BookingSent },
},
});
- }
+ }
- catch (Exception ex)
- {
+ catch (Exception ex)
+ {
JavaScriptSerializer s = new JavaScriptSerializer();
if (trace == 0)
json = s.Serialize(new
@@ -215,7 +220,7 @@ public override async System.Threading.Tasks.Task ProcessRequestAsync(HttpContex
response.Cache.SetSlidingExpiration(true);
response.Cache.SetNoStore();
response.ContentType = "application/json";
- response.Write(json);
+ response.Write(json);
response.Flush();
}
@@ -223,7 +228,7 @@ public override async System.Threading.Tasks.Task ProcessRequestAsync(HttpContex
private class OutlookData
{
- public IEnumerable events = null;
+ public IEnumerable events;
public TimeSpan startTime = new TimeSpan(0, 0, 0);
public TimeSpan endTime = new TimeSpan(23, 59, 59);
public TimeZoneInfo timeZone = TimeZoneInfo.Local;
@@ -236,254 +241,133 @@ public OutlookData(Outlook outlook, Location location, int reserveMinutes)
#region -------- EWS Data Call --------f
- private static ExtendedPropertyDefinition PR_TextBody = new ExtendedPropertyDefinition(0x1000, MapiPropertyType.String);
- private static ExtendedPropertyDefinition PR_Sensitivity = new ExtendedPropertyDefinition(0x0036, MapiPropertyType.Integer);
-
private OutlookData()
{
}
- public static async System.Threading.Tasks.Task FromFrameAsync(Outlook outlook, Location location, int reserveMinutes)
+ public static async System.Threading.Tasks.Task FromFrameAsync(Outlook outlook,
+ Location location, int reserveMinutes)
{
- OutlookData data = new OutlookData();
- DateTime
- locationTime = location.LocationTime,
- locationToday = new DateTime(locationTime.Year, locationTime.Month, locationTime.Day),
- windowBeg = locationToday, //locationTime,
- windowEnd = locationToday.AddDays(1).AddMilliseconds(-1)
- ;
-
- // EWS: create connection point
- ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
-
- ExchangeService service = new ExchangeService(outlook.EwsVersion)
+ var tenantId = ConfigurationManager.AppSettings["AzureActiveDirectoryTenantId"];
+ var clientId = ConfigurationManager.AppSettings["AzureActiveDirectoryClientId"];
+ if (tenantId == default || clientId == default)
{
- Credentials = new WebCredentials(
- outlook.Account,
- Encryptor.Current.Decrypt(outlook.Password) // 1.4.0
- ),
- };
-
- // https://msdn.microsoft.com/en-us/library/office/dn458789%28v=exchg.150%29.aspx?f=255&MSPPError=-2147217396#bk_howmaintained
- // https://social.msdn.microsoft.com/Forums/lync/en-US/bd032e3d-2501-40ba-a2b0-29a404685c35/error-exchange-web-services-are-not-currently-available?forum=exchangesvrdevelopment
- service.HttpHeaders.Add("X-AnchorMailbox", outlook.Account);
-
- // EWS: get URL
- if (!string.IsNullOrWhiteSpace(outlook.URL))
- {
- service.Url = new Uri(outlook.URL);
- }
- else
- {
- service.Url = await HttpRuntime.Cache.GetOrAddSlidingAsync(
- string.Format("exchange_account_{0}", outlook.Account),
- async (expire) =>
- {
- expire.After = TimeSpan.FromMinutes(60);
- return await System.Threading.Tasks.Task.Run(() =>
- {
- service.AutodiscoverUrl(outlook.Account, RedirectionUrlValidationCallback);
- return service.Url;
- });
- });
+ throw new Exception(
+ "App Settings must specify Azure Active Directory Client and Tenant ID to use Outlook frame");
}
- // mailbox: get display name
- data.DisplayName = outlook.Name;
- if (string.IsNullOrWhiteSpace(data.DisplayName))
- {
- var match = await System.Threading.Tasks.Task.Run(() => service.ResolveName(outlook.Mailbox));
- if (match.Count > 0)
+ var useNamePasswordCredential = new UsernamePasswordCredential(outlook.Account,
+ Encryptor.Current.Decrypt(outlook.Password),
+ tenantId,
+ clientId);
+
+ // We don't provide a way for the user to consent to these scopes.
+ // Either issue admin consent in AAD or use another method to trigger the user consent flow.
+ var graphServiceClient = new GraphServiceClient(useNamePasswordCredential,
+ new[] { "user.read", "calendars.readwrite", "calendars.readwrite.shared", "MailboxSettings.Read" });
+
+ // The data we'll now fill, and then return to the client.
+ var data = new OutlookData();
+ var locationTime = location.LocationTime;
+ var locationToday = new DateTime(locationTime.Year, locationTime.Month, locationTime.Day);
+ var windowBeg = locationToday;
+ var windowEnd = locationToday.AddDays(1).AddMilliseconds(-1);
+
+ // 1. Get the owner name of the specified mailboxes calendar and set the data.DisplayName property
+ // 2. Check if the user has working hours set. If they do, set data.startTime and data.endTime and data.timeZone to those working hours
+ // 3. If reserveMinutes > 0 then create a new event in the calendar for reserveMinutes number of minutes
+ // 4. Get a list of events today
+ // 5. Filter those events based on TBC criteria and map to data.events
+
+ // Working Hours are stored in a users Mailbox Settings.
+ var mailboxSettings = await graphServiceClient.Me.MailboxSettings.GetAsync(
+ config =>
{
- if (match[0].Contact != null)
- {
- data.DisplayName =
- match[0].Contact.CompleteName.FullName
- ?? match[0].Contact.DisplayName
- ?? data.DisplayName
- ;
- }
-
- else if (match[0].Mailbox != null)
- {
- data.DisplayName =
- match[0].Mailbox.Name
- ?? data.DisplayName
- ;
- }
- }
- //else
- // throw new ApplicationException(string.Format("Mailbox {0} not found", mailbox));
+ config.QueryParameters.Select = new[] { "workingHours" };
+ });
+
+ // If we have working hours set, then set the start and end times. Note we always assume Local time. This might be bad?
+ // I'm unsure of the behaviour of the graph api when there are no working hours set.
+ if (mailboxSettings.WorkingHours != null)
+ {
+ data.startTime = mailboxSettings.WorkingHours.StartTime?.DateTime.TimeOfDay ?? DateTime.Today.TimeOfDay;
+ data.endTime = mailboxSettings.WorkingHours.EndTime?.DateTime.TimeOfDay ??
+ DateTime.Today.AddHours(23).AddMinutes(59).TimeOfDay;
+ data.timeZone = TimeZoneInfo.Local;
}
- // mailbox: get availability
- GetUserAvailabilityResults uars = await System.Threading.Tasks.Task.Run(() =>
+ // Get the name of the owner of the calendar.
+ var calendarDetails = await graphServiceClient.Users[outlook.Account].Calendar.GetAsync(c =>
{
- return service.GetUserAvailability(
- new AttendeeInfo[] { new AttendeeInfo(outlook.Mailbox, MeetingAttendeeType.Required, true) },
- new TimeWindow(locationToday, locationToday.AddDays(1)),
- AvailabilityData.FreeBusy
- );
+ c.QueryParameters.Select = new[] { "owner" };
});
- var u = uars.AttendeesAvailability[0];
- if (u.WorkingHours != null)
- {
- data.startTime = u.WorkingHours.StartTime;
- data.endTime = u.WorkingHours.EndTime;
- data.timeZone = u.WorkingHours.TimeZone;
- }
+ data.DisplayName = calendarDetails.Owner.Name;
+ // Do we need to create an event in the calendar?
if (reserveMinutes > 0)
{
- Appointment appointment = new Appointment(service)
- {
- Subject = outlook.BookingSubject,
- Body = string.Format("{0} | {1}", Resources.Outlook_BookingOnDemand, Resources.DisplayMonkey),
- Start = DateTime.Now,
- End = DateTime.Now.AddMinutes(reserveMinutes),
- };
-
- if (outlook.Mailbox == outlook.Account)
+ try
{
- await System.Threading.Tasks.Task.Run(() =>
+ _ = await graphServiceClient.Users[outlook.Account].Calendar.Events.PostAsync(new Event
{
- appointment.Save(SendInvitationsMode.SendToNone);
- return true;
+ Subject = outlook.BookingSubject,
+ Body = new ItemBody
+ {
+ Content = $"{Resources.Outlook_BookingOnDemand} | {Resources.DisplayMonkey}",
+ },
+ Start = new DateTimeTimeZone
+ {
+ DateTime = DateTime.Now.ToString("O"),
+ TimeZone = TimeZone.CurrentTimeZone.StandardName
+ },
+ End = new DateTimeTimeZone
+ {
+ DateTime = DateTime.Now.AddMinutes(reserveMinutes).ToString("O"),
+ TimeZone = TimeZone.CurrentTimeZone.StandardName
+ }
});
}
- else
+ catch (ODataError e)
{
- appointment.Resources.Add(outlook.Mailbox);
- await System.Threading.Tasks.Task.Run(() =>
- {
- appointment.Save(SendInvitationsMode.SendOnlyToAll);
- return true;
- });
+ throw new Exception(e.Error.Message);
}
}
- // events: prep filter
- FolderId folderId = new FolderId(WellKnownFolderName.Calendar, new Mailbox(outlook.Mailbox));
- CalendarFolder calendar = await System.Threading.Tasks.Task.Run(() => CalendarFolder.Bind(service, folderId, new PropertySet()));
- CalendarView cView = new CalendarView(windowBeg, windowEnd)
+ // Get list of events in the calendar
+ var calendarView = await graphServiceClient.Users[outlook.Account].CalendarView.GetAsync(c =>
{
- PropertySet = new PropertySet(
- //BasePropertySet.FirstClassProperties,
- AppointmentSchema.Subject,
- AppointmentSchema.DateTimeCreated,
- AppointmentSchema.Start,
- AppointmentSchema.End,
- AppointmentSchema.Duration,
- AppointmentSchema.Sensitivity,
- AppointmentSchema.LegacyFreeBusyStatus
- )
- };
-
- // events: get list
- var appointments = await System.Threading.Tasks.Task.Run(() => calendar.FindAppointments(cView));
- data.events = appointments
- //.FindAppointments(cView)
- .Where(a =>
- (outlook.Privacy != Models.OutlookPrivacy.OutlookPrivacy_NoClassified || a.Sensitivity == Sensitivity.Normal) &&
- (outlook.IsShowAsAllowed(a.LegacyFreeBusyStatus))
- )
- .OrderBy(i => i.Start)
- .ThenBy(i => i.DateTimeCreated)
- .ThenBy(i => i.Subject)
- //.Take(Math.Max(1, outlook.ShowEvents))
+ c.Headers.Add("Prefer", "outlook.body-content-type=\"text\"");
+ c.QueryParameters.Select = new[] { "subject", "body", "start", "end", "bodyPreview", "createdDateTime", "sensitivity", "showAs"};
+ c.QueryParameters.StartDateTime = windowBeg.ToString("O");
+ c.QueryParameters.EndDateTime = windowEnd.ToString("O");
+ });
+ data.events = calendarView.Value
+ .Where(a =>
+ (outlook.Privacy != Models.OutlookPrivacy.OutlookPrivacy_NoClassified ||
+ a.Sensitivity == Sensitivity.Normal) &&
+ outlook.IsShowAsAllowed(a.ShowAs ?? FreeBusyStatus.Busy)
+ )
.Select(a => new EventEntry
{
Subject =
- outlook.Privacy == Models.OutlookPrivacy.OutlookPrivacy_All || a.Sensitivity == Sensitivity.Normal ? a.Subject :
- DataAccess.StringResource(string.Format("EWS_Sensitivity_{0}", a.Sensitivity.ToString())),
- CreatedOn = a.DateTimeCreated,
- Starts = a.Start,
- Ends = a.End,
- Duration = a.Duration,
- Sensitivity = a.Sensitivity,
+ outlook.Privacy == Models.OutlookPrivacy.OutlookPrivacy_All ||
+ a.Sensitivity == Sensitivity.Normal
+ ? a.Subject
+ : DataAccess.StringResource($"EWS_Sensitivity_{a.Sensitivity.ToString()}"),
+ CreatedOn = a.CreatedDateTime?.DateTime ?? new DateTime(),
+ Starts = DateTime.Parse(a.Start.DateTime),
+ Ends = DateTime.Parse(a.End.DateTime),
+ Duration = DateTime.Parse(a.End.DateTime) - DateTime.Parse(a.Start.DateTime),
+ Sensitivity = a.Sensitivity ?? Sensitivity.Normal,
Today = locationToday,
- ShowAs = a.LegacyFreeBusyStatus,
+ ShowAs = a.ShowAs ?? FreeBusyStatus.Busy,
})
- .ToList()
- ;
+ .ToList();
return data;
}
#endregion
-
- #region -------- EWS Callbacks --------
-
- private static bool RedirectionUrlValidationCallback(string redirectionUrl)
- {
- // The default for the validation callback is to reject the URL.
- bool result = false;
-
- Uri redirectionUri = new Uri(redirectionUrl);
-
- // Validate the contents of the redirection URL. In this simple validation
- // callback, the redirection URL is considered valid if it is using HTTPS
- // to encrypt the authentication credentials.
- if (redirectionUri.Scheme == "https")
- {
- result = true;
- }
- return result;
- }
-
- private static bool CertificateValidationCallBack(
- object sender,
- System.Security.Cryptography.X509Certificates.X509Certificate certificate,
- System.Security.Cryptography.X509Certificates.X509Chain chain,
- System.Net.Security.SslPolicyErrors sslPolicyErrors
- )
- {
- //return true;
- // If the certificate is a valid, signed certificate, return true.
- if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
- {
- return true;
- }
-
- // If there are errors in the certificate chain, look at each error to determine the cause.
- if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
- {
- if (chain != null && chain.ChainStatus != null)
- {
- foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status
- in chain.ChainStatus)
- {
- if ((certificate.Subject == certificate.Issuer) &&
- (status.Status ==
- System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
- {
- // Self-signed certificates with an untrusted root are valid.
- continue;
- }
- else
- {
- if (status.Status !=
- System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NoError)
- {
- // If there are any other errors in the certificate chain, the certificate is invalid,
- // so the method returns false.
- return false;
- }
- }
- }
- }
-
- // When processing reaches this line, the only errors in the certificate chain are
- // untrusted root errors for self-signed certificates. These certificates are valid
- // for default Exchange server installations, so return true.
- return true;
- }
-
- // In all other cases, return false.
- return false;
- }
-
- #endregion
}
#endregion
@@ -499,7 +383,7 @@ private class EventEntry : IComparable
public TimeSpan Duration { get; set; }
public DateTime Today { get; set; }
public Sensitivity Sensitivity { get; set; }
- public LegacyFreeBusyStatus ShowAs { get; set; }
+ public FreeBusyStatus ShowAs { get; set; }
public int CompareTo(EventEntry rhs)
{
@@ -510,14 +394,17 @@ public int CompareTo(EventEntry rhs)
int createdCheck = this.CreatedOn.CompareTo(rhs.CreatedOn);
if (createdCheck != 0)
return createdCheck;
-
+
return this.Subject.CompareTo(rhs.Subject);
}
}
private class EventEntryConverter : JavaScriptConverter
{
- public override IEnumerable SupportedTypes { get { return _supportedTypes; } }
+ public override IEnumerable SupportedTypes
+ {
+ get { return _supportedTypes; }
+ }
public override IDictionary Serialize(object obj, JavaScriptSerializer serializer)
{
@@ -530,33 +417,40 @@ public override IDictionary Serialize(object obj, JavaScriptSeri
serialized["showAs"] = "";
List flags = new List();
- foreach (var x in _sens.Where(e => e.Key == evt.Sensitivity)) // one and only
+ foreach (var x in _sens.Where(e => e.Key == evt.Sensitivity)) // one and only
{
flags.Add(x.Value.Flag);
serialized["sensitivity"] = DataAccess.StringResource(x.Value.Resource);
break;
}
- foreach (var x in _showAs.Where(e => e.Key == evt.ShowAs)) // one and only
+
+ foreach (var x in _showAs.Where(e => e.Key == evt.ShowAs)) // one and only
{
flags.Add(x.Value.Flag);
serialized["showAs"] = DataAccess.StringResource(x.Value.Resource);
break;
}
+
serialized["flags"] = flags.ToArray();
-
+
DateTime tomorrow = evt.Today.AddDays(1);
- serialized["starts"] = (evt.Starts >= tomorrow ? evt.Starts.ToShortDateString() + " " : "") + evt.Starts.ToShortTimeString();
- serialized["ends"] = (evt.Ends >= tomorrow ? evt.Ends.ToShortDateString() + " " : "") + evt.Ends.ToShortTimeString();
-
+ serialized["starts"] = (evt.Starts >= tomorrow ? evt.Starts.ToShortDateString() + " " : "") +
+ evt.Starts.ToShortTimeString();
+ serialized["ends"] = (evt.Ends >= tomorrow ? evt.Ends.ToShortDateString() + " " : "") +
+ evt.Ends.ToShortTimeString();
+
if ((int)evt.Duration.TotalHours > 0)
- serialized["duration"] = string.Format(Resources.Outlook_HrsMin, (int)evt.Duration.TotalHours, evt.Duration.Minutes);
+ serialized["duration"] = string.Format(Resources.Outlook_HrsMin, (int)evt.Duration.TotalHours,
+ evt.Duration.Minutes);
else
serialized["duration"] = string.Format(Resources.Outlook_Min, evt.Duration.Minutes);
}
+
return serialized;
}
- public override object Deserialize(IDictionary dictionary, Type type, JavaScriptSerializer serializer)
+ public override object Deserialize(IDictionary dictionary, Type type,
+ JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
@@ -564,7 +458,7 @@ public override object Deserialize(IDictionary dictionary, Type
#region Private members
private static Dictionary _sens;
- private static Dictionary _showAs;
+ private static Dictionary _showAs;
private static IEnumerable _supportedTypes;
private class EventEntryAttribute
@@ -578,18 +472,28 @@ static EventEntryConverter()
_supportedTypes = new[] { typeof(EventEntry) };
_sens = new Dictionary(4);
- _sens.Add(Sensitivity.Normal, new EventEntryAttribute { Flag = "normal", Resource = "EWS_Sensitivity_Normal" }); // 0
- _sens.Add(Sensitivity.Personal, new EventEntryAttribute { Flag = "personal", Resource = "EWS_Sensitivity_Personal" }); // 1
- _sens.Add(Sensitivity.Private, new EventEntryAttribute { Flag = "private", Resource = "EWS_Sensitivity_Private" }); // 2
- _sens.Add(Sensitivity.Confidential, new EventEntryAttribute { Flag = "confidential", Resource = "EWS_Sensitivity_Confidential" }); // 3
-
- _showAs = new Dictionary(6);
- _showAs.Add(LegacyFreeBusyStatus.Free, new EventEntryAttribute { Flag = "free", Resource = "EWS_ShowAs_Free" }); // 0
- _showAs.Add(LegacyFreeBusyStatus.Tentative, new EventEntryAttribute { Flag = "tentative", Resource = "EWS_ShowAs_Tentative" }); // 1
- _showAs.Add(LegacyFreeBusyStatus.Busy, new EventEntryAttribute { Flag = "busy", Resource = "EWS_ShowAs_Busy" }); // 2
- _showAs.Add(LegacyFreeBusyStatus.OOF, new EventEntryAttribute { Flag = "oof", Resource = "EWS_ShowAs_Oof" }); // 3
- _showAs.Add(LegacyFreeBusyStatus.WorkingElsewhere, new EventEntryAttribute { Flag = "wew", Resource = "EWS_ShowAs_Wew" }); // 4
- _showAs.Add(LegacyFreeBusyStatus.NoData, new EventEntryAttribute { Flag = "noData", Resource = "EWS_ShowAs_NoData" }); // 5
+ _sens.Add(Sensitivity.Normal,
+ new EventEntryAttribute { Flag = "normal", Resource = "EWS_Sensitivity_Normal" }); // 0
+ _sens.Add(Sensitivity.Personal,
+ new EventEntryAttribute { Flag = "personal", Resource = "EWS_Sensitivity_Personal" }); // 1
+ _sens.Add(Sensitivity.Private,
+ new EventEntryAttribute { Flag = "private", Resource = "EWS_Sensitivity_Private" }); // 2
+ _sens.Add(Sensitivity.Confidential,
+ new EventEntryAttribute { Flag = "confidential", Resource = "EWS_Sensitivity_Confidential" }); // 3
+
+ _showAs = new Dictionary(6);
+ _showAs.Add(FreeBusyStatus.Free,
+ new EventEntryAttribute { Flag = "free", Resource = "EWS_ShowAs_Free" }); // 0
+ _showAs.Add(FreeBusyStatus.Tentative,
+ new EventEntryAttribute { Flag = "tentative", Resource = "EWS_ShowAs_Tentative" }); // 1
+ _showAs.Add(FreeBusyStatus.Busy,
+ new EventEntryAttribute { Flag = "busy", Resource = "EWS_ShowAs_Busy" }); // 2
+ _showAs.Add(FreeBusyStatus.Oof,
+ new EventEntryAttribute { Flag = "oof", Resource = "EWS_ShowAs_Oof" }); // 3
+ _showAs.Add(FreeBusyStatus.WorkingElsewhere,
+ new EventEntryAttribute { Flag = "wew", Resource = "EWS_ShowAs_Wew" }); // 4
+ _showAs.Add(FreeBusyStatus.Unknown,
+ new EventEntryAttribute { Flag = "noData", Resource = "EWS_ShowAs_NoData" }); // 5
}
#endregion
diff --git a/Presentation/packages.config b/Presentation/packages.config
index 1221489..80fe289 100644
--- a/Presentation/packages.config
+++ b/Presentation/packages.config
@@ -1,7 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file