More About Managed Metadata

I recently had more fun with setting Managed Metadata values in SharePoint 2010.  This time as part of an event receiver during the Item Adding, Item Updating, and Item Updated events.  (There are interesting event ordering challenges which lead to using all of these events, but that is not the focus of this blog.)  The challenge I faced was copying values from a Document Set down to documents that were being created/uploaded to the list.  Normally, using Shared Properties would negate this but because we wanted use to edit the Managed Metadata Values in the Document Information Panel we could not use shared values because the Document Set would immediately overwrite any changes made to the document.  Instead we created an Event Receiver to manually handle the updates from the Document to the Document Set, and from the Document Set to the child documents.

The issue arose that copying the Document Set values to the Document didn’t work.  In the logging we could see our Set function get called and return success, but the values never seemed to stick.  I was using code I’ve previously blogged about:

SPField mmdField = spListItem.Fields.GetFieldByInternalName(“MyMMDField”);
string validMMDString  = mmdField.GetValidatedString(spListItem[“MyMMDField”]);
newSpListItem[“MyMMDField”] = validMMDString;

Since logging was unable to provide enough information I began testing using PowerShell.  Which looked something like:

$field = $listItem.Fields.GetFieldByInternalName(“MyMMDField”)
$validString = $field.GetValidatedString($listItem[“MyMMDField”])
$newListItem[“MyMMDField”] = $validString
$newListItem[“MyMMDField”].ToString()
>> ouput the same as the $validString
$newListItem.Update()
$newListItem[“MyMMDField”].ToString()
>> [Empty]

Interesting, before the Update() my value is there but after I loose the value.  After looking around a bit I ran across this blog, and although it was about using the web services it provides a key piece of information: Managed Metadata Fields have a Lookup Field AND a Note field.  I began exploring the various fields, again through PowerShell, and was able to identify the Note field associated with my Managed Metadata Column.  This value looks almost identical to what is in the Managed Metadata Field but does not include the WSSID.

Example:

Taxonomy Field:
123;#My Term|00000000-0000-0000-0000-000000000000;#124;#My Other Term|00000000-0000-0000-0000-000000000001

Note Field:
My Term|00000000-0000-0000-0000-000000000000;My Other Term|00000000-0000-0000-0000-000000000001

At first it appeared at the Managed Metadata Note Column’s Internal name was the Managed Metadata Column’s Column GUID with the ‘-‘s removed.  That worked great for 2 of my 3 fields, but one field didn’t use that same pattern.  Using PowerShell I printed the Managed Metadata Field Definition to the console and discovered a key property: TextField.  This field is a GUID that stores the (MSDN Article Here) Note Field’s GUID and allows you to get the Manage Metadata’s associated Note field.

Finally, once I began setting both the Managed Metadata Field AND the Note field all of my Managed Metadata values would successfully populate.

Final Code
Note: I did not include checks to ensure cast of the field to Taxonomy Field was successful and I’m also assuming that spListItem and newSPListItem share the same columns.

SPField mmdField = spListItem.Fields.GetFieldByInternalName(“MyMMDField”);
TaxonomyField taxField = mmdField as TaxonomyField;

string validMMDString  = mmdField.GetValidatedString(spListItem[“MyMMDField”]);
string mmdNoteString = spListItem[taxField.TextField].ToString();

newSpListItem[“MyMMDField”] = validMMDString;
newSpListItem[taxField.TextField] = mmdNoteString;
newSpListItem.Update();

Advertisements

Setting Managed Metadata

I recently had an issue trying to set the Managed Metadata field value on a new list item I was creating in C# code.  In the control where the user could select the managed metadata value I was using the OTB Taxonomy Control which was properly bound to my Managed Metadata Field.  From that I would then retrieve the ‘Value’ (Label|Guid) of the Term selected and call the TaxonomyFieldValue PopulateFromLabelGuidPair.  This would successfully retrieve a TaxonomyFieldValue and I would this use this as follows:

TaxonomyFieldValue mmdFieldValue = new TaxonomyFieldValue(string.Empty);
mmdFieldValue.PopulateFromLabelGuidPair(mmdFieldControl.value);
item[field.Id] = mmdFieldValue;

