Sharepoint - Enforce Drag and Drop documents to apply metadata

I came across this while searching and figured I would add my thoughts. I have also been looking for a way to tackle this problem.

The DragDrop.js file in Layouts has a function called UploadProgressFunc which is essentially just a switch statement. One of the options on that switch is uploaded. This is the function to tap into if you need to handle this differently.

function UploadProgressFunc(percentDone, timeElapsed, state) {
    state.percentDone = percentDone;
    var messageType = ProgressMessage.EMPTY;

    switch (state.status) {
        case 1:
            messageType = ProgressMessage.VALIDATION;
            break;
        case 3:
            messageType = ProgressMessage.UPLOADING;
            break;
        case 4:
            messageType = ProgressMessage.UPLOADED;
            MyNewFunction();
            break;
        case 5:
            messageType = ProgressMessage.CANCELLED;
            break;
    }
    UpdateProgressBar(messageType, state);
}

I created a separate js file called DragDrop.Extended.js, it has a copy of the UploadProgressFunc function which simply calls into a new function after setting the messageType on case 4.

This is to use Javascripts behaviour of overwriting existing functions with the new one when a duplicate loads. Doing so means I do not have to edit any OOTB files. I can just run this snippet of js in my master page.

window.onload = function(){
        setTimeout( function(){
            if(typeof UploadProgressFunc != 'undefined'){
                $.getScript("path_to_my_custom_js_file/DragDrop.Extended.js");
            }
        }, 4000);
 };

Now as it is on master page it does run for ever page. I am hoping to eventually get a better fix. The code checks if the dragdrop.js file has loaded by looking to see if the function is undefined. If it has loaded then it also loads in my file to overwrite that function.

The new function being used currently only handles 1 file at a time, I am working on making it handle multiple files at present. The new function called uses the JSOM to get information about the uploaded file. It then creates a call to to the list to retrieve the ID of the uploaded file. This is needed as it never bring that ID down to the context during upload.

With the ID and the details available in the context I am able to construct the EditForm URL for that item. I then just pass that EditForm URL into a SP Dialog.

var options = SP.UI.$create_DialogOptions();
options.title = "Add File Metadata";
options.url = editFormAddress;
options.autoSize = true;

SP.UI.ModalDialog.showModalDialog(options);

With all of this in place, after a user drags an item on to the document library, a dialog box then appears for them to add in the Metadata. It is a starting point for now. I am currently looking at making this work for multiple files and looking at a more elegant solution for loading the overwriting function script.

//edit

To get the ID(s) of uploaded files I used JSOM and queried the document library with a CAML query. You can get the library name from the context and the current users username. With those details I just search files uploaded by the user in the last few minutes.I then built a JS array of edit urls.

I then loaded in custom buttons to the modal using javascript, they cycled back and forward through the array of edit URLs built with the query results. I had to override the submit function too otherwise it closed the window after saving one. I removed the edit URLs from array after each save.

I no longer work on that project so apologies for lack of code sample in the edit.


Here's a refectored extended version of the code found in the other answers.

Features are:

  • Open EditForm after Drag&Drop
  • Open EditForm for multiple files

(function (_window) {
    var maxTimeForReplaceUploadProgressFunc = 10000;
    function replaceUploadProgressFunc() {
        if (typeof _window.UploadProgressFunc != 'undefined') {
            _window.Base_UploadProgressFunc = _window.UploadProgressFunc;
            _window.UploadProgressFunc = Custom_UploadProgressFunc;
            console.log('replaced dialog');
        } else if (maxTimeForReplaceUploadProgressFunc > 0) {
            maxTimeForReplaceUploadProgressFunc -= 100;
            setTimeout(replaceUploadProgressFunc, 100);
        }
    }
    setTimeout(replaceUploadProgressFunc, 100);


    function Custom_UploadProgressFunc(percentDone, timeElapsed, state) {
        _window.Base_UploadProgressFunc(percentDone, timeElapsed, state);
        var messageType = ProgressMessage.EMPTY;
        switch (state.status) {
            case 1:
                messageType = ProgressMessage.VALIDATION;
                break;
            case 3:
                messageType = ProgressMessage.UPLOADING;
                break;
            case 4:
                messageType = ProgressMessage.UPLOADED;
                OpenEditFormForLastItem(state);
                break;
            case 5:
                messageType = ProgressMessage.CANCELLED;
                break;
        }

        function OpenEditFormForLastItem(state) {
            var caml = '';
            caml += "<View>";
            caml += "<Query>";
            caml += "<Where>";

            if (state.files.length > 1) {
                caml += "<In>";
                caml += "<FieldRef Name='FileLeafRef'/>";
                caml += "<Values>";
            } else {
                caml += "<Eq>";
                caml += "<FieldRef Name='FileLeafRef'/>";
            }

            state.files.forEach(function (file) {
                //only succesfull uploaded files that arent overwrites
                console.log(file);
                if (file.status === 5 /*&& !file.overwrite*/) {
                    caml += "<Value Type='File'>" + file.fileName + "</Value>";
                }
            }, this);

            if (state.files.length > 1) {
                caml += "</Values>";
                caml += "</In>";
            } else {
                caml += "</Eq>";
            }

            caml += "</Where>";
            caml += "<OrderBy><FieldRef Name='ID' Ascending='True' /></OrderBy>";
            caml += "</Query>";
            caml += "<ViewFields><FieldRef Name='ID' /></ViewFields>";
            caml += "<RowLimit>500</RowLimit>";
            caml += "</View>";
            console.log(caml);

            var cntxt = SP.ClientContext.get_current();
            var web = cntxt.get_web();
            var list = web.get_lists().getByTitle(window.ctx.ListTitle);
            var query = new SP.CamlQuery();
            query.set_viewXml(caml);
            var items = list.getItems(query);
            cntxt.load(list, 'DefaultEditFormUrl');
            cntxt.load(items);
            cntxt.executeQueryAsync(function () {
                var listEnumerator = items.getEnumerator();
                function openEditForItem() {
                    if (listEnumerator.moveNext()) {
                        var item = listEnumerator.get_current();
                        var id = item.get_id();

                        var options = SP.UI.$create_DialogOptions();
                        options.title = "Add File Metadata";
                        options.url = list.get_defaultEditFormUrl() + '?ID=' + id;
                        options.autoSize = true;
                        options.dialogReturnValueCallback = openEditForItem;
                        SP.UI.ModalDialog.showModalDialog(options);
                    } else {
                        location.reload();
                    }
                }
                openEditForItem();
            }, function (error, args) {
                    console.log("failed to get new uploaded items");
                    console.log(error);
                    console.log(args);
                });
        }
    }
})(window);

This has been an issue for a long time, multiple file upload has the same problem in that you cannot apply metadata to items when uploading more than one at a time. The work around is to make the desired metadata fields required. This forces the files to remain checked out to the uploader until the required fields are populated and checked in.

You could possibly utilize jQuery UI to hook into the drop event and then look at the page to get the proper ID then use web service updates to do a bulk metadata update of the files. I haven't seen any examples of this in the wild, but it sounds feasible in theory.