Download Free Demos

Synchronization with Outlook Calendar

Introduction

This article explains how to implement calendar with DHTMLX Scheduler .NET in .NET MVC 5 that uses Outlook Calendar as data source, without using your own database.

After authorization a user will be able to see Outlook Calendar events of his Microsoft Live account. We will use Outlook REST API to interact with calendars and events. It should be noted that all code in this tutorial is valid for MVC 4 application.

sync with outlook calendar

Step 1. Getting Started

For executing methods of Outlook REST we should have access token that is received on successful authorization by OAuth 2.0 protocol. We should set up Windows Live Application and get credentials for authorization in our project.

Note: if we want to get Outlook Calendar events by API, we should select start and end loading date and one of the existing calendars. In this tutorial we will load calendars and offer a user to select one of them. Current month events will be loaded.

Step 2. Setting Up Windows Live Application

Sign in to Windows Live application management site.

1 . You will see a page that contains a list of registered apps. Click Create application on this page (skip this step if you haven't created applications before).

2 . Set an application name (e.g. "Scheduler Demo") and language, agree with the terms.

3 . In the left sidebar open App Settings and copy ClientId and Client secret. We will put them into the configuration file of our application later.

4 . In the left sidebar open API Settings and specify Redirect URL*.

*authorization is done by the OAuth 2.0 protocol. Redirect URL is our application address that matches MVC controller method that receives authorization code from Microsoft server as GET parameter. In our example it will be localhost:58342/Auth/. Note that your port will be different and depends on MVC project properties.

Step 3. Integrating with Outlook Calendar

Outlook REST API Description

For interaction with Outlook Calendar we will use Outlook REST API, see this link. The scheme of interacting with Outlook Calendar by REST API is the following:

1 . Authorization by the OAuth 2.0 protocol and getting access token.

2 . Using API methods with the received access token.

Architecture

All logic in the solution will be divided into 3 parts: MVC presentation, Outlook server interaction and OAuth2 authorization. API interaction will be separated from authorization, because it is independent from .NET, while authorization is bound to MVC. So, our solution will contain 3 assemblies:

  • Presentation.Mvc5 (MVC 5 app, Razor)
  • OutlookApi.Wrapper (Class library, describes API interaction)
  • OutlookApi.Authorization.Mvc (Class library)

API Interaction. (OutlookApi.Wrapper)

Here will be data entities and helper class, that we will use for interacting with Outlook REST API.

Models folder

Outlook REST API returns calendars and events as JSON object called Data. It contains the collection of entities, which we will deserialize into our models below:

Create the Models folder with the following model classes:

public class OutlookCalendar
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Permissions { get; set; }
    }
 
public class OutlookEvent
    {
        public string id { get; set; }
        public string name { get; set; }
        public string description { get; set; }
        public DateTime start_time { get; set; }
        public DateTime end_time { get; set; }
    }
 
internal class ReceivedCalendarsData
    {
        public List<OutlookCalendar> Data { get; set; }
    }
 
internal class ReceivedEventsData
    {
        public List<OutlookEvent> Data { get; set; }
    }

OutlookCalendarApi.cs

Add reference on the system assembly System.Web.Extensions.

Then create the OutlookCalendarApi class in the project root.

public class OutlookCalendarApi
 {
   public string AccessToken { get; set; }
 
   private const string UrlBase = "https://apis.live.net/v5.0/";
 
   public OutlookCalendarApi()
   {
   }
 
   public OutlookCalendarApi(string accessToken)
   {
     AccessToken = accessToken;
   }
 }

Requests for all Outlook Calendar API methods are similar, that's why we will write a basic request composing method, that sends it and receives a response from the API.

This method uses a generic type of entity to return after getting response from Outlook API server. Data parameter is used for sending event fields for update or create. Here are examples of requests we will compose.

private T GetResponse<T>(string uri, string method, Dictionary<string, object> data)
  {
    var request = (HttpWebRequest) WebRequest.Create(uri);
    request.Method = method;
    request.Headers.Add("Authorization", string.Format("Bearer {0}", AccessToken));
 
    if (data.Count != 0)
      {
        string paramsStr = string.Join("&", data.Select(item => String.Format("{0}={1}", item.Key, item.Value)));
        request.ContentLength = paramsStr.Length;
        request.ContentType = "application/x-www-form-urlencoded";
 
        using (var sw = new StreamWriter(request.GetRequestStream()))
          {
             sw.Write(paramsStr);
             sw.Close();
          }
      }
 
      var response = (HttpWebResponse)request.GetResponse();
      string responseText;
      using (var reader = new StreamReader(response.GetResponseStream()))
      {
         responseText = reader.ReadToEnd();
      }
 
      return new JavaScriptSerializer().Deserialize<T>(responseText);
  }

