Using SharePoint with SPServices

SPServices is a great library for use with SharePoint.

I’ll show you a quick example of how we can use it to read from a SharePoint list as part of validating new or existing list items.

Prerequisite Knowledge

You will need to have a basic understanding of HTML, and browsing the DOM structure using the browser’s developer tools.  You will also need to have a basic understanding of CAML query syntax.

Example: Check for Duplicate Items with SPServices

When we want to add a new item to our SharePoint list, we want to ensure that it isn’t a duplicate.  Coupled with this, we also want to make sure that when users edit a list item they don’t create a duplicate item too!  So to do this we need to add some validation.

First you need to download and install a copy of SharePoint Designer.

Once installed, run it up and connect to your SharePoint site (you will need the appropriate permissions to do this).  You will simply click ‘Open Site’ and enter the URL.  As an example, if the URL for your list is:

https://alkane.sharepoint.com/alkanehome/Lists/alkane%20tracker/AllItems.aspx

You will open the site:

https://alkane.sharepoint.com/alkanehome/

Once opened, click on Lists and Libraries.  Then click on your list.  Under the Forms section click on the form you wish to edit.

  • NewForm.aspx is the form you see when you create a new entry.
  • EditForm.aspx is the form you see when you edit a record.
  • DispForm.aspx is usually the read-only form view of the record.  We shouldn’t need to edit this for now.

In our example, we’d probably want to make changes to both the NewForm.aspx and the EditForm.aspx.

Select NewForm.aspx and put the SharePoint form into Advanced Mode.

Find the tag called <asp:Content ContentPlaceHolderId="PlaceHolderAdditionalPageHead" runat="server"> and just before its closing </asp:Content> tag we can start adding the following code:

Firstly add a link to the external jQuery and SPServices libraries hosted on a CDN:

<!--jQuery library-->	
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<!--SPServices library-->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2014.02/jquery.SPServices-2014.02.min.js"></script>	
	

And below this we add the validation code – I have commented inline so you can understand the logic:

<script type="text/javascript">

//function to get a SharePoint form element by its Title attribute
//there's probably a more elegant way to do this
function getElementByTitle(elementTitle, elementType)
{
	return $(elementType + "[title='" + elementTitle + "']");
}

//function to get a SharePoint form element by the start of the ID attribute
//there's probably a more elegant way to do this	
function getElementByIDStart(elementID)
{
	return $("[id^='" + elementID + "']");
}

function checkIfItemExists()
{
        //get the value of the entries in our form that we want to check
        var appVendor = getElementByTitle('Application Vendor Required Field','input').val();
	var appName = getElementByTitle('Application Name Required Field','input').val();
		
        //get the ID of the current form item
	var thisItemId = GetUrlKeyValue('ID');	
        //construct a CAML query to return all items that match this vendor name, application name but NOT the same ID (in case we are in edit mode)
        var existingItemQuery = 
	"<Query>" +
	   "<Where>" +  
              "<And>" +
	  	    "<And>" +
			"<Eq>" +
			    "<FieldRef Name='Application_x0020_Vendor' /><Value Type='Text' >" + appVendor  + "</Value>" +
        		"</Eq>" +
	               	"<Eq>" +
	                 	"<FieldRef Name='Application_x0020_Name' /><Value Type='Text' >" + appName + "</Value>" +
	               	"</Eq>" +
	            "</And>" +
                    "<Neq>" +
         		"<FieldRef Name='ID' /><Value Type='Text' >" + thisItemId + "</Value>" +
	            "</Neq>" +
               "</And>" +
          "</Where>" +
       "</Query>";
	          
        //initialise our item found count to zero
	var ItemCount = 0;
	
        //user SPServices to 'GetListItems' of the 'Alkane Tracker' list, passing our CAML query	
	$().SPServices({
	    operation: "GetListItems",
	    async: false,
	    listName: "Alkane Tracker",
	    CAMLQuery: existingItemQuery,
	    CAMLViewFields: "<ViewFields><FieldRef Name='Application_x0020_Name' /></ViewFields>",
	    completefunc: function (xData, Status) {
	      $(xData.responseXML).SPFilterNode("z:row").each(function() {
	        ItemCount = ItemCount + 1;
	      });
	    }
	  });
          
        //if we have found 1 or more items then it already exists and is a duplicate
	if (ItemCount > 0)
	{
  		alert("The proposed item already exists!");			
  		return false;  
  	}
		
	return true;
}

