Flexible SharePoint Development with Widget Wrangler

(Cross posted at Bob German's blog, Bob German's Vantage Point)

What’s a widget, and why should I care?

In economics, a widget is a name for a generic gadget or manufactured good; on the web, a widget is a generic piece of web functionality running on a page. What makes widgets special is that, unlike controls in ASP.NET or directives in AngularJS, widgets are generally released separately from the web page that hosts them, and are often deployed by end users.

If you’re reading this blog, you probably know something about Microsoft SharePoint, and this might sound familiar. A widget is a lot like a web part, only much lighter weight. In fact, widgets can easily be hosted in content editor web parts, on a list form, in a SharePoint add-in, or outside of SharePoint. If you're careful, you can reuse the same widget in all those contexts!

This work comes out of projects that Bob German and I have done at BlueMetal; for example, I used widgets when I developed the web parts on the BlueMetal's Office 365 intranet. The approach was to use light branding with widgets, with each widget running in a content editor web part.

IntranetWidgets

Widgets on the BlueMetal intranet

The widgets in the screen shot are:

  1. News feed (driven by SharePoint content)
  2. My Clients and Projects (shows links to the user's current consulting projects)
  3. Tabbed Calendar, Community, and Discussions (driven by SharePoint content)
  4. Tabbed New Hires and Anniversary carousel (driven by SharePoint content)
  5. Twitter feed

They're all written in HTML and JavaScript, and work equally well on premises or in Office 365. Each widget is an AngularJS application that can be versioned independently and dropped on any page in SharePoint. But, unlike Add-in parts, there are no IFrames. The widgets don't have to run in content editor web parts – they can run on any web page, so they're much more flexible.

So the answer to the question, "Why should I care?" is because widgets give you:

  • Flexibility: Widgets can be versioned independently and moved around freely on web pages in and out of SharePoint
  • Reusability: Widgets allow one code set to run in a web part, on a SharePoint form or page, or outside of SharePoint
  • Maintainability: Widgets written in an MV* framework like Angular or Knockout are easier to test and maintain

Any snippet of HTML with JavaScript can be considered a widget, however good widgets have additional attributes:

  • They're isolated so they won't interfere with the web page that hosts them, or with other widgets on the page. Ideally multiple copies of a widget can run on a page with no interference.

  • They load efficiently so users don't have to wait a long time for them to render on the page.

  • They're self contained so they can be reused easily. A widget that depends on various script tags, CSS files, and other elements on a page is more brittle and harder to reuse than a widget that is contained within a single HTML element.

  • They're developed using the power of modern JavaScript frameworks such as AngularJS for supportability and testability. (This is purely optional, however, and this article will also explore widgets written in jQuery or plain JavaScript.)

Introducing Widget Wrangler

Today my colleague Bob German and I are pleased to announce a new, light-weight JavaScript library for managing widgets called the Widget Wrangler. It's available now on Github for your widget wrangling pleasure. It's also part of the new JavaScript Core in the January 2015 release of Microsoft's OfficeDev Patterns and Practices library (hence the file name pnp-ww.js).

Widget Wrangler:

  • Helps avoid interference with the hosting page and other widgets
  • Loads scripts efficiently across all widgets on the page
  • Allows widgets written with MV* frameworks such as AngularJS and KnockOut, as well as plain old javascript.
  • Helps isolate your code and UI for easy portability to multiple platforms and environments

A widget consists of a single HTML element (the widget root – usually a <div>) that contains HTML for the widget, and a script tag that references the Widget Wrangler. The script tag includes custom attributes that tell Widget Wrangler what JavaScript to load and how to "boot" the widget.

For example:

The Widget Wrangler (pnp-ww.js) will load in-line, and will take care of loading the scripts the widget needs (in this case Angular and script.js) and bootstrapping the AngularJS application. The custom attributes tell Widget Wrangler how to load the widget:

Tag Required Description
ww-appname yes Used to create a name for the app. In the case of an Angular widget, this is the module that will be passed to the angular.bootstrap function when starting the widget.
ww-apptype no Currently "Angular" is the only supported framework that will auto-bind upon load completion.
ww-appbind no The function to be executed when all the script files have completed loading.
ww-appscripts yes A JSON object that defines the javascript files the widget needs in order to run

NOTE: It is necessary to specify ww-apptype (for an Angular widget) OR ww-appbind (to do the binding yourself).

The ww-appscripts element contains a JSON object that tells Widget Wrangler what scripts to load before bootstrapping the widget. This is a collection of objects in which each object contains properties as follows:

Tag Required Description
src yes URL of the script to be loaded; this can be absolute, relative to the page, or by using a tilde prefix, relative to the pnp-ww.js script (for example, src=~/myscript.js)
priority yes An integer indicating the order in which the script should be loaded; first priority 0 scripts will be loaded, then priority 1, etc. Priorities must begin at 0 and not skip any numbers, and scripts in the collection are expected to be in priority order. Multiple scripts can be declared at the same priority level in order to load them concurrently.

A widget can either run as an AngularJS application, which is bound to the widget root, or using a custom binding function specified in the ww-appbind attribute. In the latter case, the widget root DOM element is passed to the binding function so the widget can access the browser DOM relative to the widget root instead of having to find it on the page. This helps to isolate the widget. For example, it's common practice to hard-code an HTML element ID and then find it with jQuery; this works fine for one widget, but prevents multiple widgets with the same ID.

Widget Wrangler has no dependencies on SharePoint or other script libraries, and works with the same browsers as AngularJS. IE8, which is only supported by a special build of AngularJS 1.3/1.4, is not currently supported – ergo it will not work with SharePoint 2010 which forces the pages to run in IE8 emulation mode. Widget Wrangler works with the same browsers as SharePoint 2013.

Widget Wrangler tries to load the scripts needed by each widget as efficiently as possible, and will only load each script once even if it's used in multiple widgets. (NOTE: The current implementation determines what scripts are the same using their URL; a future version may be smart enough to identify multiple versions of common libraries at different URL's.) Use the "priority" property in the ww-appscripts attribute to control parallel script loading; for example all priority 0 scripts will load in parallel, followed by priority 1 scripts, etc. Priority numbers must begin at 0 and must be contiguous (i.e. 0, 1, 2…) In the example above, script.js depends on AngularJS, so AngularJS is given priority 0 (and loads first), and script.js is loaded only when Angular (and any other priority 0) scripts are loaded.

