<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>All you need is code</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/akirsman/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/akirsman/feed.xml</link>
    <description>Well, not really. But you get the idea.</description>
    <lastBuildDate>Wed, 17 Apr 2019 03:13:05 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Demo script for Docker in Azure against Linux and Windows containers</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/demo-script-for-docker-in-azure-against-linux-and-windows-containers</link>
      <pubDate>Sat, 26 Sep 2015 17:20:00 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2015/09/26/demo-script-for-docker-in-azure-against-linux-and-windows-containers/</guid>
      <description><![CDATA[These are the overall steps I followed in order to create a Docker demo in Azure, both against Linux...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size: small;">These are the overall steps I followed in order to create a Docker demo in Azure, both against Linux and Windows containers. They are not very detailed, mostly because on the Windows side things are in preview. But the pointers should help.</span></p>
<p><span style="font-size: small;">Basically, steps allow you to install Docker client in Windows, setup a Linux host VM in Azure and finally setup a Windows docker host VM in Azure.</span></p>
<p><span style="font-size: small;">Of course, you could arrive at these steps looking here and there. What I'm doing here is to outline the steps I followed in a single place.</span></p>
<p><span style="font-size: small;">Suggestions welcome.</span></p>
<h4><span style="font-size: small;">First comes the Docker Windows Client installation:</span></h4>
<ul>
<li>
<p><span style="font-size: small;">install Docker Windows client</span></p>
</li>
</ul>
<pre><span style="font-size: small;">from https://chocolatey.org/</span></pre>
<pre><span style="font-size: x-small;"><br /></span><span style="font-size: small;">@powershell -NoProfile -ExecutionPolicy Bypass -Command `</span></pre>
<pre><span style="font-size: small;">"iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" &amp;&amp; SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin</span></pre>
<pre><span style="font-size: small;"><br /></span></pre>
<pre><span style="font-size: small;">from https://chocolatey.org/packages/docker</span><br /><span style="font-size: small;">choco install docker</span></pre>
<ul>
<li>
<p><span style="font-size: small;">install openssl client on Windows</span></p>
</li>
</ul>
<pre><span style="font-size: small;">https://slproweb.com/products/Win32OpenSSL.html</span></pre>
<h4><span style="font-size: small;">Next in line is the creation of the Docker Linux Host on Azure:</span></h4>
<ul>
<li>
<p><span style="font-size: small;">create Azure Linux VM docker engine host</span></p>
</li>
</ul>
<pre><span style="font-size: small;">create an Ubuntu 14.04 Linux VM in the Azure Portal.</span></pre>
<ul>
<li>
<p><span style="font-size: small;">install docker in the host</span></p>
</li>
</ul>
<pre><span style="font-size: small;">https://docs.docker.com/installation/ubuntulinux/#installation</span></pre>
<ul>
<li>
<p><span style="font-size: small;">protect Docker in the Linux host</span></p>
</li>
</ul>
<pre><span style="font-size: small;">https://docs.docker.com/articles/https/</span><br /><span style="font-size: small;">Copy ca.pem, cert.pem and key.pem to Windows client's %USERPROFILE%\.docker</span></pre>
<ul>
<li>
<p><span style="font-size: small;">check communication between Docker client and Linux host</span></p>
</li>
</ul>
<pre><span style="font-size: small;">docker --tls -H tcp://&lt;Linux Docker host FQDN, *.cloudapp.net&gt;:4243 info</span></pre>
<ul>
<li>
<p><span style="font-size: small;">have some fun</span></p>
</li>
</ul>
<pre><span style="font-size: small;">set DOCKER_HOST=tcp://&lt;Linux Docker host FQDN, *.cloudapp.net&gt;:4243</span></pre>
<pre><span style="font-size: small;">set DOCKER_TLS_VERIFY=TRUE</span><br /><br /><span style="font-size: small;">docker run ubuntu:14.04 /bin/echo 'Hello world'</span><br /><span style="font-size: small;">docker run -t -i /bin/bash</span><br /><span style="font-size: small;">docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"</span><br /><span style="font-size: small;">docker ps</span><br /><span style="font-size: small;">docker logs &lt;id&gt;</span><br /><span style="font-size: small;">docker stop &lt;id&gt;</span><br /><span style="font-size: small;">docker ps</span><br /><span style="font-size: small;">docker version (notice Windows client &amp; Linux server)</span></pre>
<ul>
<li>
<p><span style="font-size: small;">Host a web app on the Docker Linux Host</span></p>
</li>
</ul>
<pre><span style="font-size: small;">docker run -d -P training/webapp python app.py</span><br /><span style="font-size: small;">docker logs &lt;id&gt;</span><br /><span style="font-size: small;">&lt;open endpoint in Azure, same public &amp; private ports&gt;</span><br /><span style="font-size: small;">docker run -d -p &lt;endpoint&gt;:5000 training/webapp python app.py</span><br /><span style="font-size: small;">navigate to https://&lt;Linux Docker host FQDN, *.cloudapp.net&gt;:&lt;endpoint&gt;/</span></pre>
<h4><span style="font-size: small;">Finally, we create the Windows Docker Host on Azure, a Windows Server 2016 Technical Preview version.</span></h4>
<p><span style="font-size: small;">For this part, we could create this VM manually, configure its endpoints and other plumbing.</span></p>
<p><span style="font-size: small;">But, Visual Studio provides a better experience by allowing to create a new host from the "Publish..." dialog (announcement <a title="here" href="https://weblogs.asp.net/scottgu/announcing-windows-server-2016-containers-preview" target="_parent">here</a>).</span></p>
<ul>
<li>
<p><span style="font-size: small;">publish web app to Windows container, creating the Windows container VM host in the process</span></p>
</li>
</ul>
<pre><span style="font-size: small;">install https://www.microsoft.com/en-us/download/details.aspx?id=48738</span><br /><span style="font-size: small;">install https://visualstudiogallery.msdn.microsoft.com/0f5b2caa-ea00-41c8-b8a2-058c7da0b3e4</span></pre>
<pre><span style="font-size: small;">create a new MVC app</span></pre>
<pre><span style="font-size: small;">publish project to Docker Container option, create new VM</span></pre>
<ul>
<li>
<p><span style="font-size: small;">connect to Windows Server 2016 docker host</span></p>
</li>
</ul>
<pre><span style="font-size: small;">set DOCKER_HOST=tcp://&lt;Windows Docker host FQDN, *.cloudapp.net&gt;:2376</span><br /><span style="font-size: small;">set DOCKER_TLS_VERIFY=FALSE</span><br /><span style="font-size: small;">copy Windows certs to %USERPROFILE%\.docker\</span><br /><span style="font-size: small;">docker ps</span></pre>
<h2><span style="font-size: small;">Appendix - Interesting Links</span></h2>
<p>&nbsp;</p>
<p><span style="font-size: small;">https://msdn.microsoft.com/virtualization/windowscontainers/containers_welcome</span></p>
<p><span style="font-size: small;">https://weblogs.asp.net/scottgu/announcing-windows-server-2016-containers-preview</span></p>
<p><span style="font-size: small;">https://msopentech.com/blog/2014/08/15/getting_started_docker_on_microsoft_azure/</span></p>
<p><span style="font-size: small;">https://azure.microsoft.com/en-us/blog/docker-client-for-windows-is-now-available/</span></p>
<p><span style="font-size: small;">https://docs.docker.com/windows/started/</span></p>
<p><span style="font-size: small;">https://docs.docker.com/installation/windows/ (this is for creating a Linux Container Host on top of virtualbox in Windows)</span></p>
<p><span style="font-size: small;">https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-docker-swarm/</span></p>
<p><span style="font-size: small;">https://docs.docker.com/installation/ubuntulinux/#installation (same as above; for Docker installation on the Ubuntu 14.04 created in Azure)</span></p>
<p><span style="font-size: small;">https://docs.docker.com/articles/https/ (for protecting the Docker endpoints)</span></p>
<p><span style="font-size: small;">https://msdn.microsoft.com/virtualization/windowscontainers/quick_start/manage_docker (interesting, but it doesn't include how to manage the Windows Docker Host from the Windows Docker client)</span></p>
<p><span style="font-size: small;">https://msdn.microsoft.com/virtualization/windowscontainers/about/work_in_progress (important, since there is stuff you can't do - yet)</span></p>
<p><span style="font-size: small;">https://msdn.microsoft.com/en-us/library/azure/mt125409.aspx</span></p>
<p><span style="font-size: small;">https://blogs.msdn.com/b/stevelasker/archive/2015/08/24/why-doesn-t-docker-tools-for-visual-studio-2015-show-the-new-button.aspx (I hit this issue, and installing the right versions was the solution)</span></p>]]></content:encoded>
    </item>
    <item>
      <title>Dynamics CRM 2011: restricting multiple attachments to an entity’s Notes</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/dynamics-crm-2011-restricting-multiple-attachments-to-an-entitys-notes</link>
      <pubDate>Sat, 01 Jun 2013 05:10:00 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2013/06/01/dynamics-crm-2011-restricting-multiple-attachments-to-an-entitys-notes/</guid>
      <description><![CDATA[Requirement was very simple: to be able to add a single document to the notes control of an entity...]]></description>
      <content:encoded><![CDATA[<p>Requirement was very simple: to be able to add a single document to the notes control of an entity &ndash; but no more than one document.</p>
<p>There may be a more elegant way to accomplish this, but the way I found was to</p>
<ul>
<li>go to customize the entity's form</li>
<li>configure Tab to open when the form opens</li>
<li>add jQuery to the form's libraries list.</li>
<li>put the following code in a web resource and add that web resource to the form's libraries list:</li>
</ul>
<p style="padding-left: 60px;"><span style="font-family: courier new,courier;">function EnforceSingleNote() {</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; EnforceSingleNoteImpl();</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; setInterval("EnforceSingleNoteImpl()", 500);</span><br /><span style="font-family: courier new,courier;">}</span></p>
<p style="padding-left: 60px;"><span style="font-family: courier new,courier;">function EnforceSingleNoteImpl() {</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; var notesInput = $(document.getElementById('notescontrol').contentWindow.document.getElementById('noteTextInput-create'));</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; var notesContainer = notesInput.closest('.notesContainerScrollDiv');</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; var nAttachments = notesContainer.find('.attachment').length;</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; var notesTextBoxDivs = notesContainer.find('.notesTextBoxDiv');</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; if (nAttachments &gt; 0 &amp;&amp; notesTextBoxDivs.length &gt; 0) {</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; notesTextBoxDivs.hide();</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; }</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; var notesTextCreate = notesContainer.find('.ms-crm-Note-Text-create');</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; if (nAttachments &gt; 0 &amp;&amp; notesTextCreate.length &gt; 0) {</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; notesTextCreate.hide();</span><br /><span style="font-family: courier new,courier;"> &nbsp;&nbsp;&nbsp; }</span><br /><span style="font-family: courier new,courier;">}</span></p>
<ul>
<li>add&nbsp;an OnLoad event for the Notes control pointing to the EnforceSingleNote function in the web resource created before.</li>
</ul>
<p>Basically, once every half a second,&nbsp;it checks whether there&rsquo;s a note with an attachment. If there is, then it hides de controls where user can enter a new note.</p>
<p>You may be wondering why setInterval() instead of setTimeout(). Well, when you add a file, Dynamics creates on the fly a new &ldquo;Enter a Note&rdquo; textarea. The click handler needs to be set to that new textarea, too. Running this code once every second should be enough, and its perf hit should be minimal.</p>]]></content:encoded>
    </item>
    <item>
      <title>SharePoint: To Update or not to Update?</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/sharepoint-to-update-or-not-to-update</link>
      <pubDate>Wed, 12 Dec 2012 03:50:54 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2012/12/12/sharepoint-to-update-or-not-to-update/</guid>
      <description><![CDATA[Based on Update-SPSolution reference,     The Update-SPSolution cmdlet upgrades a deployed...]]></description>
      <content:encoded><![CDATA[<p>Based on <a href="https://technet.microsoft.com/en-us/library/ff607724">Update-SPSolution</a> reference,</p>  <blockquote>   <p>The Update-SPSolution cmdlet upgrades a deployed SharePoint solution in the farm. Use this cmdlet <strong>only if a new solution contains the same set of files and features as the deployed solution</strong>. If files and features are different, the solution must be retracted and redeployed by using the Uninstall-SPSolution and Install-SPSolution cmdlets, respectively.</p> </blockquote>  <p>Given that Update-SPSolution is way more efficient than the alternative, it pays to see whether it can be leveraged.</p>  <p>So here is a PowerShell script that takes the full path of a .wsp file and a SPSolution, if it exists already in SharePoint, and compares one against the other to determine whether it is possible to get away by updating only instead of uninstalling, removing, adding and installing.</p>  <p>As you can see, it makes use of expand.exe to open the .wsp (cab) file. Therefore it assumes a specific format of its output. (Could be clearly made more robust by replacing expand.exe’s call with unpacking routine.)</p>  <p>&#160;</p>  <p><font size="2" face="Courier New">function Get-WSPFileNames([string]$fullPathWSP)</font></p>  <p><font size="2" face="Courier New">{</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; [string[]]$e = &amp; expand.exe -D $fullPathWSP</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; $f = [System.Array]::CreateInstance([object], $e.Length - 5)</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; [System.Array]::Copy($e, 3, $f, 0, $e.Length - 5)</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; $f</font></p>  <p><font size="2" face="Courier New">}</font></p>  <p><font size="2" face="Courier New">function Compare-WSPFileNames([string[]]$f1, [string[]]$f2)</font></p>  <p><font size="2" face="Courier New">{</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; if($f1.Length -ne $f2.Length)</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; {</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw &quot;Arrays expected to have the same length.&quot;</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; }</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; for($i = 0; $i -lt $f1.Length; $i++)</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; {</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160;&#160;&#160;&#160;&#160; $eq = $f1[0].Split(' ')[$f1[0].Split(' ').Length -1] -eq $f2[0].Split(' ')[$f2[0].Split(' ').Length -1]</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(!$eq)</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160;&#160;&#160;&#160;&#160; {</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return $false</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; }</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; return $true</font></p>  <p><font size="2" face="Courier New">}</font></p>  <p><font size="2" face="Courier New">function CompareWSPFiles([string]$fullPathWSP1, [string]$fullPathWSP2)</font></p>  <p><font size="2" face="Courier New">{&#160;&#160;&#160; </font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; # read WSP's</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; $f1 = Get-WSPFileNames $fullPathWSP1</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; $f2 = Get-WSPFileNames $fullPathWSP2</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; # number of files</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; if($f1.Length -ne $f2.Length)</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; {</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160;&#160;&#160;&#160;&#160; return $false</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; }</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; # files</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; if(-not (Compare-WSPFileNames $f1 $f2))</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; {</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160;&#160;&#160;&#160;&#160; return $false</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; }</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; # seems they are same</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; return $true</font></p>  <p><font size="2" face="Courier New">}</font></p>  <p><font size="2" face="Courier New">function CompareWSP($fullPathWSP, $solution)</font></p>  <p><font size="2" face="Courier New">{</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; if($solution -eq $null -or !$solution.Deployed)</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; {</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160;&#160;&#160;&#160;&#160; return $false</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; }</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; $tmp = [System.IO.Path]::GetTempFileName()</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; $solution.SolutionFile.SaveAs($tmp)</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; $same = CompareWSPFiles $fullPathWSP $tmp</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; return $same</font></p>  <p><font size="2" face="Courier New">}</font></p>  <p><font size="2" face="Courier New">function CanUpdateSolution($fullPathWSP, $solution)</font></p>  <p><font size="2" face="Courier New">{</font></p>  <p><font size="2" face="Courier New">&#160;&#160;&#160; return CompareWSP $fullPathWSP $solution</font></p>  <p><font size="2" face="Courier New">}</font></p>]]></content:encoded>
    </item>
    <item>
      <title>Creating Visual Studio Templates</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/creating-visual-studio-templates</link>
      <pubDate>Thu, 11 Oct 2012 18:18:00 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2012/10/11/creating-visual-studio-templates/</guid>
      <description><![CDATA[This article explains how to create Visual Studio project and item templates not from scratch, but...]]></description>
      <content:encoded><![CDATA[<p>This article explains how to create Visual Studio project and item templates not from scratch, but starting from prototypes that model the things we want to create with those templates.</p>
<p>For more info, go <a href="https://msdn.microsoft.com/en-us/library/ms247121.aspx">here</a>.</p>
<h2>1. <a name="_Toc335725150"></a>Creating Prototype Projects or Items</h2>
<p>First of all, create the piece (project or item) that resembles the thing you want to get created starting from the template we are going to create.</p>
<p>That&rsquo;s simply like</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/2577.image_3171180C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/2577.image_5F00_3171180C.png"><img width="617" height="149" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4278.image_thumb_4130AD10.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4278.image_5F00_thumb_5F00_4130AD10.png" border="0" /></a></p>
<p>&nbsp;</p>
<p>Then, export the template (we are going to use the exported template as a shortcut to build our Visual Studio template package):</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/0143.image_2F246983.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/0143.image_5F00_2F246983.png"><img width="343" height="308" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/6866.image_thumb_29694FDD.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/6866.image_5F00_thumb_5F00_29694FDD.png" border="0" /></a></p>
<p>&nbsp;</p>
<p>You can also start from an existing project you consider suitable as a starting point.</p>
<p>Now, depending on whether a project or item template is going to be created, follow the appropriate instructions for Project or Item Templates below.</p>
<h2>2. <a name="_Toc335725151"></a>Creating Visual Studio Project Templates</h2>
<p>Choose Project template,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/1665.image_47A3E106.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/1665.image_5F00_47A3E106.png"><img width="500" height="428" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/0207.image_thumb_24273CA1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/0207.image_5F00_thumb_5F00_24273CA1.png" border="0" /></a></p>
<p>Then complete desired data and make sure that template is NOT imported into Visual Studio, and an explorer windows is opened in the output files folder:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4571.image_36EB9A7E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4571.image_5F00_36EB9A7E.png"><img width="503" height="439" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4520.image_thumb_526076B4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4520.image_5F00_thumb_5F00_526076B4.png" border="0" /></a></p>
<p>&nbsp;</p>
<p>Keep that window opened:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/6663.image_77BA4455.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/6663.image_5F00_77BA4455.png"><img width="667" height="186" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/1031.image_thumb_1141D183.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/1031.image_5F00_thumb_5F00_1141D183.png" border="0" /></a></p>
<p>The generated template is a project template, and as such, it includes all the files of the prototype project. Regardless, we will be using it as a starting point for both Item and Project templates, because it is easier to remove items than adding them manually if we had exported as an item template.</p>
<h2>3. <a name="_Toc335725153"></a>Creating Template Project</h2>
<p>Time to create our template project. Go to File | New | Project&hellip;, then choose the highlighted options in the picture , plus an appropriate name for the template:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/8507.image_69F76280.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/8507.image_5F00_69F76280.png"><img width="618" height="426" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/3755.image_thumb_1B067754.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/3755.image_5F00_thumb_5F00_1B067754.png" border="0" /></a></p>
<p>Next step is to delete all the files from the just generated project. Select and delete them all:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/3731.image_6A7FCD10.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/3731.image_5F00_6A7FCD10.png"><img width="451" height="477" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4024.image_thumb_015E9E8D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4024.image_5F00_thumb_5F00_015E9E8D.png" border="0" /></a></p>
<p>Go to the template .zip generated before, select those files, copy them,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/8306.image_16104B73.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/8306.image_5F00_16104B73.png"><img width="685" height="368" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/7271.image_thumb_1A3A2338.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/7271.image_5F00_thumb_5F00_1A3A2338.png" border="0" /></a></p>
<p>Then navigate to the project template&rsquo;s folder and paste them:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/1070.image_47E7A063.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/1070.image_5F00_47E7A063.png"><img width="551" height="350" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/3247.image_thumb_2CCED155.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/3247.image_5F00_thumb_5F00_2CCED155.png" border="0" /></a></p>
<p>To add files into the project template (that is, to the project whose files we deleted before), go to Visual Studio, click on &ldquo;Show All Files&rdquo;, select the files copied from the .zip and include them in the project:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/2318.image_163BE734.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/2318.image_5F00_163BE734.png"><img width="419" height="435" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/6545.image_thumb_676608C4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/6545.image_5F00_thumb_5F00_676608C4.png" border="0" /></a></p>
<p>Now, delete those files that we are not interested to include in the project (remember, we started from an Project template export, therefore it may include extra items we may not want to be part in the case of an item template).</p>
<p>Then, select all files but the .vstemplate, and set their Build Action to &ldquo;Content&rdquo;:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4061.image_6A1B97EA.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4061.image_5F00_6A1B97EA.png"><img width="190" height="484" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/5516.image_thumb_7564AC27.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/5516.image_5F00_thumb_5F00_7564AC27.png" border="0" /></a></p>
<p>Select the .vstemplate and select its Build Action to &ldquo;VSTemplate&rdquo;:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/3750.image_4F5ED604.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/3750.image_5F00_4F5ED604.png"><img width="192" height="484" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/0777.image_thumb_6D5CE3F8.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/0777.image_5F00_thumb_5F00_6D5CE3F8.png" border="0" /></a></p>
<p>As a verification step, build the project. You should end up with a Visual Studio template&rsquo;s .zip file:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/7612.image_56DA1FA4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/7612.image_5F00_56DA1FA4.png"><img width="669" height="95" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/3554.image_thumb_3435E129.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/3554.image_5F00_thumb_5F00_3435E129.png" border="0" /></a></p>
<h2>4. <a name="_Toc336506676"></a>Customizing Template Files</h2>
<p>Now, it&rsquo;s time to customize the files in the template project.</p>
<h3><a name="_Toc336506677"></a>.vstemplate</h3>
<p>The .vstemplate file defines the attributes of the template, plus where the files will be copied when an item is created based on the template, among other things. Customizations are made based on the VSTemplate element schema here: <a href="https://msdn.microsoft.com/en-us/library/31cdwx28(v=vs.100).aspx">https://msdn.microsoft.com/en-us/library/31cdwx28(v=vs.100).aspx</a></p>
<p>Main things to customize in this file are:</p>
<p>- Name, Description, etc: TemplateData</p>
<p>- Template type: &lt;VSTemplate &hellip; Type="Item"&gt; or &lt;VSTemplate &hellip; Type="Project"&gt;, depending on the type of template being created.</p>
<p>- ProjectItem&rsquo;s to be created in the project that adds this item: &lt;ProjectItem ReplaceParameters="true" TargetFileName="$fileinputname$\Elements.xml"&gt;Elements.xml&lt;/ProjectItem&gt;</p>
<p>o Beware that the ReplaceParameters attribute both controls whether parameters are replaced in the file and in the TargetFileName attribute.</p>
<p>- References to be added to the project that adds this item: References</p>
<p>Couple tips here:</p>
<p>- Try to not use folders in here, &lt;ProjectItem &hellip;&gt;NO FOLDER HERE&lt;/ProjectItem&gt;, because it creates that folder&rsquo;s name in the project that is adding the item.</p>
<p>- Different .vstemplate&rsquo;s files that are referenced from the same .vsixmanifest must be named differently. Otherwise, last one added wins and that&rsquo;s the only one that shows up.</p>
<h3><a name="_Toc336506678"></a>.csproj</h3>
<p>To customize the .csproj file (the .csproj that defines the template project) right click on the template project, unload the project,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/3542.image_17D8793C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/3542.image_5F00_17D8793C.png"><img width="414" height="501" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4572.image_thumb_154F477E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4572.image_5F00_thumb_5F00_154F477E.png" border="0" /></a></p>
<p>Then edit the project,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/8877.image_054FDCED.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/8877.image_5F00_054FDCED.png"><img width="608" height="195" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/7838.image_thumb_6E415BB0.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/7838.image_5F00_thumb_5F00_6E415BB0.png" border="0" /></a></p>
<p>Main things to customize in this file are:</p>
<p>- Folder in which templates will appear in the &ldquo;New project&rdquo; or &ldquo;Add new item&rdquo; dialogs: &lt;VSTemplate &hellip;&gt;</p>
<p>&lt;OutputSubPath&gt;My Company\SharePoint 2010&lt;/OutputSubPath&gt;</p>
<p>&lt;SubType&gt;Designer&lt;/SubType&gt;</p>
<p>&lt;/VSTemplate&gt;</p>
<h3><a name="_Toc336506679"></a>Content Files</h3>
<p>The files that are generated when an item or project is created are those that were referenced as ProjectItem in the .vstemplate file.</p>
<p>As we saw it, those files were marked as Content, and ReplaceParameters="true" was set for them.</p>
<p>That implies that Visual Studio will generate those files replacing parameters specified in here: <a href="https://msdn.microsoft.com/en-us/library/eehb4faa(VS.80).aspx">https://msdn.microsoft.com/en-us/library/eehb4faa(VS.80).aspx</a></p>
<p>What to customize in each content file, is up to you. You can go from hard-coding namespaces, item names, classes etc, to, more recommended, customize those items so newly created project or item gets along well with other items and projects in the solution.</p>
<p>Typically, these are the parameters that are to be used for this task:</p>
<p>- <b>$safeitemname$</b></p>
<p><strong>- </strong><strong>$safeprojectname$</strong><strong></strong></p>
<p><strong>- </strong><strong>$rootnamespace$</strong><strong></strong></p>
<p>- <strong>$GUIDN$ (for ex. $GUID1$, $GUID2$, etc)</strong></p>
<h2>5. <a name="_Toc335725154"></a>Creating Visual Studio Package Project</h2>
<p>Finally, it&rsquo;s time to create the project that generates de Visual Studio extension that imports the templates to Visual Studio.</p>
<p>To do that, first create a VSIX solution:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4278.image_5E71A0DF.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4278.image_5F00_5E71A0DF.png"><img width="497" height="345" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/7633.image_thumb_49DC2B94.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/7633.image_5F00_thumb_5F00_49DC2B94.png" border="0" /></a></p>
<p>In order to make sure VSIX and its dependencies build correctly, we are going to add the existing template project to the extension project. To do so, right click on the extension solution and add the existing project,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/2816.image_51577534.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/2816.image_5F00_51577534.png"><img width="573" height="279" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/7711.image_thumb_7A02B4B0.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/7711.image_5F00_thumb_5F00_7A02B4B0.png" border="0" /></a></p>
<p>In the extension solution, double-click the manifest, so designer opens, and click &ldquo;Add Content&rdquo; on that designer view,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/1854.image_7CD7CDC9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/1854.image_5F00_7CD7CDC9.png"><img width="363" height="435" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/3157.image_thumb_07487C1D.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/3157.image_5F00_thumb_5F00_07487C1D.png" border="0" /></a></p>
<p>Now reference the template project added just added to the solution, referencing the right project and its right type (Project or Item Template, whatever corresponds):</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/6354.image_7BEF4212.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/6354.image_5F00_7BEF4212.png"><img width="458" height="324" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/2047.image_thumb_7AAAA933.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/2047.image_5F00_thumb_5F00_7AAAA933.png" border="0" /></a></p>
<p>As a verification step, build the solution, you should see have a .vsix being generated after its dependency project:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/7888.image_05878A7C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/7888.image_5F00_05878A7C.png"><img width="761" height="178" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/1050.image_thumb_21D4CC9C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/1050.image_5F00_thumb_5F00_21D4CC9C.png" border="0" /></a></p>
<p>&nbsp;</p>
<h2>6. <a name="_Toc335725155"></a>Debugging</h2>
<p>When a extension project is opened, and debugging is started (Debug | Continue, or F5), Visual Studio launches an instance of itself but called &ldquo;Experimental Instance&rdquo;,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/5342.image_1BBA5376.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/5342.image_5F00_1BBA5376.png"><img width="664" height="235" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/1616.image_thumb_71AA2F80.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/1616.image_5F00_thumb_5F00_71AA2F80.png" border="0" /></a></p>
<p>This allows debbuging of components that run in Visual Studio, as extensions or add-ins.</p>
<p>We need now to check whether the template is actually available. So go to create a new project and see if it&rsquo;s there:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/6165.image_730AFFFA.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/6165.image_5F00_730AFFFA.png"><img width="661" height="220" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4087.image_thumb_76C8A4CA.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4087.image_5F00_thumb_5F00_76C8A4CA.png" border="0" /></a></p>
<p>Use the template by creating a project or adding a new item, depending on the template type to be verified, and check whether it is correct or needs to be fixed.</p>
<h2>7. <a name="_Toc335725156"></a>Installing the Extension</h2>
<p>Now the the extension was verified, it&rsquo;s time to install it. Execute the .vsix file from an Administrator command prompt, then when prompted, install,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/1854.image_65289432.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/1854.image_5F00_65289432.png"><img width="481" height="336" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/6607.image_thumb_6A2AD1E1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/6607.image_5F00_thumb_5F00_6A2AD1E1.png" border="0" /></a></p>
<p>And make sure it installed correctly, and restart Visual Studio,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/0486.image_18E064B7.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/0486.image_5F00_18E064B7.png"><img width="487" height="342" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4101.image_thumb_374AA5A0.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4101.image_5F00_thumb_5F00_374AA5A0.png" border="0" /></a></p>
<p>Launch Visual Studio again and in Tools | Extension Manager&hellip;, you should see the extension being installed,</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/4645.image_7346C188.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/4645.image_5F00_7346C188.png"><img width="562" height="202" title="image" style="display: inline; background-image: none;" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/1513.image_thumb_3FDAD85F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/1513.image_5F00_thumb_5F00_3FDAD85F.png" border="0" /></a></p>
<p>Now, you are ready to use the templates in Visual Studio, not only in an Experimental Instance of it.</p>]]></content:encoded>
    </item>
    <item>
      <title>Custom Single Sign-On Scenario in SharePoint 2010</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/custom-single-sign-on-scenario-in-sharepoint-2010</link>
      <pubDate>Wed, 09 May 2012 04:09:00 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2012/05/09/custom-single-sign-on-scenario-in-sharepoint-2010/</guid>
      <description><![CDATA[&nbsp;
Here&rsquo;s the scenario: a user navigates to a site whose homepage includes a link to the...]]></description>
      <content:encoded><![CDATA[<p>&nbsp;</p>
<p>Here&rsquo;s the scenario: a user navigates to a site whose homepage includes a link to the SharePoint 2010 site. Goal is, user gets into SharePoint 2010 site with no credentials prompt. Both source and destination sites use SQL as the membership repository.</p>
<p>Easy, right? First implementation was with an http module, getting the SQL user from the cookie user brings when coming from the source site, and calling SetAuthCookie(that username). With some tricks, like figuring out that username has to be on the form of "0#.f|membershipprovider-name|username", user got into the site.</p>
<p>But later on, some problems surfaced,</p>
<ul>
<li>user could not logout correctly,</li>
<li>authorization issues i.e., users not being able to access some resources they were supposed to.</li>
</ul>
<p>So I set out to investigate, and first thing I found&nbsp; is that cookie generated by SetAuthCookie() (.ASPXAUTH) was different that of the default one you get when logged into SharePoint 2010 out of the box (FedAuth). Luckily, found the reason of this in <a href="https://blogs.technet.com/b/speschka/archive/2010/07/21/writing-a-custom-forms-login-page-for-sharepoint-2010-part-1.aspx">this great article</a> from the great Steve. That is</p>
<ul>
<li>In SharePoint 2010, FBA users are claims users, therefore</li>
<li>SetAuthCookie() doesn&rsquo;t do anymore.</li>
</ul>
<p>Let&rsquo;s now outline the solution. We need to pieces here to make this work</p>
<ul>
<li>a http module that, if it sees an cookie that shows user is authenticated, sets cookie for the SharePoint 2010 site and</li>
<li>a custom membership provider that, if a cookie value comes in place of a password, then validates the user</li>
</ul>
<p>(<span style="color: #ff0000;" color="#ff0000">BIG warning here</span> &ndash; we are passing to the custom membership provider, in the http module, as password the cookie value. This means, mitigations should be put in place in order to prevent users to, for example, put a long cookie value in a custom login page. For example, we could hash the cookie in the http module and verify that hash in the custom membership provider.)</p>
<p>First of all, we need to set the authentication cookie the right way &ndash; that is, leveraging SharePoint claims API&rsquo;s. I chose to do that from an http module&rsquo;s AuthenticateRequest:</p>
<p>public void Init(HttpApplication context) <br />{ <br />&nbsp;&nbsp;&nbsp; context.AuthenticateRequest += new EventHandler(OnAuthenticateRequest); <br />}</p>
<p>void OnAuthenticateRequest(object sender, EventArgs e) <br />{ <br />&nbsp;&nbsp;&nbsp; HttpContext context = HttpContext.Current; <br />&nbsp;&nbsp;&nbsp; HttpRequest request = context.Request; <br />&nbsp;&nbsp;&nbsp; HttpCookie cookieVal = request.Cookies[Constants.SAPCookieName]; <br />&nbsp;&nbsp;&nbsp; if (request.IsAuthenticated) <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return; <br />&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; if (cookieVal != null &amp;&amp; !String.IsNullOrEmpty(cookieVal.Value)) <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string username = GetUsernameFromCookie(cookieVal); <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!String.IsNullOrEmpty(username)) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetFBACookie(request, username, cookieVal.Value); <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp; } <br />}</p>
<p>private static void SetFBACookie(HttpRequest r, string username, string cookieValue) <br />{ <br />&nbsp;&nbsp;&nbsp; using (SPSite s = new SPSite(r.Url.AbsoluteUri)) <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPIisSettings iis = s.WebApplication.IisSettings[SPUrlZone.Default]; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPFormsAuthenticationProvider fap = iis.FormsClaimsAuthenticationProvider; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SecurityToken token = SPSecurityContext.SecurityTokenForFormsAuthentication(new Uri(r.Url.GetComponents(UriComponents.Scheme | UriComponents.HostAndPort, UriFormat.Unescaped), UriKind.Absolute), fap.MembershipProvider, fap.RoleProvider, username, cookieValue); <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPFederationAuthenticationModule fam = SPFederationAuthenticationModule.Current; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fam.SetPrincipalAndWriteSessionToken(token); <br />&nbsp;&nbsp;&nbsp; } <br />}</p>
<p>Logic here is pretty much simple and, other than replacing SetAuthCookie() by SetFBACookie(), nothing has changed in the http module from 2007 to 2010. Basically, it looks from a cookie and, if a user can be inferred from it, then sets cookie based on it.</p>
<p>Let&rsquo;s go to the membership provider part. When SecurityTokenForFormsAuthentication(uri, membershipProv, roleProv, <strong>user</strong>, <strong>password</strong>) is called, our custom membership provider&rsquo;s ValidateUser(<strong>user</strong>, <strong>password</strong>) will be called. In there, we take the password as the cookie value. If we can infer a user from it, then we validate that user. If not, validation is delegated to whatever base class we choose to inherit from (LdapMembershipProvider, SqlMembershipProvider, etc), or could have chosen a better design by inject it at runtime.</p>
<p>Here&rsquo;s the main part of the membership provider:</p>
<p>public override bool ValidateUser(string username, string password) <br />{ <br />&nbsp;&nbsp;&nbsp; // check parameters <br />&nbsp;&nbsp;&nbsp; if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (false); <br />&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; // validate we can get user from cookie and equals the user passed in <br />&nbsp;&nbsp;&nbsp; string cookieValue = password; <br />&nbsp;&nbsp;&nbsp; string userFromCookie = null; <br />&nbsp;&nbsp;&nbsp; userFromCookie = GetUsernameFromCookie(cookieValue); <br />&nbsp;&nbsp;&nbsp; if (!string.IsNullOrEmpty(userFromCookie) &amp;&amp; userFromCookie.Equals(username, StringComparison.OrdinalIgnoreCase)) <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (true); <br />&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; return base.ValidateUser(username, password); <br />}</p>
<p>Now that we have the components, we need to deploy them.</p>
<ul>
<li>in the SharePoint 2010 web application&rsquo;s web.config we are trying to access we need to configure</li>
<ul>
<li>the http module</li>
<li>the membership provider</li>
</ul>
<li>in the SecurityTokenServiceApplication IIS site&rsquo;s web.config we need to configure</li>
<ul>
<li>the membership provider</li>
</ul>
</ul>
<p>The http module entry looks like this:</p>
<p>&lt;system.webServer&gt; <br />&lt;modules runAllManagedModulesForAllRequests="true"&gt; <br />... <br />&lt;add name="MyHttpModule" type="Contoso.Providers.CustomHttpModule, Contoso, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456701234567" &hellip; /&gt; <br />&lt;/modules&gt;</p>
<p>and the membership provider entry looks like this:</p>
<p>&lt;system.web&gt; <br />&lt;membership ...&gt; <br />&lt;providers ...&gt; <br />&lt;add name="MyMembershipProvider&rdquo; type="Contoso.Providers.CustomMembershipProvider, Contoso, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456701234567" &hellip; /&gt;</p>]]></content:encoded>
    </item>
    <item>
      <title>PowerShell script for generating a Visual Studio profile</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/powershell-script-for-generating-a-visual-studio-profile</link>
      <pubDate>Wed, 21 Dec 2011 11:58:00 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2011/12/21/powershell-script-for-generating-a-visual-studio-profile/</guid>
      <description><![CDATA[&nbsp;
Idea is to start the following script and, at the same time, run a Visual Studio load test...]]></description>
      <content:encoded><![CDATA[<p>&nbsp;</p>
<p>Idea is to start the following script and, at the same time, run a Visual Studio load test against that same web app. If everything works well, at the exit of the script you should end up with a Visual Studio profile (.vsp) that you can analyze.</p>
<p>It requires <a title="Visual Studio 2010 Performance Tools" href="https://www.microsoft.com/download/en/details.aspx?id=23205">Visual Studio 2010 Performance Tools</a> installed.</p>
<p># <br /># VSProfile.ps1 <br /># Sample call: .\VSProfile.ps1 -AppPoolName "SharePoint - 80" -ProfileOutputPath C:\Temp\Profile.vsp -ProfileDurationSeconds 120 <br /># <br />param($AppPoolName, $ProfileOutputPath, $ProfileDurationSeconds)</p>
<p># set perf tools dir <br />$env:perftoolsdir = "$env:ProgramFiles (x86)\Microsoft Visual Studio 10.0\Team Tools\Performance Tools\x64"</p>
<p># enable sampling <br />&amp; "$env:perftoolsdir\vsperfclrenv" /sampleon</p>
<p># get PID of the w3wp process to profile <br />&amp; "$env:windir\System32\inetsrv\appcmd.exe" list wp | %{ if($_ -match "WP `"(?&lt;pid&gt;\d+).*(applicationPool:$AppPoolName)") { $w3wpid = [System.Int32]::Parse($matches["pid"]) } } <br />write-host "Will profile process ID $w3wpid"</p>
<p># attach to that process <br />&amp; "$env:perftoolsdir\vsperfcmd" /crosssession /start:sample /attach:$w3wpid /output:$ProfileOutputPath</p>
<p># wait <br />[System.Threading.Thread]::Sleep([System.Int32]::Parse($ProfileDurationSeconds)*1000)</p>
<p># detach <br />&amp; "$env:perftoolsdir\vsperfcmd" /detach:$w3wpid <br />&amp; "$env:perftoolsdir\vsperfcmd" /shutdown:5</p>
<p># wait for vsp file to get totally flushed <br />[System.Threading.Thread]::Sleep(20000)</p>]]></content:encoded>
    </item>
    <item>
      <title>Utility method to get SharePoint 2010 blog posts and their comments</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/utility-method-to-get-sharepoint-2010-blog-posts-and-their-comments</link>
      <pubDate>Fri, 02 Dec 2011 16:34:00 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2011/12/02/utility-method-to-get-sharepoint-2010-blog-posts-and-their-comments/</guid>
      <description><![CDATA[Say no more. Here it is. Self-explanatory.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public class...]]></description>
      <content:encoded><![CDATA[<p>Say no more. Here it is. Self-explanatory.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public class <strong>Post</strong><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string Title;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string Body;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public DateTime Published;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public IEnumerable&lt;Comment&gt; <strong>Comments</strong>;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public class <strong>Comment</strong><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string Title;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string Body;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string Author;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public DateTime Created;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static IEnumerable&lt;Post&gt; GetBlogPosts()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // TODO: get blog's site collection's url<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (SPSite s = new SPSite("<a href="https://virtualcasa1/sites/blogs">https://server/sites/myblog</a>"))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPWeb w = s.RootWeb;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // get posts<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List&lt;Post&gt; posts = new List&lt;Post&gt;();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPList pl = w.Lists["posts"];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPQuery pq = new SPQuery() { ViewFields = "&lt;FieldRef Name='Title' /&gt;&lt;FieldRef Name='Body' /&gt;&lt;FieldRef Name='PublishedDate' /&gt;" };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (SPListItem pli in pl.GetItems(pq))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // get post<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; posts.Add(new Post() { Body = (string)pli["Body"], Title = (string)pli["Title"], Published = (DateTime)pli["PublishedDate"], Comments = new List&lt;Comment&gt;() });<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // get comments<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPList cl = w.Lists["comments"];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPQuery cq = new SPQuery() { ViewFields = "&lt;FieldRef Name='Title' /&gt;&lt;FieldRef Name='Body' /&gt;&lt;FieldRef Name='Author' /&gt;&lt;FieldRef Name='Created' /&gt;&lt;FieldRef Name='PostTitle' /&gt;" };<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (SPListItem cli in cl.GetItems(cq))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPFieldLookupValue plookup = new SPFieldLookupValue((string)cli["PostTitle"]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (from Post p in posts where p.Title == plookup.LookupValue select p).FirstOrDefault&lt;Post&gt;().Comments.Add(new Comment() { Body = (string)cli["Body"], Title = (string)cli["Title"], Author = (string)cli["Author"], Created = (DateTime)cli["Created"] });<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // return<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (posts);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>]]></content:encoded>
    </item>
    <item>
      <title>ScriptLink not accepting a query string :-(</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/scriptlink-not-accepting-a-query-string</link>
      <pubDate>Wed, 30 Nov 2011 10:36:00 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2011/11/30/scriptlink-not-accepting-a-query-string/</guid>
      <description><![CDATA[&nbsp;
A typical practice for performance is to add a revision tag (e.g.,...]]></description>
      <content:encoded><![CDATA[<p>&nbsp;</p>
<p>A typical practice for performance is to add a revision tag (e.g., &ldquo;file.js?rev=123&rdquo;) to .js files so a new build, hence a new revision, brings the file to the browser cache.</p>
<p>Tried doing that with ScriptLink&rsquo;s? No way.</p>
<p>Issue is, due to the processing ScriptLink goes through, we cannot add revision to those files. Say a resulting line is</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;SharePoint:ScriptLink language="javascript" name="core.js?rev=634580691801235029" OnDemand="true" runat="server" /&gt;</p>
<p>it gonna fail here (from Reflector)</p>
<p>internal static string ComputeFileHash(string filePath) <br />{ <br />&nbsp;&nbsp;&nbsp; FileStream stream = null; <br />&nbsp;&nbsp;&nbsp; string str = null; <br />&nbsp;&nbsp;&nbsp; try <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new FileIOPermission(FileIOPermissionAccess.Read, filePath).Assert(); &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (SecurityContext.RevertToSelf()) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str = ComputeStreamHash(stream); <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (stream != null) <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stream.Close(); <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return str; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp; catch <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw; <br />&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp; return str; <br />}</p>
<p>with a stack like this:</p>
<p>System.ArgumentException: Illegal characters in path.&nbsp;&nbsp; <br /> System.Security.Permissions.FileIOPermission.HasIllegalCharacters(String[] str)&nbsp;&nbsp;&nbsp; <br /> System.Security.Permissions.FileIOPermission.AddPathList(FileIOPermissionAccess access, AccessControlActions control, String[] pathListOrig, Boolean checkForDuplicates, Boolean needFullPath, Boolean copyPathList)&nbsp;&nbsp;&nbsp; <br /> System.Security.Permissions.FileIOPermission..ctor(FileIOPermissionAccess access, String path)&nbsp;&nbsp;&nbsp; <br /> Microsoft.SharePoint.Utilities.SPUtility.ComputeFileHash(String filePath)&nbsp;&nbsp;&nbsp; <br /> Microsoft.SharePoint.Utilities.SPUtility.ComputeFileHashForLocalizedLayoutsUrl(Int32 lcidUI, String name, Boolean&amp; fUseThreadCulture)&nbsp;&nbsp;&nbsp; <br /> Microsoft.SharePoint.Utilities.SPUtility.MakeBrowserCacheSafeLayoutsUrl(String name, Boolean localizable)&nbsp;&nbsp;&nbsp; <br /> Microsoft.SharePoint.WebControls.ScriptLink.ScriptLinkInfo.ToScriptUrl(Page page)&nbsp;&nbsp;&nbsp; <br /> Microsoft.SharePoint.WebControls.ScriptLink.ScriptLinkInfo.ToScriptBlock(Page page)&nbsp;&nbsp;&nbsp; <br /> Microsoft.SharePoint.WebControls.ScriptLink.RenderScriptBlock(Page page, Boolean afterUI)&nbsp;&nbsp;&nbsp; <br /> Microsoft.SharePoint.WebControls.ScriptLink.Render(HtmlTextWriter writer)&nbsp;&nbsp;&nbsp; </p>
<p>Local repro,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string s = @"c:\blah\" + CultureInfo.CurrentCulture.LCID.ToString(CultureInfo.InvariantCulture) + "/" + "core.js?rev=123"; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileIOPermission fp = new FileIOPermission(FileIOPermissionAccess.Append, s); </p>
<p>So, stick with &lt;script&gt; elements if need this performance trick. Another workaround is to keep using ScriptLink but on files deployed under /_layouts/, in which case this codepath is not traversed.</p>]]></content:encoded>
    </item>
    <item>
      <title>Working-around “SocialCommentWebPart Title property not working”</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/working-around-socialcommentwebpart-title-property-not-working</link>
      <pubDate>Sun, 27 Nov 2011 10:48:00 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2011/11/27/working-around-socialcommentwebpart-title-property-not-working/</guid>
      <description><![CDATA[&nbsp;
A client recently hit the issue described in this post, SocialCommentWebPart Title property...]]></description>
      <content:encoded><![CDATA[<p>&nbsp;</p>
<p>A client recently hit the issue described in this post, <a title="https://community.office365.com/en-us/f/153/p/14718/67787.aspx" href="https://community.office365.com/en-us/f/153/p/14718/67787.aspx">SocialCommentWebPart Title property not working</a>.</p>
<p>Basically, the Title set in the person.aspx to the SocialCommentWebPart is not being honored e.g., &lt;SPSWC:SocialCommentWebPart <em>Title="New Title"</em> &hellip;&gt;</p>
<p>One workaround I found is to edit the web part in shared mode and set its title. Because&nbsp;this was needed to be configured during deployment, I wrote the following snippet:</p>
<p>$s = new-object Microsoft.SharePoint.SPSite("<a href="https://server/sites/mysitehost&quot;)">https://server/sites/mysitehost")</a> <br />$page = $s.RootWeb.GetFile("person.aspx") <br />if($page.CheckOutType -eq 'None') <br />{ <br />&nbsp; $page.CheckOut() <br />} <br />$wpm = $web.GetLimitedWebPartManager("person.aspx", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared) <br />$wp = $null <br />$wpm.WebParts | where {$_.WebBrowsableObject.GetType().Name -eq 'SocialCommentWebPart'} | % {$wp = $_} <br />$wp.Title = "Nuevo titulo" <br />$wpm.SaveChanges($wp) <br />$page.CheckIn("Title Changed") <br />$s.Close()</p>]]></content:encoded>
    </item>
    <item>
      <title>C# code for configuring “Navigation Editing and Sorting”</title>
      <link>https://docs.microsoft.com/archive/blogs/akirsman/c-code-for-configuring-navigation-editing-and-sorting</link>
      <pubDate>Fri, 18 Nov 2011 16:45:00 GMT</pubDate>
      <dc:creator><![CDATA[Ariel Kirsman]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/akirsman/2011/11/18/c-code-for-configuring-navigation-editing-and-sorting/</guid>
      <description><![CDATA[&nbsp;
Here&rsquo;s some code for doing what the title states. It deletes whatever is there, then...]]></description>
      <content:encoded><![CDATA[<p>&nbsp;</p>
<p>Here&rsquo;s some code for doing what the title states. It deletes whatever is there, then adds a couple of global and current navigation nodes respectively.</p>
<p>string siteUrl = "<a href="https://virtualcasa1/sites/blank/default.aspx&quot;;">https://server/sites/blank/default.aspx";</a> <br />using (SPSite site = new SPSite(siteUrl)) <br />{ <br />&nbsp;&nbsp;&nbsp; PublishingWeb pw = PublishingWeb.GetPublishingWeb(site.RootWeb); <br />&nbsp;&nbsp;&nbsp; int count = pw.Navigation.GlobalNavigationNodes.Count; <br />&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; count ; i++) <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pw.Navigation.GlobalNavigationNodes[0].Delete(); <br />&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp; SPNavigationNode prev; <br />&nbsp;&nbsp;&nbsp; prev = pw.Navigation.GlobalNavigationNodes.AddAsFirst(new SPNavigationNode("Mi Sitio", siteUrl)); <br />&nbsp;&nbsp;&nbsp; pw.Navigation.GlobalNavigationNodes.Add(new SPNavigationNode("Mi Perfil", siteUrl), prev); <br />&nbsp;&nbsp;&nbsp; count = pw.Navigation.CurrentNavigationNodes.Count; <br />&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; count; i++) <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pw.Navigation.CurrentNavigationNodes[0].Delete(); <br />&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp; prev = pw.Navigation.CurrentNavigationNodes.AddAsFirst(new SPNavigationNode("Informaci&oacute;n", siteUrl)); <br />&nbsp;&nbsp;&nbsp; pw.Navigation.CurrentNavigationNodes.Add(new SPNavigationNode("Sobre M&iacute;", siteUrl), prev); <br />&nbsp;&nbsp;&nbsp; pw.Update(); <br />}</p>
<p>This translates into this in the UI:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/5342.image_1957AA9B.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/5342.image_5F00_1957AA9B.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/47/52/metablogapi/0243.image_thumb_30367C17.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-47-52-metablogapi/0243.image_5F00_thumb_5F00_30367C17.png" width="724" height="133" /></a></p>]]></content:encoded>
    </item>
  </channel>
</rss>