//SharePoint checks if PreSaveItems return false. In this case it will not allow the form to submit.
var PreSaveItem = function() {	
    var itemDoesNotExist = checkIfItemExists();  	
    return (itemDoesNotExist);
}

</script>

That should be pretty much it for our basic example, so save your form and try it out. You’ll also want to paste the same code into EditForm.aspx too.

NewForm.aspx, EditForm.aspx, DispForm.aspx and SharePoint Designer

Every now and then I need to update some jQuery on our SharePoint site. But I do it so infrequently that I usually forget how to do it! You see, in SharePoint Designer i can select the List from under ‘Lists and Libraries’, and then select the form I want to edit from under the Forms section. From this point I can see the ASPX code and all of my jQuery. But all of the jQuery code is highlighted in yellow and disabled!

SharePoint Designer Disabled Yellow

 

 

 

At that point, I read that in order to edit things like jQuery in NewForm.aspx, EditForm.aspx and DispForm.aspx we need to put it into Advanced Mode.  But lo and behold, the Advanced Mode button is disabled too!

SharePoint Designer Advanced Mode

 

 

 

What a pain this is turning out to be!

The only way I could enable the Advanced Mode button is to make a change to some of the code which is NOT highlighted in yellow – for example a WebPart.  If i simply add a letter the Advanced Mode button becomes enabled.  Of course, I subsequently delete the letter to restore it back to its original form and the Advanced Mode button remains enabled!  If I then click the Advanced Mode button, say ‘Yes’ to save changes (because essentially it is the same) and switch to Advanced Mode, and I can then edit anything on the page!

Hide the Ribbon Controls in SharePoint 2013

I’m using modal dialog boxes (Fancybox) in my jQuery project, and I didn’t want the usual SharePoint ribbon header to appear in these.  The simple solution was to hide the ribbon controls in SharePoint 2013 using the following jQuery:

$(document).ready(function () {
            $("#titlerow").hide();
            $("#s4-ribbonrow").hide();
            $("#suiteBar").hide();
}

 

SharePoint PeopleEditor Dialog is Hidden behind the Modal dialog

In my SharePoint site I use modal dialog boxes (Fancybox, actually) to display content.  In one of these dialog boxes is a PeopleEditor SharePoint control which (when you browse for a person) opens up another dialog box!  Unfortunately this dialog box is created in the ‘parent’ DOM and not the page that is open in the Fancybox dialog (which is actually displaying content using an IFrame), so the SharePoint PeopleEditor dialog is hidden behind the modal dialog.  The PeopleEditor dialog box also gets displayed underneath my Fancybox dialog, and is generated on the fly!  So I needed to ensure that it displayed above my Fancybox dialog.  I did this using jQuery:

Using my PeopleEditor control:

     <SharePoint:PeopleEditor ID="requesterPE" Width="350px" AllowEmpty="true" ValidatorEnabled="true" AllowTypeIn="true" MultiSelect="false" SelectionSet="User" runat="server" MaximumEntities="1" />

I got hold of the ‘Browse’ button that gets generated in the DOM, and added to the click event like so:

$("#<%=requesterPE.ClientID%>_browse").click(
function () 
{ 
    var checkExist = setInterval(
    function () 
    { 
        if ($(".ms-dlgContent", top.document).length) 
        { 
                $(".ms-dlgContent", top.document).css("z-index", '9999'); 
                clearInterval(checkExist); 
        } 
    }, 100); // check every 100ms
});

What this does is loop every 100 milliseconds until the PeopleEditor dialog box (with a class name of ms-dlgContent) in the parent window (top.document) is created, and then it changes the z-index order to something higher than my Fancybox (9999).  Voila!

SharePoint 2013 and X-FRAME-OPTIONS: SAMEORIGIN

I’ve got a few lightboxes in my SharePoint 2013 Web Application, and one of the pages I need to launch in a lightbox iframe contains Plupload.  When I clicked on the link to launch the lightbox, nothing appeared in Chrome and I got an error message in Internet Explorer.  Apparently it’s due to a security feature to combat clickjacking.

I tried to fix the issue by using meta tags and also amending the response headers in IIS, but neither appeared to work.  What did work was the Ventigrate Permissive XFrame Header, but rather than include this plugin I just included the class in my project and it worked like a charm.  So….

Use the following class, remembering to specify the correct namespace for your project:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace AlkaneNamespace
{
    public class PermissiveXFrameHeaderModule : IHttpModule
    {

        public void Dispose()
        {
        }

        public void Init(HttpApplication context)
        {
            context.BeginRequest += context_BeginRequest;
            context.EndRequest += context_EndRequest;
        }

        protected void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            HttpContext context = application.Context;

            // General requests
            if (!context.Items.Contains("AllowFraming"))
                context.Items.Add("AllowFraming", String.Empty);

            // IPFS requests
            if (!context.Items.Contains("FrameOptionsHeaderSet"))
                context.Items.Add("FrameOptionsHeaderSet", String.Empty);
        }

        protected void context_EndRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            HttpContext context = application.Context;

            // XLViewer
            context.Response.Headers.Remove("X-FRAME-OPTIONS");
        }


    }
}

