JavaScript

How to Stream Files between Site Collections and Sites in SharePoint with JavaScript

Some time ago I was working on a client-side migration tool to use in SharePoint Online – to copy files and their metadata between libraries both within the same site collection, and into different site collections. It turned out to be a pretty difficult problem – but was solved in the end by using the browser itself as a lifeboat to carry the data between the site collections. If you’re reading this post you probably already looked at the Copy webservice, and “CopyIntoItemsLocal”, which only works when the source and destination are in the same site collection (and causes trouble by populating the hidden “copysource” metadata of the file in the destination file).

Anyway…

Along the way, I discovered a very nice jQuery extension called SPServices, which neatly wrapped up some of the SharePoint API commands, and made the development much more straightforward than it might otherwise have been. The snippet below uses SPServices to load a document within SharePoint into the browser’s memory, and then to write it back into a file elsewhere in SharePoint.

var source_file_url = "https://server/sites/site_collection_a/library_a/document_a.docx";
var destination_file_url = "https://sites/site_collection_b/library_b/document_b.docx";

setTimeout(function() {
    
    var streamLength = 0;
    
    // Read the SourceFileURL into memory
    $().SPServices({
        operation: "GetItem",
        Url: source_file_url,
        async: false,
        completefunc: function (xData, Status) {
            itemstream = $(xData.responseXML).find("Stream").text();
            streamLength = itemstream.length;
            itemfields = "";
            $(xData.responseXML).find("FieldInformation").each(function(){
                itemfields+=$(this).get(0).xml;
            });
        }
    });
    
    if (streamLength) {
    
        // Write the data back into the DestinationFileURL
        $().SPServices({
            operation: "CopyIntoItems",
            SourceUrl: source_file_url,
            async: false,
            DestinationUrls: [destination_file_url],
            Stream: itemstream,
            Fields:itemfields,
            completefunc: function (xData, Status) {
                // file has copied at this point
            }
        });
    }
    else {
        // failed to find list content type
    }
}, 0);

It’s worth noting that at the point the file has been copied, you probably still need to do some work around setting content types (if that is important to you) – I will cover this when I get a chance to write it up, because it’s NOT trivial (especially doing everything on the client-side).

It’s also worth noting that the snippet above is wrapped in a setTimeout call – a handy trick I discovered with asynchronous calls in JavaScript to make sure the code within completes before anything else happens. Kind of a cheap alternative to promises.

Posted by Jonathan Beckett in Notes, 0 comments

The Internet Explorer jQuery Change Event Puzzle

While working on a huge user interface development project some time ago, I came across some strange behavior from Internet Explorer that I thought might be worth sharing. In simple terms, it seems the .change jQuery event handler does not work consistently across all browsers, all environments, and all situations.

The user interface code I had written did something along the lines of the following:

$(".container").append("<input id='field_a' type='text' />");
$("#field_a").change(function(){
  // do something
});

Looks pretty straightforward, doesn’t it. It worked in Chrome, Firefox, Internet Explorer (on my work laptop), and even on the version of Chrome installed within Raspbian on a Raspberry Pi. It didn’t work in some cases on laptops used by the client on-site.

I started digging, and eventually found some similar conversations at StackOverflow. I resolved the issue by changing my code to look like this:

$(".container").append("<input id='field_a' type='text' />");
$("#field_a").on("change",function(){
  // do something
});

The only different is the use of on(), rather than change() directly. The interesting thing about this is the jQuery documentation specifically states that change() is shorthand for on(). I beg to differ. Something is different about it – I haven’t had time to look into the source of jQuery yet, but would be interested to find out if anybody else has ever seen this.

Posted by Jonathan Beckett in Notes, 0 comments

Updating SharePoint List Items with JavaScript

This is a simple example of updating a SharePoint list item via the Javascript CSOM API (I never quite know what to call it – Microsoft vary in their own naming of things).

The basic idea might be that this code would be called from a page within SharePoint, so is able to pick up the client context, and run the code. It will obviously fail if the user has no permissions to update the item in question.

function update_list_item(list_name, id, internal_field_name, value)
{
    // get connection to SharePoint
    var context = new SP.ClientContext.get_current();

    // get the current sharepoint site
    var web = context.get_web();

    // get a list from the site
    var list = web.get_lists().getByTitle(list_name);

    // get an item from the list
    var list_item = list.getItemById(id);

    // populate a property of the list item
    list_item.set_item(internal_field_name, value);

    // force sharepoint to save the change
    list_item.update();

    // tell SharePoint to do everything we just talked about
    context.executeQueryAsync(update_list_item_success, update_list_item_failure);
	
    return false;
}

function update_list_item_success(sender, args)
{
    alert("UpdateListItem Succeeded");
}

function update_list_item_failure(sender, args)
{
    alert("UpdateListItem Failed. \n" + args.get_message() + "\n" + args.get_stackTrace());
}

It’s probably worth making a few comments about updating different field types. The two standout ones that always cause trouble are URLs and Dates.

When updating a URL field, you can set the URL and label at the same time – by seperating them with – e.g. “https://google.com, Google”.

When updating a date field, SharePoint will expect the date in a specific format if you’re sending it as text. The easiest way around this without reading up on the ISO 8061 date format that SharePoint expects is just to pass it a Javascript Date object instead of text, and it will be quite happy.

Posted by Jonathan Beckett in Notes, 0 comments