Feedback for a SPGetCurrentUser Modification

Jul 8, 2012 at 4:24 AM
Edited Jul 8, 2012 at 4:25 AM

I work on an SP 2007 server that allows no server side coding, so SP Services has really opened up a whole world of possibilities for me. Thanks very much for all your work on it.

I have a SP custom list with user information that contains a lot of information not otherwise available in SharePoint (e.g. more contact and organizational information than is available for users in SharePoint/Active Directory). I though it would be a nifty idea to be able to get this user information in addition to what I could get from SPServices. I started using SPGetCurrentUser to return the email address, then querying my custom list with the email address to return additional user information (using email address as a kind of foreign key). I built all this into a function that would perform 3 SPGetCurrentUser calls to get ID, Name and Email, and then the call to my custom list to get a "complete" user object that I could then use for a variety of applications. I don't really need all of that information for every application, but having it all in a single object seems convenient for general purposes.

However, getting all this information is requiring four separate calls to SPServices, it takes a little time for my object to return, and since I was doing all this synchronously, it holds up the page loading. So I've spent a little time trying to improve the response, and have come up with a modification for (actually a cloned and mutated function) SPGetCurrentUser, which I'm calling SPGetCurrentUserAllInfo (code follows). I wanted to modify the original function in two ways: to make it asynchronous if I wished, and to return multiple fields of information.

To make it asynchronous I've added the option in the object parameter and simply passed that value on to the ajax call. I've also added an additional parameter, a callback function, that is triggered at the end of the ajax callback function.

As described in the documentation here, the SPGetCurrentUser function allows you to pass in a single desired field name, and then it makes an ajax call to the SP user page searches for the desired field and returns the value found. In order to get the value of three fields, you have to call the function three times and it, in turn has to make three ajax calls to the same page. I experimented to find a way to bring back more information from a single function call (a single ajax call to load the page), first by passing in an array of field names and returning an object with those field name parameters. I ended up though going a little further and simply trying to find all the available fields on the page and returning all of those values. I figured that there was a lot of overhead and time involved in the ajax call to get that SP user page, and relatively very little in "working it over" and returning all I could for a single call.

One thing I'm concerned about is a comment I found in the source code:

// Need this to be synchronous so we're assured of a valid value

I wanted to share my code and maybe get some advice on whether this is a good or bad approach, maybe some improvements in how I've coded things, etc. It seems to be working fine, and my pages now load as quickly as they did before, with some of the JavaScript running after the async calls are all complete. I'm getting all the information I wanted and more from a single call to SPGetCurrentUser (and a single ajax call). It all seems good, which makes me kind of nervous. So any words of wisdom, constructive advice or comments are appreciated. Here's the code:

// next is a callback function if desired
$.fn.SPServices.SPGetCurrentUserAllInfo = function(options, next) {
	var opt = $.extend({}, {
		debug: false, // If true, show error messages;if false, run silent
		async: false // Option to run ajax async if desired					
	}, options);

	// Object to contain all the user info from the form
	var userInfo = {};
	var thisField = "";

	// ID is always available, so return it
	userInfo["ID"] = _spUserId;

	// create a RegExp to return field names
	re = new RegExp("FieldInternalName=\"(\\w\+)\"", "i");


	$.ajax({
		// Need this to be synchronous so we're assured of a valid value
		async: opt.async,
		// Force parameter forces redirection to a page that displays the information as stored in the UserInfo table rather than My Site.
		// Adding the extra Query String parameter with the current date/time forces the server to view this as a new request.
		url: $().SPServices.SPGetCurrentSite() + "/_layouts/userdisp.aspx?Force=True&" + new Date().getTime(),
		complete: function (xData, Status) {
			$(xData.responseText).find("table.ms-formtable td[id^='SPField']").each(function() {

				ma = re.exec($(this).html());

				// if the match found a field name
				if (ma != null && ma.length==2)
				{
					// thisField becomes the field name
					thisField = ma[1];
					switch($(this).attr("id")) {
						case "SPFieldText":
							userInfo[thisField] = $(this).text();
							break;
						case "SPFieldNote":
							userInfo[thisField] = $(this).find("div").html();
							break;
						case "SPFieldURL":
							userInfo[thisField] = $(this).find("img").attr("src");
							break;
						// Just in case
						default:
							userInfo[thisField] = $(this).text();
							break;						
					}
				}

			});
			// run the callback function if it exists.
			if ($.isFunction(next)) {
				next(userInfo);
			}
		}
	});

	return userInfo;

}; // End $.fn.SPServices.SPGetCurrentUserAllInfo
Coordinator
Jul 9, 2012 at 3:45 AM
Edited Jul 9, 2012 at 3:47 AM