Add the same method for the data parameter with an empty dictionary for simplification

private T GetResponse<T>(string uri, string method)
 {
    return GetResponse<T>(uri, method, new Dictionary<string, object>());
 }

Add methods for getting calendars:

public IEnumerable<OutlookCalendar> GetCalendars()
  {
     var data = GetResponse<ReceivedCalendarsData>(UrlBase + "me/calendars", "GET");
     return data.Data.Where(item => item.Permissions.Equals("owner"));
  }

Add events CRUD methods (the calendarId parameter is the id of calendar the selected event belongs or will belong to):

public string CreateEvent(OutlookEvent @event, string calendarId)
        {
            var sendData = EventAsSendData(@event);
            var receivedData = GetResponse<OutlookEvent>(UrlBase + calendarId + "/events", "POST", sendData);
            return receivedData.id;
        }
 
        public string UpdateEvent(OutlookEvent @event)
        {
            var sendData = EventAsSendData(@event);
            var receivedData = GetResponse<OutlookEvent>(UrlBase + @event.id, "PUT", sendData);
            return receivedData.id;
        }
 
        public IEnumerable<OutlookEvent> ReadEvents(string calendarId, DateTime from, DateTime to)
        {
            string requestStr = String.Format(
                "{0}{1}/events?start_time={2}Z&end_time={3}Z",
                UrlBase,
                calendarId,
                from.ToString("s"),
                to.ToString("s")
                );
 
            var eventsData = GetResponse<ReceivedEventsData>(requestStr, "GET");
            return eventsData.Data;
        }
 
        public void DeleteEvent(string id)
        {
            GetResponse<object>(UrlBase + id, "DELETE");
        }

And a private helper method that we'll use for converting our data entities properties to name-value collection that will be used for request composing:

private Dictionary<string, object> EventAsSendData(OutlookEvent @event)
        {
            return new Dictionary<string, object>
            {
                {"name", @event.name},
                {"description", @event.description ?? String.Empty},
                {"start_time", @event.start_time.ToUniversalTime().ToString("s") + "Z"},
                {"end_time", @event.end_time.ToUniversalTime().ToString("s") + "Z"}
            };
        }

Authorization. (OutlookApi.Authorization.Mvc)

This assembly will contain the auth helper class. OAuth 2.0 authorization implementation is different for each platform/framework, that's why this assembly will contain non-generic MVC authorization.

Authorization process is the following. The user

  • opens a page with calendar
  • redirects into Microsoft Live log on the page
  • submits authorization
  • returns back to the calendar page as an authorized user

OAuth 2.0 authorization will be implemented as helper class: MvcAuthHelper.cs. We will authorize each time when we will visit page with calendar in MVC project, because access token expires after 1 hour. So, we will store the access token in cookie.

The algorithm of getting an access token:

  • Visit authorization page in web browser, authorize
  • Get authorization code as GET parameter after redirecting from the authorization server
  • Request an access token by sending the authorization code

So, our MvcAuthHelper.cs will have 3 public methods:

  • public object Authorize() - will redirect user to Microsoft Live authorization page. When the user is authorized, it will send him back to the page specified in the Redirect URL setting, providing authorization code in request parameters
  • public bool IsAuthorized() - return true, if authorization was successful
  • public string GetAccessToken() - return access token by our auth code

Firstly, install DotNetOpenAuth package from NuGet:

PM> Install-Package DotNetOpenAuth -ProjectName OutlookApi.Authorization.Mvc

After that install .NET MVC package:

PM> Install-Package Microsoft.AspNet.Mvc -Version 5.0.0 -ProjectName OutlookApi.Authorization.Mvc

Then install DotNetOpenAuth MVC5 Extensions package:

PM> Install-Package DotNetOpenAuth.Mvc5 -ProjectName OutlookApi.Authorization.Mvc

Add reference on system assembly System.Web

Create OutlookAuthData.cs file with the following model class:

public class OutlookAuthData
    {
        public Uri RedirectUri { get; set; }
        public string ClientId { get; set; }
        public string ClientSecret { get; set; }
        public IEnumerable<string> Scopes { get; set; }
    }

It contains authorization data, that can be found at Microsoft Live Application. Also, it contains permission scopes for interacting with Outlook REST API, see this link. We will use the following scopes:

  • wl.calendars_update
  • wl.signin
  • wl.events_create

Then write MvcAuthHelper class base.

