Get the InternalName for a SharePoint List Column

This comes up all the time in the SPServices Discussions on Codeplex. When you are making most Web services calls to SharePoint, you have to use the InternalName rather than the DisplayName. This is true whether you are using SPServices, CSOM, or REST. You may see the InternalName referred to as the StaticName as well. They are a little different, but that doesn’t matter here.

When you create a column in a list or library, you give it a DisplayName. That name may be encoded to some degree – depending on the characters you use – to become the InternalName. The InternalName never changes, no matter how many times you change the DisplayName. (This is why we end up with abominations like a column with an InternalName of “Salary” which has a DisplayName of “Name of Customer”. Rapid prototyping can paint us into these messes if we aren’t careful.)

The DisplayName is the name you’re used to seeing for columns in lists and libraries. You see these names all over the place: on list forms, in list views, etc.

There are a few [relatively] easy ways to get the InternalName if you only know the DisplayName.

  • Go to the List Settings and hover over each column’s name. You’ll see the InternalName after “&Field=” in the Status bar of your browser. Note that the name will be double encoded. For example, if a field’s DisplayName is “My Country”, you’ll see “My%5Fx0020%5FCountry”. The actual internalName is “My_x0020_Country”.
  • Call GetList, which will return the list’s schema and inspect the results in a debugger. Example:
    $().SPServices({
        operation: "GetList",
        listName: "MyList"
    });
    
  • Call SPGetStaticFromDisplay, with the list name and column name and SPServices will look up the Internal Name for you. Example:
    var thisStaticName = $().SPServices.SPGetStaticFromDisplay ({
      listName: "MyList",
      columnDisplayName: "My Country"
    });
    

Here’s a little table with some examples of how some column names end up:

DisplayName InternalName Double Encoded Name Notes
Title Title Title “Title” doesn’t have any special characters in it, so all three versions are the same.
My Country My_x0020_Country My%5Fx0020%5FCountry The space is encoded as _x0020_
The underscores are encoded as %5F
My_Country My_Country My_Country No extra encoding! Underscores are not special characters.
My-Country My__x002d_Country My%5Fx002d%5FCountry The dash is encoded as _x002d_
The underscores are encoded as %5F

 

Using RequireJS to Load the Right Version of jQuery Depending on Internet Explorer Version

Here’s a cool trick you can use with RequireJS. I found it in a post by @rnsloan called Conditionally Loading jQuery 2.x.

Since environments with SharePoint can often have a mixed bag of browser versions, this conditional setting lets us load jQuery 2.x when the browser can handle it (generally IE 9+) or jQuery 1.x when it can’t. The function document.addEventListener is undefined in IE8 and earlier – sometimes called oldIE – and defined in IE9+ – also known as modern.IE – so it’s a simple little test that ought to work.

If you don’t know why this matters, check out the Browser Support page on the jQuery site.

requirejs.config({
  paths: {
    "jquery": (document.addEventListener) ?
      ['//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min','jquery-2.1.3.min']
      :
      ['//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min','jquery-1.11.1.min']
    }
});

Note: The versions of jQuery above were current as of this writing, but may well have already moved on by now.

Calculate How Much Web Storage You’re Using

This is a quick one, but it’ll be a post I return to over and over again.

When you use Web storage (a.k.a. DOM storage) – localStorage or sessionStorage – to cache data, you’ll often want to know how much you’ve used already. Each browser gives you a different amount of storage to work with, and you don’t want to run out for no good reason.

You might be surprised what the sites you visit are up to these days. Cookies are old hat; they just don’t give us enough to work with, as they are capped at 4k of data each.

From Wikipedia:

Web storage provides far greater storage capacity (5 MB per origin in Google Chrome, Mozilla Firefox, and Opera; 10 MB per storage area in Internet Explorer; 25MB per origin on BlackBerry 10 devices) compared to 4 kB (around 1000 times less space) available to cookies.

If you need to know how long each browser has offered Web storage, check out the Web storage page on Can I use. If you’d like to understand Web storage in a more practical sense, check out my post Caching SharePoint Data Locally with SPServices and HTML5’s Web Storage.

I’ve adapted some JavaScript I found out in a post on StackOverflow to display the storage used by each object in both localStorage and sessionStorage for the current origin

var storageTypes = ["localStorage", "sessionStorage"];
var x, log = [], total = 0;

