jQuery Promise syntax to wrap SharePoint SP.SOD

 

jQuery has a special function $.Deferred - which lets you create an Deferred object to build Promise(s).

We use this to simplify everything we do in SharePoint and other JavaScript libraries.

 

Wrapping SP.ClientContext

function GetCurrentUserName() {

var deferred = $.Deferred();
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var currentUser = web.get_currentUser();
ctx.load(currentUser);
ctx.executeQueryAsync( function(sender, args) {
    deferred.resolve();
}, function() {
    deferred.fail();
});

var promise = deferred.promise();
promise.done( function() {
    var title = currentUser.get_title();
});

return promise;
}

Wrapping SP.SOD

function SPLoaded() {

var deferred = $.Deferred();
SP.SOD.executeOrDelayUntilScriptLoaded( function() { deferred.resolve(); }, "sp.js");

return deferred.promise();

}

Resolving multiple promises

var promise1 = ...
var promise2 = ...
var promise3 = ...

$.when(promise1, promise2, promise3).done(function(){

// do something

});

 

Concatenating Arrays of promises

 

var promises = [];
promises.push(promise1);
promises.push(promise2);
...

// use this syntax when you don't know how many promises are there - may be calling REST in a loop.

return $.when.apply($, promises);

 

Combining Array of Promises and SP.SOD

 

function Ready() {

var promises = [];

var deferred1 = $.Deferred();
SP.SOD.executeOrDelayUntilScriptLoaded(deferred1.resolve, "sp.js");
promises.push(deferred1.promise());

var deferred2 = $.Deferred();
SP.SOD.executeOrDelayUntilScriptLoaded(deferred2.resolve, "sp.core.js");
promises.push(deferred2.promise());

 

return $.when.apply($, promises);

}

 

Combining promises

 

$(document).ready(function(){

    var vm = new ViewModel();  // not included in above script
    var promise = vm.Ready();
    promise.done( function() {
        vm.GetCurrentUserName();

    });

});

 

(Updated) And the grand finale

 

function Ready() {

var promises = [];

// using the special javascript argument "arguments"

$.each(arguments, function(index, arg) {
    var deferred = $.Deferred();
    SP.SOD.executeOrDelayUntilScriptLoaded(deferred.resolve, arg);
    promises.push(deferred.promise());
});

return $.when.apply($, promises);

}

 

$(document).ready(function(){

    var vm = new ViewModel();  // not included in above script
    var promise = vm.Ready("sp.js", "sp.core.js");
    promise.done( function() {
        vm.GetCurrentUserName();

    });

});

Nintex Workflow Inline Function to check if SPFile is locked

 

IsDocumentWritable

 

Nintex Workflow has a fairly useful function "IsDocumentWritable" that checks if the current item that the workflow is running on is writable.

There is a small problem, it only checks if the file is Checked Out (SPFile.CheckedOutType) and not if the file was locked, say by a Desktop Client Application.

 

Add Nintex Workflow Inline Function

We can add a simple Nintex Workflow inline function to get the behaviour we wanted:

I followed Vadim's excellent blog entry: http://www.vadimtabakman.com/nintex-workflow-developing-a-custom-inline-function.aspx

 

/*
* & 'C:\Program Files\Nintex\Nintex Workflow 2010\NWAdmin.exe' -o AddInlineFunction -functionalias "fn-IsFileLocked" -assembly "MY_DLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f7c41d4a6ea1fb3" -namespace "MYNamespace" -typename "MYInlineFunctions" -method "IsFileLocked" -description "Checks if file is locked." -usage "fn-IsFileLocked(itemPath)"
*/

public static bool IsFileLocked(string itemPath)
{
    bool result = false;
    try
    {
        SPSecurity.RunWithElevatedPrivileges(() =>
        {

            using (SPSite site = new SPSite(itemPath))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    SPFile file = web.GetFile(itemPath);
                    if (!file.Exists)
                    {
                        return;
                    }

                    // true if checked out

                    result = file.LockType != SPFile.SPLockType.None;
                    return;
                }
            }
        });
    }
    catch (Exception ex)
    {
    }
    return result;
}

 

You can call this method from within Nintex Workflow Designer.

image

Reading InfoPath template's default values in code

 

String xml = "";
FormTemplate template = this.Template;
using (Stream s = template.OpenFileFromPackage("template.xml"))
{
    XPathDocument reader = new XPathDocument(s);
    XPathNavigator nav = reader.CreateNavigator();
    XPathNavigator repeat = nav.SelectSingleNode("/my:myFields/my:Repeats/my:Repeat[1]", this.NamespaceManager);
    if (repeat == null)
    {
        return;
    }
    xml = tender.OuterXml;
}
if (!String.IsNullOrEmpty(xml))
{
    XPathNavigator destination = this.CreateNavigator().SelectSingleNode("/my:myFields/my:Repeats", this.NamespaceManager);
    destination.AppendChild(xml);
}

 

