tag:blogger.com,1999:blog-57553138009061422622024-03-06T00:31:23.040-06:00Simply a programmer// a software development blog by Tim JonesTim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.comBlogger22125tag:blogger.com,1999:blog-5755313800906142262.post-24727796314138421682015-05-09T22:01:00.000-05:002015-05-09T22:23:12.388-05:00Angular message bus and smart watch<p>In my Angular apps, I typically add a couple services to $rootScope so they're available to all of my controllers. The first is a message bus for any inter-component messaging, and the other is my replacement for $watch which doesn't fire during registration.</p><p>Please note that this code is ES6, so either use an <a href="https://babeljs.io/repl/">online transpiler</a> or the transpiler of your choice. I would suggest you try <a href="https://babeljs.io/">Babel</a> if you're not using it already.</p><br />
<script src="https://gist.github.com/timgit/cde797019d4694511ac5.js"></script>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com0tag:blogger.com,1999:blog-5755313800906142262.post-56898019165123952472014-06-30T23:50:00.001-05:002017-03-16T22:59:46.885-05:00Why Bower is better than NuGet<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHKxpHaQ8j_ie_YGwgVBrvGkIpwm3EWtqiF-KnC5ZxyR1y_BIJ-NZe8I8E6OUfC_Je7nNhJ39fQqnvUSLkGri5tEIpED64UGHy7LzQ-jp_zS-2qbidbEKmMj0YDiWqokC8Hl_aOqz1K8ND/s1600/bowervsnuget.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHKxpHaQ8j_ie_YGwgVBrvGkIpwm3EWtqiF-KnC5ZxyR1y_BIJ-NZe8I8E6OUfC_Je7nNhJ39fQqnvUSLkGri5tEIpED64UGHy7LzQ-jp_zS-2qbidbEKmMj0YDiWqokC8Hl_aOqz1K8ND/s1600/bowervsnuget.png" /></a></div>
<br />
<br />
More specifically, <a href="http://bower.io/">Bower</a> is a better package manager than NuGet for client-side (JavaScript, CSS, etc.) packages, even for ASP.NET web applications where the incumbent is very well entrenched.<br />
<h3 id="apples-vs-oranges">
Apples vs. Oranges?</h3>
Is this a fair comparison? Let me offer you my disclaimer. The only reason I’m making this particular comparison is because my background is in ASP.NET web applications where NuGet was the only package manager option for quite a while. (Which means you’re probably not going see a Rails developer blogging about this :). These days, however, your average .NET web app developer is highly likely to run into a dilemma between package managers during the course of application construction. The happy path of finding all the packages you needed from just one package manager quickly accelerates into a dirt road bumpy enough to get your apple cart all jacked up. <br />
Let me start by acknowledging that NuGet is and will continue to be the standard when it comes to .NET package management. I don’t anticipate that Bower will wholesale replace NuGet as they don’t set out to achieve the same goals. Overlap between these package managers does exist, however. NuGet hosts a variety of packages that do not add .NET assemblies to your project’s reference list, JavaScript frameworks and CSS libraries being the most popular in this category. Not only that, but some of these are actually the most popular packages hosted on NuGet.org, such as <a href="https://www.nuget.org/packages/jquery" title="jQuery NuGet package">the jQuery package</a> with now over 6 million downloads. The case I’ll be making here is that where NuGet and Bower overlap in the realm of client-side packages, Bower is a better choice.<br />
<h3 id="nuget-encourages-polluting-global-scope">
NuGet encourages polluting global scope</h3>
What’s wrong with NuGet’s client-side packaging implementation then? In 3 words: <strong><em>Global scope pollution</em></strong>. Ever since NuGet first launched and we started using the jQuery package included in our ASP.NET web application templates, the infamous Scripts directory has always been the dumping ground of every JavaScript package. This directory, as well as the Content directory for CSS, has essentially become a global namespace of sorts. As soon as package authors recognize this “convention” contained in several of the most popular packages, they replicate this pattern and create their packages to deploy their JavaScript files into Scripts and their CSS files into Content. For example, here’s the scripts directory after creating a new project in Visual Studio 2013 and adding a couple Angular packages.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLd78x3F19G25WeLU89Nlkz9kefEaybA_2hFl6T3P_VAXStyNMDl6B6UlqGzpT7qwaf0w6Q0IHFsWgaInhuB_yyuUh0f6scjQU0m47lkA8kZ_ijDYwGcUXiRYGVPPljl5fSYA8DnImCsF4/s1600/NuGetPackageDestScriptsFolder.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLd78x3F19G25WeLU89Nlkz9kefEaybA_2hFl6T3P_VAXStyNMDl6B6UlqGzpT7qwaf0w6Q0IHFsWgaInhuB_yyuUh0f6scjQU0m47lkA8kZ_ijDYwGcUXiRYGVPPljl5fSYA8DnImCsF4/s1600/NuGetPackageDestScriptsFolder.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLd78x3F19G25WeLU89Nlkz9kefEaybA_2hFl6T3P_VAXStyNMDl6B6UlqGzpT7qwaf0w6Q0IHFsWgaInhuB_yyuUh0f6scjQU0m47lkA8kZ_ijDYwGcUXiRYGVPPljl5fSYA8DnImCsF4/s1600/NuGetPackageDestScriptsFolder.png" /></a></div>
<br />
<br />
<br />
What if 2 packages each want to deliver foo.js to Scripts? Well, I guess the last guy in wins in that case. The responsibility of avoiding name collisions is left solely with the package author. <br />
In the defense of package authors continuing this practice, it seems to me that they are merely following Microsoft’s lead, since we have officially sanctioned ASP.NET web application project templates installed in Visual Studio that contain pre-installed NuGet packages which install their files directly into the Scripts directory. Does Microsoft own these packages? No. They are quick to point out that you assume all risk of using 3rd party packages. But what happens when said 3rd party packages are pre-installed in the project template? While we could all agree this is a gray area, ultimately I place the responsibility of enforcing a polite ecosystem on the package manager. Ideally, a package manager should discourage these types of collisions to whatever extent is practical and appropriate. <br />
It’s also worth pointing out that the Scripts and Content folders are merely secondary delivery locations. Behind the scenes, as shown in the following screenshot, all packages are isolated quite nicely in the packages folder.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6XWEV_7uDoKBOMhFb6raaiOBdhx7KGepQbs4LJJMvMYEChyphenhyphenT3_7CJaikG_s6WUXsdPIjGsBqPxwvdgSvJ3nDUu3iExobWMZJXYsSGsRFdUl3YE06y_wlKXQ8DMGqydhyLAQxzKu4suqKA/s1600/NuGetPackagesFolder.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6XWEV_7uDoKBOMhFb6raaiOBdhx7KGepQbs4LJJMvMYEChyphenhyphenT3_7CJaikG_s6WUXsdPIjGsBqPxwvdgSvJ3nDUu3iExobWMZJXYsSGsRFdUl3YE06y_wlKXQ8DMGqydhyLAQxzKu4suqKA/s1600/NuGetPackagesFolder.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6XWEV_7uDoKBOMhFb6raaiOBdhx7KGepQbs4LJJMvMYEChyphenhyphenT3_7CJaikG_s6WUXsdPIjGsBqPxwvdgSvJ3nDUu3iExobWMZJXYsSGsRFdUl3YE06y_wlKXQ8DMGqydhyLAQxzKu4suqKA/s1600/NuGetPackagesFolder.png" /></a></div>
<br />
<br />
<div class="se-section-delimiter">
</div>
<h3 id="the-bower-alternative">
The Bower alternative</h3>
Now let’s take a look at what Bower has to offer.<br />
Run the following in your console of choice.<br />
<pre class="prettyprint prettyprinted"><code><span class="pln">bower install </span><span class="pun"><</span><span class="kwd">package</span><span class="pln"> name here</span><span class="pun">></span></code></pre>
What? Did you get an error? bower is not a recognized command? You need to <a href="http://bower.io/">install it</a> first, then. :) Once installed, this command will produce a bower_components directory with a folder for each package. For example, here’s a screenshot of my bower_components directory.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkCxX6ws6Lj4IwzxmnC89-7MJTs2ab37oeFqo3neeuUYYinI8Hpq_F92p43RDNk7sJ1Rzmsx6AxbyeQ2HvdpXbbgNDnzyUiFfXn2mia65BvlUnha_zfoFKPnw6S500nclxYRawlg0VAu_Q/s1600/bowerDirectory.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkCxX6ws6Lj4IwzxmnC89-7MJTs2ab37oeFqo3neeuUYYinI8Hpq_F92p43RDNk7sJ1Rzmsx6AxbyeQ2HvdpXbbgNDnzyUiFfXn2mia65BvlUnha_zfoFKPnw6S500nclxYRawlg0VAu_Q/s1600/bowerDirectory.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkCxX6ws6Lj4IwzxmnC89-7MJTs2ab37oeFqo3neeuUYYinI8Hpq_F92p43RDNk7sJ1Rzmsx6AxbyeQ2HvdpXbbgNDnzyUiFfXn2mia65BvlUnha_zfoFKPnw6S500nclxYRawlg0VAu_Q/s1600/bowerDirectory.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkCxX6ws6Lj4IwzxmnC89-7MJTs2ab37oeFqo3neeuUYYinI8Hpq_F92p43RDNk7sJ1Rzmsx6AxbyeQ2HvdpXbbgNDnzyUiFfXn2mia65BvlUnha_zfoFKPnw6S500nclxYRawlg0VAu_Q/s1600/bowerDirectory.png" /></a></div>
<br />
<br />
<br />
So, that’s pretty much it. What else does Bower do for us? Not a thing. <br />
<h3 id="nuget-violates-the-principle-of-separation-of-concerns">
NuGet violates the principle of Separation of Concerns</h3>
So why is Bower “better” then? Another 3 words: <strong><em>Separation of Concerns</em></strong>. The Bower team decided that a package manager’s responsibilities should not include delivery into your downstream application. This is essentially the software development principle of Separation of Concerns, package manager style. By definition and design, Bower will never have the global namespace issue that NuGet is currently suffering from because it has no intentions of offering the consumer the convenience of editing their application’s XML project file (csproj). <br />
<blockquote>
I must confess that editing a csproj file is a nice feature to have, since csproj files (or any other .NET XML project file) are the gatekeepers for all sorts of downstream delivery concerns in the .NET world, such as automated builds and deployments. While still new and emerging, there are highly productive alternatives developing in the form of <a href="http://yeoman.io/">Yeoman</a> and also <a href="http://gulpjs.com/">gulp</a> that I believe will eventually creep into the space of csproj manipulation to assist the .NET web application developer. At the very least, I would encourage you to start evaluating them. </blockquote>
It may appear at first glance that since NuGet has more features and tighter integration with Visual Studio, it must be a superior choice in the world of .NET web app developers. However, in the real world of closer inspection, these powerful features can actually harm your project.<br />
<div class="se-section-delimiter">
</div>
<h3 id="an-example-collision-failure">
An example collision failure</h3>
Allow me to submit to you an example of what could go wrong with global namespace pollution (and did go wrong on one of my projects). I will pick on the <a href="https://www.nuget.org/packages/AngularJS.Core">AngularJs.Core</a> package because of its popularity. This package emits files (angular.js being the primary one) into the Scripts folder just like the official <a href="https://www.nuget.org/packages/AngularJS">AngularJs</a> package. I think I understand the intentions behind why AngularJs.Core was created, which is that you only want the core files required to run Angular and not all of the other optional submodules (and there are quite a few). I would even go so far as to agree that it would be nice if the AngularJs package was layered in this fashion, as this is a commonly accepted practice in NuGet already. I’m only an outsider here speculating, however. I don’t know the full history of these packages. At this point, it seems confusing to have an official and unofficial package with the same deliverable, icon and description, both of which having thousands of downloads and a large set of downstream <a href="http://packages.nuget.org/v1/FeedService.svc/Packages?$filter=substringof%28%27AngularJS.Core%27,%20Dependencies%29%20eq%20true&$select=Id,Dependencies">dependent packages</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWQxyu92jMCDRk4fcvBmBO5T_twUElBqJnBVJu_nVS9NLf-j6Ni-34jPo_PdnaFs_krnFxkt_uUtiSJkCXWyrdTXechz5jCLC24dV1M0urdiAp3CJIyv1timD9KVJKvlPu5-ZdlE2NWPrV/s1600/angularjs+packages.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWQxyu92jMCDRk4fcvBmBO5T_twUElBqJnBVJu_nVS9NLf-j6Ni-34jPo_PdnaFs_krnFxkt_uUtiSJkCXWyrdTXechz5jCLC24dV1M0urdiAp3CJIyv1timD9KVJKvlPu5-ZdlE2NWPrV/s1600/angularjs+packages.PNG" /></a></div>
<br />
<br />
<blockquote>
Can you tell at first glance which one is the official package and which one is a copy? They both look rather official to me.</blockquote>
<br />
So what? What could possibly go wrong? Take a look at the diagram below. Let’s say that I have the official AngularJs package installed and then I decide to add a couple of new Angular packages. One of them (Package B in the diagram below) just so happens to have a package dependency defined against AngularJs.Core. <br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcgNq6Qq2aWOGROJ2tRoOtSGDQ2lIGKOVlHmOKEobp9eitn5yvKPDFOsPAH1gBYy-iquOh9VvUjpZREsBishTvJ0dezVR9MNFj3qXP2WFmrv-M8i6mu-hdJe1hjXT_EI1yfC4Krx2myN0O/s1600/PackageCollisionFailure.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcgNq6Qq2aWOGROJ2tRoOtSGDQ2lIGKOVlHmOKEobp9eitn5yvKPDFOsPAH1gBYy-iquOh9VvUjpZREsBishTvJ0dezVR9MNFj3qXP2WFmrv-M8i6mu-hdJe1hjXT_EI1yfC4Krx2myN0O/s1600/PackageCollisionFailure.PNG" /></a><br />
<br />
When Package B is installed, NuGet installs all dependent packages automatically (if they’re not already installed), which installs AngularJs.Core and overwrites angular.js in Scripts. Perhaps this happens on a day when a newer version of Angular is released and you had already installed it (Let’s say, 1.2.18). Installing Package B will overwrite angular.js 1.2.18 with angular.js 1.2.17 contained in AngularJs.Core because perhaps its authors haven’t had a chance to update it yet to the latest version. <br />
<blockquote>
Occasionally you’ll get a warning prompt about files that will be overwritten by packages, but I’ve noticed that this notification seems to be a bit untrustworthy at the moment.</blockquote>
Let’s say you’re a senior and experienced developer and you are lucky enough to get this friendly message in Visual Studio about a file collision. You know exactly which file should be overwritten and which one should be discarded and choose correctly. Would everyone else on your team know to make the same decision? Furthermore, if the development cycle lasts more than 3 months, chances are updates will be published for previously installed packages. This collision will continue to manifest itself for the life of your project every time you decide to install updates. <br />
<h3 id="nuget-environmental-remediation">
NuGet environmental remediation</h3>
I think there are easy solutions to remedy what’s going on with NuGet. The implementation and transition may not be so easy, but definitely easy to list out here. :)<br />
<table><thead>
<tr> <th>Responsible Party</th> <th>Remedy</th> </tr>
</thead> <tbody>
<tr> <td>NuGet team</td> <td>Prevent access to the root folders of Scripts and Content for file deliverables and force all content into package subfolders in the application project</td> </tr>
<tr> <td>Package author</td> <td>Publish a new version of your client-side package with an edited nuspec that places all files into a subfolder (ideally your package name)</td> </tr>
<tr> <td>Project team</td> <td>Continue using NuGet but delete your Scripts and Content folder (or exclude it from your project). Copy the files that you need (manually or automatically) into a different project folder so it’s clear for all team members that NuGet is not used for file delivery.</td> </tr>
</tbody></table>
<h3 id="recommendations">
Recommendations</h3>
<ol>
<li>Use Bower for JavaScript or CSS packages instead of NuGet.</li>
<li>Create a directory that mirrors the structure of bower_components and copy over only the artifacts you’re interested in using. <br />
<blockquote>
Don’t actually copy the bower_components folder into your csproj, though, as this folder can contains thousands of files. This can cause Visual Studio to consume too much memory (ReSharper, anyone?) and will generally slow things down as it’s frantically trying to update your csproj file.</blockquote>
</li>
</ol>
<br />
This recommendation should make it really easy to know when to use NuGet and when to use Bower, and simple is good.<br />
<table><thead>
<tr> <th>Technology</th> <th>Package Manager</th> </tr>
</thead> <tbody>
<tr> <td>.NET</td> <td>NuGet</td> </tr>
<tr> <td>JavaScript</td> <td>Bower</td> </tr>
<tr> <td>CSS</td> <td>Bower</td> </tr>
</tbody></table>
If you find that recommendation #2 is cumbersome to do manually (I did), then I would recommend taking a look at <a href="https://www.npmjs.org/package/gulp-bower-files">gulp-bower-files</a> as a potential solution to help you out. Will you find some warts in the Bower packaging ecosystem if you go this route? Yes, you’ll quickly find inconsistencies in the <a href="https://github.com/bower/bower.json-spec#user-content-main">main declaration</a> across packages that can be a bit frustrating at times. Gulp is still adequate to help you out here, however. Bower isn’t perfect, and they’re subject to working with real people just like any other package manager, but adopting a clear separation of concerns mentality is still a step in the right direction.<br />
<div class="se-section-delimiter">
</div>
<h3 id="parting-thoughts">
Parting Thoughts</h3>
As always, these are merely my opinions based on pain I’ve experienced working with NuGet packages that emit CSS and JavaScript. I’m not a NuGet expert, so perhaps you feel I’ve misrepresented it or even the packages that I called out by name. I welcome the feedback. I still love NuGet for .NET packages, one of which I’ll be blogging about soon as I’m in the process of publishing it. Thanks for hanging in there and hearing me out, and I hope this post has helped you out in any way possible. Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com0tag:blogger.com,1999:blog-5755313800906142262.post-24676021872318033642013-12-09T16:51:00.001-06:002013-12-10T10:58:14.962-06:00Using Fiddler to implement wildcard DNS lookups<p>Wow, this is now the third post I've done on Fiddler rules. And sequentially at that (<a href="http://simplyaprogrammer.com/2013/10/using-fiddler-to-emancipate-httponly.html">HttpOnly cookies</a> and <a href="http://simplyaprogrammer.com/2013/12/using-fiddler-to-force-web-service-to.html">CORS</a> were the previous 2 posts). What's up with that?  Seriously?  Yep.  Stuff happens.  And sometimes you have to use Fiddler to help out with that stuff.  In this case, I'm trying to set up multiple development environments to build apps for SharePoint 2013.  Anyways, this one is short and sweet compared to the other rules I've posted about, but it does have a wider application than just debugging web applications.  </p><h3>What problem does this solve?</h3><ol><li>You're building web applications with a hosting system that creates dynamic sub domains for your app (like the SharePoint app model)</li>
<li>In production, you will use DNS techniques such as wildcard CNAME records against a purchased domain, but during development this doesn't work as easily.</li>
<ol><li>You're working with a team of developers, and you may not always be able to share the same development domain name</li>
<li>Your network admin doesn't want to maintain forward lookup zones for development in the production DNS</li>
<ol><li>Even if you had a development DNS, you'd have to manage it and then jack with your TCP/IP settings and detach your normal DHCP configuration to add a new primary DNS server (and who needs all that noise?)</li>
</ol><li>Your hosts file does not support wildcard entries</li>
</ol><li>Or, perhaps you've always wanted wildcard entries in your hosts file for subdomains?  </li>
</ol><h3>How do I set this bad boy up?</h3><p><strong>UPDATE - 12/10/13: </strong>Please see the comment below from Eric regarding a more appropriate solution that avoids the rule definition.</p><p>First, you're going to create the rule definition.  Notice how the default is true for the rule instead of it normally being false/disabled?  I did this to simulate how your hosts file is "always on", but dude, change it if it's getting in your way.  </p><p>I named this one for what I'm using it for (SharePoint app domains), but you would create a separate rule for each domain that you want redirected.  Can you store all of these rules in a database and look them up at run-time instead of creating all these rules?  Yes, you can find <a href="http://fiddler2.com/documentation/Save-And-Load-Traffic/Tasks/LogToLocalDB">an example of using a database</a> if you want to do that, but JScript.NET is "not the modern programming environment you're looking for" to help you out, so caveat emptor.</p><pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">static</span> RulesOption(<span class="str">"SharePoint App Domain Override"</span>)
var m_SharePointAppDomain: boolean = <span class="kwrd">true</span>;</pre><p>Then, add the following code to the <strong>OnBeforeRequest</strong> handler.</p><pre class="csharpcode"><span class="rem">// replaces need of wildcard CNAME in DNS</span>
<span class="kwrd">if</span> (m_SharePointAppDomain) {
<span class="kwrd">if</span> (oSession.hostname.EndsWith(<span class="str">".mydevapps.com"</span>)) {
oSession.bypassGateway = <span class="kwrd">true</span>;
oSession[<span class="str">"x-overrideHost"</span>] = <span class="str">"mydevserver"</span>; <span class="rem">// DNS name or IP address of target server</span>
}
}</pre><p>Hope this helps that fraction of you out there needing a good solution to this.  Perhaps my next post will be "Using Fiddler to make bacon pancakes"!</p>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com1tag:blogger.com,1999:blog-5755313800906142262.post-42693195722575361702013-12-03T22:40:00.001-06:002013-12-04T05:41:54.678-06:00Using Fiddler to force a web service to support CORS for debugging<p>It started with a <a title="A Declaration of Independence from Tyrannical Web Services" href="http://simplyaprogrammer.com/2013/09/a-declaration-of-independence-from.html">hybrid native app developer's Declaration of Independence</a>, then came the <a href="http://simplyaprogrammer.com/2013/10/using-fiddler-to-emancipate-httponly.html">liberation of HttpOnly cookies</a>, so now we need to deal with the issue of cross domain restrictions that browsers put on XHR because of previous <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">XSS</a> exploitations.</p> <h3>Problem: Debugging in a browser</h3> <p>This post applies to you if:</p> <ol> <li>You are building some sort of native application based on HTML and JavaScript (PhoneGap in my case), where all calls to a backend web service will always be treated as a cross domain request from your XHR code.</li> <li>You are working with a web service that you don't control, and it doesn't support <a href="http://www.w3.org/TR/cors/">CORS</a>, which modern browsers use as a policy for allowing cross domain calls.</li> <li>You have come to terms with this reality and you're using native web libraries on actual devices to do your integration testing, or you're using a mock service in the browser with faked responses</li> <li>You are no longer able to run and debug your application in a browser with the actual service, and now you feel like you've been tossed back about 20 years in the past of application development and maybe even you've resorted to using console.log for your debugging and feel defeated (I know I did!)</li> </ol> <h3>Solution: Proxy FTW!</h3> <p>Surprise, Fiddler is again our proxy tool of choice to help us out. Instead of repeating <a href="http://simplyaprogrammer.com/2013/10/using-fiddler-to-emancipate-httponly.html">all the steps to set up a custom rule from the HttpOnly cookies post</a> here, please go and read that first. Once you use the following code snippet, you should be able to use the Force CORS Response rule as pictured here.</p> <p><img title="Force CORS Response Fiddler rule" style="border: 0px currentcolor; display: inline;" border="0" alt="Force CORS Response Fiddler rule" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAURIK9SrsJb3MmxCBk_lKLRqxOR72Tmr1TTBfLaUDluQqe7XEAzKo_HKewnj5Sj9jp63uswXlM4EDxcLekV7GXRqTV33-urUx7NvccUYrDfZQtZu4VVOzpFW_u-PaIPIcej2rdLiMQoRF/?imgmax=800" width="427" height="449"> </p> <h3>The Fiddler Custom Rule Implementation</h3> <p>Just like in the HttpOnly cookie rule, you should just copy and paste the following code to the bottom of the method, <strong>OnBeforeResponse</strong>, which should already be in the default rules script. </p> <pre class="csharpcode"><span class="kwrd">if</span> (m_ForceCORS &&
(
oSession.oRequest.headers.HTTPMethod == <span class="str">"OPTIONS"</span> ||
oSession.oRequest.headers.Exists(<span class="str">"Origin"</span>)
)
)
{
<span class="kwrd">if</span>(!oSession.oResponse.headers.Exists(<span class="str">"Access-Control-Allow-Origin"</span>))
oSession.oResponse.headers.Add(<span class="str">"Access-Control-Allow-Origin"</span>, <span class="str">"*"</span>);
<span class="kwrd">if</span>(!oSession.oResponse.headers.Exists(<span class="str">"Access-Control-Allow-Methods"</span>))
oSession.oResponse.headers.Add(<span class="str">"Access-Control-Allow-Methods"</span>, <span class="str">"POST, GET, OPTIONS"</span>);
<span class="kwrd">if</span>(oSession.oRequest.headers.Exists(<span class="str">"Access-Control-Request-Headers"</span>))
{
<span class="kwrd">if</span>(!oSession.oResponse.headers.Exists(<span class="str">"Access-Control-Allow-Headers"</span>))
oSession.oResponse.headers.Add(
<span class="str">"Access-Control-Allow-Headers"</span>
, oSession.oRequest.headers[<span class="str">"Access-Control-Request-Headers"</span>]
);
}
<span class="kwrd">if</span>(!oSession.oResponse.headers.Exists(<span class="str">"Access-Control-Max-Age"</span>))
oSession.oResponse.headers.Add(<span class="str">"Access-Control-Max-Age"</span>, <span class="str">"1728000"</span>);
<span class="kwrd">if</span>(!oSession.oResponse.headers.Exists(<span class="str">"Access-Control-Allow-Credentials"</span>))
oSession.oResponse.headers.Add(<span class="str">"Access-Control-Allow-Credentials"</span>, <span class="str">"true"</span>);
oSession.responseCode = 200;
}</pre>
<p>Unlike the HttpOnly cookie rule, forcing CORS is a bit more challenging. There is a chance you may need to tweak this, but I trust this example rule should get you past most of the hurdles of not having adequate documentation for this in the search engine(s). </p>
<p>Happy debugging!</p> Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com1tag:blogger.com,1999:blog-5755313800906142262.post-11127103416667933202013-10-01T22:02:00.001-05:002013-10-01T22:25:27.275-05:00Using Fiddler to emancipate HttpOnly cookies for web app debugging<p><img title="Cookie Monster meme ftw" style="margin: 0px 0px 15px 15px; border: 0px currentcolor; display: inline;" border="0" alt="Cookie Monster meme ftw" align="right" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvGZLSjIGijOP8g0wG8w_2ncCgBEuz-9OgEQR4TsG5-APiGBnYsC-_KcREjEoHssSF8D9o8aHpBn3YS7QZ0-5YPE_soD94-B7cPxH8VSllS4jGkhqvaFHe_yMYuCIzYUTBy_AzDeSLMm6K/?imgmax=800" width="398" height="237" /> In light of the <a title="A Declaration of Independence from Tyrannical Web Services" href="http://simplyaprogrammer.com/2013/09/a-declaration-of-independence-from.html">hybrid native app developer's Declaration of Independence</a>, this post might very well be the first shot of the revolution.</p><h3>Problem: Debugging in a browser</h3><p>This post applies to you if:</p><ol><li>You are building some sort of native application based on HTML and JavaScript (PhoneGap in my case)</li>
<li>You are working with a web service that you don't control, and it uses HttpOnly cookies</li>
<li>Your application requires the values of said cookies to work (most likely related to the authentication pipeline)</li>
<li>You have come to terms with this reality and you're using native web libraries to work around this fact</li>
<li>You are no longer able to run and debug your application in a browser because of #4, and now you feel like you've been tossed back about 20 years in the past of application development and maybe even you've resorted to using console.log for your debugging and feel defeated (I know I did!)</li>
</ol><h3>Solution: Proxy FTW!</h3><p>In order to restore debugging support, we're going to need to sanitize the set-cookie HTTP header on it's way down to our application, and the easiest way to do this across all browsers is to use a proxy tool. The most popular tool for this type of operation is Fiddler, so this post will cover how to set this up in Fiddler. To best explain what we're doing here, here is a before and after view of a cookie that we need to work with.</p><p><strong>Before:</strong></p><p><font size="3" face="Consolas">Set-Cookie: Flavor=chocolatechip; secure; HttpOnly</font></p><p><strong>After:</strong></p><p><font size="3" face="Consolas">Set-Cookie: Flavor=chocolatechip;</font></p><p> </p><p>For example, if you had your web application open from http://localhost/myapp, your page's JavaScript would now be able to access the Flavor cookie. So, super easy, right?  Following is my 3-step plan.</p><p> </p><h3>1: Install the syntax highlighting add-on bundle</h3><p>This step is just to make things easier on you and is not required.  </p><p><a href="http://fiddler2.com/add-ons"><img title="syntax add-on download" style="margin-right: 0px; margin-left: 0px; display: inline;" border="0" alt="syntax add-on download" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_Mj_OlxMoh9tnKZ8Zb1vwCmGF-ggji4GUaWEKqKbVQ-qDDEi412DwwcV6rUI8vwJdAcv2OPwtZljTBiN_p9OM29I9kd3UQ3jwNRWMWIlVT2oq-umJ-FZc9_zR6QgkboD4RmKf8i-ai9Fg/?imgmax=800" width="346" height="339" /></a> </p><p>Installing this add-on adds a tab to your Fiddler interface which gives you one-click access to the Fiddler Script custom rules:</p><p><img title="FiddlerScript tab" style="display: inline;" border="0" alt="FiddlerScript tab" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiifx3i54Bo54d4LT30bXxkCHUYC64aMXd7OlZgTQ1hb3BGxOo57ms5qFA0_S6AUiXb3U6AlFCS0kTQMJSIk0wysnKYq3nO8am6VksaMcANIMDiwU1xBWOXKDY5Qus42FvXaKdeFBcVOmLf/?imgmax=800" width="648" height="210" /> </p><h3>2: Create a custom rule definition using FiddlerScript</h3><p>To avoid going into too much detail here about how to create custom rules, see the <a href="http://fiddler2.com/documentation/KnowledgeBase/FiddlerScript/ModifyRequestOrResponse">official documentation</a> and the <a href="http://www.fiddlerbook.com/Book/Code/">Fiddler book site</a> which have good examples. I will say that when you first crack open the rules file, you're immediately greeted with a couple of lines of code like this: </p><pre class="csharpcode code"><span class="kwrd">public</span> <span class="kwrd">static</span> RulesOption(<span class="str">"Hide 304s"</span>)
<span class="kwrd">var</span> m_Hide304s: boolean = <span class="kwrd">false</span>;</pre><p>What is that var declaration with a colon and a type?  Well, good reader, that is likely the first and the last time you'll work with a language called <a href="http://msdn.microsoft.com/en-us/library/72bd815a(v=vs.80).aspx">JScript</a> (also referred to as JScript.NET).  I've leave the speculation (or research) up to you on how JScript ended up being the language of choice for custom rules. Let's just say it's a bit out of the way to locate a good set of documentation on this language. Hopefully you'll find examples that accomplish everything you need for your custom rule requirements either through a search or in the example below.</p><p>Add the following 2 lines of code in the file and save it in order to create a new rule definition.</p><pre class="csharpcode code"><span class="kwrd">public</span> <span class="kwrd">static</span> RulesOption(<span class="str">"Free the Cookies!"</span>)
<span class="kwrd">var</span> m_FreeTheCookies: boolean = <span class="kwrd">false</span>;</pre><p>You should now see the rule in the Rules menu. Clicking this item will enable it, so all that's left to do is add the code for the implementation. It's helpful to remember that your rule will need to be enabled every time you save the script file or close and reopen Fiddler (by default, at least).</p><p><img title="Rules menu" style="border: 0px currentcolor; display: inline;" border="0" alt="Rules menu" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikrpSCcMjOX5BZtaOcQ7nhafKIHmtQvXjvo2lr1MDsQynm_E16iIAJQRIo7pqkkZHIFSplClrvqrIKYsaTVY4pHdYWvQOt4COT0wggxklvfVUTJ-ZIHoDaSdwmjfwfuTtvTQoDG01vojzx/?imgmax=800" width="313" height="401" /> </p><h3>3. Write the custom rule implementation</h3><p>In order to sanitize the cookies, we need to add our implementation into the response handler method, <strong>OnBeforeResponse</strong>, which should already be in the default rules script.  Just add the following code to the bottom of the method.</p><pre class="csharpcode"><span class="kwrd">if</span>(m_FreeTheCookies) {
<span class="kwrd">for</span> (var x:<span class="kwrd">int</span> = 0; x < oSession.oResponse.headers.Count(); x++) {
<span class="kwrd">if</span>(oSession.oResponse.headers[x].Name.Contains(<span class="str">"Set-Cookie"</span>)) {
var cookie : Fiddler.HTTPHeaderItem = oSession.oResponse.headers[x];
<span class="kwrd">if</span>(cookie.Value.Contains(<span class="str">"HttpOnly"</span>) || cookie.Value.Contains(<span class="str">"secure;"</span>)) {
FiddlerObject.log(<span class="str">"Liberation time! Cookie to free: "</span> + cookie.Value);
cookie.Value = cookie.Value.Replace(<span class="str">"HttpOnly"</span>, String.Empty).Replace(<span class="str">"secure;"</span>, String.Empty);
}
}
}
}</pre><p>After re-enabling the rule and accessing your favorite web service with HttpOnly cookies, you should be in business. </p><p>You can use FiddlerObject.log and the Log tab to confirm the rule is working:</p><p><img title="Fiddler log" style="display: inline;" border="0" alt="Fiddler log" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiBu5SBgbw2Qz5jIzhnQVySgUnYXJNH0FgEzxROazcrp6qmmSK44YFvvxz97TCtrTeBsN8a9CD1dSXEkHPNVba32z8FS_VOXSnL3FnlWEM66_N5_DGcRnBtL36DrvPU5sXEA84DqsrywHK/?imgmax=800" width="700" height="162" /> </p><p> </p><p>Hope this helps!</p>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com1tag:blogger.com,1999:blog-5755313800906142262.post-76616383663114639602013-09-29T16:26:00.001-05:002013-09-29T21:53:21.668-05:00A Declaration of Independence from Tyrannical Web Services<p><a title="Inspired by the US Declaration of Independence" href="http://en.wikipedia.org/wiki/United_States_Declaration_of_Independence"><img title="Inspired by the US Declaration of Independence" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; float: right; margin: 0px 0px 20px 20px; border-top-width: 0px" alt="US Declaration of Independence" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghu8b5L9Lwth7pmPIP6fgVkG8PoFoEAk_D-OjgJE6GL_meL5tqzvRISXPCmxLSTzAaxQ9eYcwBi3IW2J28Mc9l5bBvn3pFnab-yL-jzaC386PMN5804V9zJUvLvrpqHSmL8lqEA7C-eXN9/?imgmax=800" width="277" height="320" /></a> When in the course of human events it becomes necessary for a HTML-based app development team to dissolve the technical bands (XHR) which have connected them with a web service (due to such security concerns as HttpOnly cookies) and to assume among the powers of the earth, the separate and equal station (native web libraries) to which the Laws of Nature and of Nature's God entitle them, a decent respect to the opinions of the web development community requires that we can still run and debug our application in a browser.</p><p>We hold these truths to be self-evident, </p><ul><li>that all web developers are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are an internet connection, code editors, XHR, Fiddler, coffee, and the pursuit of engineering excellence. </li>
<li>that HTML is increasingly becoming an architecture that allows applications to be created in a variety of platforms, not the least of these being delivered in arguably the most popular app delivery mechanisms on the planet: the app stores of the iOS and Android platform in the form of hybrid native apps. </li>
</ul><p>That to secure these rights, ISPs and ISVs are instituted among Men, deriving their just powers from the consent of the developer consumer in the free market, That whenever any Form of web service becomes destructive of these ends, it is the Right of the developer to alter or to abolish it, and to institute a layer of abstraction, laying its foundation on such principles and organizing its capabilities in such form, as to them shall seem most likely to re-establish XHR to its rightful place as a testing harness for all HTML-based application architectures. </p><p>Prudence, indeed, will dictate that web services securely established should not be changed for light and transient causes; and accordingly all experience hath shewn, that developers are more disposed to suffer, while security restrictions are sufferable, than to right themselves by abolishing the development environments to which they are provided. But when a long train of console.log() statements, pursuing invariably the same Object evinces a design to reduce them under absolute Despotism, it is their right, it is their duty, to throw off such a configuration, and to provide a platform for their application's testability.</p><p>Such has been the patient sufferance of these developers; and such is now the necessity which constrains them to alter their web service interfaces and restrictions. The history of web application security has been a longsuffering struggle of developers against hackers, resulting in reduced capabilities of JavaScript and the introduction of security sandboxes, in direct object the establishment of an absolute Tyranny over such things as simple as an HTTP header. </p><p>To prove this, let Facts be submitted to a candid development community. Some providers of web services have produced APIs which seem to ignore the fact that HTML applications can and are increasingly being created outside of a standard web browser. </p><ul><li>Some web services do not support CORS, going so far as to nullify the use of XHR in some cases </li>
<li>Some web services only allow authentication through HttpOnly cookies, nullifying XHR in all cases </li>
</ul><p>Nor have We been wanting in attempts at workarounds.  We created JSONP as a temporary solution. We have stood up facade web services. We have in some cases just given up on entire platforms and walked away in the shame of defeat.</p><p>We, therefore, the developers of web platform-based applications, appealing to the Supreme Judge of the world for the rectitude of our intentions, do, in the Name, and by Authority of the good People on our teams and on behalf of the employers, clients and customers we serve, solemnly publish and declare, </p><ul><li>That security restrictions should not trump the need for a proper debugging environment. </li>
<li>That we ought to be Free to use the same tools we've grown to depend on for web applications development even though our applications may not be targeted to eventually running in a browser. </li>
<li>That the full power of debugging tools will be restored to the web developer when his or her application is being tested in a browser. </li>
<li>That XHR will be restored to its rightful place as a mechanism for interfacing with secure web services in a development environment. </li>
</ul><p>And for the support of this Declaration, with a firm reliance on the protection of divine Providence, we mutually pledge to each other our code, our support and our sacred Honor.</p>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com2tag:blogger.com,1999:blog-5755313800906142262.post-1952636364170425982012-09-06T01:15:00.001-05:002013-09-30T22:24:11.939-05:00How to build a jQuery world clock in SharePoint<h3>Update - 9/12/12</h3>This post was deemed worthy of <a href="https://www.nothingbutsharepoint.com/sites/eusp/Pages/How-to-Build-a-jQuery-World-Clock-in-SharePoint.aspx">publishing</a> on <a href="https://www.nothingbutsharepoint.com/Pages/default.aspx">Nothing But SharePoint's site</a>. May the SEO battle begin (it's a hobby to watch Google's relevancy algorithm in action).<br />
<h3>Requirements</h3><p>On a recent project, my client asked me to produce a world clock web part on his portal’s home page. He requested a list of office locations and the time in that location, honoring Daylight Savings Time rules if applicable (<a href="http://www.timeanddate.com/worldclock/">timeanddate.com’s world clock page</a> is a good working example). The following screenshot is an example of the layout requested.</p><img alt="world clock web part" border="0" height="120" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTSQXox5mAa3lotUOwpqkSHj9q0PIXveXauIp8tvIeg6sepRouBA29oTd0nXkgkF7NpGPz8FzdV9ILoDffjWM5dBOSBetTmtOUAHtU1QC6iDXJE00b0570KwylMc0yy6qNq2T-WAd_3TUR/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; " title="world clock web part" width="197" /><br />
<p>My first reaction was, “there must be an existing world clock web part I can use, because how would it be possible that something this common isn’t already out there?”. So after searching for “sharepoint world clock”, I downloaded and installed the 4 most popular solutions (all server-side web parts), both community and commercial. I was shocked that none of them had this capability out of the box. Some would have this functionality embedded with weather features, and some would output html that required serious DOM manipulation to extract the data and place it into a tabular output like I needed. When I found one that looked like a close match, there was always something that caused it not to fit, such as the lack of support for the exact time format that I needed, or not being able to override the location name after executing a web service call. Coming to the realization that I was going to have to do something custom, I set out to write my own web part because it seems simple enough, right? :)</p><p>After a bit of reflection, I arrived at the following list of requirements.</p><ol><li>No external web service dependencies. </li>
<li>Powered by JavaScript using currently installed web parts. Try to avoid deploying a solution with compiled code. </li>
<li>Clocks should be based on a more reliable time source than the time on a client machine. </li>
<li>Customized clock format (date and time). </li>
<li>Adjusts for Daylight Savings Time when observed. </li>
</ol><h3>Storing the data</h3><p>First of all, the question may be raised here as to why we’d want to store this data ourselves. Won’t this make the data out of date as soon as a locality updates their Daylight Savings Time or decides, gasp, to change their GMT offset one day? In my humble opinion, the rate of change of this data is low enough to justify caching it in a list somewhere on your SharePoint farm. When you combine this with the fact that using an external web service API to fetch this information dynamically would come with a fee attached, it becomes a little easier to justify the infrequent edits required to keep your local storage up to date with the time zones of the world. (If you happen to be that edge case which produces a maintenance burden updating cached time zone and DST data, perhaps consider automating the import of the <a href="http://www.iana.org/time-zones">time zone database</a>?)</p><p>So if you’re still with me, let’s do this. The first order of business is to build out the data source. In order to store the data required to render clocks, I created a list in SharePoint to hold GMT offsets and Daylight Savings Time date ranges. If you’d like, you can <a href="https://dl.dropbox.com/u/101304566/WorldClock.stp">download my list template</a> to get started with your list. My list has the following design.</p><img alt="world clock list columns" border="0" height="274" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijtObDxOEWpwewRpFtATOGj6ynBFN0bk2g00tGszxGVTzlTrQkfcCuIdH_5fc5fPdS70-26SAwQe-xSvKRWhjRhklJbORVoc3Eh5TDl9VchKi1-ZBHdwGX1jXamueP5n1alAK1HW2cJqfG/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; " title="world clock list columns" width="640" /><br />
<p>I have to admit I was on the fence regarding the use of separate columns for storing date components of the Daylight Savings Time data (so I therefore reserve the right to change my mind later). The primary reason for this is because SharePoint adjusts date fields based on local time zones, even though it actually stores the date as UTC/GMT in the content database. What would be really cool here is a custom field type in SharePoint called “UTC Date” that doesn’t try do any time zone adjustments. Then, you could just use that for UI date validation and ignore the year upon retrieval when checking for Daylight Savings Time. I’ve built some custom fields types that were more involved than this, so it wouldn’t be too significant an effort, but let’s just stick with what SharePoint gives us to work with and try to keep all of our customizations away from custom Visual Studio solutions, shall we?</p><h3>Retrieving the data</h3><p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrS8QITl8OOb9MjN2hRuffkDoQo00d836ovWd4JI6pYs8KPVwl-9yU49IF83BvIzCR64n6q9OW74NfOjy6fa-BHFdBKqFjbZ7UJJ0Z70itqw5Eq3-F39GqhxjJQm_3uD1gd1inUGBQhf2Z/s1600-h/image23.png"><img align="right" alt="Creating the data view" border="0" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio5ipW_qPhFfirMtB8JEuYoN6H0q4CbnPVDwYaxhJPuzpzmrcvl0QVGXEU01-jr7GtlB-mExSVo8Vue6KF7RWUWtjTsyaNXtvvJW-o9GGagAxuWcD-YrX3EBkJJaB26dYOB2zHgAT-WmDH/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; float: right; margin: 0px 0px 20px 20px;" title="Creating the data view" width="241" /></a>To make sure I can completely control the HTML output of the list data, I used the Data Form Web Part (also named data view). I prefer these over the default XSLT List View Web Part because of their concise XSLT templates. However, you can read more about the differences on <a href="http://office.microsoft.com/en-us/sharepoint-designer-help/web-parts-for-views-and-forms-in-sharepoint-designer-2010-HA101805424.aspx">Microsoft’s comparison page</a>. To add the web part, you’ll need to use SharePoint Designer. On a web part page, select a web part zone and choose Insert, then Data View, then Empty Data View.</p><p>After choosing my World Clock list as the data source, I just added the Title field using the Multiple Item View menu choice to initialize the data view with the proper data source.</p><img alt="Initializing a Data Form Web Part" border="0" height="245" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7BLwIvsgIltJ-CP5SWhru8U2ciNpaT_j51eyX3gbxacYZdKZJvVXcOJO3ly0R1274FLCfW81HuQdwfXqoacd0Km5k9T-xZhd6JPWq9RnfZ2rCgEFFNMD_rQ1OIdTZBtX1bHGsczVtATVo/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline;" title="Initializing a Data Form Web Part" width="180" /><br />
Don’t worry about customizing anything on the data view at this point (unless it’s to disable paging), because we’re about to rock its world with a shiny new list of parameter bindings and a new XSLT stylesheet.<br />
<h4>JSON FTW or: How I Learned to Stop Worrying and Love jQuery</h4>At this point, you should switch over to the Code view so you can begin overwriting all the “less than ideal” XSLT that comes out of the box. First, let’s set up a couple of custom parameters. In the web part’s markup, find the parameterbindings xml element, and add the following two parameter bindings. While you’re at it, I’d recommend removing all the parameters except ListID. It tends to reappear even if you delete it, even though we don’t really need the List ID in the stylesheet. Besides, it’s already embedded in the data source, which is where it counts.<br />
<pre class="code csharpcode"><span class="kwrd"><</span><span class="html">ParameterBinding</span> <span class="attr">Name</span><span class="kwrd">="Title"</span> <span class="attr">Location</span><span class="kwrd">="WPProperty(Title)"</span><span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">ParameterBinding</span> <span class="attr">Name</span><span class="kwrd">="DateTimeFormat"</span> <span class="attr">Location</span><span class="kwrd">="None"</span> <span class="attr">DefaultValue</span><span class="kwrd">="ddd h:mm A"</span><span class="kwrd">/></span></pre><p>The <em>Title</em> parameter passes in the web part’s title to the clock so we can more easily control styling without having to be concerned with attempting to style the normal chrome header text (just set the Chrome to None and we’re good). However, if you’ve already customized the styling of your chrome, then have at it. This is definitely an optional step that I decided to do.</p><p>The <em>DateTimeFormat</em> parameter is a format string for displaying the time. In this example, I’m using a format string compatible with the <a href="http://momentjs.com/">moment.js library</a>. As you’ll see later on, however, this solution doesn’t require you to use moment.js, so you can plug in any JavaScript date library you may already be using. I decided to make the format string a parameter so it could be easily edited on the fly without requiring a new version of your js file to be published. This also means someone not very comfortable with JavaScript could update it with a lot less intimidation.</p><p>In order to pass data to jQuery, I’m going to take a <a href="http://en.wikipedia.org/wiki/Separation_of_concerns">separation of concerns</a> approach to XSLT, making it responsible for as little as I possibly can, which some (of the “<a href="http://regex.info/blog/2006-09-15/247">2 problems</a>” persuasion) would say is the proper way to treat this technology. Given SharePoint’s current limitation of only supporting XSLT 1.0, we have very little in the category of useful functions to help us out here anyway. Plus, XSLT has the tendency to be bloated and hard to follow, becoming a potential long-term maintenance concern.</p><p>The solution? JSON FTW! The following stylesheet transforms the List data XML into inline JSON that jQuery will then be able to deserialize easily. In this example, I’ve tried to remove absolutely everything out of the default stylesheet that I could get away with. For readability, I’ve left quotation marks in the JSON templates. Although these will be replaced with &quot; after you save and close, it won’t affect the output. That’s the not the only thing that will be modified after saving and closing, however. See those unused ddwrt namespaces? Try and delete them and they will re-inserted back into your stylesheet after you close and reopen your page in SharePoint Designer.</p><pre class="code csharpcode"><span class="kwrd"><</span><span class="html">Xsl</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:stylesheet</span> <span class="attr">version</span><span class="kwrd">="1.0"</span>
<span class="attr">xmlns:xsl</span><span class="kwrd">="http://www.w3.org/1999/XSL/Transform"</span>
<span class="attr">xmlns:ddwrt2</span><span class="kwrd">="urn:frontpage:internal"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:output</span> <span class="attr">method</span><span class="kwrd">="html"</span> <span class="attr">indent</span><span class="kwrd">="no"</span><span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="Title"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DateTimeFormat"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:template</span> <span class="attr">match</span><span class="kwrd">="/"</span>
<span class="attr">xmlns:ddwrt</span><span class="kwrd">="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:call-template</span> <span class="attr">name</span><span class="kwrd">="clocks"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">xsl:template</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:template</span> <span class="attr">name</span><span class="kwrd">="clocks"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:variable</span> <span class="attr">name</span><span class="kwrd">="Rows"</span> <span class="attr">select</span><span class="kwrd">="/dsQueryResponse/Rows/Row"</span><span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:if</span> <span class="attr">test</span><span class="kwrd">="count($Rows) &gt; 0"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">div</span> <span class="attr">id</span><span class="kwrd">="worldClock"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">div</span> <span class="attr">class</span><span class="kwrd">="config"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:call-template</span> <span class="attr">name</span><span class="kwrd">="configJSON"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">div</span> <span class="attr">class</span><span class="kwrd">="header"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$Title"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">div</span> <span class="attr">class</span><span class="kwrd">="clocks"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">table</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:for-each</span> <span class="attr">select</span><span class="kwrd">="$Rows"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">tr</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">td</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">div</span> <span class="attr">class</span><span class="kwrd">="title"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="@Title"</span><span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">td</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">td</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">div</span> <span class="attr">class</span><span class="kwrd">="clock"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:call-template</span> <span class="attr">name</span><span class="kwrd">="clockJSON"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="GMTOffset"</span> <span class="attr">select</span><span class="kwrd">="@GMTOffset"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="DSTGMTOffset"</span> <span class="attr">select</span><span class="kwrd">="@DSTGMTOffset"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="DSTBeginMonth"</span> <span class="attr">select</span><span class="kwrd">="@DSTBeginMonth"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="DSTBeginDay"</span> <span class="attr">select</span><span class="kwrd">="@DSTBeginDay"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="DSTBeginHour"</span> <span class="attr">select</span><span class="kwrd">="@DSTBeginHour"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="DSTBeginMinute"</span> <span class="attr">select</span><span class="kwrd">="@DSTBeginMinute"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="DSTEndMonth"</span> <span class="attr">select</span><span class="kwrd">="@DSTEndMonth"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="DSTEndDay"</span> <span class="attr">select</span><span class="kwrd">="@DSTEndDay"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="DSTEndHour"</span> <span class="attr">select</span><span class="kwrd">="@DSTEndHour"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:with-param</span> <span class="attr">name</span><span class="kwrd">="DSTEndMinute"</span> <span class="attr">select</span><span class="kwrd">="@DSTEndMinute"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">xsl:call-template</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">td</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">tr</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">xsl:for-each</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">table</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">xsl:if</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">xsl:template</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:template</span> <span class="attr">name</span><span class="kwrd">="configJSON"</span><span class="kwrd">></span>
{
"formatString" : "<span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DateTimeFormat"</span> <span class="kwrd">/></span>"
}
<span class="kwrd"></</span><span class="html">xsl:template</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:template</span> <span class="attr">name</span><span class="kwrd">="clockJSON"</span><span class="kwrd">></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="GMTOffset"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DSTGMTOffset"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DSTBeginMonth"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DSTBeginDay"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DSTBeginHour"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DSTBeginMinute"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DSTEndMonth"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DSTEndDay"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DSTEndHour"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:param</span> <span class="attr">name</span><span class="kwrd">="DSTEndMinute"</span> <span class="kwrd">/></span>
{
"gmtOffset": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$GMTOffset"</span> <span class="kwrd">/></span>
<span class="kwrd"><</span><span class="html">xsl:if</span> <span class="attr">test</span><span class="kwrd">="string-length($DSTGMTOffset) &gt; 0
and string-length($DSTBeginMonth) &gt; 0
and string-length($DSTBeginDay) &gt; 0
and string-length($DSTBeginHour) &gt; 0
and string-length($DSTBeginMinute) &gt; 0
and string-length($DSTEndMonth) &gt; 0
and string-length($DSTEndDay) &gt; 0
and string-length($DSTEndHour) &gt; 0
and string-length($DSTEndMinute) &gt; 0"</span><span class="kwrd">></span>,
"dstGmtOffset": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DSTGMTOffset"</span> <span class="kwrd">/></span>,
"dstBeginMonth": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DSTBeginMonth"</span> <span class="kwrd">/></span>,
"dstBeginDay": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DSTBeginDay"</span> <span class="kwrd">/></span>,
"dstBeginHour": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DSTBeginHour"</span> <span class="kwrd">/></span>,
"dstBeginMinute": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DSTBeginMinute"</span> <span class="kwrd">/></span>,
"dstEndMonth": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DSTEndMonth"</span> <span class="kwrd">/></span>,
"dstEndDay": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DSTEndDay"</span> <span class="kwrd">/></span>,
"dstEndHour": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DSTEndHour"</span> <span class="kwrd">/></span>,
"dstEndMinute": <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$DSTEndMinute"</span> <span class="kwrd">/></span>
<span class="kwrd"></</span><span class="html">xsl:if</span><span class="kwrd">></span>
}
<span class="kwrd"></</span><span class="html">xsl:template</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">xsl:stylesheet</span><span class="kwrd">></span>
<span class="kwrd"></</span><span class="html">Xsl</span><span class="kwrd">></span></pre><h3>Styling the output</h3>At this point, you should see something like this in your output:<br />
<a href="http://lh3.ggpht.com/-VLC2DD_Ewo4/UEg_my-fY8I/AAAAAAAAAe4/eNuziaefcoY/s1600-h/image5.png"><img alt="image" border="0" height="322" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitCYmQCCTAWsdgZC5W-48yLULfEBgPt3DLY7VK8YhGQtO22MMz-_YtYU0XfVF9juO4h7w-nJ8oKizJ5XmhQ4zd3DnusCoeOTvITjuZDnBsXJkZ5nEHPwpCF05JmNq_5ROQ9WfEQWR7ALyB/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; " title="image" width="230" /></a><br />
Since jQuery will wait for the page to load before it will begins processing the JSON, it would probably be a good idea to hide the JSON by default. The following CSS is one example of how we might do this. Setting the JSON elements’ CSS style to display: none is equivalent to how jQuery implements its hide() method. Therefore, whenever we’re ready to display the content after rendering, it will be as easy as calling <a href="http://api.jquery.com/show/">show()</a>. Also, in this example I’ve set the width of the containing table to 100%, so the web part will expand to fit whatever zone you place it in, or you can simply set the width in the web part properties.<br />
<pre class="code csharpcode"><span class="rem">/* world clock styles */</span>
#worldClock {
font-size: 8pt;
width:100%;
}
#worldClock .config {
display:none; <span class="rem">/* always hide config JSON */</span>
}
#worldClock .header {
text-align: center;
padding: 4px;
font-size: 12pt;
font-weight:bold;
}
#worldClock .clocks {
padding: 4px;
}
#worldClock table, #worldClock td {
width: 100%;
}
#worldClock .clock {
display: none; <span class="rem">/* hides JSON during rendering */</span>
text-align: right;
white-space: nowrap;
}
</pre><h3>Rendering the clocks using OO JavaScript</h3>Now that we’ve swept all that JSON under the CSS rug, let’s get to the fun stuff: the code. What we need to do is basically iterate over each clock and replace the JSON with a formatted time. Sounds easy, right? Before you start ROTFLOL too soon, it’s actually a lot easier and more concise than perhaps what you’ve seen in the past when stumbling across world clock scripts that work with Daylight Savings Time. Following is all the JavaScript you’ll need in order to do this in an object-oriented fashion.<br />
<pre class="code csharpcode">(<span class="kwrd">function</span>(){ <span class="rem">// IIFEs protect your globals </span>
$(<span class="kwrd">function</span>(){
<span class="kwrd">var</span> config = $.parseJSON($(<span class="str">"#worldClock .config"</span>).text());
<span class="rem">// using moment.js for parsing and formatting functions</span>
<span class="rem">// replace with your favorite date library if desired</span>
<span class="kwrd">var</span> parseFn = <span class="kwrd">function</span>(httpDateHeader) {
<span class="kwrd">var</span> gmtMoment = moment.utc(httpDateHeader);
<span class="kwrd">return</span> <span class="kwrd">new</span> Date(gmtMoment.year(),
gmtMoment.month(),
gmtMoment.date(),
gmtMoment.hours(),
gmtMoment.minutes());
};
<span class="kwrd">var</span> formatFn = <span class="kwrd">function</span>(date) {
<span class="kwrd">return</span> moment(date).format(config.formatString);
};
<span class="kwrd">var</span> gmt = getGmtFromServer(parseFn);
$(<span class="str">"#worldClock .clock"</span>).each(<span class="kwrd">function</span>() {
<span class="kwrd">var</span> json = $(<span class="kwrd">this</span>).text();
<span class="kwrd">var</span> data = $.parseJSON(json);
<span class="kwrd">var</span> clock = <span class="kwrd">new</span> Clock(data, gmt, formatFn);
$(<span class="kwrd">this</span>).text(clock.toString());
$(<span class="kwrd">this</span>).show();
});
});
<span class="rem">// issues HEAD request to the page to get date header</span>
<span class="rem">// parseFn: date parse function for the RFC 822 date header</span>
<span class="kwrd">function</span> getGmtFromServer(parseFn) {
<span class="kwrd">var</span> gmt;
$.ajax({
url: location.href,
async: <span class="kwrd">false</span>,
type: <span class="str">"HEAD"</span>,
success: <span class="kwrd">function</span>(data, textStatus, jqXHR){
gmt = parseFn(jqXHR.getResponseHeader(<span class="str">"Date"</span>));
}
});
<span class="kwrd">return</span> gmt;
}
<span class="rem">// Clock constructor</span>
<span class="kwrd">function</span> Clock(data, gmt, formatFn) {
<span class="rem">// copy properties from deserialized JSON object</span>
<span class="kwrd">for</span> (prop <span class="kwrd">in</span> data)
<span class="kwrd">if</span> (data.hasOwnProperty(prop)) <span class="kwrd">this</span>[prop] = data[prop];
<span class="kwrd">this</span>.gmt = gmt;
<span class="kwrd">this</span>.formatFn = formatFn;
}
Clock.prototype = {
getDSTBegin : <span class="kwrd">function</span>() {
<span class="kwrd">return</span> <span class="kwrd">new</span> Date(<span class="kwrd">this</span>.gmt.getUTCFullYear(),
<span class="kwrd">this</span>.dstBeginMonth - 1,
<span class="kwrd">this</span>.dstBeginDay,
<span class="kwrd">this</span>.dstBeginHour,
<span class="kwrd">this</span>.dstBeginMinute,
0);
},
getDSTEnd : <span class="kwrd">function</span>() {
<span class="kwrd">return</span> <span class="kwrd">new</span> Date(<span class="kwrd">this</span>.gmt.getUTCFullYear(),
<span class="kwrd">this</span>.dstEndMonth - 1,
<span class="kwrd">this</span>.dstEndDay,
<span class="kwrd">this</span>.dstEndHour,
<span class="kwrd">this</span>.dstEndMinute,
0);
},
toStandardTime : <span class="kwrd">function</span>() {
<span class="kwrd">return</span> (<span class="kwrd">this</span>.gmtOffset === 0)
? <span class="kwrd">this</span>.gmt
: <span class="kwrd">this</span>.gmt.addHours(<span class="kwrd">this</span>.gmtOffset);
},
toDaylightSavingsTime : <span class="kwrd">function</span>() {
<span class="kwrd">return</span> (<span class="kwrd">this</span>.dstGmtOffset === 0)
? <span class="kwrd">this</span>.gmt
: <span class="kwrd">this</span>.gmt.addHours(<span class="kwrd">this</span>.dstGmtOffset);
},
toString : <span class="kwrd">function</span>(){
<span class="kwrd">var</span> time = <span class="kwrd">this</span>.toStandardTime();
<span class="kwrd">if</span>(<span class="kwrd">typeof</span> <span class="kwrd">this</span>.dstGmtOffset !== <span class="str">"undefined"</span>)
{
<span class="kwrd">var</span> daylightSavingsTime = <span class="kwrd">this</span>.toDaylightSavingsTime();
<span class="kwrd">var</span> dstBegin = <span class="kwrd">this</span>.getDSTBegin();
<span class="kwrd">var</span> dstEnd = <span class="kwrd">this</span>.getDSTEnd();
<span class="kwrd">if</span>(time >= dstBegin && daylightSavingsTime < dstEnd)
time = daylightSavingsTime;
}
<span class="kwrd">return</span> <span class="kwrd">this</span>.formatFn(time);
}
};
<span class="rem">// adds hours to a Date instance</span>
Date.prototype.addHours = <span class="kwrd">function</span>(hours) {
<span class="kwrd">return</span> <span class="kwrd">new</span> Date(<span class="kwrd">this</span>.getTime() + hours * 60 * 60 * 1000);
};
})();</pre><h3>Finding a reliable GMT (They don’t make them like they used to)</h3>Now that I’ve shown you all my cards, allow me to explain myself a bit. One of the first items for discussion is how to reliably determine the current GMT. You know you’re going about it the wrong way if you see the following line of code anywhere in your solution, which indicates you’re relying on the current time on the client’s machine (and you have no idea where that thing has been).<br />
<pre class="code csharpcode"><span class="kwrd">var</span> date = <span class="kwrd">new</span> Date(); <span class="rem">// client-side implementation FAIL! </span></pre><p>My solution was to use the time on the web server, the assumption being that the time on the server is correct. Some may argue that this is not reliable enough, but if the clocks on your servers are off, you may likely have more significant problems (Kerberos *cough cough*) to be concerned with than just your world clock web part showing the wrong time.</p><p>Take a quick look at the <em>getGmtFromServer()</em> function above. This issues a HEAD AJAX call right back to the hosting page for the purposes of fetching the HTTP Date header, which just so happens to be the most convenient way to get the current time in GMT from SharePoint. All the XSLT date functions available to us return the local time on the server, but I found no easy way of determining the server’s time zone. Instead of making this task a much more complicated ordeal, I punted to IIS for the information I needed from the good old HTTP specification. </p><p>By the way, if you were ever searching for a good use case for the HEAD method, I would submit this one as highly appropriate. Why issue a full-blown GET request and bother the server to do all that resource rendering when you can just tell it to return what we need, which is just the headers. Unfortunately we do have to make an AJAX request to get the headers, since the browsers don’t currently trouble themselves to store the HTTP response headers anywhere after a page loads. Also, the AJAX request is a little unorthodox because I forced it to be synchronous, but I think it’s appropriate in this case (it’s an extremely lightweight request and the code is a little easier on the eyes).</p><h3>Date parsing and formatting</h3><p>While I’m on the topic of dates, this would be a good time to discuss the ready function. Since JavaScript doesn’t offer extensive date parsing or formatting out of the box, you’re basically on your own to do this yourself. Since this has been a problem for a long time, by now there are probably at least a dozen different date libraries to choose from. I initially attempted to use date.js, but for whatever reason it no longer worked correctly (It formatted midnight as a 0 instead of 12). I then searched and found <a href="http://momentjs.com/">moment.js</a>, an awesome date library that’s at least 4 years younger than date.js and smaller in size as well. It’s capabilities far exceed my requirements and it’s <a href="http://cdnjs.com/">available on a CDN</a>. Winning!</p><p>Even though I may be excited about moment.js right now, maybe I’ll change my mind later, or perhaps even find some shinier date library. This thought inspired me to write the 2 functions at the top of the ready function: <em>parseFn</em> for parsing the RFC 822 date header and <em>formatFn</em> for rendering the formatted time output. You can (and most likely will) change these functions to parse and format however you’d like without affecting how the world clock works.</p><h4>Nerdy details for those so inclined to be educated (here there be dragons)</h4>Defining functions and passing them as parameters keeps the world clock ignorant of however you decide to parse and format dates. This is an algorithmic <a href="http://en.wikipedia.org/wiki/Dependency_injection">dependency injection</a> technique called the <a href="http://en.wikipedia.org/wiki/Strategy_pattern">strategy pattern</a>. And as a bonus teachable moment, <em>parseFn</em> creates a <a href="http://lostechies.com/derekgreer/2012/02/17/javascript-closures-explained/">closure</a> by exposing the config object. The closure is how the immediate scope of <em>Clock.toString</em> understands what <em>config.formatString</em> is when it calls <em>parseFn</em>.<br />
<h3>Conclusion</h3><p>I’ve made every effort to make the JavaScript as readable and self-documenting as possible. I even sprinkled in some additional comments just in case you see something that may appear foreign at first glance (such as IIFEs and constructor functions). Even though this example represents a very specific set of requirements, I can imagine how using the XSLT to JSON to jQuery (XJQ?) technique could be quite effective in a lot of use cases.</p><p>I hope you’ve found this implementation a useful example on how to integrate jQuery with SharePoint. Think of it as “a way” to get JSON to jQuery, but not “the way”, as we do have other options available, such as the <a href="http://msdn.microsoft.com/en-us/library/ff798339.aspx">OData feed</a>. </p><p>Oh yeah, and I almost forgot to tell you the specific versions of what I was running.</p><ul><li>SharePoint 2010</li>
<li>jQuery 1.8.1 </li>
<li>moment.js 1.7.0 </li>
</ul>Happy SharePointing!Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com2tag:blogger.com,1999:blog-5755313800906142262.post-51838507282922584272012-08-27T16:21:00.001-05:002013-05-23T14:19:56.266-05:00Yes, you can do recursion in a Nintex workflow<style type="text/css">
table {
border-collapse: collapse
}
table th {
border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; padding-left: 4px; padding-right: 4px; vertical-align: top; border-top: gray 1px solid; border-right: gray 1px solid; padding-top: 4px
}
table td {
border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; padding-left: 4px; padding-right: 4px; vertical-align: top; border-top: gray 1px solid; border-right: gray 1px solid; padding-top: 4px
}
table th {
background-color: #ddd
}</style> <h3>Requirements</h3><p>I was recently introduced to the Nintex Workflow 2010 product several months ago, and during construction of one of my workflows for a client, I found myself needing recursion.  Why, you ask?  Well, this problem always seems to surface whenever a hierarchy is involved, such as folders in a file system that have subfolders. In my case, I had a list in SharePoint that contained a template of tasks that were to occur in a certain order, and each task had a relationship to another task.  For example, “Set up utilities” must occur 30 days before from “Move in to my new house”.  What I knew ahead of time was only “Move-in Date”, and I wanted my workflow to basically walk through the hierarchy of related tasks and calculate dates for all of my tasks.</p><p>If I were coding this in a programming language, I’d just create a recursive method that called itself with the base date and passed that to all related tasks so they could do their calculations.  However, in order to hand this calculation procedure over to a client that had no coders on staff, Nintex Workflow was the chosen vehicle for delivery. When I first used Nintex, I didn’t easily spot a construct that would allow me to implement recursion. Sure, workflows can call other workflows, but the number of simultaneous executing workflows could get out of hand causing SharePoint to rain on that parade rather quickly. </p><h3>Prerequisite Knowledge</h3><p>It’s helpful at this point to step back a bit and think about what we’re trying to do. The feature I want in Nintex is basically the ability to call a function (method is more of an object oriented programming label), but Nintex doesn’t provide the concept of a function. But what is a function, and how does recursion within functions normally work in a standard programming environment you ask? (you ask a lot of questions, but that’s a good thing)</p><h4>Computer Science 101 and 201</h4><p>When I was in college getting my bachelor’s degree in Computer Science, there were a couple courses I took that proved helpful to me when determining how to set this up.  The first is Assembly Programming 101, where one learns exactly what happens behind the scenes when a function is called.  The short answer is to use a data structure called a stack, which I learned in another class, Data Structures 101. In assembly, when you want to call a function that requires a parameter, you “push” the parameter (using a register to store the value) onto the stack first.  Then, once inside the function, you “pop” the value off the stack to use it.  So, the next time you’re writing methods in C#, just think about all those stack operations going on behind the scenes on your behalf.  :)</p><p><a href="http://www.csanimated.com/animation.php?t=Stack"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Stack data structure animation" border="0" alt="Stack data structure animation" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgviJyKQDOanNoeyOwqNBWbFQ6F55GCBWD0oDCwOIkQwYBdbDn3JUp68FoxD0Fk8G8Q9MC8RUzSDMw_AE-Gjg115Qtz_4f-NYWvBk-XiFZ-4BT0VJcWZ__T5fijDCX-cMDaoNRkPEaZ7hd0/?imgmax=800" width="299" height="216" /> <br />
Helpful animation explaining stacks</a></p><p>So, now that you understand how a function call might work with a stack, how does recursion work?  Instead of scaring you away with terms like “depth-first preorder tree traversal” (also from Data Structures 101), just know that we’ll need to keep a running list of tasks that have related tasks, and we’ll use a stack for that. </p><p>Here’s the algorithm: </p><ol><li>Every time you find a task (parent) that has related tasks (children), push the parent to the stack. </li>
<li>Loop over the stack, pop an item off, and get children.</li>
<li>Repeat step 1 and 2 until the list is empty.</li>
</ol><p> </p><p><a title="Helpful animations of tree traversals" href="http://www.khanacademy.org/cs/depth-first-traversals-of-binary-trees/934024358"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Preorder tree traversal animation" border="0" alt="Preorder tree traversal animation" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcWJ4TbIN7Bbm6cgZ0GQ09SoHbN2GJhSI5lUIEXeBt9mYLZKrA1T-m_d3Kq7hfp6ryp1Ov80vRDa21m_kKwwvY1BlWWIWgIdEh4P1Wc20cTEl68lEcDfR7wwcQzno8kHlblmcJIDrduYVg/?imgmax=800" width="403" height="306" /></a> <br />
<a href="http://www.khanacademy.org/cs/depth-first-traversals-of-binary-trees/934024358">Helpful animations of tree traversals</a></p><p>See, easy as pie, right?</p><h4>Nintex Workflow 2010 Tools</h4><p>Now that we’re familiar with the building blocks of what we’ll need, let’s take a look of what Nintex does provide us with to see if we can implement our algorithm.</p><p>A stack can be implemented in Nintex as a collection variable.  Collection variables in Nintex are ordered 1-dimensional arrays (for the programmers out there).  You cannot store something like an object with properties in a collection variable.  If you need a collection of objects, you’d just use multiple collections and load them in the same order (they remain ordered in the way they’re loaded).  Then, for example, you can trust the 3rd item in each collection represents the properties of the 3rd object you loaded. <br />
<img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Nintex collection variable" border="0" alt="Nintex collection variable" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvfC8iqUYqwQWwoiJ6I_eJ7_YMYawruVjMm5j9l1ZYkaTkmC7HFYxNP73M9tmsbcd180bcsxfQBcFAE-Lu6J5LHneFFWiP7TzzZyZPwG4WOvNz3WgoI3YNvwtcdub8IJAqiUjj6QN6VkKV/?imgmax=800" width="480" height="186" /></p><p>Looping can be done with using either a Loop action or a For Each action. <img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Nintex looping actions" border="0" alt="Nintex looping actions" align="right" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiINe48nMjD8V9yqGw_4AWRPTjE9lmTt0DjK5iwfn9diQ8lL-MJZo6IrDtRjxC9Mh0b3Yc2EP-vvdsrrgu_FwZ7JlPfrYftILXvXknU2AE_sGFadf5bYqGEtP5HZH3N3BzqJ1SEiEv6wxem/?imgmax=800" width="88" height="126" /></p><ul><li>The Loop action simply loops until a condition is met that causes it to stop.  </li>
<li>The For Each action loops over the contents of a collection variable, but also has a condition property that will cause it to stop processing. When the loop is first entered, a snapshot of the collection is created and used for the duration of the iterations. As you’ll see in the implementation below, this disqualifies this action from being used against a stack type of collection variable, since any changes we make to the collection during a loop are not detected by the For Each action.  </li>
</ul><h3>Implementation</h3><h4>Summary</h4><p>The screenshot below is high-level example of how the workflow needs to be structured.  Following is an overview of what’s going on.</p><p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlp5rHlODtkRWl0g4qE8UiPk9XsNwDJD8Fk9mHkE20_-aIBc3kElbGnSCATT7UOURAwO45-NWRqHSyGTb11j5IrsK4B_eXESDB-E5p1wnuvBNKbeQrSBR_OhqssaXFiqFrtZw61C-0-iiJ/s1600-h/NintexRecursion%25255B9%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: right; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Example Nintex recursion workflow" border="0" alt="Example Nintex recursion workflow" align="left" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW486eimYmsHUxhMFwUj-lQc0x2BBBhMSqYeTe4Zp9g3a3_AfKjecjthyphenhyphenjr1e8ZHTHCWLfI6C1YAd6tW7kRkW02ZPyTF_fcXzeTEg1mXNxVpDXjA8yiccqD3r8nJpcY-MbjlRVP9UoKWpQ/?imgmax=800" width="60" height="244" /></a></p><ol><li>Push a baseline task to the stack to initialize it. </li>
<li>Loop over the stack. A For Each action cannot be used against the stack collection since the collection is changed inside the loop (pushing and popping). </li>
<li>Process the task.  This includes popping the item(s) off of the stack and fetching the related tasks into a set of collections. As stated previously, a collection per column is required if you want to loop over a set of list items (or “records” or “objects”). </li>
<li>Loop over the related tasks.  This can be a For Each action, since the collections that hold related task data will not change during the loop.  In this loop, finally I can do our date calculations and create the tasks.  A search is done against each item to determine if related tasks exist.  If so, you add it to the stack. </li>
<li>Recalculate the size of the stack and repeat step 2 until the stack is empty.</li>
</ol><p> </p><h4>Nintex Workflow Variables</h4><table><tbody>
<tr> <th width="108">Variable</th> <th width="124">Type</th> <th width="375">Description</th> </tr>
<tr> <td width="108">Stack</td> <td width="124">Collection</td> <td width="375">Stack for fetching related tasks.</td> </tr>
<tr> <td width="108">Date Stack</td> <td width="124">Collection</td> <td width="375">Stack for passing the date as a parameter for calculations.</td> </tr>
<tr> <td width="108">Baseline Id</td> <td width="124">Single line of text</td> <td width="375">Stores the ID of the starting task. The first value in the Stack collection.</td> </tr>
<tr> <td width="108">Stack Count</td> <td width="124">Integer</td> <td width="375">Used by outer loop to determine to finish looping. Used by Collection operation action to count the Stack collection. Initialized to 1.</td> </tr>
<tr> <td width="108">Related Id</td> <td width="124">Single line of text</td> <td width="375">Used to pop the top item off of the Stack.</td> </tr>
<tr> <td width="108">Related Date</td> <td width="124">Date and Time</td> <td width="375">Used to pop the top item off of the Date Stack.</td> </tr>
<tr> <td width="108">index</td> <td width="124">Number</td> <td width="375">Used for synchronizing the retrieval of column data across related task collections.</td> </tr>
<tr> <td width="108">Id Collection</td> <td width="124">Collection</td> <td width="375">Used by query action to store the ID column for related tasks.</td> </tr>
<tr> <td width="108">Title Collection</td> <td width="124">Collection</td> <td width="375">Used by query action to store the Title column for related tasks.</td> </tr>
<tr> <td width="108">Days Collection</td> <td width="124">Collection</td> <td width="375">Used by query action to store the "Days from Related Task" column for related tasks.</td> </tr>
<tr> <td width="108">Id</td> <td width="124">Single line of text</td> <td width="375">Used by Collection operation action to retrieve the ID column from the Id collection.</td> </tr>
<tr> <td width="108">Title</td> <td width="124">Single line of text</td> <td width="375">Used by Collection operation action to retrieve the Title column from the Title collection.</td> </tr>
<tr> <td width="108">Days</td> <td width="124">Number</td> <td width="375">Used by Collection operation action to retrieve the "Days from Related Task" column from the Days collection.</td> </tr>
<tr> <td width="108">Date</td> <td width="124">Date and Time</td> <td width="375">Stores the result of the date calculation for a related task.</td> </tr>
<tr> <td width="108">Related Count</td> <td width="124">Single line of text</td> <td width="375">Stores the result of a query to get the count of related tasks. This is text instead of a number to avoid unnecessary type casting since I'm using the OData feed and a Web request action. Therefore, if this variable has anything besides "0", the current ID and Date are pushed to the stacks.</td> </tr>
</tbody></table><h4>Sample list definition</h4><table width="609"><tbody>
<tr> <th width="168">Column</th> <th width="126">Type</th> <th width="313">Description</th> </tr>
<tr> <td width="168">Title</td> <td width="126">Single line of text</td> <td width="313"> </td> </tr>
<tr> <td width="168">Related Task</td> <td width="126">Lookup</td> <td width="313">Lookup column back to this list</td> </tr>
<tr> <td width="168">Days From Related Task</td> <td width="126">Number</td> <td width="313"> </td> </tr>
</tbody></table><h4>Sample list data</h4><table width="609"><tbody>
<tr> <th>Title</th> <th>Related Task</th> <th>Days From Related Task</th> </tr>
<tr> <td>Gather documents for closing</td> <td>Closing</td> <td>-30</td> </tr>
<tr> <td>Closing</td> <td>Move In</td> <td>-5</td> </tr>
<tr> <td>Set up utilities</td> <td>Move In</td> <td>-30</td> </tr>
<tr> <td>Move In</td> <td> </td> <td> </td> </tr>
</tbody></table><h4>Details</h4><p>For further details, let’s dive in a little deeper into each of the steps summarized above.</p><ol><li>In order to “push” items into our stack, I’m simply adding items to the collection.  This is achieved through the use of a Collection operation action using the Add option. <br />
  <img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Pushing to the stack" border="0" alt="Pushing to the stack" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ8XKG0vFcnwR7zLgyBzJhW14mE0Qf58zFvVgHnY7QvN9r4-epVHUibPMxUSFtdVSRkLuX0tj5tRTCY-UC6xInhcehbuhhCfVORT7zXo9LSOw8xWtEt3_448VU3ysrqQw_NFJIzHyQYQOj/?imgmax=800" width="585" height="144" /> <br />
