Create a Simple SharePoint 2013 Employee Directory on Office365 – Part 2 – Set up an Employee Directory Page

The first step is, of course, to create a page where the Employee Directory will live. The approach we’re taking here is to use a page in the Search Site Collection in Office365. By default – at least in the tenants I’ve worked with – the Search Site Collection lives at https://[tenant name].sharepoint.com/search. This isn’t a sneaky hidden Site Collection like the ContentTypeHub (See: Hidden Content Type Hub on Office365 Tenants), but one that lives right out in the open.

When you go to the Search Site Collection directly, you’ll land on a very bland search page:

Search home page

You may also have created your own Enterprise Search Center, which may look a little different. It doesn’t really matter, though. All we want to do is to create a new page in the Search Site Collection you are using to house the Employee Directory.

Click on the cog (See the cog on the left as of this writing. It’s not unlikely that it’s taken on a new shape or color since then.The Cog) and then Site Content. This should show you all of the Apps lists and libraries available in the Site Collection root. If you can’t get this far, then you may not have the appropriate permissions. Go directly to Jail, do not pass Go, and do not collect $200. You’ll need to talk to your Tenant or Site Collection Administrator to get the right permissions.

If permissions aren’t a problem, then click on the Pages App Library icon.

Pages Library

Once in the Pages library, go to the ribbon and create a new page.

Add a new page

When you select Page, you’ll end up on the page where you can create the page. What you’re looking for here is the “(Welcome Page) Search People” page layout. In the image below, the page layout is not available. If it is, choose it and keep rolling. If not, first take this little detour.

Choose Page LayoutClick on the cog, go into Site Settings, and choose “Page layouts and site templates” under the “Look and Feel” section at the top of the right column. (For some reason, even when I know where something is on this page, I have a tough time spotting the right link: everything looks the same. My trick is to use Ctrl-F in the browser to search for the right word rather than just scanning for it.)

Page layouts and site templates

In the Page Layouts section, add the “(Welcome Page) Search People” page layout.

 

Enable a page layout

The section should then look like this:

Enable a page layoutNow go back to the Pages library and create a new page. I’m calling mine “Employee Directory” with the URL set to Employee-Directory.aspx. Be sure to choose the “(Welcome Page) Search People” page layout.

Create Employee Directory page

When you click Create, you’ll end up back in the Pages library. Click on the Employee Directory page. It’ll look something like this, though if you see my photo it’ll be a little weird.

Employee Directory - CreatedBelieve it or not, you now have a fully functional page that will do much of what you want. For instance, if you type “Lastname:A*” in the search box, you’ll get all of the people whose last name starts with the letter A.

Lastname:A*But we don’t want to land on the page with nothing showing. That would require more work than we want people to do. Instead, let’s set the Search Results Web Part up so that it shows some of the people in the tenant by default.

Click on the cog and Edit Page. If you’ve never done this on a Search Results page before, you may be surprised to realized that it’s just a Web Part Page with some special Web Parts already set up for you. You’ll see that you have two Web Part Zones.

  • On the left, you have the Navigation zone. It contains only one Web Part: the Refinement Web Part
  • On the right, you have the Main Zone. It contains two Web Parts: the People Search Box Web Part and the People Search Core Results Web Part.

Employee Directory in edit mode
Click on the dropdown at the top left of the People Search Core Results Web Part and choose Edit Web Part. This will open the Tool Pane for the Web Part.

People Search Core Results Web Part Tool PaneThere’s a great deal we can do with these settings, but for now we are going to just do one simple thing. Click on the Change Query button. In the Query text box, simply add contentclass=spspeople. This sets the default search to return all people, filtered by whatever is in the search box. If you click the Test query button, you should see at least a few people show up on the right in the Search Result Preview box.

Checnge query to search for peopleClick OK to save the query, OK to save the Web Part settings, and Save on the ribbon to save the page. Now you should have a very rough first pass at an Employee Directory. You should see up to 10 people in the results, perhaps along with a photo (if they have one in their profile) and a little information about each person (if certain profile properties are populated). You my also see some strange “people-like” entities. These will usually be some crawler accounts, etc. We’ll take care of those later.

