Get folder from document library as SP.ListItem

One of the recent requests was to get folder from document library and read additional properties added to folder content type on that list (only JavaScript is implied). I was starting to work on it, believing this is an easy task. But surprise, all I could find were methods to get folder but as SP.Folder, but nothing to access it as SP.ListItem. Also, no cast between these objects exists, or, if exists, I am not aware about it.

Being very clear I need different approach, I started to build my own code for this. Below I will explain it.

I always prefer to store information about SP.Context , site and web in a single place, to avoid initialization of the objects every time I need to perform a simple operation. So I created a class to store all these properties.

/// <Reference Name="MicrosoftAjax.js" />
/// <Reference Name="MicrosoftAjaxWebForms" />

Type.registerNamespace("Shp");

Shp.Info = function () {
    /// <summary>Static class which contains information about web and lists.</summary>
}

Shp.Info.context = null;
Shp.Info.web = null;
Shp.Info.lists = null;
Shp.Info.propertyBags = null;

Shp.Info.isInitialized = function () {
    ///<summary>Determine if Info class was initialized.</summary>
    return !((Shp.Info.context === null) || (Shp.Info.web === null) || (Shp.Info.lists === null) || (Shp.Info.propertyBags = null));
}

Shp.Info.getPropertyBag = function (propName) {
    ///<summary>Get site property bag.</summary>
    ///<param name="propName">Property bag</param>
    var e = Function._validateParams(arguments, [{ name: 'propName', type: String, mayBeNull: false, optional: false }], false);
    if (e) throw Error.create("Shp.Info.getPropertyBag was called with invalid parameter.");

    // Determint is Shp.Info was initialized on site level
    if (Shp.Info.propertyBags === null) {
        throw Error.create("Shp.Info was not initialized as root.");
    }

    for (_propertyBag in Shp.Info.propertyBags.get_fieldValues()) {
        if (_propertyBag === propName) {
            return Shp.Info.propertyBags.get_fieldValues()[_propertyBag];
        }
    }

    return "";
}


Shp.Info.Initialize = function (callback, args) {
    /// <summary>Initialize Shp.Info class for current web</summary>
    /// <param name="callback">Function to be passed a success callback operation</param>
    /// <param name="args">Array of additional for callback.</param>
    var e = Function._validateParams(arguments, [{ name: 'callback', type: Function, mayBeNull: false, optional: false },
                                                 { name: 'args', type: Array, mayBeNull: true, optional: true }], false);
    if (e) throw Error.create("Shp.Info.Initialize was called with invalid parameters.");

    var _cArgs = (typeof (arguments) === "undefined") ? ([]) : (args);

    Shp.Info.propertyBags = null;
    Shp.Info.context = SP.ClientContext.get_current();
    Shp.Info.web = Shp.Info.context.get_web();
    var webLists = Shp.Info.web.get_lists();
    Shp.Info.context.load(webLists);

    var fail = function (args, message) {
        alert('Cannot get web information: ' + args.get_message() + '\n' + args.get_stackTrace());
    };

    var success = function () {
        Shp.Info.lists = webLists.$2_1;
        if ((typeof (callback) !== "undefined") && (callback !== null)) callback.apply(null, _cArgs);
    };

    Shp.Info.context.executeQueryAsync(success, fail);
}


Shp.Info.InitializeAsRoot = function (callback) {
    /// <summary>Initialize Shp.Info class for top level site</summary>
    /// <param name="callback">Function to be passed a success callback operation</param>
    var e = Function._validateParams(arguments, [{ name: 'callback', type: Function, mayBeNull: false, optional: false }], false);
    if (e) throw Error.create("Shp.Info.Initialize was called with invalid parameter.");

    Shp.Info.context = SP.ClientContext.get_current();
    Shp.Info.web = Shp.Info.context.get_site().get_rootWeb();
    var webLists = Shp.Info.web.get_lists();
    var webProperties = Shp.Info.web.get_allProperties();
    Shp.Info.context.load(webLists);
    Shp.Info.context.load(webProperties);

    Shp.Info.context.executeQueryAsync(function () {
        Shp.Info.lists = webLists.$2_1;
        Shp.Info.propertyBags = webProperties;
        callback();
    },
        function (source, args) {
            alert('Cannot initialize root web: ' + args.get_message() + '\n' + args.get_stackTrace());
        }
    );
}