WebServerClient, AuthorizationServerDescription and OutgoingWebResponse in the snippet below are members of the DotNetOpenAuth library, that implements OAuth 2.0 authorization for .NET.

    public class MvcAuthHelper : IAuthorizable
    {
        private readonly HttpContextBase _httpContext;
        private readonly OutlookAuthData _authData;
        private readonly WebServerClient _client;
        private readonly OutgoingWebResponse _response;
 
        public MvcAuthHelper(HttpContextBase httpContext, OutlookAuthData authData)
        {
            _httpContext = httpContext;
            _authData = authData;
 
            var server = new AuthorizationServerDescription
            {
                AuthorizationEndpoint = new Uri("https://login.live.com/oauth20_authorize.srf"),
                TokenEndpoint = new Uri("https://login.live.com/oauth20_token.srf"),
                ProtocolVersion = ProtocolVersion.V20
            };
 
            _client = new WebServerClient(server, _authData.ClientId, _authData.ClientSecret);
            _response = _client.PrepareRequestUserAuthorization(_authData.Scopes, _authData.RedirectUri);
        }
 
    }

And the methods mentioned at the beginning of the authorization section are:

public bool IsAuthorized()
        {
            return !String.IsNullOrEmpty(_httpContext.Request.QueryString["code"]);
        }
 
        public object Authorize()
        {
            return _response.AsActionResultMvc5();
        }
 
        public string GetAccessToken()
        {
            return _client.ProcessUserAuthorization().AccessToken;
        }

Presentation. (Presentation.Mvc5)

Creating an empty MVC5 project in Visual Studio 2015 Community is implemented in the following way:

Right click your solution > Add > New Project > Visual C# > Web > ASP.NET Web Application > OK

 calendar project and outlook calendar

Then select an Empty template, check MVC checkbox and click OK.

 mvc template for calendars syncronization

Getting started

Before adding controllers, let's set a basic layout and styles, install Scheduler.NET package and set needed references.

Create the Content folder with Style.css:

html, body {
    font-family: "Arial";
}
 
.calendar-container {
    height: 800px;
}
 
.display-settings-item {
    margin: 20px;
}
 
.calendar-select-form {
    width: 400px;
    height: 140px;
    margin: 80px auto 0px auto;
    padding: 10px;
    border: 1px solid lightgray;
 
    text-align: center;
}

Create the Shared folder in Views with _Layout.cshtml file:

<!DOCTYPE html>
 
<html>
<head>
    <title>@ViewBag.Title</title>
    <link rel="stylesheet" href="@Url.Content("~/Content/Style.css")"/>
</head>
<body>
    <div>
        @RenderBody()
    </div>
</body>
</html>

Add _Viewport.cshtml in the Views folder.

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Add references on solution projects: OutlookApi.Wrapper and OutlookApi.Authorization.MVC

Install DHTMLX Scheduler.NET using NuGet:

Install-Package DHTMLX.Scheduler.NET -ProjectName Presentation.Mvc5

Controllers

In MVC application we will have 3 controllers:

  • CalendarController - main controller that will define actions for two pages: a page where a user can select one of his Outlook Calendars to be displayed and a page with a Scheduler.
  • AuthController - responsible for authorization.
  • DataAccessController - will synchronize received data from client with Outlook Calendar server

Auth Controller

Add AuthController to the Controllers folder

        /// <summary>
        /// Gets Outlook API access token, saves as cookie and redirects
        /// to calendar controller
        /// </summary>
        /// <returns></returns>
        public ActionResult Index()
        {
            var authHelper = new MvcAuthHelper(HttpContext, GetAuthData());
 
            // authorize if not authorized
            if (!authHelper.IsAuthorized()) return (ActionResult) authHelper.Authorize();
 
            // sets cookie with token
            var tokenCookie = new HttpCookie("OutlookAccessToken", authHelper.GetAccessToken());
            tokenCookie.Expires = DateTime.Now.AddHours(1);
            HttpContext.Response.SetCookie(tokenCookie);
 
            return RedirectToAction("Select", "Calendar");
        }
 
        /// <summary>
        /// Gets user's data from config file, needed for authorization by OAuth 2.0 protocol
        /// </summary>
        /// <returns></returns>
        private OutlookAuthData GetAuthData()
        {
            return new OutlookAuthData
            {
                ClientId = ConfigurationManager.AppSettings["OutlookApi_ClientId"],
                ClientSecret = ConfigurationManager.AppSettings["OutlookApi_ClientSecret"],
                RedirectUri = new Uri(ConfigurationManager.AppSettings["OutlookApi_RedirectUri"]),
                Scopes = new[] { "wl.calendars_update", "wl.signin", "wl.events_create" }
            };
        }

The GetAuthData( ) method will take data needed for authorizing by OAuth 2.0 from Web.config AppSettings section, let's add them:

<add key="OutlookApi_RedirectUri" value="[REDIRECT_URL]" />
<add key="OutlookApi_ClientId" value="[CLIENT_ID]" />
<add key="OutlookApi_ClientSecret" value="CLIENT_SECRET" />

Redirect URL, client ID and client secret can be found in your Microsoft Live dev app settings.