for(var i=0; i < storageTypes.length; i++) {
	var thisStorage = window[storageTypes[i]];
	log.push("Statistics for " + storageTypes[i]);
	log.push("--------------------------------");
	for (x in thisStorage) {
		log.push(x + " = " + ((thisStorage[x].length * 2) / 1024).toFixed(2) + "KB / " + ((thisStorage[x].length * 2) / 1024 / 1024).toFixed(2) + "MB");
		total += thisStorage[x].length * 2;
	};
	log.push("Total = " + (total / 1024).toFixed(2) + "KB / " + (total / 1024 / 1024).toFixed(2) + "MB");
	log.push("================================");
	total = 0;
}
console.log(log.join("\n"));

The results will look something like those below, which I got by running the code in a console while I was on a Yammer external network.

Statistics for localStorage
--------------------------------
yj-chat-contact-list-1328414-1512241636 = 0.18KB / 0.00MB
yj-chat-contact-list-428797-1488346713 = 0.18KB / 0.00MB
Total = 0.36KB / 0.00MB
================================
Statistics for sessionStorage
--------------------------------
Total = 0.00KB / 0.00MB
================================

Here are the results on the home page of my Office365 tenant:

 Statistics for localStorage
--------------------------------
Ribbon.Document = 0.04KB / 0.00MB
Ribbon.Read = 0.04KB / 0.00MB
Ribbon.WikiPageTab = 0.05KB / 0.00MB
SPAnimationEnabled = 0.00KB / 0.00MB
SPMySiteLinks = 0.73KB / 0.00MB
SPSuiteLinksDate = 0.11KB / 0.00MB
SPSuiteLinksJson = 46.97KB / 0.05MB
SPSuiteLinksLanguage = 0.01KB / 0.00MB
SPSuiteLinksUserKey = 0.08KB / 0.00MB
SPSuiteNavHeight = 0.01KB / 0.00MB
SPSuiteThemeInfo = 0.74KB / 0.00MB
ShellCacheIndicator = 0.07KB / 0.00MB
Total = 48.86KB / 0.05MB
================================
Statistics for sessionStorage
--------------------------------
SPAnimationEnabled = 0.00KB / 0.00MB
SPCacheLogger0 = 0.26KB / 0.00MB
SPCacheLogger1 = 0.29KB / 0.00MB
SPCacheLogger2 = 0.29KB / 0.00MB
SPCacheLogger3 = 0.27KB / 0.00MB
SPCacheLogger4 = 0.28KB / 0.00MB
SPCacheLogger5 = 0.26KB / 0.00MB
SPCacheLogger6 = 0.26KB / 0.00MB
SPCacheLogger7 = 0.67KB / 0.00MB
SPCacheLoggerSize = 0.00KB / 0.00MB
SPSuiteLinksCached = 0.01KB / 0.00MB
SPUserPhotoToken = 0.03KB / 0.00MB
UserPhotoToken = 0.03KB / 0.00MB
Total = 2.64KB / 0.00MB
================================

Create a Simple SharePoint 2013 Employee Directory on Office365 – Part 3 – Create Display Templates

In the prior two parts of the series, I covered the idea of an employee directory (or associate directory, or person directory, or whatever you call the people in your company/organization/commune) and how to create the basic page. Once again, I want to give credit to Ari Bakker’s (@aribakker) post that shows how to set up a simple a Employee Directory on SharePoint 2013: How to: Create a Simple SharePoint 2013 People Directory.

I’m going to start with Ari’s Display Templates, but take them a bit further. What you’ll want to do here will vary based on the characteristics of your organization. What’s useful for an organization with 10 people will be quite different than what’s useful for one with 100k+ people.

The client I needed this directory for has about 100 employees. As with many organizations, the Employee Directory is effectively replacing something that has been maintained in Excel and emailed out regularly. Everyone prints it out and hangs up a copy in their cube. So we really want this directory to look a lot like that old-fashioned phone list. It will have a little more info and if the data maintenance side of things holds up, the data will always be current. At the same time, since we are using Display Templates, we’re well-positioned to expand the information we display over time.

In small- to medium-sized organizations like this, it’s helpful to have an alphabetic filter. I’ve been building similar things in SharePoint for a long time. (See: Alpha Selection of List Items in a Data View Web Part (DVWP)) They are great to add to plain old list views, too. I’ve created a Control Display Template which shows the alphabetical filtering links and an Item Display Template that shows each person’s details.