Shp.Info._get_documentLibrary = function (docLibName) {
    /// <summary>Get document library from initalized web (internal use).</summary>
    /// <param name="docLibName">Document library title</param>
    var e = Function._validateParams(arguments, [{ name: 'docLibName', type: String, mayBeNull: false, optional: false }], false);
    if (e) throw Error.create("Shp.Info._get_documentLibrary was called with invalid parameter.");

    for (var i = 0; i < Shp.Info.lists.length; i++) {
        if ((Shp.Info.lists[i].get_baseType() === 1) && (Shp.Info.lists[i].get_title() === docLibName)) {
            return Shp.Info.lists[i];
        }
    }
    return null;
}

Shp.Info.registerClass("Shp.Info");

I could probably improve the code, but you got the idea. I can initialize properties for current web or for site collection web, store them in the properties of the class and keep them for later use. I do not insist on this, as this is not the scope of this article.

I also created a class to include built-in SP.ListItem and refine it. Is an optional choice, but I am not 100% happy with SP.ListItem class. For example I prefer to have an empty string returned by get_item method if field is not found in the collection or is null.


Shp.ListItem = function (listItem) {
    this.baseItem = listItem;
}

Shp.ListItem.prototype = {

    check_field: function (fldName) {
        var _fields = this.baseItem.get_fieldValues();
        return !(typeof (_fields[fldName]) === 'undefined');
    },

    get_item: function (fldName) {
        var _fldExist = this.check_field(fldName);
        var _fieldValue = ((_fldExist === false) || (this.baseItem.get_item(fldName) === null)) ? ('') : (this.baseItem.get_item(fldName));
        return _fieldValue;
    },

    get_baseItem: function () {
        return this.baseItem;
    }
}

Shp.ListItem.registerClass("Shp.ListItem");

And here is the code for getting the folder as SP.ListItem.

Shp.ListItemsOps = function () {
    /// <summary>SharePoint ListItemsOps custom static class.</summary>
}


Shp.ListItemsOps.get_folder = function (documentLibrary, folderUrl, callbackSuccess, callbackFailed) {
    /// <summary>Get parent folder</summary>
    /// <param name="callbackSuccess" type="Function" mayBeNull="false" optional="false"></param>
    /// <param name="callbackFailed" type="String" mayBeNull="true" optional="true"></param>

    var e = Function._validateParams(arguments, [{ name: 'documentLibrary', type: String, mayBeNull: false, optional: false },
                                                 { name: 'folderUrl', type: String, mayBeNull: false, optional: false },
                                                 { name: 'callbackSuccess', type: Function, mayBeNull: false, optional: false },
                                                 { name: 'callbackFailed', type: Function, mayBeNull: true, optional: true }], false);
    if (e) throw Error.create("Shp.ListItemsOps.get_folder was called with invalid parameters.");



    // Define get  folder operation (closure)
    var _getFolder = function () {

        // Get the document library
        var _docLib = Shp.Info._get_documentLibrary(documentLibrary);
        if (_docLib === null) throw Error.create(documentLibrary + ' could not be found in the site document libraries collection');

        // Build CAML query 
        var query = new SP.CamlQuery();
        query.set_viewXml("<View Scope='RecursiveAll'> ><Query><Where><And><Eq><FieldRef Name='FileRef'/><Value Type='URL'>" + folderUrl + "</Value></Eq><Eq><FieldRef Name='FSObjType'/><Value Type='Integer'>1</Value></Eq></And></Where></Query></View>");

        // Get items based on CAML
        var _results = _docLib.getItems(query);
        Shp.Info.context.load(_results);


        // Execute async
        Shp.Info.context.executeQueryAsync(function () {
            if (_results.get_count() > 0) {
                callbackSuccess(new Shp.ListItem(_results.itemAt(0)));
            }
            else {
                callbackSuccess(null);
            }
        }, function (source, args) {
            if (typeof (callbackFailed) === 'undefined') {
                alert('Cannot get folder information: ' + args.get_message());
            }
            else {
                callbackFailed(source, args);
            }
        });
    };

    // If Info class is not initialized, then initialize first and execute the operation, if not execute operation directly
    if (Shp.Info.isInitialized() === false) {
        Shp.Info.Initialize(_getFolder);
    }
    else {
        _getFolder();
    };
}
Shp.ListItemsOps.registerClass("Shp.ListItemsOps");

The CAML below did the entire trick. šŸ™‚

<View Scope='RecursiveAll'> ><Query><Where><And><Eq><FieldRef Name='FileRef'/><Value Type='URL'>" + folderUrl + "</Value></Eq><Eq><FieldRef Name='FSObjType'/><Value Type='Integer'>1</Value></Eq></And></Where></Query></View>

Try to adapt and apply the code to your situation. In case you have any ideas about how to improve it, please leave a comment.

Advertisements

Author: anvlpopescu

Nothing special to say. I'll think about it more and let your know.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.