OOB People Search ResultsWhew. That’s probably enough for now. If you want to, try adjusting some of the settings in the Tool Pane for the People Search Core Results Web Part, but don’t go too crazy. In the next post in this series, we’ll look at how we can make some changes to the Display Templates so that our results are more helpful and robust.

Note: Mikael Svenson (@mikaelsvenson), my search guru and Jedi master, pointed out to me that adding contentclass=spspeople isn’t actually necessary. Once we have some other criteria in the search query, we won’t need it there. However, at this point since we don’t, and we’d like to see some people in the results, it serves a purpose.

Don’t Use a Query String Parameter Called ID Unless You Mean the Item ID

m4s0n501

The other day I got a question from my pal D’arce Hess (@DarceHess):

I am using jsLink on a couple lists and I have an interesting circumstance I’m running into.

Scenario: I have a list of Work Centers and each work center has associated machines that are in another list.

I’m having an interesting situation that when I click on the link in the Work Center, it is supposed to reload the same page and append the Work Center ID to the url  so that it will load the associated machines in the other list. I have the functionality working, however for some reason, when it adds the ID and reloads the page, it is changing the page layout from the layout that is chosen for the initial .aspx page.

Since it is literally re-loading the same page, just with an additional ID, I’m not creating new pages. Any ideas of what may be causing this?

After some back and forth about it, I realized that D’arce was using a query string parameter named “ID”. (I know, it should have been obvious to me right away, but we have a way of using shorthand when we describe technical issues that always makes a little back and forth useful.)

ID seems to be very reserved in SharePoint. Someone probably left some sort of back door thing in SharePoint once a long time ago, because I’ve seen using the ID parameter on the query string for anything other than the item ID cause issues way back to SharePoint 2007 at least.

By switching from ID to WorkCenter for the parameter name, all was right in the world again. For example, instead of “ID=1″, “WorkCenter=1″. Not only does this fix the issue, you end up with a query string parameter name that is more descriptive. That’s useful down the road for maintenance and will even make more sense to users. “That URL is ugly” may become “Oh, I’m selecting the WorkCenter”.

Create a Simple SharePoint 2013 Employee Directory on Office365 – Part 1 – Introduction

Employee Directories are a common feature of most Intranets. In fact, I’m working on Employee Directories for two different clients right now.

Bonzai Intranet Employee Directory Example

Bonzai Intranet Employee Directory Example

This is such a common need, that it’s firmly embedded in the fantastic work that my friends over at Dynamic Owl have built into their Bonzai Intranet product. We aren’t going to be able to do something as fancy as there is in Bonzai. But with a little work – not much coding at all – we can build a perfectly good Employee Directory for a small- to mid-sized organization.

There’s a great article by Ari Bakker (@aribakker) out there that shows how to set up a simple a Employee Directory on SharePoint 2013: How to: Create a Simple SharePoint 2013 People Directory. For this series, I owe credit to Ari for getting me started and showing me the main path. I encourage you to read Ari’s post as well.

On Office365, there are some nuances, however, plus I wanted to add some secret sauce to what I implemented. The example I’ll run through in this series is similar to the Bonzai approach, but uses mostly out of the box capabilities. The one “coding” piece is the custom Display Templates I’m using to show the alphabetic search across the top of the search results and the specific columns we want in the table listing.

Here were some of my requirements:

  • Show all current employees by default, with paging set at 50 (the maximum available with search results)
  • Display a simple to use filter based on the first letter of the last name, indicating which last name letter is currently active
  • Only “light up” letters of the alphabet when at least one person has a last name starting with that letter (no links that lead to no results)
  • Show appropriate refiners in the Refinement Panel
  • Display the search results in a more tabular way to imitate the [often Excel-based] phone listing which is common in smaller organizations
  • Offer some other sorting capabilities within the current view