No exceptions were thrown, but when reviewing the list item the Managed Metadata Value was not present. I added the following lines to determine what value was actually in my mmdFieldValue variable being used to set the item’s field:

string validatedString = field.GetValidatedString(mmdFieldValue);
string validatedString2 = field.GetValidatedString(mmdFieldValue.ToString());
string justString = mmdFieldValue.ToString();

What I noticed was the validatedString2 was usually an Empty string, while the validatedString was populated, but included a leading integer. The justString usually had a value of [Label]|[GUID], as expected.

With some experimenting what I found was the validatedString, no matter what value I selected for the field always had the SAME leading integer ‘0’. This was the key to resolving the issue, and what I found was the TaxonomyFieldValue object has three properties: Label, GUID, and WssID.  The WssID was the key, it’s value was also always 0 so I tried changing it to a -1 and suddenly all my Taxonomy Values worked.

Final Code:

TaxonomyFieldValue mmdFieldValue = new TaxonomyFieldValue(string.Empty);
mmdFieldValue.PopulateFromLabelGuidPair(mmdFieldControl.value);
mmdFieldValue.WssId = -1;
item[field.Id] = mmdFieldValue;

Fixing IE11 Enter Key Problem with Search

I was recently working for a client who began having issues with the Windows 8.1 baseline and their search center.  If you are familiar with Windows 8.1 then you are aware that it comes with IE 11 which has a new User Agent string that can cause some issues with SharePoint (ref: IE11 Broke SharePoint 2010).  My task was to figure out why when a user entered a search term/key word and hit enter the page simply refreshed.

I tried several ideas, including attaching to the form submit action, to no success so I began searching the web for any other possibilities I had not though of.  Interestingly, I began running across several similar issues with Firefox from back in the SharePoint 2007 days.  After doing a little reading I began experimenting using IE11’s developer tools and found it has a similar issue to Firefox from years ago.  Here is the problem:

SharePoint’s Keyword Input has a function that does the following check

if(event1.which == 10 || event1.which == 13) { …<submit the form>; return false; }

The problem is that IE 11, when the IE-8 Compatibility Meta Tag is present on the page, returns undefined for the event1.which and therefor our submit function is never called.  Several people had this same issue with Firefox and there were lots of examples on how to resolve, but most required you hard coded the <submit the form> function name, which may or may not be consistent from site to site or even page to page.

I wanted something a bit more dynamic, and what I realized was if I grabbed the Submit function from the actual search button I could just call it directly.  In addition, rather than adding another chained function to the keypress DOM event, which could be bypassed if the event was canceled, I wanted to override the current function with my own definition.

Here is the resulting code I have used which works in IE 11, Firefox 27.0.1, and Chrome 33.0.1750.154:

