In my previous post I showed how you can use Flow to trigger a scheduled flow and retrieve an iCal feed over HTTP.
I also spent a bit of time explaining how to parse the text returned from the iCal feed into individual events and extract the information required to be able to import and synchronize the events in a SharePoint list. I mostly covered at a high-level and didn’t give you detailed steps — which is a good thing, because the article was already pretty long!
In today’s post, we’ll set up a SharePoint list and walk you through how to implement the flow, step-by-step.
Let’s get going!
To synchronize an iCal feed to SharePoint, we need two things:
And we’ll need a flow to combine the two.
For the feed, I’ll use one of CalendarLabs.com‘s iCal feeds. Being Canadian, I’ll use the Canadian Holidays feed, but feel free to use the US Holidays or any other that suits you. (Make sure that you get the URL from the Download button). I have to admit that I only learned about CalendarLabs.com when I was researching this post, but I’ll definitely be recommending it to everyone!
There are all sorts of useful iCal feeds out there. For example, if your company uses People HR to for your HR software, you can get an iCal feed of employee vacations directly from People HR and synchronize with a list called Staff Calendar.
Let’s assume you have found an iCal feed you want to sync and you tested it to make sure it returns something.
For the SharePoint list, we’ll create one. Since the iCal feed I’m using is a list of Statutory Holidays, I’ll call my new list Statutory Holidays (I know, right? How do I come up with this stuff?!)
To create the list, follow these steps:
Your list should automatically get created. Now we need to add one more field called UID to keep track of the unique identifiers for each event. The UIDs are provided by the iCal feed.
To add the column, follow these steps:
Yesterday, I briefly touched how I build the flow to extract the data. Today, we’ll do it step-by-step, starting with these steps:
Once your flow is created, follow these steps:
Try your flow by using the Test button in the upper right corner. Your Get events from iCal should return events. If it doesn’t, check the URL.
Select the + New step and type Initialize in the Choose an action window.
From the list of Actions, select Initialize variable
Rename your new action to Get list of events
In the initialize action’s details, set the Name to Events, the Type to Array and the Value to:
split(replace(body('Get_events_from_iCal'), '\\n', ''), 'BEGIN:VEVENT')
This will split the iCal feed into an array of events where BEGIN:VEVENT
can be found.
Select the + New step and type Filter in the Choose an action window
From the list of Actions, select Filter array
Rename the new action to Remove rows that are not events
In the From field, select the Events variable using the Dynamic content
Below the From field, select Edit in advanced mode and type:
@not(startsWith(item(), 'BEGIN'))
This will keep only array items that don’t start with BEGIN
, thus removing all the iCal header information and leaving you with only events.
This will create a loop for every event in the array.
Within the Loop through every event loop, select Add an action
In the Choose an action window, type compose
From the list of Actions, select Compose
Rename your new action Get all lines in event
In the Input field, you’ll want to use:
split(item(), json('{"NL":"\n"}')?['NL'])
Now, all we need to do is extract the data from every relevant line
Still within the loop, select Add an action and type Filter in the Choose an action window
From the list of Actions, select Filter
Rename the new action Find DTSTART
In the Inputs field’s Expression tab, type:
@startsWith(item(), 'DTSTART')
Add another action using Add an action and type Compose
From the list of actions, select Compose
Rename the action to Get DTSTART
In the Inputs field, type the following expression:
replace(first(body('Find_DTSTART')), 'DTSTART;VALUE=DATE:', '')
Repeat the same steps as above except that you should call the Filter action Find DTEND and use the following expression:
@startsWith(item(), 'DTEND;VALUE=')
And in the Compose action, call it Get DTEND and use the following expression
replace(first(body('Find_DTEND')), 'DTEND;VALUE=DATE:', '')
You guessed it, repeat same as above, but name the Filter action Find SUMMARY and use the following expression:
@startsWith(item(), 'SUMMARY:')
And set your Compose action to Get SUMMARY and use this expression:
replace(first(body('Find_SUMMARY')), 'SUMMARY:', '')
One last time! Repeat same as above, but name the Filter action Find UID and use the following expression:
@startsWith(item(), 'UID:')
And set your Compose action to Get UID and use this expression:
replace(first(body('Find_UID')), 'UID:', '')
Now you have all the properties we need. You should test your flow to make sure everything works.
Note that if your iCal feed has different fields that you need, you may need to adjust your actions above. For example, some feeds will return DATE-TIME
and DATE
events, so you may want to add a little condition up there to deal with such events. To keep things simple, we won’t do that here.
UID eq '
then select the Output from Get UID in the Dynamic content window.'
to close the filter queryIf you test your flow now, every Get Items action should return the following:
{
[]
}
Because there are no events to retrieve. Let’s fix that.
Still within the loop, select Add an action and type Condition in the Choose an action window
From the list of Actions, select Condition
Rename the condition Any existing events found
In the expression below type:
length(body('Get_items')?['value'])
Select by is greater than in the next field
In the next field, type 0
This will cause the condition to go to If yes when there is an existing event, and If no if there isn’t an existing event. We’ll only worry about If no for now, but you could always update the existing item in the If yes side if you wanted to. Just no today, ok?
In the If no site, select Add an action
In the Choose an action field, type Create item
In the Actions list, select Create item from SharePoint
In the Create item dialog, type your Site Address and pick Statutory Holidays from the List Name
In the Title field, bind to the Output from Get SUMMARY
We’ll skip the Start Time and End Time for now. In the UID field, set it to the Output of the Get UID action.
For the Start Time, we’ll need to do some surgery because SharePoint expects the value to be formatted as yyyy-MM-ddThh:mm:ss
. To do this, we’ll use substring()
to extract the year, month, and day from the event’s date but using the following expression:
concat(substring(outputs('Get_DTSTART'),0,4),'-',substring(outputs('Get_DTSTART'),4,2),'-',substring(body('Get_DTSTART'),6,2),'T00:00:00-00:00')
Which essentially combines the first 4 characters of the Start Date with a -
, followed by the next 2 characters of Start Date, followed by another -
, and the last two characters of Start Date, followed by T00:00:00
to set the time to midnight. If your iCal returns DATE-TIME
values, you’ll also want to parse the time element instead of setting it to 00:00:00
.
The last part -00:00
is for the timezone. If you find that your events are coming in at the wrong time, you can adjust the time zone accordingly (e.g.: +01:00
or -01:00
).
Repeat the same formula for End Time, except that you should use GET_DTEND
instead of GET_DTSTART
.
Test your workflow, and you should have a whole bunch of new events! Then try again, and you should not get more events until the iCal feed adds a new event.
(Sigh of relief!) That was a long post!
This post showed you how to get an iCal feed in Flow and import events from that feed into a SharePoint list.
You can use a similar approach for other types of feeds.
There are a few more opportunities to improve the resiliency of this flow, and to deal with all-day events that span over two days when they should really last one day… but that’ll have to be for another post.
I hope this helped?
Image by Free-Photos from Pixabay