SharePoint and Exchange Forum (SEF) 2013 Wrap Up

Panorama of Stockholm's Royal Palace

Panorama of Stockholm’s Royal Palace

I’m just returning from another splendid time at the SharePoint and Exchange Forum (SEF) conference in Stockholm, Sweden. This year, the conference was actually international: it was held on the Silja Symphony cruise ship in both the Stockholm and Helsinki harbors. It was an interesting and fun venue for a conference, and for those organizers out there looking for a fresh, new idea, this was a great one.

Silja Symphony at port in Helsinki

Silja Symphony at port in Helsinki

Many thanks to Göran Husman (@GHusman) and the entire Humandata team for a warm welcome (as always), and a job very well done on the conference. I hope to return to Stockholm – or wherever else we might sail – many times in the future.

It was great fun to have my wife Melanie (@koochiku) along on this trip – her first SharePoint conference. Now she will understand just a little bit why I go on and on about why the SharePoint community is such a wonderful thing to be a part of.

I also really enjoyed seeing my compadre in “no code” solutions (we call it code, regardless what everyone else says) and friend Christian Ståhl (@cstahl) and having the chance to take a walking tour of Helsinki with him.

Here are the slide decks from my three sessions at the conference, which are up on SlideShare. Feel free to comment here with any questions or suggestions any time.

Tack för en underbar konferens!

Item ‘Modified By’ Value Doesn’t Change: Fixing a Damaged Column Schema

I ran into an odd situation the other day at one of my clients.

Modified By ExampleWhen people edited existing list or library items in *some* lists (we couldn’t discern a pattern) the Modified By (Editor) column value did not change. The date/time stamp (Modified) was updated, but not the person who made the modification. We found several good posts out there on them Interwebz, but I though it would be useful to pull things together and outline what we tried in case it could help others.

There were two posts we found that got us started:

The first post led us to look at the Modified By column’s schema; the second led me to think that I could do the fixes using the SOAP Web Services.

I was able to pull the schema for the Modified By Site Column using the GetColumns operation in the Webs Web Service. (Of course I turned to SPServices first – it’s my hammer.)

$().SPServices({
  webURL: "/",
  operation: "GetColumns",
  completefunc: function(xData, Status) {
  }
});

I was able to look at the returned XML in Firebug, copy it out, and find the schema for the Modified By column.

Bingo. From Victor Butuza’s post, we know that the schema should look like this:

<Field ID="{d31655d1-1d5b-4511-95a1-7a09e9b75bf2}"
 ColName="tp_Editor"
 RowOrdinal="0"
 ReadOnly="TRUE"
 Type="User"
 List="UserInfo"
 Name="Editor"
 DisplayName="Modified By"
 SourceID="http://schemas.microsoft.com/sharepoint/v3"
 StaticName="Editor"
 FromBaseType="TRUE"/>

Instead, it looked like this (I’ve changed several of the attribute values to protect the guilty, but you should get the point):

<Field ID="{d31655d1-1d5b-4511-95a1-7a09e9b75bf2}"
 Name="Editor"
 SourceID="http://schemas.microsoft.com/sharepoint/v3"
 StaticName="Editor"
 Group="_Hidden"
 ColName="tp_Editor"
 RowOrdinal="0"
 Type="User"
 List="UserInfo"
 DisplayName="Modified By"
 SystemInstance="SQL_DB_Name"
 EntityNamespace="http://servername"
 EntityName="Partners"
 BdcField="OrganizationId"
 Profile=""
 HasActions="True"
 SecondaryFieldBdcNames="0"
 RelatedField="Partners_ID"
 SecondaryFieldWssNames="0"
 RelatedFieldBDCField=""
 RelatedFieldWssStaticName="Partners_ID"
 SecondaryFieldsWssStaticNames="0"
 AddFieldOption="AddFieldInternalNameHint"
 ReadOnly="TRUE"
 Version="1">
</Field>

