<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DkAAQ3szcSp7ImA9WxBXF0Q.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386</id><updated>2010-01-29T20:52:22.589+01:00</updated><title>Will Code for Nuts</title><subtitle type="html">Scribblings and constructs, from one code-dribbling lunatic to another.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.codefornuts.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.codefornuts.com/" /><link rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>69</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/blogspot/uykS" /><feedburner:info uri="blogspot/uyks" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry gd:etag="W/&quot;DkQMQn89fCp7ImA9WxNWFkQ.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-6126812175758206622</id><published>2009-10-13T11:34:00.007+02:00</published><updated>2009-10-16T13:59:43.164+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-16T13:59:43.164+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><title>Forcing the SharePoint object model to reload the 12 hive</title><content type="html">&lt;span style="font-style:italic;"&gt;Or: A bit of insight into the inner workings of the SharePoint object model&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This isn't necessarily something many have found themselves battling, but I most certainly did yesterday. As I was trying to deploy a feature containing a site definition, and subsequently create a site using the site definition, using my new &lt;a href="http://spdeploy.codeplex.com/" target="_blank"&gt;SPDeploy&lt;/a&gt; application, it all abruptly came to a nasty halt as I added the site (using SPSiteCollection.Add()):&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;File or arguments not valid for site template 'SomeTemplate#0'&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;Having triple-checked the template, feature, solution and just about everything else (yes, SPDeploy also does the equivalent of stsadm -o execadmsvcjobs), it eventually dawned on me how SharePoint goes about things when you create a new site.&lt;br /&gt;&lt;br /&gt;Just about any call in SharePoint will go through whatever object model class you're dealing with, such as SPSiteCollection, then to a SPRequestManager class, on to an SPRequest, SPRequestInternalClass and eventually into an arcane COM class (which appears to be part of the old SharePoint 1.0 structure, when COM, ISAPI and such was the shizznit). This COM class exposes hundreds of functions, ranging from list administration to tepmlate lookup.&lt;br /&gt;&lt;br /&gt;The issue in my case was two-fold. First of all, any thread in an application which uses the SharePoint object model, will access a set of SPRequest objects (based on whether you need an authenticated or non-authenticated instance) from the SPRequestManager class. These are per-thread request objects, which execute on a first-come, first-served basis. In figuring this out, I began experimenting with releasing the SPRequests for the main thread in the SPDeploy tool - which would cause them to be reinitialized in a subsequent call. I verified that this would not hurt other stateful objects, such as already lookup'd SPWebApplication instances; as all functions in these classes will acquire their SPRequest instance from SPRequestManager. And as I said: the SPRequestManager would create a new instance if an old one wasn't found.&lt;br /&gt;&lt;br /&gt;After typing up the reflection madness to clear the current thread's SPRequest objects, I gave the application another go. But still got the error - &lt;b&gt;doh&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;This confirmed my worst fears: the SPRequestInternalClass COM object, which is obviously native code, did its own caching. Pulling out Sysinternals' ProcMon and watching loads from the filesystem beginning with "web server extensions\12" backed this up further: web templates, and the rest of the hive, is only loaded once - on demand - then cached.&lt;br /&gt;&lt;br /&gt;In realizing this, the solution became pretty clear, and given the nature of SPRequestManager; not too shabby at all. For tools which require adding stuff to the 12 hive (such as deploying features with web templates), and then *use* those templates without re-executing the tool: you have to recycle the SPRequests in SPRequestManager, and recycle the OWSSVR.dll COM library.&lt;br /&gt;&lt;br /&gt;This *also* applies for PowerShell scenarios, where you during one session call into one of the SharePoint objects, such as e.g.:&lt;br /&gt;&lt;blockquote&gt;([type]"Microsoft.SharePoint.Administration.SPWebApplication")::Lookup($uri);&lt;/blockquote&gt;&lt;br /&gt;Doing so will load the OWSSVR.dll into your current session, and if you happen to e.g. trigger a function from the object model which scans the 12 hive for tepmlates; any subsequent script based addition to this folder, and then attempted usage of the additions, will fail with the error noted above.&lt;br /&gt;&lt;br /&gt;The workaround I apply in SPDeploy, which may look intrusive, but is completely harmless in the single-tool scenario, is:&lt;br /&gt;&lt;pre class="brush: csharp;"&gt;&lt;br /&gt;[DllImport("kernel32.dll")]&lt;br /&gt;private static extern IntPtr FreeLibrary(IntPtr library);&lt;br /&gt;&lt;br /&gt;[DllImport("kernel32.dll")]&lt;br /&gt;private static extern IntPtr GetModuleHandle(string lpModuleName);&lt;br /&gt;&lt;br /&gt;[DllImport("kernel32.dll")]&lt;br /&gt;private static extern IntPtr LoadLibrary(string lpFileName);&lt;br /&gt;&lt;br /&gt;public static void RecycleOwssvr()&lt;br /&gt;{&lt;br /&gt;    PropertyInfo threadCtx = typeof (SPFarm).GetProperty("ThreadContext", BindingFlags.Static | BindingFlags.NonPublic);&lt;br /&gt;    var tbl = (Hashtable) threadCtx.GetValue(null, null);&lt;br /&gt;    PropertyInfo requestProp = typeof (SPFarm).GetProperty("RequestNoAuth", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);&lt;br /&gt;    requestProp.GetValue(SPFarm.Local, null);&lt;br /&gt;    tbl.Remove(typeof (SPFarm).Assembly.GetType("Microsoft.SharePoint.SPRequestManager"));&lt;br /&gt;&lt;br /&gt;    IntPtr p = GetModuleHandle("OWSSVR.DLL");&lt;br /&gt;    FreeLibrary(p);&lt;br /&gt;    string stsadmPath = SPUtility.GetGenericSetupPath("ISAPI");&lt;br /&gt;    p = LoadLibrary(stsadmPath + @"\OWSSVR.DLL");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that will work beautifully.&lt;br /&gt;&lt;br /&gt;One note, though; the timer service has to be running for this recycling to work, otherwise some other internal component will freak out come second-go.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-6126812175758206622?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/_HmStEHykaQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/6126812175758206622/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=6126812175758206622" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6126812175758206622?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6126812175758206622?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/_HmStEHykaQ/forcing-sharepoint-tool-to-reload-12.html" title="Forcing the SharePoint object model to reload the 12 hive" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/10/forcing-sharepoint-tool-to-reload-12.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0EEQX4zfSp7ImA9WxNWEkQ.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-1172032457966892682</id><published>2009-10-11T23:01:00.003+02:00</published><updated>2009-10-11T23:13:20.085+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-11T23:13:20.085+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><title>SPDeploy update</title><content type="html">CodePlex link: &lt;a href="http://spdeploy.codeplex.com/"&gt;http://spdeploy.codeplex.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I just uploaded a new binary and source package to the CodePlex site, with some structural, config and visual improvements.&lt;br /&gt;&lt;br /&gt;First of all, web application extensions are now supported, meaning that web applications you either lookup or create as part of the configuration can be extended to intranet / extranet / custom zones. A custom rolemanager, membershipprovider and ssl usage can be specified through the attributes. See the xsd schema in the Schemas folder for more info on which attribues are available for the WebApplicationExtension node.&lt;br /&gt;&lt;br /&gt;Further, the logging now looks a lot better, thanks to clearer language and some more elaborate code behind the output formatting. I've also refurbished a lot of the general processing code, to make it easier to extend for possible project helpers. I've got more changes coming later, but with a pretty limited amount of time on my hands, I can't be specific as for when.&lt;br /&gt;&lt;br /&gt;There are still some hidden general behavior settings being tossed about in the background, which aren't obvious unless you turn to the code. I'll document and clear those up in a future (probably the next) release.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-1172032457966892682?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/NhU8HE4-kqw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/1172032457966892682/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=1172032457966892682" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/1172032457966892682?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/1172032457966892682?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/NhU8HE4-kqw/spdeploy-update.html" title="SPDeploy update" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/10/spdeploy-update.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUAQH0zcCp7ImA9WxNWEEQ.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-2510107372726868669</id><published>2009-10-09T14:37:00.003+02:00</published><updated>2009-10-09T14:44:01.388+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-09T14:44:01.388+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><category scheme="http://www.blogger.com/atom/ns#" term="architecture" /><title>Introducing SPDeploy - a quick way to (re)deploy dev and test structure to SharePoint</title><content type="html">CodePlex link: &lt;a href="http://spdeploy.codeplex.com" target="_blank"&gt;http://spdeploy.codeplex.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;About&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;SPDeploy will, based on schema validated xml configuration, lookup or create content in a local SharePoint farm, such as WebApplications (with content database and iis site / application pool), Sites, Webs. It will also handle wsp solution installations to the farm; further deployments of installed wsps into web applications; activation of features on all feature-accepting levels of the structure.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Folder, list item and document support is planned, but &lt;b&gt;not&lt;/b&gt; as a replacement for WSPs, features or plain common sense.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;With SPDeploy you can &lt;i&gt;quickly&lt;/i&gt;, and &lt;i&gt;painlessly&lt;/i&gt;, reset and redeploy your test or development environment, as well as quickly add development / third party features to the mix - &lt;b&gt;without&lt;/b&gt; incorporating them into your production ready wsps, or extending large powershell / batch scripts. The point is to encourage &lt;b&gt;proper&lt;/b&gt; integration testing in your development environment, and bringing &lt;b&gt;structure&lt;/b&gt; to your favorite SharePoint build server.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;i&gt;Important!&lt;/i&gt;&lt;/u&gt; Deployment or upgrades into a production or even UAT environments should be made with as strict a setup as possible, with (if possible) &lt;b&gt;all&lt;/b&gt; the structure definition, creation and library deployment packaged into definitions, features and solutions. &lt;u&gt;I do not suggest using SPDeploy for this.&lt;/u&gt; Test with SPDeploy - ship with wsps and upgradable features!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Developer / extension notes&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;All data classes in the project are generated from XSD, and generic configration runners make it easy to expand with custom sub nodes. Add an accepted sub node to the XSD, rebuild the project to generate new data classes, and create a &lt;b&gt;Processor&lt;/b&gt; derived class to deal with the new sub node type. A new sub node processor will automatically get the result from the lookup or creation of the node above (like a SPWeb, if you're adding processors under the XML's Web node).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Sample xml configuration data&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;If you reference the schema provided in the source package, Visual Studio will even give you autocompletion and validation of the configuration as you go.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: xml;"&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;&lt;br /&gt;&amp;lt;Deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Schemas\SPDeploy.xsd"&amp;gt;&lt;br /&gt;  &amp;lt;Solutions&amp;gt;&lt;br /&gt;    &amp;lt;Solution FileName="C:\path\to\a\Solution.wsp"/&amp;gt;&lt;br /&gt;  &amp;lt;/Solutions&amp;gt;&lt;br /&gt;  &amp;lt;WebApplications&amp;gt;&lt;br /&gt;    &amp;lt;WebApplication Action="Lookup" Name="SharePoint - 80"&amp;gt;&lt;br /&gt;      &amp;lt;SolutionDeployments&amp;gt;&lt;br /&gt;        &amp;lt;SolutionDeployment Name="Solution.wsp"/&amp;gt;&lt;br /&gt;      &amp;lt;/SolutionDeployments&amp;gt;&lt;br /&gt;      &amp;lt;Sites&amp;gt;&lt;br /&gt;        &amp;lt;Site Action="Create" Url="/" Title="RootWeb" LCID="1044" Template="STS#1" OwnerLogin="somedomain\user"&amp;gt;&lt;br /&gt;          &amp;lt;RootWeb&amp;gt;&lt;br /&gt;            &amp;lt;Features&amp;gt;&lt;br /&gt;              &amp;lt;FeatureActivation Guid="fba9e8df-b758-4197-8768-169ef727d5bb" /&amp;gt;&lt;br /&gt;            &amp;lt;/Features&amp;gt;&lt;br /&gt;            &amp;lt;Webs&amp;gt;&lt;br /&gt;              &amp;lt;Web Action="Create" Title="RecursionWeb" LCID="1044" Template="STS#1" Url="RecursionIsFun"&amp;gt;&lt;br /&gt;                &amp;lt;Webs&amp;gt;&lt;br /&gt;                  &amp;lt;Web Action="Create" Title="RecursionWeb" LCID="1044" Template="STS#1" Url="RecursionIsFun"&amp;gt;&lt;br /&gt;                    &amp;lt;Webs&amp;gt;&lt;br /&gt;                      &amp;lt;Web Action="Create" Title="RecursionWeb" LCID="1044" Template="STS#1" Url="RecursionIsFun"&amp;gt;&lt;br /&gt;                        &amp;lt;Webs&amp;gt;&lt;br /&gt;                          &amp;lt;Web Action="Create" Title="RecursionWeb" LCID="1044" Template="STS#1" Url="RecursionIsFun"&amp;gt;&lt;br /&gt;                          &amp;lt;/Web&amp;gt;&lt;br /&gt;                        &amp;lt;/Webs&amp;gt;&lt;br /&gt;                      &amp;lt;/Web&amp;gt;&lt;br /&gt;                    &amp;lt;/Webs&amp;gt;&lt;br /&gt;                  &amp;lt;/Web&amp;gt;&lt;br /&gt;                &amp;lt;/Webs&amp;gt;&lt;br /&gt;              &amp;lt;/Web&amp;gt;&lt;br /&gt;            &amp;lt;/Webs&amp;gt;&lt;br /&gt;          &amp;lt;/RootWeb&amp;gt;&lt;br /&gt;        &amp;lt;/Site&amp;gt;&lt;br /&gt;      &amp;lt;/Sites&amp;gt;&lt;br /&gt;    &amp;lt;/WebApplication&amp;gt;&lt;br /&gt;    &amp;lt;WebApplication Action="Create" Name="TestApp" Header="vm-dev" Port="8009" Database="TestContent" PoolName="TestPooly" CreatePool="true"&amp;gt;&lt;br /&gt;      &amp;lt;SolutionDeployments&amp;gt;&lt;br /&gt;        &amp;lt;SolutionDeployment Name="Solution.wsp"/&amp;gt;&lt;br /&gt;      &amp;lt;/SolutionDeployments&amp;gt;&lt;br /&gt;      &amp;lt;Sites&amp;gt;&lt;br /&gt;        &amp;lt;Site Action="Create" Url="/" Template="STS#1" LCID="1044" OwnerLogin="somedomain\user" Title="TestWeb"&amp;gt;&lt;br /&gt;          &amp;lt;RootWeb&amp;gt;&lt;br /&gt;            &amp;lt;Webs&amp;gt;&lt;br /&gt;              &amp;lt;Web Action="Create" Title="SubWeb" LCID="1044" Template="STS#1" Url="SubWebUrl"&amp;gt;&lt;br /&gt;                &amp;lt;Features&amp;gt;&lt;br /&gt;                  &amp;lt;FeatureActivation Guid="fba9e8df-b758-4197-8768-169ef727d5bb" /&amp;gt;&lt;br /&gt;                &amp;lt;/Features&amp;gt;&lt;br /&gt;              &amp;lt;/Web&amp;gt;&lt;br /&gt;            &amp;lt;/Webs&amp;gt;&lt;br /&gt;          &amp;lt;/RootWeb&amp;gt;&lt;br /&gt;        &amp;lt;/Site&amp;gt;&lt;br /&gt;      &amp;lt;/Sites&amp;gt;&lt;br /&gt;    &amp;lt;/WebApplication&amp;gt;&lt;br /&gt;  &amp;lt;/WebApplications&amp;gt;&lt;br /&gt;&amp;lt;/Deployment&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-2510107372726868669?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/qwknb0VpMJQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/2510107372726868669/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=2510107372726868669" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/2510107372726868669?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/2510107372726868669?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/qwknb0VpMJQ/introducing-spdeploy-quick-way-to.html" title="Introducing SPDeploy - a quick way to (re)deploy dev and test structure to SharePoint" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/10/introducing-spdeploy-quick-way-to.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0cCR3wycSp7ImA9WxNXF0U.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-189034007200747745</id><published>2009-10-05T23:35:00.012+02:00</published><updated>2009-10-06T00:17:46.299+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-06T00:17:46.299+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="best practices" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><title>Assigning dynamic ids to HTML and Script elements - Client Side!</title><content type="html">This question popped up on Twitter yesterday:&lt;br /&gt;&lt;br /&gt;&lt;div style="padding-left: 10px"&gt;&lt;i&gt;How do you usually handle duplicate IDs for html elements and scripts in SharePoint Web Parts, scripts and controls?&lt;/i&gt;&lt;/div&gt;&lt;br /&gt;Which is a natural concern, with all the different web parts and scripts that's being injected SharePoint portals these days. To end Monday off (in my neck of the woods anyway), I thought I'd make a quick post on what I do in my projects.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Script naming convention&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;For scripts, I have a few guidelines. First of all, my scripts follow a naming convention based on my organization handle, library concern, project name and so forth. Looking back to my previous couple of posts, e.g. the Script Loader (&lt;a href="http://www.codefornuts.com/2009/10/pieces-of-my-core-javascript-library.html" target="_blank"&gt;http://www.codefornuts.com/2009/10/pieces-of-my-core-javascript-library.html&lt;/a&gt;), this name would be grep.scriptloader - as that's a core piece of script for me. For a script specific to a web part, I'd name it similar to the webpart, e.g. grep.webparts.navigationcontrol.&lt;br /&gt;&lt;br /&gt;As for the actual script definitions, two matters apply: is it a class, or a single instance?&lt;br /&gt;&lt;br /&gt;For single instances, such as the Script Loader above, I declare it like this:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;if (!window.grep) window.grep = {};&lt;br /&gt;if (!grep.someSingleInstanceClass)&lt;br /&gt;{&lt;br /&gt;    grep.someSingleInstanceClass=&lt;br /&gt;    {&lt;br /&gt;        instanceVar: "baz",&lt;br /&gt;        foo: function() {},&lt;br /&gt;        bar: function() {}&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So basically I check if it's already been assigned, and if it hasn't, I assign it to a global variable. The above code would then be accessed such as&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;grep.someSingleInstanceClass.foo();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For multi instance classes, I follow regular javascript oo syntax, with the same has-it-been-defined-clause, such as:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;if (!window.grep) window.grep = {};&lt;br /&gt;if (!grep.someClass)&lt;br /&gt;{&lt;br /&gt;    grep.someClass = function(ctorVariable)&lt;br /&gt;    {&lt;br /&gt;        this.someVar = ctorVariable;&lt;br /&gt;        this.DoWhatever();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    grep.someClass.prototype.DoWhatever = function()&lt;br /&gt;    {&lt;br /&gt;       // ...&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    grep.someClass.prototype.AnotherFunction  = function(input)&lt;br /&gt;    {&lt;br /&gt;       // ...&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then access that such as&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;var foo = new grep.someClass("some value for the ctor");&lt;br /&gt;foo.AnotherFunction(42);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;HTML element lookups, and dynamic ids&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;For elements, I use another trick. Since giving the elements a specific id can easily crash with other web parts of the same kind, or even other web parts / controls, I tend to avoid using ids as much as possible, and rather rely on class lookups (where the class has been named similar to the web part). For such lookups to be possible, however, they must be based somewhere - you can't just search for all instances of 'css class foo' in all of the body tag.&lt;br /&gt;&lt;br /&gt;Imagine the following scenario:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;&amp;lt;div&amp;gt;This element will be added several times, by other instances of the same web part&amp;lt;/div class="webPartInstanceFrame"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;    new grep.webparts.someFrameManager(id of the previous div);&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The script convention noted further up would make sure that the class' code will only be added once, and we feed that the id of the div, so that the class can make css class based jQuery (or similar) lookups to find the elements it needs within the frame. But what's the id of the div? Assigning a static id to it would crash with other instances of the same web part, and using a class would be just as hopeless.&lt;br /&gt;&lt;br /&gt;That's where the trick applies, and the following is a fully functional example:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;    function renamePrevious(tagType, id)&lt;br /&gt;    {&lt;br /&gt;        $(document.body.lastChild)&lt;br /&gt;            .prev(tagType + ":first")&lt;br /&gt;            .attr("id", id);&lt;br /&gt;        return id;&lt;br /&gt;    }&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div&amp;gt;I want a dynamic ID!&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;    var dynamicId = renamePrevious("div", "SomePrefix" + new Date().valueOf());&lt;br /&gt;    /* renamePrevious should be passed as an argument in dynamicId's place, to avoid all variable assignments! */&lt;br /&gt;    document.getElementById(dynamicId).innerHTML += " - OK!";&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The trick works by looking up the script block's element in the html dom. This element is the last added element, as the script block executes, even if it's not at the bottom of the page. Based on this script element, it searches its previous siblings for the first match of a given tag type, then assigns a new id.&lt;br /&gt;&lt;br /&gt;You could do a lot better with the actual dynamic id than the millisecond postfix shown here, such as an incrementing (or even random) number which is added to a string, then checking if that already exists (in which case we increment / re-random and try again). I leave that to your imagination, though.&lt;br /&gt;&lt;br /&gt;As for the example further up, the frame could be identified and put into action such as:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;    new grep.webparts.someFrameManager(&lt;br /&gt;        renamePrevious("div", "E" + new Date().valueOf()));&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that would enable us to avoid any and all name crashes for our component.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-189034007200747745?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/P0E-Q1e_v3w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/189034007200747745/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=189034007200747745" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/189034007200747745?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/189034007200747745?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/P0E-Q1e_v3w/giving-web-part-html-and-script.html" title="Assigning dynamic ids to HTML and Script elements - Client Side!" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/10/giving-web-part-html-and-script.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkADQncyfSp7ImA9WxNXF0U.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-1212334663685740463</id><published>2009-10-02T16:42:00.006+02:00</published><updated>2009-10-06T00:12:53.995+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-06T00:12:53.995+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><title>Connecting SharePoint UI Elements: SPDrag and the DropBox</title><content type="html">So the last few posts have focused on some of my core javascript libraries. Nothing of the fancy stuff, just basic paper and glue type things. To make it all come together, I'll bring back up something I mentioned on Twitter about a month ago, but haven't spoken much of since: Two libraries of mine called &lt;b&gt;SPDrag&lt;/b&gt; and &lt;b&gt;DropBox&lt;/b&gt;. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;SPDrag&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The first of the two essentially makes standard SharePoint ui elements, such as files, list items and so forth, draggable. With a library building on jQuery, jQuery UI and my own Lambda framwork, it allows a developer to connect various of the SharePoint elements to his own widgets. An example of such a widget would be the Hierarchical Navigation replacement I've mentioned time and again (jQuery ajax powered, lazy-loaded SharePoint quick launch replacement). Another example is the DropBox.&lt;br /&gt;&lt;br /&gt;The Lambda Framework, &lt;a href="http://www.codefornuts.com/2009/10/pieces-of-my-core-javascript-library_02.html" target="_blank"&gt;described here&lt;/a&gt; is a key player in making this work.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;DropBox&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;One of the basic features missing in SharePoint 2007 has for me been the possibility of picking things up, taking them to another location, and dropping them into place. Be that files, folders, tasks or whatnot - I've wanted to pull them about. It's no joke that file copy / move in default SharePoint is as fun as watching grass grow. Even with webdav, it's nowhere near as pleasant as it should have been.&lt;br /&gt;&lt;br /&gt;Enter the DropBox. Pick up a file. Pick up an item. Pick whatever you want, and put it in the DropBox. Then poke around the portal at your own whimsical liking; pull the item from the DropBox and place it in a compatible container. File copy made smooth, task assignment made friendly.&lt;br /&gt;&lt;br /&gt;So am I going to release this now? No, I'm afraid not. I still have some work to do on both libraries, and I've yet to decide in which shape and form the release will take on. I'm obviously thinking OSS, but that's futile unless someone actually wants to contribute ;-)&lt;br /&gt;&lt;br /&gt;Anyhoo, here's the screencast of several of the said libraries acting together. Please watch, and supply me with ideas if the concept appeals to you!&lt;br /&gt;&lt;br /&gt;&lt;a target="_blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.screencast.com/users/einaros/folders/Jing/media/82f4d6f1-19d7-4037-ae00-6332b50d27fc#"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 216px;" src="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/SsYUEvEjQrI/AAAAAAAAEIo/C_4gQ89YLn0/s320/dropbox.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5388016075782963890" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-1212334663685740463?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/nNJDskrSVrw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/1212334663685740463/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=1212334663685740463" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/1212334663685740463?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/1212334663685740463?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/nNJDskrSVrw/connecting-sharepoint-ui-elements.html" title="Connecting SharePoint UI Elements: SPDrag and the DropBox" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/SsYUEvEjQrI/AAAAAAAAEIo/C_4gQ89YLn0/s72-c/dropbox.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/10/connecting-sharepoint-ui-elements.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkADQncyfip7ImA9WxNXF0U.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-1181061878950411506</id><published>2009-10-02T16:11:00.005+02:00</published><updated>2009-10-06T00:12:53.996+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-06T00:12:53.996+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><title>Pieces of my core javascript library: The Lambda Framework</title><content type="html">Following up on my previous post, about the Script Loader piece of my core library, here's a couple more central pieces for the stuff I'm about to tell about in posts to follow.&lt;br /&gt;&lt;br /&gt;On the spot in this post is the lambda library. Simply put, a lambda here means an expression with placeholders for parameters to come. As such, it's not unlike the closures we already have in javascript, but this does up the stakes a little bit.&lt;br /&gt;&lt;br /&gt;Closures allow us to define inline functions, and bind expressions to said function's parameters as well as those of the outside scope, ex:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;function doSomething()&lt;br /&gt;{&lt;br /&gt;    var someGreeting = "Hi";&lt;br /&gt;    var closure = function(name) { alert(someGreeting + " " + name); }&lt;br /&gt; closure("yourself"); // Should alert "Hi yourself"&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;... which is all old news.&lt;br /&gt;&lt;br /&gt;With the following lambda library, I could construct the following fulent expression tree:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt; var verification = _.first().is(10);&lt;br /&gt; alert(verification(10)); // Should alert 'true'&lt;br /&gt; &lt;br /&gt; // The same as a closure:&lt;br /&gt; // var verificationClosure = function(x) { return x == 10; }&lt;br /&gt; &lt;br /&gt; // A more advanced verification (although hardly the best we can do):&lt;br /&gt; var advancedVerification = _.and(_.first().is(10), _.second().isnt(_.first()));&lt;br /&gt; alert(advancedVerification(10, 10)); // Should alert 'false'&lt;br /&gt; alert(advancedVerification(10, 20)); // Should alert 'true'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This may seem like utter gibberish, but in certain cases it can be useful in order to define a relatively clear set of rules for an operation to take place. Specifically I'm using this in a library which establishes relationships between abstracted gui elmements. What I'm able to do is say, with an expression tree, that:&lt;br /&gt;&lt;span style="margin-left: 10px"&gt;&lt;i&gt;Given squares A and B; B can be dragged into A if B isn't already a child of A, and it has a certain inherent property.&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is actual code from my DropBox SharePoint plugin (I'll showcase that later), which uses another library of mine, SPDrag (that essentially makes standard SharePoint UI elements drag'n'droppable).&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;    grep.spdrag.connect(&lt;br /&gt;        /* target container */ $("table.ms-listviewtable"),&lt;br /&gt;        /* conditions */ [&lt;br /&gt;            _.and(&lt;br /&gt;                _.item().is_dropbox_item(),&lt;br /&gt;                _.target().is_document_library(),&lt;br /&gt;                _.not(_.item().is_child_of(_.target()))&lt;br /&gt;            )&lt;br /&gt;        ],&lt;br /&gt;        /* action on drop */ function(element)&lt;br /&gt;        {&lt;br /&gt;            self._deployFileToDocumentLibrary(element);&lt;br /&gt;        });&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Connect, in other words, is a function which accepts three parameters: a target container (in this case the a SharePoint list view), a condition for it to allow an element to be dragged onto it, and what's supposed to happen if they are dropped there.&lt;br /&gt;&lt;br /&gt;The second parameter, the conditions, is a further abstracted lambda expression tree, which introduces elements specific to the SPDrag library. &lt;i&gt;item&lt;/i&gt;, the dragged element; &lt;i&gt;target&lt;/i&gt;, the target container; and a HTML DOM type function called is_child_of which is here used to disallow dropping elements from the target container back onto the target container.&lt;br /&gt;&lt;br /&gt;Arguably, the syntax can get complex, especially with the logical operators preceding the operands rather than being between them, but it has also the ability to be very clear and precise on the domain operation at hand, avoiding as much of the function / closure / parameter clutter as possible.&lt;br /&gt;&lt;br /&gt;Without further ado, here's the lambda core, which can be (and are) abstracted into domain specific lambda expressions. In the case of the above &lt;i&gt;item&lt;/i&gt; element, that's actually a specialization of &lt;i&gt;first&lt;/i&gt; in the base lambda library, but that's for the library implementing the domain specific lambda to know only. As the end user, you'd only have to consider item representing the item.&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;if(!window.grep) window.grep = {};&lt;br /&gt;if(!window.grep.lambda)&lt;br /&gt;{&lt;br /&gt;    window.grep.lambda = window._ = &lt;br /&gt;    {&lt;br /&gt;        nth:function(x){return window.grep.tools.extend(function(){return arguments[x-1];},this);},&lt;br /&gt;        first:function(){return this.nth(1);},&lt;br /&gt;        second:function(){return this.nth(2);},&lt;br /&gt;        is:function(x){var c=this;return function(){return (typeof(x)=="function"?x.apply(this,arguments):x)==c.apply(this,arguments);}},&lt;br /&gt;        isnt:function(x){var c=this;return function(){return (typeof(x)=="function"?x.apply(this,arguments):x)!=c.apply(this,arguments);}},&lt;br /&gt;        gt:function(x){var c=this;return function(){return (typeof(x)=="function"?x.apply(this,arguments):x)&amp;lt;c.apply(this,arguments);}},&lt;br /&gt;        lt:function(x){var c=this;return function(){return (typeof(x)=="function"?x.apply(this,arguments):x)&amp;gt;c.apply(this,arguments);}},&lt;br /&gt;        and:function(){var a=arguments;return function(){for(var x=0;x&amp;lt;a.length;++x)if(!a[x].apply(this,arguments))return false;return true;};},&lt;br /&gt;        or:function(){var a=arguments;return function(){for(var x=0;x&amp;lt;a.length;++x){if(a[x].apply(this,arguments))return true;}return false;};},&lt;br /&gt;        not:function(x){return function(){return !x.apply(this,arguments);};}&lt;br /&gt;    };&lt;br /&gt;    &lt;br /&gt;    window.grep.lambdapredicate = function(handler)&lt;br /&gt;    {&lt;br /&gt;        return function()&lt;br /&gt;        {&lt;br /&gt;            var outerArgs = arguments;&lt;br /&gt;            var ctx = this; &lt;br /&gt;            return function() &lt;br /&gt;            { &lt;br /&gt;                var args = [ ctx.apply(this, arguments) ];&lt;br /&gt;                for(var i = 0; i &amp;lt; outerArgs.length; ++i)&lt;br /&gt;                {&lt;br /&gt;                    args.push(typeof(outerArgs[i]) == "function" ? outerArgs[i].apply(this, arguments) : outerArgs[i]);&lt;br /&gt;                }&lt;br /&gt;                return handler.apply(this, args);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is an excerpt from the tool library, which contains the &lt;i&gt;extend&lt;/i&gt; function used in the above lambda lib.&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;if (!window.grep) window.grep = {};&lt;br /&gt;if (!window.grep.tools)&lt;br /&gt;{&lt;br /&gt;    window.grep.tools = {&lt;br /&gt;        extend: function()&lt;br /&gt;        {&lt;br /&gt;            var r = arguments[0];&lt;br /&gt;            for(var i = 1; i &amp;lt; arguments.length; ++i)&lt;br /&gt;            {&lt;br /&gt;                for(var x in arguments[i])&lt;br /&gt;                {&lt;br /&gt;                    r[x] = arguments[i][x];&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            return r;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-1181061878950411506?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/mXkR5qKQKQ4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/1181061878950411506/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=1181061878950411506" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/1181061878950411506?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/1181061878950411506?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/mXkR5qKQKQ4/pieces-of-my-core-javascript-library_02.html" title="Pieces of my core javascript library: The Lambda Framework" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/10/pieces-of-my-core-javascript-library_02.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMDQ3kycCp7ImA9WxNXFEU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-7049405882930166864</id><published>2009-10-02T12:38:00.010+02:00</published><updated>2009-10-02T13:37:52.798+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-02T13:37:52.798+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><title>Pieces of my core javascript library: The Script Loader</title><content type="html">I'm about to release a few more open source SharePoint solutions, including a core javascript library feature. The CoreJS is a required feature for the &lt;a href="http://www.codefornuts.com/2009/08/hierarchical-ajax-navigation-solution.html" target="_blank"&gt;hierarchical navigation component&lt;/a&gt;, which will also be properly released as OSS on CodePlex before long.&lt;br /&gt;&lt;br /&gt;A central piece of the CoreJS library and activation feature, is the Script Loader. This is in fact the only piece of the library which is automatically injected (as a delegate control) into any page in the site collection it's activated for.&lt;br /&gt;&lt;br /&gt;The Script Loader's purpose is pretty much exactly what it sounds like: it's a general purpose way of loading scripts from javascript code. A sample scenario would e.g. be if you need a few external utility library, but you don't know if it has been loaded yet. Using the script loader, this would amount to:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;function loadComplete()&lt;br /&gt;{&lt;br /&gt;  alert("The scripts are loaded, and we're good to go!");&lt;br /&gt;}&lt;br /&gt;  &lt;br /&gt;grep.scriptloader.loadScripts(&lt;br /&gt;  "http://www.json.org/json2.js",&lt;br /&gt;  "myJsonAjaxLibrary.js",&lt;br /&gt;  loadComplete&lt;br /&gt;  );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Script Loader would ensure that all the scripts are loaded in order, so in this above scenario, where the fictional myJsonAjaxLibrary relies on json2.js to do its thing, myJsonAjaxLibrary would not be loaded at all until json2 is present and ready. Additionally, we can supply a function as a parameter to loadScripts, which is called once all previous tasks have completed.&lt;br /&gt;&lt;br /&gt;Making more calls to loadScripts would queue up even more scripts to be loaded. Imagine the following turn of events:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;// On top of your script&lt;br /&gt;grep.scriptloader.loadScripts("http://www.json.org/json2.js");&lt;br /&gt;  &lt;br /&gt;// ... Meanwhile (actually later) in some distant part of the same script&lt;br /&gt;grep.scriptloader.loadScripts(&lt;br /&gt;  "myLibrary.js",&lt;br /&gt;  function() { alert("myLibrary loaded"); },&lt;br /&gt;  "http://www.json.org/json2.js",&lt;br /&gt;  function() { alert("json2 loaded"); });&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's pretty common for different parts of your script setup to require different libraries, and yet other times they even require the same scripts (whew). The script loader library does two things here: It will load all scripts in order, even between calls to loadScripts. In the above case, myLibrary would *not* be loaded until json2.js has been loaded from json.org; since that was specified in the first call to loadScripts. Second, it it will check each source url, to make sure that it hasn't already been loaded. If it's already loaded; it will be skipped.&lt;br /&gt;&lt;br /&gt;In the above case, the actual sequence of events would be:&lt;ol&gt;&lt;li&gt;load json2&lt;br /&gt;&lt;/li&gt;&lt;li&gt;load myLibrary&lt;br /&gt;&lt;/li&gt;&lt;li&gt;alert(myLibrary loaded)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;alert(json2 loaded)&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Here's the full script, ready for inclusion. If you spot anything off, you'll have plenty of time to suggest changes once I've moved this and other core javascript libraries, plus the SharePoint activation feature, to a project on CodePlex!&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;if (!window.grep) window.grep = {};&lt;br /&gt;if (!window.grep.scriptloader)&lt;br /&gt;{&lt;br /&gt;  window.grep.scriptloader =&lt;br /&gt;  {&lt;br /&gt;      nocache: false,&lt;br /&gt;&lt;br /&gt;      loadScripts: function()&lt;br /&gt;      {&lt;br /&gt;          for (var i = 0; i &amp;lt; arguments.length; ++i)&lt;br /&gt;          {&lt;br /&gt;              var arg = arguments[i];&lt;br /&gt;              switch (typeof (arg))&lt;br /&gt;              {&lt;br /&gt;                  case "function":&lt;br /&gt;                  case "string": window.grep.scriptloader._queue.push(arg); break;&lt;br /&gt;                  case "object": if (arg.length &amp;gt; 0) for (var x = 0; x &amp;lt; arg.length; ++x) window.grep.scriptloader._queue.push(arg[x]); break;&lt;br /&gt;              }&lt;br /&gt;          }&lt;br /&gt;          if (!window.grep.scriptloader._active)&lt;br /&gt;          {&lt;br /&gt;              window.grep.scriptloader._active = true;&lt;br /&gt;              window.grep.scriptloader._processNextQueueItem();&lt;br /&gt;          }&lt;br /&gt;      },&lt;br /&gt;  &lt;br /&gt;      /* Private interface */&lt;br /&gt;  &lt;br /&gt;      _active: false,&lt;br /&gt;      _queue: [],&lt;br /&gt;      _loaded: {},&lt;br /&gt;&lt;br /&gt;      _processNextQueueItem: function()&lt;br /&gt;      {&lt;br /&gt;          if (window.grep.scriptloader._queue.length &amp;gt; 0)&lt;br /&gt;          {&lt;br /&gt;              var item = window.grep.scriptloader._queue.shift();&lt;br /&gt;              if (typeof (item) == "function")&lt;br /&gt;              {&lt;br /&gt;                  item();&lt;br /&gt;                  window.grep.scriptloader._processNextQueueItem();&lt;br /&gt;              }&lt;br /&gt;              else if (typeof (item) == "string")&lt;br /&gt;              {&lt;br /&gt;                  if (!window.grep.scriptloader._loaded[item])&lt;br /&gt;                  {&lt;br /&gt;                      window.grep.scriptloader._loaded[item] = true;&lt;br /&gt;                      window.grep.scriptloader._loadScript(item, window.grep.scriptloader._processNextQueueItem);&lt;br /&gt;                  }&lt;br /&gt;                  else&lt;br /&gt;                  {&lt;br /&gt;                      window.grep.scriptloader._processNextQueueItem();&lt;br /&gt;                  }&lt;br /&gt;              }&lt;br /&gt;          }&lt;br /&gt;          else&lt;br /&gt;          {&lt;br /&gt;              window.grep.scriptloader._active = false;&lt;br /&gt;          }&lt;br /&gt;      },&lt;br /&gt;  &lt;br /&gt;      _loadScript: function(src, onload)&lt;br /&gt;      {&lt;br /&gt;          var js = document.createElement('script');&lt;br /&gt;          if (window.grep.scriptloader.nocache)&lt;br /&gt;          {&lt;br /&gt;              js.src = src + "?" + Math.random();&lt;br /&gt;          }&lt;br /&gt;          else&lt;br /&gt;          {&lt;br /&gt;              js.src = src;&lt;br /&gt;          }&lt;br /&gt;          js.type = 'text/javascript';&lt;br /&gt;  &lt;br /&gt;          if (js.readyState)&lt;br /&gt;          {&lt;br /&gt;              js.onerror = function() { alert("Error loading script: " + this.src); };&lt;br /&gt;              js.onreadystatechange = function()&lt;br /&gt;              {&lt;br /&gt;                  if (js.readyState == "loaded" || js.readyState == "complete")&lt;br /&gt;                  {&lt;br /&gt;                      js.onreadystatechange = null;&lt;br /&gt;                      if (onload)&lt;br /&gt;                      {&lt;br /&gt;                          onload(src);&lt;br /&gt;                      }&lt;br /&gt;                  }&lt;br /&gt;              };&lt;br /&gt;          }&lt;br /&gt;          else&lt;br /&gt;          {&lt;br /&gt;              js.onload = function()&lt;br /&gt;              {&lt;br /&gt;                  if (onload)&lt;br /&gt;                  {&lt;br /&gt;                      onload(src);&lt;br /&gt;                  }&lt;br /&gt;              };&lt;br /&gt;          }&lt;br /&gt;  &lt;br /&gt;          var head = document.getElementsByTagName('head')[0];&lt;br /&gt;          if (head)&lt;br /&gt;          {&lt;br /&gt;              head.appendChild(js);&lt;br /&gt;          }&lt;br /&gt;          else&lt;br /&gt;          {&lt;br /&gt;              document.body.appendChild(js);&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-7049405882930166864?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/_p2M-Jroqbs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/7049405882930166864/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=7049405882930166864" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/7049405882930166864?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/7049405882930166864?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/_p2M-Jroqbs/pieces-of-my-core-javascript-library.html" title="Pieces of my core javascript library: The Script Loader" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/10/pieces-of-my-core-javascript-library.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkANR3g6fSp7ImA9WxNXE00.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-2115515659354105920</id><published>2009-09-30T11:10:00.007+02:00</published><updated>2009-09-30T11:59:56.615+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-30T11:59:56.615+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="best practices" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="ramblings" /><category scheme="http://www.blogger.com/atom/ns#" term="worst practices" /><title>Dear SharePoint Team: ItemDeleted? Seriously?</title><content type="html">&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.formview.itemdeleted.aspx" target="_blank"&gt;ItemDeleted&lt;/a&gt; - a would-be handy event receiver, but in reality no more useful than a box full of used matches.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;An analogy, if I may, to explain the state of affairs&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;While away on business, you're phoned up by your home town police, and given the brief message:&lt;br /&gt;- &lt;span style="font-style: italic;"&gt;Someone very dear to you just passed away.&lt;/span&gt;&lt;br /&gt;Caught slightly off guard, but immediately inquisitive, you might ask:&lt;br /&gt;- &lt;span style="font-style: italic;"&gt;Who?!&lt;/span&gt;&lt;br /&gt;Which is rewarded with a mere:&lt;br /&gt;- &lt;span style="font-style: italic;"&gt;I don't know.&lt;/span&gt;&lt;br /&gt;- &lt;span style="font-style: italic;"&gt;What do you mean "I don't know"? For God's sake man, someone just died, and you can't tell me who?&lt;/span&gt;&lt;br /&gt;- &lt;span style="font-style: italic;"&gt;No.&lt;/span&gt;&lt;br /&gt;- &lt;span style="font-style: italic;"&gt;Well do you know anyone who could know, then?&lt;/span&gt;&lt;br /&gt;- &lt;span style="font-style: italic;"&gt;No.&lt;/span&gt;&lt;br /&gt;- &lt;span style="font-style: italic;"&gt;Great .. Thanks a heap.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;What I'm saying is ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;ItemDeleted knows *nothing* about the item that was deleted, apart from its now invalid index into the list. While the event receiver properties does contain the fields BeforeProperties and AfterProperties, the former of which one would expect to contain some information about the state of affairs *prior* to the delete taking place; both are &lt;span style="font-style: italic;"&gt;null&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Next up, there's ItemDeleting. After all, that signals the same possible state change, and unlike it's dimwitted brother '&lt;span style="font-style: italic;"&gt;Deleted&lt;/span&gt;, it *does* have the BeforeProperties initialized. Upon discovering this, you might decide to use this receiver to process item deletions, such as in my current project - to signal item changes across farm boundaries. What you're not considering, then, is that ItemDeleting is just an indication of what &lt;span style="font-style: italic;"&gt;might&lt;/span&gt; happen. Any number of receivers following yours may set SPItemEventProperties.Cancel to abort the delete, thus possibly invalidating the state of your application (parts of it thinking an item is deleted, other parts knowing it's not).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;So what can we do, then?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Parse the RecycleBin?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Possible, but even if we did iterate all items in there (possibly *a lot* of overhead), we don't have any idea at all what the items title, name or guid might have been.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Store event handler instance / static data in ItemDeleting, then check in ItemDeleted?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Unlike the RecycleBin, this would work, and it would give a reliable indication of what item was deleted, but it'd cost an arm and a leg efficiency-wise. Event handler instance data is a no-go, I should add, since event handler classes are initialized once per call to a receiver function (ugh). You could use an interlocked static field, but 1. it would be very inefficient in any environment with more than zero users (locking and unlocking the field for each call), 2. I would grant you *no* guarantees in a multi-frontend farm setup, or even a multi-worker process environment.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Store state info in the SPItemEventProperties.ReceiverData &lt;/span&gt;&lt;span style="font-style: italic;"&gt; from ItemDeleting, then process it in ItemDeleted&lt;/span&gt;&lt;span style="font-style: italic;"&gt;?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Once again: would require inefficient thread interlocking, possibly not visible across frontends / processes, and certainly the most ugly solution you'd ever put into production code. You should be ashamed.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Store data in [web properties / custom list / user profile / own database / an usb drive] from ItemDeleting, then process it in ItemDeleted?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sure, that would work, and all apart from the usb drive it would probably be accessible from all processes / frontends. Your major pain in the neck here, though, is the lingering fact that ItemDeleted *may not be executed*. Imagine installing some third party WillProtectYourDataAndCrap wsp solution, which essentially will cancel all ItemDeleting events. Then imagine your custom table / properties / whatever, suddenly containing 50.000 could-once-have-been deleted entries, which were never processed or removed since ItemDeleted never executed. At that point: welcome a custom SPJobDefinition to clean up every x minutes. Also give yourself complementary slap on the back, since you've just implemented the worlds most over-engineered and likely-to-break-down-horribly "What was just deleted?"-mechanism.&lt;br /&gt;&lt;br /&gt;Man, wouldn't it have been great if ItemDeleted could just &lt;span style="font-weight: bold;"&gt;tell you what was just deleted&lt;/span&gt;?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-2115515659354105920?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/kwWYv84T66A" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/2115515659354105920/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=2115515659354105920" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/2115515659354105920?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/2115515659354105920?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/kwWYv84T66A/dear-sharepoint-team-itemdeleted.html" title="Dear SharePoint Team: ItemDeleted? Seriously?" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/09/dear-sharepoint-team-itemdeleted.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4MSX8-eCp7ImA9WxNQEks.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-7613226388381624534</id><published>2009-09-18T09:36:00.007+02:00</published><updated>2009-09-18T11:09:48.150+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-18T11:09:48.150+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="Ajax" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><title>Filling a SharePoint web service gap: Fetching current username, using client-side only jQuery Ajax</title><content type="html">I'm stumped as for why no such service was included in the first place, seeing as the code is a one-liner, and I imagine it quite useful to most service clients.&lt;br /&gt;&lt;br /&gt;Having first settled on a custom service (code &lt;a target="_blank" href="http://pastebin.com/f7fa481ca"&gt;here&lt;/a&gt;) to deliver the user name, deploying that isn't always an option. If you've only got client-side access to the portal, you *need* to do it with a script..&lt;br /&gt;&lt;br /&gt;So I came up with this solution, which should work in most cases:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;window.userInfo = &lt;br /&gt;{&lt;br /&gt;    /* Public interface */&lt;br /&gt;    getCurrentUsername: function(callback)&lt;br /&gt;    {&lt;br /&gt;        $.get(&lt;br /&gt;            "/_layouts/userdisp.aspx?Force=True", &lt;br /&gt;            null, &lt;br /&gt;            function(data)&lt;br /&gt;            {&lt;br /&gt;                var username = window.userInfo._findUsernameFieldText(data);&lt;br /&gt;                callback(username);&lt;br /&gt;            }, "html");&lt;br /&gt;    },&lt;br /&gt;    &lt;br /&gt;    /* Private interface */&lt;br /&gt;    _findUsernameFieldText: function(pageData)&lt;br /&gt;    {&lt;br /&gt;        var fields = $(pageData).find("table.ms-formtable td#SPFieldText");&lt;br /&gt;        for(var i = 0; i &amp;lt; fields.length; ++i)&lt;br /&gt;        {&lt;br /&gt;            field = fields.get(i);&lt;br /&gt;            for(node = field.firstChild; &lt;br /&gt;                node.nextSibling != null; &lt;br /&gt;                node = node.nextSibling)&lt;br /&gt;            {&lt;br /&gt;                if(node.nodeType == 8 &amp;&amp; /FieldInternalName=\"UserName\"/.test(node.nodeValue))&lt;br /&gt;                    return $(field).text().replace(/(^[\s\xA0]+|[\s\xA0]+$)/g, '');&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What I realized, as I pondered this yesterday, is that there are obviously a lot of pages in the portal which lists the current user's info - including the user profile page. Extracting the username should be no harder than asynchronously loading the page (using jQuery) and parsing the resulting form looking for a field commented with 'FieldInternalName="UserName"'.&lt;br /&gt;&lt;br /&gt;Should the userdisp page be violently customized, this would be less likely to work, but being a _layouts page; such a customization is unlikely.&lt;br /&gt;&lt;br /&gt;Granted the above code, you can test it by pasting that (in a script tag) plus the following into the html source of a content editor web part:&lt;br /&gt;&lt;pre class="brush: html;"&gt;&lt;br /&gt;&amp;lt;script src="http://www.google.com/jsapi"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;    google.load("jquery", "1");&lt;br /&gt;    google.setOnLoadCallback(function() &lt;br /&gt;    {&lt;br /&gt;        userInfo.getCurrentUsername(function(usr) { alert("[" + usr + "]"); });&lt;br /&gt;    });&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a target="_blank" href="http://dl.getdropbox.com/u/1878671/getCurrentUser.txt"&gt;Full source code download here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-7613226388381624534?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/0LlB70Omk8c" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/7613226388381624534/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=7613226388381624534" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/7613226388381624534?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/7613226388381624534?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/0LlB70Omk8c/filling-sharepoint-web-service-gap.html" title="Filling a SharePoint web service gap: Fetching current username, using client-side only jQuery Ajax" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/09/filling-sharepoint-web-service-gap.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0AGSXg_fCp7ImA9WxNQE0g.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-3701327278163200901</id><published>2009-09-17T22:32:00.006+02:00</published><updated>2009-09-19T11:15:28.644+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-19T11:15:28.644+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="Ajax" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><title>Forcing SharePoint into asynchronous AJAX-like submission, using jQuery</title><content type="html">A while ago I posted a screencast on Twitter, of me navigating around a WSS 3.0 document library, without any synchronous reloads (full page refreshes): changing sort settings, creating folders and deleting files - all updated in place. If you didn't catch that, here's the screencast:&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a target="_blank" href="http://dl.getdropbox.com/u/1878671/asyncPoint.swf" rel="shadowbox;height=918;width=516"&gt;&lt;img src="http://dl.getdropbox.com/u/1878671/asyncPoint.jpg"/&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;The demo looks pretty cool (he said confidently), and I feel that these are features SharePoint is lacking. With SharePoint 2010, this will all have been improved majorly, so it's admittedly not the most useful feature to be adding now - a mere few months before the release of '10. For the sake of (mis)using JavaScript and jQuery, though - let's stride on!&lt;br /&gt;&lt;br /&gt;The code behind the demo is pretty intrusive - I'll be the first to admit that - and it's in no way complete. What it does, is override the usual SharePoint document library submit behavior, with a jQuery asynchronous load. The loaded data, which would be the page returned from the server, is chopped up and updated at the relevant spots on the existing page. If the user opens a folder in a document library, the asynch call loads the page with the view of the folder, then strips all the stuff which isn't the view of the folder, and replaces the current folder view: straight forward DOM stuff.&lt;br /&gt;&lt;br /&gt;For this hack to (kinda) work, I also override some standard SharePoint APIs which returns state information (e.g. which document library folder we're currently viewing). I spent a minimal amount of time completing this, so I have no doubt that some state info will be missing or wrong in the code below, but it does seem to work fairly well for folder navigation, creation and item deletion. Sorting, however, is another matter entirely.&lt;br /&gt;&lt;br /&gt;So all in all, there are quite a few kinks. The create folder popup is included mostly for show, and looks horrible. I didn't adapt this to work with the upload form, although that improvement would be pretty simple to complete. One of the more prominent issues, however, is the scripts which are returned along with the asynchronous loads. IE8 freaks out somewhat by these, and throws an error at you when you e.g. delete an item in a library. The fix is to ignore scripts in the parts of the page we keep (the view of the folder, etc.), but that's another thing I didn't look into.&lt;br /&gt;&lt;br /&gt;All in all, I consider this a curiosity, rather than something of actual value. I'm positive someone with more time on their hands than me could improve it a ton, but then again - why bother with SharePoint 2010 coming up :-)&lt;br /&gt;&lt;br /&gt;Excuses aside, here's the important part of the script:&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;var wssHook =&lt;br /&gt;{&lt;br /&gt;    /* Public interface */&lt;br /&gt;    &lt;br /&gt;    setupWSSHooks: function()&lt;br /&gt;    {&lt;br /&gt;        this.hookWSSApi("FilterFieldV3");&lt;br /&gt;        this.hookWSSApi("GetSource");&lt;br /&gt;        this.hookWSSApi("GetUrlKeyValue");&lt;br /&gt;        SubmitFormPost = this.hookedSubmitFormPost;&lt;br /&gt;        STSNavigate = this.hookedSTSNavigate;&lt;br /&gt;    },&lt;br /&gt;    &lt;br /&gt;    /* Private interface */&lt;br /&gt;    &lt;br /&gt;    hookWSSApi: function(api)&lt;br /&gt;    {&lt;br /&gt;        this[api] = window[api];&lt;br /&gt;        var body = window[api].toString();&lt;br /&gt;        eval("window[api]=function" + body.substring(body.indexOf("(")).replace("window.location.href", "window.realurl?window.realurl:window.location.href"));    &lt;br /&gt;    },&lt;br /&gt;    &lt;br /&gt;    hookedSubmitFormPost: function(url, forcesubmit, getonly)&lt;br /&gt;    {&lt;br /&gt;        window.realurl = url;&lt;br /&gt;        var postvars = {};&lt;br /&gt;        if(!getonly)&lt;br /&gt;        {&lt;br /&gt;            $(document.forms[MSOWebPartPageFormName]).find("input").each(function(id, element) { postvars[element.name] = element.value; });&lt;br /&gt;        }&lt;br /&gt;        else&lt;br /&gt;        {&lt;br /&gt;            postvars = "";&lt;br /&gt;        }&lt;br /&gt;        $("table#MSO_ContentTable&amp;gt;tbody").load(url + " table#MSO_ContentTable&amp;gt;tbody", postvars, &lt;br /&gt;            function(resp, status, req) &lt;br /&gt;            { &lt;br /&gt;                var location = req.getResponseHeader("Location");&lt;br /&gt;                if(location)&lt;br /&gt;                { window.realurl = location; }&lt;br /&gt;            });&lt;br /&gt;    },&lt;br /&gt;    &lt;br /&gt;    hookedSTSNavigate: function(url)&lt;br /&gt;    {&lt;br /&gt;        var shown = false;&lt;br /&gt;        var dialog = $("&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;");&lt;br /&gt;        dialog&lt;br /&gt;            .dialog({&lt;br /&gt;                    width: "80%", &lt;br /&gt;                    height: "300", &lt;br /&gt;                    modal: true,&lt;br /&gt;                    close: function(event, ui) { dialog.remove(); SubmitFormPost(window.location.href, false, true); }&lt;br /&gt;                })&lt;br /&gt;            .append(&lt;br /&gt;                    $("&amp;lt;iframe frameborder='0' width='100%' height='100%' src='" + url + "' /&amp;gt;")&lt;br /&gt;                        .load(function(){ &lt;br /&gt;                                if(shown) { dialog.dialog('close'); return; }&lt;br /&gt;                                shown = true;&lt;br /&gt;                                var c = $(this).contents();&lt;br /&gt;                                var ctrls = c.find("td.ms-bodyareaframe&amp;gt;table").clone();&lt;br /&gt;                                ctrls.find("script").remove();&lt;br /&gt;                                c.find("table#.ms-main:first").remove();&lt;br /&gt;                                c.find("form#aspnetForm").append(ctrls);&lt;br /&gt;                            })&lt;br /&gt;                );&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can download the entire thing, including jQuery / jQuery-UI loads from the Google CDN, &lt;a target="_blank" href="http://dl.getdropbox.com/u/1878671/asyncSharePoint.txt"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you want to give it a test run in your own portal; head to a document library, add a content editor web part and add the code downloaded from the link above as the html source.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-3701327278163200901?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/_A9uTlMh70o" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/3701327278163200901/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=3701327278163200901" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/3701327278163200901?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/3701327278163200901?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/_A9uTlMh70o/forcing-sharepoint-into-asynchronous.html" title="Forcing SharePoint into asynchronous AJAX-like submission, using jQuery" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/09/forcing-sharepoint-into-asynchronous.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QHRn8-cCp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-4142159416970601890</id><published>2009-09-17T13:19:00.006+02:00</published><updated>2009-09-17T14:08:57.158+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T14:08:57.158+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="tools" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><title>Dealing with abducted SharePoint features</title><content type="html">If you've ever gotten an error message when deploying a feature with stsadm or WSPBuilder, resembling the following:&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;The feature '...' uses the directory "NewFeatureName" in the solution. However, it is currently installed in the farm to the directory "OldFeatureName". Uninstall the existing feature before you install a new version of the solution.&lt;/i&gt;&lt;/blockquote&gt;... or you've loaded up SharePoint Manager 2007, checked the feature definitions, only to find one or more features without the green "ALL IS OK" icon, an error reading "&lt;i&gt;Object reference not set to an instance of an object&lt;/i&gt;" when you try to open them, and no option to delete.&lt;br /&gt;&lt;br /&gt;... or you've otherwise been slapped a message indicating that you've once had a feature installed somewhere, but although its files are now gone, SharePoint won't allow you to deploy a new one with the same guid.&lt;br /&gt;&lt;br /&gt;The solution is relatively simple, and can be automated with a lookup of the local farm's feature definitions, deleting those who throw an "not found" error when you attempt to access its properties. Kind as I am, I've already thrown this together for you, and made it available as a download &lt;a href="http://dl.getdropbox.com/u/1878671/Grep.SharePoint.Tools.CleanFeatures.zip"&gt;here (Grep.SharePoint.Tools.CleanFeatures)&lt;/a&gt;. The source code follows below:&lt;br /&gt;&lt;pre class="brush: c-sharp;"&gt;&lt;br /&gt;namespace Grep.SharePoint.Tools.CleanFeatures&lt;br /&gt;{&lt;br /&gt;    using System;&lt;br /&gt;    using System.Linq;&lt;br /&gt;    using Microsoft.SharePoint;&lt;br /&gt;    using Microsoft.SharePoint.Administration;&lt;br /&gt;&lt;br /&gt;    internal class Program&lt;br /&gt;    {&lt;br /&gt;        private static void Main(string[] args)&lt;br /&gt;        {&lt;br /&gt;            if (!args.Contains("/D"))&lt;br /&gt;            {&lt;br /&gt;                Console.Out.WriteLine("Listing missing features. Invoke with /D to delete as they are found.");&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;                Console.Out.WriteLine("Deleting missing features.");&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            SPFarm farm = SPFarm.Local;&lt;br /&gt;            foreach (SPFeatureDefinition feature in farm.FeatureDefinitions)&lt;br /&gt;            {&lt;br /&gt;                try&lt;br /&gt;                {&lt;br /&gt;                    SPFeaturePropertyCollection p = feature.Properties;&lt;br /&gt;                }&lt;br /&gt;                catch (SPException e)&lt;br /&gt;                {&lt;br /&gt;                    if (e.ErrorCode == -2146232832)&lt;br /&gt;                    {&lt;br /&gt;                        Console.Out.WriteLine("Not found: {0} ({1})",&lt;br /&gt;                                              feature.Id,&lt;br /&gt;                                              feature.RootDirectory.Substring(feature.RootDirectory.LastIndexOf(@"\") + 1));&lt;br /&gt;                        if (args.Contains("/D"))&lt;br /&gt;                        {&lt;br /&gt;                            feature.Delete();&lt;br /&gt;                        }&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            Console.Out.WriteLine("Done");&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-4142159416970601890?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/4AEHckW96rk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/4142159416970601890/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=4142159416970601890" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/4142159416970601890?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/4142159416970601890?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/4AEHckW96rk/dealing-with-abducted-sharepoint.html" title="Dealing with abducted SharePoint features" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/09/dealing-with-abducted-sharepoint.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QGSX4_eCp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-8943443592226297378</id><published>2009-09-17T10:23:00.017+02:00</published><updated>2009-09-17T14:08:48.040+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T14:08:48.040+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="tools" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><title>Find who's not mutually following you on Twitter, using jQuery Ajax / JSON</title><content type="html">Admittedly not the most useful use of the Twitter API, nor of jQuery, but nevertheless here it is. &lt;br /&gt;&lt;br /&gt;Two different APIs are used; one to check the statuses of all friends of user X, which supplies us with 1. friends, 2. screen names and 3. user ids. The second API retrieves all user ids of the user X's followers. The two resulting lists of these APIs are diffed against each other, and voila - you've got the illoyal ones.&lt;br /&gt;&lt;br /&gt;No username or password required to do this (unlike some other services I've seen), as long as your profile isn't protected (in which case you can easily modify it to supply a user / pass). It's completely harmless to run, as long as you don't hammer the Twitter server. The code will spend one API call (out of your total 150 per hour) per 100 friends of the user you target, and another single call to fetch all the followers. If you have more than 10k friends: don't run this.&lt;br /&gt;&lt;br /&gt;Here's a fully functional demo. Feel free to try it on your own screen name. Again, no password or cross site scripting takes part here, so it's completely harmless. Input a screen name in the input box, and click the "check" button.&lt;br /&gt;&lt;br /&gt;&lt;script type="text/javascript" src="http://dl.getdropbox.com/u/1878671/blogTwitterCheck.js"&gt;&lt;/script&gt;&lt;div id="friendChecker" style="display: none"&gt;Username:&lt;input type="text" id="targetScreenName" style="width:130px"/&gt;&lt;input type="button" value="Check" onclick="javascript:FollowerChecker.start()" style="width:70px"/&gt;&lt;div id="nonFollowersLoader" style="background-image: url(http://dl.getdropbox.com/u/1878671/loading.gif); background-position: center center; background-repeat: no-repeat; width: 275px; height: 200px; position: absolute; z-index: 5000;"&gt;&lt;/div&gt;&lt;br /&gt;        &lt;div id="nonFollowers" style="background: #AAAAAA; border: 1px solid #777777; width: 275px; height: 200px; overflow-y: scroll;"&gt;&lt;br /&gt;        &lt;/div&gt;&lt;br /&gt;    &lt;/div&gt;If you want to toy around with the code, it can be found &lt;a target="_blank" href="http://dl.getdropbox.com/u/1878671/test.html.txt"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-8943443592226297378?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/rjENK8u-aVI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/8943443592226297378/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=8943443592226297378" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/8943443592226297378?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/8943443592226297378?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/rjENK8u-aVI/find-whos-not-mutually-following-you-on.html" title="Find who's not mutually following you on Twitter, using jQuery Ajax / JSON" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/09/find-whos-not-mutually-following-you-on.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYAQng6fip7ImA9WxNQEE4.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-2431577668640554895</id><published>2009-09-10T12:50:00.009+02:00</published><updated>2009-09-15T19:35:43.616+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-15T19:35:43.616+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="ramblings" /><category scheme="http://www.blogger.com/atom/ns#" term="worst practices" /><title>SharePoint logger bug workaround</title><content type="html">So yesterday I blogged about a bug in the SharePoint tracelog code, which would result in a ridiculous and unnecessary exception, should you happen to create a site collection, with feature file deployment, from a WCF service. If you haven't read that - feel free to do that now: &lt;a href="http://einaros.blogspot.com/2009/09/tis-sharepoint-bug-it-is.html"&gt;'Tis a SharePoint bug, it is!&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Anyhoo, after giving up on having it fixed by the powers that be anytime soon, and not wanting to be too intrusive (ie. patching standard code, or injecting a hotfix at runtime), I went with a much more ... logical ... solution.&lt;br /&gt;&lt;br /&gt;Allow me to demonstrate.&lt;br /&gt;&lt;br /&gt;While this approach from yesterday didn't work:&lt;br /&gt;&lt;a target="_blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/Sqe4wt2mSmI/AAAAAAAAEH0/5eLyFjtChdE/s1600-h/2009-09-09_1603.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 79px;" src="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/Sqe4wt2mSmI/AAAAAAAAEH0/5eLyFjtChdE/s320/2009-09-09_1603.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5379471426999175778" /&gt;&lt;/a&gt;&lt;div style="text-align: center; font-size: 9pt"&gt;Click it to view larger version.&lt;/div&gt;&lt;br /&gt;This most certainly does:&lt;br /&gt;&lt;a target="_blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SqjbH5QxWlI/AAAAAAAAEH8/3ZOyD1EdYQs/s1600-h/2009-09-10_1249.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 62px;" src="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SqjbH5QxWlI/AAAAAAAAEH8/3ZOyD1EdYQs/s320/2009-09-10_1249.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5379790683570788946" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In layman's terms: In the WCF service I now - rather than create the site coll directly - 1. Create a new thread, which creates the site coll, 2. Wait for the thread to end, and 3. Return. Makes perfect sense (actually it does). The stack rewind done in the trace api will stop at the entry into the worker thread, and thus never get to the DynamicMethod in WCF, which breaks it all apart.&lt;br /&gt;&lt;br /&gt;Time for coffee.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-2431577668640554895?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/33AKCWqTwUQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/2431577668640554895/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=2431577668640554895" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/2431577668640554895?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/2431577668640554895?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/33AKCWqTwUQ/sharepoint-bug-that-left-us-all-alone.html" title="SharePoint logger bug workaround" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/Sqe4wt2mSmI/AAAAAAAAEH0/5eLyFjtChdE/s72-c/2009-09-09_1603.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/09/sharepoint-bug-that-left-us-all-alone.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUcDRXw5eip7ImA9WxNRFUo.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-6285121582605769697</id><published>2009-09-10T09:42:00.005+02:00</published><updated>2009-09-10T10:57:54.222+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-10T10:57:54.222+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="work" /><title>SharePoint Conference 2009, I'm coming for you!</title><content type="html">Last year it was hosted in Seattle. I had a great time going there, and did catch some interesting presentations, but can't say that the technical content brought me to bits in awe. This year, with the community and contributors having matured for even longer, I'm thinking it'll be a blast. With the upcoming release of SP2010, and the posibility of presentations on that as well, I really don't see how it can fail. If worst comes to worst, there's always poker and booze, right? Or juice and pancakes; whatever suits your fancy.&lt;br /&gt;&lt;br /&gt;Should *you* find your way to Vegas at the same time (18th of October =&gt;), feel free to slap me a tweet/mail, so we can formally exchange our hellos!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-6285121582605769697?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/e4SkiWvEcH8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/6285121582605769697/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=6285121582605769697" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6285121582605769697?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6285121582605769697?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/e4SkiWvEcH8/sharepoint-conference-2009-im-coming.html" title="SharePoint Conference 2009, I'm coming for you!" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/09/sharepoint-conference-2009-im-coming.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkEHR3o5eSp7ImA9WxNXEUk.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-7063312931069938130</id><published>2009-09-09T14:32:00.019+02:00</published><updated>2009-09-28T16:37:16.421+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-28T16:37:16.421+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="ramblings" /><category scheme="http://www.blogger.com/atom/ns#" term="worst practices" /><title>'Tis a SharePoint bug, it is!</title><content type="html">I ran into this a few weeks ago, but had to delay dealing with it, for the sake of [somegoodreason]. Now I'm back on track, and I'm obviously going to whine about it to you.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;The background for this problem is simple:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I create a site collection from a site definition, using code. This sitedef contains a couple of aspx files, which are put on the base of the new site's root web. To put the files there, I have Module tags in a feature's elements xml. The module tag contains a Name attribute, which is required, and used by SharePoint to look up the library it is to put the files in.&lt;br /&gt;&lt;br /&gt;The thing is, though - ghostable files on the root of a web don't go in a library. So as SharePoint prepares the site, it fails to locate a non-existing library (which it wouldn't use anyway), and takes note of the error, but then carries on, as the library *really* wasn't required (told you).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Here's me explaining that by use of poor penmanship and malplaced creativity:&lt;/span&gt;&lt;br /&gt;&lt;a target="_blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/Sqe4GzpBamI/AAAAAAAAEHs/a5Ve5tyxVOo/s1600-h/SP1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px;" src="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/Sqe4GzpBamI/AAAAAAAAEHs/a5Ve5tyxVOo/s320/SP1.png" alt="" id="BLOGGER_PHOTO_ID_5379470706998340194" border="0" /&gt;&lt;/a&gt;&lt;div style="text-align: center; font-size: 9pt;"&gt;Click it to view larger version.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So while that scenario works out ok, what I'm actually doing, is calling the create-sitecollection code via a WCF service. And that plays out as follows (gore included):&lt;br /&gt;&lt;a target="_blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/Sqe4wt2mSmI/AAAAAAAAEH0/5eLyFjtChdE/s1600-h/2009-09-09_1603.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 79px;" src="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/Sqe4wt2mSmI/AAAAAAAAEH0/5eLyFjtChdE/s320/2009-09-09_1603.png" alt="" id="BLOGGER_PHOTO_ID_5379471426999175778" border="0" /&gt;&lt;/a&gt;&lt;div style="text-align: center; font-size: 9pt;"&gt;Click it to view larger version.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The problem is rooted in SharePoint's logging code, which includes a routine to step back through the call stack, taking note of the where the failed call originated from. In this case, that would pass through the calls made by SharePoint to look up the library, deploy the file, open the elements, [...] and eventually into the service - which is internally in WCF an invoked DynamicMethod.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;SharePoint's logger essentially does this:&lt;/span&gt;&lt;br /&gt;&lt;pre class="brush: c-sharp;"&gt;StackFrame frame = trace.GetFrame(i);&lt;br /&gt;builder.Append(frame.GetMethod().DeclaringType.ToString());&lt;br /&gt;&lt;/pre&gt;And that doesn't sit well with the DynamicMethod, which isn't really your run of the mill method (it's a dynamically compiled at runtime), and sure as batman doesn't have a DeclaringType. Calling the ToString() on a null reference ... not good at all.&lt;br /&gt;&lt;br /&gt;Thus, SharePoint logs itself to bits and pieces, and I'm left none the wiser at the other end of the line.&lt;br /&gt;&lt;br /&gt;So the moral of the story is:&lt;ol&gt;&lt;li&gt;&lt;a href="http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake"&gt;Null references are bad&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Assuming that all methods are declared somewhere, in a language which supports emitting and compiling code at runtime - also bad&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Logging events that aren't actually errors, and which no part of the application cares about either way, &lt;span style="font-style: italic;"&gt;even when logging is turned off&lt;/span&gt; - well you'll figure that one out yourself.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;Update: I made a new post (and obviously an illustration) on a workaround: &lt;a href="http://einaros.blogspot.com/2009/09/sharepoint-bug-that-left-us-all-alone.html"&gt;here&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-7063312931069938130?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/KhOc8QEumVU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/7063312931069938130/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=7063312931069938130" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/7063312931069938130?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/7063312931069938130?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/KhOc8QEumVU/tis-sharepoint-bug-it-is.html" title="'Tis a SharePoint bug, it is!" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/Sqe4GzpBamI/AAAAAAAAEHs/a5Ve5tyxVOo/s72-c/SP1.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/09/tis-sharepoint-bug-it-is.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UNSH05cCp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-8676810176344934585</id><published>2009-08-10T02:25:00.016+02:00</published><updated>2009-09-17T14:08:19.328+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T14:08:19.328+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="Ajax" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><title>SharePoint hierarchical Quick Launch replacement using jQuery ajax</title><content type="html">(&lt;a href="http://bit.ly/B66MX" target="_blank"&gt;See here&lt;/a&gt; for a screencast of the in-development version)&lt;br /&gt;(Solution (wsp) download available &lt;a href="http://indev.no/Grep.SharePoint.Navigation.wsp"&gt;here&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;The navigation control features (among other things):&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Collapsible tree-view of the entire site collection&lt;br /&gt;&lt;li&gt;Lazy-loaded (jQuery ajax + json) sub-site structure&lt;br /&gt;&lt;li&gt;Node title editing, hiding and reordering (also ajax)&lt;br /&gt;&lt;li&gt;Customizable UI&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Unlike many other ajax based controls, this doesn't post-back to the page itself, but rather to a standalone web service which is deployed with the feature. Consequently, this control will *not* cause a heap of server-side overhead (instantiation of many unnecessary controls, and so forth) caused by the slight horror that is e.g. plain ASP.NET Ajax.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/SoA2xNSuf7I/AAAAAAAAEHk/xKei8o63Qr0/s1600-h/1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 189px;" src="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/SoA2xNSuf7I/AAAAAAAAEHk/xKei8o63Qr0/s320/1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368350974835326898" /&gt;&lt;/a&gt;&lt;br /&gt;The resulting Grep.SharePoint.Navigation control solution can be &lt;a href="http://indev.no/Grep.SharePoint.Navigation.wsp"&gt;downloaded here&lt;/a&gt;, and taken for a test drive. At some point, I'll make a CodePlex site for it.&lt;br /&gt;&lt;br /&gt;The setup is pretty easy. You need to activate two features, and the control will automatically replace the default quick launch navigation.&lt;br /&gt;&lt;br /&gt;Once you've installed the solution, and deployed it to a site, head over to the web application features:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/Sn9p-BnDqJI/AAAAAAAAEG0/rCnseyaBhsw/s1600-h/2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 294px;" src="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/Sn9p-BnDqJI/AAAAAAAAEG0/rCnseyaBhsw/s320/2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368125795153979538" /&gt;&lt;/a&gt;&lt;br /&gt;Make sure you select the right web application (use the drop-down in the top-right corner of the feature page to change this), and not e.g. your central admin web app. Nothing will be harmed if you happen to activate it for the wrong application - the navigation control simply won't work.&lt;br /&gt;&lt;br /&gt;Once you've selected the correct web app, enable the extension feature:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/Sn9qJd6wOlI/AAAAAAAAEG8/P0wgzN4ehws/s1600-h/3.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 30px;" src="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/Sn9qJd6wOlI/AAAAAAAAEG8/P0wgzN4ehws/s320/3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368125991731346002" /&gt;&lt;/a&gt;&lt;br /&gt;Next, head over to the site you want to test it on, navigate to the site features (not site collection features), and enable the Navigation injection feature:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/Sn9qjJryk-I/AAAAAAAAEHE/_V4k-HKxHdw/s1600-h/4.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 24px;" src="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/Sn9qjJryk-I/AAAAAAAAEHE/_V4k-HKxHdw/s320/4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368126432976475106" /&gt;&lt;/a&gt;&lt;br /&gt;You can undo the navigation replacement at any time, by deactivating this very feature.&lt;br /&gt;&lt;br /&gt;To edit the navigation, you can - as a site collection administrator - press the link above the navigation control, seen here:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SoAwkYjEpPI/AAAAAAAAEHU/jQgTcdNXxi4/s1600-h/5.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 267px; height: 227px;" src="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SoAwkYjEpPI/AAAAAAAAEHU/jQgTcdNXxi4/s320/5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368344157448611058" /&gt;&lt;/a&gt;When node editing is activated, unchecking the check box will hide the node from display, changing the name will rename the node (but not actually rename the underlying web / list). To restore default settings for the node, press the arrow button next to it.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SoAwkhqXF3I/AAAAAAAAEHc/e-fgiiHt4Tg/s1600-h/6.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 271px; height: 254px;" src="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SoAwkhqXF3I/AAAAAAAAEHc/e-fgiiHt4Tg/s320/6.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5368344159895099250" /&gt;&lt;/a&gt;&lt;br /&gt;Also, a style sheet is shipped along with the control, in your 12\TEMPLATE\LAYOUTS\Grep.SharePoint.Navigation folder - feel free to play around with that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-8676810176344934585?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/xF4SOd5hhyA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/8676810176344934585/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=8676810176344934585" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/8676810176344934585?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/8676810176344934585?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/xF4SOd5hhyA/hierarchical-ajax-navigation-solution.html" title="SharePoint hierarchical Quick Launch replacement using jQuery ajax" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/SoA2xNSuf7I/AAAAAAAAEHk/xKei8o63Qr0/s72-c/1.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/08/hierarchical-ajax-navigation-solution.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QBQngyfip7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-6870258694801019320</id><published>2009-07-31T15:08:00.004+02:00</published><updated>2009-09-17T14:09:13.696+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T14:09:13.696+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ramblings" /><title>Don't take it from me .. So here's a book</title><content type="html">I've read a few SharePoint books, and I've poked through even more. Most of them, it should be no surprise, aren't really of much worth to the most dedicated of us. All too many books - and I find this especially so for SharePoint - seem to be mere paraphrases of what's already available on MSDN; hardly worth paying hard earned pigeons for.&lt;br /&gt;&lt;br /&gt;A couple of months ago, after a pretty hefty pause from all technical offline references, I spent some time reading &lt;span style="font-weight:bold;"&gt;Building the SharePoint User Experience&lt;/span&gt;. Originally thinking this was going to be another "here's a list - it can hold stuff, but don't ask me how" kind of book, I was sort of a skeptic. What I found, however, was that it actually went pretty deep in subjects I had been pondering myself, not to mention all the stuff I had no idea about. &lt;br /&gt;&lt;br /&gt;The best part, though, is the fact that it dares point out all the horrid, horrid stuff you're bound to encounter with SharePoint. The stuff that many others will happily ignore, so as not to displease anyone. Originally being a C++ dev, I'm used to stuff being awkward, ugly and downright painful - so I want to be told, rather than stumbling ass backwards into every trap there is. In short, this book saved my behind a few times in a library I was deploying at the time, so I feel I'm doing you a favor by giving you a heads up here.&lt;br /&gt;&lt;br /&gt;The author (Bjørn Furuknap) also maintains a very helpful blog, at &lt;a href="http://furuknap.blogspot.com/"&gt;furuknap.blogspot.com&lt;/a&gt;, so you should at least give that a go.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-6870258694801019320?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/OjvsmvDv0g8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/6870258694801019320/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=6870258694801019320" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6870258694801019320?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6870258694801019320?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/OjvsmvDv0g8/dont-take-it-from-me-so-heres-book.html" title="Don't take it from me .. So here's a book" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/07/dont-take-it-from-me-so-heres-book.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkEDRn0_eCp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-1717869757453435213</id><published>2009-06-11T22:48:00.017+02:00</published><updated>2009-09-17T13:57:57.340+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T13:57:57.340+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="Bing" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><title>Hovering Bing Search using jQuery</title><content type="html">Taking the Bing API for another test drive, I wrote up a jQuery / JSON Ajax driven website tool as well. The following little thing will show a semi-transparent hovering box as you select text on a web site. If you click the box, a Bing search will be made, and the top 10 results will be shown in the popup.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/SjGPAsLIAsI/AAAAAAAAEGQ/fHk8X-4Xf8s/s1600-h/cap4.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 376px; height: 97px;" src="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/SjGPAsLIAsI/AAAAAAAAEGQ/fHk8X-4Xf8s/s400/cap4.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5346211474686083778" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/SjGPG34W7vI/AAAAAAAAEGY/1d5Vy0J3vSM/s1600-h/cap5.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 177px;" src="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/SjGPG34W7vI/AAAAAAAAEGY/1d5Vy0J3vSM/s400/cap5.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5346211580907810546" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Feel free to give it a go at &lt;a href="http://indev.no/hoverbing.html" target="_blank"&gt;the example page&lt;/a&gt;. The initial JavaScript is shown in full shortly, but for updated source code and examples, I suggest you head over to the &lt;a href="http://hoverbing.codeplex.com" target="_blank"&gt;HoverBing CodePlex project&lt;/a&gt; I just created.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;(function()&lt;br /&gt;{&lt;br /&gt;    getSelectedText = function()&lt;br /&gt;    {&lt;br /&gt;        if(window.getSelection){&lt;br /&gt;            return window.getSelection().toString();&lt;br /&gt;        }&lt;br /&gt;        else if(document.getSelection){&lt;br /&gt;            return document.getSelection();&lt;br /&gt;        }&lt;br /&gt;        else if(document.selection){&lt;br /&gt;            return document.selection.createRange().text;&lt;br /&gt;        }&lt;br /&gt;    }   &lt;br /&gt;&lt;br /&gt;    alternate = function(counter, norm, alt)&lt;br /&gt;    {&lt;br /&gt;        return counter % 2 == 0 ? alt : norm;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    createHoverBingObject = function(settings) &lt;br /&gt;    {&lt;br /&gt;        HoverBingObject = &lt;br /&gt;        {&lt;br /&gt;            // Settings&lt;br /&gt;            appId: settings.appId,&lt;br /&gt;            numResults: settings.numResults,&lt;br /&gt;            sources: settings.sources,&lt;br /&gt;            title: settings.title,&lt;br /&gt;            &lt;br /&gt;            activateSearchPopup: function(x, y)&lt;br /&gt;            {&lt;br /&gt;                var popup = $("#SearchPopup");&lt;br /&gt;                if(x &amp;&amp; y)&lt;br /&gt;                {&lt;br /&gt;                    popup&lt;br /&gt;                        .css({&lt;br /&gt;                            cursor: "pointer",&lt;br /&gt;                            left: x + 5,&lt;br /&gt;                            top: y + 5&lt;br /&gt;                        })&lt;br /&gt;                        .show();&lt;br /&gt;                    HoverBingObject.repositionToFitScreen();&lt;br /&gt;                    HoverBingObject.fadePopupByDistance(x, y);&lt;br /&gt;                }&lt;br /&gt;                popup.data("SearchPopup_State", "waiting");&lt;br /&gt;                $().bind('mousemove', HoverBingObject.mouseMoveSearchPopup);&lt;br /&gt;            },&lt;br /&gt;&lt;br /&gt;            create: function() &lt;br /&gt;            {&lt;br /&gt;                HoverBingObject.initSearchPopup();&lt;br /&gt;                $().mouseup(function(e) {&lt;br /&gt;                        var selText = getSelectedText();&lt;br /&gt;                        var popup = $("#SearchPopup");&lt;br /&gt;                        var state = popup.data("SearchPopup_State");&lt;br /&gt;                        if((state == undefined || state == "hidden") &amp;&amp; &lt;br /&gt;                           selText.length &amp;gt; 0)&lt;br /&gt;                        {&lt;br /&gt;                            popup.data("SearchPopup_Query", HoverBingObject.washQueryText(selText));&lt;br /&gt;                            HoverBingObject.activateSearchPopup(e.pageX, e.pageY);&lt;br /&gt;                        }&lt;br /&gt;                        else if(state != "stapled")&lt;br /&gt;                        {&lt;br /&gt;                            HoverBingObject.deactivateSearchPopup();&lt;br /&gt;                        }&lt;br /&gt;                    });&lt;br /&gt;                $().mousedown(function(e) {&lt;br /&gt;                        var popup = $("#SearchPopup");&lt;br /&gt;                        var state = popup.data("SearchPopup_State");&lt;br /&gt;                        if(state != undefined &amp;&amp; state == "waiting")&lt;br /&gt;                        {&lt;br /&gt;                            HoverBingObject.deactivateSearchPopup();&lt;br /&gt;                        }&lt;br /&gt;                    });&lt;br /&gt;            },&lt;br /&gt;&lt;br /&gt;            deactivateSearchPopup: function()&lt;br /&gt;            {&lt;br /&gt;                var popup = $("#SearchPopup");&lt;br /&gt;                popup.find("span").empty();&lt;br /&gt;                popup&lt;br /&gt;                    .fadeTo(0, 0)&lt;br /&gt;                    .hide()&lt;br /&gt;                    .data("SearchPopup_State", "hidden");&lt;br /&gt;                $().unbind('mousemove', HoverBingObject.mouseMoveSearchPopup);&lt;br /&gt;            },&lt;br /&gt;&lt;br /&gt;            fadePopupByDistance: function(mouseX, mouseY)&lt;br /&gt;            {&lt;br /&gt;                var popup = $("#SearchPopup");&lt;br /&gt;                var pos = popup.position();&lt;br /&gt;                pos.left += popup.width() * 0.5;&lt;br /&gt;                pos.top += popup.height() * 0.5;&lt;br /&gt;                var dist = Math.round(Math.sqrt(Math.pow(mouseX - pos.left, 2) + Math.pow(mouseY - pos.top, 2)));&lt;br /&gt;                popup.fadeTo(0, Math.max(1 - dist / 500, 0));&lt;br /&gt;            },&lt;br /&gt;            &lt;br /&gt;            initSearchPopup: function()&lt;br /&gt;            {&lt;br /&gt;                // Create box&lt;br /&gt;                var box = document.createElement("div");&lt;br /&gt;                $(box)&lt;br /&gt;                    .attr("id", "SearchPopup")&lt;br /&gt;                    .attr("class", "HoverBing")&lt;br /&gt;                    .css({&lt;br /&gt;                            position: "absolute",&lt;br /&gt;                            opacity: 0&lt;br /&gt;                        })&lt;br /&gt;                    .mouseenter(function() {&lt;br /&gt;                            var state = $("#SearchPopup").data("SearchPopup_State");&lt;br /&gt;                            if(state == "waiting") &lt;br /&gt;                            {&lt;br /&gt;                                HoverBingObject.stapleSearchPopup();&lt;br /&gt;                            }&lt;br /&gt;                        })&lt;br /&gt;                    .mouseleave(function() {&lt;br /&gt;                            var popup = $("#SearchPopup");&lt;br /&gt;                            var state = popup.data("SearchPopup_State");&lt;br /&gt;                            if(state == "stapled") &lt;br /&gt;                            {&lt;br /&gt;                                HoverBingObject.activateSearchPopup();&lt;br /&gt;                            }&lt;br /&gt;                        })&lt;br /&gt;                    .data("SearchPopup_State", "hidden");&lt;br /&gt;                &lt;br /&gt;                // Create header control&lt;br /&gt;                var header = document.createElement("div");&lt;br /&gt;                $(header)&lt;br /&gt;                    .attr("class", "Header")&lt;br /&gt;                    .html(HoverBingObject.title);&lt;br /&gt;&lt;br /&gt;                // Create content control&lt;br /&gt;                var content = document.createElement("span");&lt;br /&gt;                $(content)&lt;br /&gt;                    .attr("class", "Content")&lt;br /&gt;                &lt;br /&gt;                // Append header and content to outer box&lt;br /&gt;                $(box)&lt;br /&gt;                    .append(header)&lt;br /&gt;                    .append(content);&lt;br /&gt;                    &lt;br /&gt;                // Add outer box to body&lt;br /&gt;                $(document.body).append(box);&lt;br /&gt;            },&lt;br /&gt;&lt;br /&gt;            mouseMoveSearchPopup: function(e)&lt;br /&gt;            {&lt;br /&gt;                HoverBingObject.fadePopupByDistance(e.pageX, e.pageY);&lt;br /&gt;            },&lt;br /&gt;&lt;br /&gt;            onClickSearchPopup: function()&lt;br /&gt;            {&lt;br /&gt;                var popup = $("#SearchPopup");&lt;br /&gt;                var query = popup.data("SearchPopup_Query");&lt;br /&gt;                var contents = popup.find("span").empty();&lt;br /&gt;                $.getJSON("http://api.search.live.net/json.aspx" + &lt;br /&gt;                            "?AppId=" + HoverBingObject.appId + &lt;br /&gt;                            "&amp;Market=en-US&amp;Query=" + query + &lt;br /&gt;                            "&amp;Sources=" + HoverBingObject.sources +&lt;br /&gt;                            "&amp;Web.Count=" + HoverBingObject.numResults + &lt;br /&gt;                            "&amp;JsonType=callback&amp;JsonCallback=?", &lt;br /&gt;                            HoverBingObject.onSearchResultsReceived);&lt;br /&gt;            },&lt;br /&gt;&lt;br /&gt;            onSearchResultsReceived: function(data)&lt;br /&gt;            {&lt;br /&gt;                var popup = $("#SearchPopup");&lt;br /&gt;                var contents = popup.find("span").empty();&lt;br /&gt;&lt;br /&gt;                if(data.SearchResponse == null) &lt;br /&gt;                {&lt;br /&gt;                    contents.html("No search results returned");&lt;br /&gt;                    return;&lt;br /&gt;                }&lt;br /&gt;                &lt;br /&gt;                $.each(data.SearchResponse.Web.Results, function(i ,item) {&lt;br /&gt;                        contents.append(&lt;br /&gt;                                $(document.createElement("a"))&lt;br /&gt;                                    .attr("class", alternate(i, "ResultLine", "ResultLineAlt"))&lt;br /&gt;                                    .css({&lt;br /&gt;                                            display: 'block'&lt;br /&gt;                                        })&lt;br /&gt;                                    .click(function() {&lt;br /&gt;                                        HoverBingObject.deactivateSearchPopup();&lt;br /&gt;                                    })&lt;br /&gt;                                    .mouseenter(function() {&lt;br /&gt;                                            $(this).addClass("ResultLineHover");&lt;br /&gt;                                        })&lt;br /&gt;                                    .mouseleave(function() {&lt;br /&gt;                                            $(this).removeClass("ResultLineHover");&lt;br /&gt;                                        })&lt;br /&gt;                                    .text(item.Title)&lt;br /&gt;                                    .attr("href", item.Url)&lt;br /&gt;                                    .attr("target", "_blank")&lt;br /&gt;                            );&lt;br /&gt;                    });&lt;br /&gt;                    &lt;br /&gt;                HoverBingObject.repositionToFitScreen();&lt;br /&gt;            },&lt;br /&gt;            &lt;br /&gt;            repositionToFitScreen: function()&lt;br /&gt;            {&lt;br /&gt;                var popup = $("#SearchPopup");&lt;br /&gt;                var pos = popup.position();&lt;br /&gt;                if(pos.left + popup.width() &amp;gt; $(window).width())&lt;br /&gt;                {&lt;br /&gt;                    popup.css({left: $(window).width() - popup.width()});&lt;br /&gt;                }&lt;br /&gt;                if(pos.top + popup.height() &amp;gt; $(window).height())&lt;br /&gt;                {&lt;br /&gt;                    popup.css({top: $(window).height() - popup.height()});&lt;br /&gt;                }&lt;br /&gt;            },&lt;br /&gt;&lt;br /&gt;            stapleSearchPopup: function()&lt;br /&gt;            {&lt;br /&gt;                $("#SearchPopup")&lt;br /&gt;                    .data("SearchPopup_State", "stapled")&lt;br /&gt;                    .fadeTo(0, 1)&lt;br /&gt;                    .one('click', HoverBingObject.onClickSearchPopup);&lt;br /&gt;                $().unbind('mousemove', HoverBingObject.mouseMoveSearchPopup);&lt;br /&gt;            },&lt;br /&gt;            &lt;br /&gt;            washQueryText: function(text)&lt;br /&gt;            {&lt;br /&gt;                return escape(text.replace(/[^'"A-z0-9]/g, "+")).substr(0, 100);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    window.HoverBing = function(options)&lt;br /&gt;    {&lt;br /&gt;        settings = jQuery.extend({&lt;br /&gt;                title: "Click to Bing Search",&lt;br /&gt;                appId: "96AE4D816B34AA03F44EEBC53F4C23F9A146C011",&lt;br /&gt;                numResults: 10,&lt;br /&gt;                sources: "web"&lt;br /&gt;            }, options);&lt;br /&gt;&lt;br /&gt;        if(typeof(HoverBingObject) == 'undefined')&lt;br /&gt;        {&lt;br /&gt;            createHoverBingObject(settings);&lt;br /&gt;            HoverBingObject.create();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;})();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And there you have it. To use it, link the jQuery-1.3.2 javascript, include the above JavaScript, and the CSS styles found on the CodePlex site linked from the top, then activate it by using a jQuery ready handler such as:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;$(function() { HoverBing(); });&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-1717869757453435213?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/4W-cxiQh0R0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/1717869757453435213/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=1717869757453435213" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/1717869757453435213?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/1717869757453435213?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/4W-cxiQh0R0/hovering-bing-search-using-jquery.html" title="Hovering Bing Search using jQuery" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/SjGPAsLIAsI/AAAAAAAAEGQ/fHk8X-4Xf8s/s72-c/cap4.PNG" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/06/hovering-bing-search-using-jquery.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkEFRX47fSp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-6876148018752288316</id><published>2009-06-11T15:41:00.011+02:00</published><updated>2009-09-17T13:56:54.005+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T13:56:54.005+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Bing" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><category scheme="http://www.blogger.com/atom/ns#" term="Silverlight" /><title>Writing a Silverlight powered Bing news web part for SharePoint</title><content type="html">Microsoft recently unveiled their Bing search engine, and swiftly followed up by exposing an API free of request count limits. Grabbing that opportunity to write a really simple news fetcher web part for SharePoint, I set out to attach a Silverlight client to the Bing API, using Windows Communication Foundation (WCF).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Getting started with Bing&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Essentially all you need to use Bing in your .NET apps, is an API id, which is available from &lt;a href="http://www.bing.com/developers" target="_blank"&gt;the Bing developer site&lt;/a&gt;. Specifically, you can create an app id &lt;a href="http://www.bing.com/developers/createapp.aspx" target="_blank"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The developer site also hosts a bunch of other resources, such as how to get started with other clients (using JSON or XML and so forth), so if you're interested - check that out.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Setting up your client&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I chose to write a client in Silverlight, for a bunch of reasons, really. First of all I like how easy it is to wrap Silverlight apps together in Visual Studio, and second, it's really simple to make them shine using Blend. So while I could use jQuery, or just about any other technology, Silverlight pulled me in solely on how easy it is to get something working.&lt;br /&gt;&lt;br /&gt;If you wish to test the Bing api from a console app, after creating your app id, you can do so by:&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Create a new console project in Visual Studio.&lt;br /&gt;&lt;li&gt;Add a service reference to the Bing web services (http://api.search.live.net/search.wsdl?AppID=&lt;i&gt;YourAppIdHere&lt;/i&gt;).&lt;br /&gt;&lt;li&gt;Name the service namespace BingService, for simplicity.&lt;br /&gt;&lt;li&gt;Slap the following code in there (be sure to replace the app id there as well):&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;pre class="brush: c-sharp;"&gt;&lt;br /&gt;namespace TestApp&lt;br /&gt;{&lt;br /&gt;    using System;&lt;br /&gt;    using BingService;&lt;br /&gt;&lt;br /&gt;    internal class Program&lt;br /&gt;    {&lt;br /&gt;        private static void Main(string[] args)&lt;br /&gt;        {&lt;br /&gt;            var bingClient = new LiveSearchPortTypeClient();&lt;br /&gt;            SearchResponse response = bingClient.Search(new SearchRequest&lt;br /&gt;                                                            {&lt;br /&gt;                                                                AppId = "YOUR APP ID HERE",&lt;br /&gt;                                                                Query = "einaros",&lt;br /&gt;                                                                Sources = new[] {SourceType.Web}&lt;br /&gt;                                                            });&lt;br /&gt;            foreach (WebResult result in response.Web.Results)&lt;br /&gt;            {&lt;br /&gt;                Console.Out.WriteLine(result.Title + ": " + result.Url);&lt;br /&gt;            }&lt;br /&gt;            Console.WriteLine("Done");&lt;br /&gt;            Console.ReadKey();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Running that should give you a bunch of url's you'd never wish to browse to.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Going all Silverlighty&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Now that you've got the basics going, it's time to pull out your Silverlight. If you haven't installed the developer sdk / runtime and such, now would be a great time to do so. Head over to the &lt;a href="http://silverlight.net/GetStarted/" target="_blank"&gt;Silverlight developer site&lt;/a&gt; for instructions on how.&lt;br /&gt;&lt;br /&gt;With the environment setup, create a new Silverlight application in Visual Studio. Be sure to answer "yes" when you're asked whether to create a new test project as well. You'll need this to access the web services.&lt;br /&gt;&lt;br /&gt;If you named your project anything like BingSilverNews, you should now see two projects; BingSilverNews and BingSilverNews.Web. The latter you won't have to touch at all, so feel free to collapse that.&lt;br /&gt;&lt;br /&gt;The main project, however, should get the same service reference as previously added.&lt;br /&gt;&lt;center&gt;&lt;a target="_blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SjESTOSoLWI/AAAAAAAAEFw/5d8ikHKW1YQ/s1600-h/cap1.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 273px;" src="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SjESTOSoLWI/AAAAAAAAEFw/5d8ikHKW1YQ/s400/cap1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5346074354128530786" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;With the service reference added, head to your Page.xaml markup, and add a simple templated listbox as follows:&lt;br /&gt;&lt;pre class="brush: xml;"&gt;&lt;br /&gt;&amp;lt;UserControl x:Class="BingSilverNews.Page"&lt;br /&gt;    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" &lt;br /&gt;    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&amp;gt;&lt;br /&gt;    &amp;lt;Grid x:Name="LayoutRoot" Background="White"&amp;gt;&lt;br /&gt;        &amp;lt;ListBox x:Name="NewsResults"&amp;gt;&lt;br /&gt;            &amp;lt;ListBox.ItemTemplate&amp;gt;&lt;br /&gt;                &amp;lt;DataTemplate&amp;gt;&lt;br /&gt;                    &amp;lt;StackPanel Orientation="Vertical"&amp;gt;&lt;br /&gt;                        &amp;lt;TextBlock Text="{Binding Path=Title}" FontWeight="Bold"/&amp;gt;&lt;br /&gt;                        &amp;lt;TextBlock Text="{Binding Path=Snippet}" TextWrapping="Wrap" Width="300"/&amp;gt;&lt;br /&gt;                        &amp;lt;HyperlinkButton NavigateUri="{Binding Path=Url}"&amp;gt;&amp;lt;TextBlock Text="Read More"/&amp;gt;&amp;lt;/HyperlinkButton&amp;gt;&lt;br /&gt;                    &amp;lt;/StackPanel&amp;gt;&lt;br /&gt;                &amp;lt;/DataTemplate&amp;gt;&lt;br /&gt;            &amp;lt;/ListBox.ItemTemplate&amp;gt;&lt;br /&gt;        &amp;lt;/ListBox&amp;gt;&lt;br /&gt;    &amp;lt;/Grid&amp;gt;&lt;br /&gt;&amp;lt;/UserControl&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If you're entirely unfamiliar with Silverlight, you may want to give Scott Guthrie's &lt;a href="http://weblogs.asp.net/scottgu/pages/silverlight-tutorial-part-1-creating-quot-hello-world-quot-with-silverlight-2-and-vs-2008.aspx" target="_blank"&gt;tutorial&lt;/a&gt; a go. There's really not much complexity to the above construct though.&lt;br /&gt;&lt;br /&gt;What we're doing is adding a ListBox, and calling that "NewsResults", to ensure that we've got somewhere to put our results.&lt;br /&gt;&lt;br /&gt;The Silverlight listbox however knows precious little about Bing, news results or anything remotely like that. It knows how to stack items in a neat little list, and that's it. To simplify handling of results from Bing, we're going to instruct it on how to handle each line of the results, using an item template.&lt;br /&gt;&lt;br /&gt;Notice the DataTemplate above. The contents of that block will be repeated for each item in the Bing search result array. It's got a stack panel, which basically puts UI elements on top of, or alongside, each other, and two text blocks and a hyperlink. The key part of making the template work, is obviously tying the output to something from the active Bing search result line. That's where the Bindings come in. If you look to the title textblock, the "{Binding Path=Title}" simply makes the template fetch the value for the textblock's text attribute from the Title field of whatever object is being processed. Repeat that explanation for the rest of the template's controls, and we can move on. I'll soon get back to where the listbox (and the templates) get the data from.&lt;br /&gt;&lt;br /&gt;Now turning to the codebehind, we're going to create a search service instance once more, and do a timed search every 30 seconds or so.&lt;br /&gt;&lt;pre class="brush: c-sharp;"&gt;&lt;br /&gt;namespace BingSilverNews&lt;br /&gt;{&lt;br /&gt;    using System;&lt;br /&gt;    using System.Threading;&lt;br /&gt;    using System.Windows.Controls;&lt;br /&gt;    using BingService;&lt;br /&gt;&lt;br /&gt;    public partial class Page : UserControl&lt;br /&gt;    {&lt;br /&gt;        private readonly LiveSearchPortTypeClient _bingClient;&lt;br /&gt;        private readonly Timer _updateTimer;&lt;br /&gt;&lt;br /&gt;        public Page()&lt;br /&gt;        {&lt;br /&gt;            InitializeComponent();&lt;br /&gt;&lt;br /&gt;            _bingClient = new LiveSearchPortTypeClient();&lt;br /&gt;            _bingClient.SearchCompleted += OnSearchCompleted;&lt;br /&gt;            _updateTimer = new Timer((state) =&amp;gt; OnTimer());&lt;br /&gt;            _updateTimer.Change(new TimeSpan(0), new TimeSpan(0, 0, 0, 30));&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void OnSearchCompleted(object sender, SearchCompletedEventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if ((e.Result != null) &amp;&amp;&lt;br /&gt;                (e.Result.News != null) &amp;&amp;&lt;br /&gt;                (e.Result.News.Results != null))&lt;br /&gt;            {&lt;br /&gt;                // We've got news!&lt;br /&gt;                NewsResults.ItemsSource = e.Result.News.Results;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void OnTimer()&lt;br /&gt;        {&lt;br /&gt;            if (CheckAccess())&lt;br /&gt;            {&lt;br /&gt;                _bingClient.SearchAsync(new SearchRequest&lt;br /&gt;                                            {&lt;br /&gt;                                                Sources = new[] {SourceType.News},&lt;br /&gt;                                                Query = "Microsoft",&lt;br /&gt;                                                AppId = "YOUR APP ID"&lt;br /&gt;                                            });&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;                Dispatcher.BeginInvoke(OnTimer);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The source should resemble that of the first search client we made way up. The only difference here, is that we do it on a timer, and assign the result to a Silverlight ListBox, rather than just writing each result to console.&lt;br /&gt;&lt;br /&gt;Worth noting is the fact that Silverlight requires an asynchronous service call model. That means that each call you make to a service, will return immediately. Only upon call to a predefined callback will the remote call actually have happened (and any return value be brought back). In this case, that's happens through the _bingClient.SearchCompleted property, which is told to execute OnSearchCompleted every time a search completes. The actual search call is now called _bingClient.SearchAsync(), rather than Search. Other than that, the search client behaves pretty much the same as in the previous app.&lt;br /&gt;&lt;br /&gt;The timer does have some voodoo to it. Since it's happening on a separate thread from the UI, and we're updating UI controls as a result of the service call, we have to jump about a bit. This resembles the InvokeRequired semantics from WinForms: Only the UI thread can update it's controls - anything else will result in an exception, to avoid concurrent (and crashing) updates to the same controls. Because of this, we have to check for access to the UI on each OnTimer call, and if not granted, have the UI dispatcher schedule to call OnTimer once more in its own context. In other words, the UI and timer will run in different threads. Once the timer is signaled on it's thread, it will tell the UI to execute the _bingClient.SearchAsync call in its own thread.&lt;br /&gt;&lt;br /&gt;Upon completion of each search, the search result array (noted by e.Result.News.Results), which has items of the NewsResult class, each holding Title, Snippet and Url fields, is assigned to the listbox. What will happen is that the listbox processes the array, one item at a time, and instantiates the contents of the xaml's DataTemplate for each one. Remembering that the template adressed the Title, Snippet and Url fields in its bindings, it's now obvious where these come from.&lt;br /&gt;&lt;br /&gt;Compiling and running the app should result in something like the following(only my results are localized for Norwegian Bing):&lt;br /&gt;&lt;center&gt;&lt;a target="_blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SjEbrqFm3bI/AAAAAAAAEF4/UGF7fCfMpWw/s1600-h/cap2.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 337px;" src="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SjEbrqFm3bI/AAAAAAAAEF4/UGF7fCfMpWw/s400/cap2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5346084669511622066" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Using this in SharePoint&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Getting Silverlight going in SharePoint is a slightly discomforting process. That's why someone took the time to write the &lt;a href="http://www.codeplex.com/SL4SP" target="_new"&gt;Silverlight in SharePoint Blueprints&lt;/a&gt;. While all the steps there aren't strictly required, it does get the job done.&lt;br /&gt;&lt;br /&gt;Once you've made the necessary changes to your portal, I also suggest you adobt the brilliant &lt;a href="http://www.codeplex.com/wspbuilder" target="_blank"&gt;WSPBuilder&lt;/a&gt; to speed up the process of writing (and deploying) webparts. There's a walkthrough of the WSPBuilder Visual Studio add-in &lt;a href="http://www.zimmergren.net/archive/2009/04/08/wspbuilder-walkthrough-of-the-visual-studio-add-in.aspx"&gt;here&lt;/a&gt;, which should get you going even quicker.&lt;br /&gt;&lt;br /&gt;Once you've got this all setup, wrapping up the Silverlight web part is quite simple. Create a new WSPBuilder project, and add a new WSPBuilder Web Part with Feature item to that project. Name the project (and webpart) something meaningful, for your own good.&lt;br /&gt;&lt;br /&gt;In my example setup, I've expanded the structure of the WSPBuilder project as follows:&lt;br /&gt;&lt;center&gt;&lt;a target="_blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SjEdi5lQUeI/AAAAAAAAEGA/COAfgQwMHs8/s1600-h/cap3.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 268px; height: 400px;" src="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SjEdi5lQUeI/AAAAAAAAEGA/COAfgQwMHs8/s400/cap3.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5346086718075326946" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Note the LAYOUTS and BingNews folders added under the TEMPLATE folder. Putting the .xap-file from your Silverlight project here (or better yet, adding a pre-build step to copy it to that project folder, from your Silverlight project's output folder), will make the WSPBuilder solution deploy it to the relative layouts folder of portal you deploy to. The .xap *is* the Silverlight, so this step is required for anything to work properly beyond here.&lt;br /&gt;&lt;br /&gt;You'll also want to change the .NET runtime version for the project to 3.5, and reference the System.Web.Silverlight assembly, as well as System.Web.Extensions.&lt;br /&gt;&lt;br /&gt;Heading back to the codebehind of your new webpart project, it needs a few (but not many) changes:&lt;br /&gt;&lt;pre class="brush: c-sharp;"&gt;&lt;br /&gt;namespace Grep.SharePoint.BingNews&lt;br /&gt;{&lt;br /&gt;    using System;&lt;br /&gt;    using System.Collections.Generic;&lt;br /&gt;    using System.ComponentModel;&lt;br /&gt;    using System.Runtime.InteropServices;&lt;br /&gt;    using System.Web.UI;&lt;br /&gt;    using System.Web.UI.SilverlightControls;&lt;br /&gt;    using System.Web.UI.WebControls;&lt;br /&gt;    using System.Web.UI.WebControls.WebParts;&lt;br /&gt;&lt;br /&gt;    [Guid("your guid")]&lt;br /&gt;    public class BingSearch : WebPart&lt;br /&gt;    {&lt;br /&gt;        private bool _error;&lt;br /&gt;        private ScriptManager _scriptManager;&lt;br /&gt;&lt;br /&gt;        public BingSearch()&lt;br /&gt;        {&lt;br /&gt;            ExportMode = WebPartExportMode.All;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Create all your controls here for rendering.&lt;br /&gt;        /// Try to avoid using the RenderWebPart() method.&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        protected override void CreateChildControls()&lt;br /&gt;        {&lt;br /&gt;            if (!_error)&lt;br /&gt;            {&lt;br /&gt;                try&lt;br /&gt;                {&lt;br /&gt;                    base.CreateChildControls();&lt;br /&gt;                    var sl = new Silverlight();&lt;br /&gt;                    sl.ID = ID + "_SL";&lt;br /&gt;                    sl.Width = Unit.Percentage(100);&lt;br /&gt;                    sl.Height = Height;&lt;br /&gt;                    sl.Source = "~/_layouts/BingNews/Grep.Silverlight.BingNews.xap";&lt;br /&gt;                    Controls.Add(sl);&lt;br /&gt;                }&lt;br /&gt;                catch (Exception ex)&lt;br /&gt;                {&lt;br /&gt;                    HandleException(ex);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Clear all child controls and add an error message for display.&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        /// &amp;lt;param name="ex"&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;        private void HandleException(Exception ex)&lt;br /&gt;        {&lt;br /&gt;            _error = true;&lt;br /&gt;            Controls.Clear();&lt;br /&gt;            Controls.Add(new LiteralControl(ex.Message));&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Ensures that a script manager exists on the page.&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        /// &amp;lt;param name="e"&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;        protected override void OnInit(EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if (!_error)&lt;br /&gt;            {&lt;br /&gt;                try&lt;br /&gt;                {&lt;br /&gt;                    base.OnInit(e);&lt;br /&gt;                    _scriptManager = ScriptManager.GetCurrent(Page);&lt;br /&gt;                    if (_scriptManager == null)&lt;br /&gt;                    {&lt;br /&gt;                        _scriptManager = new ScriptManager();&lt;br /&gt;                        _scriptManager.ID = "ScriptManager1";&lt;br /&gt;                        Controls.AddAt(0, _scriptManager);&lt;br /&gt;                        if (Page.Form != null)&lt;br /&gt;                        {&lt;br /&gt;                            Page.Form.Controls.AddAt(0, _scriptManager);&lt;br /&gt;                        }&lt;br /&gt;                        else&lt;br /&gt;                        {&lt;br /&gt;                            throw new Exception("No form tag found on current page. ScriptManager cannot be added.");&lt;br /&gt;                        }&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;                catch (Exception ex)&lt;br /&gt;                {&lt;br /&gt;                    HandleException(ex);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Ensures that the CreateChildControls() is called before events.&lt;br /&gt;        /// Use CreateChildControls() to create your controls.&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        /// &amp;lt;param name="e"&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;        protected override void OnLoad(EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if (!_error)&lt;br /&gt;            {&lt;br /&gt;                try&lt;br /&gt;                {&lt;br /&gt;                    base.OnLoad(e);&lt;br /&gt;                    EnsureChildControls();&lt;br /&gt;                }&lt;br /&gt;                catch (Exception ex)&lt;br /&gt;                {&lt;br /&gt;                    HandleException(ex);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This really looks a lot like the default WSPBuilder template, with a couple of modifications. I've removed the web part properties (you could keep them, though, and use those to pass keywords to your Silverlight Bing client, using the Silverlight control's InitParams), and I've added an OnInit override which adds a ScriptManager to the page - if that's not there already. The Silverlight control requires a ScriptManager to spawn Silverlight instances on the fly, so this is also a quite required step.&lt;br /&gt;&lt;br /&gt;I've also changed the webpart's base class to System.Web.UI.WebControls.WebParts.WebPart (which is recommended by Microsoft, for all new web parts).&lt;br /&gt;&lt;br /&gt;Other than that, all it does is create a Silverlight control instance, setup the source, and initialize the Silverlight's widt / height from the webpart's own width / height, and add it to the control stack.&lt;br /&gt;&lt;br /&gt;Building the source, wrapping a WSP (using the WSPBuilder project context menu), and deploying it to your SharePoint portal. Then activating the feature, and adding the webpart, should add a simple little Bing News box to your portal. Expanding it further, I leave up to you. Being Silverlight, you can add all kinds of fancy effects to the UI, if you please, and even test it from an outside-of-SharePoint web project.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-6876148018752288316?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/8G_IsxSUPfU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/6876148018752288316/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=6876148018752288316" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6876148018752288316?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6876148018752288316?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/8G_IsxSUPfU/bing-powered-silverlight-enabled.html" title="Writing a Silverlight powered Bing news web part for SharePoint" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SjESTOSoLWI/AAAAAAAAEFw/5d8ikHKW1YQ/s72-c/cap1.PNG" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/06/bing-powered-silverlight-enabled.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UNSH05cCp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-6305639189107044243</id><published>2009-06-01T23:59:00.009+02:00</published><updated>2009-09-17T14:08:19.328+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T14:08:19.328+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="TemplateLibraryConnector" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><title>New and Improved Template Library Connector</title><content type="html">If you're new to the Template Library Connector, here's the nutshell version:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-weight:bold;"&gt;Grep's Template Library Connector&lt;/span&gt; adds the option to use a SharePoint document library as a template library for other document libraries. The templates are hierarchically shown in the "New" menu submenus of the document libraries, and instantiates exactly like common document templates. Changes to the template library are instantly available through the "New" menu of *all* linked document libraries.&lt;/blockquote&gt;&lt;br /&gt;There's an older, a bit more technical post available &lt;a href="http://einaros.blogspot.com/2009/05/using-document-libraries-as-template.html"&gt;here&lt;/a&gt;, with install instructions.&lt;br /&gt;&lt;br /&gt;Downloads (source and binary) is available at the CodePlex project site: &lt;a href="http://templatelibconnector.codeplex.com/" target="_blank"&gt;Grep's Template Library Connector&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;News for v1.5 include:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Full WSS support&lt;br /&gt;&lt;li&gt;No more webpart to connect document libraries: all config done from the list's settings page.&lt;br /&gt;&lt;li&gt;Will work correctly with webpart pages hosting multiple document library views (ListViewWebParts)&lt;/ul&gt;From v1.0:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Connect &lt;b&gt;any&lt;/b&gt; document library to another document library, with one serving template documents to the other.&lt;br /&gt;&lt;li&gt;Not require any &lt;b&gt;administration&lt;/b&gt; of new templates, other than uploading them to the template library - &lt;b&gt;Instantly&lt;/b&gt; making them available to all document libraries linked with the template library.&lt;br /&gt;&lt;li&gt;Allow document libraries and template libraries to reside in &lt;b&gt;different sites&lt;/b&gt;.&lt;br /&gt;&lt;li&gt;Supports Content Types in the template and document libraries, with workflow connections, columns, data information panels and the other goodies that provides.&lt;br /&gt;&lt;li&gt;Make the templates instantly, and &lt;b&gt;hierarchically&lt;/b&gt;, available from the &lt;b&gt;"New" menu&lt;/b&gt; in the document library.&lt;/ul&gt;&lt;br /&gt;&lt;div style="text-align: center; width: 100%;"&gt;&lt;div style="display: inline-block; text-align: left;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.screencast.com/users/einaros/folders/Jing/media/df616cbe-aeda-4d82-92da-88a5713b2571" target="_blank"&gt;&lt;img style="display: inline; width: 350px; height: 207px;" src="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SiRRWEVQx9I/AAAAAAAAEFI/ZCbcoYukfVE/s400/demo1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5342484497530144722" /&gt;&lt;/a&gt;&lt;br/&gt;&lt;div style="text-align: left; font-size: 8pt; display: inline"&gt;Demonstration Video&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-6305639189107044243?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/4PRkif4ZQl4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/6305639189107044243/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=6305639189107044243" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6305639189107044243?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/6305639189107044243?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/4PRkif4ZQl4/new-and-improved-template-library.html" title="New and Improved Template Library Connector" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SiRRWEVQx9I/AAAAAAAAEFI/ZCbcoYukfVE/s72-c/demo1.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/06/new-and-improved-template-library.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UNSH05cSp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-9120306956180437047</id><published>2009-05-30T14:04:00.039+02:00</published><updated>2009-09-17T14:08:19.329+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T14:08:19.329+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="TemplateLibraryConnector" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><title>Using Document Libraries as Template Libraries</title><content type="html">&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SiFRrYYaf4I/AAAAAAAAEDo/ukuAzSPdSE4/s1600-h/gtlc1.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 155px;" src="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SiFRrYYaf4I/AAAAAAAAEDo/ukuAzSPdSE4/s400/gtlc1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5341640438759194498" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;span style="font-size: 8pt; color: blue"&gt;&lt;a href="http://templatelibconnector.codeplex.com/" target="_blank"&gt;Download the source / wsp at CodePlex&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 10pt; color: green"&gt;Update June 1st: Version 1.5 released. The new version no longer requires a webpart, nor a lot of configuration, so I've updated the outlines seen in this post. See &lt;a href="http://einaros.blogspot.com/2009/06/new-and-improved-template-library.html"&gt;this new post&lt;/a&gt; for more info on the update.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Background&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;SharePoint document libraries are capable of handling many day-to-day tasks, right out of the box; there's no question about that. When you approach the need or wish for simple to maintain template libraries, that's an entirely different story.&lt;br /&gt;&lt;br /&gt;A usual approach is to configure content types, and a set of hard-to-maintain URL links between these and actual documents in other locations. Administrative tasks, such as adding and removing templates, quickly becomes next to impossible, as you'll have to create new content types, and add /remove these manually for each document library you want the templates available in.&lt;br /&gt;&lt;br /&gt;Another issue is the fact that the "New" menu for document libraries will show all content types at the root level. You can't categorize them in submenus, so large amounts of templates will be everything but a smooth ride for the end user.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Fixing things with a simple feature&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Realizing that the aforementioned widespread pain is completely unnecessary, I set out yesterday to write a feature which can:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Connect &lt;b&gt;any&lt;/b&gt; document library to another document library, with one serving template documents to the other.&lt;br /&gt;&lt;li&gt;Not require any &lt;b&gt;administration&lt;/b&gt; of new templates, other than uploading them to the template library - &lt;b&gt;Instantly&lt;/b&gt; making them available to all document libraries linked with the template library.&lt;br /&gt;&lt;li&gt;Allow document libraries and template libraries to reside in &lt;b&gt;different sites&lt;/b&gt;.&lt;br /&gt;&lt;li&gt;Make the templates instantly, and &lt;b&gt;hierarchically&lt;/b&gt;, available from the &lt;b&gt;"New" menu&lt;/b&gt; in the document library.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;.. And make it all available to all of you, complete with source code, pre-built solution/feature, instructions and documentation.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Installation and Usage&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a style="font-size: 10pt" href="http://templatelibconnector.codeplex.com/"&gt;Download the latest source and install packages here&lt;/a&gt;.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Unpack and run the install.bat file from the root folder. This will install the solution, and deploy it to all site collections. If you want more fine-grained control, feel free to install and deploy the .wsp-file on your own.&lt;br /&gt;&lt;li&gt;Open a SharePoint portal with document libraries you want to template enable.&lt;br /&gt;&lt;li&gt;Open the Site Settings, and navigate to the Site Feature page.&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SiF-fCpfldI/AAAAAAAAEDw/-eJAAirZQiY/s1600-h/gtlc2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 321px; height: 275px;" src="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SiF-fCpfldI/AAAAAAAAEDw/-eJAAirZQiY/s400/gtlc2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5341689704790070738" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;li&gt;Enable "Grep's SmartDocument Template Library Connector"&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/SiF-fG5GRfI/AAAAAAAAED4/jYzytye2RSM/s1600-h/gtlc3.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 34px;" src="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/SiF-fG5GRfI/AAAAAAAAED4/jYzytye2RSM/s400/gtlc3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5341689705929262578" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;li&gt;Open a document library you want to expose templates in, and open it's settings.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/SiRrg9PrcRI/AAAAAAAAEFQ/NltWqQxVIYA/s1600-h/gtlc5.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 205px;" src="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/SiRrg9PrcRI/AAAAAAAAEFQ/NltWqQxVIYA/s400/gtlc5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5342513271908561170" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Navigate to the "Template Library Connector settings" entry, under "General Settings".&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SiRr-z2uwgI/AAAAAAAAEFY/q5vxb2xlBEQ/s1600-h/2009-06-02_0201.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 263px;" src="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/SiRr-z2uwgI/AAAAAAAAEFY/q5vxb2xlBEQ/s400/2009-06-02_0201.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5342513784784077314" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Make sure you select "Yes" to enable template library connections, pick a document library to act as a template library, and save.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SiRsdlH6vRI/AAAAAAAAEFg/Sal7SNq5_es/s1600-h/2009-06-02_0203.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 374px;" src="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SiRsdlH6vRI/AAAAAAAAEFg/Sal7SNq5_es/s400/2009-06-02_0203.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5342514313405578514" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Navigate back to the document library, and verify that the "New" menu is wired up properly.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/SiRs8feVtvI/AAAAAAAAEFo/acmhmBAKFg4/s1600-h/2009-06-02_0205.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 331px;" src="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/SiRs8feVtvI/AAAAAAAAEFo/acmhmBAKFg4/s400/2009-06-02_0205.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5342514844464953074" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Adding new documents to the selected template library will instantly make new templates available from the "New" menu.&lt;br /&gt;&lt;li&gt;You can optionally add a column called "Description" to the template document library, for additional text under each template, in the "New" menu view. If you wish to call this something other than "Description", the column name can be changed through the webpart settings shown above.&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;And that should be it. If you've got any questions or problems, feel free to drop me a note through the comments here.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Getting Technical&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Writing a solution like this takes a few steps, and a bit of tinkering with undocumented SharePoint libraries. Here's a rough cut of what's covered in the source:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Utilizing delegate controls to install handlers for all opened pages&lt;br /&gt;&lt;li&gt;Find all ListViewWebParts on the webpart page, and retrieve entries from its toolbar.&lt;br /&gt;&lt;li&gt;Adding items to ListViewWebPart's menus&lt;br /&gt;&lt;li&gt;Adding submenus to the "New" menu&lt;br /&gt;&lt;li&gt;Adding submenus to submenus&lt;br /&gt;&lt;li&gt;Utilizing the default SharePoint document template JavaScripts to create new documents&lt;br /&gt;&lt;li&gt;Retrieving hierarchies from SharePoint document libraries&lt;br /&gt;&lt;li&gt;Writing and exposing list settings pages&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;A few of these steps, such as adding menus and submenus, are powered by extension libraries I've included in the source code. A simple thing like adding a submenu to a submenu isn't easily and readily available in the SharePoint object model, but with the extension libraries in the source project, it's ridiculously simple!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Code Pieces&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I won't be covering everything here, but some key parts of the functionality, which I haven't seen elsewhere on the web, will be brought forward.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Extending the LiewViewWebPart's "New" menu&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The ListViewWebPart has a toolbar, but this isn't immediately accessible from its object model. Why this is so is oblivious to me, as I see many possible benefits of extending the default views.&lt;br /&gt;&lt;br /&gt;Ignoring the ListViewWebPart's attempt at hiding it, and the fact that even toolbar access functions are internal, I extended the System.Web.UI.Control class to give me the following edge:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;1:&amp;nbsp;&lt;/span&gt;&lt;span style='color:#000084; font-weight:bold; '&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;static&lt;/span&gt;&amp;nbsp;T&amp;nbsp;FindChildByType&amp;lt;T&gt;(&lt;span style='color:#000084; font-weight:bold; '&gt;this&lt;/span&gt;&amp;nbsp;Control&amp;nbsp;self)&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;2:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;where&amp;nbsp;T&amp;nbsp;:&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;class&lt;/span&gt;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;3:&amp;nbsp;&lt;/span&gt;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;4:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;stack&amp;nbsp;=&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;new&lt;/span&gt;&amp;nbsp;Stack(self.Controls);&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;5:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;while&lt;/span&gt;&amp;nbsp;(stack.Count&amp;nbsp;&gt;&amp;nbsp;0)&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;6:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;7:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;control&amp;nbsp;=&amp;nbsp;stack.Pop()&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;as&lt;/span&gt;&amp;nbsp;Control;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;8:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;if&lt;/span&gt;&amp;nbsp;(control&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;is&lt;/span&gt;&amp;nbsp;T)&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;9:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;10:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;return&lt;/span&gt;&amp;nbsp;control&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;as&lt;/span&gt;&amp;nbsp;T;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;11:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;12:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;foreach&lt;/span&gt;&amp;nbsp;(Control&amp;nbsp;child&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;in&lt;/span&gt;&amp;nbsp;control.Controls)&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;13:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;14:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stack.Push(child);&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;15:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;16:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;17:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;18:&amp;nbsp;&lt;/span&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The approach is pretty straight forward: it will do a stack-based iteration of the control class, until a control of the target type is found.&lt;br /&gt;&lt;br /&gt;Including this allows you to call a function such as FindChildByType&lt;NewMenu&gt;() on any class deriving from System.Web.UI.Control. Doing just that on the ListViewWebPart will, with no further hassle, let you access its inner NewMenu.&lt;br /&gt;&lt;br /&gt;Once you get an instance of the NewMenu class (which derives from the superbly named ToolBarMenuButton class), you can call functions such as AddMenuItem and AddMenuItemSeparator (see &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.webcontrols.newmenu_members.aspx"&gt;MSDN&lt;/a&gt;). There's however no function to add submenus (or submenus to submenus, for that matter), so that will require another extension.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;1:&amp;nbsp;&lt;/span&gt;&lt;span style='color:#000084; font-weight:bold; '&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;static&lt;/span&gt;&amp;nbsp;SubMenuTemplate&amp;nbsp;AddSubMenuAt(&lt;span style='color:#000084; font-weight:bold; '&gt;this&lt;/span&gt;&amp;nbsp;ToolBarMenuButton&amp;nbsp;self,&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;int&lt;/span&gt;&amp;nbsp;index,&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;string&lt;/span&gt;&amp;nbsp;id,&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;string&lt;/span&gt;&amp;nbsp;displayName,&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;2:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;string&lt;/span&gt;&amp;nbsp;imageUrl,&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;string&lt;/span&gt;&amp;nbsp;description)&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;3:&amp;nbsp;&lt;/span&gt;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;4:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SubMenuTemplate&amp;nbsp;child&amp;nbsp;=&amp;nbsp;CreateSubMenu(id,&amp;nbsp;displayName,&amp;nbsp;imageUrl,&amp;nbsp;description);&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;5:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.MenuTemplateControl.Controls.AddAt(index,&amp;nbsp;child);&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;6:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;return&lt;/span&gt;&amp;nbsp;child;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;7:&amp;nbsp;&lt;/span&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The key part in the above code is the call to CreateSubMenu, which returns a SubMenuTemplate. The latter is a contruct from the Microsoft.SharePoint.WebControls namespace, which is used for several menus around the SharePoint UI, such as the Site Settings menu you get in MOSS when the "Office SharePoint Server Publishing" feature is enabled.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;1:&amp;nbsp;&lt;/span&gt;&lt;span style='color:#000084; font-weight:bold; '&gt;private&lt;/span&gt;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;static&lt;/span&gt;&amp;nbsp;SubMenuTemplate&amp;nbsp;CreateSubMenu(&lt;span style='color:#000084; font-weight:bold; '&gt;string&lt;/span&gt;&amp;nbsp;id,&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;string&lt;/span&gt;&amp;nbsp;displayName,&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;2:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;string&lt;/span&gt;&amp;nbsp;imageUrl,&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;string&lt;/span&gt;&amp;nbsp;description)&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;3:&amp;nbsp;&lt;/span&gt;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;4:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;child&amp;nbsp;=&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;new&lt;/span&gt;&amp;nbsp;SubMenuTemplate();&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;5:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child.ID&amp;nbsp;=&amp;nbsp;id;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;6:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child.Text&amp;nbsp;=&amp;nbsp;displayName;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;7:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child.Description&amp;nbsp;=&amp;nbsp;description;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;8:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;if&lt;/span&gt;&amp;nbsp;(&lt;span style='color:#000084; font-weight:bold; '&gt;string&lt;/span&gt;.IsNullOrEmpty(imageUrl))&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;9:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;10:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;imageUrl&amp;nbsp;=&amp;nbsp;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;/_layouts/images/MenuNewItem.gif&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;11:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child.ImageUrl&amp;nbsp;=&amp;nbsp;imageUrl;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;12:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;13:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;else&lt;/span&gt;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;14:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;15:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;if&lt;/span&gt;&amp;nbsp;(imageUrl.IndexOf(&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;/&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;)&amp;nbsp;==&amp;nbsp;-1)&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;16:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;17:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;imageUrl&amp;nbsp;=&amp;nbsp;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;/_layouts/images/&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;+&amp;nbsp;imageUrl;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;18:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;19:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child.ImageUrl&amp;nbsp;=&amp;nbsp;imageUrl;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;20:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;21:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;return&lt;/span&gt;&amp;nbsp;child;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;22:&amp;nbsp;&lt;/span&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;With these functions, along with a few more included in the source project, such as extending SubMenuTemplate to allow addition of nested submenus; adding menus and submenus is easy as pie.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Adding meaningful menu items&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Getting to add menu items is one thing, but actually adding something meaningful is what obviously makes it useful. In this webpart, the goal was to make it possible to instantiate document (templates) from other document libraries, and that turns out to be surprisingly simple if you study how Microsoft does regular template instantiation. The javascript function called "createNewDocumentWithProgID" is the key here, and it accepts such parameters as source file url and target folder url. If the menu items are passed calls to this javascript in its click handler, with the correct urls, document instantiation is in the bag as well.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;In Closing&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So that's about it. Feel free to check the code, use the webpart, develop it further and share alike. For any questions or comments, use the comment feature on this blog.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-9120306956180437047?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/ZNXb6aK4oW0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/9120306956180437047/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=9120306956180437047" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/9120306956180437047?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/9120306956180437047?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/ZNXb6aK4oW0/using-document-libraries-as-template.html" title="Using Document Libraries as Template Libraries" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/SiFRrYYaf4I/AAAAAAAAEDo/ukuAzSPdSE4/s72-c/gtlc1.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/05/using-document-libraries-as-template.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UNSH05cSp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-4949216344894668040</id><published>2009-05-18T14:11:00.020+02:00</published><updated>2009-09-17T14:08:19.329+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T14:08:19.329+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="Greasemonkey" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><category scheme="http://www.blogger.com/atom/ns#" term="FireBug" /><title>Effective SharePoint UI prototyping using jQuery, FireBug and Greasemonkey</title><content type="html">&lt;span style="font-size: 8pt; color: green"&gt;Demo script download available at the end of the post.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 8pt; color: green"&gt;Updated May 19th, with clearer code examples.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Background&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Customizing SharePoint's user interface, or even updating your own interfaces, can be a tiresome thing to do. Usually you'll have to deal with multiple master pages, many master page overrides, multiple css files and a wide array of dynamic code from XSLTs or web parts. All this makes it quite difficult to maintain a clear understanding and overview of the UIs you're customizing, or what the impact of your changes will be.&lt;br /&gt;&lt;br /&gt;To add to the already high stress levels, many changes - especially those where you're deploying changes to your own web parts - will require recompiles, iis resets, feature reactivation or similar time consuming steps.&lt;br /&gt;&lt;br /&gt;In this article I want to propose an alternative approach to UI changes, using jQuery prototyping. While jQuery isn't necessarily something you'd want to use to do all SharePoint UI restructuring, it does come in handy if you want to make simple runtime enhancements to the current UI, or even want to extend your custom parts with simple asynchronous behavior, or neat visual effects.&lt;br /&gt;&lt;br /&gt;To accomplish this, in a way which will require no recompilation, redelpoyment or other nasty things, and overall keep the time between change and prototype visualization as low as possible; we'll be using the FireFox addons FireBug and Greasemonkey. The former is a brilliant DOM / CSS / Html / Network explorer, built into FireFox' interface, with lightning quick structure inspection and navigation. Greasemonkey, on the other hand, can inject custom javascript into any loaded page, without making any serverside changes.&lt;br /&gt;&lt;br /&gt;The combination of these tools will allow us to write advanced javascripts in our favorite javascript editor, and see the results immediately, without having to upload any files to the server. A simple browser refresh will suffice.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Requirements&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;FireFox (3.0.10 is the latest at the time of writing)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;FireBug&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Greasemonkey&lt;/li&gt;&lt;li&gt;Knowledge of JavaScript and HTML / DOM.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Installing&lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Download and install &lt;a href="http://www.mozilla.com/en-US/firefox/all.html"&gt;FireFox&lt;/a&gt;, if you haven't done this already.&lt;/li&gt;&lt;li&gt;Start FireFox, and open the Addons config screen, available from the Tool menu.&lt;/li&gt;&lt;li&gt;Activate "Get Addons" function on the top bar, then search for and install FireBug and Greasemonkey.&lt;/li&gt;&lt;li&gt;Restart FireFox.&lt;/li&gt;&lt;li&gt;Navigate to a SharePoint portal you wish to prototype, and confirm that you've got a set of FireBug / Greasemonkey icons in your browser status bar, such as:&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/ShFbA2UMnMI/AAAAAAAAECw/0EY83tSNpbo/s1600-h/1.png"&gt;&lt;img style="cursor: pointer; width: 320px; height: 246px;" src="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/ShFbA2UMnMI/AAAAAAAAECw/0EY83tSNpbo/s320/1.png" alt="" id="BLOGGER_PHOTO_ID_5337147103549103298" border="0" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;/li&gt;&lt;li&gt;Right click the Greasemonkey icon, make sure it's set to enabled, and click New User script:&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/ShFb1t61tKI/AAAAAAAAEC4/Z2W3GmPvaNw/s1600-h/2.png"&gt;&lt;img style="cursor: pointer; width: 283px; height: 188px;" src="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/ShFb1t61tKI/AAAAAAAAEC4/Z2W3GmPvaNw/s320/2.png" alt="" id="BLOGGER_PHOTO_ID_5337148011828327586" border="0" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Fill in values for the namespace and script title, such as:&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/ShFcLm1QQpI/AAAAAAAAEDA/Tdyj9N9vYb4/s1600-h/3.png"&gt;&lt;img style="cursor: pointer; width: 314px; height: 320px;" src="http://2.bp.blogspot.com/_rBg2qaJ_4Bw/ShFcLm1QQpI/AAAAAAAAEDA/Tdyj9N9vYb4/s320/3.png" alt="" id="BLOGGER_PHOTO_ID_5337148387882975890" border="0" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;The "Includes" box should be the url to the page you wish to prototype. This can be changed later, so stick with the default value for now. In other words, don't copy this from my example above.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Next you'll be asked to select which editor to use for script editing. This can be any text editor of your choice, such as notepad, or in my case, Visual Studio.&lt;/li&gt;&lt;li&gt;Once the editor selection is completed, it will be opened, you'll see an empty script. To test that you've got it all setup correctly, you can try adding an alert statement, and reload the page.&lt;/li&gt;&lt;li&gt;Next click the FireBug icon, and activate the HTML function:&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/ShFfjcjMIpI/AAAAAAAAEDI/hR4PG13Sj38/s1600-h/4.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 194px;" src="http://1.bp.blogspot.com/_rBg2qaJ_4Bw/ShFfjcjMIpI/AAAAAAAAEDI/hR4PG13Sj38/s320/4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5337152095974597266" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;/li&gt;&lt;li&gt;If you're unfamiliar with FireBug, play around with the Inspect function, to see how various UI elements correspond to the HTML source code. Selecting an element also enables you to immediately see the active CSS styles for said element.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Prototyping something useful&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Assignment: Making the Quick Launch on the left collapsible.&lt;br /&gt;&lt;br /&gt;"Useful" is relative, I agree, but this is at least example use of how to hotplug jQuery into the mix, and how you can use that to experiment with UI changes you'd later wish to deploy to a master page, or portal page.&lt;br /&gt;&lt;br /&gt;From here on we'll concentrate mainly on the script opened in your chosen editor. As of now, all the content should be a commented "UserScript" section - which is good. If you've entered anything else, feel free to remove that now.&lt;br /&gt;&lt;br /&gt;So, without further stalling, lets code.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Loading scripts (such as jQuery) at runtime&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Greasemonkey lets us inject custom scripts, but rather than messing any more around with it's configuration, we'll do the rest of the script loading with code. Since this is a prototype, and we'll be doing all of the prototyping in FireFox; loading scripts is simple. &lt;br /&gt;&lt;br /&gt;The only complicating step, is the fact that Greasemonkey will execute Greasemonkey scripts in a sandbox, isolating certain functionality from the scripts already loaded in the browser. The reasoning here is that Greasemonkey, and its scripts, will execute with greater privileges than those loaded by the browser. To prevent browser-loaded scripts from getting unwanted access from your custom Greasemonkey scripts, and possible do naughty stuff on your system, Greasemonkey forces us to take certain actions to access the stuff outside the sandbox. If you wish to read more about that, search for "unsafeWindow" in your favorite search engine.&lt;br /&gt;&lt;br /&gt;I've put together a set of functions, which will load custom scripts in order, and map the objects from these into the namespace available from the Greasemonkey sandbox. The functions looks as follows, and you can go ahead and input these in your prototype script. Reading and understanding them are optional steps -- we'll soon be getting to the interesting stuff.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;1:&amp;nbsp;&lt;/span&gt;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;&amp;nbsp;mapUnsafeObjects(objectArray)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;2:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;if&lt;/span&gt;&amp;nbsp;(objectArray&amp;nbsp;==&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;null&lt;/span&gt;)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;3:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;return&lt;/span&gt;;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;4:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;5:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;6:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;for&lt;/span&gt;&amp;nbsp;(&lt;span style='color:#000084; font-weight:bold; '&gt;var&lt;/span&gt;&amp;nbsp;i&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;in&lt;/span&gt;&amp;nbsp;objectArray)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;7:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;var&lt;/span&gt;&amp;nbsp;objectName&amp;nbsp;=&amp;nbsp;objectArray[i];&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;8:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window[objectName]&amp;nbsp;=&amp;nbsp;unsafeWindow[objectName];&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;9:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;10:&amp;nbsp;&lt;/span&gt;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;11:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;12:&amp;nbsp;&lt;/span&gt;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;&amp;nbsp;loadScript(url,&amp;nbsp;objectArray,&amp;nbsp;loadCallback)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;13:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;var&lt;/span&gt;&amp;nbsp;js&amp;nbsp;=&amp;nbsp;document.createElement(&lt;span style='color:#0000ff; '&gt;'script'&lt;/span&gt;);&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;14:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;js.src&amp;nbsp;=&amp;nbsp;url;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;15:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;js.type&amp;nbsp;=&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'text/javascript'&lt;/span&gt;;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;16:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;js.wrappedJSObject.onload&amp;nbsp;=&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;()&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;17:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mapUnsafeObjects(objectArray);&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;18:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;loadCallback();&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;19:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;20:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.getElementsByTagName(&lt;span style='color:#0000ff; '&gt;'head'&lt;/span&gt;)[0].appendChild(js);&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;21:&amp;nbsp;&lt;/span&gt;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;22:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;23:&amp;nbsp;&lt;/span&gt;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;&amp;nbsp;loadScripts(scriptArray,&amp;nbsp;completeCallback)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;24:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;if&lt;/span&gt;&amp;nbsp;(scriptArray.length&amp;nbsp;==&amp;nbsp;0)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;25:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;completeCallback();&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;26:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;27:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;var&lt;/span&gt;&amp;nbsp;scriptEntry&amp;nbsp;=&amp;nbsp;scriptArray.shift();&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;28:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;loadScript(scriptEntry.url,&amp;nbsp;scriptEntry.objects,&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;()&amp;nbsp;{&amp;nbsp;loadScripts(scriptArray,&amp;nbsp;completeCallback);&amp;nbsp;});&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;29:&amp;nbsp;&lt;/span&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;So these are the only utility functions you need to load external scripts. You can reuse them in any prototyping scripts you feed into Greasemonkey, to load whatever external content you need.&lt;br /&gt;&lt;br /&gt;Here's an example on how to load the jQuery, jQuery UI and JSON2. We won't be needing more than the first in the following prototype, but if you wish to play around with jQuery UI (which has some cool drag and drop features, among other things), or the ajax + JSON functionality in jQuery, the others will be handy.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;1:&amp;nbsp;&lt;/span&gt;loadScripts([&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;2:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{url:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;http://jquery.com/src/jquery-latest.js&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;,&amp;nbsp;objects:&amp;nbsp;[&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;jQuery&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;,&amp;nbsp;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;$&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;]},&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;3:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{url:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;http://jquery-ui.googlecode.com/svn/tags/latest/ui/minified/jquery-ui.min.js&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;,&amp;nbsp;objects:&amp;nbsp;[]},&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;4:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{url:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;http://www.json.org/json2.js&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;,&amp;nbsp;objects:&amp;nbsp;[&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;JSON&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;]}&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;5:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;],&amp;nbsp;onLoadComplete);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;What this will do, is load one script at a time, making sure it's completely loaded before skipping to the next in line. For each script loaded, an array of objects is mapped into the sandbox namespace, for easy access in the prototype script. Once all scripts are loaded, the callback function named at the end will be called.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;First jQuery action&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Thus, the onLoadComplete function will be where our action will take place. Once this is called, jQuery will be fully loaded, and available for use. To demonstrate this, create a callback such as follows:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;1:&amp;nbsp;&lt;/span&gt;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;&amp;nbsp;onLoadComplete()&amp;nbsp;&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;2:&amp;nbsp;&lt;/span&gt;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;3:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;a&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;).css({&amp;nbsp;fontSize:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'18pt'&lt;/span&gt;&amp;nbsp;});&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;4:&amp;nbsp;&lt;/span&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Once you save this, along with the previous code snippets, in the Greasemonkey script opened in your chosen browser, and reload the portal, all html anchor elements will get a ridiculous font size. Not too useful, but it at least confirms everything is working properly. If the jQuery syntax is unfamiliar to you, now would be a good time to read up on some basic examples at &lt;a href="http://docs.jquery.com" target="_blank"&gt;jQuery.com&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Collapsible Quick Launch&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is when FireBug will come in handy. Writing the code is simple, and Greasemonkey is great, but without a proper tool to navigate the html, we'd be banging heads into walls before long.&lt;br /&gt;&lt;br /&gt;With the FireBug window opened, and pointed to "HTML" mode, hit Inspect and select the section just below a main quick launch link, as shown in this screen shot:&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/ShFnwPt88bI/AAAAAAAAEDQ/gFljpdgxdWQ/s1600-h/5.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 285px; height: 320px;" src="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/ShFnwPt88bI/AAAAAAAAEDQ/gFljpdgxdWQ/s320/5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5337161111961399730" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Doing this tells us that the quick launch main menu items have IDs such as "zz2_QuickLaunchMenunN", where N is the menu item index. Tapping into this with jQuery is pretty simple, with a selector such as &lt;span style="font-style:italic;"&gt;$("tr[id^=zz2_QuickLaunchMenun]")&lt;/span&gt;. What we'd like to do with this, is to attach a click handler to collapse / expand the table row below it, &lt;span style="font-style:italic;"&gt;if&lt;/span&gt; the following table row isn't another main menu item.&lt;br /&gt;&lt;br /&gt;Adding such a handler, in a pretty straight-forward manner, would look like:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;1:&amp;nbsp;&lt;/span&gt;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;&amp;nbsp;onLoadComplete()&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;2:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;()&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;3:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;tr[id^=zz2_QuickLaunchMenun]&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;)&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;4:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.next(&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;[id=]&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;).toggle()&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;5:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.prev().click(&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;(evt)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;6:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&lt;span style='color:#000084; font-weight:bold; '&gt;this&lt;/span&gt;).next().toggle();&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;7:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;evt.preventDefault();&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;8:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;9:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;10:&amp;nbsp;&lt;/span&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This function will install a handler to be run once the DOM tree is loaded and ready for manipulation: the "$(function(){})" syntax. This handler will, noted by line number:&lt;dl&gt;&lt;dt&gt;3&lt;br /&gt;&lt;dd&gt;Select all tr nodes with an id beginning with "zz2_QuickLaunchMenun". Hence forth called "main items".&lt;br /&gt;&lt;dt&gt;4&lt;br /&gt;&lt;dd&gt;For each of these main items, select the next sibling, if it has an empty id attribute. The empty id is an important point, as we don't want one main item to collapse the following main item, in the case of there being no child menu between the two. If there's no child menu, lines 5-8 will do nothing more for this main item. In this case, the selector from line 3 will bring us to the next main item instead.&lt;br /&gt;&lt;dt&gt;4&lt;br /&gt;&lt;dd&gt;Toggle (collapse) the child menu.&lt;br /&gt;&lt;dt&gt;5&lt;br /&gt;&lt;dd&gt;Select the sibling's previous item, which brings the selection state back to the main item&lt;br /&gt;&lt;dt&gt;5&lt;br /&gt;&lt;dd&gt;Install a click handler to the main item. &lt;br /&gt;&lt;dt&gt;6&lt;br /&gt;&lt;dd&gt;Use the toggle() function from jQuery to collapse or expand the sibling (child menu).&lt;br /&gt;&lt;dt&gt;7&lt;br /&gt;&lt;dd&gt;Simply prevents the click handler from bubbling further on to other nodes down the tree.&lt;/dl&gt;If jQuery is still new to you, you may just have noticed that jQuery selectors are cascading. Selecting one node, then doing another selector expression on that, will make a relative selection. That's how we, in the previous example, can move from the parent to the child, then back to the parent.&lt;br /&gt;&lt;br /&gt;Saving and reloading the page, should confirm that the above code is working. When you click the background of the main quick launch items, the section will collapse or expand.&lt;br /&gt;&lt;br /&gt;To wrap things up, we're going to add another piece of UI code, to animate the font size of the quick launch menu items, as they are mouse hovered. Expand / replace the function to resemble the following:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;1:&amp;nbsp;&lt;/span&gt;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;&amp;nbsp;onLoadComplete()&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;2:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;()&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;3:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;tr[id^=zz2_QuickLaunchMenun]&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;)&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;4:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.mouseenter(&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;(evt)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;5:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&lt;span style='color:#000084; font-weight:bold; '&gt;this&lt;/span&gt;).find(&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;a&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;).animate({&amp;nbsp;fontSize:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'14px'&lt;/span&gt;&amp;nbsp;});&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;6:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;})&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;7:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.mouseleave(&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;(evt)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;8:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&lt;span style='color:#000084; font-weight:bold; '&gt;this&lt;/span&gt;).find(&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;a&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;).animate({&amp;nbsp;fontSize:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'11px'&lt;/span&gt;&amp;nbsp;});&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;&amp;nbsp;9:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;})&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;10:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.next(&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;[id=]&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;).toggle()&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;11:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.prev().click(&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;(evt)&amp;nbsp;{&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;12:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&lt;span style='color:#000084; font-weight:bold; '&gt;this&lt;/span&gt;).next().toggle();&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;13:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;evt.preventDefault();&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;14:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;15:&amp;nbsp;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;br /&gt;&lt;span style='color:#4444FF; font-weight:bold; '&gt;&amp;nbsp;16:&amp;nbsp;&lt;/span&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Saving the script and reloading the page will actually uncover a bug in our previous selector, which wrongfully assumed that only the main items of the quick launch menu have the previously mentioned IDs. As it happens, sub items also employ this ID. While the click handler won't be affected (due to the sibling check), the mouseenter and mouseleave hover effect will be. Hovering the menu will now animate everything, while we want only the main items to be affected.&lt;br /&gt;&lt;br /&gt;Pulling FireBug back up real quick will show that the items we actually want to attach to, have a parent table with an ID of "zz2_QuickLaunchMenu". To fix the issue we can thus take advantage of a more specific jQuery selector syntax: &lt;span style="font-style:italic;"&gt;$("#zz2_QuickLaunchMenu &gt; tbody &gt; tr[id^=zz2_QuickLaunchMenun]")&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;This tells jQuery to look for a tr node with an id which starts with "zz2_QuickLaunchMenun", that has a direct parent element of type &lt;span style="font-style:italic;"&gt;tbody&lt;/span&gt;, which in turn has a direct parent element with id "zz2_QuickLaunchMenu". Saving and reloading the page will confirm that the hover effect now only affects the main menu items.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Where to go from here&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What I've demonstrated is a way to install and setup a hotpluggable SharePoint UI development environment. jQuery, and various other scripts, have vast possibilities for expanding SharePoints UI, and essential to exploring these, is a way to doing so quickly, without redeploying a bunch of stuff to the development environment (possibly polluting that, with nonsensical experiments).&lt;br /&gt;&lt;br /&gt;Once you're done with the prototyping, you would obviously have to deploy jQuery, or whatever scripts you use, to the portal. Either in shape of a master page change, a new aspx page, web parts page template or similar. How you should deploy it depends largely on what you're installing, and what you want it to override.&lt;br /&gt;&lt;br /&gt;There's no doubt in my mind that SharePoint development can benefit from such hotplug prototyping, however. Both the previously blogged about CodeConsole web part, and this simple prototyping environment, can - if used with care - simplify your life as a SharePoint developer or designer. Any opinions on how it could be done differently, are much obliged of course!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Downloads&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/ShKNSlsZlDI/AAAAAAAAEDY/FV7zAl1uZZ8/s1600-h/7.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 143px; height: 209px;" src="http://4.bp.blogspot.com/_rBg2qaJ_4Bw/ShKNSlsZlDI/AAAAAAAAEDY/FV7zAl1uZZ8/s320/7.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5337483858882958386" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://indev.no/MyProtoType.js.txt"&gt;MyProtoType.js&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The final example code, with a decent looking expander icon next to the collapsible rows.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-4949216344894668040?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/q3ZAuxNtXL4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/4949216344894668040/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=4949216344894668040" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/4949216344894668040?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/4949216344894668040?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/q3ZAuxNtXL4/quick-sharepoint-ui-prototyping-using.html" title="Effective SharePoint UI prototyping using jQuery, FireBug and Greasemonkey" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_rBg2qaJ_4Bw/ShFbA2UMnMI/AAAAAAAAECw/0EY83tSNpbo/s72-c/1.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/05/quick-sharepoint-ui-prototyping-using.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UNSH04eCp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-2027836855959522989</id><published>2009-05-18T13:51:00.004+02:00</published><updated>2009-09-17T14:08:19.330+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T14:08:19.330+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="Ajax" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><title>CodeConsole Minor Update</title><content type="html">The &lt;a href="http://einaros.blogspot.com/2009/05/codeconsole-web-part-for-sharepoint.html"&gt;CodeConsole Web Part&lt;/a&gt; just got a minor update, to allow simple inclusion of other assemblies and namespaces. This came in handy when I was debugging some SPContext-dependent functionality in a few libraries I deployed to a portal's bin dir.&lt;br /&gt;&lt;br /&gt;In the new version, the source / wsp of which is still available &lt;a href="http://indev.no/CodeConsoleWP.zip"&gt;here&lt;/a&gt;, the syntax is updated to support '@' prefixing for namespaces, and '#' for assemblies. Such as:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;@MyLibrary&lt;br /&gt;#Some.NameSpace.From.MyLibrary&lt;br /&gt;MyClass&amp;nbsp;foo&amp;nbsp;=&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;new&lt;/span&gt;&amp;nbsp;MyClass();&lt;br /&gt;output.Write(foo.SomeFunction());&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Where the first MyLibrary reference would be an assembly in e.g. the bin folder of the current web application, or the GAC. No .dll suffix required.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-2027836855959522989?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/4cFMvT579KA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/2027836855959522989/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=2027836855959522989" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/2027836855959522989?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/2027836855959522989?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/4cFMvT579KA/codeconsole-minor-update.html" title="CodeConsole Minor Update" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/05/codeconsole-minor-update.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUIHRHY6eyp7ImA9WxNQF00.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-7323466892726243536</id><published>2009-05-13T15:52:00.019+02:00</published><updated>2009-09-23T12:58:55.813+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-23T12:58:55.813+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="Ajax" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><title>CodeConsole Web Part for SharePoint</title><content type="html">&lt;span style="font-weight: bold;"&gt;About the CodeConsole Web Part&lt;/span&gt;&lt;br /&gt;&lt;a style="font-size: 8pt;" href="http://indev.no/CodeConsoleWP.zip"&gt;Download Source Code + WSP&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is a web part I've found myself wanting several times, when I've been either too lazy to compile and deploy code, or haven't had the development tools available on the target SharePoint box.&lt;br /&gt;&lt;br /&gt;In short, the CodeConsole Web Part ..&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Executes C# code, entered into a text area, on the server. The code can access the SharePoint object model, and other .NET / ASP.NET objects.&lt;/li&gt;&lt;li&gt;Displays output from the execution in an output text area.&lt;/li&gt;&lt;li&gt;Uses jQuery to make asynchronous requests to a custom .asmx service on the server.&lt;/li&gt;&lt;li&gt;Uses JSON to serialize / deserialize data, should more complex structures be required by someone who decides to use it.&lt;/li&gt;&lt;/ul&gt;A Screen Shot of the Web Part in action:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://indev.no/codeconsole.jpg"&gt;&lt;img src="http://indev.no/codeconsole.jpg" alt="CodeConsole Screen Shot" Width="500px" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size: 8pt; font-style: italic"&gt;Click the image for a larger version&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:8pt; font-weight: bold;"&gt;Update: Uploaded a demonstration screen capture video, in case a screen shot isn't enough. &lt;a href="http://www.screencast.com/users/einaros/folders/Jing/media/1142c4f7-a9b8-4f64-8310-06e4c8c9336c" target="_blank"&gt;Check it out&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In this shot, the input frame is white, and the output frame is black. The code passed through the input box will have the following using directives available to it by default:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;using System;&lt;/li&gt;&lt;li&gt;using System.IO;&lt;/li&gt;&lt;li&gt;using System.Text;&lt;/li&gt;&lt;li&gt;using Microsoft.SharePoint;&lt;/li&gt;&lt;/ul&gt;This can easily be extended in the attached code, otherwise you'll have to use fully qualified names in the code.&lt;br /&gt;&lt;br /&gt;For output redirection, the code entered will have an object called 'output' available. This is a System.IO.TextWriter, meaning it will have such functions as Write and WriteLine. The above example screen shot uses this output object, so turn to that if you're confused.&lt;br /&gt;&lt;br /&gt;Installing the web part is pretty straight forward, but will (due to the jQuery / JSON use) involve a few manual config steps. &lt;a href="http://einaros.blogspot.com/2009/05/enabling-json-serialization-and-http.html"&gt;See my previous blog post for more information on that&lt;/a&gt;. Within the source code package, there's a folder called "wsp", within which you'll find a precompiled solution file, which can be deployed to a working SharePoint 3.0 portal.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;About the implementation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The code isn't complex at all, and I'm sure many would point out that I could have done things a little bit differently here and there (such as rewriting how the web part is displayed, and taking optional configuration input to decide whether or not to link the json / jquery javascripts - which may not be necessary, if they've already been imported by another webpart, or in the master page).&lt;br /&gt;&lt;br /&gt;Essential files all reside in the TEMPLATE\FEATURES\CodeConsoleWP folder, where:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;CodeConsole.cs&lt;/span&gt; is the web part's source file. This emits all ui and javascript, including references to jQuery and JSON. Most essential of which is the following javascript. Yes, it's simple. Feel free to extend it. I'm sure intellisense would be a nice addition ;)&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: jscript;"&gt;&lt;br /&gt;$(function(e){&lt;br /&gt;    var input = $("#CodeConsole_input");&lt;br /&gt;    var output = $("#CodeConsole_output");&lt;br /&gt;    $("#CodeConsole_execute").click(function(be) {&lt;br /&gt;        output.text("Compiling, please wait.");&lt;br /&gt;        var code = {'code' : input.text()};&lt;br /&gt;        var jsonStr = JSON.stringify(code);&lt;br /&gt;        $.ajax({&lt;br /&gt;            type: 'POST',&lt;br /&gt;            url: '/_vti_bin/CodeConsoleSVC.asmx/ExecuteCode',&lt;br /&gt;            data: jsonStr,&lt;br /&gt;            contentType: 'application/json; charset=utf-8',&lt;br /&gt;            dataType: 'json',&lt;br /&gt;            success: function(msg) { output.text(msg.d); },&lt;br /&gt;            error: function(xhr, msg) { output.text("Ajax Error:\n" + msg + "\n" + xhr.responseText); }&lt;br /&gt;        });&lt;br /&gt;    });&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;CodeConsoleSVC.cs&lt;/span&gt; is the web service source file, which basically looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: c-sharp;"&gt;&lt;br /&gt;namespace CodeConsoleWP&lt;br /&gt;{&lt;br /&gt;    using System;&lt;br /&gt;    using System.IO;&lt;br /&gt;    using System.Web.Script.Services;&lt;br /&gt;    using System.Web.Services;&lt;br /&gt;&lt;br /&gt;    [WebService(Namespace = "http://tempuri.org/")]&lt;br /&gt;    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]&lt;br /&gt;    [ScriptService]&lt;br /&gt;    public class CodeConsoleSVC : WebService&lt;br /&gt;    {&lt;br /&gt;        [WebMethod]&lt;br /&gt;        public string ExecuteCode(string code)&lt;br /&gt;        {&lt;br /&gt;            using (TextWriter stringWriter = new StringWriter())&lt;br /&gt;            {&lt;br /&gt;                try&lt;br /&gt;                {&lt;br /&gt;                    Dynamic.ExecuteCode(code, stringWriter,&lt;br /&gt;                                        new[] {"System", "System.Text", "System.IO", "Microsoft.SharePoint"});&lt;br /&gt;                    return stringWriter.ToString();&lt;br /&gt;                }&lt;br /&gt;                catch (Exception ex)&lt;br /&gt;                {&lt;br /&gt;                    return "Error:\n" + ex.Message;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Most interesting in the above web service, which will be deployed to the /_vti_bin/, along with other SharePoint services, is the call to Dynamic.ExecuteCode. This is a very simple class I typed up, which generates an assembly in-memory, referencing all assemblies of the hosting assembly (the web part, meaning SharePoint assemblies and such will be referenced), wrapping the code, executes it and returns an optional return value.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Dynamic.cs&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: c-sharp;"&gt;&lt;br /&gt;internal static class Dynamic&lt;br /&gt;{&lt;br /&gt;    public static object ExecuteCode(string code, TextWriter output, string[] namespaces)&lt;br /&gt;    {&lt;br /&gt;        using (CodeDomProvider provider = CodeDomProvider.CreateProvider("C#"))&lt;br /&gt;        {&lt;br /&gt;            var parameters = new CompilerParameters&lt;br /&gt;                                 {&lt;br /&gt;                                     GenerateExecutable = false,&lt;br /&gt;                                     IncludeDebugInformation = false,&lt;br /&gt;                                     MainClass = "temp",&lt;br /&gt;                                     GenerateInMemory = true&lt;br /&gt;                                 };&lt;br /&gt;&lt;br /&gt;            foreach (AssemblyName refasm in Assembly.GetExecutingAssembly().GetReferencedAssemblies())&lt;br /&gt;            {&lt;br /&gt;                parameters.ReferencedAssemblies.Add(Assembly.Load(refasm).Location);&lt;br /&gt;            }&lt;br /&gt;            string usingDirectives = "";&lt;br /&gt;            foreach (string ns in namespaces)&lt;br /&gt;            {&lt;br /&gt;                usingDirectives += "using " + ns + ";";&lt;br /&gt;            }&lt;br /&gt;            CompilerResults results = provider.CompileAssemblyFromSource(&lt;br /&gt;                parameters, &lt;br /&gt;                @"namespace Dynamic {" + &lt;br /&gt;                usingDirectives + &lt;br /&gt;                @"public class Code { public object Run(System.IO.TextWriter output) { " + &lt;br /&gt;                code + &lt;br /&gt;                "; return null; } } }");&lt;br /&gt;                &lt;br /&gt;            if (results.Errors.Count &amp;gt; 0)&lt;br /&gt;            {&lt;br /&gt;                var sb = new StringBuilder();&lt;br /&gt;                foreach (CompilerError error in results.Errors)&lt;br /&gt;                {&lt;br /&gt;                    sb.AppendLine(String.Format("{0},{1}: {2}", error.Line, error.Column, error.ErrorText));&lt;br /&gt;                }&lt;br /&gt;                throw new CompileException(sb.ToString());&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;                object obj = results.CompiledAssembly.CreateInstance("Dynamic.Code");&lt;br /&gt;                object ret = obj.GetType().InvokeMember("Run", BindingFlags.InvokeMethod, null, obj,&lt;br /&gt;                                                        new object[] {output});&lt;br /&gt;                return ret;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public class CompileException : Exception&lt;br /&gt;    {&lt;br /&gt;        public CompileException(string s)&lt;br /&gt;            : base(s)&lt;br /&gt;        {&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Compiling and installing&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Upon build, the source will be compiled, and put along with the service endpoint in a SharePoint solution file (wsp). This is taken care of by the build scripts in the DeploymentFiles folder. This file will end up in the CodeConsoleWP\wsp\ folder. To install the web part, just deploy this solution to the SharePoint server. Be sure to follow the instructions in my previous post, to add json/ajax support to SharePoint, though - otherwise the service call will fail miserably.&lt;br /&gt;&lt;br /&gt;If you've got any questions, drop me a note in the comments here, on twitter, or by email.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://indev.no/CodeConsoleWP.zip"&gt;Download Source Code + WSP&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-7323466892726243536?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/5vf4Gymjw2I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/7323466892726243536/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=7323466892726243536" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/7323466892726243536?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/7323466892726243536?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/5vf4Gymjw2I/codeconsole-web-part-for-sharepoint.html" title="CodeConsole Web Part for SharePoint" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/05/codeconsole-web-part-for-sharepoint.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UNSH04eSp7ImA9WxNQEUU.&quot;"><id>tag:blogger.com,1999:blog-1575476679976002386.post-8363282827946565759</id><published>2009-05-13T11:38:00.005+02:00</published><updated>2009-09-17T14:08:19.331+02:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-17T14:08:19.331+02:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".NET" /><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint" /><category scheme="http://www.blogger.com/atom/ns#" term="Ajax" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><title>Enabling JSON serialization and jQuery posts for SharePoint web services</title><content type="html">While making the finishing touches to a new web part I'm publishing here shortly, I found myself wanting to make JSON enabled jQuery AJAX requests to custom .asmx services I'd deploy to the _vti_bin service root of my SharePoint portal. The incentive is simple; jQuery is quite nice for quickly typing up dynamic UIs, and by connecting that to a service backend, I can do even more fun stuff (which will become clear in my next post). An alternative to jQuery would be to stick with ASP.NET Ajax, but to be honest, I find that a lot less comfortable, when my code is mostly client side (javascript), and I'm deploying it all to SharePoint.&lt;br /&gt;&lt;br /&gt;The overhead of using ASP.NET ajax for simple stuff like this is another story altogether, and while I don't have any specific numbers, I'm sure you'd see quite a bit more data on the wire (large scripts, xml encapsulation, etc.), compared to simple jQuery ajax post requests with JSON for data encapsulation. If you're interested in that part, throw in a few google searches - I'm sure it's been thoroughly covered elsewhere.&lt;br /&gt;&lt;br /&gt;Anyhoo, the point here is jQuery/JSON and whatnot, so if that's what you're after; keep reading.&lt;br /&gt;&lt;br /&gt;What I soon discovered after attempting to make simple $.ajax calls from jQuery, was that HttpPost isn't enabled for the _vti_bin folder. Trying to call /_vti_bin/MyService.asmx/SomeMethod will result in some less-than-intuitive error page (especially when seen through the deserialized error callback of jQuery's .ajax()). This is essentially caused by the default _vti_bin config not allowing HttpPost service calls. Your options are either to change the configuration, which I'll soon outline, or make SOAP envelope calls. I find the latter fairly tedious, having to construct fairly large XML structures in strings, rather than just calling an url like that of SomeMethod above.&lt;br /&gt;&lt;br /&gt;Fixing the config, to allow simple service call urls, is pretty simple. Open the web.config from your "Web Server Extensions\12\ISAPI" folder, and flick the "remove" keyword of HttpPost to "add". The system.web configuration should look like:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;system.web&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;webServices&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;protocols&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;remove&lt;/span&gt;&amp;nbsp;name=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;HttpGet&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;/&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;add&lt;/span&gt;&amp;nbsp;name=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;HttpPost&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;/&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;remove&lt;/span&gt;&amp;nbsp;name=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;HttpPostLocalhost&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;/&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;add&lt;/span&gt;&amp;nbsp;name=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;Documentation&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;/&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;&amp;lt;/&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;protocols&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;&amp;lt;/&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;webServices&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;customErrors&lt;/span&gt;&amp;nbsp;mode=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;On&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;/&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style='color:#7f0055; '&gt;&amp;lt;/&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;system.web&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;At this point, you'll be able to call the web services through HTTP Post requests, even from jQuery, such as:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;$.ajax({&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;type:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'POST'&lt;/span&gt;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;url:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'/_vti_bin/MyService.asmx/SomeMethod'&lt;/span&gt;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'{}'&lt;/span&gt;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;contentType:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'application/json;&amp;nbsp;charset=utf-8'&lt;/span&gt;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dataType:&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'json'&lt;/span&gt;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;success:&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;(msg)&amp;nbsp;{&amp;nbsp;alert(msg.d);&amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;error:&amp;nbsp;&lt;span style='color:#000084; font-weight:bold; '&gt;function&lt;/span&gt;(xhr,&amp;nbsp;msg){&amp;nbsp;alert(msg&amp;nbsp;+&amp;nbsp;&lt;span style='color:#0000ff; '&gt;'&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;\n&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;'&lt;/span&gt;&amp;nbsp;+&amp;nbsp;xhr.responseText);&amp;nbsp;}&lt;br /&gt;});&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Still there's the case of JSON, however, so the JavaScript above would still trigger the error callback, with your web service's return value encapsulated in XML. Should you wish to use XML for data transport, you'd be all set now.&lt;br /&gt;&lt;br /&gt;To enable JSON, you'll first of all need the .NET 3.5 framework installed on the server. If you haven't already done this (2008 should have it pre-installed), go do so now. Once that's in place, there's a few quick changes which need to be made to your web application's web.config - not dissimilar to the general steps to make ASP.NET Ajax working.&lt;br /&gt;&lt;br /&gt;Within &amp;lt;system.web&amp;gt;&amp;lt;httphandlers&amp;gt; tag, add the following line, right after the initial &amp;lt;remove&amp;gt;:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;add&lt;/span&gt;&amp;nbsp;verb=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;POST&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;path=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;*.asmx&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;validate=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;false&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;type=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;System.Web.Script.Services.ScriptHandlerFactory,&amp;nbsp;System.Web.Extensions,&amp;nbsp;Version=3.5.0.0,&amp;nbsp;Culture=neutral,&amp;nbsp;PublicKeyToken=31BF3856AD364E35&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;/&gt;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Within the &amp;lt;safecontrols&amp;gt; tag, add the following at the bottom:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style='color:#7f0055; '&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#7f0055; '&gt;SafeControl&lt;/span&gt;&amp;nbsp;Assembly=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;System.Web.Extensions,&amp;nbsp;Version=3.5.0.0,&amp;nbsp;Culture=neutral,&amp;nbsp;PublicKeyToken=31BF3856AD364E35&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;Namespace=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;System.Web.UI&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;TypeName=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;*&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;Safe=&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;True&lt;/span&gt;&lt;span style='color:#0000ff; '&gt;"&lt;/span&gt;&amp;nbsp;&lt;span style='color:#7f0055; '&gt;/&gt;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This should pretty much be all you need, and you should be all set for using jQuery and JSON to query your own custom _vti_bin deplyed services, from web parts, pages and so forth.&lt;br /&gt;&lt;br /&gt;My next post will provide an example of this combination, calling a custom .asmx service from jQuery, with JSON serialization for parameters / return value, neatly wrapped up in a WSP.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1575476679976002386-8363282827946565759?l=www.codefornuts.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/uykS/~4/8Gty3__evdU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.codefornuts.com/feeds/8363282827946565759/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1575476679976002386&amp;postID=8363282827946565759" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/8363282827946565759?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1575476679976002386/posts/default/8363282827946565759?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/uykS/~3/8Gty3__evdU/enabling-json-serialization-and-http.html" title="Enabling JSON serialization and jQuery posts for SharePoint web services" /><author><name>Einar Otto Stangvik</name><uri>http://www.blogger.com/profile/14646027402573050232</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="13642993560347940791" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.codefornuts.com/2009/05/enabling-json-serialization-and-http.html</feedburner:origLink></entry></feed>