Here’s what the alphabetic filtering looks like:

Alphabetic Filtering

Note that several of the letters are not “lit up”. That’s because no one in the organization has a last name starting with I, Q, X, or Y. Few things are more annoying than clicking on a link like this only to be told that “Nothing here matches your search”.

Nothing here matches your search

If nothing matches, then why did you show me the link?!?!?! So there’s a little magic in the Control Display Template to figure out which letters should not be lit up. That logic will ensure that we only can click on letters where there are actual results, even as people come and go.

Like I said, there are two Display Templates here. These little buggers tend to work in pairs.

Display Template Layout

Source: http://msdn.microsoft.com/en-us/library/office/jj945138(v=office.15).aspx

Think of the Control template as the outer one and the Item template as the inner one which we iterate for every individual item in the result set coming back from search. This can be a little tricky and also a little confusing. Where should we draw that dotted line? Well, you’ll see a lot of inconsistency on this. In my two Display Templates for the Employee Directory, I’m using the Control template to display the alphabetic filter links and to create the table which will contain the items, but I’m rendering the table header in the Item template. That just seems to make more sense to me because that way the column headers sit with the rendering of the actual data, not is a separate place (the Control template). Another thing to consider here is that ideally we want the two types of Display Templates to work atomically: we should be able to mix and match different Control and Item templates based on our needs. For instance, in a really large organization, we may not need to check for which letter to light up, so we could just use a Control Template that doesn’t do that piece.

OK, enough chit-chat. Let’s look at the code.

I’ve created a folder in the Site Collection under _catalogs/masterpage to hold everything I’m doing here.

Employee Directory Code

In the real installation, that folder is named for the client, but here I’ve called it “_EmployeeDirectory”. Note the leading underscore: that ensures that the folder will always show up at the top of the listing under masterpage; otherwise I have to scroll a lot.

As you can see, I’m pretty organized about how I store things in the folder. I have subfolders for:

  • css – Any CSS files that are a part of this solution.
  • Display Templates – This folder structure mimics the one that SharePoint uses out of the box. I even mirror the subfolder names, like Search, so that it’s clear what type of Dsiplay Templates are in there. Because each of the Display Templates has a Content Type in it, SharePoint knows how to find the files in these custom folders.

Control Display Template If I had any custom JavaScript in the solution, I’d have a js folder, images files would go into an images folder, etc.

Here are the custom parts of the Control Display Template. The basic logic is this:

  • Line 3 – Include some custom CSS. In a full installation this would probably occur in the master page, but I’ve chucked it out to share the important bits here.
  • Line 4 – Include jQuery. I’m using jQuery to handle a bunch of things since it makes life easier.
  • Line 7 – This div is just the outer container for the template.
  • Lines 10-15 – Declare some variables we’ll need later
  • Lines 19-36 – Emits the markup for the alphabetic filters.
  • Lines 22-28 – Loop through all of the letters in the alphabet and make the calls to search to find out if that letter should be “lit up”.
  • Line 31 – Emit the hard-wired “All” link.
  • Lines 38-53 – This block is where I figure out which letters to unlight change the CSS for each. Yes, After fiddling around with this for a while, I decided to load the page with *all* the letter lit up, and turn off the ones that don’t have data behind them. This makes for a good regression if we have one: all the links will be lit up even if the script fails.
  • Lines 55-76 – This function called getSearchResultsUsingREST makes a call to the Search Service using REST and passes back a promise. Each call requests just the first result (rowlimit=1) and only the WorkId property (selectproperties=’WorkId’). This makes the call extremely “light”. We don’t need to know how many people fall into the letters bucket; we just want to know if *any* do.
<body>
	<script>
		$includeCSS(this.url, "~sitecollection/_catalogs/masterpage/_EmployeeDirectory/css/EmployeeDirectory.css");
		$includeLanguageScript(this.url, location.protocol + "//code.jquery.com/jquery-1.11.2.min.js");
	</script>

    <div id="Control_SearchResults">

<!--#_
		// Make REST calls to the Search API to "light up" the letters.
		var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		var letters = alphabet.split("");
		var lettersSearch = []; // Array to hold the AJAX promises
		var currentSearch = (location.href.indexOf("k=lastname%3A") > 0) ? location.href.split("k=lastname%3A")[1].substr(0, 1) : "";
		var thisPage = location.pathname;