The main repository for the Widget Wrangler is here; it's also a part of the OfficeDev Patterns and Practices Library here. Please use the main repository for access to the Widget Wrangler tester and for pull requests.

Widgets and JavaScript Frameworks

Widgets can be written using any number of JavaScript frameworks; this section will explore some of the most popular.

AngularJS Widgets

AngularJS is a favorite framework to use with widgets, mainly because of its MV* design pattern and rich selection of services and directives. However AngularJS was really designed for single-page applications (SPAs) that take over an entire web page. A typical AngularJS application is "auto-bootstrapped" using the ng-app directive; while this is fine for SPAs, the documentation clearly states that you can only have one ng-app directive on a page.

To get around this limitation and allow many widgets on a page, the Widget Wrangler uses the angular.bootstrap() function; there is no hard limit on the number of Angular apps that can run on a page using this method.

(NOTE: If you want to use Widget Wrangler in a page that already uses AngularJS, ensure that the widget doesn't overlap the existing Angular application – i.e. it can't be inside the element that is decorated with ng-app. Also ensure the versions of Angular are the same or similar enough that both the SPA and widget(s) will work with either one.)

You can find a simple AngularJS widget at http://bit.ly/ww-ng1. This sample uses Plunker so you can run and experiment with the code right in your web browser. In this sample you'll see two instances of a Hello World widget which vary only in their view so one of them says goodbye instead of hello. This shows how to embed the view right into the widget so you can make each instance render differently.

A more advanced example can be found at http://bit.ly/ww-ng2. This example shows a weather forecast, and demonstrates how to pass configuration information – in this case the location of the weather forecast – into the application via the ng-init directive in the view. It also shows how to use ng-include to place the view in an HTML template so it's shared by all instances of the widget.

WeatherWidgets

Weather Widgets

Here is the markup for one of the weather widgets:

The Angular controller includes a function to fetch the weather forecast as soon as Angular processes the ng-init binding:

A third example at http://bit.ly/ww-ng3 shows how to connect two Angular widgets. This is accomplished via a service that relays messages in the form of JavaScript objects from senders to receivers over named channels.

If you look at the code you may notice that this service communicates via a shared object that hangs off the window object. Normally in Angular a service could store such an object locally, and the service (declared as a factory) would be shared by all who reference it. But that doesn't work here since each widget is a completely separate Angular application. Modules, services, etc. with the same names are all isolated completely within each widget, and Angular does a great job keeping them separate. In the sample, each sender and receiver widget gets its own service instance, so information is shared outside of Angular in the window object.

Knockout Widgets

