Monday, August 8, 2016

Convert SharePoint list column / field to a Managed Metadata term set, then update source list to have a taxonomy field with that same value.

We have a few lists that have been around for years at my current client that we needed to reference in many other lists, forms, and applications. Ours was a list of customers. My admin said he wished we had a managed metadata term store with the list of values to be shared across apps, forms, Farms, and even other platforms. So I wrote a quick script to do just that. This is a console application that needs to run on the server.

Here's what it does:
  1. Compliments you on how good you look today.
  2. Asks you for a list of information it needs to run.
    • Url of the Site that has the list, that has the field/column you want to convert to a term set
    • Name of the list that has the field/column that has the values you want to convert to a term set
    • Name of your existing term store (The name of the service application)
    • Name of existing term group the new term set should get created in
    • Name you want the new term set to be called
    • Name of the Managed Metadata column the script will create and populate the value of in the list.
  3. Creates a TaxonomySession, gets a handle on the Termstore and group, and then creates the termset.
  4. Creates the new TaxonomyField (Managed Metadata Column) on the list
  5. Iterates through the list items, grabs the value from the field you want to convert to a termset, creates the term, then populates the MM column.
  6. Outputs errors
So that's it, HTH. If this saves you thousands of hours of copy and paste, click a banner ad, or something ;)

Example of inputs: 

static void Main(string[] args)
        {
            Console.WriteLine("Looking good today, is that a new shirt? Paste in URL of web that hast the customers list we need to use to pupulate the termset please.");
            string sUrl = Console.ReadLine();
            Console.WriteLine("What is the name of list with the column we are converting to a term set?");
            string sListName = Console.ReadLine();
            Console.WriteLine("What is the name of the column we are converting?");
            string sOriginalFieldName = Console.ReadLine();
            Console.WriteLine("What is the name of your termstore? (name of MM service app)");
            string sTermStore = Console.ReadLine();
            Console.WriteLine("Which existing term group do you want the term set in?  (i'll ask for termset name next)");
            string sGroupName = Console.ReadLine();
            Console.WriteLine("What do you want the term set to be called?");
            string sSetName = Console.ReadLine();
            Console.WriteLine("What do you want the new taxonomy column named in the source list?");
            string sColumnName = Console.ReadLine();
            Console.WriteLine();

            //Cache errors to show later
            List errors = new List();

            //Get the site object for the TaxonomySession constructor
            using (SPSite oSPsite = new SPSite(sUrl))
            {
                TaxonomySession ts = new TaxonomySession(oSPsite);

                //Get the termstore and group
                TermStore termStore = ts.TermStores[sTermStore];
                Group group = termStore.Groups[sGroupName];

                //Create the termset and a test term to be sure we're good permissions wise...
                TermSet termSet = group.CreateTermSet(sSetName);
                Term termToDelete = termSet.CreateTerm("temp1", 1033);
                termStore.CommitAll();
                Console.WriteLine("OK term set is created, adding column to list now.");

                //Now get a handle on the list and add the taxonomy field 
                using (SPWeb web = oSPsite.OpenWeb())
                {
                    web.AllowUnsafeUpdates = true;
                    SPList list = web.Lists[sListName];
                    bool fieldExists = list.Fields.ContainsField(sColumnName);
                    if (!fieldExists)
                    {
                        TaxonomyField field = list.Fields.CreateNewField("TaxonomyFieldType", sColumnName) as TaxonomyField;
                        field.SspId = termSet.TermStore.Id;
                        field.TermSetId = termSet.Id;
                        field.TargetTemplate = string.Empty;
                        field.AllowMultipleValues = false; //if you switch this to true, you'd have to change to TaxonomyFieldValueCollection
                        field.CreateValuesInEditForm = true;
                        field.Open = true;
                        field.AnchorId = Guid.Empty;
                        list.Fields.Add(field);
                        list.Update();
                        web.Update();
                        //get a new handle on the list since the schema changed.
                        list = web.Lists[sListName];
                    }

                    //now get a handle on it from the list context for edit later
                    TaxonomyField taxonomyField = list.Fields[sColumnName] as TaxonomyField;

                    foreach (SPListItem item in list.Items)
                    {
                        //get the field value from the list, then create the term with the name
                        string sCustomer = item[sOriginalFieldName].ToString();
                        Console.WriteLine("Creating term for {0}.", sCustomer);
                        //surrond with try/catch incase you have duplicates (the code will let you know at the end)
                        try
                        {
                            Term termCustomer = termSet.CreateTerm(sCustomer, 1033);
                            termStore.CommitAll();

                            //now that ther term exists in the term store, set the value of the new taxonomy field in the listitem
                            TaxonomyFieldValue taxonomyFieldValue = new TaxonomyFieldValue(taxonomyField);
                            taxonomyFieldValue.TermGuid = termCustomer.Id.ToString();
                            taxonomyFieldValue.Label = termCustomer.Name;

                            item[sColumnName] = taxonomyFieldValue;
                            item.SystemUpdate();
                            list.Update();
                        }
                        catch (TermStoreOperationException ex)
                        {
                            errors.Add("Error with " + sCustomer + " - Msg:" + ex.Message);
                        }
                        catch (Exception x)
                        {
                            errors.Add("Error with " + sCustomer + " - err:" + x.Message);
                        }
                    }
                    web.Update();
                    web.AllowUnsafeUpdates = false;
                }//dispose web
            }//dispose site

            Console.WriteLine("Done. Hit Enter to see list of errors we ran into.");
            Console.ReadLine();
            foreach (string er in errors)
            {
                Console.WriteLine(er);

            }
            Console.WriteLine("that's it, hit enter to end program.");
            Console.ReadLine();
        }