</li>
<li>Loop over the stack while the count of items in the stack collection is greater than 0. This is achieved using a variable, Stack Count, and evaluating against this in the Loop action. <br />
<img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Stack loop condition" border="0" alt="Stack loop condition" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL3Y1tYSns0NOhNvZU0f62iLkYq5nPzQi6S0lrNKbDjFgxkmYKF5mrNHd4m4eTsLg8boe4hA44pMvkGysGpKiyGS6XEozHYow0KoyXxiPjce6nY7YqWQ3oCDP9ZhNubwqRRaEIN9Jegfdx/?imgmax=800" width="582" height="140" /> <br />
</li>
<li>Process the task. In order to pop items off of the stack, you can again use the Collection operation action with the Pop option as shown in the following screenshot. <br />
<img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Popping from the stack" border="0" alt="Popping from the stack" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7VZop6IanZHt7KLTbh-sy-lAvR0E-n4ZxeDBbVI-258iPnRj77b2d19MiAz2ZbfxDBrKem48xMvyHa_YNbQ3NRIumxBY1waiNR8Y2Id0Li6nnayc2HYGl_5cmAeITwY5ECMujihtm9YOY/?imgmax=800" width="563" height="106" /> <br />
Once I have the Related Id, I can issue a query against our list to get all related tasks.  This can be achieved using either a Query list action or a Query XML action against the <a href="http://msdn.microsoft.com/en-us/library/ff798339.aspx">OData list data feed</a>. To store the related tasks, you’ll create a collection to hold each column that you’re concerned with. For my purposes, I’ll need at least the ID, Title and “Days From Related Task” columns in order to create my list items. This means I’ll have 3 collections (as shown above in the variables list).  <br />
</li>
<li>Loop over the related tasks. This loop action can be a For Each since I’ve already loaded the collections from the previous query (the collections are replaced by the query action in the previous step). This is where the index variable becomes important.  The For Each action will iterate over a collection and store the index (incrementing number starting at 1) in a variable (named appropriately I might say).  <br />
<img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="For Each action properties" border="0" alt="For Each action properties" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9kBLI2fYkHyKemFtJOi8r3vmak_iyiIoI0RXRvBxn7YRlJfuxKtBX8u1hZJ6yXlFYJOmlpO2DiyFSB5gBcaH0LuOt8wqbpvwwnpTYGXbAbQXmFZGdR7-2WooW6Cts4pexDZI2eYkCnTj_/?imgmax=800" width="560" height="187" /> <br />
<br />
The first actions in the loop fetch the values from the remaining collections based on this index: <br />
<img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Collection operation actions to fetch remaining properties" border="0" alt="Collection operation actions to fetch remaining properties" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsoUEqAX7OVFVmaHxFvDRzgvVQFicSCL21jXaJLngzbzWlrR69LCV-18e0TbbzhWUprPtwA0BYBWOU9DtbpYpdA-23CxgvdPpmZJtOiJ7bWBB-A9tDhLoxSOoqzSiAQIgL7RzsjYzp3kNc/?imgmax=800" width="157" height="257" /> <br />
<br />
Here’s an example of what the Get Title operation looks like: <br />
<img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Synchronized Collection operation action" border="0" alt="Synchronized Collection operation action" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrmf2Y7Xeu_ufwpDVJJzYMWbtOk2Ym2mMxIx5FOxSf7_m2sxyskBrbbQITNid9lzRc69kczRtK36YkdJm7ZXH3ciYrxld_4m7170KZxB65dwhfNaABDAduKIzJGQQ5DW5IvlU9r9BPunPi/?imgmax=800" width="555" height="140" /> <br />
<br />
Now that I have all the column values into variables, I can do my date calculation and create my task. <br />
<br />
At this point I had a choice in how to implement my recursion algorithm.  Either I could check for related tasks now (issue a count aggregate query) or later (let step 3 handle what to do if no related tasks were found from the query actions).  I chose to issue a count query (a special quality and appropriate plug for the OData feed) to determine if the current item’s ID and date get pushed to the stack.  This kept the stack cleaner in my opinion and simplified the retrieval logic in step 3. Pushing items to the stacks are accomplished in an identical manner as in step 1 using a Collection operation action with the Add option. <br />
</li>
<li>Recalculate the size of the stack.  This can be done using a Collection operation action with the Count option. <br />
<img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Counting the stack collection" border="0" alt="Counting the stack collection" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTphcAosnE-Eby8dZko9Sxn6taF3y7amQjHaVx5tYsfkcurGSJWHOAyPVxPYtg9-DikVH8Fb6sSka0RUKsmbbjyLISMC8OPC_xQ8PhWXVa37mc8q_DD3TGfffEBsjbSQtuyjKejGiYks2D/?imgmax=800" width="554" height="115" /></li>
</ol><h3>Final concerns</h3><p>There are 2 final things that need to be mentioned here to set you up for success: Maintenance and Safe looping.</p><h4>Maintenance</h4><p>Implementing an algorithm like this in a workflow is an advanced technique and it’s complicated.  Whenever I’m writing complicated C# code, I try my best to make it as simple and possible for the best long-term maintenance story. There are all sorts of software development techniques I can pull from to accomplish that, but I’ll oversimplify them here in a list and we can see how you can do the same thing in Nintex. </p><table width="611"><tbody>
<tr> <th width="279">Code</th> <th width="330">Nintex Workflow</th> </tr>
<tr> <td width="279">Descriptive variable names</td> <td width="330">Descriptive variable names</td> </tr>
<tr> <td width="279">Descriptive method names</td> <td width="330">Descriptive workflow action names</td> </tr>
<tr> <td width="279">Refactor - Extract method</td> <td width="330">Descriptive-named action sets</td> </tr>
<tr> <td width="279">Commenting</td> <td width="330">“User-generated” comments can be added to actions</td> </tr>
<tr> <td width="279">Logging</td> <td width="330">“Log in history list” action</td> </tr>
<tr> <td width="279">Unit testing</td> <td width="330">You’re kidding, right?  No one has written the NintexUnit framework to my knowledge. If you want unit testing in a workflow, you’re most likely going to be using a full-blown Visual Studio workflow with .NET and not Nintex.</td> </tr>
</tbody></table><p>Based on the table above, following is a list of things to make sure you do in a workflow to make it easier to maintain and support over time.</p><ul><li>In most cases, rename the workflow action to explain what it’s doing. Please don’t leave the default text there whenever you have more than 1 type of that action in a workflow. If you do a lot of logging in your workflows, rename each logging action to explain what’s inside. This will help you out later if you ever need to delete a variable that may be referenced in them. </li>
<li>Use descriptive variable names. You can be even more verbose than you could be in code since you can have spaces in your variable names. </li>
<li>You should also add your own comments to the actions whenever there is any doubt as to what a particular action is up to. </li>
<li>Add logging actions whenever you think something may go wrong that might require research.  A common thing to do would be to log your variables.  <br />
<br />
One way to do this is to add text and variables to the “Message to log on completion” text box on the action. This helps to reduce the number of logging actions in your workflow.  I normally do this on actions that set variables, as in the screenshot below where I set the Stack Count variable. <br />
<img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Customized logging within an action" border="0" alt="Customized logging within an action" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTw73ibuNLCeV2_9vOaowp1lm6xlh0KUm-DfXn-OoSKyHvgkMRIV9yYZqCR6atKDTm2SciZnCTBSrNjxWpvWov_u2SNSO2TAE5K05JZWghZj3SlU3ZLkLIc_Aw-911bAto50B1K1mP-VSf/?imgmax=800" width="586" height="337" /> </li>
<li>Use Action set actions to group related workflow actions whenever possible.  You can then minimize them to make the workflow easier to navigate.</li>
</ul><h4>Safe looping</h4><p>I can no longer avoid mentioning Safe looping, the elephant in the room. Safe looping is a Nintex global setting in Central Admin that makes sure someone doesn’t write an out of control looping algorithm (real easy to do that with the State Machine workflow action) that would bring a web front end server down with excessive load. It does this by inserting a pause of 5 minutes (derived from a timer job interval setting in your SharePoint environment) for each loop iteration during publish time. This means our Loop action will have pauses inserted while we’re “stacking it where they lacking it” (really sorry for that, but I couldn’t resist). This is not good, folks. When I was working with a client on a similar workflow, the difference between having safe looping turned off or on was whether the workflow would finish in 30 seconds or 30 minutes, respectively. Waiting 30 minutes for a workflow was not an acceptable option.</p><p>The solution is easier than you think, and you don’t have to sacrifice the safety of your farm in the process, either. All you need to do is make sure that safe looping is turned off when you publish your workflow. Since this setting requires an iisreset to take effect immediately, you could use the following steps to make sure you have a delay-free workflow at run-time.</p><ol><li>Turn Safe looping off </li>
<li>Do an iisreset to force the setting to refresh </li>
<li>Publish your workflow </li>
<li>Turn Safe looping back on </li>
<li>Do an iisreset to force the setting to refresh</li>
</ol><p>This is obviously something you’d do outside of normal business hours, since users don’t tend to like the server going down (FAIL!). Since safe looping is enforced by modifying your workflow by injecting pauses during a publish, you can “safely” turn Safe looping back on and it won’t go back and mess with your workflow.   </p><p>Is this setting a deal-breaker in your environment? Well, even though Nintex comes out of the box with Safe looping turned on, it is, after all, still a Central Admin setting in the UI, so this is definitely a supported configuration. As long as you exercise caution when overriding the default, it should be viewed like any other executable code you put in production.  Hopefully you have multiple environments where you can do your workflow testing, so there should be a certain level of confidence by the time you’re ready to publish a workflow in a production farm, which is then followed by testing.</p><p>In my opinion Nintex workflows are supposed to help us solve a problem quickly, but I think this setting often gets directly in the way of that. Perhaps we’ll see some enhancements in the Nintex platform in the future to better support something like the steps I listed above, but it’s quite possible it won’t happen given the general direction of SharePoint 2013 moving away from web front ends being responsible for executing workflows.  </p><h3>Conclusion</h3><p>It’s likely that your implementation of recursion will have nothing to do with a self-referencing lookup column, in which case the fashion in which you control how items get pushed and popped from your stacks would be completely different.  Whatever the use case, I hope you see from my example how you could achieve recursion walking any tree of related items.</p><p>And, last but not least, I’ve <a title="Recursion example workflow export" href="https://dl.dropbox.com/u/101304566/Recursion_Template.nwf">exported a site workflow</a> that you can use to inspect the design a little closer, or even use as a starting point for your recursion needs.  I wrapped the whole workflow in an action set so you can save it as a snippet and move it to any other type (such as a reusable template or list workflow).</p><p>Cheers!</p><div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:553b5e26-da8b-427e-beb4-5e2ae80b6c79" class="wlWriterEditableSmartContent">Technorati Tags: <a href="http://technorati.com/tags/Nintex" rel="tag">Nintex</a>,<a href="http://technorati.com/tags/Workflow" rel="tag">Workflow</a>,<a href="http://technorati.com/tags/SharePoint" rel="tag">SharePoint</a>,<a href="http://technorati.com/tags/Recursion" rel="tag">Recursion</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com1tag:blogger.com,1999:blog-5755313800906142262.post-27171983585361697572012-06-03T00:01:00.001-05:002012-06-04T22:40:59.290-05:00Ext Js 4 presentation at VNext Dallas<p>I had the privilege of presenting a couple months ago at the VNext Dallas user group on my experiences with Ext Js 4 during a recent engagement at a client. The slide deck is embedded below. If you’d like a little more context than what you can get in the slides (<a title="Why my slides don't have a bunch of bullets" href="http://simplyaprogrammer.com/2008/08/presentation-of-technical-content.html">I make the slides like this for a reason, you know</a>), I would point you towards <a href="http://usergroup.tv/videos/ext-js">the video of the presentation</a> on <a href="http://usergroup.tv">UserGroup.tv</a>, the guys that make the existence of the video possible. </p><p>I’d like to thank <a href="http://www.ShawnWeisfeld.com">Shawn Weisfeld</a> especially for recording this session, and for always showing up at the user group meetings no matter the size! His dedication to the community is unlike anything I’ve seen, folks.</p><br />
<embed type="application/x-shockwave-flash" src="http://s0.videopress.com/player.swf?v=1.03" width="400" height="300" wmode="direct" seamlesstabbing="true" allowfullscreen="true" allowscriptaccess="always" overstretch="true" flashvars="guid=sxxdq5D2&isDynamicSeeking=true"></embed><br />
<br />
<div style="width: 425px" id="__ss_13178417"><strong style="margin: 12px 0px 4px; display: block"><a title="Ext JS 4" href="http://www.slideshare.net/simplyaprogrammer/ext-js-4" target="_blank">Ext Js 4 - Slides</a></strong> <iframe height="355" marginheight="0" src="http://www.slideshare.net/slideshow/embed_code/13178417" frameborder="0" width="425" marginwidth="0" scrolling="no" allowfullscreen></iframe> <div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"></div></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com0tag:blogger.com,1999:blog-5755313800906142262.post-34251080118457112982011-02-18T23:54:00.002-06:002011-02-19T00:49:50.862-06:00“Prism is not a MVVM framework!” and other goodness from the MVVM SmackdownA couple months ago I participated in a friendly “smackdown” of MVVM frameworks at the <a href="http://ntsilverlight.com/">North Texas Silverlight User Group</a>. To sum up the exchange, I represented the <a href="http://compositewpf.codeplex.com/">Prism</a> offering from the Microsoft Patterns and Practices group (which is awesome by the way for composite applications), <a href="http://www.david-yancey.com/blog/">Dave Yancey</a> presented <a href="http://jounce.codeplex.com/">Jounce</a>, <a href="http://coderhaven.wordpress.com/">Bonas Khanal</a> presented <a href="http://mvvmlight.codeplex.com/">MVVM Light</a>, and <a href="http://www.codeconfessions.com/">Justin Weinberg</a> presented <a href="http://caliburnmicro.codeplex.com/">Caliburn Micro</a>. If you want more information about my provocative post title, you’ll need to get the context from my video in the list below.<br />
<ul>
<li><a href="http://live.ineta.org/Presentation/ViewVideo/190">MVVM Smackdown Pt 1: Prism by Tim Jones</a> </li>
<li><a href="http://live.ineta.org/Presentation/ViewVideo/192">MVVM Smackdown Pt 2: Jounce by David Yancy</a> </li>
<li><a href="http://live.ineta.org/Presentation/ViewVideo/189">MVVM Smackdown Pt 3: MVVM Light by Bonas Khanal</a> </li>
<li><a href="http://live.ineta.org/Presentation/ViewVideo/191">MVVM Smackdown Pt 4: Caliburn Micro by Justin Weinberg</a> </li>
</ul>
<div class="wlWriterEditableSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:4e1dec0f-7a7b-4f84-a917-93329ac6bebe" style="display: inline; float: none; margin: 0px; padding: 0px;">
Technorati Tags: <a href="http://technorati.com/tags/MVVM" rel="tag">MVVM</a>,<a href="http://technorati.com/tags/Silverlight" rel="tag">Silverlight</a>,<a href="http://technorati.com/tags/Prism" rel="tag">Prism</a>,<a href="http://technorati.com/tags/Jounce" rel="tag">Jounce</a>,<a href="http://technorati.com/tags/MVVM+Light" rel="tag">MVVM Light</a>,<a href="http://technorati.com/tags/Caliburn+Micro" rel="tag">Caliburn Micro</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com1tag:blogger.com,1999:blog-5755313800906142262.post-41525132597566732312010-09-09T01:09:00.002-05:002010-09-09T11:16:58.160-05:00WCF BasicHttpBinding does not always mean W3C SOAP 1.1<p>I spent a couple hours tonight troubleshooting why a vendor's SOAP 1.1 service couldn't talk to WCF's <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.basichttpbinding.aspx">BasicHttpBinding</a>. In this case I was getting the exception:</p> <blockquote> <p><strong>Server returned an invalid SOAP Fault.</strong></p> <p>Inner Exception: XmlException: Message=End element 'Fault' from namespace 'http://schemas.xmlsoap.org/soap/envelope/' expected. Found element 'tla:billybob' from namespace 'http://schemas.xmlsoap.org/soap/envelope/'. Line 2, position 568.</p> </blockquote> <p>Was the service sending .NET bad XML?  What’s the deal?  Well, the exception goes into more detail complaining about an unexpected element.</p> <p><a href="http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383507">W3C says</a> regarding SOAP 1.1. faults:</p> <blockquote> <p>Other Fault subelements MAY be present, provided they are namespace-qualified.</p> </blockquote> <p>Turns out it the service is just fine according to W3C since these extra fault elements were namespace-qualified. However, BasicHttpBinding is more restrictive than the SOAP spec.  Man, this is giving me a flashback of <a href="http://simplyaprogrammer.com/2008/06/parsing-dates-for-web-feeds-in-net.html">.NET being picky with date-parsing</a>! The issue was that .NET only supports SOAP 1.1 in this binding if it's also <a href="http://www.ws-i.org/profiles/basicprofile-1.1-2004-08-24.html#SOAPFAULT">WS-I BP 1.1 compliant</a>:</p> <blockquote> <p><a name="R1000">R1000</a>: When an ENVELOPE is a Fault, the <code>soap:Fault</code> element MUST NOT have element children other than <code>faultcode</code>, <code>faultstring</code>, <code>faultactor</code> and <code>detail</code>.</p> </blockquote> <p>I calmly pointed out to said vendor that I thought it would be a good idea to update their SOAP endpoint to be a little more friendly with .NET. We’ll see!</p> <p> </p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:cea17a30-2d13-44ad-86e8-41b6491fcf18" class="wlWriterEditableSmartContent">Technorati Tags: <a href="http://technorati.com/tags/WCF" rel="tag">WCF</a>,<a href="http://technorati.com/tags/BasicHttpBinding" rel="tag">BasicHttpBinding</a>,<a href="http://technorati.com/tags/SOAP+1.1" rel="tag">SOAP 1.1</a>,<a href="http://technorati.com/tags/.NET" rel="tag">.NET</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com0tag:blogger.com,1999:blog-5755313800906142262.post-42435768034783709152008-08-27T22:14:00.004-05:002013-05-23T14:29:51.326-05:00Using SharePoint for Extranets<p><a href="http://windowsitpro.com/systems-management/using-sharepoint-extranets-27-aug-2008" title="Using SharePoint for Extranets in Windows IT Pro Magazine"><img align="left" alt="Using SharePoint for Extranets in Windows IT Pro magazine" border="0" height="260" src="http://lh3.ggpht.com/pictures.jones.family/SLYYGzthwcI/AAAAAAAAAF4/iuxQOHYqOGk/Using%20SharePoint%20for%20Extranets%20-%20Sept%202008%20-%20Windows%20IT%20Pro%5B6%5D.jpg?imgmax=800" style="border-width: 0px; margin: 0px 15px 0px 0px;" width="193" /></a> My article, <a href="http://windowsitpro.com/systems-management/using-sharepoint-extranets-27-aug-2008" title="Using SharePoint for Extranets in Windows IT Pro">Using SharePoint for Extranets</a>, was just published in the September issue of Windows IT Pro magazine. In my <a href="http://simplyaprogrammer.com/2008/05/importing-files-into-sharepoint.html" title="Importing files into a SharePoint document library using regular expressions and WebDAV">post</a> about importing files into SharePoint, I hinted that I had replaced a custom extranet with a WSS 3.0 extranet. This article is a brief summary of what I learned about configuring SharePoint as an extranet platform during that project.</p><p>Only after this experience did I decide that something needed to be written about this subject. Specifically, I spend quite a bit of time advocating zone address consistency (having 2 or more authentication zones use the same site address). I was a bit disappointed that SharePoint ignored IIS's IP address binding feature, which makes collaboration much easier when multiple zones are used, or when SSL is needed (especially for extranets).</p><p>It's brief because it had to be. As you would be able to tell by reading some of my posts, I lean towards using more words than less to get my points across. When I first submitted the article to them, it was over 4000 words. I had to delete 1500 words to fulfill the article submission requirements. Of course this almost required a complete rewrite, and it's most likely the reason that the online edition of the article contains an extra sidebar and 2 graphics that wouldn't fit in the printed edition.</p><p>The gracious editorial staff (thanks Gayle!) over at Windows IT Pro agreed to allow the public access to the online edition of the article for about 2 weeks so I can share it with you.</p><div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:f8602cff-33da-48ad-95bd-96f424f887f4" style="display: inline; margin: 0px; padding: 0px;">Technorati Tags: <a href="http://technorati.com/tags/SharePoint" rel="tag">SharePoint</a>,<a href="http://technorati.com/tags/Extranet" rel="tag">Extranet</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.comtag:blogger.com,1999:blog-5755313800906142262.post-30777572716287607272008-08-24T22:36:00.007-05:002013-07-27T10:10:39.088-05:00Integrating LexisNexis InterAction with Portals<p align="left"><a title="Download the white paper Integrating LexisNexis InterAction with Portals" href="https://dl.dropboxusercontent.com/u/101304566/IntegratingInterActionWithPortals.pdf">Printer and reader-friendly version</a><br />
<a title="Jump to the slide deck" href="#SlideDeck">Slide deck</a></p><p>This white paper discusses how to integrate the LexisNexis InterAction CRM into a Web-based portal alongside data from other systems. The reason for this paper’s existence is my presentation at the 2008 International Legal Technology Association (ILTA) conference. I was one of 3 panelists for the session titled “InterAction - A Different Perspective Using Your Firm’s Portal”. </p><p>This paper and my presentation answers the How question, assuming that you already know about the What (InterAction) and the Why (the value gained by contextualizing business entity information in a portal). In order to best answer the How question, I’ve broken down the process into 3 steps: prepare, design and build. Specifically, I’m referring to preparing the integration, designing the presentation and building the solution.</p><h3>Prepare the Integration</h3><p>Before we can discuss how to integrate InterAction into a portal, we need to know some background information. First, we need to know what portals are and what integration is.</p><h4>Portals</h4><p>Originally, portals were simply entryways, such as doors or gates. Recently, information technologists have used the word to indicate a digital entry point for multiple information sources. Portals exist on a variety of platforms, but the ones most popular today are web sites. The popularity of portals has exploded recently because of their ability to present data from diverse sources in a unified, yet customized, way. Instead of requiring the user to access each data source individually, a portal can surface all of them on a single page, allowing a dashboard-type overview. Portals are typically centered on a topic or entity of importance. For example, portals can currently be located that bring together information related to news, sports and fitness. For public portals such as Yahoo or iGoogle, the entity is you. </p><p>As an example, imagine an enterprise portal site within your organization that, instead of displaying information customized to a user, displays information about one of your clients. A client’s documents from your document management system might be displayed alongside their contacts from your contact relationship manager (CRM) and revenue information from your billing system. In order to present a page with all of this information, a portal needs to be intelligent enough to inquire about this client from each system one at time. This information is then gathered and displayed on the screen in its assigned locations. The portal is able to do this only because it knows how to ask each system for the right data. And the reason it knows how to ask is because these systems are related to each other, or integrated in some way. </p><h4>Integration</h4><p>Systems integration could be explained as relationships between systems. Systems can integrate with one another only after they share something in common. This common information is usually in the form of entities, such as people, companies or projects. For example, a billing system contains the client name and its billing contact information. The CRM also contains this information about the client. In order to make these systems talk, a translation needs to be created in order to map one entity to another. This mapping information is usually stored in one of the systems, but it’s possible to also store it externally in an independent system.</p><h5>Unique Identifiers</h5><p>Within each system, data structures are usually in place to easily manage entity data. For example, in a billing system’s database, instead of copying a client’s name into every corner of the system related to a client, a unique identification (ID) number is created for the client and referenced. The use of an ID allows updated information to be shown across the entire system whenever a change is made to an entity’s data. </p><p>Using a unique ID is not merely for convenience or to save disk space, however. Use my name, for example: Tim Jones. My name is very common. Therefore, the chances of finding another person with the name Tim Jones are higher than normal. If a portal were to ask the billing system, “Show me all the invoices for Tim Jones”, it might reply with “Which Tim Jones? I have 3 clients with that name.” In this case, it’s very important that the portal knows which Tim Jones it needs. It not only needs to know my name, but it most likely needs to ask for my information by my ID. Since the portal needs to get the ID from somewhere first, we need to decide where to store the IDs and their corresponding entity mappings.</p><h5>Integrating with InterAction</h5><p>InterAction has been designed with systems integration in mind. Besides single and multi-value user-defined fields (InterAction refers to them as Additional Fields), InterAction includes an EAI (Enterprise Application Integration) component called Application Collaboration (AC). AC abstracts InterAction’s data model to make it easy to load data from other systems. AC can import new information and keep existing entity relationship data clean and current, making it the obvious alternative to manual data entry.</p><p>Given its focus on integration, InterAction makes a compelling case for the storage of entity relationship data for other systems. In addition, InterAction offers several options for portal integration. Sometimes the amount of entity data needed from a specific system is very minimal, in which case it may be ideal to import this information entirely into InterAction. This would simplify the portal by reducing the number of systems that need to be queried and also increase performance of page loading.</p><p>InterAction is the system most likely to be maintained by a team of stewards within a firm. There will be times when integration data issues arise, requiring human intervention. If you’ve chosen to use InterAction for integration, you would already have resources available to keep the integration running smoothly.</p><h3>Design the Presentation</h3><p>To design the presentation of InterAction data in your portal, you’ll first need to choose a data retrieval option. After the data retrieval option is chosen, you’ll need to choose a presentation method.</p><h4>Choose a Retrieval Option</h4><p>InterAction offers an above average amount of options for retrieving data, but since we’re dealing with portals, we’re only interested in those options that are Web-based (server side), or at least Web-friendly. To best design the presentation of InterAction data, we’ll need to know what these options are, then determine which combination of them will best accomplish our needs. InterAction includes 4 different server-side options for data retrieval. They are presented here in chronological order.</p><h5>Database Query</h5><p>The first and oldest retrieval option is to write a query directly against the InterAction database. This option has been available for the life of the product. Developers familiar with SQL will have no problems writing reports and reverse engineering InterAction’s data model. However, the database model is very complicated and might change after an upgrade, breaking existing queries. If you choose this option, also be aware that you are bypassing InterAction's security model. Because of these reasons and the obvious risk of messing something up, InterAction does not recommend it. As of 5.5, InterAction only supports the Microsoft SQL Server database management system.</p><h5>InterAction Web Client</h5><p>The InterAction web client has been available since version 4.x. Significant enhancements were made in version 5.0, including custom search forms. Displaying data directly from the web client should be considered an option since it’s already a server side solution. If the standard views in the web client do not show the needed information, minor customizations can be made. However, InterAction doesn’t recommend this since customizations are typically lost during an upgrade. Customization involves manipulating objects called nuggets, and requires knowledge of HTML and XML.</p><h5>InterAction XML Methods</h5><p>The recommended server-side data retrieval method is to use XML methods hosted on the InterAction application server. XML methods have been available since InterAction 4.x, and function like an Application Programming Interface (API). XML methods not only retrieve data, but also have the capability of updating it as well. XML methods are called by requesting a URL with the method name and its required parameters. The output of the method is XML text. The InterAction web client contains a list of available methods and documentation. </p><p>Extensible Markup Language, or XML, is data that describes itself. Unlike HTML, the markup language used for web sites, XML is not restricted to a predefined set of elements. It was designed to be a universal data format that all systems could understand. Even though XML has been around for 10 years, blogs and other news outlets syndicating information using XML in recent years have caused its popularity to increase rapidly. Even Microsoft has decided to change the Office suite’s native file formats to XML instead of the previous binary formats as of Office 2007. Later on in Appendix A, you can see an example of XML in use by InterAction.</p><h5>SQL Server Reporting Services</h5><p>As of version 5.5, SQL Server Reporting Services (SSRS) is integrated into InterAction’s reporting module. Since SSRS is web-based, this becomes another option to consider. This is by far the most user-friendly server-side method of extracting data from InterAction, and also extremely versatile. SSRS introduces several useful features such as aggregates, grouping, caching and charting. Furthermore, SSRS also supports 3<sup>rd</sup> party controls to extend functionality, such as more advanced contextual charting. Even though SSRS contains “SQL” in its name, this option only requires knowledge of basic report writing. </p><p><b> </b></p><h5>Summary</h5><p>Remember, your needs may require you to use a combination of these options. If the data you need from InterAction is not accessible from an XML method or SQL report, you may be forced to write a database query. </p><p>To make it easier to remember the retrieval options, Figure 1 summarizes them by plotting the implementation difficulty against InterAction's recommendations.</p><p><img style="border-width: 0px;" alt="InterAction data retrieval methods graph" src="http://lh3.ggpht.com/pictures.jones.family/SLIon89pM3I/AAAAAAAAAFw/a59ZzHwhSzs/IA_Retrieval_Methods_WherePlot5.png?imgmax=800" width="506" border="0" height="480" /> </p><p>Figure 1 InterAction Data Retrieval options by recommendation and difficulty</p><p><b> </b></p><h4>Choose a Presentation Method</h4><p>When choosing to display external data from a system into a web page, you usually have a couple options.</p><h5>Custom Programming</h5><p>Since portals are web sites, we need to take the output of a database query or a system’s API and convert it to HTML somehow. Web application frameworks such as ASP.NET exist to make this easier. However, even with the help of ASP.NET, XML is challenging to work with. Because of this, XSLT was created to transform XML into other forms, such as HTML. However, even if you use XSLT it still requires the services of a Web developer familiar with XML and HTML.</p><h5>Screen Scraping</h5><p>If the external data being integrated is already contained on a web site, you can use a method called screen scraping to embed that information into a portal. This method is sometimes used out of necessity, if the system being scraped doesn’t have an easy alternative method of exposing its data. For example, Google’s product search feature scrapes vendor web sites to determine pricing. It does this since a common API doesn’t exist (and most likely won’t exist anytime soon) between them.</p><h4>Microsoft SharePoint</h4><p>Microsoft SharePoint is a popular portal platform in use today by enterprises. If your firm plans on using SharePoint, you can take advantage of its built-in tools. A variety of ways exist within the SharePoint development ecosystem to implement custom programming on web pages. One of the popular choices is to use a component called a Web Part. Multiple Web Parts can be arranged on a single page to create any desired layout. The following list of SharePoint Web Parts could be used for integration purposes.</p><h5>Page Viewer Web Part</h5><p>The Page Viewer Web Part is used for simple screen scraping. Behind the scenes, it simply renders an iframe HTML element. This could be used to show portions of a custom web site, the InterAction Web Client or a SQL report. </p><p>To best display SQL reports, you will want to hide the header and toolbars. In order to do this, you need to access the report from the reportserver path instead of the default reports path that shows the SQL Report Manager web site. The parameters you would use need to be hidden and the values passed in the address of the report. Finally, to hide the toolbar, add "&rc:toolbar=false" to the address of the report as well. The following is an example call to a parameterized SQL report that displays without any headers or toolbars. In this example, MatterNumber and MonthsHistory are hidden parameters.</p><p><code>http://webserver/ReportServer?/Portal/MatterSummary&rs:Command=Render&rc:toolbar=False&MatterNumber=999999.999999&MonthsHistory=2</code></p><h5>Content Editor Web Part</h5><p>The Content Editor Web Part can also be used for screen scraping. It allows adding custom HTML fragments onto the page. If you need more control over the presentation of your iframe, you'd probably use this instead of a Page Viewer.</p><h5>SQL Report Viewer Web Part</h5><p>If you use SQL Reporting Services in SharePoint integrated mode and you've installed the Reporting Services Add-in for SharePoint Technologies, you can use the Report Viewer Web Part to display SQL reports. SharePoint integrated mode is only available for SQL Server 2005 SP2 and either Windows SharePoint Services (WSS) 3.0 or Microsoft Office SharePoint Server (MOSS) 2007. In an integration scenario, you're most likely going to need to pass parameters to the report. However, this Web Part will not allow you to pass parameters to the report and hide the toolbar at the same time. Because of this, you'll most likely be sticking to screen scraping with a Page Viewer.</p><h5>XML Web Part</h5><p>The XML Web Part transforms XML using XSLT. Its data source can be XML entered directly, or hardcoded, into the configuration, or a link to a Web page or file that returns XML. Likewise, the XSLT can also be hardcoded or linked. If you link to an external source for your XML or XSLT, make sure it is available to SharePoint’s application pool identity or by anonymous access. If you've configured SharePoint for Kerberos authentication, make sure the user has access to the path.</p><h5>Data View Web Part</h5><p>The Data View Web Part is only available through FrontPage 2003 (for WSS 2.0) or SharePoint Designer 2007 (for WSS 3.0). Data View supports a variety of data sources, such as database queries, XML, web services and SharePoint lists.</p><h5>Business Data Catalog Web Parts</h5><p>If you use MOSS 2007 Enterprise edition, you can use the Business Data Catalog (BDC) Web Parts to pull information from web services or databases. This would simply be an alternative to the Data View Web Part.</p><h5>Custom Web Part</h5><p>If you are not satisfied with the Web Parts shipped with SharePoint, you can either purchase one from a 3<sup>rd</sup> party Web Part vendor or create your own. To create your own Web Part, you'll need a programmer familiar with .NET. Alternatively, if you already have Visual Studio you can create a user control using a visual designer. User controls cannot be used in SharePoint out of the box, so you'll need to download and install a free Web Part called SmartPart.</p><h5>Other Custom Solutions</h5><p>Since SharePoint is simply an ASP.NET application, it can accept other custom .NET controls besides Web Parts. It’s much easier to write a custom .NET control than a custom reusable Web Part. SharePoint also contains a capability called Application pages that allows custom server-side .NET coding and higher performance.</p><h3>Build the Solution</h3><p>This section outlines the steps necessary to present the output of an InterAction XML method within a Microsoft SharePoint portal. The data will be displayed by transforming XML into HTML using the XML Web Part.</p><h4>Calling the XML Method</h4><p>There are several ways to use InterAction XML methods depending on your environment and requirements. The following explanation describes one technique of calling XML methods from another web application. In this case, SharePoint is that web application.</p><h5>Bypass default authentication</h5><p>By default, XML web methods authenticate the caller using the same authentication (IIS authentication) as the web client. Unless you are using Kerberos delegation, SharePoint will be calling the XML method on the user's behalf. This breaks the normal single sign-on authentication used by InterAction. One alternative to this configuration is to give SharePoint's service account access to InterAction.</p><p>In order to make XML methods easier to execute in these scenarios, InterAction has created an alternative authentication mechanism on a custom port, port 8100. Accessing an XML method over port 8100 instead of the default port 80 bypasses the default authentication.</p><p>For example, instead of using <code>http://webserver/interaction</code>, you would use <code>http://webserver:8100/interaction</code></p><h5>URL authentication</h5><p>If you choose to use port 8100 for your Web Parts, you will need to use parameters to authenticate. The two parameters needed are accountName and userPassword. InterAction will execute the XML method based on the privileges and security context associated with the user account supplied in the accountName parameter. </p><p>If you would like to avoid hardcoding the password into the URL, you can also establish a trust relationship between InterAction and the portal server. To do this, add the SharePoint server's IP address to the trustedAddresses entry in InterAction.cfg and then restart InterAction services. In this configuration, you only need to provide the accountName parameter to tell InterAction which user context to execute under.</p><p>Note: Trusting SharePoint might give too much power to users with designer privileges. Before trusting SharePoint, consider who has access to Web Parts that could exploit this trust.</p><h5>Example XML Method Call</h5><p>An example call the findContacts XML method is displayed below. This example retrieves the first (using maxResultCount) contact that bob has access to with last name Jones. Since the URL is missing the userPassword parameter, results will only be returned if the IP address making the request is trusted by InterAction.</p><p><code>http://webserver:8100/interaction/findcontacts?accountname=bob&lastname=jones&maxresultcount=1</code></p><h4>Creating the Web Part</h4><p>To insert an XML Web Part, put any SharePoint page in Edit mode then add it from the list of available Web Parts. Insert the path to the InterAction XML method and add the parameters. Then, insert the XSLT into the Web Part. If you need to share this Web Part to multiple pages, you should consider placing the XSLT in a file and linking to it instead of embedding it. After clicking OK, the XSLT will be applied against the XML and the HTML output will be displayed on the screen. </p><p>Appendix A contains an example XML output from the findcontacts method, XSLT to transform it into HTML, and the resulting HTML and screenshot of the output.</p><h3>Conclusion</h3><p>As of 2008, systems integration is still not an easy task. This paper intends to cover just enough material to grasp the majority of the issues involved with integrating InterAction into a portal. If you would prefer to hear this information presented, ILTA will be making an audio recording of my presentation available for download. </p><h3>Appendix A</h3><p>This section contains examples of XML and XSLT for InterAction XML Methods.</p><h3>Sample XML</h3><h4>Request URL for the findContacts method</h4><p><code>http://webserver:8100/interaction/findcontacts?useraccount=bob&userpassword=123&contactid=1231231234&includeadditionalfields=2/1231;2/1232;2/1233</code></p><p><b> </b></p><h4>XML results</h4><div style="border: 1px dashed black; background: white none repeat scroll 0% 50%; padding-left: 20px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-bottom: 15px; padding-bottom: 20px;font-family:lucida console;font-size:9pt;color:black;"><pre style="margin: 0px;"><span style="color:blue;"><?</span><span style="color: rgb(163, 21, 21);">xml</span><span style="color:blue;"> </span><span style="color:red;">version</span><span style="color:blue;">=</span>"<span style="color:blue;">1.0</span>"<span style="color:blue;"> </span><span style="color:red;">standalone</span><span style="color:blue;">=</span>"<span style="color:blue;">yes</span>"<span style="color:blue;"> ?></span></pre><pre style="margin: 0px;"><span style="color:blue;"><</span><span style="color: rgb(163, 21, 21);">methodResponse</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">methodname</span><span style="color:blue;">></span>findcontacts<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">methodname</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">requestId</span><span style="color:blue;">></span>73203<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">requestId</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">errorCode</span><span style="color:blue;">></span>0<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">errorCode</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">errorText</span><span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">processingTime</span><span style="color:blue;">></span>00:00:03.234<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">processingTime</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">results</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">resultCode</span><span style="color:blue;">></span>0<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">resultCode</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">resultText</span><span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">resultCount</span><span style="color:blue;">></span>1<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">resultCount</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">totalResultCount</span><span style="color:blue;">></span>1<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">totalResultCount</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">result</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">contactClass</span><span style="color:blue;">></span>IAL<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">contactClass</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">contactId</span><span style="color:blue;">></span>1231231234<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">contactId</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">IAContactId</span><span style="color:blue;">></span>1231231234<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">IAContactId</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">contactState</span><span style="color:blue;">></span>4<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">contactState</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">contactStateSortOrder</span><span style="color:blue;">></span>3<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">contactStateSortOrder</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">contactStateToolTip</span><span style="color:blue;">></span>My Contact and Firm Contact<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">contactStateToolTip</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">contactType</span><span style="color:blue;">></span>person<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">contactType</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">displayName</span><span style="color:blue;">></span>Jones, Tim<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">displayName</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">department</span><span style="color:blue;">></span>Information Technology<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">department</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">office</span><span style="color:blue;">></span>Houston<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">office</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">changeDate</span><span style="color:blue;">></span>2007-05-02 09:37:49.547<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">changeDate</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">inMyFolder</span><span style="color:blue;">></span>true<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">inMyFolder</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">isPrivate</span><span style="color:blue;">></span>false<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">isPrivate</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">sourceFolderId</span><span style="color:blue;">></span>12884901886<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">sourceFolderId</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">salutation</span><span style="color:blue;">></span>Mr.<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">salutation</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">firstName</span><span style="color:blue;">></span>Tim<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">firstName</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">lastName</span><span style="color:blue;">></span>Jones<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">lastName</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">commonName</span><span style="color:blue;">></span>Tim<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">commonName</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">fullName</span><span style="color:blue;">></span>Tim Jones<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">fullName</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">jobTitle</span><span style="color:blue;">></span>Web Developer<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">jobTitle</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">ape</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">electronicAddress</span><span style="color:blue;"> </span><span style="color:red;">type</span><span style="color:blue;">=</span>"<span style="color:blue;">E-mail</span>"<span style="color:blue;"> </span><span style="color:red;">typeId</span><span style="color:blue;">=</span>"<span style="color:blue;">1</span>"<span style="color:blue;"> </span><span style="color:red;">relationship</span><span style="color:blue;">=</span>"<span style="color:blue;">Business</span>"<span style="color:blue;"> </span><span style="color:red;">relationshipId</span><span style="color:blue;">=</span>"<span style="color:blue;">1</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">electronicAddressId</span><span style="color:blue;">></span>1231231234<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">electronicAddressId</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">description</span><span style="color:blue;">></span>Office<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">description</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">formattedElectronicAddress</span><span style="color:blue;">></span>tim.jones@company.com<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">formattedElectronicAddress</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">isGlobal</span><span style="color:blue;">></span>true<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">isGlobal</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">owningContactId</span><span style="color:blue;">></span>1231231234<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">owningContactId</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">electronicAddress</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">ape</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">additionalFields</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">additionalField</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">definitionId</span><span style="color:blue;">></span>1231231231<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">definitionId</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">name</span><span style="color:blue;">></span>Building Floor<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">name</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">value</span><span style="color:blue;">></span>23<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">value</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">additionalField</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">additionalField</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">definitionId</span><span style="color:blue;">></span>1231231233<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">definitionId</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">name</span><span style="color:blue;">></span>Map URL<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">name</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">value</span><span style="color:blue;">></span>http://officemaps/HOU23.aspx?Mylbl=lbl23123<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">value</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">additionalField</span><span style="color:blue;">> </span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">additionalField</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">definitionId</span><span style="color:blue;">></span>1231231232<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">definitionId</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">name</span><span style="color:blue;">></span>EmpPicture<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">name</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">value</span><span style="color:blue;">></span>http://portal/photos/timjones.jpg<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">value</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">additionalField</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">additionalFields</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">result</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">results</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">resultBytes</span><span style="color:blue;">></span>3657<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">resultBytes</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">methodResponse</span><span style="color:blue;">></span></pre></div><h3>Sample XSLT for the findContacts method</h3><div style="border: 1px dashed black; background: white none repeat scroll 0% 50%; padding-left: 20px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-bottom: 15px; padding-bottom: 20px;font-family:lucida console;font-size:9pt;color:black;"><pre style="margin: 0px;"><span style="color:blue;"><?</span><span style="color: rgb(163, 21, 21);">xml</span><span style="color:blue;"> </span><span style="color:red;">version</span><span style="color:blue;">=</span>"<span style="color:blue;">1.0</span>"<span style="color:blue;"> </span><span style="color:red;">encoding</span><span style="color:blue;">=</span>"<span style="color:blue;">UTF-8</span>"<span style="color:blue;"> ?></span></pre><pre style="margin: 0px;"><span style="color:blue;"><</span><span style="color: rgb(43, 145, 175);">xsl:stylesheet</span><span style="color:blue;"> </span><span style="color:red;">version</span><span style="color:blue;">=</span>"<span style="color:blue;">1.0</span>"<span style="color:blue;"> </span><span style="color:red;">xmlns:xsl</span><span style="color:blue;">=</span>'<span style="color:blue;">http://www.w3.org/1999/XSL/Transform</span>'<span style="color:blue;">></span></pre><pre style="margin: 0px;"></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:template</span><span style="color:blue;"> </span><span style="color:red;">match</span><span style="color:blue;">=</span>"<span style="color:blue;">/</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:choose</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:when</span><span style="color:blue;"> </span><span style="color:red;">test</span><span style="color:blue;">=</span>"<span style="color:blue;">/methodResponse/results/resultCount != '1'</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">br</span><span style="color:blue;">/><</span><span style="color: rgb(163, 21, 21);">strong</span><span style="color:blue;">></span>Couldn't find this contact ID in InterAction<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">strong</span><span style="color:blue;">><</span><span style="color: rgb(163, 21, 21);">br</span><span style="color:blue;">/></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(43, 145, 175);">xsl:when</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:otherwise</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:apply-templates</span><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">/methodResponse/results/result</span>"<span style="color:blue;">/></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(43, 145, 175);">xsl:otherwise</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(43, 145, 175);">xsl:choose</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(43, 145, 175);">xsl:template</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:template</span><span style="color:blue;"> </span><span style="color:red;">match</span><span style="color:blue;">=</span>"<span style="color:blue;">/methodResponse/results/result</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">table</span><span style="color:blue;"> </span><span style="color:red;">border</span><span style="color:blue;">=</span>"<span style="color:blue;">0</span>"<span style="color:blue;"> </span><span style="color:red;">cellpadding</span><span style="color:blue;">=</span>"<span style="color:blue;">4</span>"<span style="color:blue;"> </span><span style="color:red;">cellspacing</span><span style="color:blue;">=</span>"<span style="color:blue;">4</span>"</pre><pre style="margin: 0px;"><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">border: 1px solid gray; background-color:#EEEEEE</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">tr</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;"> </span><span style="color:red;">valign</span><span style="color:blue;">=</span>"<span style="color:blue;">top</span>"<span style="color:blue;"> </span><span style="color:red;">rowspan</span><span style="color:blue;">=</span>"<span style="color:blue;">2</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">img</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">border:1px solid gray;</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:attribute</span><span style="color:blue;"> </span><span style="color:red;">name</span><span style="color:blue;">=</span>"<span style="color:blue;">src</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:value-of</span><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">additionalFields/additionalField[3]/value</span>"<span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(43, 145, 175);">xsl:attribute</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">img</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;"> </span><span style="color:red;">valign</span><span style="color:blue;">=</span>"<span style="color:blue;">top</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">font-size:2.0em;</span>"<span style="color:blue;">><</span><span style="color: rgb(43, 145, 175);">xsl:value-of</span><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">fullName</span>"<span style="color:blue;"> /></</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">font-size:1.2em;</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:value-of</span><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">jobTitle</span>"<span style="color:blue;"> /><</span><span style="color: rgb(163, 21, 21);">br</span><span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:value-of</span><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">department</span>"<span style="color:blue;"> /><</span><span style="color: rgb(163, 21, 21);">br</span><span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:value-of</span><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">office</span>"<span style="color:blue;"> /></span> Office<span style="color:blue;"><</span><span style="color: rgb(163, 21, 21);">br</span><span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span> Floor:</span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">a</span><span style="color:blue;"> </span><span style="color:red;">target</span><span style="color:blue;">=</span>"<span style="color:blue;">_blank</span>"<span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">text-decoration:underline</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:attribute</span><span style="color:blue;"> </span><span style="color:red;">name</span><span style="color:blue;">=</span>"<span style="color:blue;">href</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:value-of</span><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">additionalFields/additionalField[2]/value</span>"<span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(43, 145, 175);">xsl:attribute</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:value-of</span><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">additionalFields/additionalField[1]/value</span>"<span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">a</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:if</span><span style="color:blue;"> </span><span style="color:red;">test</span><span style="color:blue;">=</span>"<span style="color:blue;">string-length(assistantName) </span><span style="color:red;">&gt;</span><span style="color:blue;"> 0</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">font-size:1.2em;</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">br</span><span style="color:blue;"> /></span>Assistant <span style="color:blue;"><</span><span style="color: rgb(43, 145, 175);">xsl:value-of</span><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">assistantName</span>"<span style="color:blue;"> /></</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(43, 145, 175);">xsl:if</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">tr</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">tr</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">vertical-align:bottom;text-align:right;font-size:1em;</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">a</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">text-decoration:none</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(43, 145, 175);">xsl:attribute</span><span style="color:blue;"> </span><span style="color:red;">name</span><span style="color:blue;">=</span>"<span style="color:blue;">href</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;">mailto:<span style="color:blue;"><</span><span style="color: rgb(43, 145, 175);">xsl:value-of</span></pre><pre style="margin: 0px;"><span style="color:blue;"> </span><span style="color:red;">select</span><span style="color:blue;">=</span>"<span style="color:blue;">ape/electronicAddress[@type='E-mail' and @relationship='Business']/formattedElectronicAddress</span>"<span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(43, 145, 175);">xsl:attribute</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">img</span><span style="color:blue;"> </span><span style="color:red;">align</span><span style="color:blue;">=</span>"<span style="color:blue;">absmiddle</span>"<span style="color:blue;"> </span><span style="color:red;">border</span><span style="color:blue;">=</span>"<span style="color:blue;">0</span>"<span style="color:blue;"> </span><span style="color:red;">src</span><span style="color:blue;">=</span>"<span style="color:blue;">/_layouts/images/EML16.gif</span>"<span style="color:blue;"> /></span> Send email</pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">a</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">tr</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">table</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(43, 145, 175);">xsl:template</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"></pre><pre style="margin: 0px;"><span style="color:blue;"></</span><span style="color: rgb(43, 145, 175);">xsl:stylesheet</span><span style="color:blue;">></span></pre></div><h3>Output</h3><h4>Screenshot</h4><p><img style="border-width: 0px;" alt="Output Screenshot" src="http://lh4.ggpht.com/pictures.jones.family/SLIoqV1AOfI/AAAAAAAAAF0/yPsNPiRicKg/SampleOutputHTML5.png?imgmax=800" width="372" border="0" height="273" /> </p><h4>HTML</h4><div style="border: 1px dashed black; background: white none repeat scroll 0% 50%; padding-left: 20px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-bottom: 15px; padding-bottom: 20px;font-family:lucida console;font-size:9pt;color:black;"><pre style="margin: 0px;"><span style="color:blue;"><</span><span style="color: rgb(163, 21, 21);">table</span><span style="color:blue;"> </span><span style="color:red;">border</span><span style="color:blue;">=</span>"<span style="color:blue;">0</span>"<span style="color:blue;"> </span><span style="color:red;">cellpadding</span><span style="color:blue;">=</span>"<span style="color:blue;">4</span>"<span style="color:blue;"> </span><span style="color:red;">cellspacing</span><span style="color:blue;">=</span>"<span style="color:blue;">4</span>"<span style="color:blue;"> </span></pre><pre style="margin: 0px;"><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">border: 1px solid gray;background-color: #EEEEEE</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">tr</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;"> </span><span style="color:red;">valign</span><span style="color:blue;">=</span>"<span style="color:blue;">top</span>"<span style="color:blue;"> </span><span style="color:red;">rowspan</span><span style="color:blue;">=</span>"<span style="color:blue;">2</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">img</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">border: 1px solid gray;</span>"<span style="color:blue;"> </span></pre><pre style="margin: 0px;"><span style="color:blue;"> </span><span style="color:red;">src</span><span style="color:blue;">=</span>"<span style="color:blue;">http://portal/photos/timjones.jpg</span>"<span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;"> </span><span style="color:red;">valign</span><span style="color:blue;">=</span>"<span style="color:blue;">top</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">font-size: 2.0em;</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span> Tim Jones</span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">font-size: 1.2em;</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span> Web Developer</span><span style="color:blue;"><</span><span style="color: rgb(163, 21, 21);">br</span><span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span> Information Technology</span><span style="color:blue;"><</span><span style="color: rgb(163, 21, 21);">br</span><span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span> Houston Office</span><span style="color:blue;"><</span><span style="color: rgb(163, 21, 21);">br</span><span style="color:blue;"> /></span></pre><pre style="margin: 0px;"><span> Floor:</span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">a</span><span style="color:blue;"> </span><span style="color:red;">target</span><span style="color:blue;">=</span>"<span style="color:blue;">_blank</span>"<span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">text-decoration: underline</span>"<span style="color:blue;"> </span></pre><pre style="margin: 0px;"><span style="color:blue;"> </span><span style="color:red;">href</span><span style="color:blue;">=</span>"<span style="color:blue;">http://officemaps/HOU23.aspx?Mylbl=lbl23123</span>"<span style="color:blue;">></span>23<span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">a</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">div</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">tr</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">tr</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">vertical-align: bottom; text-align: right; font-size: 1em;</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">a</span><span style="color:blue;"> </span><span style="color:red;">style</span><span style="color:blue;">=</span>"<span style="color:blue;">text-decoration: none</span>"<span style="color:blue;"> </span><span style="color:red;">href</span><span style="color:blue;">=</span>"<span style="color:blue;">mailto:tim.jones@company.com</span>"<span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> <</span><span style="color: rgb(163, 21, 21);">img</span><span style="color:blue;"> </span><span style="color:red;">align</span><span style="color:blue;">=</span>"<span style="color:blue;">absmiddle</span>"<span style="color:blue;"> </span><span style="color:red;">border</span><span style="color:blue;">=</span>"<span style="color:blue;">0</span>"<span style="color:blue;"> </span><span style="color:red;">src</span><span style="color:blue;">=</span>"<span style="color:blue;">/_layouts/images/EML16.gif</span>"<span style="color:blue;"> /></span></pre><pre style="margin: 0px;">Send email <span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">a</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">td</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"> </</span><span style="color: rgb(163, 21, 21);">tr</span><span style="color:blue;">></span></pre><pre style="margin: 0px;"><span style="color:blue;"></</span><span style="color: rgb(163, 21, 21);">table</span><span style="color:blue;">></span></pre></div><h3></h3><h3><a name="SlideDeck">Slide Deck</a></h3><p>Below are the slides from my presentation.</p><div id="__ss_566398" style="width: 425px; text-align: left;"><a title="Integrating LexisNexis InterAction with Portals" style="margin: 12px 0px 3px; display: block; font-family: helvetica,arial,sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; text-decoration: underline;" href="http://www.slideshare.net/simplyaprogrammer/integrating-lexisnexis-interaction-with-portals-presentation?src=embed">Integrating LexisNexis InterAction with Portals</a><embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=ia-and-portals-tim-jones-1219505932160671-8&stripped_title=integrating-lexisnexis-interaction-with-portals-presentation" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed> <div style="font-size: 11px; padding-top: 2px; font-family: tahoma,arial; height: 26px;">View SlideShare <a title="View Integrating LexisNexis InterAction with Portals on SlideShare" style="text-decoration: underline;" href="http://www.slideshare.net/simplyaprogrammer/integrating-lexisnexis-interaction-with-portals-presentation?src=embed">presentation</a> or <a style="text-decoration: underline;" href="http://www.slideshare.net/upload?src=embed">Upload</a> your own. (tags: <a style="text-decoration: underline;" href="http://slideshare.net/tag/2008">2008</a> <a style="text-decoration: underline;" href="http://slideshare.net/tag/ilta">ilta</a>)</div></div><p></p><div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:f83285ea-f13c-4a2f-8188-42ee98173a08" style="margin: 0px; padding: 0px; display: inline; float: none;">Technorati Tags: <a href="http://technorati.com/tags/LexisNexis%20InterAction" rel="tag">LexisNexis InterAction</a>,<a href="http://technorati.com/tags/Portal" rel="tag">Portal</a>,<a href="http://technorati.com/tags/SharePoint" rel="tag">SharePoint</a>,<a href="http://technorati.com/tags/Systems%20Integration" rel="tag">Systems Integration</a>,<a href="http://technorati.com/tags/Web%20Development" rel="tag">Web Development</a>,<a href="http://technorati.com/tags/XML" rel="tag">XML</a>,<a href="http://technorati.com/tags/XSLT" rel="tag">XSLT</a>,<a href="http://technorati.com/tags/ILTA" rel="tag">ILTA</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com1tag:blogger.com,1999:blog-5755313800906142262.post-83680841470264218162008-08-12T21:22:00.011-05:002013-05-23T21:47:47.060-05:00The presentation of technical contentMy right brain is sore, but I'm told that means it's working. <br />
If you are called upon to present technical content, and aren't sure of the best way to go about doing it, this lengthy blog post will hopefully save you hours of research and inspiration-searching. If you'd prefer to skip all the background information, feel free to jump to the <a href="http://simplyaprogrammer.com/2008/08/presentation-of-technical-content.html#top3" title="Top 3 things I learned about presentations">top 3 things I learned</a>.<br />
<h3>My history with technical presentations</h3><p><a href="http://www.amazon.com/gp/product/0735627355/ref=as_li_ss_il?ie=UTF8&tag=simplyaprogra-20&linkCode=as2&camp=217145&creative=399373&creativeASIN=0735627355" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://ws.assoc-amazon.com/widgets/q?_encoding=UTF8&Format=_SL110_&ASIN=0735627355&MarketPlace=US&ID=AsinImage&WS=1&tag=simplyaprogra-20&ServiceVersion=20070822" /></a><br />
I've sat through many software development-related presentations over the years, and have even given a handful to small audiences. However, I've never given a presentation at anything like a conference before. That's about to change in a couple weeks however, when I'll be speaking about portals, systems integration and web development at the 2008 <a href="http://www.iltanet.org/" title="International Legal Technology Association">ILTA</a> <a href="http://conference.iltanet.org/" title="ILTA educational conference">conference</a>. In preparation, I decided to throw out what little knowledge I had about how to create a good presentation and started researching the topic.</p><p>I was at the 2005 Microsoft <a href="http://www.pnpsummit.com/" title="Patterns and Practices Summit">Patterns and Practices Summit</a> and saw <a href="http://devhawk.net/" title="Harry Pierson's blog">Harry Pierson's</a> architecture presentation. His presentation seemed a lot simpler than all the others since it lacked a page full of bullet points. During the presentation he mentioned that he was trying out a new presentation approach he had recently read about in <a href="http://www.beyondbulletpoints.com/" title="Cliff Atkinson's web site">Cliff Atkinson's</a> book <a href="http://www.amazon.com/Beyond-Bullet-Points-PowerPoint%C2%AE-Presentations/dp/0735623872/simplyaprogra-20" title="Beyond Bullet Points on Amazon.com">Beyond Bullet Points</a> (BBP). I didn't run out and buy the book back then, but I did get an idea about the concept from the free material available through Cliff's <a href="http://msevents.microsoft.com/cui/SearchDisplay.aspx?culture=en-US#culture=en-US;eventType=3;sortKey=;sortOrder=;pageEvent=false;hdnInitialCount=;searchcontrol=yes;kwdAny=%22cliff%20atkinson%22" title="Microsoft on-demand webcasts presented by Cliff Atkinson">Webcasts</a> and <a href="http://www.beyondbulletpoints.com/public/department15.cfm" title="Free resources on Beyond Bullet Points web site">sample chapters</a>.</p><h3>Researching for the presentation</h3><h4>Collecting information</h4><p><a href="http://www.amazon.com/gp/product/0321525655/ref=as_li_ss_il?ie=UTF8&tag=simplyaprogra-20&linkCode=as2&camp=217145&creative=399369&creativeASIN=0321525655" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://ws.assoc-amazon.com/widgets/q?_encoding=UTF8&Format=_SL110_&ASIN=0321525655&MarketPlace=US&ID=AsinImage&WS=1&tag=simplyaprogra-20&ServiceVersion=20070822" /></a>I started my research for presentations by looking up BBP to see what Cliff's been up to the last couple years. Since he recently published an updated edition of BBP for PowerPoint 2007, I decided it was a good time to buy. I created a new web feed folder for presentations in <a href="http://www.snarfware.com/" title="Snarfer, a free web feed reader">Snarfer</a> and added his <a href="http://www.beyondbulletpoints.com/blog/" title="Cliff Atkinson's blog">blog</a>.</p><p>Next, I let Google guide me to the most popular presentation blogs, which immediately lead me to <a href="http://www.garrreynolds.com/" title="Garr Reynolds's web site">Garr Reynolds's</a> blog <a href="http://www.presentationzen.com/" title="Garr Reynold's Presentation Zen blog">Presentation Zen</a>. I discovered that Garr recently wrote a <a href="http://www.amazon.com/Presentation-Zen-Simple-Design-Delivery/dp/0321525655/simplyaprogra-20" title="Garr Reynolds's book Presentation Zen">book</a> about presentations, also titled Presentation Zen (PZ), so I added it to the cart. If you'd like a quick overview of his points, watch the <a href="http://www.youtube.com/watch?v=DZ2vtQCESpk" title="Garr Reynold's presentation to Google about presentations">video of the metapresentation</a> he made to Google employees. His <a href="http://www.presentationzen.com/presentationzen/2006/03/no_excuse_for_t.html" title="No excuse for tedium: Advice on giving technical presentations">post</a> on technical presentations was very relevant to my needs, as well as a link he provided to an awesome <a href="http://tos.org/resources/publications/sci_speaking.html" title="Scientifically Speaking - Tips for Preparing and Delivering Scientific Talks and Using Visual Aids">booklet</a> (partially funded by our federal tax dollars) on the subject by the Oceanography Society.</p><p>From these first two sources, I found references to <a href="http://www.duartedesign.com/" title="Nancy Duarte's web site">Nancy Duarte's</a> presentation work and <a href="http://slideology.com/" title="Slideology blog">her blog</a>. Her team was the one that helped out Al Gore with An Inconvenient Truth. Nancy did a <a href="http://www.vizthink.com/blog/2008/06/18/webinar-creating-powerful-presentations-with-nancy-duarte/" title="VizThink webinar: Creating Powerful Presentations with Nancy Duarte">webinar</a> on how to create powerful presentations for <a href="http://www.vizthink.com/" title="VizThink web site">VizThink</a>, a new visual thinking community that love thinking visually and creating <a href="http://en.wikipedia.org/wiki/Mind_map" title="Mind map information from Wikipedia">mind maps</a>. Nancy also recently finished working on her book, <a href="http://www.amazon.com/gp/product/0596522347/simplayaprogra-20" title="slide:ology: The Art and Science of Creating Great Presentations by Nancy Duarte">slide:ology</a>, but it wasn't published until this month, so I couldn't get it in time.</p><p><a href="http://www.amazon.com/gp/product/1591843065/ref=as_li_ss_il?ie=UTF8&tag=simplyaprogra-20&linkCode=as2&camp=217145&creative=399369&creativeASIN=1591843065" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://ws.assoc-amazon.com/widgets/q?_encoding=UTF8&Format=_SL110_&ASIN=1591843065&MarketPlace=US&ID=AsinImage&WS=1&tag=simplyaprogra-20&ServiceVersion=20070822" /></a><br />
Around the time I was doing my primary research in July, Dan Roam's book, <a href="http://www.amazon.com/Back-Napkin-Solving-Problems-Pictures/dp/1591841992/simplyaprogra-20" title="The Back of the Napkin: Solving Problems and Selling Ideas with Pictures - On Amazon">The Back of the Napkin</a> (BOTN), was still fresh off of the press. It created quite a buzz in the presentation circles, so I thought it was worth looking into. Almost everyone has something good to say about it, and the cover just looks cool. It wasn't a book on presentations specifically, but it was right up my alley in terms of relevance. Considering my topics, so I was intrigued about the possibility of being able to communicate my topics using simple pictures.</p><p>Garr's book begins with a presentation-style forward (a one-page series of slides) by <a href="http://blog.guykawasaki.com/" title="Guy Kawasaki's blog">Guy Kawasaki</a>. Guy is a former Apple fellow and considered to have this whole speaking-presenting thing down by his peers. Guy's <a href="http://alltop.com/" title="Alltop, a collection of portals containing the most popular content on a given subject">Alltop.com</a> site was featured in his blog (of course you'd expect some self-promotion) as a service that makes it easy to find top blogs or sites on a subject. Browsing Alltop lead me to their speaking aggregate page, <a href="http://speaking.alltop.com/" title="Alltop's speaking aggregate page">speaking.alltop.com</a>. This has Nancy's, Garr's and Cliff's blog all on the page, which is a good sign so far. In addition to these, it has a bunch of others as well, which should point you to just about any other niche related to presenting/speaking that you'd need.</p><h4>Processing information</h4>Instead of providing yet another detailed book review, I'll just provide a simple chronology with oversimplified summaries. Overall, I don't regret purchasing any of these books. This may or may not have something to do with me being a book junkie. I would actually like to buy an additional 3 already. However, I think you'll agree with me on these.<br />
<h5>The Back of the Napkin</h5>I started by reading BOTN, which seemed to be more approachable because of the front cover, and also more of a prerequisite to books specifically about presentations. The main idea of BOTN is that pictures are the best for communicating and therefore the best for solving problems. Dan's argument is that everyone is still just as creative as they were during childhood and can still draw whether or not they admit to it. One of the coolest things I took away from this book was how the Visual Thinking Codex on page 141 brought it all together, and how you can "tune" your images based on your audience. BOTN inspired me to create a graph with a custom coordinate system to summarize a topic. This diagram made its way into my presentation which I'll be posting here in a couple weeks when the conference starts.<br />
<h5>Presentation Zen</h5>I read PZ next. The main idea of PZ is that slides should be as simple as possible and include stunning visuals. In agreement with the sources I'd read previously, Garr urges you to plan presentations away from the computer "in analog". Garr uses the term <a href="http://www.presentationzen.com/presentationzen/2006/04/slideuments_and.html" title="Garr's blog post on the slideument">slideument</a> to mock presentations that are also intended to present the amount of information in a document. He encourages us to create handouts or documents to free our presentations of unnecessary details (advice that I followed for the conference). PZ is full of examples of slides that promote simplicity and clarity over noise and information overload, which makes it the best resource for slide layouts and design ideas.<br />
<h5>Beyond Bullet Points</h5>I had read a couple of sample chapters of BBP before I received all my books, so I saved this book for last. Cliff makes a fantastic case for abandoning the way that PowerPoint and other slideware encourages you to use templates and bullet points to present information. He spends a good amount of time discussing the scientific research on how the brain receives, processes and stores information in the context of multimedia presentations. Specifically, the brain prefers images over text, and reading while listening is more challenging than most of the alternatives. Including the section on research was great, as it makes the issue at hand more objective than just some experts' opinions (However, I have yet to find anyone who holds a counter position). Cliff also advises to plan in analog away from the computer, and even goes further by providing template worksheets to fill out for help designing the flow of the presentation.<br />
<h5>Book reading summary</h5>Every resource I've come across stresses simplicity in terms of slide design. However, Cliff's approach is by far the most rigid I've seen. His advice, based on his research of course, is to never stray away from the same layout on a slide, to always use the same font and size in the same place, to always use complete sentences in active voice, and more of these types of recommendations. I loved BBP because it finally helped me pull the entire structure of my presentation together. It is very good at forcing you to whittle down your message to the core. However, once I arrived at my core, I sided with recommendations from others regarding slide design (such as PZ's recommendation of asymmetry). However, I did implement some of Cliff's suggestions, such as the first 5 slides overview and using motif (I chose a pyramid) for main topic slides. Overall, BBP is the best resource for the PDLC (presentation development life cycle).<br />
<h4>Searching for inspiration</h4><h5>Looking for metaphors</h5>You would think that everything I've mentioned so far would inspire me with more than enough ideas, but since this was my first attempt at using visuals to present information, I struggled with finding just that right picture to show. What image best describes portals or systems integration? Even though I settled on some images, I still have no idea. For me this was the most difficult thing about the entire process (remember my complaint about my right brain being sore?). I was prepared to pay for stock photos, but I still had a hard time finding ones I liked. Most likely the problem was me, not the stock photo sites (such as <a href="http://www.istockphoto.com/index.php">iStockPhoto</a>). I actually ended up finding quite a bit of free photos from <a href="http://www.sxc.hu/" title="Stock.XCHNG, a free stock photo web site">Stock.XCHNG</a>, but not without a considerable time investment. You can waste hours looking for photos, which is why it's so important that you plan your slides and pick your image ideas before you start browsing photos (I didn't quite do it right, so consider yourself warned).<br />
<h5>Watching presentations</h5>A common recommendation I read was to watch videos of great presentations for examples. Garr's blog and book recommended watching presentations from the <a href="http://www.ted.com/index.php/" title="Technology, Education, Design (TED) conference">TED conference</a>, which were awesome. I was also referred to Steve Jobs's keynotes as well. This really helps by providing examples of delivery and how to engage the audience. Also, for additional inspiration for slide design and entire presentations, check out the most popular presentations posted to <a href="http://www.slideshare.net/">SlideShare</a>. However, keep in mind that SlideShare presentations tend to be designed to provide information without a speaker, so they're more story-like and usually include more words.<br />
<br />
<h3><a name="top3"></a> Top 3 things I learned about presentations</h3><h4>Presentations are a specific type of communication medium</h4>Presentations are intended to be presented, not printed or read. Presentations should not be able to communicate independently from the presenter explaining and elaborating the contents of the slides. If the target medium is something that needs to be read, a document, not a presentation, is the most appropriate delivery medium. Creating a presentation is a lot more like storyboarding a movie script than it is like writing a document. Avoid the slideument!<br />
<h4>Presentations are old, but slideware is new</h4>Presentations have been made for years before PowerPoint came along. Have you ever stopped and considered how you might present without presentation software? Stay away from PowerPoint as long as you can. Do all of your storyboarding on paper, post-its or white boards. It takes far too long to sketch ideas within PowerPoint unless you have a Tablet PC and insist on using a computer.<br />
<h4>Bullet points are bad</h4>Have you ever questioned if bullet points displayed on a screen are the best way of transferring knowledge to the audience? If top educational researchers and experts such as <a href="http://www.sociablemedia.com/articles_mayer.htm" title="The Cognitive Load of PowerPoint: Q&A with Richard E. Mayer by Cliff Atkinson">Dr. Richard Meyer</a>, <a href="http://www.unsw.edu.au/news/pad/articles/2007/mar/Cognitive_load_theory.html" title="John Sweller featured in an article discussing cognitive load theory">John Sweller</a>, the father of the cognitive load theory, and <a href="http://www.edwardtufte.com/tufte/books_pp" title="Edward Tufte's essay The Cognitive Style of PowerPoint">Edward Tufte</a> all advise against this approach would you be willing to hear their arguments? Our brains cannot digest and retain the amount of information on a slide with bullet points. They prefer pictures over text, actually.<br />
<br />
<br />
<div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:cf9a72d6-6ee4-4b61-ad7d-356338a5832e" style="display: inline; margin: 0px; padding: 0px;">Technorati Tags: <a href="http://technorati.com/tags/PowerPoint" rel="tag">PowerPoint</a>,<a href="http://technorati.com/tags/Presentation" rel="tag">Presentation</a>,<a href="http://technorati.com/tags/bullet%20points" rel="tag">bullet points</a>,<a href="http://technorati.com/tags/presenting" rel="tag">presenting</a>,<a href="http://technorati.com/tags/speaking" rel="tag">speaking</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com0tag:blogger.com,1999:blog-5755313800906142262.post-92163696499611487632008-08-07T23:02:00.005-05:002017-03-16T22:48:00.344-05:00ASP.NET Enterprise Single Sign-On with BlackBerry Smartphones<strong>Update 7/27/13:</strong> BlackBerry has removed the developer journal from their web site (it's quite old by now), but for the sake of removing broken links and keeping this here for sentimental reasons, the download links are below.<br />
<img align="right" alt="Example web site hierarchy used for SSO in the article" src="http://lh3.ggpht.com/pictures.jones.family/SJvFVA5_qZI/AAAAAAAAAFs/2bmY95w-Fao/MSL%20-%20Web%20Site%20Hierarchy%20-%20For%20Blog%5B9%5D.png?imgmax=800" height="225" style="margin: 0px;" width="240" /> My article about single sign-on (SSO) using ASP.NET and BlackBerry devices was just published in the August 2008 BlackBerry Developer Journal.<br />
<br />
<a href="https://www.dropbox.com/s/v83vlg3u8ybw38c/BlackBerryDeveloperJournal-0501.pdf?dl=1" title="PDF of the BlackBerry Developer Journal, Volume 5, Issue 1">Download the article</a><br />
<a href="https://www.dropbox.com/s/kc32mqfqf94kx0k/MobileSiteLibrary.zip?dl=1" title="Sample Visual Studio solution for BlackBerry SSO">Download the sample code</a><br />
<br />
I was able to work a lot of concepts into the article, the following being a small sample of the variety.<br />
<br />
<ul>
<li>Cookie authentication</li>
<li>Kerberos <a href="http://msdn.microsoft.com/en-us/magazine/cc188757.aspx" title="MSDN article discussing the Kerberos extension Service-for-User-to-Self">S4U2Self</a> implementation</li>
<li>Custom .NET configuration section</li>
<li>Application of the <a href="http://msdn.microsoft.com/en-us/library/ms978764.aspx" title="Page Controler software architecture pattern">Page Controller</a> and <a href="http://msdn.microsoft.com/en-us/library/ms978727.aspx" title="Intercepting Filter software architecture pattern">Intercepting Filter</a> (and why it's so cool for IIS 7) architecture patterns</li>
</ul>
<br />
One of the cool takeaways included in the sample code is a method in C# that simulates ASP.NET's Active Directory authorization check.<br />
Writing is so much more time consuming than I originally imagined. It was definitely a learning process. I now have much more of an appreciation for those that set out to write an entire book.<br />
<br />
<div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:27b0ce8f-605f-4271-a5c5-745185e12a44" style="display: inline; float: none; margin: 0px; padding: 0px;">
Technorati Tags: <a href="http://technorati.com/tags/BlackBerry" rel="tag">BlackBerry</a>,<a href="http://technorati.com/tags/SSO" rel="tag">SSO</a>,<a href="http://technorati.com/tags/ASP.NET" rel="tag">ASP.NET</a>,<a href="http://technorati.com/tags/authentication" rel="tag">authentication</a>,<a href="http://technorati.com/tags/cookie" rel="tag">cookie</a>,<a href="http://technorati.com/tags/enterprise" rel="tag">enterprise</a>,<a href="http://technorati.com/tags/architecture" rel="tag">architecture</a>,<a href="http://technorati.com/tags/pattern" rel="tag">pattern</a>,<a href="http://technorati.com/tags/page%20controller" rel="tag">page controller</a>,<a href="http://technorati.com/tags/intercepting%20filter" rel="tag">intercepting filter</a>,<a href="http://technorati.com/tags/S4U2Self" rel="tag">S4U2Self</a></div>
Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com5tag:blogger.com,1999:blog-5755313800906142262.post-70495566301425834352008-07-22T12:30:00.002-05:002013-05-23T21:53:00.746-05:00Extracting assemblies from the Global Assembly Cache in Windows Explorer<p>Here's a quick tip I accidentally discovered about extracting assemblies from .NET's <a href="http://msdn.microsoft.com/en-us/library/yf1d93sz.aspx">Global Assembly Cache</a> (GAC).  In the past, when I wanted to extract .NET assemblies from the GAC, I was accustomed to opening a command window and executing copy commands.  It's tedious, but it works. This is necessary because of <a title="shfusion documentation" href="http://msdn.microsoft.com/en-us/library/34149zk3(v=vs.80).aspx">shfusion</a> (shfusion.dll), a Windows Explorer extension that controls the interaction of the the <nobr>%windir%\assembly</nobr> folder and prevents extraction of its assemblies. You can add assemblies into the GAC from Windows Explorer, but you can't extract them. </p><p>I've had to call Microsoft support in the past for one issue or another, and on one call I watched them unregister shfusion.dll (regsvr32 /u shfushion.dll) in order to extract multiple assemblies out of the GAC. However, I remember trying this on another machine one time and it didn't quite work.</p><p>I recently experienced the joy of needing to extract a handful of assemblies out of the GAC to manually reinstall an application. After using the command console a couple times, I thought I'd try to open one of the paths from the run box and to my amazement it worked.  It opened the GAC in Windows Explorer and bypassed shfusion. I've confirmed this works on XP, Vista and Server 2003. </p><h5>Finally, the tip</h5><p>Open the run command box (or quick search box in Vista)  and enter the following.</p><p><code>%windir%\assembly\gac</code> </p><p> </p><div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:b690a1c2-4bac-45df-aff7-22013c0fca06" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">Technorati Tags: <a href="http://technorati.com/tags/.NET" rel="tag">.NET</a>,<a href="http://technorati.com/tags/Global%20assembly%20cache" rel="tag">Global assembly cache</a>,<a href="http://technorati.com/tags/GAC" rel="tag">GAC</a>,<a href="http://technorati.com/tags/assembly" rel="tag">assembly</a>,<a href="http://technorati.com/tags/shfusion" rel="tag">shfusion</a>,<a href="http://technorati.com/tags/windows%20explorer" rel="tag">windows explorer</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com3tag:blogger.com,1999:blog-5755313800906142262.post-52307886560773590242008-06-26T23:48:00.002-05:002013-09-30T22:02:39.282-05:00Parsing dates for web feeds in .NET<p>During the initial development of <a title="FeedFly - Open source Windows Mobile feed reader" href="http://feedfly.codeplex.com">FeedFly</a> last year, I was exposed to the evils of date parsing for web feeds.  While working on <a title="FeedFly 0.2.1" href="http://www.codeplex.com/feedfly/Release/ProjectReleases.aspx?ReleaseId=14359">release 0.2.1</a>, I revisited this issue when fixing a date parsing bug. Before I forget all of this, it helps to write it down.</p><h3>Feed formats and dates</h3><p>If you decide to start syndicating a web feed these days, you're basically limited to the two most popular feed formats: RSS or Atom.  <a title="RSS on Wikipedia" href="http://en.wikipedia.org/wiki/RSS_(file_format)">RSS</a> seems to have the upper hand right now in terms of popularity, but some believe <a title="Atom on Wikipedia" href="http://en.wikipedia.org/wiki/Atom_(standard)">Atom</a> will eventually take over because it's being pushed as an official standard. For the purposes of a web feed, both of these formats need to be able to store and transmit dates and times.  Each format has it's own date and time format, however.  </p><h4>RSS</h4><p>RSS feeds are supposed to use the date format outlined in section 5 of <a title="RFC822: Standard for ARPA Internet Text Messages" href="http://www.w3.org/Protocols/rfc822/#z28">RFC 822</a> (published in 1982). Here are some example RFC 822 dates:</p><pre><code>Wed, 28 May 08 07:00:00 EDT
Thu, 10 Apr 08 02:30:00 UT
Tue, 10 Jun 08 05:15:00 -0600
Thu, 17 Apr 08 22:45:00 GMT</code></pre><p>In 1989, <a title="RFC 1123 - Requirements for Internet Hosts -- Application and Support" href="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</a> was created to update the RFC 822 format to use 4-digit years (section 5.2.14). Also included was a recommendation that all dates should be limited to GMT (universal time) or include a numerical offset. So, for RFC 1123's purposes, only the last date in the above list <em>should</em> be used. But it's supposed to be backwards compatible. Don't take my word for it, though:</p><blockquote><p>There is a strong trend towards the use of numeric timezone indicators, and implementations SHOULD use numeric timezones instead of timezone names. However, all implementations MUST accept either notation. If timezone names are used, they MUST be exactly as defined in RFC-822.</p></blockquote><p>I interpret this to mean that while RFC 1123 strongly encourages you to use numerical offsets or GMT, you could ignore the recommendation and still be in compliance. Because of this, the following dates should be considered to be in both RFC 822 and RFC 1123 format. However, only the bottom 2 are recommended by RFC 1123.</p><pre><code>Wed, 28 May 2008 07:00:00 EDT
Thu, 10 Apr 2008 02:30:00 UT
Tue, 10 Jun 2008 05:15:00 -0600
Thu, 17 Apr 2008 22:45:00 GMT</code></pre><p>Researching the <a title="History of RSS web feed format" href="http://cyber.law.harvard.edu/rss/rssVersionHistory.html">history of RSS</a> lead to some dark places on the web. This format was evidently born out of a lot of heated "collaboration". All versions of RSS history (controversial or not) I could dig up stated it began in 1997. The reason I looked up the history was to figure out why it's flavor of RFC 822 did not follow the recommendations in RFC 1123, or make any references to it.  Well, I didn't find an answer for that, and it's even more perplexing given that the HTTP specification was <a title="HTTP 1.0 specification - RFC 1945" href="http://www.ietf.org/rfc/rfc1945.txt">recommending</a> RFC 1123 exclusively as early as 1996. Here's a revealing excerpt:</p><blockquote><p>Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123</p><p>... [this date format] is preferred as an Internet standard and represents a fixed-length subset of that defined by RFC 1123 [6] (an update to RFC 822 [7]).</p></blockquote><p>Why am I spending so much precious time on this?  .NET only parses RFC 822 dates according to RFC 1123's recommendations, that's why. They (the .NET team) are in safe territory here (I guess) since they can simply point to HTTP's exclusive use of RFC 1123, but more on this later.</p><h4>Atom</h4><p>The Atom format <a title="Date constructs in Atom format" href="http://atompub.org/rfc4287.html#date.constructs">specifies</a> that its dates must conform to <a title="RFC 3389: Date and Time on the Internet: Timestamps" href="http://www.ietf.org/rfc/rfc3339.txt">RFC 3389</a>, which defines a simple profile of the <a title="Summary of ISO 8601 on Wikipedia" href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> date format. Just looking at the number of the RFC indicates it's more recent. Proposed in 2002, RFC 3389 is 20 years younger than RFC 822, so you'd expect it to include some "lessons learned" from previous date formats.</p><p>Here is an example date in  in RFC 3389: <code>1985-04-12T23:20:50.52Z</code></p><p>The benefit of this date format is that it's more efficient to parse from a computer's perspective.  It does this at the cost of being less user friendly, since we now don't know what day of the week it is or what time zone offset it was created in. Of course, modern software frameworks  like .NET or Java can easily produce most of this user friendly information for you after calling upon their rich date and time libraries.</p><h3>Date parsing in .NET</h3><p>For parsing date time strings .NET offers us <a href="http://msdn.microsoft.com/en-us/library/system.datetime.parse.aspx">DateTime.Parse</a> and <a href="http://msdn.microsoft.com/en-us/library/system.datetime.parseexact.aspx">DateTime.ParseExact</a>. Both can parse a list of  <a title=".NET Standard Date and Time Format Strings" href="http://msdn.microsoft.com/en-us/library/az4se3k1.aspx">standard date and time formats</a> included in the framework. ParseExact can also parse custom date and time formats.</p><h4>Parsing RSS dates</h4><p>For RSS-type dates, .NET includes the standard date format string "r" for RFC 1123. When used in combination with <a href="http://msdn.microsoft.com/en-us/library/zdtaw1bw.aspx">DateTime.ToString</a>, "r" will always produce a universal date with a "GMT" suffix.  However, for parsing purposes, .NET will parse a numerical offset without complaint. However, as hinted to above, .NET will throw an exception if it encounters a time zone name as specified in RFC 822.  </p><p>What do you think about this?  Should the <a title=".NET base class library team" href="http://msdn.microsoft.com/en-us/netframework/aa569604.aspx">BCL team</a> be forced to abide by RFC 1123's command that it "MUST accept either notation"? I think so. RFC 822 is certainly not going to change which time zones it contains, so hard-coding this into DateTime somewhere couldn't hurt.  It was certainly easy enough for me to build a <a href="http://msdn.microsoft.com/en-us/library/system.collections.specialized.listdictionary.aspx">ListDictionary</a> solution as a workaround. </p><p>Furthermore, given the fact that Java is about to release an updated API for date parsing (<a title="JSR 310 Date and Time API" href="https://jsr-310.dev.java.net/">JSR 310</a>) that will most likely parse these time zone names, .NET's going to have to catch up (hint). Now, I don't know this for a fact regarding JSR 310, but from what I could find from searching, they're considering a lot of ideas from the popular <a title="joda-time: date and time enhancements for Java" href="http://joda-time.sourceforge.net/api-release/org/joda/time/format/DateTimeFormat.html">joda-time library</a>.</p><h4>Parsing Atom dates</h4><p>After all that mess with RFC 822, Atom's use of RFC 3389 is refreshing from a parsing perspective. .NET includes the "o" format for parsing what appears to be RFC 3389 dates.  However, MSDN says this format is for ISO 8601. Given the huge variety of date formats in ISO 8601 (the W3C <a href="http://www.w3.org/TR/NOTE-datetime">commented briefly</a> about this), I'd prefer the documentation to be a bit more specific about what it will and will not parse. But hey, it parses just fine, so I'll take it.</p><h3>I prefer Atom</h3><p>I have no idea what preference the small slice of the development community that writes .NET feed reader software has in terms of feed formats.  But I'm sure they appreciate Atom when it comes time to parse dates. I don't know if it's a freedom thing or a popularity thing, but I see much more abuse of date formats in RSS feeds than I do in Atom feeds.  For example, <a href="http://feeds.autoblog.com/weblogsinc/autoblog">AutoBlog's RSS feed</a> uses RFC 3389 dates in its pubDate element. That's a no-no, but I still parse it.</p><p> </p><div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:3a4de7cb-83bf-4b7d-aed7-4a1f6be48495" style="padding-right: 0px; display: inline; padding-left: 0px; float: none; padding-bottom: 0px; margin: 0px; padding-top: 0px">Technorati Tags: <a href="http://technorati.com/tags/date%20time" rel="tag">date time</a>,<a href="http://technorati.com/tags/parsing" rel="tag">parsing</a>,<a href="http://technorati.com/tags/.NET" rel="tag">.NET</a>,<a href="http://technorati.com/tags/Atom" rel="tag">Atom</a>,<a href="http://technorati.com/tags/RSS" rel="tag">RSS</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com0tag:blogger.com,1999:blog-5755313800906142262.post-77446766079638499342008-05-29T22:39:00.001-05:002013-09-30T22:01:38.931-05:00Icon formats for Windows Mobile<p>About 6 months ago, I created a custom icon for my Windows Mobile application, <a title="FeedFly - Open source Windows Mobile feed reader" href="http://feedfly.codeplex.com/">FeedFly</a>. I think I used one of those "We'll make your icon for free from an image! It's totally FREE!!!" web sites. I chased that down with a trial edition of an icon editor (it might have been Microangelo) to make a couple modifications.</p><p>My icon turned out great, or at least I thought it did until I deployed it to my device. The transparent background wasn't transparent. It was either white (as shown on the start menu graphic below) or this weird smoky gray color (shown on HTC's Home Today Screen plug-in next to the fixed one). I tried opening the icon in several different applications (Visual Studio, <a title="Infranview - freeware image viewer" href="http://www.irfanview.net/">Infranview</a>, and even old MS Paint). Each one told me I actually had a transparent background (in more or less words).</p><p><a href="http://lh4.ggpht.com/pictures.jones.family/SD92-nRSLeI/AAAAAAAAAD4/TXItnb0dAF0/FeedFly-SmallIconComparison%5B5%5D.jpg?imgmax=800"><img style="border: 0px none ; margin: 0px 20px 0px 10px;" alt="FeedFly-SmallIconComparison" src="http://lh5.ggpht.com/pictures.jones.family/SD92-3RSLfI/AAAAAAAAAEA/9kcU3rFZdSc/FeedFly-SmallIconComparison_thumb%5B3%5D.jpg?imgmax=800" border="0" height="320" width="240" /></a> <a href="http://lh6.ggpht.com/pictures.jones.family/SD92_HRSLgI/AAAAAAAAAEI/quCo8qyYIkU/FeedFly-LargeIconComparison%5B9%5D.jpg?imgmax=800"><img style="border: 0px none ; margin: 0px;" alt="FeedFly-LargeIconComparison" src="http://lh3.ggpht.com/pictures.jones.family/SD92_XRSLhI/AAAAAAAAAEQ/YenEhLKOjG8/FeedFly-LargeIconComparison_thumb%5B7%5D.jpg?imgmax=800" border="0" height="320" width="240" /></a> </p><p>Since I'm working on a <a title="FeedFly 0.2" href="http://www.codeplex.com/feedfly/Release/ProjectReleases.aspx?ReleaseId=13825">new release</a> for FeedFly, I thought I'd once again look for a solution to my icon transparency woes. A couple days ago, I stumbled upon <a title="IcoFX freeware icon editor" href="http://icofx.ro/">IcoFX</a> and installed it to see if it could be of any help. Well, it was. </p><p>It turns out that the handy web site I used to generate the icon provided me with a 32 bit color icon along with the <a title="Alpha transparency discussed on Wikipedia" href="http://en.wikipedia.org/wiki/Alpha_transparency">alpha transparency</a> included within the original image. Using IcoFX, I converted my icon to 256 color (8 bit) and removed the alpha transparency. After deploying to my device, it is shown correctly now. </p><p>On a related note, let me start by saying I realize that the target audience for a tool such as an icon editor is a small slice of the general user population. However, for an operating system that uses icons so extensively, I still can't believe an icon editor is not included in Windows. I'm on a budget, and I can't justify purchasing something like Photoshop just to edit an icon. I'm nowhere near an artist, so I definitely won't make icon editing a habit (I get pretty frustrated as it is), but I do end up fiddling with an icon once every other year it seems (mostly <a title="favicon topic on Wikipedia" href="http://en.wikipedia.org/wiki/Favicon">favicons</a> lately).</p><p>Finally, this post isn't meant to imply that all the high bit counts won't work for icons (read <a title="Windows Mobile team's blog post on color support" href="http://blogs.msdn.com/windowsmobile/archive/2005/09/07/462187.aspx">this</a> for more information), but to hopefully help you avoid some frustration trying to track this issue down. Just make sure your icon is not 32 bit with alpha.</p><p></p><div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:41e2b4fc-2128-4f59-a874-e315107801b0" style="margin: 0px; padding: 0px; display: inline;">Technorati Tags: <a href="http://technorati.com/tags/Windows%20Mobile" rel="tag">Windows Mobile</a>,<a href="http://technorati.com/tags/Icon" rel="tag">Icon</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com4tag:blogger.com,1999:blog-5755313800906142262.post-78131928514804438192008-05-26T14:48:00.006-05:002013-05-23T21:58:23.710-05:00Importing files into a SharePoint document library using regular expressions and WebDAV<p>I just finished writing a utility to export a folder hierarchy of files from my existing custom extranet to a SharePoint document library. The custom extranet was database-driven and allowed the user to name a file or folder whatever he or she wished up to a maximum of 500 characters. When I wrote this extranet 6 years ago in classic ASP, I'd just <a title="HTMLEncode method documentation on MSDN" href="http://msdn.microsoft.com/en-us/library/w3te6wfz.aspx">HTML encode</a> whatever name the user wished and store it in the database. Whenever a folder or file was retrieved, it was always by using the ever-so-not-user-friendly URL parameter "id=". </p><p>I already knew I would need to remove restricted characters from my folder and files names that SharePoint does not allow. Furthermore, SharePoint's document libraries actually display the full folder path in the URL, which means I'll need to be concerned about the total path length. </p><p>My migration plan was to build a physical folder hierarchy for staging the files, then use <a title="Web-based Distributed Authoring and Versioning" href="http://en.wikipedia.org/wiki/WebDAV">WebDAV</a> (SharePoint's explorer view for document libraries) for importing the hierarchy into SharePoint within Windows. This method will allow me to keep the utility focused on a simpler task than actually importing the files into SharePoint and make sure I don't have to worry about server timeouts. </p><h3>Naming restrictions</h3><p>SharePoint has <a title="Information about the characters that you cannot use in sites, folders, and files in SharePoint Portal Server 2003 or in SharePoint Server 2007" href="http://support.microsoft.com/default.aspx?scid=kb;en-us;905231">naming restrictions</a> for sites, groups, folders and files. Since I'm only interested in folders and files, only the following restrictions will be considered.</p><ul><li>Invalid characters:<strong><code> \ / : * ? " ' < > | # { } % ~ & </code></strong></li>
<li>You cannot use the period character consecutively in the middle the name </li>
<li>You cannot use the period character as the first or the last character </li>
</ul><p>Someone already familiar with this topic will notice that I added the apostrophe to the official restricted character list. During my own testing, SharePoint complained when I uploaded a file with an apostrophe, so I added it to the list.</p><h3>Length restrictions</h3><p>Besides naming restrictions, SharePoint also has the following length restrictions (from <a href="http://support.microsoft.com/kb/894630">KB 894630</a>).</p><ul><li>A file or a folder name cannot be longer than 128 characters. </li>
<li>The total URL length cannot be longer than 260 characters. </li>
</ul><h4>128 character limit for folders and files</h4><p>Regarding the 128 character limit, you can't use SharePoint's UI to get to this limit. The text box's maxlength property is set to 123 for both folders and files. I don't have any inside sources, but my guess is that the SharePoint team did this to make sure the total file name would not exceed 128 characters if the extension was 4 characters (as is the case with Office 2007 file formats like docx and xlsx). The odd thing is that the folder text box is limited to 123 characters as well. However, if you put the document library into Explorer view, you can rename a folder to allow the full 128 characters. I bet there's some reuse going on between the data entry screens for the file and the folder in this case (also something a programmer on the SharePoint team might want to do).</p><h4>260 character limit for URLs</h4><p>I've done some WebDAV importing to this particular SharePoint farm in the past, and I'm pretty sure I ran into paths close to the 260 character limit, so I investigated this. I found several instances where the total URL exceeded 260 characters. </p><p>KB 894630 mentioned above also says:</p><blockquote><p>To determine the length of a URL, .... convert the string in the URL to Universal Character Set (UCS) Transformation Format 16 (UTF-16) format, and then count the number of 16-bit characters in the string.</p></blockquote><p>However, it should probably say something like "decode the URL first, then count the characters" to make it easier to understand. I created a folder hierarchy to test out the 260 character limit. Following is a URL (notice the %20 space codes) to a test file copied from the address bar of the browser. When the URL is encoded, it contains 346 characters.</p><blockquote><p>http://intranet.xyzco.com/sites/Testing/Documents/A%20longer%20than%20 usual%20folder%20name%20for%20testing/Subfolder%201%20also%20has%20 a%20long%20name/3rd%20level%20subfolder%20about%20related%20 documents/4th%20level%20subfolder%20about%20more%20specific%20 documents/5th%20level%20subfolders%20are%20possible%20in%20this%20 hierarchy/1234567.txt</p></blockquote><p>The decoded URL is:</p><blockquote><p>http://intranet.xyzco.com/sites/Testing/Documents/A longer than usual folder name for testing/Subfolder 1 also has a long name/3rd level subfolder about related documents/4th level subfolder about more specific documents/5th level subfolders are possible in this hierarchy/1234567.txt </p></blockquote><p>Counting the characters in the URL gave me 284. To get closer to 260, I subtracted the 25 characters for the web application:</p><blockquote><p>284 – 25 (Length of http://intranet.abcco.com) = 259 characters</p></blockquote><p>I didn't get a perfect 260, but it's close enough for me to believe that the web application host header name is not included in the limit. This is just a guess on my part, though.</p><h4>Why the 260 character limit?</h4><p>A 260 character limit on the URL is interesting, considering both Windows and most internet browsers support paths much longer. It's not merely a coincidence that 260 also just so happens to be the value of the infamous <a title="KB 177665 discussing MAX_PATH" href="http://support.microsoft.com/kb/177665">MAX_PATH constant</a> from the Windows API. .NET uses MAX_PATH because .NET relies on the Windows API behind the scenes. There are API workarounds, as <a title="Long Paths in .NET, Part 1 of 3 by Kim Hamilton" href="http://blogs.msdn.com/bclteam/archive/2007/02/13/long-paths-in-net-part-1-of-3-kim-hamilton.aspx">discussed</a> on the BCL team blog, but I think it's safe to assume that this limit is imposed on SharePoint by .NET in some way.</p><h3>Removing invalid characters and patterns using a regular expression</h3><p>The String object's Replace method doesn't contain an overload for replacing an array of strings, so I looked into using a regular expression to clean folder and file names. </p><p>Regular expressions have their own special characters that must be escaped if used for searching:</p><blockquote><p><strong><code>[ \ ^ $ . | ? * + ( ) </code></strong></p></blockquote><p>Out of these, the following are also SharePoint's invalid characters: <strong><code>* ? | \ </code></strong>These are the characters that will need to be escaped in our regular expression.</p><p>After a bit of fiddling, I came up with the following 4 expressions:</p><ol><li><strong><code>[\*\?\|\\/:"'<>#{}%~&]</code></strong> for removing invalid characters </li>
<li><strong><code>\.{2,}</code></strong> for replacement of consecutive periods </li>
<li><strong><code>^[\. ]|[\. ]$</code></strong> for removing spaces and periods from the beginning and end of a folder or file name </li>
<li><strong><code>" {2,}"</code></strong> for replacement of consecutive spaces (enclosed by quotation marks so you can see the space) </li>
</ol><p>I added a couple of rules to these expressions because of my migration strategy. Since I'm using WebDAV and building a physical folder hierarchy in Windows, I also need to be concerned about any <a title="MSDN: Naming a file" href="http://msdn.microsoft.com/en-us/library/aa365247.aspx">additional restrictions</a> imposed by the OS (a folder or file name can't end with a space). Also, I'm replacing consecutive spaces with a single space.</p><p>All expressions are used by Regex.Replace(). Expressions 1 and 3 are replaced by String.Empty. 2 and 4 are replaced by a period and a space, respectively. In regards to the order of the replacements, it's important that the invalid character replacement is applied first. Combining these expressions and replacing at once might create a problem after invalid characters are replaced. For example, the name %.afile.txt would become .afile.txt if done all at once, violating the rule that a period cannot be the first character.</p><p>After all replacements have been made, it's still possible to have one of the rules violated. For example, a folder named "Folder one . and . " (ends with space, period, space) would still be invalid after 1 pass of expression 3. It would still be invalid after a 2nd pass. Because of this, the beginning and end rule should be used in a loop until no matches are found. This doesn't help performance, but I was willing to compromise since my largest extranet (9000 files and hundreds of folders) was processed within a minute. Plus, I know the minute I post this someone's going to read it and say, "What was he thinking? It's so much faster to do it this way...".</p><h3>Fixing length restrictions</h3><p>To make sure you include as many characters from the original folder or file name as possible, the naming restrictions should be enforced before the length restrictions.</p><p>To know how long a file name can be, it's important to know how close we are to the maximum allowed path length. Since I'm using a physical file hierarchy to stage the files, I can simply check the current folder's path length. Instead of going into too much detail about this, take a look at the maxLength integer in the following code listing. maxLength is what I used to determine how long a folder or file could be given the current path length.</p><h3>An example method in C#</h3><p>Following is the method I ended up with, along with some global variable initializations. You'll notice I added the tab character to the invalid characters list. During an export, I found a file name with embedded tab characters, so it was added to the list as well.</p><div style="border: 1px dashed black; background: white none repeat scroll 0% 50%; padding-left: 20px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-bottom: 15px;font-family:lucida console;font-size:9pt;color:black;"><pre style="margin: 0px;"><span style="color:blue;">
private</span> <span style="color:blue;">const</span> <span style="color:blue;">int</span> MAXFOLDERLENGTH = 128, MAXFILELENGTH = 123;</pre><pre style="margin: 0px;"><span style="color:blue;">private</span> <span style="color:blue;">int</span> MAXURLLENGTH = 259;
</pre><pre style="margin: 0px;"><span style="color:blue;">private</span> <span style="color:teal;">Regex</span> invalidCharsRegex =</pre><pre style="margin: 0px;"><span style="color:blue;">new</span> <span style="color:teal;">Regex</span>(<span style="color:maroon;">@"[\*\?\|\\\t/:""'<>#{}%~&]"</span>, <span style="color:teal;">RegexOptions</span>.Compiled);
</pre><pre style="margin: 0px;"><span style="color:blue;">private</span> <span style="color:teal;">Regex</span> invalidRulesRegex = </pre><pre style="margin: 0px;"><span style="color:blue;">new</span> <span style="color:teal;">Regex</span>(<span style="color:maroon;">@"\.{2,}"</span>, <span style="color:teal;">RegexOptions</span>.Compiled);
</pre><pre style="margin: 0px;"><span style="color:blue;">private</span> <span style="color:teal;">Regex</span> startEndRegex = </pre><pre style="margin: 0px;"><span style="color:blue;">new</span> <span style="color:teal;">Regex</span>(<span style="color:maroon;">@"^[\. ]|[\. ]$"</span>, <span style="color:teal;">RegexOptions</span>.Compiled);
</pre><pre style="margin: 0px;"><span style="color:blue;">private</span> <span style="color:teal;">Regex</span> extraSpacesRegex = </pre><pre style="margin: 0px;"><span style="color:blue;">new</span> <span style="color:teal;">Regex</span>(<span style="color:maroon;">" {2,}"</span>, <span style="color:teal;">RegexOptions</span>.Compiled);
</pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> </span><span style="color:gray;"><summary></span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> Returns a folder or file name that </span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> conforms to SharePoint's naming restrictions</span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> </span><span style="color:gray;"></summary></span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> </span><span style="color:gray;"><param name="original"></span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> The original file or folder name. </span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> For files, this should be the file name without the extension. </span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> </span><span style="color:gray;"></param></span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> </span><span style="color:gray;"><param name="currentPathLength"></span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> The current folder's path length</span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> </span><span style="color:gray;"></param></span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> </span><span style="color:gray;"><param name="maxItemLength"></span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> The maximum allowed number of characters for this file or folder.</span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> For a file, it will be MAXFILELENGTH.</span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> For a folder, it will be MAXFOLDERLENGTH.</span></pre><pre style="margin: 0px;"><span style="color:gray;">///</span><span style="color:green;"> </span><span style="color:gray;"></param></span></pre><pre style="margin: 0px;"><span style="color:blue;">private</span> <span style="color:blue;">string</span> GetSharePointFriendlyName(<span style="color:blue;">string</span> original</pre><pre style="margin: 0px;">, <span style="color:blue;">int</span> currentPathLength, <span style="color:blue;">int</span> maxItemLength)</pre><pre style="margin: 0px;">{</pre><pre style="margin: 0px;"><span style="color:green;">// remove invalid characters and some initial replacements</span></pre><pre style="margin: 0px;"><span style="color:blue;">string</span> friendlyName = extraSpacesRegex.Replace(</pre><pre style="margin: 0px;">invalidRulesRegex.Replace(</pre><pre style="margin: 0px;">invalidCharsRegex.Replace(</pre><pre style="margin: 0px;">original, <span style="color:teal;">String</span>.Empty).Trim()</pre><pre style="margin: 0px;">, <span style="color:maroon;">"."</span>)</pre><pre style="margin: 0px;">, <span style="color:maroon;">" "</span>);
</pre><pre style="margin: 0px;"><span style="color:green;">// assign maximum item length</span></pre><pre style="margin: 0px;"><span style="color:blue;">int</span> maxLength = (currentPathLength + maxItemLength > MAXURLLENGTH)</pre><pre style="margin: 0px;">? MAXURLLENGTH - currentPathLength</pre><pre style="margin: 0px;">: maxItemLength;
</pre><pre style="margin: 0px;"><span style="color:blue;">if</span> (maxLength <= 0)</pre><pre style="margin: 0px;"><span style="color:blue;">throw</span> <span style="color:blue;">new</span> <span style="color:teal;">ApplicationException</span>(</pre><pre style="margin: 0px;"><span style="color:maroon;">"Current path is too long for importing into SharePoint"</span>);
</pre><pre style="margin: 0px;"><span style="color:green;">// return truncated name if length exceeds maximum </span></pre><pre style="margin: 0px;"><span style="color:blue;">if</span> (friendlyName.Length > maxLength)</pre><pre style="margin: 0px;">friendlyName = friendlyName.Substring(0, maxLength - 1).Trim();
</pre><pre style="margin: 0px;"><span style="color:green;">// finally, check beginning and end for periods and spaces</span></pre><pre style="margin: 0px;"><span style="color:blue;">while</span> (startEndRegex.IsMatch(friendlyName))</pre><pre style="margin: 0px;">friendlyName = startEndRegex.Replace(</pre><pre style="margin: 0px;">friendlyName, <span style="color:teal;">String</span>.Empty);
</pre><pre style="margin: 0px;"><span style="color:blue;">return</span> friendlyName;</pre><pre style="margin: 0px;">}</pre></div><p>A typical call to this method would look similar to the following. In this listing, parent is a DirectoryInfo object pointing to the current folder.</p><div style="background: white none repeat scroll 0% 50%; padding-left: 20px; font-size: 9pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: black; font-family: lucida console;"><pre style="margin: 0px;">fileName = GetSharePointFriendlyName(fileName</pre><pre style="margin: 0px;">, parent.FullName.Length + 1, MAXFILELENGTH);</pre><pre style="margin: 0px;">folderName = GetSharePointFriendlyName(folderName</pre><pre style="margin: 0px;">, parent.FullName.Length + 1, MAXFOLDERLENGTH);</pre></div><h3>Testing the import to SharePoint using empty files</h3><p>The best test would be to actually upload the files via WebDAV to a staging environment. However, if you receive an error message because of name restrictions or path length during the process, it's difficult to pick back up where the error occurred. </p><p>To quickly preview an upload, I modified my export utility to create empty files instead of building the folder hierarchy with the actual files. You can use these for a mock import in WebDAV even though SharePoint's UI will not allow you upload an empty file. The following line was used to create the files. </p><div style="background: white none repeat scroll 0% 50%; padding-left: 20px; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-bottom: 20px;font-family:lucida console;font-size:9pt;color:black;"><pre style="margin: 0px;"><span style="color:blue;">using</span> (<span style="color:teal;">StreamWriter</span> sw = <span style="color:teal;">File</span>.CreateText(fileName.ToString())) { };</pre></div><p>The using statement makes sure the StreamWriter is closed after the file is created. I learned this the hard way when the OS threw an exception about a file being locked.</p><p>Another benefit of using empty files is to preview the migration for your users. They can browse the document library and offer their approval. Since we've had to remove some characters and possibly truncate names, this could be very important to the success of the migration. </p><h3>Export Utility</h3><p>Just to offer some eye candy for this post, I ended up with something that looked like this:</p><p><img style="border: 0px none ;" alt="Export utility screenshot" src="http://lh6.ggpht.com/pictures.jones.family/SDsT-uC4WmI/AAAAAAAAADw/-qKJwEWWAwo/image%5B5%5D.png?imgmax=800" border="0" height="331" width="541" /> </p><div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:c7faba72-ed3b-4a27-933a-4825b4f55923" style="margin: 0px; padding: 0px; display: inline;">Technorati Tags: <a href="http://technorati.com/tags/SharePoint" rel="tag">SharePoint</a>,<a href="http://technorati.com/tags/Regular%20Expressions" rel="tag">Regular Expressions</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com15tag:blogger.com,1999:blog-5755313800906142262.post-11131004454545604662008-05-21T21:22:00.003-05:002013-07-27T09:36:14.393-05:00Creating a batch of Active Directory accounts for SharePoint with the help of Excel<p>I recently deployed an extranet using SharePoint and Active Directory (AD). At my company, when a new extranet is requested, the request is typically accompanied with a list of new user accounts that should be created as well. Sometimes this list can contain over 20 accounts.</p><p>Using the Active Directory Users and Computers applet is not my favorite interface for creating accounts, and especially not for a large batch of them. I found myself having to edit an account 3 times to get it to appear the way I wanted in AD and in SharePoint. I looked into my scripting options, and ran across <a title="dsadd user command documentation from TechNet" href="http://technet2.microsoft.com/windowsserver2008/en/library/9e274947-2dec-4448-a822-8dd2f688fcec1033.mspx?mfr=true">dsadd</a>. This command has everything I need in order to create AD accounts in one step. However, it's very tedious to have to type the command given the customization I wanted and to remember the syntax rules.</p><h3>Using Excel to create dsadd commands</h3><p>To make this process easier, I created an Excel <a title="Download the dsadd command generator spreadsheet" href="https://dl.dropboxusercontent.com/u/101304566/dsadd-Command-Generator.xls">spreadsheet</a> to generate the dsadd command based on a collection of cells. I've used Excel several times in the past to generate batches of commands or SQL statements. Even if you don't need a dsadd command, this spreadsheet is a useful reference for building other batches of commands.</p><p><a title="Download the dsadd command generator spreadsheet" href="https://dl.dropboxusercontent.com/u/101304566/dsadd-Command-Generator.xls"><img style="border-width: 0px;" alt="Screenshot of dsadd spreadsheet - Click to download" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoJqYJTHjliKRbPhI3miTb3U9LEY6VvgNMz5OJ64QWmoHV2o4bKmRJM676SjL8YEDpqxe0-I0HV1noJC7Yau4spZh_52VHiywfMkJ0RJshsjEvDrZteBWdHxUaUzun2bO1jMrlU58d_Tkv/s400/dsadd-spreadsheet-screenshot.jpg" border="0" /></a></p><p>After downloading the spreadsheet, you'll need to fill in the data for your company's Active Directory and the OU you'll be creating the accounts in. You'll then probably want to extend the cell formulas beyond the single row that's included. After you've filled in everything, copy the cells in the script column and paste it into a text editor like Notepad. Save it as a cmd file, and you can double click it to execute all the scripts. </p><p>You can add or remove more columns to accommodate the other dsadd parameters. Regarding additional information fields, I was only interested in company and department.</p><p>Tip: If you'd like to save the results of the commands, add an append <a title="TechNet: Using command redirection operators" href="http://technet.microsoft.com/en-us/library/bb490982.aspx">redirection operator</a> to the call to the script file from a console screen. This would be helpful to find out what went wrong if some of your commands failed. </p><p>For example,</p><blockquote><p><code>createaccounts.cmd >> c:\dsadd.log 2>&1</code></p></blockquote><p>will send all the commands and any errors to the dsadd.log file instead of the console. It's important to remember to add <code>2>&1</code> at the end, since dsadd sends errors to stderr, not stdout.</p><h3>Adding a batch of user accounts to a SharePoint site collection</h3><p>After the accounts have been added to AD, you can reuse the spreadsheet to add multiple accounts to SharePoint. Following are the steps I've used to translate a column of user account names into a semicolon-delimited list for the add user screen in SharePoint.</p><ol><li>Copy the column of cells that have the account name (Domain\username) and Paste Special into Word to avoid it creating a table. You only want unformatted text. </li>
<li>Replace each line feed character (type ^p into the find box) with a semicolon. </li>
<li>Copy and paste into the SharePoint add user screen. </li>
</ol><p></p><div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:bbc7b895-8150-49de-8a14-90bd18f309e1" style="margin: 0px; padding: 0px; display: inline;">Technorati Tags: <a href="http://technorati.com/tags/SharePoint" rel="tag">SharePoint</a>,<a href="http://technorati.com/tags/Excel" rel="tag">Excel</a>,<a href="http://technorati.com/tags/Active%20Directory" rel="tag">Active Directory</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com3tag:blogger.com,1999:blog-5755313800906142262.post-88827724568067016072008-05-11T16:04:00.001-05:002013-05-22T22:26:42.059-05:00A Windows Mobile feed reader: FeedFly<p><a title="FeedFly - Open source Windows Mobile feed reader hosted on CodePlex" href="http://www.codeplex.com/feedfly" target="_blank"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height="56" alt="FeedFly logo - Windows Mobile feed reader" src="http://lh5.ggpht.com/tjneweyes/SCdfUg2ng6I/AAAAAAAAACM/tAwYXStVzQw/Logo_Gray%5B8%5D.jpg?imgmax=800" width="240" align="left" border="0" /></a> Yesterday I published my first open source project, <a title="FeedFly - Open source Windows Mobile feed reader hosted on CodePlex" href="http://www.codeplex.com/feedfly">FeedFly</a>. FeedFly was the focus of my senior project course last semester. This class is required to graduate with a bachelor in Computer Science at <a title="University of Houston, Clear Lake - School of Science and Computer Engineering" href="http://prtl.uhcl.edu/portal/page/portal/HOMEPAGE/TAB_PROGRAMS?OSS=/PGM/OVERVIEW/SCE" target="_blank">my university</a>.</p><p>We were given the choice to write any kind of software application, but it had to get enough votes in order to form a team. I was lucky since my idea got a couple votes. About 300 man hours later, we gave our final presentation and I nervously gave a demo. Besides ActiveSync messing with my device, the demo was fine. I had to leave it on, since I was relying on it for my data connection. The room we were presenting in was like a steel box with no windows.</p><p>Including myself, we were a team of 3 programmers. I attempted to manage the timeline of the project using a modified version of <a title="Scrum software development methodology - As presented by Mountain Goat Software" href="http://www.mountaingoatsoftware.com/scrum" target="_blank">Scrum</a>. It was as effective as it could be given we couldn't do daily stand-ups. Plus, it made the Gannt chart something very easy to look at: </p><p><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height="199" alt="Project Plan Schedule" src="http://lh3.ggpht.com/tjneweyes/SCdfVA2ng7I/AAAAAAAAACU/U7RwXkFLvhc/Project%20Plan%20Schedule%20-%20Small%5B5%5D.jpg?imgmax=800" width="640" border="0" /> </p><p>This chart shows our 1 week architecture sprint at the beginning, 3 2-week development sprints, and a 1-week documentation and presentation sprint. However, generating the schedule wasn't easy. I had to enter our weird college work hours and account for our school holidays for Microsoft Project to get the end date just right.</p><p>I'm very happy with how FeedFly turned out. If I wasn't, it wouldn't have been published as an open source project. I learned a lot about the .NET Compact Framework during development, and I think the project serves as a good example application that implements some best practices. If you're a compact framework developer, I'd love to get your feedback about it.</p><p>Oh, and I got an A on the project by the way.</p><p></p><div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:1cd34ec8-7fd7-43b9-b888-af876ccc2ba0" style="PADDING-RIGHT: 0px; DISPLAY: inline; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-TOP: 0px">Technorati Tags: <a href="http://technorati.com/tags/.NET%20Compact%20Framework" rel="tag">.NET Compact Framework</a>,<a href="http://technorati.com/tags/Windows%20Mobile" rel="tag">Windows Mobile</a>,<a href="http://technorati.com/tags/Feed%20reader" rel="tag">Feed reader</a>,<a href="http://technorati.com/tags/Feed" rel="tag">Feed</a>,<a href="http://technorati.com/tags/RSS" rel="tag">RSS</a>,<a href="http://technorati.com/tags/Atom" rel="tag">Atom</a></div>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com0tag:blogger.com,1999:blog-5755313800906142262.post-89503013952618660462008-05-02T09:48:00.000-05:002013-05-22T22:26:51.487-05:00RSS can't catch up to email yet<p>When I first learned about <a href="http://www.w3schools.com/xml/default.asp">XML</a>, I thought it sounded like a great idea. I had a hard time coming up with an excuse to use it in my earlier programming days (would have involved a rewrite, or couldn't cost-justify the implementation of that cool, new self-describing configuration system), but .NET changed all of that (for me, being a Microsoft-experienced dev) and now XML is extremely easy to work with. Even Microsoft is using it in Office 2007 for all their new file formats. For example, did you know you could rename a docx file to zip then unpack and inspect it? What you'll see is a folder hierarchy of XML files, which could be edited in Notepad if you're so inclined. If you eat XML for breakfast, you don't even need Office to create Office documents. I think we would all agree that XML has definitely arrived and is here to stay. </p><p>I mention RSS in the title because this particular XML <a href="http://en.wikipedia.org/wiki/Web_feed">web feed</a> standard has become so popular it has become synonymous with "web feed" itself. The following video pretty much sums up the goals of using web feeds instead of the typical models of gathering your information from the internet.</p><p><embed src="http://www.youtube.com/v/0klgLsSxGsU&hl=" width="425" height="355" type="application/x-shockwave-flash" wmode="transparent" color2="0x999999" rel="0&color1="></embed></p><p>In short, web feeds are a perfect application of the theory of XML. It makes personal blogs just as "subscribe-able" as our magazines and radio shows available at any time via podcasts. I should probably spend an entire post praising podcasts since I'm always listening to them now. Ever since my phone became my mp3 player, my car stereo hasn't been turned on (I have one of those after-market ones that plays mp3 disks, too. It always reads "Standby"). I fear this introduction was a bit lengthy, but I'm trying make everyone in my audience happy (and may it never change). </p><p>Why the negative title about RSS? My point is that even though web feeds are more popular now than ever before, the barrier of entry is still too high to participate in all of its glory. I'm sure you know plenty of non-technical computer users out there. Are they subscribing to feeds? Probably not. Last semester, I graduated with my Computer Science bachelor's degree (finally - it took me 7 years with my day job). My senior project was a Windows Mobile feed reader named FeedFly (In process of creating an open source project for this - stay tuned). During our final presentation, in a room full of technical experts and industry advisors, we asked how many of them actually subscribed to blogs (indicating they regularly use a feed reader). I would say the response was less than 10 percent, and I was one of those with a raised hand. </p><p>I created a training session on blogging for my company 3 years ago. I was able to convince one of the teams to replace their email newsletter with a blog, and boy did it take off. It's still on the <a href="http://www.google.com/search?q=energy+legal">first page</a> of a Google search without having to pay any <a href="http://en.wikipedia.org/wiki/Search_engine_optimization">SEO</a> "specialist" vendors (I don't like most of these - another post). In this training, I predicted that web feeds would take off once Internet Explorer 7 and Outlook 2007 started shipping with built-in feed readers. I was wrong. Once I used the Outlook 2007 RSS client, I understand why. It's nowhere near as integrated as it needs to be in order to get everyone to use it. Plus, it's not easy to work with once you have a lot of subscriptions. If you're curious what reader I use, it's <a href="http://www.snarfware.com/">Snarfer</a> with the <a href="http://www.bloglines.com/">Bloglines</a> synchronization feature. Oh, and <a href="http://www.dopplerradio.net/">Doppler</a> for the podcasts. </p><p>What's the solution for the lack of adoption so far? The best solution would be to purchase your own domain name for hosting your blog, so you have the freedom of moving it to different hosts. Then, offer an <b>email subscription</b> feature for those that don't use readers. I'm sure future versions of feed readers will be much more user-friendly, and I'm still convinced it will take off. <a href="http://www.feedburner.com/">FeedBurner</a> is one of several free services that offers email distribution. With email distribution, your subscribers will receive an email with your new posts to the feed. Now, you won't become frustrated when people get tired of opening their feed readers you set up for them so they can check if your blog has a new post.</p><p>Even though FeedBurner is quite useful for email, I see it's primary purpose as an abstraction of a feed, so the feed source can be changed independent of all subscriptions. If you didn't have this abstraction layer set up and you moved your feed to a new hosting platform, you'd have to post a request on your old feed informing your subscribers to change their subscription to the new feed address. I've seen these posts several times in the feeds I've subscribed to over the years.</p><p>Let's be honest. If you don't have more than a couple of feeds subscribed, it's not worth setting up a blog reader at all. The simple action of opening a reader is step 1. Step 1 could have been to just visit the web site instead. If you have 2 feeds subscribed, you save yourself a whole step. That's a lot of software installation or configuration just to save a step. Maybe web feeds will never take off because of this. They only offer a significant advantage to someone that is spending too much time looking around for updated information.</p>Tim Joneshttp://www.blogger.com/profile/04669425604711302507noreply@blogger.com0