Some way or other, the Modified By (Editor) column had been changed. We noticed quite a few odd things in the schema above, which I’ve highlighted. It looks like attributes from some BDC connection had made it into the schema, which wasn’t good. More importantly, the important FromBaseType="TRUE" attribute was missing.

I tried for a while to get the SOAP operation UpdateColumns to work in a test environment. (I was very careful to do this in a snapshot of a VM because messing with the Modified By column could be catastrophic.) Caution proved wise, as each time I tried to update the Modified By schema, the Site Column management pages (_layouts/mngfield.aspx and _layouts/fldedit.aspx) no longer worked properly. Even though I believe I had the syntax correct (based on Karthick‘s post above – note that the MSDN documentation is pretty sparse), I simply couldn’t get UpdateColumns to work without breaking things.

So I turned to Powershell. I’m no Powershell guy, but it’s a scripting language like any other and I found some good starting points out on the Web to get me going. What I ended up with was a script that repaired all of the attributes of the Modified By column in a single list.

$s = get-spsite http://servername
$w = $s.OpenWeb("/sitename/")
$l = $w.Lists["listname"]
$f = $l.Fields.GetFieldByInternalName("Editor")

write-host "BEFORE field at " $w.Url  " List "  $l.Title  "  is " $f.schemaxml

#add at the end of the schema the needed string and update the field and list
$f.SchemaXML = $f.SchemaXML -replace ' SystemInstance="SQL_DB_Name"',''
$f.SchemaXML = $f.SchemaXML -replace ' EntityNamespace="http://servername"',''
$f.SchemaXML = $f.SchemaXML -replace ' EntityName="Partners"',''
$f.SchemaXML = $f.SchemaXML -replace ' BdcField="OrganizationId"',''
$f.SchemaXML = $f.SchemaXML -replace ' Profile=""',''
$f.SchemaXML = $f.SchemaXML -replace ' HasActions="True"',''
$f.SchemaXML = $f.SchemaXML -replace ' SecondaryFieldBdcNames="0"',''
$f.SchemaXML = $f.SchemaXML -replace ' RelatedField="Partners_ID"',''
$f.SchemaXML = $f.SchemaXML -replace ' SecondaryFieldWssNames="0"',''
$f.SchemaXML = $f.SchemaXML -replace ' RelatedFieldBDCField=""',''
$f.SchemaXML = $f.SchemaXML -replace ' RelatedFieldWssStaticName="Partners_ID"',''
$f.SchemaXML = $f.SchemaXML -replace ' SecondaryFieldsWssStaticNames="0"',''
$f.SchemaXML = $f.SchemaXML -replace ' AddFieldOption="AddFieldInternalNameHint"',''
$f.SchemaXML = $f.SchemaXML -replace '/>',' FromBaseType="TRUE" />'

$f.Update()
$l.Update()

write-host "FIXED field at " $w.Url  " List "  $l.Title  "  is " $f.schemaxml

$w.Dispose();
$s.Dispose();

I also wrote a Powershell script to find all of the lists where this aberration had occurred in the Modified By column:

$siteURL = "http://servername"
$site = Get-SPSite($siteURL)

$errors = 0
$thisWebNum = 0

foreach($web in $site.AllWebs) {

  $thisWebNum = $thisWebNum + 1
  write-host $thisWebNum " Web " $web.Url  " Created on "  $web.Created

  $listCounter = $web.Lists.Count

  for($i=0;$i -lt $listCounter;$i++) {

    $list = $web.Lists[$i]
    $thisListNum = $i + 1

    write-host "(" $thisListNum "/" $listCounter ") [" $list.Title "] Created on "  $list.Created

    $f = $list.Fields.GetFieldByInternalName("Editor")

    if ($f.SchemaXML -NotMatch 'FromBaseType="TRUE"')
    {
      $errors = $errors + 1
      write-host "  Issue in schema " $f.schemaxml
    }
  }
  $web.Dispose();
}
$site.Dispose();

write-host "TOTAL ISSUES: " $errors

This script told us that we had 283 lists with the issue. Clearly this has been going on for a long time and no one had caught it, though we still can’t see any patterns in the date/time stamps or which lists have the issue.