KnockoutJS is another great example of an MVVM style JavaScript library. There's an example of simple Knockout widgets athttp://bit.ly/ww-ko1. There are two instances of the widget on the page to demonstrate isolation; here is one of the widgets:

Notice that this time the ww-appBind attribute is specified; this contains the binding function myWidget.Load. script.js contains this function:

Notice how the binding function uses the new keyword to make a new ViewModel object for each widget; without this, isolation would be lost and all the widgets would share the same ViewModel and data.

jQuery Widgets

Here's an example that not only shows a jQuery widget, but demonstrates how to take existing jQuery code and make it into a Widget. In this case, it's based on this jQuery UI example of a color picker. The original sample includes several references to specific element ID's, so the code would need to be modified to handle more than one color picker on a page.

ColorWidgets

jQueryUI sample made into a widget, now supports multiple instances on a page

You can see the widget version at http://bit.ly/ww-jq1. As you can see, there are two instances of the widget on the page; all the code is shared yet they work independently. To make this work, the following code changes were needed:

  • Change the element ID's to classes, so it's legal to have more than one
  • Add a bootstrap function similar to the Knockout example, that creates a new "controller" for each widget instance
  • When the widget bootstraps, pass the element into the jQuery code and reference the elements relative to the element. For example, $('#red') becomes $(element).find('.red')

Plain JavaScript Widgets

Sometimes less is more, and plain JavaScript is better and faster than using even a light-weight library like jQuery. If you want to use Widget Wrangler on its own, without any other libraries, check out the example at http://bit.ly/ww-js1. This is a widget that Ford Prefect would love!

Notice that it uses the new keyword in the binding function to create a new object for each widget instance. It also generates a unique index for each instance that's used in a button click attribute. This index is passed into the click event handler to allow it to find the correct instance when the event fires.

Widgets in SharePoint

The Patterns and Practices library includes an example that shows how to use widgets in various kinds of SharePoint projects. The example is a Microsurvey that asks a single question, then shows a simple graph of all the responses to that question.

SurveyWidget

Microsurvey Widget – Question and Results Views

The example can be packaged and deployed three ways:

  • As a SharePoint Hosted Add-in
  • Directly in a SharePoint site using drag-and-drop deployment by and end user
  • Directly in a SharePoint site using PowerShell deployment from a central site, so a single copy of the solution can be used in many sites. This has the advantage that the solution can be updated in one place and the change will be immediately available in all sites.

The solution includes a web part and custom new, edit, and display forms for managing the list of questions. It's also smart enough to deploy its own list storage using JavaScript, so the questions and answers lists are generated the first time the solution is used.

Widgets allow a high degree of reuse in this example. For example, the code to display a question is written as a widget; it appears in the web part (or add-in part), and in the New and Edit forms. Thus one copy of the widget is used in 3 places, reducing code duplication and allowing all of them to be updated by editing the common code.

For a deep dive on the Microsurvey sample, including a quick introduction to AngularJS, check out Bob's Collab365 talk,Building Flexible SharePoint Solutions with AngularJS. This will show you various ways of using and deploying widgets in SharePoint, however it uses the precursor to Widget Wrangler, which was called InitUI.js. The sample code in github has since been updated to use Widget Wrangler.

The Widget Wrangler Manifesto

The Widget Wrangler is open source, and we welcome suggestions and pull requests at https://github.com/Widget-Wrangler/ww. (Please submit pull requests against the dev branch!) If you're thinking of contributing, please keep these points in mind. Widget Wrangler:

  1. Has no dependencies on any other scripts or frameworks
  2. Is easy to use
  3. Minimizes impact on the overall page when several instances are present
  4. Matches AngularJS 1.x browser support
  5. Is tested and works well with SharePoint Online and SharePoint 2013 or greater, however it in no way depends on SharePoint

Widget Wrangler Tester

The Widget Wrangler main repo includes a test program that makes it easy to exercise the library with a large number of widgets on a page.

WidgetTester

Widget Wrangler Tester

The test program is written in ASP.NET, and it dynamically generates test scripts and Angular applications that check to ensure that dependencies are loaded, and that track the elapsed time during the test. To run it, start the WWBase project in Visual Studio on the Test/TestPage.aspx page.

Enter your scenario in the text box on the left side of the page. Each line in the scenario is a widget entered in the form:

In this example the tester will fabricate two scripts, and set up the Widget Wrangler to first load Script1, then Script2, and then bootstrap the application called AppName. Here's the widget the test program would generate for this line in a scenario:

You can test parallel script loading by using parenthesis; for example:

will generate a widget that loads scripts S1 and S2 in parallel, then loads S3 when both of those have loaded.

