jQuery

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