SPServices Stories #17: Multiple Form Fields Autocomplete for SharePoint 2010/2013 using JavaScript

Introduction

Anyone who follows this series knows that I’m always on the lookout for interesting and useful implementations of SPServices. A few weeks ago, a tweet in my feeds caught my eye:

Anton Khritonenkov Anton Khritonenkov, who is a Technical Lead at Plumsail in Russia, has come up with a nice way to enable autocomplete behavior for multiple list columns based on related lists.

While I’ve got the SPAutocomplete function in SPServices, it’s pretty rudimentary and I usually suggest using the jQueryUI autocomplete function, as Anton does here. The other function in SPServices that is somewhat related to what Anton is doing in SPDisplayRelatedInfo. By using the completefunc there, you could enable some of the behavior that Anton has built, but not as cleanly.

Anton explains things well in his post below, which he was kind enough to allow me to repost here from his original on the CodeProject site.

Multiple Form Fields Autocomplete for SharePoint 2010/2013 using JavaScript

Introduction

Filling forms could be painful sometimes, especially for forms with many fields. In this article I’ll describe approach which can simplify forms filling significantly. In my case I needed to implement order form interface. In fact it contains a lot of fields I can pre-fill based on chosen customer, such as address, ZIP code and account number. All of this data is already stored in separate list called “Customers directory”.

Final requirements could looks like this:

When user starts typing in the customer field, I need to suggest list of customer names from Customers directory. When user chooses customer from suggestions list, I need to read data multiple field values from Customers directory and fill corresponding fields in the order form. User can correct filled values later if needed.

To implement such functionality I used to use JavaScript and this case is not an exception. There are many reasons for this:

  • It well fits Office 365 restrictions.
  • It easily migrates from older SharePoint versions.
  • It is easily to debug without slow SharePoint solution deployment.
  • REST SharePoint 2013 API or SPServices are almost such powerful as server code.
  • Finally I just like it.

In this article I’ll use SPServices jQuery plugin for communication with SharePoint services. It works for SharePoint 2013 as well as for SharePoint 2010. I’ll also use jQuery UI Autocomplete plugin to implement suggestions functionality.

plumFormAutocomplete plugin works well for single field as much as for multiple fields.  Plugin supports text fields only.

Plugin in action looks like this:

How to use plumFormAutocomplete jQuery plugin

You can use this plugin without looking into the code. Firstly, I’ll describe how to use it, then if you still will be interested, you can look inside plugin implementation.

Prerequisites

If you don’t have jQuery, jQuery UI or SPServices, you can download fresh version from official sites. For tutorial purposes I assume that names of downloaded files and folders are following:

  • jquery.min.js 
  • jquery.SPervices.min.js 
  • jquery-ui-1.10.3.custom.min.js
  • jquery-ui-1.10.3.custom.min.css
  • css folder for jQuery UI

I also assume that you have jquery.plumFormAutocomplete.js downloaded from source code of this article (Source code download link).

Upload jquery.min.js, jquery.SPServices.min.js, jquery-ui-1.10.3.custom.min.js and jquery.plumFormAutocomplete.js files to Style Library within your site collection. You also need to upload jQuery UI CSS styles located in the same folder with jquery-ui-1.10.3.custom.min.js. Then open New or Edit form, where autocomplete plugin will be used and add js and CSS links to placeholder PlaceHolderAdditionalPageHead.  You can use following snippet:


Configure and call plugin

Now you are ready to configure and call plugin. For my case plugin call looks like this:

//get control for autocomplete field
var fieldControl = $.getFieldControl('Title');

//call autocomplete plugin for field control
fieldControl.plumFormAutocomplete({
  sourceList: 'Customers directory',
  sourceMatchField: 'Title',
  labelFields: ['Title', 'ZIPCode'],
  labelSeparator: ', ',
  fillConcatenatedLabel: false,
  fieldsMapping: [{sourceField: 'Address', targetField: 'CustAddress'},
    {sourceField: 'AccountNumber', targetField: 'CustAccountNumber'},
    {sourceField: 'ZIPCode', targetField: 'CustZIPCode'}]
});

You can wrap plugin call inside jQuery $(document).ready() function to ensure that code will be executed after page is loaded.