_#-->                    

		<!-- Alpha selector container -->
		<div id="edir-alpha-links">
			<span><a href=_#=thisPage=#_>All</a></span>
<!--#_
		// Loop through the alphabet and make a call to the search API for each starting letter
		for(var i=0; i < letters.length; i++) {
			var searchTerm = letters[i];
			var letterClass = (currentSearch === searchTerm) ? "selected" : "";		

			// TODO Build the calls into one REST $batch. See: http://www.andrewconnell.com/blog/part-1-sharepoint-rest-api-batching-understanding-batching-requests
			lettersSearch[i] = getSearchResultsUsingREST("LastName%3A" + searchTerm + "*");
_#-->
			<span id="edir-alpha-links-_#= searchTerm =#_">
				<a class="_#=letterClass=#_" href="_#= location.pathname =#_#k=lastname%3A_#= searchTerm =#_*">_#= searchTerm =#_</a>
			</span>
<!--#_
		}
_#-->
		</div>
<!--#_
		// When all the requests have completed...
		$.when.apply($, lettersSearch).done(function() {

			for(var i=0; i < letters.length; i++) {

				var searchTerm = letters[i];
				var count = this[i];

				var letter = $("#edir-alpha-links-" + letters[i]);
				if(count == 0) {
					letter.html(searchTerm);
					letter.addClass("no-link");
				};
			}

		});

		function getSearchResultsUsingREST(queryText) {

			var result = new $.Deferred();
			var resultCount = 0;

			// We only need to fetch the first result for each letter to know if we should light it up
			// The sourceId is the GUID for the 'Local People Results' Result Source
			var searchUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/search/query?querytext='" + queryText + "'&sourceId=%27b09a7990-05ea-4af9-81ef-edfab16c4e31%27&rowlimit=1&selectproperties='WorkId'";

			var p = $.ajax({
				url: searchUrl,
				method: "GET",
				headers: { "Accept": "application/json; odata=verbose" }
			});

			p.done(function(data) {
				// Return just the count of results, which will be 0 or 1
				result.resolveWith(data.d.query.PrimaryQueryResult.RelevantResults.RowCount);
			});

			return result.promise();
		}

_#-->

Next up is the Item Display Template. In this one, I’ve gone as simple as possible. Ive gotten rid of a lot of the encoding, null value tests, etc. that most of the out-of-the-box Displey Templates have just to keep it simple. Below I’m showing *everything* in the body of the template. It’s really stripped down.

 

  • Line 9-21 – If this is the first item in the result set, emit the table header.
  • Line 23-33 – Emit the details for each person in the rsult set. Here I’m showing:
    • Name (full name)
    • Work Phone
    • Email
    • Department
    • Office

Your organization will undoubtedly have a few other fields you want to add, you may one to remove one or two of these, etc.

<body>
    <div id="Item_Person">
<!--#_
		if(!$isNull(ctx.CurrentItem) && !$isNull(ctx.ClientControl)){
			var encodedPath = $urlHtmlEncode(ctx.CurrentItem.Path);
_#-->
<!--#_
			// If this is the first item in the results, emit the table header
			if(ctx.CurrentItemIdx === 0) {
_#-->
				<thead>
					<tr class="ms-viewheadertr ms-vhltr">
						<td class="ms-vh2">Name</td>
						<td class="ms-vh2">Work Phone</td>
						<td class="ms-vh2">Email</td>
						<td class="ms-vh2">Department</td>
						<td class="ms-vh2">Office</td>
					</tr>
				</thead>
<!--#_
			}
_#-->
			<tr>
				<td class="ms-vb2">
					<a clicktype="Result" id="NameFieldLink" href="_#= encodedPath =#_" title="_#= ctx.CurrentItem.PreferredName =#_">_#= ctx.CurrentItem.PreferredName =#_</a>
				</td>
				<td class="ms-vb2">_#=ctx.CurrentItem.WorkPhone=#_</td>
				<td class="ms-vb2">
					<a href="mailto:_#=ctx.CurrentItem.WorkEmail=#_">_#=ctx.CurrentItem.WorkEmail=#_</a>
				</td>
				<td class="ms-vb2">_#= ctx.CurrentItem.Department =#_</td>
				<td class="ms-vb2">_#= ctx.CurrentItem.OfficeNumber =#_</td>
			</tr>
<!--#_
	}
