tag:blogger.com,1999:blog-82362914343635305682024-03-04T23:13:10.254-08:00Rodrigo Silva's BlogSharePoint and Office365 development tips.RodCoderhttp://www.blogger.com/profile/08206822216774270650noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-8236291434363530568.post-31190904468374837772016-11-11T05:11:00.002-08:002016-11-11T05:17:53.491-08:00Power BI: A simple trick to present KPI's and RAG status in tables As some of you might already know, Power BI is a fantastic tool for interactive reports and dashboards. Although, if you've been doing some heavy lifting with it, you might also know that it has its limitations.<br />
<div>
In order to help you with one of them, I'll describe a step-by-step approach to display KPI's or RAG status in simple tables.</div>
<div>
<br /></div>
<div>
Scenario:</div>
<div>
I will be using data pulled from project online, in this specific case, tasks. Each task has a RAG status value in a field called 'RAGSched' (be aware that this is a custom field, hence it can have any other name). The icons I'm going to use, also belong to PWA and are stored in the '_layouts/15/inc/PWA' folder.</div>
<div>
To start, I'm going to create a static table to store the icon's name and url, then, I'm going to create a function to retrieve the url passing the icon's name as a parameter and finally apply a bit of logic in a new field, to get the right image according to the RAG field's values.</div>
<div>
<br /></div>
<div>
<b>1) Tenant and Site Collections parameters - as a good practice :)</b></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpg4oZ8KNqrgR2ecpnr_g0QW3fWcBDhcNuqhedHfs736DT0Cvrg1LlKUgc_qWxTLu8IK3ti30xGYpTJy-QZVJpCsshRGZYFcpjzd38L0uraat6O-YR_zdoVW2Pec1ZiBV0Ca22eUYVHiOC/s1600/1.png" imageanchor="1"><img border="0" height="154" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpg4oZ8KNqrgR2ecpnr_g0QW3fWcBDhcNuqhedHfs736DT0Cvrg1LlKUgc_qWxTLu8IK3ti30xGYpTJy-QZVJpCsshRGZYFcpjzd38L0uraat6O-YR_zdoVW2Pec1ZiBV0Ca22eUYVHiOC/s200/1.png" width="200" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKZwuR-J3LjRpZEMhwlXMBwmBBq1QebuBRZg79u3TSHvAuT2wDTrxzWge6HhQqkj36v1diLPOO2aui7jYbVGxFSy4t8sghbl5AY2oFnatldISP6xkgxrVLegF_B-U-n-mW25t3zjaXQswG/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKZwuR-J3LjRpZEMhwlXMBwmBBq1QebuBRZg79u3TSHvAuT2wDTrxzWge6HhQqkj36v1diLPOO2aui7jYbVGxFSy4t8sghbl5AY2oFnatldISP6xkgxrVLegF_B-U-n-mW25t3zjaXQswG/s400/2.png" width="368" /></a></div>
<div>
<b>2) The static table</b></div>
<div>
This will be just a simple table with the relative path to the icons.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMp5tkuTDg4-rxcyLZOAc5drLmhdBJt2H6vdQZrHnYqtGfEegZsS1mb44npaa3PySGyv9bDYfynRmTIUDiUBlRTqdnUx4wQLW1al5TEfJ3xIFM6X_07GvDf3ijk1IRfpT_W091Qb3mTGf-/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="315" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMp5tkuTDg4-rxcyLZOAc5drLmhdBJt2H6vdQZrHnYqtGfEegZsS1mb44npaa3PySGyv9bDYfynRmTIUDiUBlRTqdnUx4wQLW1al5TEfJ3xIFM6X_07GvDf3ijk1IRfpT_W091Qb3mTGf-/s400/3.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<b>3) The function</b></div>
<div>
Ok, now let's select 'New Source' > 'Blank Query' and then click the 'Advanced Editor' button to insert our DAX function:</div>
<div>
<br /></div>
<div>
<div>
<span style="color: #0b5394;">let</span></div>
<div>
<span style="color: #0b5394;"> IconUrl = (IconName as text) => </span></div>
<div>
<span style="color: #0b5394;"> let</span></div>
<div>
<span style="color: #0b5394;"> Source = List.First(Table.Column(Table.SelectRows(Icons, each [IconName] = IconName) as table, "ImageName" as text)),</span></div>
<div>
<span class="Apple-tab-span" style="white-space: pre;"><span style="color: #0b5394;"> </span></span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>#"CalculatedUrl" = "https://" & Tenant & ".sharepoint.com" & SiteCollection & "/" & Source</span></div>
<div>
<span style="color: #0b5394;"> in</span></div>
<div>
<span style="color: #0b5394;"> CalculatedUrl</span></div>
<div>
<span style="color: #0b5394;">in</span></div>
<div>
<span style="color: #0b5394;"> IconUrl</span></div>
</div>
<div>
<span style="color: #0b5394;"><br /></span></div>
<div>
I called it 'GetIconUrl'.</div>
<div>
<br /></div>
<div>
<b>4) Getting the data and adding the new RAG column</b></div>
<div>
As mentioned before, I'm going to be pulling data from PWA, which means I will add a new OData Feed query. Since it's created in the advanced editor mode, I'll break it step-by-step.</div>
<div>
<br /></div>
<div>
<span style="color: #0b5394;">Source = OData.Feed("https://" & Tenant & ".sharepoint.com" & SiteCollection & "/_api/ProjectData/Tasks?$select=TaskId,ProjectId,TaskName,</span><span style="color: red;">RAGSched</span><span style="color: #0b5394;">,TaskComments"),</span></div>
<div>
<span style="color: #0b5394;"><br /></span></div>
<div>
Here we define the source, using our parameter 'Tenant' and 'SiteCollection' to build the complete url. I'm querying the tasks feed and selecting the fields I want to work with (notice that 'RAGSched' is the field I'll be looking at).</div>
<div>
<br /></div>
<div>
<div>
<span style="color: #0b5394;">#"Tasks - Add RAG Column" = Table.AddColumn(Source, "</span><span style="color: red;">RAG Status</span><span style="color: #0b5394;">", each </span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (Text.Length([RAGSched]) > 0)</span></div>
<div>
<span style="color: #0b5394;"> then</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span> if Text.Contains([RAGSched], "Completed") </span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>then </span><span style="color: red;">GetIconUrl</span><span style="color: #0b5394;">("Milestone-Completed")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if Text.Contains([RAGSched], "On Schedule")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>then GetIconUrl("Milestone-OnSchedule")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else<span class="Apple-tab-span" style="white-space: pre;"> </span></span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if Text.Contains([RAGSched], "not critical")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>then GetIconUrl("Milestone-NonCritical")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>GetIconUrl("Milestone-Critical")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else </span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>"")</span></div>
</div>
<div>
<span style="color: #0b5394;"><br /></span></div>
<div>
The second part will add the new column 'RAG Status' and set the icon url according to the RAG field's value.</div>
<div>
<br /></div>
<div>
Here's the complete code block for the query:</div>
<div>
<br /></div>
<div>
<div>
<span style="color: #0b5394;">let</span></div>
<div>
<span style="color: #0b5394;"> Source = OData.Feed("https://" & Tenant & ".sharepoint.com" & SiteCollection & "/_api/ProjectData/Tasks?$select=TaskId,ProjectId,TaskName,RAGSched,TaskComments"),</span></div>
<div>
<span style="color: #0b5394;"> #"Tasks - Add RAG Column" = Table.AddColumn(Source, "RAG Status", each </span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if (Text.Length([RAGSched]) > 0)</span></div>
<div>
<span style="color: #0b5394;"> then</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span> if Text.Contains([RAGSched], "Completed") </span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>then GetIconUrl("Milestone-Completed")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if Text.Contains([RAGSched], "On Schedule")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>then GetIconUrl("Milestone-OnSchedule")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else<span class="Apple-tab-span" style="white-space: pre;"> </span></span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>if Text.Contains([RAGSched], "not critical")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>then GetIconUrl("Milestone-NonCritical")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>GetIconUrl("Milestone-Critical")</span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>else </span></div>
<div>
<span style="color: #0b5394;"><span class="Apple-tab-span" style="white-space: pre;"> </span>"")</span></div>
<div>
<span style="color: #0b5394;">in</span></div>
<div>
<span style="color: #0b5394;"> #"Tasks - Add RAG Column"</span></div>
</div>
<div>
<br /></div>
<div>
At this point, if everything went as expected, the new field will show the icon's url:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDhElEEIMs1By_4Ti4SgIZJbkLjwAbsOTHFPVJyR1caUYuPPoSdlAaLJUpMlvX1unc89DEdaHlxrvuK1Xbrozlwy565jwfkCd1A1d9wnbCSNAH1L9TVEcLHedxoXCDZOzkxQ0yoZ2yxzU9/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="110" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDhElEEIMs1By_4Ti4SgIZJbkLjwAbsOTHFPVJyR1caUYuPPoSdlAaLJUpMlvX1unc89DEdaHlxrvuK1Xbrozlwy565jwfkCd1A1d9wnbCSNAH1L9TVEcLHedxoXCDZOzkxQ0yoZ2yxzU9/s320/4.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
And this is how your 'Edit Queries' window should look like:<br />
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPOyZO2e5GZo35ddAT1WwAaZQeXJlLHTkkA4tV15VnMbpQ_31Czw_UJPnNmg6Cyh8AwYjgYfThxHpKOmr3gfY-j-KOCXWza0X7B7sV_qieizsGWdGuU5gohMw_zrI0lwcRTl1JMyHfNfYJ/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPOyZO2e5GZo35ddAT1WwAaZQeXJlLHTkkA4tV15VnMbpQ_31Czw_UJPnNmg6Cyh8AwYjgYfThxHpKOmr3gfY-j-KOCXWza0X7B7sV_qieizsGWdGuU5gohMw_zrI0lwcRTl1JMyHfNfYJ/s320/5.png" width="194" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<b>5) Applying the right data category</b></div>
<div>
The only thing missing now is to tell Power BI that our new column contains an image url. To do that, just navigate to your data, select the new column (RAG Status), click on 'Modeling' > 'Data Category' and select 'Image URL'.<br />
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpKdwtqY4SHEZPmGHrh8_33bkPg8jIdpS7-yLdhX46qwmG47nXEM5ca5prkBSa1Jxcu7W3BNCWtvqQJO4jXj4MGsjU5ZWULBrdIfWYUw_fKsXdhN2pPiuePXzT1unlK8NXR8sUOriuoAx8/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpKdwtqY4SHEZPmGHrh8_33bkPg8jIdpS7-yLdhX46qwmG47nXEM5ca5prkBSa1Jxcu7W3BNCWtvqQJO4jXj4MGsjU5ZWULBrdIfWYUw_fKsXdhN2pPiuePXzT1unlK8NXR8sUOriuoAx8/s320/6.png" width="320" /></a></div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
Now go to your report's page, add the table visual, select the columns you want to show and apply some styling... it should look similar to the following table:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxN_kavYlZNanV3ihNDYHsvRRlvjGUFrxWQTHpV0oOIWVgc7Zw91M8mbmWFQv8QFtvyvhhy0ctNX-WKXncdfvyv_Il85ArLtINHGNSZ-U_T-NcxC1zImmHOayylmlob4LOL-7AQ8I3EgYA/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="348" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxN_kavYlZNanV3ihNDYHsvRRlvjGUFrxWQTHpV0oOIWVgc7Zw91M8mbmWFQv8QFtvyvhhy0ctNX-WKXncdfvyv_Il85ArLtINHGNSZ-U_T-NcxC1zImmHOayylmlob4LOL-7AQ8I3EgYA/s400/7.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
Hope it helps!!!</div>
RodCoderhttp://www.blogger.com/profile/08206822216774270650noreply@blogger.com4tag:blogger.com,1999:blog-8236291434363530568.post-69405475088253048302016-05-16T11:51:00.000-07:002016-05-16T11:51:07.802-07:00Yammer Analytics: Getting all the relevant information from your company's social network<span style="font-family: Courier New, Courier, monospace;">Hi guys :)</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Today I've decided to show you how to put together all the relevant information from Yammer, using the export API (/export) and a few complementary API calls.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<b><span style="font-family: Courier New, Courier, monospace;">The scenario:</span></b><br />
<span style="font-family: Courier New, Courier, monospace;">Ideally, this would be an Azure web job running each day, pulling information from Yammer and storing it into an Azure Database. After the information being stored, we will use some Power BI magic to show the data in a cool interactive way.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Let's start with the <a href="https://developer.yammer.com/docs/data-export-api" target="_blank">export API</a>, by creating a method to perform the call and retrieve the csv files in a zipped folder.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">ZipArchive</span> GetExport(<span style="color: #4ec9b0;">DateTime</span> exportStartDate)
{
<span style="color: #569cd6;">var</span> url <span style="color: #b4b4b4;">=</span> <span style="color: #4ec9b0;">String</span><span style="color: #b4b4b4;">.</span>Format(<span style="color: #d69d85;">"https://www.yammer.com/api/v1/export?model=Message&model=User&model=Group&model=Topic&model=UploadedFileVersion&model=DocumentVersion&access_token={0}&include=csv&include_ens=false&since="</span> <span style="color: #b4b4b4;">+</span> exportStartDate<span style="color: #b4b4b4;">.</span>ToString(<span style="color: #d69d85;">"yyyy-MM-dd"</span>) <span style="color: #b4b4b4;">+</span> <span style="color: #d69d85;">"T00%3A00%3A00%2B00%3A00"</span>, <span style="color: #569cd6;">this</span><span style="color: #b4b4b4;">.</span>accessToken);
<span style="color: #4ec9b0;">HttpWebRequest</span> request <span style="color: #b4b4b4;">=</span> <span style="color: #4ec9b0;">WebRequest</span><span style="color: #b4b4b4;">.</span>Create(url) <span style="color: #569cd6;">as</span> <span style="color: #4ec9b0;">HttpWebRequest</span>;
request<span style="color: #b4b4b4;">.</span>Headers<span style="color: #b4b4b4;">.</span>Add(<span style="color: #d69d85;">"Authorization"</span>, <span style="color: #d69d85;">"Bearer"</span> <span style="color: #b4b4b4;">+</span> <span style="color: #d69d85;">" "</span> <span style="color: #b4b4b4;">+</span> <span style="color: #569cd6;">this</span><span style="color: #b4b4b4;">.</span>accessToken);
<span style="color: #569cd6;">using</span> (<span style="color: #4ec9b0;">HttpWebResponse</span> response <span style="color: #b4b4b4;">=</span> request<span style="color: #b4b4b4;">.</span>GetResponse() <span style="color: #569cd6;">as</span> <span style="color: #4ec9b0;">HttpWebResponse</span>)
{
<span style="color: #569cd6;">using</span> (<span style="color: #569cd6;">var</span> stream <span style="color: #b4b4b4;">=</span> response<span style="color: #b4b4b4;">.</span>GetResponseStream())
{
<span style="color: #569cd6;">return</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">ZipArchive</span>(stream);
}
}
}</span></pre>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Notes:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<ul>
<li><span style="font-family: Courier New, Courier, monospace;">Since we're going to run the job everyday, the exportStartDate will be DateTime.Now.AddDays(-1);</span></li>
<li><span style="font-family: Courier New, Courier, monospace;">The access token will be defined in the App.config file, as shown below</span></li>
</ul>
<div>
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: grey;"><</span><span style="color: #569cd6;">appSettings</span><span style="color: grey;">></span>
<span style="color: grey;"> <</span><span style="color: #569cd6;">add</span><span style="color: grey;"> </span><span style="color: #92caf4;">key</span><span style="color: grey;">=</span><span style="color: grey;">"</span><span style="color: #c8c8c8;">Token</span><span style="color: grey;">"</span><span style="color: grey;"> </span><span style="color: #92caf4;">value</span><span style="color: grey;">=</span><span style="color: grey;">"</span><span style="color: #c8c8c8;">19094-23I3k4uZtdgXXXXXXXXXX</span><span style="color: grey;">"</span><span style="color: grey;"> /></span>
<span style="color: grey;"></</span><span style="color: #569cd6;">appSettings</span><span style="color: grey;">></span></span></pre>
</div>
<span style="font-family: Courier New, Courier, monospace;"><span style="color: #990000;"><br /></span>
<span style="color: #990000;">In order not to make this post too big, I will only demonstrate how to process messages, which is the "messages.csv" file entry within the export zip folder.</span></span><br />
<span style="font-family: Courier New, Courier, monospace;"><span style="color: #990000;"><br /></span>
So here's how our code will start:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #57a64a;">//Date to export from</span>
<span style="color: #4ec9b0;">DateTime</span> exportSince <span style="color: #b4b4b4;">=</span> <span style="color: #4ec9b0;">DateTime</span><span style="color: #b4b4b4;">.</span>Now<span style="color: #b4b4b4;">.</span>AddDays(<span style="color: #b4b4b4;">-</span><span style="color: #b5cea8;">1</span>);
<span style="color: #57a64a;">//Get util methods and classes</span>
<span style="color: #4ec9b0;">YammerUtil</span> util <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">YammerUtil</span>(token, permalink);
<span style="color: #4ec9b0;">ZipArchive</span> zip <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">null</span>;
<span style="color: #57a64a;">//DB Context</span>
<span style="color: #4ec9b0;">AzureContext</span> dbContext <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">AzureContext</span>();
<span style="color: #569cd6;">try</span>
{
zip <span style="color: #b4b4b4;">=</span> util<span style="color: #b4b4b4;">.</span>GetExport(exportSince);
<span style="color: #57a64a;">//Proceed only if there's data to import</span>
<span style="color: #569cd6;">if</span> (zip <span style="color: #b4b4b4;">!=</span> <span style="color: #569cd6;">null</span>)
{
<span style="color: #57a64a;">//Zip archive entries</span>
<span style="color: #4ec9b0;">ZipArchiveEntry</span> msgsCSV <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">null</span>;
<span style="color: #569cd6;">foreach</span> (<span style="color: #4ec9b0;">ZipArchiveEntry</span> entry <span style="color: #569cd6;">in</span> zip<span style="color: #b4b4b4;">.</span>Entries)
{
<span style="color: #569cd6;">if</span> (entry<span style="color: #b4b4b4;">.</span>Name<span style="color: #b4b4b4;">.</span>Equals(<span style="color: #d69d85;">"Messages.csv"</span>, <span style="color: #b8d7a3;">StringComparison</span><span style="color: #b4b4b4;">.</span>CurrentCultureIgnoreCase))
msgsCSV <span style="color: #b4b4b4;">=</span> entry;
}</span></pre>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Some of the code above was already explained, so I'm going to detail the rest of it:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<ul>
<li><span style="font-family: Courier New, Courier, monospace;">YammerUtil is the class where I've created some general methods (like the export one)</span></li>
<li><span style="font-family: Courier New, Courier, monospace;">AzureContext is my database context to use Entity Framework with the Azure DB.</span></li>
<li><span style="font-family: Courier New, Courier, monospace;">We will declare a ZipArchiveEntry object to get the "messages.csv" file entry, by iterating through all the entries inside the retrieved zip folder.</span></li>
</ul>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Now, considering that we've successfully retrieved our messages, let's declare the lists and methods' classes. These methods will help us to store our objects in Azure and also to perform some complementary calls to retrieve some relevant information.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #569cd6;">if</span> (msgsCSV <span style="color: #b4b4b4;">!=</span> <span style="color: #569cd6;">null</span>)
{
<span style="color: #57a64a;">//Lists</span>
<span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Message</span>> messages <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Message</span>>();
<span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Like</span>> likes <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Like</span>>();
<span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Share</span>> shares <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Share</span>>();
<span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Mention</span>> userMentions <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Mention</span>>();
<span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Mention</span>> tagMentions <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Mention</span>>();
<span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Praise</span>> praises <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Praise</span>>();
<span style="color: #4ec9b0;">List</span><<span style="color: #569cd6;">string</span>> threadIDs <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">List</span><<span style="color: #569cd6;">string</span>>();
<span style="color: #57a64a;">//Methods</span>
<span style="color: #4ec9b0;">MessageMethods</span> messageMethods <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">MessageMethods</span>(util, dbContext);
<span style="color: #4ec9b0;">MentionMethods</span> mentionMethods <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">MentionMethods</span>(util, dbContext);
<span style="color: #4ec9b0;">LikeMethods</span> likeMethods <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">LikeMethods</span>(util, dbContext);
<span style="color: #4ec9b0;">PraiseMethods</span> praiseMethods <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">PraiseMethods</span>(util, dbContext);
<span style="color: #4ec9b0;">ShareMethods</span> shareMethods <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">ShareMethods</span>(util, dbContext);</span></pre>
</div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">From "messages.csv" we can automatically get values like "message_id", "replied_to_id", "group_id", "user_id", etc. Although, we might need some more information, such as, how many likes does this message have? how many shares? Does it mention or praise any user? Does it contain any relevant tags?</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">In order to retrieve all this information, complementary calls have to be made.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Let's see some methods...</span><br />
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><span style="font-family: Courier New, Courier, monospace;">messageMethods<span style="color: #b4b4b4;">.</span>GetMessageDetails(message);
</span></pre>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">We will need the message attachments, so let's start by appending the attachments to the current message object:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">Message</span> GetMessageDetails(<span style="color: #4ec9b0;">Message</span> msg)
{
<span style="color: #569cd6;">try</span>
{
<span style="color: #569cd6;">var</span> url <span style="color: #b4b4b4;">=</span> <span style="color: #4ec9b0;">String</span><span style="color: #b4b4b4;">.</span>Format(<span style="color: #d69d85;">"https://www.yammer.com/api/v1/messages/{0}.json?access_token={1}"</span>, msg<span style="color: #b4b4b4;">.</span>id<span style="color: #b4b4b4;">.</span>ToString(), util<span style="color: #b4b4b4;">.</span>accessToken);
<span style="color: #569cd6;">var</span> json <span style="color: #b4b4b4;">=</span> util<span style="color: #b4b4b4;">.</span>GetYammerJson(url);
<span style="color: #4ec9b0;">Message</span> apiStats <span style="color: #b4b4b4;">=</span> <span style="color: #4ec9b0;">JsonConvert</span><span style="color: #b4b4b4;">.</span>DeserializeObject<<span style="color: #4ec9b0;">Message</span>>(json);
<span style="color: #569cd6;">if</span> (apiStats <span style="color: #b4b4b4;">!=</span> <span style="color: #569cd6;">null</span> <span style="color: #b4b4b4;">&&</span> apiStats<span style="color: #b4b4b4;">.</span>attachments <span style="color: #b4b4b4;">!=</span> <span style="color: #569cd6;">null</span>)
msg<span style="color: #b4b4b4;">.</span>attachments <span style="color: #b4b4b4;">=</span> apiStats<span style="color: #b4b4b4;">.</span>attachments;
}
<span style="color: #569cd6;">catch</span> (<span style="color: #4ec9b0;">Exception</span> ex)
{
<span style="color: #4ec9b0;">Console</span><span style="color: #b4b4b4;">.</span>WriteLine(<span style="color: #d69d85;">"Message ID: "</span> <span style="color: #b4b4b4;">+</span> msg<span style="color: #b4b4b4;">.</span>id <span style="color: #b4b4b4;">+</span> <span style="color: #d69d85;">". Error getting message details: "</span> <span style="color: #b4b4b4;">+</span> ex<span style="color: #b4b4b4;">.</span>Message);
}
<span style="color: #569cd6;">return</span> msg;
}</span></pre>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Once done, we're going to process our messages:</span><br />
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #57a64a;">//process messages</span>
util<span style="color: #b4b4b4;">.</span>ProcessMessages(messageMethods, messages, likes, shares, userMentions, tagMentions, praises);</span></pre>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Here's the code...</span><br />
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">void</span> ProcessMessages(<span style="color: #4ec9b0;">MessageMethods</span> messageMethods, <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Message</span>> messages, <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Like</span>> likes, <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Share</span>> shares, <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Mention</span>> userMentions, <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Mention</span>> tagMentions, <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Praise</span>> praises)
{
<span style="color: #57a64a;">//do processing based on processing selections</span>
<span style="color: #569cd6;">foreach</span>(<span style="color: #569cd6;">var</span> message <span style="color: #569cd6;">in</span> messages)
{
<span style="color: #569cd6;">try</span>
{
<span style="color: #57a64a;">//get praises </span>
<span style="color: #569cd6;">if</span> (message<span style="color: #b4b4b4;">.</span>attachments <span style="color: #b4b4b4;">!=</span> <span style="color: #569cd6;">null</span>)
{
<span style="color: #569cd6;">foreach</span> (<span style="color: #4ec9b0;">attachment</span> attch <span style="color: #569cd6;">in</span> message<span style="color: #b4b4b4;">.</span>attachments)
{
<span style="color: #569cd6;">if</span> (attch<span style="color: #b4b4b4;">.</span>type<span style="color: #b4b4b4;">.</span>Equals(<span style="color: #d69d85;">"praise"</span>))
praises<span style="color: #b4b4b4;">.</span>AddRange(GetUserPraises(attch,message));
}
}
<span style="color: #57a64a;">//get mentions</span>
userMentions<span style="color: #b4b4b4;">.</span>AddRange(GetUserMentions(message<span style="color: #b4b4b4;">.</span>bodyText, message<span style="color: #b4b4b4;">.</span>id));
tagMentions<span style="color: #b4b4b4;">.</span>AddRange(GetTagMentions(message<span style="color: #b4b4b4;">.</span>bodyText, message<span style="color: #b4b4b4;">.</span>id));
<span style="color: #57a64a;">//get likes</span>
likes<span style="color: #b4b4b4;">.</span>AddRange(messageMethods<span style="color: #b4b4b4;">.</span>GetMessageLikes(message<span style="color: #b4b4b4;">.</span>id<span style="color: #b4b4b4;">.</span>ToString())<span style="color: #b4b4b4;">.</span>Select(x <span style="color: #b4b4b4;">=></span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">Like</span>() { messageId <span style="color: #b4b4b4;">=</span> message<span style="color: #b4b4b4;">.</span>id, userId <span style="color: #b4b4b4;">=</span> x<span style="color: #b4b4b4;">.</span>id }));
<span style="color: #57a64a;">//get shares</span>
shares<span style="color: #b4b4b4;">.</span>AddRange(messageMethods<span style="color: #b4b4b4;">.</span>GetMessageShares(message<span style="color: #b4b4b4;">.</span>id<span style="color: #b4b4b4;">.</span>ToString())<span style="color: #b4b4b4;">.</span>Select(x <span style="color: #b4b4b4;">=></span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">Share</span>() { messageId <span style="color: #b4b4b4;">=</span> message<span style="color: #b4b4b4;">.</span>id, userId <span style="color: #b4b4b4;">=</span> x<span style="color: #b4b4b4;">.</span>sender_id, created_at <span style="color: #b4b4b4;">=</span> x<span style="color: #b4b4b4;">.</span>created_at }));
}
<span style="color: #569cd6;">catch</span> (<span style="color: #4ec9b0;">Exception</span>)
{
<span style="color: #57a64a;">//ignored in the blog's post</span>
}
}
}</span></pre>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">... and here's the remaining code for some of the called functions
</span><br />
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Praise</span>> GetUserPraises(<span style="color: #4ec9b0;">attachment</span> attachment,<span style="color: #4ec9b0;">Message</span> msg)
{
<span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Praise</span>> praisedUsers <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Praise</span>>();
<span style="color: #569cd6;">if</span> (attachment<span style="color: #b4b4b4;">.</span>praised_user_ids <span style="color: #b4b4b4;">!=</span> <span style="color: #569cd6;">null</span>)
{
<span style="color: #569cd6;">foreach</span>(<span style="color: #569cd6;">int</span> userId <span style="color: #569cd6;">in</span> attachment<span style="color: #b4b4b4;">.</span>praised_user_ids)
{
<span style="color: #4ec9b0;">Praise</span> praise <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">Praise</span>
{
praisedUserId <span style="color: #b4b4b4;">=</span> userId,
praisorUserId <span style="color: #b4b4b4;">=</span> <span style="color: #4ec9b0;">Convert</span><span style="color: #b4b4b4;">.</span>ToInt32(msg<span style="color: #b4b4b4;">.</span>user_id),
description <span style="color: #b4b4b4;">=</span> attachment<span style="color: #b4b4b4;">.</span>description,
icon <span style="color: #b4b4b4;">=</span> attachment<span style="color: #b4b4b4;">.</span>icon,
praised_at <span style="color: #b4b4b4;">=</span> msg<span style="color: #b4b4b4;">.</span>created_at
};
praisedUsers<span style="color: #b4b4b4;">.</span>Add(praise);
}
}
<span style="color: #569cd6;">return</span> praisedUsers;
}
<span style="color: #569cd6;">public</span> <span style="color: #569cd6;">static</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Mention</span>> GetUserMentions(<span style="color: #569cd6;">string</span> body, <span style="color: #569cd6;">int</span> msgId)
{
<span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Mention</span>> userMentions <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">List</span><<span style="color: #4ec9b0;">Mention</span>>();
<span style="color: #569cd6;">if</span> (<span style="color: #b4b4b4;">!</span><span style="color: #4ec9b0;">String</span><span style="color: #b4b4b4;">.</span>IsNullOrEmpty(body))
{
<span style="color: #569cd6;">if</span> (<span style="color: #b4b4b4;">!</span><span style="color: #4ec9b0;">String</span><span style="color: #b4b4b4;">.</span>IsNullOrEmpty(body))
{
<span style="color: #57a64a;">//check for user mentions</span>
<span style="color: #569cd6;">var</span> body2 <span style="color: #b4b4b4;">=</span> body;
<span style="color: #569cd6;">while</span> (body2<span style="color: #b4b4b4;">.</span>IndexOf(<span style="color: #d69d85;">"[User:"</span>) <span style="color: #b4b4b4;">!=</span> <span style="color: #b4b4b4;">-</span><span style="color: #b5cea8;">1</span>)
{
<span style="color: #569cd6;">var</span> m <span style="color: #b4b4b4;">=</span> body2<span style="color: #b4b4b4;">.</span>Substring(body2<span style="color: #b4b4b4;">.</span>IndexOf(<span style="color: #d69d85;">"[User:"</span>));
m <span style="color: #b4b4b4;">=</span> m<span style="color: #b4b4b4;">.</span>Substring(<span style="color: #b5cea8;">0</span>, m<span style="color: #b4b4b4;">.</span>IndexOf(<span style="color: #d69d85;">"]"</span>) <span style="color: #b4b4b4;">+</span> <span style="color: #b5cea8;">1</span>);
<span style="color: #569cd6;">if</span> (m<span style="color: #b4b4b4;">.</span>Length <span style="color: #b4b4b4;">==</span> <span style="color: #b5cea8;">0</span>)
body2 <span style="color: #b4b4b4;">=</span> <span style="color: #d69d85;">""</span>;
<span style="color: #569cd6;">else</span>
{
<span style="color: #569cd6;">var</span> id <span style="color: #b4b4b4;">=</span> m<span style="color: #b4b4b4;">.</span>Substring(<span style="color: #b5cea8;">6</span>);
id <span style="color: #b4b4b4;">=</span> id<span style="color: #b4b4b4;">.</span>Substring(<span style="color: #b5cea8;">0</span>, id<span style="color: #b4b4b4;">.</span>IndexOf(<span style="color: #d69d85;">':'</span>));
<span style="color: #569cd6;">var</span> mention <span style="color: #b4b4b4;">=</span> m<span style="color: #b4b4b4;">.</span>Substring(m<span style="color: #b4b4b4;">.</span>IndexOf(id) <span style="color: #b4b4b4;">+</span> id<span style="color: #b4b4b4;">.</span>Length <span style="color: #b4b4b4;">+</span> <span style="color: #b5cea8;">1</span>);
mention <span style="color: #b4b4b4;">=</span> mention<span style="color: #b4b4b4;">.</span>Substring(<span style="color: #b5cea8;">0</span>, mention<span style="color: #b4b4b4;">.</span>Length <span style="color: #b4b4b4;">-</span> <span style="color: #b5cea8;">1</span>);
body2 <span style="color: #b4b4b4;">=</span> body2<span style="color: #b4b4b4;">.</span>Substring(body2<span style="color: #b4b4b4;">.</span>IndexOf(m) <span style="color: #b4b4b4;">+</span> m<span style="color: #b4b4b4;">.</span>Length);
userMentions<span style="color: #b4b4b4;">.</span>Add(<span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">Mention</span>() { messageId <span style="color: #b4b4b4;">=</span> msgId<span style="color: #b4b4b4;">.</span>ToString(), mentionId <span style="color: #b4b4b4;">=</span> id, mentionName <span style="color: #b4b4b4;">=</span> mention });
}
}
}
}
<span style="color: #569cd6;">return</span> userMentions;
}</span></pre>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">And finally, we add/update our objects in the database:
</span><br />
<pre style="background: rgb(30, 30, 30); color: gainsboro; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #57a64a;">//Update messages in DB</span>
<span style="color: #569cd6;">foreach</span> (<span style="color: #4ec9b0;">Message</span> msg <span style="color: #569cd6;">in</span> messages)
{
messageMethods<span style="color: #b4b4b4;">.</span>UpdateMessage(msg);
}</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: #57a64a; font-family: Courier New, Courier, monospace;">//Here's the method (Using EF)</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">bool</span> UpdateMessage(<span style="color: #4ec9b0;">Message</span> message)
{
<span style="color: #4ec9b0;">Message</span> target <span style="color: #b4b4b4;">=</span> dbContext<span style="color: #b4b4b4;">.</span>Messages<span style="color: #b4b4b4;">.</span>Where(entity <span style="color: #b4b4b4;">=></span> entity<span style="color: #b4b4b4;">.</span>id <span style="color: #b4b4b4;">==</span> message<span style="color: #b4b4b4;">.</span>id)<span style="color: #b4b4b4;">.</span>AsQueryable()<span style="color: #b4b4b4;">.</span>FirstOrDefault();
<span style="color: #569cd6;">if</span> (target <span style="color: #b4b4b4;">==</span> <span style="color: #569cd6;">null</span>)
{
CreateMessage(message);
}
<span style="color: #569cd6;">else</span>
{
dbContext<span style="color: #b4b4b4;">.</span>Entry(target)<span style="color: #b4b4b4;">.</span>CurrentValues<span style="color: #b4b4b4;">.</span>SetValues(message);
}
<span style="color: #569cd6;">return</span> dbContext<span style="color: #b4b4b4;">.</span>SaveChanges() <span style="color: #b4b4b4;">></span> <span style="color: #b5cea8;">0</span>;
}</span></pre>
<span style="font-family: Courier New, Courier, monospace;">
</span></pre>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">After updating our data, we can then create some Power BI reports and add individual components to our Dashboards, to create something like the dashboard below:
</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh77lA38GxDbKTOCjh7WvylL8QC5Oa5zSrN2BbVgPSjlIxI9WebjtjKwo4GGAKIFqvGVgAHAVK0KGUXe3FtWRsr29pYOJBL9_5WE6CWG_9_YgY7bc6G3WIyIIaaIW9WfIxAjSOVJfJOhzmx/s1600/yammerdashboard.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: Courier New, Courier, monospace;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh77lA38GxDbKTOCjh7WvylL8QC5Oa5zSrN2BbVgPSjlIxI9WebjtjKwo4GGAKIFqvGVgAHAVK0KGUXe3FtWRsr29pYOJBL9_5WE6CWG_9_YgY7bc6G3WIyIIaaIW9WfIxAjSOVJfJOhzmx/s1600/yammerdashboard.png" /></span></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Courier New, Courier, monospace;">Notes:</span></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<ul>
<li><span style="font-family: Courier New, Courier, monospace;">Some information had to be omitted, as this is a solution in production.</span></li>
<li><span style="font-family: Courier New, Courier, monospace;">Most part of the code was based on Richard diZerega's <a href="https://blogs.msdn.microsoft.com/richard_dizeregas_blog/2014/04/09/yammer-analytics-with-excel-and-power-bi/" target="_blank">Yammer Analytics post</a>.</span></li>
</ul>
<div>
<span style="font-family: Courier New, Courier, monospace;">Let me know if you have any doubts... happy coding =)</span></div>
RodCoderhttp://www.blogger.com/profile/08206822216774270650noreply@blogger.com1tag:blogger.com,1999:blog-8236291434363530568.post-44134804219344713622015-11-04T08:38:00.000-08:002015-11-04T08:38:34.344-08:00_api/me/getrecentdocs stopped working! How can I replace it?<span style="font-family: Courier New, Courier, monospace;">Hi there,</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">So, as it seems, one of our favourite endpoints stopped working. This morning I had a call from one of our clients saying they were unable to view their recent documents in the homepage.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">I did a quick investigation and I faced this strange error when trying to call my-site.sharepoint.com/_api/me/getrecentdocs:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWb2hytjnLEEnMb2uX6RnH8iIqJqr84X5vS5jhGK2_r9HznCf2QxGyQduzVdoQuvdOpGdN1mlbe0yQV5Pp3W5_UOwxnWPdUQQnUjiCIjzwJEH-tzbn2BL_b6ZX8E3HNZh8fPNPyVdOobSs/s1600/error.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><span style="font-family: Courier New, Courier, monospace;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWb2hytjnLEEnMb2uX6RnH8iIqJqr84X5vS5jhGK2_r9HznCf2QxGyQduzVdoQuvdOpGdN1mlbe0yQV5Pp3W5_UOwxnWPdUQQnUjiCIjzwJEH-tzbn2BL_b6ZX8E3HNZh8fPNPyVdOobSs/s1600/error.png" /></span></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">So I started to read about this and it seems that this API stopped working a few days ago (guess nobody noticed before!?). Waiting for a (possible) fix was not an option, so I started to develop a workaround for this.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Here's the code of our previous solution:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="font-family: Courier New, Courier, monospace;"> <span style="color: #008800; font-weight: bold;">var</span> uri <span style="color: #333333;">=</span> <span style="color: #007020;">String</span>.format(<span style="background-color: #fff0f0;">"{0}/_api/me/getrecentdocs/?$top={1}&$filter=startswith(LinkLocation, '{2}')&$select=FileName,LinkLocation,Application"</span>, serverUrl, rowLimit, <span style="color: #007020;">encodeURIComponent</span>(belowWebUrl));
<span style="color: #888888;">// Query</span>
jQuery.ajax({
url<span style="color: #333333;">:</span> uri,
dataType<span style="color: #333333;">:</span> <span style="background-color: #fff0f0;">"json"</span>,
type<span style="color: #333333;">:</span> <span style="background-color: #fff0f0;">"GET"</span>,
headers<span style="color: #333333;">:</span> { <span style="background-color: #fff0f0;">"ACCEPT"</span><span style="color: #333333;">:</span> <span style="background-color: #fff0f0;">"application/json;odata=nometadata"</span> }
})</span></pre>
</div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">When the request was completed (.done), we were getting the array of object like this:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="font-family: Courier New, Courier, monospace;"> <span style="color: #888888;">// Render results</span>
<span style="color: #008800; font-weight: bold;">var</span> arrayOfDocuments <span style="color: #333333;">=</span> result.value;
</span></pre>
</div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">For each object within the array (d), it was simple to get property values, using "d.PropertyName". Ex: d.FileName</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">This solution was really simple, but since it isn't working any more, let's jump into the workaround!</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<b><span style="font-family: Courier New, Courier, monospace;">Solution (using the search api):</span></b><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Let's build our request url, passing the base Uri, query text, query template, selected properties, row limit and sort it descending, using the last modified date (getting the most recent on top, to achieve the same result).</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="font-family: Courier New, Courier, monospace;"> <span style="color: #008800; font-weight: bold;">var</span> baseUri <span style="color: #333333;">=</span> serverUrl <span style="color: #333333;">+</span> <span style="background-color: #fff0f0;">"/_api/search/query"</span>;
<span style="color: #008800; font-weight: bold;">var</span> queryText <span style="color: #333333;">=</span> <span style="background-color: #fff0f0;">"querytext='*'"</span>;
<span style="color: #008800; font-weight: bold;">var</span> queryTemplate <span style="color: #333333;">=</span> <span style="background-color: #fff0f0;">"querytemplate='(AuthorOwsUser:{User.AccountName} OR EditorOwsUser:{User.AccountName}) AND ContentType:Document AND IsDocument:1 AND -Title:OneNote_DeletedPages AND -Title:OneNote_RecycleBin NOT(FileExtension:mht OR FileExtension:aspx OR FileExtension:html OR FileExtension:htm)'"</span>;
<span style="color: #008800; font-weight: bold;">var</span> selectProps <span style="color: #333333;">=</span> <span style="background-color: #fff0f0;">"selectproperties='Path,Filename,SPWebUrl'"</span>;
<span style="color: #008800; font-weight: bold;">var</span> uri <span style="color: #333333;">=</span> baseUri <span style="color: #333333;">+</span> <span style="background-color: #fff0f0;">"?"</span> <span style="color: #333333;">+</span> queryText <span style="color: #333333;">+</span> <span style="background-color: #fff0f0;">"&"</span> <span style="color: #333333;">+</span> queryTemplate <span style="color: #333333;">+</span> <span style="background-color: #fff0f0;">"&rowlimit="</span> <span style="color: #333333;">+</span> rowLimit.toString() <span style="color: #333333;">+</span> <span style="background-color: #fff0f0;">"&bypassresulttypes=false&"</span> <span style="color: #333333;">+</span> selectProps <span style="color: #333333;">+</span> <span style="background-color: #fff0f0;">"&sortlist='LastModifiedTime:descending'&enablesorting=true"</span>;
</span></pre>
<pre style="line-height: 125%; margin: 0;"><span style="font-family: Courier New, Courier, monospace;"><span style="color: #888888;"> // Query</span>
jQuery.ajax({
url<span style="color: #333333;">:</span> uri,
dataType<span style="color: #333333;">:</span> <span style="background-color: #fff0f0;">"json"</span>,
type<span style="color: #333333;">:</span> <span style="background-color: #fff0f0;">"GET"</span>,
headers<span style="color: #333333;">:</span> { <span style="background-color: #fff0f0;">"ACCEPT"</span><span style="color: #333333;">:</span> <span style="background-color: #fff0f0;">"application/json;odata=nometadata"</span> }
})
</span></pre>
</div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Now, the way we are going to get our array will be a bit different:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="font-family: Courier New, Courier, monospace;"> <span style="color: #008800; font-weight: bold;">var</span> arrayOfDocuments <span style="color: #333333;">=</span> result.PrimaryQueryResult.RelevantResults.Table.Rows;
</span></pre>
</div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">In order to have the same easiness on accessing the object's properties (d.FileName), we will have to simplify the data structure, like this:
</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="font-family: Courier New, Courier, monospace;"> <span style="color: #888888;">// Simplify the data strucutre</span>
<span style="color: #008800; font-weight: bold;">var</span> arrayOfDocumentsSimplified <span style="color: #333333;">=</span> [];
_.each(arrayOfDocuments, <span style="color: #008800; font-weight: bold;">function</span> (d) {
<span style="color: #008800; font-weight: bold;">var</span> doc <span style="color: #333333;">=</span> {};
_.each(d.Cells, <span style="color: #008800; font-weight: bold;">function</span> (c) {
doc[c.Key] <span style="color: #333333;">=</span> c.Value;
});
arrayOfDocumentsSimplified.push(doc);
});
</span></pre>
</div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">And that's it! Our most recent documents are showing up again :)</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig-UA-KRUUWPA_e5EnhgomJBmmnGchxMpG4A3_qVMqrnK816bd3wbK_FJdAKNtkA5nMaMpLLqzJeDovDkJX6m37gaauBEJpoPP7MynrhkEb5EHGACnExoddLxGWMsCx0i1586B_Np8u6oT/s1600/docs.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><span style="font-family: Courier New, Courier, monospace;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig-UA-KRUUWPA_e5EnhgomJBmmnGchxMpG4A3_qVMqrnK816bd3wbK_FJdAKNtkA5nMaMpLLqzJeDovDkJX6m37gaauBEJpoPP7MynrhkEb5EHGACnExoddLxGWMsCx0i1586B_Np8u6oT/s320/docs.png" width="238" /></span></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Hope you find it useful!</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">RS</span>RodCoderhttp://www.blogger.com/profile/08206822216774270650noreply@blogger.com1tag:blogger.com,1999:blog-8236291434363530568.post-6540401079047993402015-08-18T04:06:00.000-07:002015-08-18T04:06:00.364-07:00Query office graph in Office Add-in using SharePoint search<div style="text-align: left;">
<span style="font-family: Courier New, Courier, monospace;">I've been playing around with Office add-ins lately, so I decided to write a post about it.</span></div>
<div style="text-align: left;">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div style="text-align: left;">
<span style="font-family: Courier New, Courier, monospace;">In this example we are going to login to SharePoint using Office 365 APIs, as described by Richard diZerega in <a href="http://blogs.msdn.com/b/richard_dizeregas_blog/archive/2015/01/27/connecting-to-sharepoint-from-an-office-app.aspx" target="_blank">this</a> blog post.</span></div>
<div style="text-align: left;">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div style="text-align: left;">
<span style="font-family: Courier New, Courier, monospace;">To authenticate, we will perform a manual OAuth flow, which was really well described by Chaks <a href="http://chakkaradeep.com/index.php/working-with-office365apis-the-raw-version/" target="_blank">here</a>.</span></div>
<div style="text-align: left;">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div style="text-align: left;">
<span style="font-family: Courier New, Courier, monospace;">Transcribing Richard's description on the authentication process:</span></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<span style="font-family: Georgia, Times New Roman, serif;">"<span style="background-color: white; color: #333333; font-size: 12.1104001998901px; line-height: 18.165599822998px;">Here are the high-level steps for this flow and cross-window communication:</span></span></div>
<ol style="background-color: white; color: #333333; font-size: 12.1104001998901px; line-height: 18.165599822998px;">
<li style="text-align: left;"><span style="font-family: Georgia, Times New Roman, serif;">Check for a user cookie (which maps to a refresh token in a database)</span></li>
<li style="text-align: left;"><span style="font-family: Georgia, Times New Roman, serif;">If the user doesn't have a cookie…generate a new GUID and store as cookie</span></li>
<li style="text-align: left;"><span style="font-family: Georgia, Times New Roman, serif;">Launch the OAuth flow with Azure AD in a new window (passing the GUID as reference)</span></li>
<li style="text-align: left;"><span style="font-family: Georgia, Times New Roman, serif;">Use the authorization code returned from the OAuth flow to get access and refresh token</span></li>
<li style="text-align: left;"><span style="font-family: Georgia, Times New Roman, serif;">Store the refresh token in the database with the GUID user reference</span></li>
<li style="text-align: left;"><span style="font-family: Georgia, Times New Roman, serif;">Prompt the user to refresh the Office app (which can now lookup the refresh token by the GUID user reference that is stored in a cookie)</span></li>
<li style="text-align: left;"><span style="font-family: Georgia, Times New Roman, serif;">Use the refresh token in the app to get resource-specific access tokens for data retrieval<span style="background-color: transparent;">"</span></span></li>
</ol>
<div style="text-align: left;">
<span style="font-family: 'Courier New', Courier, monospace;">Here is the script that is launched for unknown users:</span></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVC_LBL_qK-O4iD8kAmddII0f_sq6Xf2gjhSxq1bVP8qQ56jCzhUK30GS_2tQ6y1aM0PTwHWcOLJDMyIVRsXncq8f1dH8VWC4eZlVqonf7oVPrSHbebiexiDJP1WTH6nJ_I1dk_7am84UE/s1600/01-ScriptForUnknown.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVC_LBL_qK-O4iD8kAmddII0f_sq6Xf2gjhSxq1bVP8qQ56jCzhUK30GS_2tQ6y1aM0PTwHWcOLJDMyIVRsXncq8f1dH8VWC4eZlVqonf7oVPrSHbebiexiDJP1WTH6nJ_I1dk_7am84UE/s1600/01-ScriptForUnknown.png" /></a></div>
<br />
<span style="font-family: Courier New, Courier, monospace;">And here is the OAuthController for managing the authorization code response from Azure AD: </span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBamLyqgR8gSnXYQYjYOoxLmT9UwHSmvYb6dw3S-fjIu4RaCM8XEuApXyuT5TjYjE4mtRj8BZznQkv9WHLG7Ff0ZPktq8r3dm0pQLLEEUNQowHMH8oVhloLaEwiWLj__oJb38BNAxRkquE/s1600/02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBamLyqgR8gSnXYQYjYOoxLmT9UwHSmvYb6dw3S-fjIu4RaCM8XEuApXyuT5TjYjE4mtRj8BZznQkv9WHLG7Ff0ZPktq8r3dm0pQLLEEUNQowHMH8oVhloLaEwiWLj__oJb38BNAxRkquE/s1600/02.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">To get the access token using the code retrieved by the authentication flow, the following code will be executed:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirrb0VKvtAg-KHi2Dn-FS4wd0QeBoX7sPTWc18tfto5XjgFa-C6K26lJThyF-zvpiOfwupZEfvxae-d6HbKWRvmirejhe7cBYZbOm2AFcLrGvd_rgnZPbzxfe2nk8zPN_QrhN03q9TbaXf/s1600/03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirrb0VKvtAg-KHi2Dn-FS4wd0QeBoX7sPTWc18tfto5XjgFa-C6K26lJThyF-zvpiOfwupZEfvxae-d6HbKWRvmirejhe7cBYZbOm2AFcLrGvd_rgnZPbzxfe2nk8zPN_QrhN03q9TbaXf/s1600/03.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">All the values must be defined in the web.config, as we're retrieving them using our 'SettingsHelper' class:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9PC6cwRKiuKBuxwNmHPLm_BhFs5ih-z39nnkySDaBmEU0fRbgEJURgGSqAEPb5m-VJg5rT-XmJtwi8FCv1pwrcxNLTwNqdjrZqpZXRTghmuOoPiJvN3jVuuTYsV-06PomAMLXT080kmIE/s1600/04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9PC6cwRKiuKBuxwNmHPLm_BhFs5ih-z39nnkySDaBmEU0fRbgEJURgGSqAEPb5m-VJg5rT-XmJtwi8FCv1pwrcxNLTwNqdjrZqpZXRTghmuOoPiJvN3jVuuTYsV-06PomAMLXT080kmIE/s1600/04.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1792d_BgbuuWMilQrQsrqyYU8V5MD3liMZVa36eqcGYfwICPraedhAvLRqdglmaXBxmaEBQBbihAnfUgt_o-9bEWmjz6Isz5lVUWvVREcJi5Sb3zN1BrGvZXWagYqP6Gm1lFMqAF1nAn5/s1600/05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1792d_BgbuuWMilQrQsrqyYU8V5MD3liMZVa36eqcGYfwICPraedhAvLRqdglmaXBxmaEBQBbihAnfUgt_o-9bEWmjz6Isz5lVUWvVREcJi5Sb3zN1BrGvZXWagYqP6Gm1lFMqAF1nAn5/s1600/05.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">After the last action, a view will be retrieved, informing the user to close the prompted dialog. Nothing new till now, all of this was already described by Richard.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Since we're authenticated now, let's start building our File's class to store the values we need:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoCTlWk0enFhW9AKFFoSIZ5CjMXmlLcNjZRC2YM7e6EdRiA4VTvpwEKwjk4DCRwOQqZLbToWLff3VgmepAw9jh2bSVG43CaP9z3YJq4htsHN35ZgkICjMP0oZtc27UE_ohNSvbePZDzSVi/s1600/06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoCTlWk0enFhW9AKFFoSIZ5CjMXmlLcNjZRC2YM7e6EdRiA4VTvpwEKwjk4DCRwOQqZLbToWLff3VgmepAw9jh2bSVG43CaP9z3YJq4htsHN35ZgkICjMP0oZtc27UE_ohNSvbePZDzSVi/s1600/06.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Now we can create our FileRepository class, to implement the method to get the files from SharePoint using search API...</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggHH9vKLQhowpA1LBAcdiSLb2Mj5RHFGiBm7gmpDi9rQEfPDYCX-PSl5VsdmMGmBXonwCHx3uwGq5AjuyN-AhsRAgUoNqAgykkM4raE4f0C60jO89eZHxvLVxz-R4xlmLxm5mdUMkhfpPV/s1600/07.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggHH9vKLQhowpA1LBAcdiSLb2Mj5RHFGiBm7gmpDi9rQEfPDYCX-PSl5VsdmMGmBXonwCHx3uwGq5AjuyN-AhsRAgUoNqAgykkM4raE4f0C60jO89eZHxvLVxz-R4xlmLxm5mdUMkhfpPV/s1600/07.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Our 'GetFiles' method will receive a token and a parameter which will indicate what do I want to retrieve. In order to summarize, I will explain the "My Work" query:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Georgia, Times New Roman, serif;">query = "/search/query?Querytext='*'&Properties='GraphQuery:AND(ACTOR(391380\\,action\\:1003)\\,ACTOR(391380\\,OR(action\\:1003\\,action\\:1036\\,action\\:1037\\,action\\:1039)))'&selectproperties='Title,Path,ViewsLifeTime,LastModifiedTime,SiteID,WebId,UniqueId,SecondaryFileExtension,SiteTitle,SPWebUrl,ServerRedirectedURL,EditorOWSUSER'";</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<br />
<ul>
<li><span style="font-family: 'Courier New', Courier, monospace;">/search/query?Querytext='*' - We're using the search api and defining the text as '*', which is any text.</span></li>
<li><span style="font-family: Courier New, Courier, monospace;">&Properties='GraphQuery:AND(ACTOR(391380\\,action\\:1003)\\,ACTOR(391380\\,OR(action\\:1003\\,action\\:1036\\,action\\:1037\\,action\\:1039))) - Here we're saying that we want to specify properties of a graph query (using actor(s) and action(s)). In this example I'm using my own Actor ID "</span><span style="font-family: 'Courier New', Courier, monospace;">391380", since we're getting access using app permissions, it won't work if I define the actor as "ME".</span></li>
<li><span style="font-family: Courier New, Courier, monospace;">&selectproperties='Title,Path,ViewsLifeTime(...) - finally, let's bring the properties we need (specified in the File class).</span></li>
</ul>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">After our request being processed, we'll store the response relevant information:</span></div>
<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDSYh_CM0hUco1L3aR7w4v2pho3pBu5konzPDit_m_vvwwrg-pvvdH-l49ohlyPXqhGfk6HZarF3CUXSsnOQRFFGw0LhqiQaJUIXbUUKmOF1OdYqLLzt8dgD4sHn7R5xMyDISKXJIU1clm/s1600/08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDSYh_CM0hUco1L3aR7w4v2pho3pBu5konzPDit_m_vvwwrg-pvvdH-l49ohlyPXqhGfk6HZarF3CUXSsnOQRFFGw0LhqiQaJUIXbUUKmOF1OdYqLLzt8dgD4sHn7R5xMyDISKXJIU1clm/s1600/08.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">With all the business logic done, let's talk about controllers and views...</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Controller:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Our controller will get the stored token and get the files using the token and a parameter to indicate that I want "My Work" files (Created and/or modified by me)</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9TiIn46bKkePQFj-orl52onrLfNCZImBrt1sEGg6MlL7LLYrBHeVN_wLkepB3LusCFpCidKxcTi1SJnsxW9NuaZxuWRTa3tNvtb-k72pIQxHKfhoa7tG3RA7xzieG4WYm5b_2pKYquHfr/s1600/09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9TiIn46bKkePQFj-orl52onrLfNCZImBrt1sEGg6MlL7LLYrBHeVN_wLkepB3LusCFpCidKxcTi1SJnsxW9NuaZxuWRTa3tNvtb-k72pIQxHKfhoa7tG3RA7xzieG4WYm5b_2pKYquHfr/s1600/09.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">View:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Let's call the Index action in our view with a fancy button...</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdBwwRRQ_ivguudX8Kq9rUse58-UoVORonTUSdlEUW5v-ypSkZtdDceaMtkMwUYOHzm_U9L-ZBMqAjsu1J0yrRh0JijDcLj5NmYHHQg2yva7RL1HvquM0sZLAUvypt5BkEicLCDxktg9le/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdBwwRRQ_ivguudX8Kq9rUse58-UoVORonTUSdlEUW5v-ypSkZtdDceaMtkMwUYOHzm_U9L-ZBMqAjsu1J0yrRh0JijDcLj5NmYHHQg2yva7RL1HvquM0sZLAUvypt5BkEicLCDxktg9le/s1600/10.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace;">Aaand it's done! A little bit of creativity and you can, for example, to have Delve in an Office add-in:</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7KCVzUGeUJ5Yd8EhiU5ylSoPxRDFdscyS7Q3-VNZ46VKsSO0xPLGv9EQJsPG4_zQIo9vGRSJpYD0vhZ5vCsmo3dMddZS7hQKBH-e86R7PF4Jd4oirJhuNSV-vi-hGvWinT961tv3SV12g/s1600/Delve+Add+In.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7KCVzUGeUJ5Yd8EhiU5ylSoPxRDFdscyS7Q3-VNZ46VKsSO0xPLGv9EQJsPG4_zQIo9vGRSJpYD0vhZ5vCsmo3dMddZS7hQKBH-e86R7PF4Jd4oirJhuNSV-vi-hGvWinT961tv3SV12g/s1600/Delve+Add+In.png" /></a></div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace;">Happy coding!</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>RodCoderhttp://www.blogger.com/profile/08206822216774270650noreply@blogger.com0tag:blogger.com,1999:blog-8236291434363530568.post-60243304245755433322015-07-16T08:14:00.002-07:002015-07-16T08:14:44.239-07:00Downloading files from OneDrive using the Office 365 API (MVC add-in)<span style="font-family: Courier New, Courier, monospace;">For this example I used the "Office 365 Starter Project for ASP.NET MVC" developed by Microsoft, which you can find <a href="https://github.com/OfficeDev/O365-ASPNETMVC-Start" target="_blank">here</a>, including all the instructions to set it up.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">This sample uses</span> "<span style="background-color: white; color: #666666; font-family: Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; line-height: 22.3999996185303px;">the Office 365 API Tools to demonstrate basic operations against the Calendar, Contacts, and Mail service endpoints in Office 365 from a single-tenant ASP.NET MVC application.</span>"<span style="font-family: Courier New, Courier, monospace;">, so when you click in "My Files" button, you get a list of all files and folders within your personal OneDrive and the capability of deleting them. </span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">What I'm going to demonstrate is how to extend those capabilities, by adding a new link (Download) which will allow you to download the files you want. </span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">First, we have to create the method to download the file in our FileOperations class, located inside the "Helpers" folder.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9Nqm6RhXz2K6yG0V0R9ZxYbLBLfFlzLKk6EjoIMmWmLQ0vJizrF-Vy_O55WkB4mh5QSAqAtwjeRZN4JlF0iJ6HAuASL9YCGjbFSamdIQte5qrzb7wEP-VyKvmjEMcQ1d-36WceJrZeHnH/s1600/fileOperationsMethod.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9Nqm6RhXz2K6yG0V0R9ZxYbLBLfFlzLKk6EjoIMmWmLQ0vJizrF-Vy_O55WkB4mh5QSAqAtwjeRZN4JlF0iJ6HAuASL9YCGjbFSamdIQte5qrzb7wEP-VyKvmjEMcQ1d-36WceJrZeHnH/s1600/fileOperationsMethod.png" /></a></div>
<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace;">Then, we have to create our "Download" action in the file controller. This action will be a FileStreamResult, as we want to send binary content using a Stream instance. </span><br />
<span style="font-family: Courier New, Courier, monospace;">Finally we will return the result of type File, passing the Stream instance and the file name. </span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Note: In order to dynamically retrieve the item content type, we will use MimeMapping.GetMimeMapping method.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHClk_-dgqcdTj25-Cfkj_W8-JvGEEY3kwXOUybQctfcGO5uOYGnEnqLYC0GgAs1Qogj0NbPLZfRdsIRwjd_sxXGajFVUwzWqRB4j6gZhM85Ot6cE8weLEqbrg1qk-LYqJjDR-1FyoCLHZ/s1600/FileStreamResult.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHClk_-dgqcdTj25-Cfkj_W8-JvGEEY3kwXOUybQctfcGO5uOYGnEnqLYC0GgAs1Qogj0NbPLZfRdsIRwjd_sxXGajFVUwzWqRB4j6gZhM85Ot6cE8weLEqbrg1qk-LYqJjDR-1FyoCLHZ/s1600/FileStreamResult.png" /></a></div>
<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">To finish, we need to edit the view and add an action link to our controller. This action will be displayed only when the item is a file.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheXXfeH_BbL4ECj1IaS1JVune8eyp61UeIO6VtPT7qr-cWXFQAMQpFLmWD_RY__Zz1HXS-OTEY7DI9P37bE7MykM9nIa07SCmCWZM7Nyo2-yBFmvTEyDy3Af_bLcUHR5Id6APq2ld5qF-5/s1600/View.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheXXfeH_BbL4ECj1IaS1JVune8eyp61UeIO6VtPT7qr-cWXFQAMQpFLmWD_RY__Zz1HXS-OTEY7DI9P37bE7MykM9nIa07SCmCWZM7Nyo2-yBFmvTEyDy3Af_bLcUHR5Id6APq2ld5qF-5/s1600/View.png" /></a></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">... The result:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7D9yIFyOughexrnN1-647EvR0m8U70DZlx8alB4bE-h3zbNPIPqxPioxXdo0NCFjN9dSJ_LvjQPIHcXKJ2QYiiFFuGzmdAEguvAV1c_todBX4QeN5ZBZefaWhkcCOVfMKTfExaR9OTCrD/s1600/MyFiles.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7D9yIFyOughexrnN1-647EvR0m8U70DZlx8alB4bE-h3zbNPIPqxPioxXdo0NCFjN9dSJ_LvjQPIHcXKJ2QYiiFFuGzmdAEguvAV1c_todBX4QeN5ZBZefaWhkcCOVfMKTfExaR9OTCrD/s1600/MyFiles.png" /></a></div>
<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Happy coding ;)</span>RodCoderhttp://www.blogger.com/profile/08206822216774270650noreply@blogger.com0tag:blogger.com,1999:blog-8236291434363530568.post-60195175161386248522015-07-13T05:04:00.000-07:002015-07-13T05:40:39.380-07:00JSLink with callback handler and synchronous ajax calls to validate user input.<span style="color: orange; font-family: Courier New, Courier, monospace; font-size: large;"><b>Scenario:</b></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: large;"><b><br /></b></span><span style="font-family: Courier New, Courier, monospace;">For this specific case, I based myself on one of our client's requests, which was "to create some sort of validation, when a user submits an item in a booking list". </span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span><span style="font-family: Courier New, Courier, monospace;">There was a list indicating the stock of each item available for booking, so the approach was to request the number of items available (via ajax call) in the mentioned list and if that number was greater than "0", then the user should be able to make a booking.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="color: orange; font-family: Courier New, Courier, monospace; font-size: large;"><b>Details:</b></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: large;"><b><br /></b></span>
<span style="font-family: Courier New, Courier, monospace;">Material field name : <b>Material</b></span><br />
<span style="font-family: Courier New, Courier, monospace;">Item availability list name : <b>Item Availability</b></span><br />
<span style="font-family: Courier New, Courier, monospace;">Booking list name: <b>Material Bookings</b></span><br />
<b><span style="font-family: Courier New, Courier, monospace;"><br /></span></b>
<b><span style="color: orange; font-family: Courier New, Courier, monospace; font-size: large;">Solution:</span></b><br />
<b><span style="font-family: Courier New, Courier, monospace; font-size: large;"><br /></span></b>
<span style="font-family: Courier New, Courier, monospace;">After the JSLink file being referenced in the list definition (Schema.xml), we can start developing our business logic.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<br />
<ul>
<li><span style="font-family: 'Courier New', Courier, monospace;">Adding the callback handler to the new form.</span></li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT3nlawqWqm5WhE7SZrDt3sb6XyQzEUmoBLxh0EcdoVX1R2ii-Ss462IDhkT9ZSsr8rOuHkxCJuqUZSXQ01kYlO8ZOLGFdNiCQ3HVwUolH3hm2U6UrEW81HQFQ7ao_alwpizsXUbfpC12t/s1600/AddCallbackHandler.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT3nlawqWqm5WhE7SZrDt3sb6XyQzEUmoBLxh0EcdoVX1R2ii-Ss462IDhkT9ZSsr8rOuHkxCJuqUZSXQ01kYlO8ZOLGFdNiCQ3HVwUolH3hm2U6UrEW81HQFQ7ao_alwpizsXUbfpC12t/s640/AddCallbackHandler.png" width="640" /></a></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<ul>
<li><span style="font-family: 'Courier New', Courier, monospace;">In the "AddCallbackHandler" function we will have to:</span></li>
</ul>
</div>
<span style="font-family: Courier New, Courier, monospace;"></span><br />
<ol><ol><span style="font-family: Courier New, Courier, monospace;">
<li>Get form context for current field</li>
<li>Create a validator set to register the required validator and our custom validator</li>
<li>Register our error callback</li>
<li>Return the field default html (choice in this case) and a <span> tag which will contain our custom error message to be displayed when saving a new item is not allowed.</li>
</span></ol>
<span style="font-family: Courier New, Courier, monospace;">
</span></ol>
<span style="font-family: Courier New, Courier, monospace;">
</span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirf0rJGSk5RIv3As7rw7UWnUumlk_l4PWfaSLrQR8_uGiE4jIt1G-Rcog53C-utL7xGpLfp5zCEFkEggiiN3iGNH9UFYbVf_qAFhF_VV7eTNOzjK6Ji_n_q497zkGOHzFiMVXKAmHsTIRR/s1600/AddFieldValidators.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirf0rJGSk5RIv3As7rw7UWnUumlk_l4PWfaSLrQR8_uGiE4jIt1G-Rcog53C-utL7xGpLfp5zCEFkEggiiN3iGNH9UFYbVf_qAFhF_VV7eTNOzjK6Ji_n_q497zkGOHzFiMVXKAmHsTIRR/s1600/AddFieldValidators.png" /></a><span style="font-family: Courier New, Courier, monospace;"></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span>
<br />
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<span style="font-family: Courier New, Courier, monospace;">
</span>
<br />
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<br />
<ul><span style="font-family: Courier New, Courier, monospace;">
</span></ul>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<ul><span style="font-family: Courier New, Courier, monospace;">
<li>Now let's define our custom validation</li>
</span></ul>
</div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Courier New, Courier, monospace;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4rA-9CZ_HA2SmljdZaO3MAyTtxOzgbPxmC9luosQaxa84pGG_LVFfvnpVXVOtJJbM1Hm5yFrNMhtvKYote_8kdbnPiYEB1oqqxej-K2XTlaPOJBJpEASf9jmvAdpvrp0dQI7WSXKY_aXQ/s1600/CustomValidation.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4rA-9CZ_HA2SmljdZaO3MAyTtxOzgbPxmC9luosQaxa84pGG_LVFfvnpVXVOtJJbM1Hm5yFrNMhtvKYote_8kdbnPiYEB1oqqxej-K2XTlaPOJBJpEASf9jmvAdpvrp0dQI7WSXKY_aXQ/s1600/CustomValidation.png" /></a></span></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXzgZ4YRMo5nE17Aa_i6qA30af79fF0ajUFW-VUOHvbzCNaUNV3Mc3MXef5JkY_RXixF4PIxQX_8isZS34YcgpwubldOeZBiaT-HWxTIGaj8xhHvOcDdBzTfsmFs-YBm5K7OgUAn0P5fV3/s1600/GetResultsFromQuery.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXzgZ4YRMo5nE17Aa_i6qA30af79fF0ajUFW-VUOHvbzCNaUNV3Mc3MXef5JkY_RXixF4PIxQX_8isZS34YcgpwubldOeZBiaT-HWxTIGaj8xhHvOcDdBzTfsmFs-YBm5K7OgUAn0P5fV3/s1600/GetResultsFromQuery.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">I developed a separate function to get the results, just because we needed to make more than one call. Note: "async" was set to false, otherwise it would have to be promisified.</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
</div>
<span style="font-family: Courier New, Courier, monospace;">
</span></div>
<ul style="font-family: 'Courier New', Courier, monospace; font-size: 12.8000001907349px;">
<li><span style="font-size: small;">To finish, we just have to define what happens when an error occurs.</span></li>
</ul>
<div class="separator" style="clear: both; font-family: 'Courier New', Courier, monospace; font-size: 12.8000001907349px; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghAX9bJF6kVxVF3t14BKhqgARffKnn3Vsb5I875ynLqvnHkAG5wIe4SDLekRuSFKlA8nQUHFMXaObSNk4EuRno856gHxoWfHBx8LFm4BxvUVUPCaE2wcbfUAaidZbodEUXCTGWZ6kpvbwi/s1600/onError.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghAX9bJF6kVxVF3t14BKhqgARffKnn3Vsb5I875ynLqvnHkAG5wIe4SDLekRuSFKlA8nQUHFMXaObSNk4EuRno856gHxoWfHBx8LFm4BxvUVUPCaE2wcbfUAaidZbodEUXCTGWZ6kpvbwi/s1600/onError.png" /></a></div>
<div style="font-family: 'Courier New', Courier, monospace; font-size: 12.8000001907349px;">
<span style="font-size: small;"><br /></span></div>
<div style="font-family: 'Courier New', Courier, monospace; font-size: 12.8000001907349px;">
<span style="font-size: small;"><br /></span></div>
<div style="font-family: 'Courier New', Courier, monospace; font-size: 12.8000001907349px;">
<span style="font-size: small;"><br /></span>
<span style="font-size: small;"><br /></span>
<span style="font-size: small;"><br /></span>
<span style="font-size: small;"><br /></span>
<span style="font-size: small;"><br /></span>
<span style="font-size: small;">Find the full code below:</span></div>
<div style="font-family: 'Courier New', Courier, monospace; font-size: 12.8000001907349px;">
<span style="font-size: small;"><br /></span></div>
<div style="font-family: 'Courier New', Courier, monospace; font-size: 12.8000001907349px;">
</div>
<script src="https://gist.github.com/RodCoder/98178b6bacc0b5a8d7df.js"></script>
<br />
<div style="font-family: 'Courier New', Courier, monospace; font-size: 12.8000001907349px;">
<span style="font-size: small;"><br /></span></div>
<div style="font-family: 'Courier New', Courier, monospace; font-size: 12.8000001907349px;">
<span style="font-size: small;">Hope you find it useful ;)</span></div>
<br />
<div>
</div>
<br />
<br />RodCoderhttp://www.blogger.com/profile/08206822216774270650noreply@blogger.com0