Wednesday, February 18, 2015

Get SharePoint or IIS Application Pool account usernames and PASSWORDS with powershell.

Sometimes you need to know a service account password and don't have it. Maybe your dog ate the documentation to your dev environment. Powershell to the rescue!

PS U:\> Add-WindowsFeature Web-WMI | Format-List
PS U:\> Get-CimInstance -Namespace root/MicrosoftIISv2 -ClassName IIsApplicationPoolSetting -Property Name, WAMUserName, WAMUserPass | select Name, WAMUserName, WAMUserPass

Fun and scary.

Wednesday, May 22, 2013

Records.BulkDeclareItemsAsRecords input string format issues

Recently I was working on some code to bulk declare hundreds of thousands of files in a folder as record and came across an error when calling Microsoft.Office.Policy.Records.BulkDeclareItemsAsRecords() as noted here:

http://msdn.microsoft.com/en-us/library/microsoft.office.recordsmanagement.recordsrepository.records.bulkdeclareitemsasrecords(v=office.14).aspx

This technet article has very limited documentation and no examples of how to use the class. It states that the first input parameter should be a "A comma delimited string of item IDs." If you do that, you'll get an exception: "input string in incorrect format". This is because you actually need to delimit the string with a | (pipe character).

Here's a snippet of my code that loops through the library and process batches of x number of items at a time:


 SPWeb thisWeb = SPContext.Current.Web;
                            SPFolder thisFolder = thisWeb.GetFolder(gFolderID);
                            SPDocumentLibrary thisLibrary = (SPDocumentLibrary)thisWeb.Lists[thisFolder.ParentListId];

                             // Query to get the unprocessed items in folder.
                              SPQuery query = new SPQuery();
                              query.Folder = thisFolder;
                              SPListItemCollection unprocessedItems = thisLibrary.GetItems(query);
                              string itemStr = "";


                                  // Build the csv string for the bulkdeclare method
                              for (int i = 0; i < unprocessedItems.Count; i++)
                                  {
                                      int itemID = unprocessedItems[i].ID;
                                      itemStr += itemID.ToString() + "|";
                                      iProcessedSoFar++;
                          

                                  //if we are at the batch size, then run the job and reset processed counter and csv string
                                      if (iProcessedSoFar >= iBatchSize)
                                      {
                                          //snip off that last comma
                                          itemStr = itemStr.TrimEnd('|');

                                          Records.BulkDeclareItemsAsRecords(itemStr, thisLibrary);
                                          
                                          itemStr = "";
                                          iProcessedSoFar = 0;

                                      }

                                  }

Tuesday, November 29, 2011

SharePoint 2010 Error 8313 Dead SearchService.svc


Search is dead on your SP2010 farm.

You see this error in the ULS logs:

CoreResultsWebPart::OnInit: Exception initializing: System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.Office.Server.Search.WebControls.CoreResultsWebPart.SetPropertiesOnQueryReader() at Microsoft.Office.Server.Search.WebControls.CoreResultsWebPart.OnInit(EventArgs e)



You also see this error in your server application logs:

SharePoint Web Services Round Robin Service Load Balancer Event: EndpointFailure
Process Name: OWSTIMER
Process ID: 7596
AppDomain Name: DefaultDomain

AppDomain ID: 1
Service Application Uri: urn:schemas-microsoft-com:sharepoint:service:2313ae1967844030a54407c13c36d972#authority=urn:uuid:3f2c33a4777749139422ac1249fc9288&authority=https://server:32844/Topology/topology.svc
Active Endpoints: 1
Failed Endpoints:1
Affected Endpoint: http://server:32843/2313ae1967844030a54407c13c36d972/SearchService.svc