The test program shows an index for each widget to demonstrate that each one is isolated, and a blinking asterisk to show that the data binding continues to work after all the widgets are loaded. On the right of the screen, you can see a log of scripts loaded and the timings.

Backlog

Here are some of the enhancement ideas on our backlog; please comment and help us set our priorities!

  • Smarter detection of duplicate or already loaded scripts (e.g. AngularJS loaded from two different URL's)
  • Version number checking for libraries such as Angular and jQuery, so a widget can declare the range of versions it supports; possible co-existence of multiple library versions (See this proof of concept)
  • Angular 2.0 support
  • Diagnostic widget you can add to a page to show load sequence, timings, and exceptions
  • IE 8 support (to have parity with SharePoint 2013 browser support)

 

JSLink Validation – from Basic to Advanced

Custom field validation using JSLink is an extremely powerful beast. In this post I’m going to make an effort to demystify the different levels of validation you can put into your custom template and how to put it together. Everything I’m about to cover has been covered before in different ways and in different combinations. My hope is that this will help separate out what’s needed and what’s not depending on your scenario… so to that end I’ll cover three scenarios. Basic, which will be OOB validation that is custom applied. By that I mean you want to optionally make a field required just like SharePoint does, but you want to control when it’s required.  Custom, which will be a custom validation function that renders its error message just like OOB validation error messages are rendered.  And finally, advanced, where not only do you want to write a custom validation but you want to control how the error state is communicated to the user.

So let’s start at the beginning and we’ll build on the solution from there. First I want to establish the framework for the solution:

Basic

Basic validation is fairly straight forward. You would simply add this code inside of your custom field rendering function (editTaskOwner).

First set up the form context and then create a new “ValidatorSet”:

In the next line we add the new validator to the validation set:

And then lastly, we attach the validation set to the field. In the case of this example I’m using formCtx.fieldName… but this could obviously also be a simple string. I bring this up, because there are limitations on what types of fields you can customize using Custom Templates, namely Taxonomy fields… this is a way to add validation to them from somewhere else in your code.

Note: If you’ve noticed I skipped line 4, more on that later.

The Result

Custom

If you want to write your own validation then you need to do a few extra steps.

Create the custom validation function. This function would go within your validation function but outside of the field custom render function (see the framework at the top)

Modify the RegisterValidator call

 (Optional) Depending on how you render the field you may have to add the following code. What I mean by that is if you use one of these OOB field rendering functions you do not need this line, if you develop your own layout then you will need this to “attach” the error message to the right object in the DOM. In this example my custom people picker field is rendering html wrapped with <div id=”TaskOwnerDiv”></div>. So I need to reference the div’s ID in the SPFormControl_AppendValidationErrorMessage call.

The Result

Advanced

So, if that didn’t seem advanced enough for you, the last scenario is that you may want to customize how the “error” is displayed to the user. Maybe you want to display an image, or collect all the validation messages into one area. That’s possible by doing the following:

Write custom error rendering code. This code needs to be completely outside of the custom rendering template code. Here’s a really basic example.

Modify the registration of the error callback, which causes your custom function to be fired if the isError flag is true.

The Result

So, as you can see custom form validation is extraordinarily powerful with Custom Templates and can allow you to really take SharePoint to the next level.

JSLink Custom User Field Schema

I had the requirement of setting the default value of a person field to the current user.  After looking around in the great wide internet I found a very helpful article by Glenn Reian which got me started.

Where I ran into a problem was that my user field had customized settings that weren't being pulled through into the custom implementation of my people picker.  As it turns out the issue was with the schema that is passed to the SPClientPeoplePicker_InitStandaloneControlWrapper function.  In Glenn's example (and every other example I found out there) this schema is hard coded, which is perfectly acceptable in most cases.  However, I needed some values to be slightly different to adhere to my column settings.  

As it turns out there are two solutions.  The first, obvious one, is to adjust the schema manually in the code.  And again this may be a fine solution.  But as Glenn did, I had separated my concerns and created what I hoped to be a fairly reusable version of initializePeoplePicker.  So now I needed to enhance that function to pass through adendums to the schema or maybe it's own schema.

What I found was something i wasn't quite expecting.  The schema I needed was actually right there in the context variable in JSLink.  So, using Glenn's implementation and extending it slightly I just modified initialzePeoplePicker to the following: 

var initUserDefaultPeoplePicker = function (ctx, peoplePickerElementId, ppSchema) {
    if (ppSchema === null) {
        ppSchema = {};
        ppSchema['PrincipalAccountType'] = 'User';
        ppSchema['ShowUserPresence'] = true;
        ppSchema['SearchPrincipalSource'] = 15;
        ppSchema['ResolvePrincipalSource'] = 15;
        ppSchema['AllowMultipleValues'] = false;
        ppSchema['MaximumEntitySuggestions'] = 50;
        ppSchema['Width'] = '280px';
    }
    var uri = _spPageContextInfo.webAbsoluteUrl + "/_api/SP.UserProfiles.PeopleManager/GetMyProperties";
    getAjax(uri).done(function(user) {         
        // Set the default user by building an array with one user object
        var users = new Array(1);
        var currentUser = new Object();
        currentUser.AutoFillDisplayText = user.DisplayName;
        currentUser.AutoFillKey = user.AccountName;
        currentUser.Description = user.Email;
        currentUser.DisplayText = user.DisplayName;
        currentUser.EntityType = "User";
        currentUser.IsResolved = true;
        currentUser.Key = user.AccountName;
        currentUser.Resolved = true;
        users[0] = currentUser;         // Render and initialize the picker
        SPClientPeoplePicker_InitStandaloneControlWrapper(peoplePickerElementId, users, ppSchema);
    });
};
and then from the custom rendering function for the user field I passed the schema associated with the field through:
function efTaskOwner(ctx) {
    var retVal = '<div id="TaskOwnerDiv">';
    retVal += initDefaultPeoplePicker(ctx.CurrentItem["TaskOwner"], 'TaskOwnerDiv', tx.CurrentFieldSchema)
    retVal += '</div><span class="etmRequiredField"></span>';     
    return retVal;
}

SharePoint 2013 JSLink – All Fields Rendered

While creating a custom Client Template using JSLink, I came up against the issue of knowing when all the fields were rendered on the form.  To explain where the issue arises let me first take just a moment to explain when building a custom template for this type of form, where you want to manipulate the fields, you have available to you both a Pre and Post Render function.  What that does is fire the function attached to it either pre or post each custom field rendering being executed.

The reason I bring this up is that there could be some misconception that it fires before field rendering starts and after all field rendering is complete, but that’s not the case. So if your form has 10 fields, these functions will each fire 10 times.  I also found document.ready to be unreliable as it often fired before all the fields were rendered, and further, if I needed to make decisions based on the context of the form, I would no longer have access to that information.

So, the solution does in the end involve the OnPostRender function of the Template Override, but what you do there is what counts. So just to put everything in context, and for brevity, here is the shell of the custom Client Template file.  Note the declaration of the postfields variable inside of My.CustomTemplate.

Ok, now we need to fill in the onPostRenderTemplate function.  Primarily, we need to know when we’ve gotten through all the fields on the form. This is accomplished by incrementing the "global" postfields variable within the onPostRenderTemplate function.  The question is what are we testing it against to know when we've rendered all the fields.

The answer is JavaScript prototype function keys which seems to be fairly well supported.

The Object.keys() method returns an array of a given object’s own enumerable properties, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).

