Aug 20, 2010

SharePoint Scripts On Demand (SP.SOD)

When you are working with Ribbon UI extensions, Client OM or with complex SharePoint 2010 java scripts, you may noticed a Java Script class called SP.SOD. Built-in SharePoint scripts use this class extensively. Unfortunately SP.SOD (I guess it stands for SharePoint Scripts On Demand) is very rarely documented – until yet :)

SharePoint 2010 makes extensive use of java scripts. These scripts are located in several different files. And commonly these scripts have dependencies to functions and classes defined in other script files. Means the built-in SharePoint scripts and for sure your own scripts have to care about when which script will be loaded. And exactly this is the job of SP.SOD class! SP.SOD is defined in init.js and will be load in the HTML head section automatically.

ScriptLink

SP.SOD works tightly together with the ScriptLink server control. ScriptLink is a SharePoint server control for registering java script files.
<SharePoint:ScriptLink ID="scriptLink1" runat="server" Name="My_Script.js" LoadAfterUI="true" OnDemand="false" />

In the “Name” property you should enter the name of the script file. The control assumes that the script is located in the layout’s root folder. You could also use a relative URL instead of the script name, but cause problems with OnDemand=true. The intention is definitely to use the script name. Putting your scripts directly into the layouts folder without a solution/feature specific subfolder seems to be against the best practice, but is required! I recommend using solution/feature prefix for the files instead of a folder.

Localizable” true instructs the control to search the script in the language specific subfolders of the layout’s folder (e.g. _layouts/1033/MyScript.js).

LoadAfterUI” determines where the script reference will be rendered in the HTML document. False inserts the reference into the HTML head. True inserts the reference at the end of the HTML body.

OnDemand” true specifies that the script should be loaded on demand. False will render the following script code:

<script type="text/javascript">
// <![CDATA[
document.write('<script type="text/javascript" src="/_layouts/my_script.js"></' + 'script>');
// ]]>
</script>

True will render:
<script type="text/javascript">RegisterSod("my_script.js", "\u002f_layouts\u002fmy_script.js");</script>

As you see “true” doesn’t insert the script reference directly, instead it calls the function RegistedSod. RegisterSod is the same as SP.SOD.registerSod(key, url). The script will not be loaded until it get explicitly requested (see SP.SOD.executeFunc)!

Instead of using SPScriptLink in a declarative way to register scripts you could also do this with some static methods of SPScriptLink in the code behind:


SP.SOD.executeOrDelayUntilScriptLoaded

SP.SOD.executeOrDelayUntilScriptLoaded(func, scriptName) schedules an asynchronous callback function (func) which will be called when the script has signaled finished loading. Signaled finished loading means that the script has called notifyScriptLoadedAndExecuteWaitingJobs(scriptName). All SharePoint built-in scripts will call notifyScriptLoadedAndExecuteWaitingJobs when they have finished loading. ExcuteOrDelayUntilScriptLoaded does not trigger loading an on demand script (SOD)!

There is a very similar function pair called executeOrDelayUntilEventNotified/notifyEventAndExecuteWaitingJobs. The only diffrence is that  the executeOrDelayUntilScriptLoaded/executeOrDelayUntilScriptLoaded function pair prefixes internally the scriptName with “sp.scriptloaded-“.

Example 1:

My_ApplicationPage.aspx
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<SharePoint:ScriptLink ID="sl" runat="server" Name="My_Script.js" LoadAfterUI="true" OnDemand="false" />
<script type="text/javascript" language="javascript">

function myCallback() {
//sayHello is defined in MyScript.js
sayHello();
}

alert('1');

ExecuteOrDelayUntilScriptLoaded(myCallback, "my_script.js");

alert('2');

</script>
</asp:Content>

My_Script.js
alert('3');

function sayHello() {
alert('4');
}

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script.js");