The top part of the code is particularly useful if you want to use the Default Values for repeating sections in InfoPath.  Your code will read the xml for the default values and insert them into the repeating section.  I've previously hardcoded these XML segments for insert, but that's extremely error prone when you inevitably update your XML template with new and more exciting child elements and attributes.

Wrap up: SharePoint Saturday Adelaide and Brisbane

 

There is always an relevant tweet.

Tomás Lázaro@tomzalt May 22 The book Javascript Ninja has a Samurai on the cover. That happens because JS is not strongly typed.

This is a Post-Event update post.

Adelaide

In Adelaide, I went at a good pace and gone through the TypeScript example, demos but had very little time remaining for discussions or questions.

The main feedback I got was perhaps there was too much time (still!) given to JavaScript and we can all spend more time in TypeScript and the demos.  Also, there was questions regarding deployment.

 

Brisbane

In Brisbane, I trimmed the JavaScript discussion but added a silly demo that got people laughing.  But possibly still ate my time.  I was not able to go through the sections on adding TypeScript to your existing JavaScript.  But I was able to cover the deployment scenarios for SharePoint 2010 and 2007.

 

Working with SP 2007

  • Editing:
    • Use Content Editor webpart and point to a HTML file, which then references a JavaScript file generated from TypeScript.
    • Use VS.NET 2012/2013 with WebDAV
  • Deploying:
    • Package as farm solution web part
    • Include map file to debug in IE12 / Chrome.

Working with SP 2010

  • Editing
    • You can use Content Editor as above
    • You can build VS.NET farm or sandbox solutions and use TypeScript directly
  • Deploying
    • Use Sandbox solution to deploy a sandbox webpart
    • Reference a JavaScript file generated from TypeScript
    • Package as sandbox solution

Deployment – SP 2013 / Office 365

  • Using App for SharePoint to deploy an App Part
  • Do not create code behind. Reference JavaScript file generated from TypeScript
  • Configure App permissions
  • Package as SharePoint “App”
  • When deploying – grant permissions to App

 

Download Links:

 

If you are using TypeScript in your environment, let me know and tell me how it is going for you.

SP2010 Forcing previously deployed file to update to latest version in site definition

 

I’m a big fan of quickly making changes to my SharePoint JavaScript file in SharePoint Designer and then test whether they work correctly in the browser.  Quick browser refresh and I’m testing.

But I don’t recommend this on your test or production environments.  For those cases, you should package your JavaScript files into a solution and deploy them.

SharePoint 2013 ReplaceContent Attribute

SP2013 introduced a new attribute ReplaceContent=”True” http://msdn.microsoft.com/en-us/library/office/ms459213(v=office.15).aspx when this attribute is set to True, when your feature is activated it will replace existing files on SharePoint with the contents of the files from the package.  This attribute is excellent for deploying JavaScript files.

 

SharePoint 2010 IgnoreIfAlreadyExists Attribute

In SP2010, you aren’t as lucky.  The IgnoreIfAlreadyExist attribute needs to be set to true.  Otherwise your feature will most likely fail to activate if a file already exists.  This is troublesome because with the ignore flag on, your JavaScript files won’t update to the latest new version.

There are a few approaches that people take.  Some chooses to delete all the assets when deactivating the feature.  Then on reactivation, the assets are recreated brand new with the latest bits.  This works fine for CSS, JavaScript files, but will not work for MasterPage or PageLayout files that are in use.

 

Add Reset to Site Definition

I propose this fantastic method:

SPFile.RevertContentStream();

http://msdn.microsoft.com/en-us/library/office/microsoft.sharepoint.spfile.revertcontentstream(v=office.15).aspx

This forces the SPFile to revert back to the site definition.  Essentially this is what SharePoint Designer does when you click on

image

 

When I deploy to production, but I know I need to bump 2 of my JavaScript files to the latest site definition, I run this powershell.

PS D:\> $web = Get-SPWeb http://server
PS D:\> $file = $web.GetFile("Style Library/app/John/john1.js")
PS D:\> $file.CustomizedPageStatus
Uncustomized
PS D:\> $file.RevertContentStream()
PS D:\> $file = $web.GetFile("Style Library/app/John/john2.js")
PS D:\> $file.CustomizedPageStatus
Uncustomized
PS D:\> $file.RevertContentStream()

 

This works whether the file is Uncustomized (deployed via a previous package) or Customized (deployed manually or updated by a user).  After the method is run, the file content will be the latest version that was in the deployed WSP package.

Try it out.  Quite useful for fixing 1 JavaScript file in a package.