My hope is that by following the steps in these posts, you’ll be able to build a simple but useful Employee Directory for your organization or clients. In the specific case I’m working on now, the organization has about 100 active employees. Based on your organization’s size and specific make up, you will probably want to tweak things along the way.

Here’s a view showing roughly where we’ll end up:

Employee Directory

If there are specific requirements you have that I don’t mention here, please add them into the comments and I’ll try to cover them.

SPServices Stories #22 : SPServices SharePoint Attachments in Internet Explorer 9

Introduction

This is another story I ran across on Twitter and wanted to add to the SPServices Stories series. I wrote about my own experience getting attachment uploading to work in my post Uploading Attachments to SharePoint Lists Using SPServices. My cheat in that case was to change the DOCTYPE to HTML5 in a SharePoint 2010 master page. It was a cheat in the sense that it broke a lot of basic SharePoint functioning. Luckily I could get away with it in that particular case.

In this story, Jason Williams (@JayWll) explains how he got uploading to work with the fileReader polyfill. As much as all of us would like to be working with the latest and greatest technologies, in reality most organizations – especially larger ones – don’t update their tech very often. In Jason’s case he needed the upload capability to work with IE9, which doesn’t have a lot of the HTML5 spec implemented in it.

I’ve combined two of Jason’s posts here, SPServices addAttachment jquery example and SPServices SharePoint Attachments in Internet Explorer 9. Jason built on a post from Brendon Wilbore (@bjwildbore) to get things up and running. This is one of the great things about the SharePoint community: there are many people out there who are generous enough to post their work so that we can build upon it.

SPServices SharePoint Attachments in Internet Explorer 9

Brendon Wilbore:

If you’re having a few issues adding attachments via ajax and SPServices on SharePoint have a look over the code snippets below.

To upload a file to a list you need to make use of the fileReader javascript class, using the readAsDataURL method and stripping the first part off the dataurl to get the base64 component. Then submit this to SPServices.

I’ve been asked a few times to add the ability to upload attachments to SharePoint tools that I’ve created, and I’ve never been able to achieve it until I eventually came across this blog post last week.

If (like me) you’re developing in a front-end only way without any server-side programming then it seems like this is the way to upload files and attach them to SharePoint list items.

It relies on the javascript fileReader feature so your users will need a fairly modern browser… which is where I ran into trouble. The default browser deployed within my company is Internet Explorer 9, and that doesn’t have fileReader support.

With much work and even more googling I was able to get this technique to work in Internet Explorer 9. In the future I’ll write more about how I managed it, and how you can too!


There was a problem, though – the solution relies on the fileReader JavaScript feature which requires Internet Explorer 10, and the default browser deployed within my organization is Internet Explorer 9. What we need is a fileReader alternative for older browsers. Thankfully, such a thing exists. Today I’m going to post some example code that uses the fileReader polyfill and works in older browsers.

What You Need

The code has several pre-requisites. You’ll need jQuery, jQuery UI, SPServices, SWFObject and the JavaScript and flash file that form the fileReader polyfill.

For the purposes of my demo I created a simple SharePoint list called “File Attachment Test.” The list has a single field – title – and attachments to the list are enabled. Your list is probably named differently, so you’ll need to change the references in the code to reflect your list name.

The Code