We have defined a script link for My_Script.js which will be inserted at the end of the HTML body (LoadAfterUI=true). In the inline script in the head we have registered the function myCallback to get executed when MyScript.js has signaled finished loading. The alerts will popup in the sequence 1-2-3-4. You could also wait for any built-in script to have finished loading e.g. executeOrDelayUntilScriptLoaded(myCallback, “sp.ui.js”).

You could also use the static method ScriptLink.RegisterDelayedExecutionScript of SPScriptLink to register a delayed script execution (instead of the line: ExecuteOrDelayUntilScriptLoaded(myCallback, "my_script.js");

SP.SOD.registerSod

With SP.SOD.registerSod(key, url) you can manual register an on demand script via JavaScript.
<script type='text/javascript'>RegisterSod('my_script.js', '/_layouts/my_script.js'); </script>

SP.SOD.executeFunc

SP.SOD.executeFunc(key, functionName, fn) is used to load on demand scripts (ScriptLink.OnDemand=true).
The “key” parameter must match to the ScriptLink’s Name property (Use small letters for key, because an issue with string normalizing in RegisterSodDep).

functionName” awaits a type name of an ASP.NET AJAX JavaScript class. ExecuteFunc first checks if the AJAX class has already been registered, when not it checks additionally if a SOD with this key has already been loaded and finally it will load the SOD. Load means adding dynamically the corresponding script tag to the HTML head. The check for the type helps to ensure that the script has not been already loaded via an usual script tag before. When you don’t want to work with AJAX JavaScript classes you can use “null” value for the functionName.

fn defines a callback that will be executed when the SOD has signaled finished loading.

Example 2:

My_ApplicationPage.aspx
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<SharePoint:ScriptLink ID="sl" runat="server" Name="My_Script.js" LoadAfterUI="true" OnDemand="false" />
<!-- RegisterSod('my_script.js', '/_layouts/My_Script.js'); -->

<script type="text/javascript" language="javascript">

function myCallback() {
sayHello();       
}

alert('1');

SP.SOD.executeFunc("my_script.js", null, myCallback);

alert('2');

</script>
</asp:Content>

My_Script.js keeps unchanged.

The example is similar to example 1. The difference is that ScriptLink registers a SOD (ScriptLink.OnDemand=true) script. MyScript.js will not be loaded until it will be explicitly demanded with “SP.SOD.executeFunc(‘my_script.js’);”. When the SOD has signaled finished loading with “SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs(‘my_script.js’);”  myCallback will be executed. The alerts will popup in the sequence 1-2-3-4 again.

SP.SOD.registerSodDep

SP.SOD.registerSodDep(key, dep) can register SOD dependency. Key defines the SOD that depends on the SOD defined in dep. When the SOD in key will be requested, the SOD dependency will ensure that the SOD defined in dep will loaded before the actual SOD defined in key. You can also define a chain of dependencyies. This means the SOD in dep can also have dependencies, and the dependencies can have dependencies too and so on. The SOD loading mechanism keeps care to resolve all the required dependencies.

Example 3:

My_ApplicationPage.aspx
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<script type="text/javascript" language="javascript">
function myCallback() {
sayHello();       
}

alert('1');

function myInit(){
SP.SOD.executeFunc("my_script.js", null, myCallback);
alert('2');
}

_spBodyOnLoadFunctionNames.push("myInit");

RegisterSod('my_script.js', '/_layouts/My_Script.js');
RegisterSod('my_script2.js', '/_layouts/My_Script2.js');

RegisterSodDep('my_script.js', 'my_script2.js');

</script>
</asp:Content>


My_Script.js
alert('4');

function sayHello() {
alert('5');
sayHello2();
}

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script.js");
My_Script2.js
alert('3');

function sayHello2() {
alert('6');
}

SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("my_script2.js");

The example registers two SODs via JavaScript (you could do this with the ScriptLink control as well) and defines that my_script.js depends on my_script2.js. When my_script.js will be requested via “SP.SOD.executeFunc(‘my_script.js’, null, myCallback);” SP.SOD will first load my_script2.js and then my_script.js. The alerts will popup in the sequence 1-2-3-4-5-6.

Summary

The idea of on demand script loading make really sense. SharePoint 2010 loads really a lot of JavaScripts - this took time! So the idea is: first load the HTML and let it render by the browser so that the user is able to read the requested information as fast as possible. And in the second step load the behavior (the JavaScripts).

Jul 28, 2010

My Development Box

Here is the actual configuration of my development box. SharePoint and for sure .NET development works like a charm :)

