Get list items created in current month

DateRangesOvelap might work when you are working with calendar list, but for sure is not working with custom lists. For this, you need to write your own CAML query and get the data based on it. Below I have a created a function to get the items and returns a promise object.

    function GetListItems(listName, caml) {
        let promise = new Promise(function (resolve, reject) {
            let ctx = SP.ClientContext.get_current();
            let oList = ctx.get_web().get_lists().getByTitle(listName);
            let camlQuery = new SP.CamlQuery();
            camlQuery.set_viewXml(caml);
            let oListItems = oList.getItems(camlQuery);
            ctx.load(oListItems);
            ctx.executeQueryAsync(function () {
                resolve(oListItems);
            }, function (sender, args) {
                reject(args.get_message());
            });
        });
        return promise;
    } 

Now let’s built the CAML query and call the function.

function getDaysInMonth(year, month) {
    // Get numbers of days in a month
    return new Date(year, month + 1, 0).getDate();
}

let currentDate = new Date();
let firstDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
let lastDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), getDaysInMonth(currentDate.getFullYear(), currentDate.getMonth()));

let caml = '<view>' +
    '<Query>' +
    '<Where>' +
    '<And>' +
    '<Geq><FieldRef Name="Created" /><Value Type="DateTime">' + firstDate.format('yyyy-MM-dd') + '</Value></Geq>' +
    '<Leq><FieldRef Name="Created" /><Value Type="DateTime">' + lastDate.format('yyyy-MM-dd') + '</Value></Leq>' +
    '</And>' +
    '</Where>' +
    '</Query>' +
    '</View > ';

GetListItems('List Name', caml).then(function (items) {
    // Do something with the items
}, function (err) {
    // Do something with error message
});

You can notice, is not complicated to achieve this using JS CSOM. Of course you can apply the filter on different date and time fields. The next article will be probably how to get the items for a specified week, as is more probably to be a goal in many projects.

Advertisements

Get data from multiple lists using Promise.all

I always had this question in mind, how can I optimize data operations using JavaScript CSOM. With some help from Promise object, this is possible. Below, I have written a small portion of code for getting data from lists and returning a promise object.

class $SPData {

    constructor() {
        throw '$SPList is a static class and cannot be instantiated';
    }

    /**
     * Get list items
     * @param {string} listName
     * @param {string} camlQuery
     * @param {string} webUrl
     */
    static GetListItems(listName, caml, webUrl) {
        let e = Function.validateParameters(arguments, [{ name: 'listName', type: String, mayBeNull: false, optional: false },
        { name: 'caml', type: String, mayBeNull: false, optional: false },
        { name: 'webUrl', type: String, mayBeNull: false, optional: true }], true);
        if (e) throw e;

        let promise = new Promise(function (resolve, reject) {
            let ctx = (arguments.length === 2) ? SP.ClientContext.get_current() : new SP.ClientContext(webUrl);
            let oList = ctx.get_web().get_lists().getByTitle(listName);
            let camlQuery = new SP.CamlQuery();
            camlQuery.set_viewXml(caml);
            let oListItems = oList.getItems(camlQuery);
            ctx.load(oListItems);
            ctx.executeQueryAsync(function () {
                resolve(oListItems);
            }, function (sender, args) {
                reject(args.get_message());
            });
        });
        return promise;
    }
}

Now, let’s see how you can use this in your code.

let getItemsFromListOne = $SPData.GetListItems('List One', '<View&gt;</View&gt;');
let getItemsFromListTwo = $SPData.GetListItems('List Two', '<View&gt;</View&gt;');
Promise.all([getItemsFromListOne, getItemsFromListTwo]).then(function (values) {
    let itemsFromListOne = values[0];
    let itemsFromListTwo = values[1];
});

This should simplify the way you are working with SharePoint data. You do not need to have wait for a call to finish to start another one. I have tested only to get data from the lists, but it can used also to insert and update list items also.

Adding image in master page (SharePoint apps model)

When I developing SharePoint apps, I prefer to use my custom master page, as the idea is to create responsive design. One of the issues I had was to how to add a logo or an image in the master page. The solution is still classic asp.net control called image.