You have tried everything that every other blog post, forum answer, KB article has told you to do. You've used your insanely good SharePoint debugging skills, you have rebuilt your search service application (2x) and for some reason this error just won't go away.

I have no idea what causes this, but I know how to fix it.
To know for sure that you are having the same problem as me, find the virtual application in IIS Manager under "SharePoint Web Services" that has the same ID as referenced in BOLD above in the windows application logs. (If you aren't actually doing round robin with this service, it may be on one server and not the other, so check both). See the connection in the pic below to the error above?



Click advanced settings and find the application pool it's running under. Mine was a bunch of crazy letters and numbers, like 71283482842042sdf203402. Go to the application pool under "application pools" and you'll see it is stopped. Am I right so far? Well the problem you are having seems to be an issue with the managed account that app pool is running under not working anymore for some reason. Here's how I fixed it:

  1. Logged into AD users/computers and manually reset the password to something I know.
  2. Logged into WFE and went back to IIS Manager and reset application pool credentials to use new password.
  3. Started app pool and made sure it stayed up
  4. Logged into WFE server and ran SharePoint powershell command: Set-SPManagedAccount -Identity domain\searchapppoolAccount -UseExistingPassword
  5. Updated the services MMC with this new password and restarted the SharePoint Server Search 14 Service (on all machines in the farm)
  6. Now navigate on your WFE server to the URL that I had bolded above and instead of seeing a 503 error, you should now see a nice service WSDL page: http://server:32843/2313ae1967844030a54407c13c36d972/SearchService.svc

No idea what just happened, but it worked. Now, beer.

Monday, May 17, 2010

Error 'CE_OTHER' when trying to convert document to page using the document conversion feature.

I went back and forth, googling my brains out for a day trying to figure out why I couldn't get document conversions working on my '07 MOSS farm. It turns out that I had to run the "Office Document Conversions Launcher Service" and the "Office Document Load Balancer Service" as the Local System account. Nothing else is good enough apparently. When restarting these services be sure to start the load balancer first, but you probably already knew that since you've been googling your brains out all day trying to find the solution to this. Here's the fun you'll find in the logs on the WFE if you're having this problem:



Requesting launcher from load balancer
Executing method on launcher
Trying to get launcher from load balancer
error code CE_OTHER from the document conversions framework in the callback handler
ScheduledItemEventReceiver:ItemUpdating for item '[page name]
Error 'CE_OTHER' when trying to convert document to page using the document conversion feature.
Failed to generate .
Converting the document to a page failed. The converter framework returned the following error: CE_OTHER
CreatePage: DocumentPagificationException when attempting to convert page: Converting the document to a page failed. The converter framework returned the following error: CE_OTHER at at Microsoft.SharePoint.Publishing.Internal.TransformDoneHandler.Microsoft.SharePoint.ISPConversionProcessor.PostProcess(SPExecutingConversionRequest etr, CreateDCInfo cdti, Boolean& runDefaultPostProcessing) at Microsoft.SharePoint.Administration.SPDocumentTransformDriver.Transform(SPWebApplication wa, ExecutingTransformRequest etr) at Microsoft.SharePoint.SPFile.Convert(Guid ConverterId, String newFileName, String configInfo, String handlerAssembly, String handlerClass, Byte priority, String peopleToAlert, Boolean sendACopy, Boolean synchronous, Guid& workItemId) at Microsoft.SharePoint.Publis

Thursday, November 19, 2009

SharePoint Forms Based Authentication on IIS7, Server 2008

Most tutorials available on the internet for setting up DBA on MOSS are specific to IIS6. I just found a quirk with IIS7 that may slow you down. If you are getting

provider: SQL Network Interfaces, error: 26

and you know that your connection string and connectivity between servers is good, then you may need to force IIS7 to see your new membership and role providers by default. From onenote:

For IIS 7 and forms based authentication, you have to go to your site and specify the default provider for membership and roles in IIS after you have made the additions to the web.config that all the tutorials tell you. The first time you click each, you have to let it try the default and then time out, this takes a minute. Once it has timed out, you click "Set Default Provider" in the right hand column and select your provider. You have to do this for both Roles and Users. Prior to doing this make sure both of your providers appear in the Providers feature of IIS.

Wednesday, August 13, 2008

Type: SchemaValidationException, Exception Message: Schema validation found non-datatype errors.

If you get that error when opening an infopath form after programmatically editing the form, you need to set the PreserveWhitespace property to true:

XmlDocument myDoc = new XmlDocument();
myDoc.PreserveWhitespace = true;
myDoc.Load(myInStream);