Hardware

  • HP Elitebook 8540w
  • Processor: Intel Core i7 -820 Quad 1.73 GHz
  • RAM: 16 GB DDR3-SDRAM 1333 MHz (4x 4GB)
  • Display: 15.6” HD+ integrated 2 MP Webcam
  • Graphic: nVidia Quadro FX880M
  • HD: 500 GB SATA 7.200rpm
  • Connect: Intel 802.11 a/b/g/n , Bluetooth, UMTS

Software

For SharePoint demos and workshops I’m using the Contoso Hyper-V image. Because Contoso didn’t use setup best practices (e.g. admin service accounts) I use for development a from scratch installed SharePoint Hyper-V image.

Jul 18, 2010

User Defined Actions in Nintex Workflow 2010 (Domain Actions)

My favorite new feature in Nintex Workflow 2010 are User Defined Actions (UDA). An UDA is a kind of workflow template which can be used like any other Nintex action. More technically spoken it is an composite activity. Comfortable is that you can design them in the same easy way as an usual Nintex workflow. A technically minded workflow designer can create domain UDAs, maybe to encapsulate web service calls to a SAP system. And later on the business users can use them in their workflows without any technical background knowledge. Time for an example…

The “Send Tweet” User Defined Action

In this example we will create a UDA for posting Twitter status updates. Imagine we are an social marketing aware company and want to give our marketing team the ability to send tweets from Nintex workflows. They should be able to design the workflows themselves (usually approval workflows) and should not care about technical details. Perfect scenario for an UDA.

Site Actions > Nintex Workflow 2010 > Manage UDAs

image

Click “Create”

image

Start designing the UDA in the familiar Nintex WF designer

image

Define input and output parameters

image

Create an input parameter for the status update message (Message).

image

Drag an “Build dynamic string” action for building the URL and a “Web request” action for posting the update to the designer.

image

Configuration of the “Build Url…” action.

image  
http://api.twitter.com/1/statuses/update.xml?status={WorkflowVariable:Message}

The result variable “Url” can be created directly the config dialog via the “Variables” menu item in the ribbon.

Configuration of the “Post…” action.

image

You only need to configure Url, Username & Password and that the POST verb should be used.

Tip: Instead of defining the user credentials inline you can use a Nintex workflow constant of type credentials. The advantage is that you can change the credentials without changing and republishing the UDA. 

Configure UDA settings

image

The “Title” defines the display name of the action and the “Category” defines in which group it will be shown in the designer. Also nice is that you can define you own icons.

Publish the UDA

image

image

By default the UDA is scoped for the current team site, but you can promote it to the site collection or farm level. Additionally you can import and export UDAs.

Use the “Send tweet” action in a workflow 

image

The “Send tweet” action is now available in the “Twitter” group on the workflow actions menu on the left. Now the marketing team could easily use it within an approval action.

Configuration of “Send Tweet”

image  

The marketing team only have to provide the message for the Twitter status update and don’t care about any technical detail.

Test it

Start the workflow and approve the tweet.

image 

image 

You can easily encapsulate more Twitter API calls like this and build kind of a domain language. More ideas about Twitter and Nintex you can find on Markus Alt’s blog. Twitter is just a trivial example, you could also use this approach to design domain actions SAP or for any other system.