DaddyUnit:

Thanks for the ideas. I actually added the multiple value idea into the latest alpha already; you're just a little behind me!http://spservices.codeplex.com/workitem/10079

My implementation is a little different because I wanted to maintain backward compatibility:

 // Function which returns the account name for the current user in DOMAIN\username format
 $.fn.SPServices.SPGetCurrentUser = function(options) {
  var opt = $.extend({}, {
   fieldName: "Name",    // Specifies which field to return from the userdisp.aspx page
   fieldNames: {},     // Specifies which fields to return from the userdisp.aspx page - added in v0.7.2 to allow multiple columns
   debug: false     // If true, show error messages; if false, run silent
  }, options);
  // The current user's ID is reliably available in an existing JavaScript variable
  if(opt.fieldName === "ID") {
   return _spUserId;
  }
  var thisField = "";
  var theseFields = {};
  var fieldCount = opt.fieldNames.length > 0 ? opt.fieldNames.length : 1;
  var thisUserDisp;
  // Get the UserDisp.aspx page using AJAX
  $.ajax({
   // Need this to be synchronous so we're assured of a valid value
   async: false,
   // Force parameter forces redirection to a page that displays the information as stored in the UserInfo table rather than My Site.
   // Adding the extra Query String parameter with the current date/time forces the server to view this as a new request.
   url: $().SPServices.SPGetCurrentSite() + "/_layouts/userdisp.aspx?Force=True&" + new Date().getTime(),
   complete: function (xData, Status) {
    thisUserDisp = xData;
   }
  });
  for(i=0; i < fieldCount; i++) {
   // The current user's ID is reliably available in an existing JavaScript variable
   if(opt.fieldNames[i] === "ID") {
    thisField = _spUserId;
   } else {
    var thisTextValue;
    if(fieldCount > 1) {
     thisTextValue = RegExp("FieldInternalName=\"" + opt.fieldNames[i] + "\"", "gi");
    } else {
     thisTextValue = RegExp("FieldInternalName=\"" + opt.fieldName + "\"", "gi");
    }
    $(thisUserDisp.responseText).find("table.ms-formtable td[id^='SPField']").each(function() {
     if(thisTextValue.test($(this).html())) {
      // Each fieldtype contains a different data type, as indicated by the id
      switch($(this).attr("id")) {
       case "SPFieldText":
        thisField = $(this).text();
        break;
       case "SPFieldNote":
        thisField = $(this).find("div").html();
        break;
       case "SPFieldURL":
        thisField = $(this).find("img").attr("src");
        break;
       // Just in case
       default:
        thisField = $(this).text();
        break;      
      }
      // Stop looking; we're done
      return false;
     }
    });
   }
   if(opt.fieldNames[i] !== "ID") {
    thisField = (thisField !== undefined) ? thisField.replace(/(^[\s\xA0]+|[\s\xA0]+$)/g, '') : null;
   }
   if(fieldCount > 1) {
    theseFields[opt.fieldNames[i]] = thisField;
   }
  }
  return (fieldCount > 1) ? theseFields : thisField;
 }; // End $.fn.SPServices.SPGetCurrentUser

Since you've been knee deep in the function, I welcome any critique you might have.

The reason I've left the function async is that, generally speaking, people want to use the values right away. Waiting for them clearly makes sense in your case, but I try to keep things as simple as possible.

The way you've gone about implementing the syncronous idea is great, though. I'm thinkign about including it as an option, but still leaving the default as asynch.

M.

Jul 16, 2012 at 12:29 AM

I'm glad your adding a multiple-field select.

I would definitely encourage you to add an async option, so that I won't need my bit of custom code anymore.