function ensureReturn()
{
var jsFunction = $(“input[name=’InputKeywords’]”).attr(“onkeypress”);
var jsSplit = jsFunction.split(‘ ‘);
var funcName = jsSplit[jsSplit.length-1].split(‘(‘)[0];

jsFunction = $(“a[title=’Search’]”).attr(“href”);
jsSplit = jsFunction.split(‘:’);
var submitFuncName = jsSplit[jsSplit.length-1];

var newFunc = funcName + ” = function(event) { if(event.keyCode == 10 || event.keyCode == 13) { ” + submitFuncName + “; try { event.cancelBubble = true; } catch(e){} return false; } };”
//newFunc should be something like:
//function SFBE88B03_OSBEK(event1) { if(event1.keyCode == 10 || event1.keyCode == 13) { SFBE88B03_Submit(); try { event1.cancelBubble = true; } catch(e){} return false; } }
eval(newFunc);
}
_spBodyOnLoadFunctionNames.push(“ensureReturn”);

Once that was placed in a JavaScript file and included on the search and results pages the enter key began working again.

I Hate SharePoint Apps like I Hate Mark Zuckerberg

This blog post has been tumbling in my head since I began working on SharePoint 2013 Apps.  This is probably the third time, at least, I have tried to express my thoughts on the development shifts in SharePoint from 2007 & 2010 to 2013.

Beware some things in this post may be offensive to people, especially Mark Zuckerberg, but I wanted to bring a little humor into the geek speak.

Do I Really Hate SharePoint Apps?

I think hating SharePoint Apps is like hating someone like Mark Zuckerberg for creating Facebook.  I don’t hate him for creating Facebook and making billions, I hate that I didn’t think of creating Facebook first.  I could have created a web site where we share things that should be kept to ourselves.

“… I meet this chick at the bar and we hooked up and now my _____ is all swollen and it burns when I …”,

WOW, I really didn’t need to know that, but oh great you also included a picture!  Because that image won’t be permanently burned into my memory from here until the end of time, THANKS Friend! And now to perform a lobotomy on myself.

Or where adults can act like immature teenagers by unfriending each other (please read with ditsy Valley Girl ‘accent’)

“Suzie unfriended you because your friends with Katie who just started dating Charlie even though she knew that Suzie liked Charlie and wanted to go out with him.”

Yeah, next time you read your Facebook feed I dare you not to think of this post!

But alas, Mark beat me to it.

So no, I don’t hate SharePoint Apps, but there are parts of them I haven’t fallen in love with, or even understand why they work this way instead of that.

Augmenting SharePoint or Bolting On

I was talking with a coworker recently and described my past, and current, experience with SharePoint and described it as “Augmenting SharePoint” either to improve the user experience (custom content types and lists to allow users to collect information consistently), build automation capabilities/processes (workflows that would handle publishing and retiring of content), or use data from other systems to enhance information in SharePoint (KPIs from Project Server along with Task Lists in SharePoint).

As my coworker pointed out, Apps are much less of the augmentation and much more of a bolt on.  Sure, you can borrow some of what SharePoint has in Apps, but your App isn’t going to be used in the same context and location that user created a document library or enabled/disabled features.  Our Apps are isolated, associated with SharePoint, but they aren’t SharePoint just like a Zynga game (or whoever makes your favorite Facebook game) isn’t Facebook and doesn’t change Facebook.

Change of Perspective

When I approached SharePoint Apps with the mindset that I was going to augment what SharePoint gave the user, I found nothing but frustration.  I would think first about what SharePoint could provide: lists for storing data, user security, etc. but I would quickly realize my App expected too much and that there was too little focus on my application.

I have now tried to spin that perspective 180 degrees, thinking first about what I want to build, maybe it as better calendar, and what it should do even without SharePoint.  Then after I knew what my App should do and how I was going to make it work I would think if there was anything I needed from SharePoint.

More customizations with Client Side Rendering

Recently I posted about how you can use Client Side Rendering (CSR) to display contacts on a map.  After posting about how to display the entire list data, I decided I wanted to see how CSR could  be used to change the user experience while adding, updating, and viewing list items, updating items, and viewing items.  This post will provide a high level overview of how I was able to change the display during New, Display, and Edit of an individual item and then later I will actually put this together with the Contact Map idea.

Where’s the Documentation?

As mentioned in the previous blog, this work was all pretty much done 100% in the dark.  I was able to find a few blogs about how to style specific fields, in particular those custom fields being added by a feature, but there really isn’t much about the full CSR capabilities.  In order to figure some of this stuff out I ended up using the IE Developer Tools Console to view various objects, in particular the SPClientTemplates.TemplateManager and SPClientTemplates._defaultTemplates JavaScript objects.

Specific Items mean Specific Field(s)

My original goal was to do something, really anything, with all the input fields.  However, to date, I haven’t been able to figure out how to get my CSR to change all the fields, but I have been able to impact specific field(s) and in the remainder of this post I will focus on impacting my custom field ‘Lat’.  This being said, the CSR does include a OnPostRender function which you can use to ‘go back’ and update the fields, but it doesn’t do anything while the field is being rendered.

CSRs need to self execute, and to prevent namespace pollution I’m using the self wrapping anonymous function.

(function() {
})();

We will again use the overrideCtx and overrideCtx.Templates objects to specify the various items we want to control.

var overrideCtx = {};
overrideCtx.Templates = {};

Unlike our Contacts on a List example this time rather than overriding the Item template we are going to override the Fields.  Fields is a bit different from the item in that rather than providing a function which is passed a context object, Fields is an object that specifies the field(s) we want to override during render.  For each field specified we can then provide our rendering template function which receives the current Field’s context.

overrideCtx.Templates.Fields = {

‘Lat’: {

‘NewForm’: function(ctx) { return (“<div style=’border: 1px solid blue;’>Hello World</div>”); }
‘EditForm’: function(ctx) { return (“<div style=’border: 1px solid green;’>Hello World</div>”); }

}

};

Initially, the names ‘NewForm’ and ‘EditForm’ which match up with the URL /NewForm.aspx and /EditForm.aspx caused me to assume the naming’s should use the name of the ASPX form.  However, ‘DispForm’ (/DispForm.aspx) did not provide the expected rendering and this is where the IE Developer Tools were critical.  Using the IE Developer Tools and the Console I was able to look at the SPClientTemplates._defaultTemplates where I was able to traverse the object structure and found the function ‘DisplayForm’.

Related

SharePoint Contacts on a Map with Client Side Rendering

Bye InfoPath, Hello Apps for Office & Lightswitch

The Microsoft Office announced  on Friday that InfoPath 2013 will be the final version released.  This is a significant announcement because it means any customer with custom SharePoint workflows, Office Document Information Panels, and any other Data Entry Applications will have to move those to a new technology, but Microsoft hasn’t said which one.

My Predictions

I believe Microsoft will recommend customers utilize one of two capabilities.  First, for customized Document Information Panels (those forms that can be added to Office applications, like Word, and can tie back to LOB systems or SharePoint) I believe Microsoft will recommend the use of the new Apps for Office Applications.  The Apps for Office allow creation of custom applications built on Web technologies like HTML5 and JavaScript and a set of Office JavaScript APIs are available for handling bindings to Office Templates.  The significant advantage, and disadvantage, here is that much can be done with customizing the retrieval of information from external systems, as well as customization of the injection of that information into the Document itself, but this also puts greater responsibility on the developers to request the information and push it to the application.

Second, for other business applications where data entry forms are needed, outside of the web, I believe Microsoft will push for the use of LightSwitch.  LightSwitch is a trimmed down version of the Visual Studio environment that allows for quick development of data entry applications/forms.  The information can easily be connected to XML or SQL data sources making this an easy replacement for the InfoPath Form entry design and use.

Feedback

I’d love to hear other’s predictions about what to do with the InfoPath applications that exist out there, so please leave a comment with your thoughts.

SharePoint Contacts on a Map with Client Side Rendering

Several years ago, back in the dark days of SharePoint (MOSS) 2007, Tom Resing and I worked together on a demo displaying contacts from a SharePoint contact list on a Bing/Google Map.  He and I presented this at several SharePoint Saturday events (Boston, Virginia Beach, Richmond, Tampa) and found highly positive response.  It has been a while since Tom and I worked together, and a lot has changed in SharePoint so I wanted to revisit our old demo and take a newly available approach.

SharePoint 2007 & 2010

When Tom and I first worked together jQuery was early in development, HTML5 was only being discussed, and Chrome didn’t exist.  JavaScript was used a little, but because of the browser limitations it didn’t have the capabilities it has today, so SharePoint relied heavily on XML and XSLT to edit and customize the display of information.  The demo was remarkably simple but required significant work with XSLTs.  We would do the demo in SharePoint 2007 by adding a SharePoint DataView Web Part (DVWP) and customize the XSLT list and item rendering to add the map and geocode the items.  This worked well, but injecting JavaScript through an XSLT translation required HTML encoding of symbols like ‘&’ and ‘>’ which really made the JavaScript code very difficult to read and understand.

History Lesson: You can find Tom’s old slide deck here.

SharePoint 2013 – Client Side Rendering (CSR)

Today jQuery is a mature, and a common tools set, HTML5 is becoming more and more common, and business applications rely on JavaScript almost as much as Server Side code.  SharePoint 2013 introduced a new method, utilizing JavaScript, to provide rendering of lists rather than using the ‘old’ method of XSLT.  Given this improvement, I thought it was worthwhile to revisit the old demo, and give it a refresh.

Updated Demo

I have been unable to find a solid MSDN CSR API reference, but I can tell you CSR does NOT use the JSOM when executing so no need to perform context loads, queries, etc.  CSR actually seems to provide a JavaScript object that is a JSON’ized version of the List View and Rows so we can actually access row columns by using .[Static Row Column Name].

I’ve created a separate page where I could work with the list data and CSR to ensure I would be able to access my list even if the JavaScript wasn’t working and I would highly recommend others do the same at least during development.  Once your done developing, the CSR can be applied directly to a list/list view.  Also, on the page I added the Bing and jQuery references using a Script web part because I found that adding it in the CSR code did not guarantee that those files would actually be loaded.

From all the code examples I have seen, and JavaScript best practices, CSR files should have a self contained object so your variables and functions do not pollute the global namespace.  However, your CSR must be self executing, so begin with something like:

(function() {
})();

Now we need to create the Template Override object.  This object will be provided to and used by SharePoint to implement the rendering of the list and list items based on what areas we choose to override.

var overrideCtx = {};
overrideCtx.Templates = {};

Our template has to perform three main tasks:

  1. Add a Div that the Bing Map API can use to display the map tiles
  2. Capture each item in the List
  3. Initialize the map and add the geocoded items to it

In order to perform these task we will override the Header, Item, and OnPostRender.

One really nice aspect of the CSR is we can either call JavaScript functions, or we can return back HTML for rendering.  In the Header function we will just return the necessary HTML to add our div which the Bing API will use to display it’s map.  Add the following to override the Header

overrideCtx.Templates.Header = function(ctx) {

 return (“<div id=’mapDiv’ style=’position: relative; width: 450px; height: 450px;’></div>”);

 }

Next we want to capture each item in our list.  Because we don’t have the Bing Map loaded yet we will just store the items so once the Map and Search Manager are loaded we can the perform the geocoding and rendering.  To override our Items use the following:

overrideCtx.addressArray = new Array();
overrideCtx.Templates.Item = function(ctx) {

overrideCtx.addressArray.push({Title: ctx.CurrentItem.Title,
Address: (ctx.CurrentItem.WorkAddress + ” ” …)});

//This has been truncated for more efficient reading

}

Now that we have our Div and Items we can load the Map, Geocode the items, and add them to the display.  I’m going to avoid in depth discussion on how to work with the Bing Maps and Geocoding in this blog, but if you want more detail check out the Bing Map API.

To perform the initialization of the map and search I chose to use the OnPostRender, which ensures all other templating has been performed and is complete meaning everything I need to initialize and geocode is ready.

overrideCtx.OnPostRender = function () {

overrideCtx.Map = new Microsoft.Maps.Map(…);
Microsoft.Maps.loadModule(‘Microsoft.Maps.Search’, { callback: function() {


overrideCtx.geocodeRequest();

 }});

overrideCtx.geocodeRequest = function () {

$.each(overrideCtx.addressArray, function(index, val) {

var currentContact = {

where: val.Address,
callback: function (result, userData) {

var pushpin = new Microsoft.Maps.Pushpin(…);
overrideCtx.map.entities.push(pushpin);

},

};

});

}

Finally, now that our template is ready to be used we need to register it with SharePoint.  At the end of the self invoking function add:

SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);

Use Our CSR

With that our CSR is ready, now add the CSR js file to your site (I prefer to use the SiteAssets library because I’ve had issues in the past with Document Libraries not always returning the expected HTTP headers).  On a page with the Contacts List View (which should include the Address, City, State, and Zip fields) edit the List View web part and expand the Miscellaneous section.  There you will find a new text box ‘JS Link’ where you can add the url to your CSR file.  The JS Link field allows you to also use the ~site url token to make referencing your file easier and ensure the path is properly formatted for SharePoint to load.

Once you’ve added the CSR the your JS Link Save your changes and enjoy the new Client Side Rendering.

If your interested in my actual CSR JS file you can download it in PDF format here.