<html>
<head>
  <meta charset="utf-8" />
  <title>File Attachment Test</title>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
  <script type="text/javascript" src="js/jquery.FileReader.min.js"></script>
  <script type="text/javascript" src="js/jquery.SPServices-2013.01.min.js"></script>
  <script type="text/javascript">
    var selectedfile = false;

    $(document).ready(function() {
      $('input#itemfile').fileReader({filereader: 'js/filereader.swf'});

      $('input#itemfile').change(function(e) {
        selectedfile = e.target.files[0];

        $('span#filename').html(selectedfile.name);
        $('span#fileinput').hide();
      });

      $('input#createitem').click(function() {
        $().SPServices({
          operation: 'UpdateListItems',
          async: false,
          listName: 'File Attachment Test',
          batchCmd: 'New',
          webURL: '/demo',
          valuepairs: [
            ['Title', $('input#itemtitle').val()]
          ],
          completefunc: function(xData, Status) {
            if (Status == 'success' && $(xData.responseXML).find('ErrorCode').text() == '0x00000000') {
              currentitem = $(xData.responseXML).SPFilterNode("z:row").attr("ows_ID");
              alert('List item created with ID ' + currentitem);

              if (selectedfile) {
                filereader = new FileReader();
                filereader.filename = selectedfile.name;

                filereader.onload = function() {
                  data = filereader.result;
                  n = data.indexOf(';base64,') + 8;
                  data = data.substring(n);

                  $().SPServices({
                    operation: 'AddAttachment',
                    async: false,
                    listName: 'File Attachment Test',
                    listItemID: currentitem,
                    fileName: selectedfile.name,
                    attachment: data,
                    completefunc: function(xData, Status) {
                      alert('File uploaded');
                    }
                  });
                };

                filereader.onabort = function() {
                  alert('Upload aborted');
                };

                filereader.onerror = function() {
                  alert('Upload error');
                };

                filereader.readAsDataURL(selectedfile);
              }
            } else alert('List item creation failed');
          }
        })
      });
    });
  </script>
</head>
<body>
  <p>Title:<br><input type="text" id="itemtitle"></p>
  <p>File:<br><span id="fileinput"><input type="file" id="itemfile"></span><span id="filename"></span></p>
  <p><input type="button" id="createitem" value="Go!"></p>
</body>
</html>

Notes

The fileReader polyfill takes the file input box and puts the flash file on top of it, so that the file selection and upload is handled by flash instead of natively in the browser. I found that this fell apart if the file input box didn’t remain in the same place on the page. In other words, I had problems if I tried to use jQuery’s .show() and .hide() functions (or similar).

I solved this by putting the file selection form in a pop-up window. If the page you place your form on is static (i.e. nothing changes after the DOM is loaded) then you shouldn’t have this problem.

Enjoy!

jQuery Library for SharePoint Web Services (SPServices) 2014.02 Released

SPServicesJust in time for the holiday gift-giving season, I’m releasing SPServices 2014.02. This is the second release in 2014 (which you should be able to glean from the release name). If you’re using an earlier version of SPServices, I strongly recommend an upgrade. Read on.

The most important change in this release is due to an egregious error on my part that goes way back to 2013.01. Paul Tavares (@paul_tavares) spotted quite a while back and I was just too dumb to realize how important it was to fix. The net effect of my mistake was that SPServices was always caching requests, regardless how you set cacheXML. On pages where we simply called SPServices a few times on page load, it usually wouldn’t make much of a difference, but a little more memory might be required for the browser. However, in things like Single Page Applications (SPAs) which use SPServices for the data transport layer (See my blog post series: Single-Page Applications (SPAs) in SharePoint Using SPServices), it could make a *huge* difference. It effectively meant that for every unique request, we were caching data whether we should or not. Over a long page lifespan, that could add up to a lot of memory eaten up. Bad news. All better now.

I found out over the last few months the insidious ” Required Field” issue that I thought I had fixed completely in 2014.01 wasn’t totally fixed. For folks on SharePoint 2010 who applied last December’s CU, the markup in the DOM was a little different than the fix I had put into 2014.01. (See: Office 365 Update Changes ‘Display Name’ on Required Fields) This *should* be fixed for everyone now. Please let me know if you see any issues going forward. And Microsoft, please don’t do that again.

There is a handful of new operations in this release, added due to user requests:

  • WebPartPages.SaveWebPart2
  • RecordsRepository.GetRecordRouting
  • RecordsRepository.GetRecordRoutingCollection
  • RecordsRepository.GetServerInfo
  • RecordsRepository.SubmitFile