Let us look at this code sample in more detail. Code is divided into two parts:

  1. Get control for autocomplete field
  2. Call autocomplete plugin for field control

For the first step you need to specify internal name of autocomplete field for getFieldControl function. It is ‘Title’ in my case.

In the second step you need to call plugin for received autocomplete field and configure plugin options. Plugin options are structured as a single object as any jQuery plugin options.

Plugin options

  • sourceList – name or GUID of source list, where suggestions will be taken. It is ‘Customers directory’ in my case.
  • sourceMatchField – internal name of the field in the source list. This field will be used to find matching list items for autocomplete keywords.
  • labelFields – an optional parameter, you can specify source list field internal names array. All field values for these  fields will be concatenated with labelSeparator and displayed in autocomplete suggestion as a single string like this: Value1, Value2, …, ValueN.
  • labelSeparator – an optional parameter, it is separator for labelFields concatenation, for example it could be comma with space (‘, ‘).
  • fillConcatenatedLabel – an optional parameter, set true if you need to fill autocomplete textbox with all concatenated labelFields values, set false if you need to fill autocomplete text box only with single field value.
  • fieldsMapping – an optional parameter, it is an array of field mapping objects. Each object declares mapping from source list field to target list field. In my case names of source and target fields are the same. For example Address in Customer directory and Address in Orders list.

Mapping object has following syntax:

{sourceField: 'Internal name of source field', targetField: 'Internal name of target field'}

Note: You can specify only non optional parameters, plugin will work correctly. This plugin works well as single field autocomplete too, just do not fill optional parameters.

Plugin configuration without optional parameters could look like this:

fieldControl.plumFormAutocomplete({
    sourceList: 'Customers directory',
    sourceMatchField: 'Title'
});

Internal implementation of the plugin

Let us look at the full plugin source code. You can download it here.

There are three major part inside the code:

  1. Getting text field input.
  2. Apply jQueryUIi autocomplete plugin and SPServices to get suggestions.
  3. Implementing helper functions.

To get field input I used jQuery selectors and simple regular expression. Unfortunately SharePoint doesn’t provide any method to get field controls from JavaScript, it only stores field internal name inside html comment in the following format:

<!-- FieldName="Title" FieldInternalName="Title" FieldType="SPFieldText" -->

So, I had to parse it to find control I needed. Final function was added to jQuery:

//function gets text field control by internal name
$.getFieldControl = function (fieldInternalName) {
  var regexStr = 'FieldInternalName="' + fieldInternalName + '"'
  var regex = new RegExp(regexStr, 'i');
  var fieldCell = $('td.ms-formbody').filter(function () {
    return (regex).test($(this).html())
  });
  return $(fieldCell.find('input')[0]);
}

In the next step I applied jQuery UI autocomplete plugin and implemented source and select plugin functions. Source function calls source list using SPServices and CAML to get suggestions. When suggestion found, I store all mapped field values inside autcomplete object:

source: function (request, response) {
  var autocompleteVals = [];
  var k = 0;

  $().SPServices({
    operation: "GetListItems",
    async: false,
    listName: options.sourceList,
    CAMLViewFields: getViewFields(options.fieldsMapping),
    CAMLQuery: getCamlQuery(options.sourceMatchField, request.term),
    completefunc: function (xData, Status) {
      $(xData.responseXML).SPFilterNode("z:row").each(function () {
        var queryResult = this;
        var fieldsValues = getFieldsValues(options.fieldsMapping, queryResult);
        var labelText = getLabelText(fieldsValues);
        autocompleteVals[k] = {
          label: labelText,
          value: options.fillConcatenatedLabel ? labelText  :
          extractFieldValue(fieldsValues, options.sourceMatchField),
          fieldsValues: fieldsValues
        };

        k++;

        function getLabelText(fieldValues){
          var result = '';
          if(options.labelFields) {
            for(i = 0; i < options.labelFields.length; i++) {	        		               var fieldName = options.labelFields[i];               var fieldVal = extractFieldValue(fieldValues, fieldName);               if(fieldVal != '') {                 if(i > 0) {
                  result += options.labelSeparator;
                }
                result += fieldVal;
              }
            }
          } else {
            result += extractFieldValue(fieldValues, options.sourceMatchField);
          }
          return result;
        }

      });
      response(autocompleteVals);
    }
  });

}