_#-->
    </div>
</body>

Finally, here’s the CSS I included above:

/* Alpha links for search */
#edir-alpha-links {
	margin-top:10px;
}
#edir-alpha-links span {
	padding:0 3px 0 3px;
	width:35px;
	font-size:18px;
}
#edir-alpha-links span.no-link {
	padding:0 6px 0 6px;
}
#edir-alpha-links span a {
	padding:0 3px 0 3px;
	border-radius:6px 6px 6px 6px;
	background-color:#005581;
	color:#fff;
}
#edir-alpha-links span a.selected {
	background-color:#E08B24;
}
/* Alpha links for search */

Please don’t use this as-is. I’m so disappointed when I see one of my examples shows up somewhere with exactly the same colors and fonts!

Once we’ve switched to these Display Templates, we have something like this:
Employee Directory - Part 3

You can see we still have a few oddities here. This screen grab is from my demo environment, so there aren’t that many real people in the results. (Note that only a few of the letters are lit.) We’re also seeing some odd “people” that aren’t people at all. There’s probably some other filtering we’d like to do, maybe a few more settings to tweak. Next up I’ll show you some of the things you’ll probably want to do with your Search Schema to make things work a little better. WARNING: Powershell is coming. Sad, but true.

Here are the files from this post in a ZIP file: _EmployeeDirectory Display Templates and CSS

Semantic Versioning for SPServices?

Late last year I committed to put SPServices onto Github to encourage greater collaboration and contribution from the community. It’s definitely there now, and there are several things I’ve been struggling with since I moved it there:

  • What to name each version
  • How many versions to move into the repo
  • Whether it actually will increase community submissions at all
  • etc.

So far, I have done one release while SPServices has been on Github, which was 2014.02 [Codeplex][ [Github]. Even for that one new release, I wasn’t sure how to structure things. Everyone is moving away for including version numbers in their file names. I’m not sure that’s such a good thing. By including the version number in the file name, we can tell – at a glance – what version we’re working with. Since many SPServices users are more on the end user end of the spectrum rather than the developer end, that’s an excellent thing to be able to do.

Andreas Schultz has recommended that I switch to Semantic Versioning in a pull request for bower.json. Bower is a package manager, and there are a host of others: npm, NuGet, Bower, Ender, volo, component, Jam, and the list goes on. The number of these things is increasing fast. (I’d include links, but these things are easy to find.)

This is a tricky one. I’ve watched as this “semantic versioning” thing gets rolling, and I’m not sure how I feel about it.

Over the first several years I increased the versions for SPServices from 0.2.3 to 0.7.2. The fact that I wasn’t getting to version 1.0 was more me being cantankerous than anything else. Then I had 3 or 4 people tell me – separately – that they weren’t allowed to use software in their organizations unless it was version 1.0 or above. What a crock!

So I switched to numbering like 2013.01 back in May, 2013. The version is greater than 1.0! Since then, I’ve had 2013.02, 2013.02a, 2014.01, and 2014.02. It marches forward and gives some indication of what the versions is, or at least its age.

Semantic versioning seems to give people an excuse to introduce breaking changes. Here’s the summary of the idea from the SemVer site:

Given a version number MAJOR.MINOR.PATCH, increment the:

  1. MAJOR version when you make incompatible API changes,
  2. MINOR version when you add functionality in a backwards-compatible manner, and
  3. PATCH version when you make backwards-compatible bug fixes.

Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

One of my goals with SPServices going way back to 1999 has been not to introduce *any* breaking changes. I think I’ve done a pretty good job on that, too. Whether you’re using SPServices on SharePoint 2007 with no updates installed or SharePoint 2013 on Office365, it *ought* to work the same way. Code that you wrote with version 0.2.3 *ought* to still work, too, though most likely you’re running a more recent version of jQuery and other things which would make using that ancient version pretty dumb. (That and the fact that the first version didn’t do much.)

If I were to follow the semanatic versioning rules, I’m not sure if I would be at 1.1.14 or 1.14.02 or what. Removing version numbers from files makes me uncomfortable, though.

Switching to semantic versioning seems to be hopping on a train without really understanding if driving is just fine and will get us there at the same time. What are your opinions on this? If you add your $.02 into the comments, please indicate what type of user you are, e.g., end user (“I paste SPCasecadeDropdowns into my forms” or developer “I write solutions using SPServices as the data transport layer” or…