<asp:Image runat="server" ImageUrl="../../Images/sp_logo.png" /&gt;

You can apply style on the image by setting CssClass property of the control, as well as you can use all other properties. See full documentation for this tag here: http:// https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.image?view=netframework-4.7.2

Create folder with properties using JavaScript CSOM

JavaScript CSOM is already on the market for a while. Introduces on version 2010, it can cover today a lot of scenarios. One of them is to create a folder in a document library with some other associated fields populated. This was one of my recent tasks. Code below should to do everything for you:

Type.registerNamespace('Shp');
        
        
        Shp.DocLib = function() {
            throw 'Cannot initiate Shp.DocLib static class';        
        }
        
        Shp.DocLib.createFolder = function(listName, folderName, fields, webUrl, success, fail) {
            var e = Function.validateParameters(arguments, [{ name: 'listName', type: String, optional: false, mayBeNull: false },
                                                            { name: 'folderName', type: String, optional: false, mayBeNull: false },
                                                            { name: 'fields', type: Object, optional: false, mayBeNull: false },
                                                            { name: 'webUrl', optional: false, mayBeNull: true },
                                                            { name: 'success', type: Function, optional: false, mayBeNull: false },
                                                            { name: 'fail', type: Function, optional: true, mayBeNull: false }], true);
           if(e) throw e;

   		  var fail = fail || function (err) { alert(err); };
          var ctx = (webUrl === null) ? SP.ClientContext.get_current() : new SP.ClientContext(webUrl);          
          Shp.DocLib._createFolder(listName, folderName, fields, ctx, success, fail);
        }
        
        Shp.DocLib._createFolder = function(listName, folderName, fields, ctx, success, fail) {
        
        	var oLib = ctx.get_web().get_lists().getByTitle(listName); 
        	var itemCreateInfo = new SP.ListItemCreationInformation();
        	itemCreateInfo.set_underlyingObjectType(SP.FileSystemObjectType.folder); 
        	itemCreateInfo.set_leafName(folderName);
        	var oFolder = oLib.addItem(itemCreateInfo);
        	for (var field in fields) {
                if (fields.hasOwnProperty(field) === true) {
                    oFolder.set_item(field, fields[field]);
                }
            };
            
     		oFolder.update();
            ctx.load(oFolder);     
            
			ctx.executeQueryAsync(function () {
                success(oFolder);
            }, function (sender, args) {
                fail(args.get_message());

            });			  
        }
        
        Shp.DocLib.registerClass('Shp.DocLib');

In order to use, you just need to provide right types of parameters, as a validation is performed.

Shp.DocLib.createFolder("List Name", "Folder Name", { "Field1": "some value", "Field2": "some value" }, "path to sharepoint",
    function (folder) {
        // Do something with the created folder
    }, function (err) {
        // Do something with the error message
    });

Web URL parameter can be null, if you want to use the client context for current web. Also, if you do not provide an error callback method, a simple alert with an error is displayed.

Attaching a file to a list item in SharePoint add-in model

Long time since I wrote about the SharePoint. Is still my job, I am still working on this, but I simply did not find the time to discover new things. Now, as SharePoint add-in model is more and more adopted in SharePoint development, due to the migration of large companies to Office 365, I am starting to face new challenges. One of this is how to attach a file to an existing list item.

<input type="file" id="attachment1" />