now in your web.config, add the reference to your class, namespace and assembly like so:

<configuration>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true">
            <remove name="PermissiveXFrameHeaderModule" />
            <add name="PermissiveXFrameHeaderModule" type="AlkaneNamespace.PermissiveXFrameHeaderModule, AlkaneSolutions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=aa13051522563c02" />
        </modules>
    </system.webServer>
</configuration>

AlkaneNamespace is the namespace of the class, PermissiveXFrameHeaderModule is the name of the class and AlkaneSolutions is the name of the assembly in the GAC.  To get the version/PublicKeyToken information you can follow the post here.

 

Parsing a SharePoint Lookup Field using SPFieldLookupValue

This is a quick example of parsing a SharePoint Lookup field using SPFieldLookupValue.  As we know, SharePoint lookup fields are usually formed as such:

[Lookup ID];#[Lookup Value]

Rather than manually parsing this result (by perhaps splitting the returned string by ‘;#’) we can simply obtain these values using the SPFieldLookupValue:

SPFieldLookupValue splv = new SPFieldLookupValue(oListItem["User_x0020_Email"].ToString());

lookupIdStr = splv.LookupId.ToString();
lookupValueStr = splv.LookupValue.ToString();

 

 

Find a Users SharePoint Group Membership

This example shows how we can find a users SharePoint group membership by utilising SPGroupCollection.  Remember that if you are running your code with SPSecurity.RunWithElevatedPrivileges, you should set the SPUser value OUTSIDE of this elevated code otherwise it will not work for the current user running the browser session.

SPUser user = SPContext.Current.Web.CurrentUser;

SPGroupCollection groupColl = user.Groups;
foreach (SPGroup grp in groupColl)
{
    groupMembership.Text += grp.Name + ",";
}

 

Reading from a SharePoint List using ASP.Net

You may wish to consult this article first to see how to add a reference to the SharePoint library.  This post describes reading from a SharePoint List using ASP.Net.

This example shows how we can define a CAML query to return a set of data from a SharePoint list.

try
{
    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPSite site = new SPSite(ConfigurationManager.AppSettings["ApplicationPortal"].ToString()))
        {
            using (SPWeb oweb = site.OpenWeb())
            {
                    
                SPList appTrackV2 = oweb.Lists[ConfigurationManager.AppSettings["TrackerV2List"].ToString()];

                SPQuery oQuery = new SPQuery();
		//Specify row limit defining how many rows (max) you want to return
                oQuery.RowLimit = 999;
		//specify fields you want to return in dataset here
                oQuery.ViewFields = "<FieldRef Name='Client'/><FieldRef Name='Requester'/>";
                oQuery.ViewFieldsOnly = true;
                oQuery.IncludeMandatoryColumns = false;
                oQuery.DatesInUtc = true;
                oQuery.ViewAttributes = "Scope='RecursiveAll'";

                string caml = "";
		//caml query to define what we want to return
                caml = "<Where><Eq><FieldRef Name='Title'></FieldRef><Value Type='Text'>" + uamid + "</Value></Eq></Where>";
                oQuery.Query = caml;

                SPListItemCollection collListItems3 = appTrackV2.GetItems(oQuery);

                string clientstr = "";

		//loop through each item in list and do something
                foreach (SPListItem oListItem in collListItems3)
                {
                    clientstr = oListItem["Client"].ToString();
                }
            }
        }
   });
}
catch (SPException ex)
{
    //Handle Exception          
}

 

