четверг, 17 марта 2011 г.

Creating State workflows with SharePoint Designer and InfoPath


I just want to share our experience about creating quite complex workflows with SharePoint designer. 

The main problem with SPD is that you can’t create loops in your workflow – it can only proceed to next step or wait for something. This is why, for example, it is hard to implement things like custom approval workflows with SPD, as they might require returning the document to some previous state and getting additional reviews/comments.

In the internet there was an idea that you can start another workflow by creating a list item in some dummy list, and if needed you can implement some kind of looping with this technique.

We considered this as too much of unneeded customization and complications, and came to a new approach. Firs of all, you need an InfoPath form that will store 2 additional fields: ProcessState and PrevProcessState. Each action that the user makes in the InfoPath form will save the current value of ProcessState into PrevProcessState and update ProcessState to a new value.

Then you create a WF that starts every time an item is changed and create many steps that have something like “If ProcessState=XXX and PrevProcessState=YYY then …”. Using this idea you can implement any workflow you need with SPD+InfoPath. Each of this IF statements in the workflow will represent a transition between 2 states in the workflow, therefore in the end you get a way to create State Machine Workflows instead of Activity WFs. I think it is a great possibility. 

People Picker performance in SharePoint 2010


When you have large and complicated AD structure you will probably run into problems with PeoplePicker controls in SharePoint. Either they will be working slowly or not working at all. We faced a problem that they were not possible to use because of the slowness.

According to official MSDN documentation, PeoplePicker is first sending a DNS query to determine the nearest Global Catalog server, then sends several LDAP requests. Sometimes the DNS can affect the performance, and sometimes it is the LDAP itself. Network latency also might be an issue. 

In our case the poor performance of People Picker could not be explained by latency/server load. Typing my last name "Inyushin" in the control and clicking the "Check user names" icon in people picker caused a huge delay - about 5 minutes. When using samAccountName or email the people picker was working quickly but searching by last name or first name was too slow. Another interesting symptom was that searching users using the search dialog was working normally.

We started to investigate and ended up with the following solution:

1. Configured the people picker by using the following PowerShell script to avoid querying AD when possible:

$web = get-spweb("http://webapp_root")
$webapp = $web.Site.WebApplication
$ps = $webapp.PeoplePickerSettings
$ps.ActiveDirectoryRestrictIsolatedNameLevel=$true
$webapp.Update()

2. Configured people picker to search accounts in our domains by using the following STSADM commands

stsadm.exe -o setapppassword -password P@SSWORD
 stsadm.exe -o setproperty -pn peoplepicker-searchadforests -pv "domain:d1.company.com,d1\account_name,P@SSWORD;domain:d2.company.com,d2\account_name,P@SSWORD"
Having done so the people picker started to work quickly and resolve the user names within seconds.

пятница, 4 марта 2011 г.

Modifying the SharePoint Taxonomy Fields in code

While investigating this issue, I have found several posts that sugested a quite complicated way to modify taxonomy fields through code. Here comes some code that would to exactly that in a quite simple way. I have found this method in internet, but modified it to use the AnchorId of the taxonomy field, as otherwise it won't work with fields that are not mapped to the root of a term store.


public static void SetTaxonomyFieldValue(SPWeb web, SPListItem item, SPField field,
 string value, bool isAddingValuesToTermSetAllowed)
{
    // Gets the taxonomy field instance
    TaxonomyField managedField = field as TaxonomyField;
    // Gets the current taxonomy session
    TaxonomySession session = new TaxonomySession(web.Site, false);
    // Gets the term store (by SspId)
    var termStoreCol = session.TermStores[managedField.SspId];
    // Gets the terms of a specific term set (by TermSetId)
    var parentTerm = termStoreCol.GetTerm(managedField.TermSetId, managedField.AnchorId);
    var termCollection = parentTerm.Terms;
    if (isAddingValuesToTermSetAllowed)
    {
        if (!parentTerm.Terms.Any(t => t.Labels.Any(l => l.Value == value)))
        {
            parentTerm.CreateTerm(value, 1033);
            termStoreCol.CommitAll();
            SetTaxonomyFieldValue(web, item, field, value, false);
            return;
        }
    }
    // Sets the field value using the list of terms
    if (managedField.AllowMultipleValues)
    {
        var listTerms = new List<Term>();
        listTerms.Add(termCollection[value]);
        managedField.SetFieldValue(item, listTerms);
    }
    else
    {
        managedField.SetFieldValue(item, termCollection[value]);
    }
    item.Update();
}