Ergo, if you look at the ctx.Template.Fields and get the length that gives you the number of Fields on the form that will be "rendered" and provides you a way of telling when the last Field has been rendered.

So now I can execute some fancy functions to do thinks like:

Hide Fields

Modifying the Fields label to make it look like it was Required

or some other post rendering customization based as I stated on values in the ctx variable.

Send Email through Visual Studio Workflow in SharePoint 2013

EmailActionWhen building a custom SharePoint 2013 Workflow using Visual Studio I ran into a very odd problem trying to send a notification email to a group of users using the Email action (specifically Microsoft.SharePoint.DesignTime.Activities.Email). 

I could send email from a SharePoint 2013 workflow created using SharePoint Designer, but could not do the same from my custom workflow created in Visual Studio without the workflow throwing an error.  I’m not even going to post the error because as it turns out it’s a generic error message that could show up for any number of reasons and has little to nothing to do with the problem which was partially why it took so long to solve.

My colleague Bob German (Blog ~ twitter: @Bob1German ) gave me an assist debugging and testing out the issue… By using Fiddler, set up to run on my SharePoint server under the Workflow Manager App Pool account, I was able to see why the call works from a SharePoint Designer workflow but not my custom VS2013 workflow. What I found was that when the call was made from the SharePoint Designer workflow, the “To” address was in the form of the users login name and not the email address.  So even though the documentation states that the “To” property “Gets or sets the user names or email addresses that represent the recipients of the email message.”, that is apparently not entirely the case.  By tweaking the value I pulled from the LookupSPUser activity from Email to LoginName and adding that value to the recipient’s collection, everything worked beautifully.