Insert into a SharePoint List using ASP.Net

This post describes how to insert into a SharePoint list using ASP.Net.  First add a reference to the SharePoint DLL for your specific version.  I’m working with SharePoint 2010, and the DLL can be found in the following location:

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.dll

Ensure in your page that you import the namespace:

using Microsoft.SharePoint;

Now I’ve just dumped some example code below, which inserts into a Text field (obtained from a Textbox), a Multi choice field (obtained from a CheckList box) and a User field (obtained from a SharePoint PeopleEditor control).  I’ve also left a few comments in intentionally, as I tried different ways of doing things.  I found that if I ran this chunk of code with elevated privileges it inserted the entry into the list but didn’t trigger the associated workflow.  I also tried to run it as an impersonated user, but it still didn’t trigger the workflow.  So I commented out the RunWithElevated privileges line and it all worked well – the list item inserted and the associated workflow triggered.

You can also see that for the list name, I store this in the Web.Config file since I use the same list throughout the site.

try
{
    //we can use this to run in the context of a specific user, by passing in
    //superToken to SPSite

    // var user = SPContext.Current.Web.AllUsers[@"domain\user"];
    //var superToken = user.UserToken;

    //**IMPORTANT - Cannot run elevated otherwise Workflow wont run!!**

    // SPSecurity.RunWithElevatedPrivileges(delegate
    //{
    using (SPSite oSite = new SPSite(ConfigurationManager.AppSettings["ApplicationPortal"].ToString()))
    {
        using (SPWeb oweb = oSite.OpenWeb())
        {
            oSite.AllowUnsafeUpdates = true;
            oweb.AllowUnsafeUpdates = true;

            SPList supportLib = oweb.Lists[ConfigurationManager.AppSettings["UAMRequestList"].ToString()];
    
            SPListItem newItem = supportLib.Items.Add();

            //add a string to a Text field
            newItem["Title"] = "My Title";
                   
            //add to a multi choice field
            SPFieldMultiChoiceValue tp = new SPFieldMultiChoiceValue();
                    
	    //loop through each item in the checkbox list.  If checked, add it
            foreach (ListItem cBox in targetplatformCBL.Items)
            {
                if (cBox.Selected)
                {
                    tp.Add(cBox.Text);                          
                }
            }                  

            newItem["Target_x0020_Platform"] = tp;

	    //add to a User field
                  
            // SPUser user = oweb.EnsureUser("domain\\user");
            //string sRequester = user.ID.ToString() + ";#" + user.LoginName.ToString();
            // newItem["Requester"] = sRequester;

            PickerEntity entPropMgr = (PickerEntity)requesterPE.ResolvedEntities[0];
            SPFieldUserValue Proposalmgrvalue = new SPFieldUserValue(SPContext.Current.Web, Convert.ToInt16(entPropMgr.EntityData[PeopleEditorEntityDataKeys.UserId]), entPropMgr.Description);

            newItem["Requester"] = Proposalmgrvalue;
                    
            newItem.Update();

                  
        }
    }
          
}
catch (SPException ex)
{
    //handle exception   
}