Finally, I recently moved my SPServices development repository over to Github at sympmarc/SPServices. I’m still getting my feet wet with Github (See: SPServices and Github – This Time I Mean It), but I plan to have my most current work available there going forward. I’ll still be posting the releases over on the Codeplex site, and I’ll monitor the issues there as well. I’m hoping that by using Github, we’ll have more people contributing to SPServices. It’s always been a community-driven project, but I’d love to get more direct contributions.

This release will be, as all of the recent releases have been, available on cdnjs as soon as possible. If you’d like to serve up SPServices from a CDN, cdnjs is the place to get it. cdnjs hosts a lot of JavaScript libraries that don’t have enough coverage to interest the big boy CDNs, so check it out in any case.

Finally, I want to thank Paul Tavares again for his help with SPServices over the years and with this release in particular. Both he and Josh McCarty (@joshmcrty) have also been trying to help get me over onto Github for a long time, but my thick skull just hasn’t gotten it before. I’m using Jetbrains Webstorm as my IDE these days and the Github integration there has finally made Github make sense to me. As we say here in Massachusetts, “light dawns on Marblehead“.

As usual, there are also a number of additional changes to fix existing bugs or improve efficiency (yes, I’m still able to improve on my old code, and I expect that will continue).

You can see the full list of enhancements and improvements on the download page. Note the link to the Issue Tracker items for this release. For posterity, here are links to the release notes showing all the fixes and improvements from the Issue Tracker on Codeplex.

New Functionality

Issue Tracker Item Function Description

 

New Operations

Web Service Operation Options MSDN Documentation Issue Tracker Item
WebPartPages DeleteWebPart pageUrl, storageKey, storage WebPartPagesWebService.DeleteWebPart Method 10273
WebPartPages SaveWebPart2 pageUrl, storageKey, webPartXml, storage, allowTypeChange WebPartPagesWebService.SaveWebPart2 Method 10273
ALPHA6 RecordsRepository GetRecordRouting recordRouting RecordsRepository.GetRecordRouting Method 10257
RecordsRepository GetRecordRoutingCollection NA RecordsRepository.GetRecordRoutingCollection Method 10257
RecordsRepository GetServerInfo NA RecordsRepository.GetServerInfo Method 10257
RecordsRepository SubmitFile fileToSubmit, properties, recordRouting, sourceUrl, userName RecordsRepository.GetRecordRouting Method 10257

 

Bug Fixes and Efficiency

Issue Tracker Item Function Description
10267 $().SPServices.SPComplexToSimpleDropdown Possible bug in SPComplexToSimpleDropdown
10253 $().SPServices.SPFindMMSPicker SPFindMMSPicker missing from 2014.01
10279 $().SPServices.SPGetListItemsJson SPGetListItemsJson Doesn’t Handle Attachments Correctly
10284 $().SPServices.SPFilterDropdown SPFilterDropdown Not filtering dropdown column.
10272 $().SPServices.SPGetCurrentSite $().SPServices.SPGetCurrentSite Documentation
ALPHA5 10277 $().SPServices.SPGetCurrentUser fix for SPGetCurrentUser at webUrl /
10248 $().SPServices.SPGetCurrentUser Trouble with SPSservices and Sharepoint Form
10265 $().SPServices.SPGetCurrentUser $().SPServices.SPGetCurrentUser isn’t returning ID
10254 $().SPServices.SPDisplayRelatedInfo SPDisplayRelatedInfo – DIV named improperly with Required field
10256 $().SPServices.SPCascadeDropdowns SPCascadeDropdowns Required Lookup issue
10262 $().SPServices.SPComplexToSimpleDropdown SPComplexToSimpleDropdown – required fields get renamed without the ‘Required Field’ appended to the field name
10146 $().SPServices ResolvePrincipals with addToUserInfoList=true requires SOAPAction
10271 $().SPServices.SPGetListItemsJson SPGetListItemsJson not using defaults.webURL
10283 $().SPServices.SPGetListItemsJson CAMLViewName not working?
ALPHA7 10182 $().SPServices async on version 2013.01 and caching
10298 $().SPServices GetTermSets argument names are slightly wrong
10299 $().SPServices WebUrl not working