LoginName

As an aside, from this same Fiddler investigation, I also found that the SharePoint Designer email action formats the body as an HTML message and if you want to use that you can by embedding your body in between the following tags:

<HTML><HEAD> <META name=GENERATOR content=\"MSHTML 11.00.9600.17924\"></HEAD><BODY><FONT face=\"Segoe UI\">

<Email Body Here>

</FONT></BODY></HTML>

Future Proof – Javascript Dashboard Code Samples

To support my colleague Bob German and his great talk on Future Proofing your SharePoint solutions I helped him by building a SharePoint charting dashboard that used client side scripting libraries to pull data from a SharePoint list and display it using commercially available client side charting library.  He has asked me to share those solutions and so finally here they are.  I really appreciate all of Bob's great feedback and hope these examples can help people out there get started on building their own dashboards.

ScreenShot_2010

For all the great information, please see Bob's article that talks about Future Proofing your apps at http://blogs.msdn.com/b/bobgerman/archive/2013/10/05/future-proof-solutions-part-1-take-a-walk-on-the-client-side.aspx

Javascript Dashboard Demo

More Future Proof Code Samples from Bob German

Quick Primer on InfoPath and SharePoint 2010 Time Zones

InfoPath functions such as Today() and Now() pick up the time zone based on the settings of the SharePoint server.  By default the web is set to the time zone of the server, which is thereby the default of any new sites created as well as new user profiles created.  For global organizations with users around the world, there are situations where you will want the automated date/time stamps to display the local time for the user rather than the server time.  This article will show you how to set that up.

The time zone of the web can be changed in central administration.  The time zone of the site can be changed by going to site settings / regional settings.  In addition a user can change their personal time zone settings by going to “my settings / my regional settings”. 

The InfoPath form I created to test the scenario has 2 fields.  The first is set by default using the now() function when the form is created, the second is set by an action on form load, again using the now() function.  The first field is also used to name the form when it’s first submitted.  Using the magic of VMs, I set the time zone of the host machine and the site settings to be Hawaii time zone (GMT-10).  I then have a separate test user whose regional settings I set to Pacific (GMT-8) and am using the browser on a machine whose time zone is set to EST (GMT-5).

On the machine whose time zone is set to Hawaii, I created an instance of the InfoPath form.  Note that not only are the two fields set to the current Hawaii time but also the Created and Modified dates show as Hawaii time (Figure 1).

  Image5

Figure 1: Form in Hawaii time zone from machine in Hawaii time zone.

If I log into SharePoint as the same user but from the host machine whose time zone is set to EST I still see the exact same results.  Note the machine’s time is 12:35. (Figure 2)

  Image6

Figure 2: Form in Hawaii time zone from machine in Eastern time zone

Now, by logging into SharePoint as the user who changed the regional settings to Pacific time, I see something very different (Figure 3).  Note that the Created and Modified dates in the SharePoint list are adjusted to my regional settings which are Pacific time zone.  And when I open the form the refresh date time uses the now() function which uses my regional time zone setting to set that date/time.

  Image7

Figure 3: Form in Hawaii time zone from machine in Eastern time zone as user in Pacific time zone.

Conclusion

If you need all the date/time stamps to appear the same regardless of the user’s location, then either override or leave the default regional settings for your web and make sure the users do not change the regional settings of the web.  If the users want to see the information converted to their own time zone, they should change their regional settings to their own time zone.

InfoPath – Show links to documents in a library from the form

I had an interesting client issue recently where the users were copying and pasting the contents of entire e-mail messages into a text box inside an InfoPath form.  Although useful in that the information was captured with the form, the implementation was tedious at best when trying to find information potentially buried there.  Long story short by putting the e-mail messages in a document library that was linked to the InfoPath form library by the ID I was able to have a parent-child relationship.  The issue then was how to display those e-mails from within the form. 

Obviously the ideal solution was to be able to actually preview them, but barring the funds to take on a challenge like that, and very little hope that the exercise would be fruitful it was decided to try and at least show a list of the "messages" that were stored in the document library related to the form that was open.  Sounds simple I'm sure, and in the end it was fairly straight forward but a few little gotcha's had to be worked around.

My first thought was to make a secondary data connection to the document library in question.  Unfortunately, I figured out rather quickly that getting the name of the file or the path to the file wasn't happening.  Enter REST services for SharePoint… i.e. /_vti_bin/listdata.svc