Select function fills values inside mapped fields according to matched item from source lists. It reads values stored inside ui.item and fills corresponding fields based on suggestion selection.

select: function (event, ui) {
  //Fill all depended fields
  $.each(ui.item.fieldsValues, function () {
    var fieldVal = this;
    var fieldInput = $.getFieldControl(fieldVal.key);

    var outputVal = fieldVal.value;

    if (outputVal) {
      var lookupSeparator = ';#';
      if (outputVal.indexOf(lookupSeparator) != -1) {
        var ind = outputVal.indexOf(lookupSeparator);
        var length = lookupSeparator.length;
        var startInd = ind + length;
        outputVal = outputVal.substring(startInd, outputVal.lenght)
      }
      fieldInput.val(outputVal);
    }

  });
}

Maybe you discovered that there are three helper functions inside plugin: getFieldsValues, getViewFields, and getCamlQuery.

getFieldsValues parses SPServices response and fills autocomplete object according to specified fields mapping.

//get values for all mapped fields
function getFieldsValues(fieldsMapping, queryResult) {
  var result = [];
  $.each(fieldsMapping, function () {
    var fieldMapping = this;
    var val = $(queryResult).attr("ows_" + fieldMapping.sourceField);
    result.push({ key: fieldMapping.targetField, value: val, sourceKey: fieldMapping.sourceField});
  });

  var sourceVal = $(queryResult).attr("ows_" + options.sourceMatchField);
  result.push({ value: sourceVal , sourceKey: options.sourceMatchField});

  return result;
}

getViewFields generates ViewFields xml for CAML query according to fields mapping.

//get view fields for all mapped fields
function getViewFields(fieldsMapping) {
  var result = "";
  var isSourceFieldAdded = false;

  if(fieldsMapping){
    $.each(fieldsMapping, function () {
      var mapping = this;
      var viewField = "";
      result += viewField;
    });

    isSourceFieldAdded = fieldsMapping.filter(function(){
      return this.sourceField == options.sourceMatchField;
    }).length > 0;
  }

  if(!isSourceFieldAdded){
    result += "";
  }

  result += "";
  return result;
}

getCamlQuery generates CAML query according to filter column internal name and keyword from input.

//get CAML query for keyword
function getCamlQuery(colname, keyword) {
  var where = "" + keyword + "";
  var orderBy = "";
  var query = "" + where + orderBy + "";
  return query;
}

Updates

03.10.2013

  • Additional optional parameters added: labelFields, labelSeparator, fillConcatenatedLabel.
  • Reading lookup fields from source list implemented.
  • Minor bug fixes.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
m4s0n501

Single-Page Applications (SPAs) in SharePoint Using SPServices – Part 1 – Introduction

Single-page applications (SPAs) are nothing new on the Web. However, like Responsive Web Design (RWD), SPAs are gaining favor as a way to enable real work with an improved user experience (UX).

From the Wikipedia definition of SPA:

A single-page application (SPA), also known as single-page interface (SPI), is a web application or web site that fits on a single web page with the goal of providing a more fluid user experience akin to a desktop application.

SPAs first started popping up regularly – at least based on my experience – in creative contexts on public Web sites. A post called 40 Inspiring Single Page Websites from Gisele Muller shows some great examples of the SPA approach. Most of the sites Gisele highlights are marketing sites: they are primarily publishing content in a one-to-many context. Also note that the date of her post is early 2010. These sites have been out there for a while and are becoming more prevalent.

SPA Sketch

SPA concept sketch image from viaboxx systems at http://viaboxxsystems.de/event-driven-gui

We see many more examples of SPAs all the time, whether we think about them this way or not. The most obvious and prevalent examples are the Facebook and Yammer News Feeds. In the News Feed examples, we tend to stay on that single page until we see something scroll by or some content change that makes us want to drill into the details, taking us away from the SPA intentionally based on an action – usually a mouse click – we take. Other full-scale applications that enable us to do real work, like Trello, are good examples of the SPA concept. We also see the SPA idea in apps on our phones, phablets, tablets, and even showing up in Windows 8 world. In other words, this is the wave of the future for apps.

