<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>C# Archives - Blog IT</title>
	<atom:link href="https://blogit.create.pt/tag/c/feed/" rel="self" type="application/rss+xml" />
	<link>https://blogit.create.pt/tag/c/</link>
	<description>Create IT blogger community</description>
	<lastBuildDate>Thu, 10 Jan 2019 12:46:22 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>
	<item>
		<title>How to add your custom content files into a nuget package (for Visual Studio)</title>
		<link>https://blogit.create.pt/gustavobrito/2018/01/12/how-to-add-your-custom-content-files-into-a-nuget-package-for-visual-studio/</link>
					<comments>https://blogit.create.pt/gustavobrito/2018/01/12/how-to-add-your-custom-content-files-into-a-nuget-package-for-visual-studio/#comments</comments>
		
		<dc:creator><![CDATA[Gustavo Brito]]></dc:creator>
		<pubDate>Fri, 12 Jan 2018 15:49:08 +0000</pubDate>
				<category><![CDATA[Nuget]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[$rootnamespace$]]></category>
		<category><![CDATA[add files]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[cml]]></category>
		<category><![CDATA[content]]></category>
		<category><![CDATA[contentFiles]]></category>
		<category><![CDATA[custom]]></category>
		<category><![CDATA[customfiles]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[external]]></category>
		<category><![CDATA[external files]]></category>
		<category><![CDATA[nupkg]]></category>
		<category><![CDATA[nuspec]]></category>
		<category><![CDATA[package]]></category>
		<category><![CDATA[packaging]]></category>
		<category><![CDATA[pp]]></category>
		<category><![CDATA[targets]]></category>
		<category><![CDATA[transform]]></category>
		<category><![CDATA[visual studio]]></category>
		<category><![CDATA[visual studio nuget]]></category>
		<category><![CDATA[xdt]]></category>
		<category><![CDATA[XML]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/gustavobrito/?p=1214</guid>

					<description><![CDATA[<p>Yesterday, I was assigned to fix a nuget package solution that was not packing all the needed files. I had a bad time searching online for answers, and had to dig in by myself. After I discovered how to include the files that I needed, I decided to create a simple tutorial that has it [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/gustavobrito/2018/01/12/how-to-add-your-custom-content-files-into-a-nuget-package-for-visual-studio/">How to add your custom content files into a nuget package (for Visual Studio)</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Yesterday, I was assigned to fix a nuget package solution that was not packing all the needed files. I had a bad time searching online for answers, and had to dig in by myself. After I discovered how to include the files that I needed, I decided to create a simple tutorial that has it all!</p>
<p>This tutorial will show you how to include your non-compilable items into a nuget package (nupkg). This steps apply to any type of file.</p>
<p><span id="more-1214"></span></p>
<p><strong>DISCLAIMER: You must have a nuspec file and a targets file. If you don&#8217;t have one yet, download one from somewhere and change the metadata accordingly.</strong></p>
<p><strong>Let&#8217;s start!</strong></p>
<ol>
<li>Open up your solution in Visual Studio</li>
<li>Open your solution&#8217;s nuspec file and look for <strong>&lt;files&gt;</strong> tag</li>
<li>You need to reference <strong>all folders and all files</strong> that you want to include in your nuget. Example below:</li>
</ol>
<blockquote>
<pre class="brush: xml; title: ; notranslate">
&lt;files&gt;
    &lt;file src=&quot;lib\net452\your_compiled_assembly.dll&quot; target=&quot;lib\net452&quot; /&gt;
    &lt;file src=&quot;Your_Folder\*.pp&quot; target=&quot;content\Your_Folder&quot;/&gt;
    &lt;file src=&quot;Your_Folder\Your_Sub_Folder\*.*&quot; target=&quot;content&quot; /&gt;
    &lt;file src=&quot;*.xdt&quot; target=&quot;content&quot; /&gt;
 &lt;/files&gt;
</pre>
</blockquote>
<p>4.  After you added all files into nuspec config, now you can open your targets file. Don&#8217;t forget to import targets into your csproj file like the example below</p>
<pre class="brush: xml; title: ; notranslate">
&lt;Import Project=&quot;your_targets_file.targets&quot; Condition=&quot;Exists('your_targets_file.targets')&quot; /&gt;
</pre>
<p>5. Now to targets file, you must index <strong>ALL </strong>files that you want to add to your nuget. <strong>This step is very important</strong></p>
<div>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;Project ToolsVersion=&quot;14.0&quot; DefaultTargets=&quot;Build&quot; xmlns=&quot;http://schemas.microsoft.com/developer/msbuild/2003&quot;&gt;
 &lt;TargetName=&quot;CreatePackage&quot;&gt;
   &lt;PropertyGroup&gt;
   &lt;Configuration&gt;Release&lt;/Configuration&gt;
   &lt;PackageSource&gt;bin\$(Configuration)\Package&lt;/PackageSource&gt;
   &lt;NuSpecPath&gt;$(MSBuildProjectName).nuspec&lt;/NuSpecPath&gt;
   &lt;/PropertyGroup&gt;
   &lt;RemoveDirDirectories=&quot;$(PackageSource)&quot;/&gt;
   &lt;MSBuildProjects=&quot;$(MSBuildProjectFullPath)&quot; Targets=&quot;Rebuild&quot;Properties=&quot; TargetFrameworkVersion=v4.0; OutputPath=bin\$(Configuration)\net40;Configuration=$(Configuration)&quot; BuildInParallel=&quot;$(BuildInParallel)&quot;/&gt;


   &lt;Copy SourceFiles=&quot;bin\$(Configuration)\net40\$(AssemblyName).dll&quot; DestinationFolder=&quot;$(PackageSource)\lib\net40&quot;/&gt;
   &lt;Copy SourceFiles=&quot;$(NuSpecPath)&quot; DestinationFolder=&quot;$(PackageSource)&quot;/&gt;
   &lt;Copy SourceFiles=&quot;Your_Folder\Your_Class_File.cs.pp&quot; DestinationFolder=&quot;$(PackageSource)\Your_Folder&quot;/&gt;
   &lt;Copy SourceFiles=&quot;Your_Folder\Yet_Another_Class.cs.pp&quot; DestinationFolder=&quot;$(PackageSource)\Your_Folder&quot;/&gt;
   &lt;Copy SourceFiles=&quot;Your_Folder\Your_Sub_Folder\A_Class.cs.pp&quot; DestinationFolder=&quot;$(PackageSource)\Your_Folder\Your_Sub_Folder&quot;/&gt;
   &lt;Copy SourceFiles=&quot;Your_Images_Folder\Your_Image.jpeg&quot; DestinationFolder=&quot;$(PackageSource)\Your_Images_Folder&quot;/&gt;
   &lt;Copy SourceFiles=&quot;Your_root_file.xml&quot; DestinationFolder=&quot;$(PackageSource)&quot;/&gt;
 &lt;/Target&gt;
&lt;/Project&gt;
</pre>
</div>
<p>6. After you added all your source files into your targets file, you are ready to pack it!!</p>
<p><strong>File extensions for Transformations and generated classes:</strong></p>
<ol>
<li>A <strong>pp </strong>file extension is a class that&#8217;s generated when a nuget is installed in any solution. Usually, the class namespace is replaced by &#8220;<strong>$rootnamespace$&#8221;</strong> (without quotes) and the extension is added (yourclass.cs<strong>.pp</strong>)</li>
<li>A <strong>xdt </strong>file extension is a <a href="https://msdn.microsoft.com/en-us/library/dd465326(v=vs.110).aspx">XML Transformation procedure</a>.</li>
</ol>
<p>Example of a <strong>*.pp </strong>class ready to be packed into a nuget:</p>
<pre class="brush: csharp; title: ; notranslate">
namespace $rootnamespace$.App_Start
{
 using System;
 using System.Collections.Generic;
 using System.Configuration;
 using System.Linq;
...
}
</pre>
<p>That&#8217;s all folks!</p>
<p>The post <a href="https://blogit.create.pt/gustavobrito/2018/01/12/how-to-add-your-custom-content-files-into-a-nuget-package-for-visual-studio/">How to add your custom content files into a nuget package (for Visual Studio)</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/gustavobrito/2018/01/12/how-to-add-your-custom-content-files-into-a-nuget-package-for-visual-studio/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title>Activating all Publishing Feature &#8211; Programmatically &#038; Powershell &#8211; SharePoint 2010, 2013, 2016 &#038; Online:</title>
		<link>https://blogit.create.pt/fabiocarvalho/2017/07/28/activating-all-publishing-feature-programmatically-powershell-sharepoint-2010-2013-2016-online/</link>
					<comments>https://blogit.create.pt/fabiocarvalho/2017/07/28/activating-all-publishing-feature-programmatically-powershell-sharepoint-2010-2013-2016-online/#respond</comments>
		
		<dc:creator><![CDATA[Fábio Carvalho]]></dc:creator>
		<pubDate>Fri, 28 Jul 2017 21:50:48 +0000</pubDate>
				<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Office]]></category>
		<category><![CDATA[Microsoft 365]]></category>
		<category><![CDATA[Tools]]></category>
		<category><![CDATA[SharePoint]]></category>
		<category><![CDATA[Administration]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Publishing Site]]></category>
		<category><![CDATA[SharePoint 2010]]></category>
		<category><![CDATA[SharePoint 2013]]></category>
		<category><![CDATA[SharePoint 2016]]></category>
		<category><![CDATA[SharePoint Online]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/fabiocarvalho/?p=3774</guid>

					<description><![CDATA[<p>Hi Everyone!!! Today i will talk about Publishing Features!!! During this afternoon I was trying to activate Publishing Features but i wasn&#8217;t having success because Page Library not was created on activation! So there are more than two Publishing Feature that we need have in consideration, it&#8217;s not enough Activate Publishing Infrastructure Feature and Publishing Feature. If [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/fabiocarvalho/2017/07/28/activating-all-publishing-feature-programmatically-powershell-sharepoint-2010-2013-2016-online/">Activating all Publishing Feature &#8211; Programmatically &#038; Powershell &#8211; SharePoint 2010, 2013, 2016 &#038; Online:</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Hi Everyone!!!</p>
<p>Today i will talk about Publishing Features!!! During this afternoon I was trying to activate Publishing Features but i wasn&#8217;t having success because Page Library not was created on activation! So there are more than two Publishing Feature that we need have in consideration, it&#8217;s not enough Activate Publishing Infrastructure Feature and Publishing Feature. If you want activate all Publishing Features you can do it pragmatically or using Powershell script:</p>
<p><span style="color: #800000"><strong>SharePoint Server &#8211; Powershell:</strong></span></p>
<pre class="brush: bash; title: ; notranslate"> 
Disable-SPFeature –identity 'publishingSite' -URL http://server/ -force
Disable-SPFeature –identity 'PublishingResources' -URL http://server/-force
Disable-SPFeature –identity 'Publishing' -URL http://server/ -force
Disable-SPFeature –identity 'PublishingLayouts' -URL http://server/ -force
Disable-SPFeature –identity 'publishingweb' -URL http://server/ -force

Enable-SPFeature –identity 'publishingSite' -URL http://server/ -force
Enable-SPFeature –identity 'PublishingResources' -URL http://server/ -force
Enable-SPFeature –identity 'Publishing' -URL http://server/ -force
Enable-SPFeature –identity 'PublishingLayouts' -URL http://server/ -force
Enable-SPFeature –identity 'publishingweb' -URL http://server/ -force
</pre>
<p><span style="color: #800000"><strong>SharePoint Online &#8211; Powershell:</strong></span></p>
<pre class="brush: bash; title: ; notranslate"> 
Disable-SPOFeature –Identity &quot;F6924D36-2FA8-4f0b-B16D-06B7250180FA&quot; -Scope Site
Disable-SPOFeature –Identity &quot;AEBC918D-B20F-4a11-A1DB-9ED84D79C87E&quot; -Scope Site
Disable-SPOFeature –Identity &quot;22A9EF51-737B-4ff2-9346-694633FE4416&quot; -Scope Web
Disable-SPOFeature –Identity &quot;D3F51BE2-38A8-4e44-BA84-940D35BE1566&quot; -Scope Site
Disable-SPOFeature –Identity &quot;94C94CA6-B32F-4da9-A9E3-1F3D343D7ECB&quot; -Scope Web

Enable-SPOFeature –Identity &quot;F6924D36-2FA8-4f0b-B16D-06B7250180FA&quot; -Scope Site
Enable-SPOFeature –Identity &quot;AEBC918D-B20F-4a11-A1DB-9ED84D79C87E&quot; -Scope Site
Enable-SPOFeature –Identity &quot;22A9EF51-737B-4ff2-9346-694633FE4416&quot; -Scope Web
Enable-SPOFeature –Identity &quot;D3F51BE2-38A8-4e44-BA84-940D35BE1566&quot; -Scope Site
Enable-SPOFeature –Identity &quot;94C94CA6-B32F-4da9-A9E3-1F3D343D7ECB&quot; -Scope Web
</pre>
<p><strong><span style="color: #800000">Also that you can activate Programmatically:</span></strong></p>
<pre class="brush: jscript; title: ; notranslate"> 

        private void ActivatePublishingFeatures(SPWeb web, SPSite site)
        {
            // Publishing Site
            string sharePointPublishingSite = &quot;f6924d36-2fa8-4f0b-b16d-06b7250180fa&quot;;
            Guid sharePointPublishingSiteGuid = new Guid(sharePointPublishingSite);

            if (site.Features&#x5B;sharePointPublishingSiteGuid] == null)
            {
                site.Features.Add(sharePointPublishingSiteGuid, true);
            }

            // Publishing Resources
            string sharePointPublishingResources = &quot;aebc918d-b20f-4a11-a1db-9ed84d79c87e&quot;;
            Guid sharePointPublishingResourcesGuid = new Guid(sharePointPublishingResources);

            if (site.Features&#x5B;sharePointPublishingResourcesGuid] == null)
            {
                site.Features.Add(sharePointPublishingResourcesGuid, true);
            }

            // Publishing
            string sharePointPublishing = &quot;22a9ef51-737b-4ff2-9346-694633fe4416&quot;;
            Guid sharePointPublishingGuid = new Guid(sharePointPublishing);

            if (web.Features&#x5B;sharePointPublishingGuid] == null)
            {
                web.Features.Add(sharePointPublishingGuid, true);
            }

            // Publishing Layouts
            string sharePointPublishingLayouts = &quot;d3f51be2-38a8-4e44-ba84-940d35be1566&quot;;
            Guid sharePointPublishingLayoutsGuid = new Guid(sharePointPublishingLayouts);

            if (site.Features&#x5B;sharePointPublishingLayoutsGuid] == null)
            {
                site.Features.Add(sharePointPublishingLayoutsGuid, true);
            }

            // Publishing Web
            string sharePointServerPublishingWeb = &quot;94c94ca6-b32f-4da9-a9e3-1f3d343d7ecb&quot;;
            Guid sharePointServerPublishingWebGuid = new Guid(sharePointServerPublishingWeb);

            if (web.Features&#x5B;sharePointServerPublishingWebGuid] == null)
            {
                web.Features.Add(sharePointServerPublishingWebGuid, true);
            }
        }
</pre>
<p>Thanks</p>
<p><span style="color: #800000"><a href="https://www.linkedin.com/in/fmrgc/"><strong>Fábio Carvalho</strong></a></span><br />
SharePoint Consultant<br />
<strong>|create|</strong><span style="color: #ff0000"><strong>it</strong></span><strong>|</strong></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>The post <a href="https://blogit.create.pt/fabiocarvalho/2017/07/28/activating-all-publishing-feature-programmatically-powershell-sharepoint-2010-2013-2016-online/">Activating all Publishing Feature &#8211; Programmatically &#038; Powershell &#8211; SharePoint 2010, 2013, 2016 &#038; Online:</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/fabiocarvalho/2017/07/28/activating-all-publishing-feature-programmatically-powershell-sharepoint-2010-2013-2016-online/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>KendoUI Grid, OData, WebAPI and Entity Framework</title>
		<link>https://blogit.create.pt/andresantos/2017/07/05/kendoui-grid-odata-webapi-and-entity-framework/</link>
					<comments>https://blogit.create.pt/andresantos/2017/07/05/kendoui-grid-odata-webapi-and-entity-framework/#respond</comments>
		
		<dc:creator><![CDATA[André Santos]]></dc:creator>
		<pubDate>Wed, 05 Jul 2017 10:42:53 +0000</pubDate>
				<category><![CDATA[Umbraco]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[azure]]></category>
		<category><![CDATA[Backoffice extension]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[entity framework]]></category>
		<category><![CDATA[grid]]></category>
		<category><![CDATA[kendoui]]></category>
		<category><![CDATA[odata]]></category>
		<category><![CDATA[sql database]]></category>
		<category><![CDATA[webapi]]></category>
		<category><![CDATA[webapp]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/andresantos/?p=1764</guid>

					<description><![CDATA[<p>In the latest project I was involved with, there was a need to present database tables data directly in the Umbraco&#8217;s backoffice, where all CRUD operations must be supported. This tables could be something like 1000 to 1 million rows and both the website and database would be hosted in Azure, in a WebApp and [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2017/07/05/kendoui-grid-odata-webapi-and-entity-framework/">KendoUI Grid, OData, WebAPI and Entity Framework</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap dropcap3">I</span>n the latest project I was involved with, there was a need to present database tables data directly in the Umbraco&#8217;s backoffice, where all CRUD operations must be supported. This tables could be something like 1000 to 1 million rows and both the website and database would be hosted in Azure, in a WebApp and SQL Database respectively. Users should be able to create complex filters and order the data as they wish as well as update the data individually and in batch.</p>
<p>From the get-go there were some challenges we needed to overcome in order to create an usable system, that was user-friendly, fast, reliable, and where no conflitcts would come up when data was being updated from multiple users.</p>
<p>The solution was a new Umbraco Package we called <em>Search Manager</em> (since the database tables ultimately feed an Azure Search index) that uses <a href="http://demos.telerik.com/kendo-ui/grid/index">Telerik&#8217;s Kendo UI Grid</a>, WebAPI and Entity Framework. The following presents the process it took to reach the end result.</p>
<p><span id="more-6483"></span></p>
<h2>Step 1</h2>
<p>We created a simple Umbraco Package that implemented a Kendo UI Grid which got its data from a WebAPI method that returned all rows from a table using Petapoco.</p>
<p>This allowed us to have the complete table data in memory using a Kendo UI Grid. It worked great but had major problems:</p>
<ul>
<li>A table with around 40k rows was actually a 40 MBs json download in order to populate the grid</li>
<li>Users could be working with outdated data, since it was loaded in memory and changes could have been made to the source</li>
</ul>
<h2>Step 2</h2>
<p>We started using the server paging feature of the Kendo Grid. This dropped the download size greatly, but we still had problems:</p>
<ul>
<li>Even though we are working with only a subset of results client-side, we were still getting the complete table data server-side, using petapoco.</li>
</ul>
<p>We needed an easy way to translate the client-side queries, such as filters, paging, order, etc to SQL queries.</p>
<h2>Step 3</h2>
<p>We changed the type of queries being generated by the Grid to OData. Using the <a href="https://www.nuget.org/packages/Microsoft.AspNet.WebApi.OData">OData WebAPI nuget package</a> we are able to map the odata query  to a C# object and then directly apply the query to any IQueryable data.</p>
<p>Petapoco does not provide any IQueryable data support, so we changed our ORM to Entity Framework, which does, and <em>voilá</em>!</p>
<h2>Finally</h2>
<p>With everything in place, we are able to have a filtered subset of results in a Grid, that gets exactly what it needs to display from a dynamically generated SQL query.</p>
<ul>
<li>It is fast because we only get exactly what we need:</li>
</ul>
<p>An OData query generated by the Kendo UI Grid looks like this:</p>
<pre class="brush: plain; title: ; notranslate">
http://localhost:14231/umbraco/backoffice/SearchManager/PricesApi/Read?%24inlinecount=allpages&%24top=20&%24skip=40&%24orderby=ProviderName+desc&%24filter=(NatureProcCode+eq+%27exames%27+and+AvgAmtClaimed+gt+10)
</pre>
<p>Which is translated to the follwing SQL query:</p>
<pre class="brush: sql; title: ; notranslate">
exec sp_executesql N'SELECT 
    &#x5B;Project1].&#x5B;Approved] AS &#x5B;Approved], 
    &#x5B;Project1].&#x5B;DiffWeightedRate] AS &#x5B;DiffWeightedRate], 
    &#x5B;Project1].&#x5B;SubmitPrvId] AS &#x5B;SubmitPrvId], 
    &#x5B;Project1].&#x5B;PracticeSeq] AS &#x5B;PracticeSeq], 
    &#x5B;Project1].&#x5B;NatureProcCode] AS &#x5B;NatureProcCode], 
    &#x5B;Project1].&#x5B;GroupProcCode] AS &#x5B;GroupProcCode], 
    &#x5B;Project1].&#x5B;Network] AS &#x5B;Network], 
    &#x5B;Project1].&#x5B;AmtClaimedWeighted] AS &#x5B;AmtClaimedWeighted], 
    &#x5B;Project1].&#x5B;ManualPrice] AS &#x5B;ManualPrice], 
    &#x5B;Project1].&#x5B;IsImported] AS &#x5B;IsImported], 
    &#x5B;Project1].&#x5B;DiffCalculatedRate] AS &#x5B;DiffCalculatedRate], 
    &#x5B;Project1].&#x5B;AmtContract] AS &#x5B;AmtContract], 
    &#x5B;Project1].&#x5B;AvgAmtClaimed] AS &#x5B;AvgAmtClaimed], 
    &#x5B;Project1].&#x5B;ProviderName] AS &#x5B;ProviderName], 
    &#x5B;Project1].&#x5B;ClinicName] AS &#x5B;ClinicName]
    FROM ( SELECT 
        &#x5B;Extent1].&#x5B;Approved] AS &#x5B;Approved], 
        &#x5B;Extent1].&#x5B;DiffWeightedRate] AS &#x5B;DiffWeightedRate], 
        &#x5B;Extent1].&#x5B;SubmitPrvId] AS &#x5B;SubmitPrvId], 
        &#x5B;Extent1].&#x5B;PracticeSeq] AS &#x5B;PracticeSeq], 
        &#x5B;Extent1].&#x5B;NatureProcCode] AS &#x5B;NatureProcCode], 
        &#x5B;Extent1].&#x5B;GroupProcCode] AS &#x5B;GroupProcCode], 
        &#x5B;Extent1].&#x5B;Network] AS &#x5B;Network], 
        &#x5B;Extent1].&#x5B;AmtClaimedWeighted] AS &#x5B;AmtClaimedWeighted], 
        &#x5B;Extent1].&#x5B;ManualPrice] AS &#x5B;ManualPrice], 
        &#x5B;Extent1].&#x5B;IsImported] AS &#x5B;IsImported], 
        &#x5B;Extent1].&#x5B;DiffCalculatedRate] AS &#x5B;DiffCalculatedRate], 
        &#x5B;Extent1].&#x5B;AmtContract] AS &#x5B;AmtContract], 
        &#x5B;Extent1].&#x5B;AvgAmtClaimed] AS &#x5B;AvgAmtClaimed], 
        &#x5B;Extent1].&#x5B;ProviderName] AS &#x5B;ProviderName], 
        &#x5B;Extent1].&#x5B;ClinicName] AS &#x5B;ClinicName]
        FROM (SELECT 
    &#x5B;PriceGrid].&#x5B;Approved] AS &#x5B;Approved], 
    &#x5B;PriceGrid].&#x5B;DiffWeightedRate] AS &#x5B;DiffWeightedRate], 
    &#x5B;PriceGrid].&#x5B;SubmitPrvId] AS &#x5B;SubmitPrvId], 
    &#x5B;PriceGrid].&#x5B;PracticeSeq] AS &#x5B;PracticeSeq], 
    &#x5B;PriceGrid].&#x5B;NatureProcCode] AS &#x5B;NatureProcCode], 
    &#x5B;PriceGrid].&#x5B;GroupProcCode] AS &#x5B;GroupProcCode], 
    &#x5B;PriceGrid].&#x5B;Network] AS &#x5B;Network], 
    &#x5B;PriceGrid].&#x5B;AmtClaimedWeighted] AS &#x5B;AmtClaimedWeighted], 
    &#x5B;PriceGrid].&#x5B;ManualPrice] AS &#x5B;ManualPrice], 
    &#x5B;PriceGrid].&#x5B;IsImported] AS &#x5B;IsImported], 
    &#x5B;PriceGrid].&#x5B;DiffCalculatedRate] AS &#x5B;DiffCalculatedRate], 
    &#x5B;PriceGrid].&#x5B;AmtContract] AS &#x5B;AmtContract], 
    &#x5B;PriceGrid].&#x5B;AvgAmtClaimed] AS &#x5B;AvgAmtClaimed], 
    &#x5B;PriceGrid].&#x5B;ProviderName] AS &#x5B;ProviderName], 
    &#x5B;PriceGrid].&#x5B;ClinicName] AS &#x5B;ClinicName]
    FROM &#x5B;dbo].&#x5B;PriceGrid] AS &#x5B;PriceGrid]) AS &#x5B;Extent1]
        WHERE (&#x5B;Extent1].&#x5B;NatureProcCode] = @p__linq__0) AND (&#x5B;Extent1].&#x5B;AvgAmtClaimed] &gt; @p__linq__1)
    )  AS &#x5B;Project1]
    ORDER BY &#x5B;Project1].&#x5B;ProviderName] DESC, &#x5B;Project1].&#x5B;AmtClaimedWeighted] ASC, &#x5B;Project1].&#x5B;AmtContract] ASC, &#x5B;Project1].&#x5B;Approved] ASC, &#x5B;Project1].&#x5B;AvgAmtClaimed] ASC, &#x5B;Project1].&#x5B;ClinicName] ASC, &#x5B;Project1].&#x5B;DiffCalculatedRate] ASC, &#x5B;Project1].&#x5B;DiffWeightedRate] ASC, &#x5B;Project1].&#x5B;GroupProcCode] ASC, &#x5B;Project1].&#x5B;IsImported] ASC, &#x5B;Project1].&#x5B;ManualPrice] ASC, &#x5B;Project1].&#x5B;NatureProcCode] ASC, &#x5B;Project1].&#x5B;Network] ASC, &#x5B;Project1].&#x5B;PracticeSeq] ASC, &#x5B;Project1].&#x5B;SubmitPrvId] ASC
    OFFSET @p__linq__2 ROWS FETCH NEXT @p__linq__3 ROWS ONLY  option (Recompile)',N'@p__linq__0 varchar(8000),@p__linq__1 decimal(2,0),@p__linq__2 int,@p__linq__3 int',@p__linq__0='exames',@p__linq__1=10,@p__linq__2=40,@p__linq__3=20
</pre>
<ul>
<li>It&#8217;s user-friendly and reliable because users can filter the data as if they were using an excel file on steroids</li>
<li>It avoids conflicts because we only get a subset of the results each time and each change is commited in a single transaction</li>
</ul>
<h2>Code samples:</h2>
<p>&nbsp;</p>
<pre class="brush: jscript; title: grid setup; notranslate">
grid = $(&quot;#grid&quot;).kendoGrid({
                dataSource: new kendo.data.DataSource({
                    transport: transport,
                    schema: schema,
                    serverFiltering: true,
                    serverPaging: true,
                    serverSorting: true,
                    type: &quot;odata&quot;,
                    pageSize: 20
                }),
                dataBound: onDataBound,
                pageable: true,
                filterable: true,
                sortable: true,
                scrollable: false,
                navigatable: true,
                resizable: true,
                batch: true,
                columnMenu: true,
                editable: true,
                toolbar: &#x5B;
					{ template: '&lt;a class=&quot;k-button k-button-icontext k-grid-save-batch&quot; href=&quot;\\#&quot;&quot;&gt;&lt;span class=&quot;k-icon k-i-update&quot;&gt;&lt;/span&gt;Guardar Alterações&lt;/a&gt;' },
					{ name: &quot;cancel&quot;, text: &quot;Cancelar&quot; },
                ],
                columns: &#x5B;...]
            });
</pre>
<p>&nbsp;</p>
<pre class="brush: csharp; title: WebAPI; notranslate">
&#x5B;HttpGet]
        public IHttpActionResult Read(ODataQueryOptions&lt;PriceGrid&gt; opts)
        {
            Mapper.CreateMap&lt;PriceGrid, PriceGridBO&gt;();

            using (var context = new EntitiesContext())
            {
                using (var qh = new HintScope(context, QueryHintType.Recompile))
                {
                    List&lt;PriceGrid&gt; results = opts
                        .ApplyTo(context.PriceGrids)
                        .Cast&lt;PriceGrid&gt;()
                        .ToList();

                    List&lt;PriceGridBO&gt; boEntities = results.Select(x =&gt; Mapper.Map&lt;PriceGrid, PriceGridBO&gt;(x)).ToList();

                    PriceResponse response = new PriceResponse()
                    {
                        Entities = boEntities,
                        Total = Request.ODataProperties().TotalCount.HasValue ? Request.ODataProperties().TotalCount.Value : 0
                    };

                    return Ok(response);
                }
            }
        }
</pre>
<p>The post <a href="https://blogit.create.pt/andresantos/2017/07/05/kendoui-grid-odata-webapi-and-entity-framework/">KendoUI Grid, OData, WebAPI and Entity Framework</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/andresantos/2017/07/05/kendoui-grid-odata-webapi-and-entity-framework/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Umbraco and Donut Output Cache</title>
		<link>https://blogit.create.pt/andresantos/2016/06/30/umbraco-and-donut-output-cache/</link>
					<comments>https://blogit.create.pt/andresantos/2016/06/30/umbraco-and-donut-output-cache/#respond</comments>
		
		<dc:creator><![CDATA[André Santos]]></dc:creator>
		<pubDate>Thu, 30 Jun 2016 14:22:01 +0000</pubDate>
				<category><![CDATA[Umbraco]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[controller]]></category>
		<category><![CDATA[Devtrends]]></category>
		<category><![CDATA[Donut Output Cache]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[mvc 5]]></category>
		<category><![CDATA[MVCDonutCaching]]></category>
		<category><![CDATA[performance]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/andresantos/?p=1571</guid>

					<description><![CDATA[<p>Donut Output Caching is a type of output caching where certain parts of a web page are not cached. It&#8217;s a simple way of boosting your site performance! ASP.NET doesn&#8217;t provide a native way of donut output caching, so we must resort to a great NuGet package called MVCDonutCaching. You can read all about it here: http://www.devtrends.co.uk/blog/donut-output-caching-in-asp.net-mvc-3. [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2016/06/30/umbraco-and-donut-output-cache/">Umbraco and Donut Output Cache</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Donut Output Caching is a type of output caching where certain parts of a web page are not cached. It&#8217;s a simple way of boosting your site performance!</p>
<p>ASP.NET doesn&#8217;t provide a native way of donut output caching, so we must resort to a great NuGet package called <a href="https://github.com/moonpyk/mvcdonutcaching">MVCDonutCaching</a>. You can read all about it here: <a href="http://www.devtrends.co.uk/blog/donut-output-caching-in-asp.net-mvc-3">http://www.devtrends.co.uk/blog/donut-output-caching-in-asp.net-mvc-3</a>.</p>
<p>Since Umbraco is built on top of ASP.NET MVC, we can easily use this package on our websites. There are, however, some caveats, because Umbraco doesn&#8217;t use the native MVC routing.</p>
<p>For Umbraco, every page that uses the same document type is processed through the same route, therefore, if we have 100 news articles that use the NewsItem document type, from the moment we open one news article page, every other news item page will display the same information, despite having a different URL!</p>
<p><span id="more-6482"></span></p>
<p>The way to bypass this issue is the following:</p>
<p>Create a Global.cs file that includes the logic to cache a page based on their url and not the document type</p>
<pre class="brush: csharp; title: Global.cs; notranslate">
namespace CreateIT.Web
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Umbraco.Web;

    public class Global : UmbracoApplication
    {
        public override string GetVaryByCustomString(HttpContext context, string custom)
        {
            if (custom.ToLower() == &quot;url&quot;)
            {
                return &quot;url=&quot; + context.Request.Url.AbsoluteUri;
            }

            return base.GetVaryByCustomString(context, custom);
        }
    }
}
</pre>
<p>Then, instruct Umbraco to use this class when launching the UmbracoApplication by changing the Global.asax file:</p>
<pre class="brush: plain; title: Global.asax; notranslate">
&amp;lt;%@ Application Codebehind=&quot;Global.cs&quot; Inherits=&quot;CreateIT.Web.Global&quot; Language=&quot;C#&quot; %&amp;gt;
</pre>
<p>Finally, setup an Output Cache profile in your Web.config:</p>
<pre class="brush: xml; title: Web.config; notranslate">
&amp;lt;system.web&amp;gt;
    (...)
    &amp;lt;caching&amp;gt;
      &amp;lt;outputCacheSettings&amp;gt;
        &amp;lt;outputCacheProfiles&amp;gt;
          &amp;lt;add enabled=&quot;true&quot; name=&quot;CreateIT.Actions&quot; duration=&quot;900&quot; location=&quot;Server&quot; varyByCustom=&quot;url&quot; varyByParam=&quot;*&quot; /&amp;gt;
        &amp;lt;/outputCacheProfiles&amp;gt;
      &amp;lt;/outputCacheSettings&amp;gt;
    &amp;lt;/caching&amp;gt;
&amp;lt;/system.web&amp;gt;
</pre>
<p>And decorate the chosen controllers (that you used to <a href="https://our.umbraco.org/documentation/reference/routing/custom-controllers">hijack the Umbraco routes</a>) with the Donut Output Cache attribute:</p>
<pre class="brush: csharp; title: HomepageController.cs; notranslate">
&#x5B;DonutOutputCache(CacheProfile = &quot;CreateIT.Actions&quot;)]
public class HomepageController : Umbraco.Web.Mvc.RenderMvcController
{
</pre>
<p>If you wish to exclude a certain part of your page from the cache, just create a Surface Controller action that returns a partial view and use the extension method provided by MVCDonutCaching for @Html.Action().</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2016/06/30/umbraco-and-donut-output-cache/">Umbraco and Donut Output Cache</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/andresantos/2016/06/30/umbraco-and-donut-output-cache/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Remote resource handling in a MVC website project</title>
		<link>https://blogit.create.pt/andresantos/2016/04/15/remote-resource-handling-in-a-mvc-website-project/</link>
					<comments>https://blogit.create.pt/andresantos/2016/04/15/remote-resource-handling-in-a-mvc-website-project/#comments</comments>
		
		<dc:creator><![CDATA[André Santos]]></dc:creator>
		<pubDate>Fri, 15 Apr 2016 18:01:43 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[action filter]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[mvc 5]]></category>
		<category><![CDATA[resources]]></category>
		<category><![CDATA[resx]]></category>
		<category><![CDATA[viewmodel]]></category>
		<category><![CDATA[visual studio]]></category>
		<category><![CDATA[webapi]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/andresantos/?p=1381</guid>

					<description><![CDATA[<p>I don&#8217;t have very fond memories of using .Net resource files (.resx) to handle the translation of static web page elements such as form labels. The Visual Studio resx editor was slow and using it to change resources that were more than a single line of text was a pain. I still have nightmares where [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2016/04/15/remote-resource-handling-in-a-mvc-website-project/">Remote resource handling in a MVC website project</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I don&#8217;t have very fond memories of using .Net resource files (.resx) to handle the translation of static web page elements such as form labels.</p>
<p>The Visual Studio <em>resx</em> editor was slow and using it to change resources that were more than a single line of text was a pain. I still have nightmares where I&#8217;m trying to edit complete email templates on a single field of a <em>resx</em> file&#8230;</p>
<p>The biggest problem with resource files, however, is that they are included in the project. This creates a problem for both the developers and the content managers, since the content managers are not able to change resources themselves, and the developers need to be changing resources files when they could be doing something productive.</p>
<p>The following will show a way to handle resource items in a MVC website project, where the resources can be stored wherever they are more easily handled by content managers: a SharePoint list, a database table, an excel file, or something completely different.</p>
<p><span id="more-6480"></span></p>
<p>The process is really simple:</p>
<ol>
<li>Select every resource item key needed to render the current page</li>
<li>Fetch the resources associated with the keys provided at the end of the current action</li>
<li>Populate a ViewModel property with the resource items</li>
<li>Use the resource items in their respective places in the markup</li>
</ol>
<h3>1 &#8211; Compile a list of resource keys</h3>
<p>Typically in a website, there are certain areas of the layout that are common across all pages, the header and the footer being the most prominent candidates. There are, however, other areas that can be reused across a smaller subset of pages such as a common banner in the FAQ pages. Taking this into account, we split the resource keys by groups, where each group (a list of strings) contains every resource key needed for that area and load them on each action, like so:</p>
<pre class="brush: csharp; title: Area resource keys; notranslate">
model.ContentResourceKeys = new List&amp;lt;string&amp;gt;();
model.ContentResourceKeys.AddRange(ResourceHelper.SOCIALNETWORK_RESOURCE_KEYS);
model.ContentResourceKeys.AddRange(ResourceHelper.HOTEL_BANNER_RESOURCE_KEYS);
model.ContentResourceKeys.AddRange(ResourceHelper.DOCUMENTS_RESOURCE_KEYS);
model.ContentResourceKeys.AddRange(ResourceHelper.GENERAL_FOOTER_BANNERS_RESOURCE_KEYS);
model.ContentResourceKeys.AddRange(ResourceHelper.LANDING_PAGE_SEARCH_AREA_RESOURCE_KEYS);
model.ContentResourceKeys.AddRange(ResourceHelper.LANDING_PAGE_MODULES_RESOURCE_KEYS);
</pre>
<p>The ContentResourceKeys property is defined in a <em>MasterModel</em> that is extended by every View Model used across the site, as can be seen here: <a href="http://blogit.create.pt/andresantos/2014/11/07/pre-set-common-viewmodel-preperties-before-action-result/">http://blogit.create.pt/andresantos/2014/11/07/pre-set-common-viewmodel-preperties-before-action-result/</a>. That post also describes the Action Filter that is executed after every action on the site and it&#8217;s the place where we append the footer and header resources to the <strong>ContentResourceKeys</strong> list:</p>
<pre class="brush: csharp; title: Master resouce keys; notranslate">
List&amp;lt;string&amp;gt; resourceKeys = new List&amp;lt;string&amp;gt;();
resourceKeys.AddRange(ResourceHelper.HEADER_RESOURCE_KEYS.ToList());
resourceKeys.AddRange(ResourceHelper.LOGIN_RESOURCE_KEYS.ToList());
resourceKeys.AddRange(ResourceHelper.FOOTER_RESOURCE_KEYS.ToList());
resourceKeys.AddRange(ResourceHelper.LOADING_RESOURCE_KEYS.ToList());
resourceKeys.AddRange(ResourceHelper.HEADER_COOKIES_RESOURCE_KEYS.ToList());

if (model.ContentResourceKeys != null &amp;amp;&amp;amp; model.ContentResourceKeys.Count &amp;gt; 0)
{
    resourceKeys.AddRange(model.ContentResourceKeys);
}
</pre>
<h3>2 &#8211; Fetch the resources</h3>
<p>After compiling the resource keys needed, we go on and fetch the associated resource items. The project where this was implemented used a WebAPI to serve all the page content and the resource items were no exception. So, we send a POST request to the resource WebAPI method and fetch the needed resources, for the user language, from the available backoffice (we used a SharePoint list).</p>
<pre class="brush: csharp; title: Fetch the keys; notranslate">
model.Resources.AddRange(ResourceFacade.ListResources(model.CurrentLanguageCode, resourceKeys));
</pre>
<h3>3 &#8211; Add them to the View Model</h3>
<p>The <em>Resources</em> property available in the View Model where we added the result of the call to the resources WebAPI method is a string Dictionary. Since C# Dictionaries don&#8217;t provide an <em>AddRange</em> method, we create an extension method that enables us to append resources to a dictionary that&#8217;s not empty. Moreover, with this extension method we are able to scrap duplicate keys:</p>
<pre class="brush: csharp; title: Dictionary AddRange extension method; notranslate">
public static class IDictionaryExtensions
{
    public static void AddRange&amp;lt;T, S&amp;gt;(this IDictionary&amp;lt;T, S&amp;gt; source, IDictionary&amp;lt;T, S&amp;gt; collection)
    {
        if (collection == null)
        {
            throw new ArgumentNullException(&quot;Collection is null&quot;);
        }

        foreach (var item in collection)
        {
            if (!source.ContainsKey(item.Key))
            {
                source.Add(item.Key, item.Value);
            }
        }
    }
}
</pre>
<h3>4 &#8211; Use the resources in the views</h3>
<p>Since every View Model extends the MasterModel, we are able to use the same property everywhere:</p>
<pre class="brush: xml; title: Usage in the view; notranslate">
&amp;lt;footer id=&quot;footer&quot;&amp;gt;
  &amp;lt;div class=&quot;container&quot;&amp;gt;
    &amp;lt;div class=&quot;newsletter&quot;&amp;gt;
      &amp;lt;h2&amp;gt;@Model.Resources&#x5B;&quot;Footer.Newsletter.Title&quot;]&amp;lt;/h2&amp;gt;
</pre>
<p>This way, we are able to manage the resource keys in a SharePoint list, or any other backoffice solution.</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2016/04/15/remote-resource-handling-in-a-mvc-website-project/">Remote resource handling in a MVC website project</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/andresantos/2016/04/15/remote-resource-handling-in-a-mvc-website-project/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Building an Umbraco 7 backoffice extension – Part III</title>
		<link>https://blogit.create.pt/andresantos/2015/11/30/building-an-umbraco-7-backoffice-extension-part-iii/</link>
					<comments>https://blogit.create.pt/andresantos/2015/11/30/building-an-umbraco-7-backoffice-extension-part-iii/#comments</comments>
		
		<dc:creator><![CDATA[André Santos]]></dc:creator>
		<pubDate>Mon, 30 Nov 2015 15:02:06 +0000</pubDate>
				<category><![CDATA[Umbraco]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[AngularJS]]></category>
		<category><![CDATA[Backoffice extension]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[package]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[Section]]></category>
		<category><![CDATA[WebMatrix]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/andresantos/?p=841</guid>

					<description><![CDATA[<p>The previous post (Part II) showed how to populate the content tree of a custom Umbraco backoffice section. This one presents a way for content managers to quickly handle each post pending approval, which means we&#8217;re going to create our own AngularJS controller and view. AngularJS is a MVC javascript framework mainly mantained by Google which [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/11/30/building-an-umbraco-7-backoffice-extension-part-iii/">Building an Umbraco 7 backoffice extension – Part III</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap dropcap3">T</span>he previous post (<a title="Building an Umbraco 7 backoffice extension - Part II" href="http://blogit.create.pt/andresantos/2015/11/23/building-an-umbraco-7-backoffice-extension-part-ii" target="_blank" rel="noopener">Part II</a>) showed how to populate the content tree of a custom Umbraco backoffice section. This one presents a way for content managers to quickly handle each post pending approval, which means we&#8217;re going to create our own AngularJS controller and view.</p>
<p><a title="Angular.JS" href="https://angularjs.org/" target="_blank" rel="noopener">AngularJS</a> is a MVC javascript framework mainly mantained by Google which primary goal is to aid in the development of single-page applications. It&#8217;s use on the Umbraco backoffice was the most notable change when Umbraco 7 was first released.</p>
<p>When the content manager selects one node pending approval we want him to be able to take three quick actions:</p>
<ul>
<li>get some information about that node;</li>
<li>quickly access it in the content section;</li>
<li>quickly publish it.</li>
</ul>
<p><span id="more-6478"></span></p>
<p>New Angular controllers and views must be placed in a specific Umbraco directory, following a specific directory structure:</p>
<ul>
<li>the main directory must reside at ~\App_Plugins\ApproveIt &#8211; the plugin name</li>
<li>the views and controllers must reside at App_Plugins\ApproveIt\backoffice\approvalTree\ &#8211; the tree controller name</li>
</ul>
<p>First things first:</p>
<p>Create a package manifest so that Umbraco knows there are new javascript files available, and place it in the root of App_Plugins\ApproveIt directory.</p>
<pre class="brush: plain; title: package.manifest; notranslate">

{
	javascript: &#x5B;
		'~/App_Plugins/ApproveIt/backoffice/approvalTree/edit.controller.js',
		'~/App_Plugins/ApproveIt/approval.resource.js'
	]
}

</pre>
<p>Then we must create the javascript files referenced by the manifest and the respective angular view.</p>
<pre class="brush: jscript; title: approval.resource.js; notranslate">

angular.module(&quot;umbraco.resources&quot;)
	.factory(&quot;approvalResource&quot;, function ($http) {
	    return {
	        getById: function (id) {
	            return $http.get(&quot;backoffice/ApproveIt/ApprovalApi/GetById?id=&quot; + id);
	        },
	        publish: function (id) {
	            return $http.post(&quot;backoffice/ApproveIt/ApprovalApi/PostPublish?id=&quot; + id);
	        }
	    };
	});
</pre>
<p>This javascript file provides a new <a title="Angular Service" href="https://docs.angularjs.org/guide/services">Angular Service</a> that can then be used by Angular controllers. This service is no more than a facade for the ApprovalApiController created on <a title="Building an Umbraco 7 backoffice extension - Part II" href="http://blogit.create.pt/andresantos/2015/11/23/building-an-umbraco-7-backoffice-extension-part-ii" target="_blank" rel="noopener">Part II</a>. The methods it connects to are created further on.</p>
<pre class="brush: jscript; title: edit.controller.js; notranslate">

angular.module(&quot;umbraco&quot;).controller(&quot;Approval.ApprovalEditController&quot;,
	function ($scope, $routeParams, approvalResource, notificationsService, navigationService) {

	    $scope.loaded = false;

	    if ($routeParams.id == -1) {
	        $scope.node = {};
	        $scope.loaded = true;
	    }
	    else {
	        approvalResource.getById($routeParams.id).then(function (response) {
	            $scope.node = response.data;
	            $scope.loaded = true;
	        });
	    }

	    $scope.publish = function (node) {
	        approvalResource.publish(node.Id).then(function (response) {
	            $scope.node = response.data;
	            $scope.contentForm.$dirty = false;
	            navigationService.syncTree({ tree: 'approvalTree', path: &#x5B;-1, -1], forceReload: true });
	            notificationsService.success(&quot;Success&quot;, node.Name + &quot; has been published&quot;);
	        });
	    };

	});

</pre>
<p>This Angular controller is called when the Approve It section is being used:</p>
<ul>
<li>If no node is selected (for instance, when we access this section for the first time) we do nothing;</li>
<li>If a node is selected (<em>$routeParams.id != -1</em>) we request the node details through our approval.resource service facade</li>
<li>Finally, the quick publish method is also implemented here. This methods calls the ApprovalResource service which then invokes the publish method of the ApprovalApiController.</li>
</ul>
<pre class="brush: xml; title: edit.html; notranslate">

&lt;form name=&quot;contentForm&quot;
      ng-controller=&quot;Approval.ApprovalEditController&quot;
      ng-show=&quot;loaded&quot;
      ng-submit=&quot;publish(node)&quot;
      val-form-manager&gt;
    &lt;umb-panel&gt;
        &lt;umb-header&gt;
            &lt;div class=&quot;span7&quot;&gt;
                &lt;umb-content-name placeholder=&quot;@placeholders_entername&quot;
                                  ng-model=&quot;node.Name&quot; /&gt;
            &lt;/div&gt;
            &lt;div class=&quot;span5&quot;&gt;
                &lt;div class=&quot;btn-toolbar pull-right umb-btn-toolbar&quot;&gt;
                    &lt;umb-options-menu ng-show=&quot;currentNode&quot;
                                      current-node=&quot;currentNode&quot;
                                      current-section=&quot;{{currentSection}}&quot;&gt;
                    &lt;/umb-options-menu&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/umb-header&gt;
        &lt;div class=&quot;umb-panel-body umb-scrollable row-fluid&quot;&gt;
            &lt;div class=&quot;tab-content form-horizontal&quot; style=&quot;padding-bottom: 90px&quot;&gt;
                &lt;div class=&quot;umb-pane&quot;&gt;

                    &lt;umb-control-group label=&quot;Name&quot; description=&quot;Content's name&quot;&gt;
                        &lt;input type=&quot;text&quot; class=&quot;umb-editor umb-textstring&quot; ng-model=&quot;node.Name&quot; required /&gt;
                    &lt;/umb-control-group&gt;

                    &lt;div class=&quot;umb-tab-buttons&quot; detect-fold&gt;
                        &lt;div class=&quot;btn-group&quot;&gt;
                            &lt;a class=&quot;btn&quot; ng-href=&quot;#/content/content/edit/{{node.Id}}&quot;&gt;
                                Show
                            &lt;/a&gt;
                        &lt;/div&gt;
                        &lt;div class=&quot;btn-group&quot;&gt;
                            &lt;button type=&quot;submit&quot; data-hotkey=&quot;ctrl+s&quot; class=&quot;btn btn-success&quot;&gt;
                                Publish
                            &lt;/button&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/umb-panel&gt;
&lt;/form&gt;
</pre>
<p>The node view presents the waiting approval page name and two buttons: one to quickly navigate to it&#8217;s details and another to publish it.</p>
<p>Finally, we need to update the ApprovalApiController.cs to include two new methods:</p>
<pre class="brush: csharp; title: ; notranslate">

public IContent GetById(int id)
{
    IContent content = ApplicationContext.Services.ContentService.GetById(id);
    return content;
}

public IContent PostPublish(int id)
{
    IContent node = ApplicationContext.Services.ContentService.GetById(id);
    ApplicationContext.Services.ContentService.Publish(node);

    return node;
}
</pre>
<ul>
<li>GetById: returns the node details to the angular view</li>
<li>PostPublish: publishes the node being viewed</li>
</ul>
<p>By following these steps we are able to create a complete new Umbraco backoffice section that provides us with a way to manage every site content waiting approval for publishing:</p>
<p><a href="http://blogit-create.com/wp-content/uploads/2015/11/approveit1.0.png"><img fetchpriority="high" decoding="async" class="aligncenter wp-image-1031" src="http://blogit-create.com/wp-content/uploads/2015/11/approveit1.0.png" alt="approveit1.0" width="600" height="410" /></a></p>
<p><em>If you want to see how this plugin as been evolving, you can check its source code in github: <a title="https://github.com/ViGiLnT/ApproveIt" href="https://github.com/ViGiLnT/ApproveIt" target="_blank" rel="noopener">https://github.com/ViGiLnT/ApproveIt</a>. You can also download the plugin ready to be installed at Umbraco&#8217;s extensions repository: <a title="https://our.umbraco.org/projects/backoffice-extensions/approve-it/" href="https://our.umbraco.org/projects/backoffice-extensions/approve-it/" target="_blank" rel="noopener">https://our.umbraco.org/projects/backoffice-extensions/approve-it/</a>. Other Umbraco 7 tutorials and information can be found here: <a title="http://umbraco.github.io/Belle" href="http://umbraco.github.io/Belle" target="_blank" rel="noopener">http://umbraco.github.io/Belle</a>.</em></p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/11/30/building-an-umbraco-7-backoffice-extension-part-iii/">Building an Umbraco 7 backoffice extension – Part III</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/andresantos/2015/11/30/building-an-umbraco-7-backoffice-extension-part-iii/feed/</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
			</item>
		<item>
		<title>Building an Umbraco 7 backoffice extension – Part II</title>
		<link>https://blogit.create.pt/andresantos/2015/11/23/building-an-umbraco-7-backoffice-extension-part-ii/</link>
					<comments>https://blogit.create.pt/andresantos/2015/11/23/building-an-umbraco-7-backoffice-extension-part-ii/#respond</comments>
		
		<dc:creator><![CDATA[André Santos]]></dc:creator>
		<pubDate>Mon, 23 Nov 2015 10:00:56 +0000</pubDate>
				<category><![CDATA[Umbraco]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[AngularJS]]></category>
		<category><![CDATA[Backoffice extension]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[package]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[Section]]></category>
		<category><![CDATA[WebMatrix]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/andresantos/?p=721</guid>

					<description><![CDATA[<p>The previous post (Building an Umbraco 7 backoffice extension – Part I), demonstrated how easy it is to create a new section in Umbraco&#8217;s backoffice. This post will show how we can populate this new section with meaningful content coming directly from the backoffice. To begin with, we must reference a few more assemblies (also [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/11/23/building-an-umbraco-7-backoffice-extension-part-ii/">Building an Umbraco 7 backoffice extension – Part II</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap dropcap3">T</span>he previous post (<a title="Building an Umbraco 7 backoffice extension – Part I" href="http://blogit.create.pt/andresantos/2015/11/16/building-an-umbraco-7-backoffice-extension-part-i/" target="_blank" rel="noopener">Building an Umbraco 7 backoffice extension – Part I</a>), demonstrated how easy it is to create a new section in Umbraco&#8217;s backoffice. This post will show how we can populate this new section with meaningful content coming directly from the backoffice.</p>
<p>To begin with, we must reference a few more assemblies (also available at the Umbraco&#8217;s installation <em>bin</em> directory):</p>
<ul>
<li>umbraco.dll</li>
<li>cms.dll</li>
<li>Umbraco.Core.dll</li>
<li>System.Web.Http</li>
<li>System.Net.Http.Formatting</li>
</ul>
<p>Then we can populate our section with a tree containing content awaiting approval. To accomplish this we must extend an Umbraco class named TreeController and decorate it with information about our <strong>Approve It</strong> plugin:</p>
<p><span id="more-6471"></span></p>
<pre class="brush: csharp; title: ; notranslate">

&#x5B;Tree(&quot;approveIt&quot;, &quot;approvalTree&quot;, &quot;Content for Approval&quot;)]
&#x5B;PluginController(&quot;ApproveIt&quot;)]
public class ApprovalTreeController : TreeController
{
   protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
   {
       var ctrl = new ApprovalApiController();
       var nodes = new TreeNodeCollection();

       IUser user = UmbracoContext.Security.CurrentUser;

       //check if we're rendering the root node's children
       if (id == Constants.System.Root.ToInvariantString())
       {
           foreach (IContent content in ctrl.GetAll(user))
           {
                var node = CreateTreeNode(
                    content.Id.ToString(),
                    &quot;-1&quot;,
                    queryStrings,
                    content.Name,
                    &quot;icon-document&quot;,
                    false);

               nodes.Add(node);
            }
       }

        return nodes;
    }

    protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
    {
        var menu = new MenuItemCollection();

        if (id == Constants.System.Root.ToInvariantString())
        {
            // root actions
            menu.Items.Add&lt;RefreshNode, ActionRefresh&gt;(
                ApplicationContext.Services.TextService.Localize(ActionRefresh.Instance.Alias));
        }

        return menu;
    }
}
</pre>
<p>To create a tree we must implement two methods:</p>
<ol>
<li>GetTreeNodes: in here we call the GetAll method of the ApprovalApiController that we&#8217;ll create in the following. This GetAll method returns a list of content waiting approval and we just iterate through it all and create tree nodes;</li>
<li>GetMenuForNode: presents a dropdown menu when we right click the root node to refresh the list.</li>
</ol>
<p>Next we create our custom WebAPI and name it ApprovalApiController. For it to be available in our site, we need only extend the UmbracoAuthorizedJsonController.</p>
<pre class="brush: csharp; title: ; notranslate">

&#x5B;PluginController(&quot;ApproveIt&quot;)]
public class ApprovalApiController : UmbracoAuthorizedJsonController
{
    public IList&lt;IContent&gt; UnpublishedContent { get; set; }

    public IEnumerable&lt;IContent&gt; GetAll(IUser user)
    {
        UnpublishedContent = new List&lt;IContent&gt;();

        IList&lt;IContent&gt; root = ApplicationContext.Services.ContentService.GetRootContent().ToList();

        foreach (IContent content in root)
        {
            GetNode(content);
        }

        return UnpublishedContent;
    }

    private void GetNode(IContent node)
    {
        if (!node.Published)
        {
            UnpublishedContent.Add(node);
        }

        foreach (IContent child in ApplicationContext.Services.ContentService.GetChildren(node.Id))
        {
            GetNode(child);
        }
    }
}

</pre>
<p>The GetAll method, iterates through all the nodes in the content section and finds the ones that are not published, that is, that are waiting approval.</p>
<p>With these two classes we are able to populate our section with a tree containing the content nodes that are waiting approval:</p>
<p><a href="http://blogit-create.com/wp-content/uploads/2015/11/approveit2.png"><img decoding="async" class="alignnone size-medium wp-image-791" src="http://blogit-create.com/wp-content/uploads/2015/11/approveit2-167x300.png" alt="approveit2" width="167" height="300" /></a></p>
<p>The next post in the series will show how to create the Angular view and controller that will enable content managers to actually manage the content waiting approval.</p>
<p><em>The Approve It extension can be found here: <a title="https://our.umbraco.org/projects/backoffice-extensions/approve-it/" href="https://our.umbraco.org/projects/backoffice-extensions/approve-it/" target="_blank" rel="noopener">https://our.umbraco.org/projects/backoffice-extensions/approve-it/</a>. The complete source code can be found here: <a title="https://github.com/ViGiLnT/ApproveIt" href="https://github.com/ViGiLnT/ApproveIt" target="_blank" rel="noopener">https://github.com/ViGiLnT/ApproveIt</a>.</em></p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/11/23/building-an-umbraco-7-backoffice-extension-part-ii/">Building an Umbraco 7 backoffice extension – Part II</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/andresantos/2015/11/23/building-an-umbraco-7-backoffice-extension-part-ii/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Building an Umbraco 7 backoffice extension &#8211; Part I</title>
		<link>https://blogit.create.pt/andresantos/2015/11/16/building-an-umbraco-7-backoffice-extension-part-i/</link>
					<comments>https://blogit.create.pt/andresantos/2015/11/16/building-an-umbraco-7-backoffice-extension-part-i/#comments</comments>
		
		<dc:creator><![CDATA[André Santos]]></dc:creator>
		<pubDate>Mon, 16 Nov 2015 15:34:25 +0000</pubDate>
				<category><![CDATA[Umbraco]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[AngularJS]]></category>
		<category><![CDATA[Backoffice extension]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[package]]></category>
		<category><![CDATA[Plugin]]></category>
		<category><![CDATA[WebMatrix]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/andresantos/?p=461</guid>

					<description><![CDATA[<p>Umbraco (https://umbraco.com/) is a lean and powerful CMS built on top of current .NET technologies and Javascript frameworks. It provides developers with a varied and simple to use collection of APIs, it is easy to customize and doesn&#8217;t get in the way between the coded markup and the HTML that is actually rendered. The next series [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/11/16/building-an-umbraco-7-backoffice-extension-part-i/">Building an Umbraco 7 backoffice extension &#8211; Part I</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap dropcap3">U</span>mbraco (<a href="https://umbraco.com/">https://umbraco.com/</a>) is a lean and powerful CMS built on top of current .NET technologies and Javascript frameworks. It provides developers with a varied and simple to use collection of APIs, it is easy to customize and doesn&#8217;t get in the way between the coded markup and the HTML that is actually rendered.</p>
<p>The next series of posts will show how to build plugins that extend the default Umbraco backoffice capabilities.</p>
<p><span id="more-6467"></span></p>
<h2>How is it built?</h2>
<p>The release of Umbraco&#8217;s version 7 presented a completely redesigned backoffice. It went from a  typical MVC website to a single page application built using <a title="AngularJS" href="https://angularjs.org/" target="_blank" rel="noopener">AngularJS</a>. Umbraco custom WebAPIs are used by Angular controllers in order to read information from the backoffice.</p>
<h2>How can we change it?</h2>
<p>A typical Umbraco 7 backoffice extension requires:</p>
<ol>
<li>Creation of Angular controllers, views and probably models;</li>
<li>Umbraco WebAPI extensions in order to access Umbraco&#8217;s backoffice and for custom server side development;</li>
<li>Extension of some Umbraco classes to customize common backoffice functionalities.</li>
</ol>
<h2>What to do?</h2>
<p>These series of posts will gradually present a way to build a new backoffice section, that will provide content managers with a centralized area where they can approve changes made to content. This backoffice extension was named<strong> Approve It</strong>. Natively, Umbraco sends an email to each content approver when changes are made to content nodes but, if an approver wants to see what nodes were updated just by visiting the backoffice, he must traverse every node to see which ones present a change waiting approval. This gets harder as the number of content nodes increase, and the changed nodes can be difficult to spot as we can see from the image below:</p>
<figure id="attachment_541" aria-describedby="caption-attachment-541" style="width: 285px" class="wp-caption aligncenter"><a href="http://blogit-create.com/wp-content/uploads/2015/11/changedcontent.png"><img decoding="async" class="wp-image-541 size-medium" src="http://blogit-create.com/wp-content/uploads/2015/11/changedcontent-285x300.png" alt="changedcontent" width="285" height="300" /></a><figcaption id="caption-attachment-541" class="wp-caption-text">The red arrows point to changed content &#8211; we can&#8217;t tell by the parent node if one of its children has changed</figcaption></figure>
<h2>Preparing the development environment</h2>
<ol>
<li>Download the latest and greatest version of Umbraco 7 (currently at v7.3.1): <a title="https://our.umbraco.org/contribute/releases/731/" href="https://our.umbraco.org/contribute/releases/731/ " target="_blank" rel="noopener">https://our.umbraco.org/contribute/releases/731/ </a></li>
<li>Download and setup WebMatrix: <a title="http://www.microsoft.com/web/webmatrix/" href="http://www.microsoft.com/web/webmatrix/" target="_blank" rel="noopener">http://www.microsoft.com/web/webmatrix/</a></li>
<li>Create a new web site in WebMatrix selecting the folder which contains Umbraco&#8217;s downloaded .zip file contents and go through the Setup
<ol>
<li>Select customize</li>
<li>Use a SQL CE database</li>
<li>Choose any starter kit (I chose Txt Starter Kit)</li>
</ol>
</li>
<li>Create a new Empty ASP.NET project in Visual Studio (I&#8217;m using VS2015) and add folders and core references for MVC</li>
</ol>
<h2>Creating a new backoffice section</h2>
<p>Creating a new section in Umbraco is easy:</p>
<ol>
<li>Add references to the following three assemblies available in the <em>bin</em> Umbraco directory:
<ul>
<li>businesslogic.dll</li>
<li>interfaces.dll</li>
<li>System.Web.Mvc.dll (need to replace the one Visual Studio automatically referenced)</li>
</ul>
</li>
<li>Create a new .cs file in the project with the following code:</li>
</ol>
<pre class="brush: csharp; title: ; notranslate">

&#x5B;Application(&quot;approveIt&quot;, &quot;ApproveIt&quot;, &quot;icon-people&quot;, 15)]
public class Section : IApplication
{
}

</pre>
<p>Simply implementing the IApplication interface, creates a new Umbraco backoffice section with the properties defined above.</p>
<p>The next step is translating the section name. If we don&#8217;t translate it, it will show up as [approveIt]. So, we must edit the file<em> \Umbraco\Config\Lang\en.xml</em> (for the UK version), and append the &#8220;approveIt&#8221; line in the &#8220;sections&#8221; area:</p>
<pre class="brush: xml; title: ; notranslate">

&amp;amp;amp;lt;area alias=&quot;sections&quot;&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;concierge&quot;&amp;amp;amp;gt;Concierge&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;content&quot;&amp;amp;amp;gt;Content&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;courier&quot;&amp;amp;amp;gt;Courier&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;developer&quot;&amp;amp;amp;gt;Developer&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;installer&quot;&amp;amp;amp;gt;Umbraco Configuration Wizard&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;media&quot;&amp;amp;amp;gt;Media&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;member&quot;&amp;amp;amp;gt;Members&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;newsletters&quot;&amp;amp;amp;gt;Newsletters&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;settings&quot;&amp;amp;amp;gt;Settings&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;statistics&quot;&amp;amp;amp;gt;Statistics&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;translation&quot;&amp;amp;amp;gt;Translation&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;users&quot;&amp;amp;amp;gt;Users&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;help&quot; version=&quot;7.0&quot;&amp;amp;amp;gt;Help&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;forms&quot;&amp;amp;amp;gt;Forms&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;analytics&quot;&amp;amp;amp;gt;Analytics&amp;amp;amp;lt;/key&amp;amp;amp;gt;
  &amp;amp;amp;lt;key alias=&quot;approveIt&quot;&amp;amp;amp;gt;Approve It&amp;amp;amp;lt;/key&amp;amp;amp;gt;
&amp;amp;amp;lt;/area&amp;amp;amp;gt;

</pre>
<p><strong>Note</strong>: for the new section to appear you must add it to the available sections your user can see. Furthermore, because of caching, you probably need to restart the website and open a browser window in private mode in order to see the new section.</p>
<p>This is how it looks:<br />
<a href="http://blogit-create.com/wp-content/uploads/2015/11/approveit.png"><img decoding="async" class="alignnone size-full wp-image-681" src="http://blogit-create.com/wp-content/uploads/2015/11/approveit.png" alt="approveit" width="112" height="271" /></a></p>
<p>So, we have a new section, but there&#8217;s nothing we can do with it. The next post in the series will show how we can add some content to this newly created Umbraco backoffice area.</p>
<p><em>The Approve It extension can be found here: <a title="https://our.umbraco.org/projects/backoffice-extensions/approve-it/" href="https://our.umbraco.org/projects/backoffice-extensions/approve-it/" target="_blank" rel="noopener">https://our.umbraco.org/projects/backoffice-extensions/approve-it/</a>. The complete source code can be found here: <a title="https://github.com/ViGiLnT/ApproveIt" href="https://github.com/ViGiLnT/ApproveIt" target="_blank" rel="noopener">https://github.com/ViGiLnT/ApproveIt</a>.</em></p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/11/16/building-an-umbraco-7-backoffice-extension-part-i/">Building an Umbraco 7 backoffice extension &#8211; Part I</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/andresantos/2015/11/16/building-an-umbraco-7-backoffice-extension-part-i/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Azure CDN, BlobStorage and ImageResizer</title>
		<link>https://blogit.create.pt/andresantos/2015/06/26/azure-cdn-blobstorage-and-imageresizer/</link>
					<comments>https://blogit.create.pt/andresantos/2015/06/26/azure-cdn-blobstorage-and-imageresizer/#comments</comments>
		
		<dc:creator><![CDATA[André Santos]]></dc:creator>
		<pubDate>Fri, 26 Jun 2015 15:48:38 +0000</pubDate>
				<category><![CDATA[CDN]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[azure]]></category>
		<category><![CDATA[azurereader2]]></category>
		<category><![CDATA[blob storage]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[cdn]]></category>
		<category><![CDATA[feature receiver]]></category>
		<category><![CDATA[image resizer]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[mvc 5]]></category>
		<category><![CDATA[sharepoint]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/andresantos/?p=371</guid>

					<description><![CDATA[<p>In a recent project, we had the need to serve website assets and website content images through a CDN network. Furthermore, each image presented in the website content must be available in multiple resolutions. We ended up serving both the assets and content images through the same Microsoft Azure CDN. This post describes the way we [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/06/26/azure-cdn-blobstorage-and-imageresizer/">Azure CDN, BlobStorage and ImageResizer</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span class="dropcap dropcap3">I</span>n a recent project, we had the need to serve website assets and website content images through a CDN network. Furthermore, each image presented in the website content must be available in multiple resolutions.</p>
<p>We ended up serving both the assets and content images through the same Microsoft Azure CDN. This post describes the way we implemented it.</p>
<p>First the easiest part. We have our site in a Azure Website, in order to serve the site&#8217;s assets through an Azure CDN we only needed to point the CDN to the website&#8217;s URL. So, if the website&#8217;s assets are located in something like <em>http://sitedomain/assets/*</em>, instead of referencing the assets through a relative url such as <em>/assets/*</em>, we just need to use the CDN url to serve them: <em>http://cdnurl/assets/*</em>. Through web.config transforms we are also able to use local assets during development and change to CDN only on the production environment.</p>
<p>Regarding the website image content, the solution has three phases.</p>
<p><span id="more-6465"></span></p>
<p><strong>1.</strong> The content is managed over SharePoint 2013, and the images are no exception. We created a feature receiver that uploads to Azure Blob Storage every time an image is uploaded, or modified,  on any Picture Library, and then save its known CDN URL in the library item.</p>
<pre class="brush: csharp; title: ; notranslate">
string cdnUrl = Configuration.Settings.ImageUploader.CdnUrl;
string containerStr = Configuration.Settings.ImageUploader.Container;

// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting(IMAGES_STORAGE_CONNECTION_STRING));

// Create the blob client.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

// Retrieve reference to a container.
CloudBlobContainer container = blobClient.GetContainerReference(containerStr);

// Creates the container if it doesn't exist
container.CreateIfNotExists();

// Set the blobs access level to public
container.SetPermissions(new BlobContainerPermissions
{
    PublicAccess = BlobContainerPublicAccessType.Blob
});

string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file.Name);
string extension = Path.GetExtension(file.Name);

fileNameWithoutExtension += &quot;-&quot; + DateTime.Now.Ticks;

// Build file name to upload
string fileName = string.Format(&quot;{0}/{1}{2}&quot;, file.Item.ParentList.Title.ToLower(), fileNameWithoutExtension.Replace(&quot; &quot;, &quot;-&quot;), extension).ToLower();

// Retrieve reference to a blob named &quot;file.name&quot;.
CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);

// Create or overwrite the &quot;file.name&quot; blob with contents from a local file.
using (var fileStream = file.OpenBinaryStream())
{
    blockBlob.UploadFromStream(fileStream);

    // Update SP item CDN URL column
    string finalUrl = HttpUtility.UrlPathEncode(string.Format(&quot;{0}/{1}/{2}?{3}&quot;, cdnUrl, containerStr, fileName, IMAGE_RESIZER_FIRST_PARAMETER));

    properties.ListItem&#x5B;Configuration.Settings.SP_COLUMN_CDN_URL] = finalUrl.ToLower();
    properties.ListItem.SystemUpdate(false);
}
</pre>
<p><strong>2.</strong> Having the images in a Azure Blob Storage, we decided to use the <a title="Image Resizer ASP.NET Library and Image Server" href="http://imageresizing.net/" target="_blank" rel="noopener">Image Resizer ASP.NET Library</a> and its <a title="AzureReader2 BlobStorage Plugin" href="http://imageresizing.net/docs/v4/plugins/azurereader2" target="_blank" rel="noopener">AzureReader2 plugin</a> to resize and serve images directly through our Azure Website. The implementation is as easy as installing a nuget package: Image Resizer. After configuring the Blob Storage endpoint, and setting the prefix (we use ~/azure), we are able to access the images through an URL such as: <em>http://sitedomain/azure/image/path/in/blobstorage</em>. With this working we can resize and modify the images in any way available to us with the Image Resizer library and described in detail here: <a title="http://imageresizing.net/docs/v4" href="http://imageresizing.net/docs/v4" target="_blank" rel="noopener">http://imageresizing.net/docs/v4</a>.</p>
<p><strong>3.</strong> Since our CDN is pointing directly to our Azure Website, we can use the CDN URL instead of the azure website one. For instance, the fake image I talked about the previous bullet can be accessed with the following URL: <em>http://cdnurl/azure/image/path/in/blobstorage</em>. This way, the first time someone opens an image through the CDN URL it is handled by the Image Resizer library. The  second time, CDN has already cached it and distributed it around the world and it is served much faster!</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/06/26/azure-cdn-blobstorage-and-imageresizer/">Azure CDN, BlobStorage and ImageResizer</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/andresantos/2015/06/26/azure-cdn-blobstorage-and-imageresizer/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>A Multi-Tenant Website in MVC</title>
		<link>https://blogit.create.pt/andresantos/2015/06/18/a-multi-tenant-website-in-mvc/</link>
					<comments>https://blogit.create.pt/andresantos/2015/06/18/a-multi-tenant-website-in-mvc/#comments</comments>
		
		<dc:creator><![CDATA[André Santos]]></dc:creator>
		<pubDate>Thu, 18 Jun 2015 17:28:18 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[multi-tenancy]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[mvc 5]]></category>
		<category><![CDATA[viewengine]]></category>
		<guid isPermaLink="false">http://blogit.create.pt/andresantos/?p=301</guid>

					<description><![CDATA[<p>In my most recent project we had the need to build multiple websites that would share some common functionalities but would be installed in separate servers and would be available in different domains. We were using .NET 4.5 and MVC 5 and there was no out of the box solution for this problem. Throughout my [&#8230;]</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/06/18/a-multi-tenant-website-in-mvc/">A Multi-Tenant Website in MVC</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In my most recent project we had the need to build multiple websites that would share some common functionalities but would be installed in separate servers and would be available in different domains.</p>
<p>We were using .NET 4.5 and MVC 5 and there was no <em>out of the box</em> solution for this problem. Throughout my search I came upon <a title="Setting Up an MVC4 Multi-Tenant Site v1.1" href="http://pt.scribd.com/doc/140181293/Setting-Up-an-MVC4-Multi-Tenant-Site-v1-1" target="_blank" rel="noopener">this</a> paper and we took some ideas from it. This post describes the main changes to a <em>normal</em> website project we had to make in order to make this work.</p>
<p><span id="more-6464"></span></p>
<p>First of all we had to create a <strong>Core</strong> MVC5 project that we set up to be used as a Class Library. We must also reference <em>System.Runtime.Remoting</em>. This Core project contains the shared functionality that is used on each site and as such is referenced by them. This project follows the usual MVC architecture where the only difference is that the views must have the build action set to <strong>embedded resource</strong>. It&#8217;s set to this build action so that the site projects can get them from the Core project assembly upon launch. This is accomplished through a custom view engine that is used on each site:</p>
<pre class="brush: csharp; title: ; notranslate">
public class EmbeddedResourceViewEngine : RazorViewEngine
{
    public EmbeddedResourceViewEngine()
    {
        ViewLocationFormats = new&#x5B;]
        {
            // Code below will search the client before core.
            &quot;~/Views/{1}/{0}.aspx&quot;, &quot;~/Views/{1}/{0}.ascx&quot;, &quot;~/Views/Shared/{0}.aspx&quot;,
            &quot;~/Views/Shared/{0}.ascx&quot;, &quot;~/Views/{1}/{0}.cshtml&quot;,
            &quot;~/Views/{1}/{0}.vbhtml&quot;, &quot;~/Views/Shared/{0}.cshtml&quot;,
            &quot;~/Views/Shared/{0}.vbhtml&quot;, &quot;~/Views/Shared/Partials/{0}.cshtml&quot;,
            &quot;~/Views/Shared/Partials/{0}.vbhtml&quot;, 

            // The embedded view will be copied to a tmp folder
            // using a similar structure to the View Folder
            &quot;~/CoreViews/Views/{1}/{0}.cshtml&quot;,
            &quot;~/CoreViews/Views/Shared/{0}.cshtml&quot;,
            &quot;~/CoreViews/Views/{1}/{0}.vbhtml&quot;
        };

        PartialViewLocationFormats = new&#x5B;]
        {
            &quot;~/Views/Shared/{0}.cshtml&quot;, // Client first
            &quot;~/CoreViews/Views/Shared/{0}.cshtml&quot;,
            &quot;~/Views/Shared/Partials/{0}.cshtml&quot;,
        };

        // Handle all areas generically
        AreaViewLocationFormats = new&#x5B;]
            {
                &quot;~/Areas/{2}/Views/{1}/{0}.cshtml&quot;, 

                // client Specific
                &quot;~/CoreViews/Areas/{2}/Views/{1}/{0}.cshtml&quot;, 

                // Pick up core  if no client
            };

        AreaPartialViewLocationFormats = new&#x5B;]
            {
                &quot;~/Areas/{2}/Views/Shared/{0}.cshtml&quot;, 

                // Client Specific – not used in demo
                &quot;~/CoreViews/Areas/{2}/Views/Shared/{0}.cshtml&quot;

                // Pick up core  if no client
            };

        SaveAllViewsToTempLocation();
    }

    private static void SaveAllViewsToTempLocation()
    {
        IEnumerable&lt;string&gt; resources =
            typeof(EmbeddedResourceViewEngine).Assembly.GetManifestResourceNames()
                .Where(name =&gt; name.EndsWith(&quot;.cshtml&quot;));
        foreach (string res in resources)
        {
            SaveViewToTempLocation(res);
        }
    }

    private static void SaveViewToTempLocation(string res)
    {
        // Get the file path to manipulate and the fileName for re-addition later.
        string&#x5B;] resArray = res.Split('.');

        // rebuild split to get the paths.
        string filePath = string.Join(&quot;/&quot;, resArray, 0, resArray.Count() - 2) + &quot;/&quot;;
        string fileName = string.Join(&quot;.&quot;, resArray, resArray.Count() - 2, 2);

        // replace name of project, with temp file to save to.
        string rootPath = filePath.Replace(&quot;ProjectName&quot;, &quot;~/CoreViews&quot;);

        // Set in line with the server folder...
        rootPath = HttpContext.Current.Server.MapPath(rootPath);
        if (!Directory.Exists(rootPath))
        {
            Directory.CreateDirectory(rootPath);
        }

        // Save the file to the new location.
        string saveToLocation = rootPath + fileName;
        Stream resStream = typeof(EmbeddedResourceViewEngine).Assembly.GetManifestResourceStream(res);

        System.Runtime.Remoting.MetadataServices.MetaData.SaveStreamToFile(resStream, saveToLocation);
    }
}
</pre>
<p>With this Core project in place we can now have multiple website projects that use some common features defined in Controllers, Models, Views or any other custom code, such as custom attributes. We can also redefine Actions for a specific website, or use a different view for each website.</p>
<p>Views and PartialViews defined in the Core project must explicitly inherit from <em>System.Web.Mvc.WebViewPage</em> because they no longer know their base type from the Web.Config.</p>
<p>The project that motivated the use of this solution required the creation of 4 sites. One that contained all functionality (<em>common</em>), and 3 others that only contained an homepage similar to the common site (with one exception). For this reason we created 2 website projects:</p>
<ul>
<li>Common &#8211; Used to create the main site that contains all functionality</li>
<li>Specific &#8211; Used to create 3 sites where only one contains a specific homepage</li>
</ul>
<p>Instead of creating 3 specific website projects we decided to create only one because we can define what type of site it is by configuration when we publish it (using different publish profiles and web.config transforms).</p>
<p>With this setup, the Core project basically contains the Homepage Models,Views and Controller and some other core functionalities. The Common site project contains almost everything, except an homepage which it inherits from the Core project. The specific site project redefines the homepage for 1 of the 3 sites it represents and that also inherit from the Core project.</p>
<p>Without this setup we would have to replicate the homepage and other core functionalities across 4 website projects.</p>
<p>&nbsp;</p>
<p>The post <a href="https://blogit.create.pt/andresantos/2015/06/18/a-multi-tenant-website-in-mvc/">A Multi-Tenant Website in MVC</a> appeared first on <a href="https://blogit.create.pt">Blog IT</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blogit.create.pt/andresantos/2015/06/18/a-multi-tenant-website-in-mvc/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>