The first step was to validate the URL for the REST service that I would add as a datasource in the InfoPath form.  To do this I used the URL http://sp2010/test/_vti_bin/listdata.svc where sp2010/test is the path to the site that housed the document library.  What resulted was this:

listdata.svc screenshot

If you then modify the URL to put /TestDocuments after the service call… in other words: http://sp2010/test/_vti_bin/listdata.svc/TestDocuments you will see the contents of the Test Documents library.  There are a lot of references on the web for quering REST data so I won't go into it here suffice to say that the best way to figure out what you need to query is to view the source of the resulting page.  So when I showed the contents of the Test Document library I saw the following:

Test Document listing

Then when I viewed the source of the page I could see that to filter for the Form field I would need to use "FormId" (I found out the hard way that this seems to be case sensitive as FormID didn't work).

Image3

Ergo, my final url to show all the documents filtered by the Form field (in this case where that value was 1) would be:
http://sp2010/test/_vti_bin/listdata.svc/TestDocuments?$filter=FormId eq 1

Ok, now I needed to be able to connect to the data from InfoPath.  That's a simple enough process, simply add a data connection for a REST Service.  Use the URL from above, but do not have it retrieve data by default.

 Now you have a couple options about how you form the URL.  If you want to linked document to open in the browser you're going to have to jump through some serious hoops.  Certainly doable though and I'll have more on that in a moment.

If it's ok that the document opens in the client side applications than the solution is much simpler, all you'll need to do is create a link to the document using the following XPath function: concat(xdServerInfo:get-SharePointSiteUrl(), m:properties/ns1:Path, "/", m:properties/ns1:Name)

So step by step, here's how you'll finish configuring your InfoPath form:

1. Insert a repeating section and bind it to the "entry" of the REST service.

2. Add a hyperlink control into the repeating section and set it's "Link To" data source value to concat(xdServerInfo:get-SharePointSiteUrl(), m:properties/ns1:Path, "/", m:properties/ns1:Name).  You can also set the "Display" data source to m:properties/ns1:Name.

3. If you haven't already done so, create a secondary service to the same SharePoint list that you're submitting the InfoPath form to and get the ID value.  Make sure you're only getting the value for the current form, and that you're getting it automatically on form load.

4. Create an FormLoad action event (or a Action rule on a button) and set it to "Change REST URL".  Set the URL for the REST call to: concat(xdServerInfo:get-SharePointSiteUrl(), "_vti_bin/listdata.svc/TestDocuments?$filter=FormId eq ", max(ID)).  This will then load the list of documents to display in the form.

Voila, a list of document hyperlinks shown in the InfoPath form.

Open Documents In Browser

Now, if opening the documents using the client application isn't enough here's the good, the bad and the hack you need to put together to have your hyperlink open the document in the browser window.  First, an example function:

concat(xdServerInfo:get-SharePointSiteUrl(), "_layouts/", concat(substring("Word", 1, contains(m:properties/ns1:Name, "docx") * string-length("Word")), substring("xl", 1, contains(m:properties/ns1:Name, "xlsx") * string-length("xl"))), "Viewer.aspx?id=", m:properties/ns1:Path, "/", m:properties/ns1:Name)

Told you it was going to be ugly.  This section of the function
concat(substring("Word", 1, contains(m:properties/ns1:Name, "docx") * string-length("Word")), substring("xl", 1, contains(m:properties/ns1:Name, "xlsx") * string-length("xl")))
is what decides which viewer you need to use.  The example above only handles .xslx and .docx documents… you'll need to expand the function to support other types of documents.

Now the hack.  This kills me actually.  The hyperlink control in InfoPath does not support XPath functions as the "Link To" value.  Ergo, you can't just put this function in the hyperlink control and have it work nice and neat.  The work around I came up with involves using a rule to set the value of one of the fields in the REST secondary datasource to the XPath for the hyperlink and then referencing that field from the hyperlink's "Link To" data source property. 

I know, this is ugly, but I figured since we're only going to be reading data I can use one of the fields I don't need to display to the users as a holding area for this information.  If you wanted to, the much cleaner solution would be to write code behind and populate a repeating field in the main section of the InfoPath form with the hyperlink values and probably the display value too.  However, code behind wasn't acceptable in my scenario.

Ok, so here's what I did.

5. Go to the REST secondary data source, expand entry and select m:properties.

6. Add an action rule to "Set a fields value" and then set a field from the properties area (I used CopySource) to the XPath value shown above.  What will happen is that as the data loads the CopySource field will be populated with the hyperlink you want that particular entry to use to load the document in the browser.

7. Replace the "Link To" data source value of the hyperlink control you added in Step 2 with the field you set the value of in step 6.  You should have already set the "Display" data source, but if not go ahead and do it now.

Voila, publish the form and the list of documents will be displayed for the user that show the linked document in the browser when clicked on.

Image4

Using SQL Reporting Services Report Viewer Web Part to display Access Services report.

As you may or may not be aware when an Access database is published to a SharePoint site the reports in the database are converted into SQL Reporting Services reports.  Great, you think… that should mean you can host the report elsewhere in the SharePoint higherarchy simply by using the SQL Reporting Services Report Viewer Web Part.  And in fact, you can, it's just a little tricky getting it configured.  Certainly not anything you can't overcome but hopefully by posting this information it'll help save some time.

So first, we need to create the page that we're going to put our report on.  Next, you're going to want to insert a SQL Reporting Services Report Viewer Web Part.  And finally, we need to configure it, which includes setting the location of the report as well as any report aparameters.

Configuring the Web Part

The first step is to set the report that the Web Part should display.  To do that you need the URL to the rdl file that represents the Access report.  To get that, browse to the SharePoint site hosting the Access application.  Next, manually change the URL after the site name to show all site content.  You do this by appending "_layouts/viewlsts.aspx" after the site which should look something like the following where "Access Services Demo" is the name of my SharePoint site:

ViewListURL

Now in the listing of the site's contents you'll see a document library called Report Definitions.  Browse to that library and you'll see all your Access Reports defined there.  Now, go back to the web part properties.  As shown below click on the ellipses of the Report parameter and then put in the URL of the report definition document library.

ReportViewer

When you click the green arrow it will load a list of the reports available in that library.  Select the one you want to display and click OK.

SetReportLocation

Now, back in the web part properties expand the parameters section and click on Load Parameters.

In Access Services all reports have the following parameters:

  • AccSrv_SiteId: This is the GUID of the root site collection.
  • AccSrv_SiteZone: Default
  • AccSrv_WebId: This is the GUID of the site hosting the Access application
  • AccSrv_TimeOffset: TimeZone offset
  • AccSrv_Where: A where clause for the report
  • AccSrv_DataLCID: The Data Language Reference ID (1033 is the English Default)
  • AccSrv_CollationLCID: The collation Language Reference ID (2070 is the English Default)

I have found that for most reports setting the AccSrv_SiteId and AccSrv_WebId are all that is necessary for the report to generate properly.  However, under certain circumstances where there was summation and grouping in the report I've had to set the DataLCID value to 1033 (the default for English).  All the other values can be left to use the report default.

Note: There are many ways to get the GUID for a site but I find the quickest way is to use a tool like SharePoint Manager 2010. 

If you have the need to adjust the Where parameter of the report keep in mind that you need to pass the entire where clause as you would type it in the filter parameter of the report if you were working in Access.  You also must include a beginning "=" sign. 

For instance the following "Where" clause limits the report to those records where the value of the [Quarter] field is equal to 1.  (Even though [Quarter] is a numeric field I still have to pass it as a string).

WhereClause

And then here's what the report looks like when rendered:

Report

If you want to pass the Where clause in using a Query String filter you can do that but you'll first need to modify the parameter of the report by going back to the Report Definition library, and modifying the parameters of the report so that the AccSrv_Where parameter is changed to "Prompt" instead of hidden.  The only downside is that the report shows the user the where clause when it's rendered but this can be overcome by using a little JQuery script to hide the <div> that shows in the report.

ManageParameters

Note also in the above screen shot that one of the option is "Edit in Report Builder".  If you don't have a complex report you can modify the report definition using this SQL Report Builder tool, which is downloaded for you automatically.  This was how I initially determined the values of some of the parameters by showing them in the header and then running the report from within the Access UI on the SharePoint site.

Setting an Application Page Title from code

I’m currently dealing with a requirement where I need to the set an application page’s title from the code behind.

By default the form has a ContentPlaceHolder control with a
ContentPlaceHolderID="PlaceHolderPageTitle"
which in the master page is the text that will fall in the header's title tag. Unfortunately, <asp:Content> controls are not added to the hierarchy at runtime so I can’t access it directly.

There are good ways to modify this title on the client side with Javascript but client side code doesn’t really suit my needs as the title will come from some custom manipulation that’s better done on the server. It did cross my mind that I could do this minipulation and then post it back in script to have it update but it seemed kind of like taking the long way around. Anyway, long story short I found a way to access that title in the Page_Load event but if anyone out there has any better ideas I’d love to hear them.