Since the idea with SPAs is that one need not leave the single page to accomplish some high percentage of the tasks at hand, it’s a great concept to apply in a SharePoint context. With SharePoint, we are trying to fundamentally change the way people work, moving from a hierarchical to a more collaborative work style. While this work evolution has been progressing well over the last dozen or so years of SharePoint’s existence, SharePoint itself has changed a lot, as should the way we consider building our applications within it.

The days where a clunky postback-ridden application was acceptable is fading into the rear view mirror. The SharePoint Product Group has taken steps toward giving SharePoint itself an SPA “feel” with the introduction of the Minimal Download Strategy (MDS) in SharePoint 2013. See Wictor Wilén‘s (@wictor) excellent post SharePoint 2013 – Introduction to the Minimal Download Strategy (MDS) for details on how MDS works. MDS doesn’t give us a true SPA per se, but the goals for MDS get us a much more fluid, SPA feel for SharePoint.

But what if you are using older versions of SharePoint? Maybe 2007 or 2010? Or even 2003? (Yes, I still run into a few people using 2003 from time to time.) Well, there’s no need to despair, because the tooling to create SPAs is there for you to use anytime you want it. With AJAX available for use as the data transport mechanism, we can fetch content from the SharePoint server anytime we choose without a page refresh. In the past, I’ve taken various different approaches to this, like Refreshing a Page Section with a User-selected Interval Set with jQueryUI’s Slider or Refreshing a Web Part Without a Postback Using jQuery’s AJAX Function. You’ll also see a few SPA examples in my SPServices Stories series, like SPServices Stories #13: Durandal SP3: Developing SharePoint SPAs Made Easy from Rainer Wittman (@RainerAtSpirit).

My friend and SPServices co-conspirator, Paul Tavares (@paul_tavares), and I have been discussing several Lists Web Services operations of late that I’d never paid much attention to: GetListItemChanges and GetListItemChangesSinceToken. Together with the simpler GetListItems – the true workhorse of the SPServices toolkit – we have three variations on a theme: fetching content from a list. Using those three “get” operations, along with the UpdateListItems operation to write changes back to the list, we can build our own SPAs in SharePoint.

We could choose to use some of the fancy frameworks out there, but in this series, I’m going to keep it simple. I’m going to limit myself to jQuery and SPServices only to see what I can come up with. (I may sprinkle in a little fairy dust using jQueryUI for fun.) While frameworks like Knockout or AngularJS can be extremely helpful tools, I find that in many cases simplicity works better for me – it keeps me closer to the data and lets me control what goes on. I also think that I can keep the examples more straightforward by avoiding other plugins.

Let’s get to work…

SharePoint Designer 2013’s Missing Design View – Still

Here is a question that came in from my blog today.

My company is starting to plan to migrate to SP 2013 and I just found out that the design and split view is not included in SP Designer. This is going to be absolutely devasting for us as this is really the only way we do development. We have so many items created with XSL that I can’t imagine what we are going to do when we migrate. Can you update me on if MS has reconsidered adding that back and if not, where do I go to post my comments and to add my voice to those that are not happy?

Unfortunately, questions about the missing Design View in SharePoint Designer 2013 are fairly commonplace and sadly I have no good news. Those views are gone and as far as I know are not coming back. Any uproar that occurred due to my earlier posts like SharePoint Designer 2013′s Missing Design View – It’s Official fell on deaf ears.

I tend to write my own XSL, and one can certainly continue to do so using SharePoint Designer 2013. The Design View was always inexact, but since it was where we saw any error messages, I find that is why I miss it the most.

Display Templates – which are built of markup and script – are worth looking at, as are frameworks like Knockout or AngularJS. Yes, new things to learn, but more the way Web development is headed.

I also don’t know of anywhere to go to register complaints about this. I find that the MSDN Forums may make you feel better, but are rarely monitored by Microsoft people who can push for changes. Microsoft Connect is supposed to be the place to report bugs and give feedback, but things seem to languish there as well.

None of this is much solace for people who need to maintain existing XSL-based solutions. Bottom line: we all need to learn new tricks and/or rewrite our older XSL-based solutions.

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>