Tags

, , ,

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