Set routing settings in App_Start > RouteConfig.cs, setting AuthControler as the default controller.

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Auth", action = "Index", id = UrlParameter.Optional }
            );
        }

Calendar Controller

On successful authorization we will get the Select method of Calendar controller, where we will suggest a user to select the calendar to display. So, let's create CalendarController with the Select() method:

public ActionResult Select()
        {
            // get available calendars for DropDownList
            var api = new OutlookCalendarApi(Request.Cookies["OutlookAccessToken"].Value);
            var calendars = api.GetCalendars().Select(c => new SelectListItem { Text = c.Name, Value = c.Id }).ToList();
            ViewBag.Calendars = calendars;
 
            return View();
        }

Then create its view:

@{
    ViewBag.Title = "Calendar select page";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
<div class="calendar-select-form">
    <h2 style="margin-bottom: 40px;">Select calendar</h2>
 
    @using (Html.BeginForm("Index", "Calendar"))
    {
        <div class="display-settings-item">
            @Html.DropDownList("calendarId", (List<SelectListItem>) ViewBag.Calendars)
            <button type="submit">Display</button>
        </div>
    }
</div>

Then add the Index() method to CalendarController, which will initialize and render our calendar:

public ActionResult Index()
        {
            var scheduler = ConfigureScheduler();
            return View(scheduler);
        }
 
private DHXScheduler ConfigureScheduler()
        {
            var scheduler = new DHXScheduler();
 
            scheduler.LoadData = true;
            scheduler.EnableDataprocessor = true;
 
            string calendarId = Request["calendarId"];
 
            // load only this month events
            scheduler.EnableDynamicLoading(SchedulerDataLoader.DynamicalLoadingMode.Month);
 
            // compose data action for sending dateFrom, dateTo and accessToken
scheduler.DataAction = String.Format(
                "{0}?calendarId={1}",
                Url.Action("Data", "DataAccess"),
                Url.Encode(calendarId)
                );
 
 
            scheduler.SaveAction = String.Format(
                "{0}?calendarid={1}",
                Url.Action("Save", "DataAccess"),
                Url.Encode(calendarId)
                );
 
            scheduler.Lightbox.Add(new LightboxText("text", "Name"));
            scheduler.Lightbox.Add(new LightboxText("description", "Details"));
 
            // TODO: implement!
 
            return scheduler;
        }

After that add it's view:

@model DHTMLX.Scheduler.DHXScheduler
@{
    ViewBag.Title = "Outlook Calendar";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
<div class="calendar-container">
    @Html.Raw(Model.Render())
</div>

DataAccess Controller

Now add the Event model to the Models folder:

public class Event
    {
        public string id { get; set; }
        public string text { get; set; }
        public string description { get; set; }
        public DateTime start_date { get; set; }
        public DateTime end_date { get; set; }
    }

DataAccess controller will have 2 public methods:

  • Data( ) - will return events data to our client using Outlook
  • Save(string id, FormCollection actionValues) - will update Outlook Calendar events due to our scheduler calendar
public ActionResult Data()
        {
            var api = new OutlookCalendarApi
            {
                AccessToken = Request.Cookies["OutlookAccessToken"].Value
            };
 
            var events = api.ReadEvents(
                Request["calendarId"],
                DateTime.Parse(Request.QueryString["from"]),
                DateTime.Parse(Request.QueryString["to"])
                );
 
            return new SchedulerAjaxData(events.Select(item => new Event
            {
                id = item.id,
                text = item.name,
                description = item.description,
                start_date = item.start_time,
                end_date = item.end_time
            }));
        }
 
public ActionResult Save(string id, FormCollection actionValues)
        {
            var action = new DataAction<string>(actionValues);
            var @event = DHXEventsHelper.Bind<Event>(actionValues);
 
            try
            {
                var api = new OutlookCalendarApi
                {
                    AccessToken = Request.Cookies["OutlookAccessToken"].Value
                };
 
                switch (action.Type)
                {
                    case DataActionTypes.Insert:
                        action.TargetId = api.CreateEvent(EventToOutlookEvent(@event), Request["calendarId"]);
                        break;
                    case DataActionTypes.Update:
                        api.UpdateEvent(EventToOutlookEvent(@event));
                        break;
                    case DataActionTypes.Delete:
                        api.DeleteEvent(id);
                        break;
                }
            }
            catch
            {
                action.Type = DataActionTypes.Error;
            }
 
            return new AjaxSaveResponse(action);
        }
 
private OutlookEvent EventToOutlookEvent(Event @event)
        {
            return new OutlookEvent
            {
                description = @event.description,
                name = @event.text,
                start_time = @event.start_date,
                end_time = @event.end_date,
                id = @event.id
            };
        }

Was this article helpful?

Yes No