As HTML5 is the new standard for web, file control is able to read the content of a file as a binary string, so no more needed to server side languages. For rest, jQuery and SharePoint JavaScript libraries will do the work for you.


       Type.registerNamespace('Shp');

       Shp.Attachments = function () {
            /// <summary>Shp Attachments static class</summary>
            throw 'Cannot initiate Shp.Attachments static class';
        };

        Shp.Attachments.get_file = function (fileInput) {
            /// <summary>This method is used to get the content of the file as binary string</summmary>

            var deffered = jQuery.Deferred();
            var reader = new FileReader();
            reader.onload = function (e) { 
                deffered.resolve(e.target.result);
            };  

            reader.onerror = function (e) {
                deffered.reject(e.target.error);
            };

            reader.readAsBinaryString(fileInput.files[0]);
            return deffered.promise();
        };


        Shp.Attachments.add = function (listName, itemId, fileInput, webUrl, success, fail) {
            /// <summary>Add attachments</summary>
            /// <param>List name</param>
            /// <param>Item Id</param>
            /// <param>File input controls</param>
            /// <param>Web url</param>
            var e = Function.validateParameters(arguments, [{ name: 'listName', type: String, optional: false, mayBeNull: false },
                                                            { name: 'itemId', type: String, optional: false, mayBeNull: false },
                                                            { name: 'filesInput', type: HTMLElement, optional: false, mayBeNull: false },
                                                            { name: 'webUrl', type: String, optional: false, mayBeNull: true },
                                                            { name: 'success', type: Function, optional: false, mayBeNull: false },
                                                            { name: 'fail', type: Function, optional: true, mayBeNull: false }], true);
            if (e) throw e;


            var webUrl = webUrl || _spPageContextInfo.webAbsoluteUrl;
            var fail = fail || function (err) { alert(err); };

            Shp.Attachments.get_file(fileInput).then(function (fileContent) {
                var parts = fileInput.value.split("\\");
                var fileName = parts[parts.length - 1];
                // Attachments add internal method
                Shp.Attachments._add(listName, itemId, fileContent, fileName, webUrl, success, fail);
            });
        };

        Shp.Attachments._add = function (listName, itemId, fileContent, fileName, webUrl, success, fail) {

            var scriptBase = webUrl + "/_layouts/15/";
            jQuery.getScript(scriptBase + "SP.RequestExecutor.js", function () {

                var executor = new SP.RequestExecutor(webUrl);
                executor.executeAsync({
                    url: webUrl + "/_api/web/lists/GetByTitle('" + listName + "')/items(" + itemId + ")/AttachmentFiles/add(FileName='" + fileName + "')",
                    method: "POST",
                    binaryStringRequestBody: true,
                    body: fileContent,
                    state: "Update",
                    success: function () {
                        success(itemId);
                    },
                    fail: function (data) {
                        fail(data.responseText);
                    }

                });

            });
        };

        Shp.Attachments.registerClass('Shp.Attachments');

Once code is organized, attaching a file become an easy task.

 Shp.Attachments.add('Tickets', '2', document.getElementById('attachment1'), null, function (itemId) {
        alert(itemId);
    }, function (err) {
        alert('Error: ' + err);
    });

Shp.Attachments.add accepts following parameters:

  • List name as string
  • Item id as string
  • HTML file control as HTML element
  • Web URL. It can be nul and in this case is used _spPageContextInfo.webAbsoluteUrl
  • Success method to be executed if attachment is added
  • Fail method to be executed if attachment failed

Copy, adapt the code according to your needs and use it. Happy coding! If it helps, you can just say hi to me and I will be happy as well. 🙂

Send email using SharePoint API

I see all over examples about how to send emails using SharePoint API. I am not going to reinvent the code, but I am just organizing it into a little bit more object orientated way. And again, Microsoft Ajax library is here to help us. As always, first step is to create the namespace where you want to place the objects.

Type.registerNamespace('Shp.Utility');

Once I done it, I need to create an object containing email properties.

Shp.Utility.EmailProperties = function (to, from, subject, body) {
    /// <summary>Create an instance of Shp.Utility.EmailProperties
    /// <param name="to" type="String" mayBeNull="false" optional="false">To</param>
    /// <param name="from" type="String" mayBeNull="false" optional="false">From</param>
    /// <param name="subject" type="String" mayBeNull="false" optional="false">Email Subject</param>
    /// <param name="body" type="String" mayBeNull="false" optional="false">Email Body</param>
    var e = Function.validateParameters(arguments, [{ name: 'to', type: Array, elementType: String, elementMayBeNull: false, optional: false, mayBeNull: false },
                                                    { name: 'from', type: String, optional: false, mayBeNull: false },
                                                    { name: 'subject', type: String, optional: false, mayBeNull: false },
                                                    { name: 'body', type: String, optional: false, mayBeNull: false }], true);
    if (e) throw e;

    this.to = to;
    this.from = from;
    this.subject = subject;
    this.body = body;
    this.cc = [];
    this.bcc = [];
}