We’ve fixed the two lists where the client folks had noticed the Modified By issue as well as the Site Column itself. We’re pausing at this point just to make sure that we don’t see any oddities based on the fix, but we’re optimistic that we know what was happening and that we have a valid fix. After we let things settle for a little while, we’ll run the fix script on the rest of the lists with the issue by combining the two scripts to loop through all of the lists with the issue.

Have you ever run into a broken schema issue like this in your environment? If so, how did you fix it?

<UPDATE date=”2013-10-17″>

We hadn’t seen any issues since we’d applied the fix I described above, so today I ran through the rest of the messed up lists and applied the fix. The Powershell script below loops through all of the sites in the Site Collection, then through all of the lists in each site, and fixes the Editor column where it’s got issues.

$siteURL = "http://servername"
$site = Get-SPSite($siteURL)

$errors = 0
$thisWebNum = 0

foreach($web in $site.AllWebs) {

  $thisWebNum = $thisWebNum + 1

  $listCounter = $web.Lists.Count

  for($i=0;$i -lt $listCounter;$i++) {

    $list = $web.Lists[$i]
    $thisListNum = $i + 1

    $f = $list.Fields.GetFieldByInternalName("Editor")

    if ($f.SchemaXML -NotMatch 'FromBaseType="TRUE"')
    {
      $errors = $errors + 1

      # fix the schema and update the field and list
      $f.SchemaXML = $f.SchemaXML -replace ' SystemInstance="GivingData"',''
      $f.SchemaXML = $f.SchemaXML -replace ' EntityNamespace="http://servername"',''
      $f.SchemaXML = $f.SchemaXML -replace ' EntityName="Partners"',''
      $f.SchemaXML = $f.SchemaXML -replace ' BdcField="OrganizationId"',''
      $f.SchemaXML = $f.SchemaXML -replace ' Profile=""',''
      $f.SchemaXML = $f.SchemaXML -replace ' HasActions="True"',''
      $f.SchemaXML = $f.SchemaXML -replace ' SecondaryFieldBdcNames="0"',''
      $f.SchemaXML = $f.SchemaXML -replace ' RelatedField="Partners_ID"',''
      $f.SchemaXML = $f.SchemaXML -replace ' SecondaryFieldWssNames="0"',''
      $f.SchemaXML = $f.SchemaXML -replace ' RelatedFieldBDCField=""',''
      $f.SchemaXML = $f.SchemaXML -replace ' RelatedFieldWssStaticName="Partners_ID"',''
      $f.SchemaXML = $f.SchemaXML -replace ' SecondaryFieldsWssStaticNames="0"',''
      $f.SchemaXML = $f.SchemaXML -replace ' AddFieldOption="AddFieldInternalNameHint"',''
      $f.SchemaXML = $f.SchemaXML -replace '/&gt;',' FromBaseType="TRUE" /&gt;'

      $f.Update()
      $list.Update()
      write-host "FIXED field at " $w.Url  " List "  $l.Title  "  is " $f.schemaxml
    }

    if ($errors -gt 0)
    {
      write-host $thisWebNum " Web " $web.Url  " Created on "  $web.Created  " had " $errors " errors"
    }
    $errors = 0;

    $web.Dispose();
  }
$site.Dispose();

</UPDATE>

SharePoint Saturday New Hampshire (SPSNH) 2013 Wrap Up

Thanks to everyone who attended my session at SharePoint Saturday New Hampshire (that’s SPSNH to many of you) over the weekend.

Thanks also to everyone who worked so hard to make SPSNH happen this year. It’s a helluva lot of work to put together events like this, and everyone deserves a big round of applause, no matter how many times they do it.

Slides for my session SharePoint Solutions with SPServices are posted on SlideShare, or you can view them here.

Rock on with SPServices, and let me know what cool stuff you come up with!

How to: Selecting Multi-Select Choice Columns in Forms with jQuery

As I’ve written many times in the past, I get a lot of questions via this blog. I try to answer some of them, but often they simply come in too fast and furious for me to get to them all.

Answering the questions gives me some benefits, too, or course. First, I often learn something I didn’t know (always a good thing), and second, I often turn the question and its answer into a blog post like this.

This one came in a few weeks back and I just found it as I was cleaning out my inbox.

Your blogs, post really helpful to me. [Nice - always start a blind question with some sort of pleasant preface. Really.]

Currently I am working in a project with SharePoint Server 2013. I am using jquery/javascript for few custom operations. I have a list “Task Assignment” where I am using a choice type column with “checkboxes multiple selection” enabled named “Task Category”. I need to trigger onchange or click event when user check/uncheck any items of the choice field.

But I could not get the element by id or tag name. I have worked with radio button using below statement.

$('input:radio[name$="0b57f_$RadioButtonChoiceField"]').bind("change", reviewedChanged);

I have tried to change this to work with multiple selection choice field.

$('input:checkbox[name$="f7c37_MultiChoiceTable"]').bind("change", assignTask);

But it is not working. Can you please suggest me.

Thanks in advance. [Also nice. It's amazing how few people ever say please or thank you.]

When I answer, I always preface my answer with something like this. (I have a signature set up in Outlook with this text because I use it so much.)

First off, thanks for turning to me with your question.  I’m happy to try to help, but you should really use a public forum to post your question. Some of the forums that can be useful are:

* SharePoint forums on MSDN (http://social.msdn.microsoft.com/Forums/sharepoint/)

* SharePoint on StackExchange (http://sharepoint.stackexchange.com)

Many people are active in these forums and you’re likely to get prompt responses. Additionally, others can benefit from finding any useful answers you get in the forums.

I was off the grid in Africa when this question came in. If it had any sort of time sensitivity, then sending it just to me wasn’t a good strategy. Plus, I may simply have never answered.

9-17-2013 2-19-59 PMThen I get to the answer. In this case, in my SharePoint Online (2013) test environment, a multi-select checkbox renders as a set of input elements like so:

<table id="test_2b0bc958-232c-4e77-a8b1-7b3a1745f84d_MultiChoiceTable" cellspacing="1" cellpadding="0">
  <tr>
    <td>
      <span title="a" class="ms-RadioText">
      <input id="test_2b0bc958-232c-4e77-a8b1-7b3a1745f84d_MultiChoiceOption_0" type="checkbox">
      	<label for="test_2b0bc958-232c-4e77-a8b1-7b3a1745f84d_MultiChoiceOption_0">a</label>
      </span>
    </td>
  </tr>
  <tr>
    <td>
      <span title="b" class="ms-RadioText">
        <input id="test_2b0bc958-232c-4e77-a8b1-7b3a1745f84d_MultiChoiceOption_1" type="checkbox">
        <label for="test_2b0bc958-232c-4e77-a8b1-7b3a1745f84d_MultiChoiceOption_1">b</label>
      </span>
    </td>
  </tr>
...

My column name is “test”, so we see ids like “test_2b0bc958-232c-4e77-a8b1-7b3a1745f84d_MultiChoiceOption_0″. This means the selector could be something like:

$("input[id^='test_'][id*='_MultiChoiceOption_']").change(function() {
  alert($(this).attr("id"));
});

The code above is what I used to test. I find that this syntax makes more sense from a readability perspective over using bind, but that’s more of a stylistic thing.

The selector breaks down like this:

  • find input elements – $(“input
  • where the id starts with “test_” – [id^='test_']
  • and the id contains “_MultiChoiceOption_” – [id*='_MultiChoiceOption_']“).

The most important message here is that you can’t make any assumptions about the way ids are constructed in SharePoint because they are all over the map. You simply have to get good at using the various DOM inspectors, including the Internet Explorer Developer Toolbar and Firebug.

Keep in mind that some controls (not this one) may be rendered differently in IE than in other browsers – even in SharePoint 2013 – so you need to test your selectors in multiple browsers. Once you get familiar with how things like ids and “controls” – on the browser side, controls are collections of HTML elements and script – are rendered, you’ll feel more comfortable knowing when you’ve got it right.

Setting a Rich Text Column in a SharePoint Form with jQuery – SharePoint 2010

I have gotten several questions on an older post of mine titled Setting a Rich Text Column in a SharePoint Form with jQuery from a guy named Travis. He’s been struggling to try to use the script in the post to get at the text in a Rich Text Editor (RTE) in SharePoint 2007.

I just took a look at an RTE in SharePoint 2010 in both IE and Firefox. The markup for it is considerably different than it was in SharePoint 2007, and looking at my older post, I can tell from the screenshots that I was in 2007. The good thing is that it looks identical in both browsers in 2010, which wasn’t usually the case in 2007.

Here’s what the column looks like in SharePoint 2010 with the same text typed into it as in the previous example:

9-11-2013 12-56-16 PM

And here’s what the markup looks like in IE10:

9-11-2013 1-04-32 PMThe script in the older post shouldn’t work in any browser, since the markup is different. Rather than a textarea, the typed text goes into a div. It was pretty easy to select the textarea in 2007 because it had its title attribute set to the DisplayName of the column. In 2010, there’s no such obvious “hook” to select on.

Instead, we have to fall back to looking for the column name in the comment which is above each column’s control on the page. To recap, each cell in the left column where the names of the columns show has the CSS class ms-formlabel applied to it. In the right column, where the editing controls are, each cell has the class ms-formbody applied to it.

To find the RTE in the page, we have to loop through all of the ms-formbody cells to find the right one. I do this in SPServices for some things as well, because SharePoint is very inconsistent when it comes to providing a good way to select form elements.

Here’s my findFormField function from SPServices:

// Finds the td which contains a form field in default forms using the comment which contains:
//  <!--  FieldName="Title"
//    FieldInternalName="Title"
//    FieldType="SPFieldText"
//  -->
// as the "anchor" to find it. Necessary because SharePoint doesn't give all field types ids or specific classes.
function findFormField(columnName) {
  var thisFormBody;
  // There's no easy way to find one of these columns; we'll look for the comment with the columnName
  var searchText = RegExp("FieldName=\"" + columnName.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") + "\"", "gi");
  // Loop through all of the ms-formbody table cells
  $("td.ms-formbody, td.ms-formbodysurvey").each(function() {
    // Check for the right comment
    if(searchText.test($(this).html())) {
      thisFormBody = $(this);
      // Found it, so we're done
      return false;
    }
  });
  return thisFormBody;
} // End of function findFormField

So the script to get the HTML value of the RTE – assuming we have the function above available to us – is:

var thisFieldHtml = findFormField("System Description").find(".ms-rtestate-write div").html();
alert(thisFieldHtml);

Unfortunately (fortunately?) SharePoint keeps track of where our cursor is at any given time during the editing process by maintaining two spans inside the div.

<span id="ms-rterangecursor-start"></span><span id="ms-rterangecursor-end"></span>

Here’s what it looks like in an alert when I’ve just typed “big “:

9-11-2013 1-33-09 PMIf we highlight a section of text, then that highlighted text is contained within the two spans. Here I’ve highlighted the word “over”:

<span id="ms-rterangecursor-start"></span>over<span id="ms-rterangecursor-end"></span>

So if we want to get the HTML value of the RTE without the editing spans, we need one more line of code to remove them:

var thisFieldHtml = findFormField("System Description").find(".ms-rtestate-write div");
thisFieldHtml.find("#ms-rterangecursor-start, #ms-rterangecursor-end").remove();
alert(thisFieldHtml.html());

If we just want the text value of the RTE, then we don’t need to bother stripping out the spans:

var thisFieldText = findFormField("System Description").find(".ms-rtestate-write div").text();
alert(thisFieldText);

Note: I’ve also tried this in SharePoint 2013. There, the artificial surrounding div which SharePoint wrapped the Rich Text in with the earlier version is not present, so we don’t need to look inside it:

var thisFieldText = findFormField("Reason for Nomination").find(".ms-rtestate-write").text();
alert(thisFieldText);

Travis, I hope this helps!