Shp.Utility.EmailProperties.prototype = {


    add_cc: function (cc) {
        /// <signature>
        ///     <summary>Add CC to email properties</summary>
        ///     <param name="cc" type="String" mayBeNull="false" optional="false">CC</param>
        ///     <returns type="Shp.Utility.EmailProperties"></returns>
        /// </signature>
        /// <signature>
        ///     <summary>Add CC to email properties</summary>
        ///      <param name="cc" type="Array" elementType="String" elementMayBeNull="false" mayBeNull="false" optional="false">CC</param>
        ///      <returns type="Shp.Utility.EmailProperties"></</returns>
        /// </signature>
        var e1 = Function.validateParameters(arguments, [{ name: 'cc', type: String, optional: false, mayBeNull: false }], true);
        var e2 = Function.validateParameters(arguments, [{ name: 'cc', type: Array, elementType: String, elementMayBeNull: false, optional: false, mayBeNull: false }], true);
        if (e1 != null && e2 != null) throw e1 || e2;

        var instance = this;
        if (e1 == null)
            instance.cc.push(cc);
        if (e2 == null)
            instance.cc = Array.clone(cc);

        return instance;
    },

    add_bcc: function (bcc) {
        /// <signature>
        ///     <summary>Add CC to email properties</summary>
        ///     <param name="bcc" type="String" mayBeNull="false" optional="false">CC</param>
        ///     <returnts type="Shp.Utility.EmailProperties" />
        /// </signature>
        /// <signature>
        ///     <summary>Add CC to email properties</summary>
        ///     <param name="bcc" type="Array" elementType="String" elementMayBeNull="false" mayBeNull="false" optional="false">CC</param>
        ///     <returnts type="Shp.Utility.EmailProperties" />
        /// </signature>
        var e1 = Function.validateParameters(arguments, [{ name: 'bcc', type: String, optional: false, mayBeNull: false }], true);
        var e2 = Function.validateParameters(arguments, [{ name: 'bcc', type: Array, elementType: String, elementMayBeNull: false, optional: false, mayBeNull: false }], true);
        if (e1 != null && e2 != null) throw e1 || e2;

        var instance = this;
        if (e1 == null)
            instance.bcc.push(bcc);
        if (e2 == null)
            instance.bcc = Array.clone(bcc);

        return instance;

    },

    GenerateRequestBody: function () {
        /// <summary>Generate Ajax request body</summary>
        var instance = this;
        var request = {
            'properties': {
                '__metadata': { 'type': 'SP.Utilities.EmailProperties' },
                'From': instance.from,
                'To': { 'results': instance.to },
                'CC': { 'results': instance.cc },
                'BCC': { 'results': instance.bcc },
                'Body': instance.body,
                'Subject': instance.subject
            }
        }
        return JSON.stringify(request);
    }

}

Shp.Utility.EmailProperties.registerClass('Shp.Utility.EmailProperties');

Here is actually the point where the code is simplified. Creating email properties object makes you write less code in the Ajax call. Generate request body method is using all necessary properties of the object to create string representation of JSON object to be used as the request body.

var from = 'someone@domain.com';
var body = 'Email body'; // Pay attention, you might need to encode it
var subject = 'Email subject';
var to = ['person1@domain.com', 'person2@domain.com'];

// Create email properties object
var emailProperties = new Shp.Utility.EmailProperties(to, from, subject, body);

// Add CC and BCC
emailProperties.add_cc(['person3@domain.com', 'person4@domain.com']);
emailProperties.add_bcc(['person3@domain.com', 'person4@domain.com']);

// Forgot to add someone, no worries 
// Passing a string instead of array of strings makes the method not to act like a setter. Instead is adding email to CC or BCC field.
emailProperties.add_cc('forgotten@domain.com');
emailProperties.add_bcc('forgotten@domain.com');

Now, as we have the object containing the email properties, is time to handle the Ajax call.


Shp.Utility.Email = function () {
    /// <summary>Shp.Utility.Email static class</summary>
    throw 'You cannot initialize an instance of Shp.Utility.Email static class';
}


Shp.Utility.Email.SendEmail = function (emailProperties, success, fail) {
    /// <summary>Send an email using SharePoint API</summary>
    /// <param name="emailProperties" type="Shp.Utility.EmailProperties" optional="false" mayBeNull="false">Email Properties</param>
    /// <param name="success" type="Function" optional="false" mayBeNull="false">Success</param>
    /// <param name="fail" type="Function" optional="true" mayBeNull="false">Fail</param>
    var e = Function.validateParameters(arguments, [{ name: 'emailProperties', type: Shp.Utility.EmailProperties, optional: false, mayBeNull: false },
                                                    { name: 'success', type: Function, optional: false, mayBeNull: false },
                                                    { name: 'fail', type: Function, optional: true, mayBeNull: false }], true);
    if (e) throw e;


    Shp.Utility.Email._SendEmail(emailProperties, success, fail || function (errCode, errText) {
        alert('Send email Ajax call failed with error code ' + errCode + ':' + errText);
    });
}

Shp.Utility.Email._SendEmail = function (emailProperties, success, fail) {
    var ajax = new Sys.Net.WebRequest();
    ajax.set_url(_spPageContextInfo.webServerRelativeUrl + '/_api/SP.Utilities.Utility.SendEmail');
    ajax.set_httpVerb('POST');
    ajax.set_body(emailProperties.GenerateRequestBody());
    ajax.get_headers()['Accept'] = 'application/json;odata=verbose';
    ajax.get_headers()['Content-Type'] = 'application/json;odata=verbose';
    ajax.get_headers()['X-RequestDigest'] = $get('__REQUESTDIGEST').value;

    ajax.add_completed(function (executor, eventArgs) {
        if (executor.get_statusCode() >= 200 && executor.get_statusCode() < 300 && executor.get_responseAvailable() === true) {
            success(executor.get_responseData());
        }
        else {
            alert('Ajax failed with error code ' + executor.get_statusCode() + ':' + executor.get_responseData());
        }
    });

    ajax.invoke();
}

Shp.Utility.Email.registerClass('Shp.Utility.Email');

This class is very simple. It contains a method called “SendEmail” which accepts 3 arguments:

  • Email properties, which is the object created before
  • A function to be executed if Ajax is successfully, which accept obtained data from server as parameter.
  • A function to be executed if Ajax fails. This function accepts error code and error text as parameters, but is not mandatory. If not specified, an alert containing this information is shown.

As an example, check the code below.

Shp.Utility.Email.SendEmail(emailProperties, function (data) {
    // Do something with data
});

There one important thing I need to mention. It seems you can send emails only to the users registered to the SharePoint. If not registered, the user won’t receive emails, but the email doesn’t fail as the rest of registered users will receive it.

There is always a way to improve the code and if you have suggestions, please let me know. 🙂

Accessing some SharePoint properties from JavaScript

We know now Microsoft is pushing the SharePoint development to client side as much as possible. This means developers should use JavaScript more instead of server side code where is possible and Microsoft made this easier by exposing some SharePoint properties to client side code. There is a JavaScript object called _spPageContextInfo which contains some properties you might use in your code:

  • webServerRelativeUrl
  • webAbsoluteUrl
  • siteAbsoluteUrl
  • serverRequestPath
  • layoutsUrl
  • webTitle
  • webTemplate
  • tenantAppVersion
  • isAppWeb
  • webLogoUrl
  • webLanguage
  • currentLanguage
  • currentUICultureName
  • currentCultureName
  • clientServerTimeDelta
  • siteClientTag
  • crossDomainPhotosEnabled
  • webUIVersion
  • webPermMasks
  • pageListId
  • pageItemId
  • pagePersonalizationScope
  • userId
  • systemUserKey
  • alertsEnabled
  • siteServerRelativeUrl
  • allowSilverlightPrompt

List is long and you can figure out yourself how to use them. Probably the most noticeable property is userId. I remember when people moved to SharePoint 2013, a lot of developers were upset they could not use any more _spUserId. That’s probably they did not know it was replaced with _spPageContextInfo.userId. I would say, Microsoft did not communicate enough new functionalities, but this doesn’t mean they did not offer something to replace old functionality.