Monday, April 20, 2009

Moving to Abstractions-R-Us

Due to our inability to make a SharePoint server work sufficiently, my company is no longer going to be using SharePoint, and I am no longer doing SharePoint development. I will continue blogging at

Tuesday, November 18, 2008

Displaying correct user names

In looking for an answer to our current dilemma, I found many people are struggling with a problem we already solved, so here I share the solution.

A common issue is that, even though display names in AD may be "FirstName LastName", they are not pulled into SharePoint, letting display names in SP default to "Domain\Username".

The solution: Make sure the account used to import users is trusted by the domain (controller). You have to give it perms to access things like display name and email address.

Take our former situation for example. Here, we have Domain-A (which has the DC which most user accounts are on) and Domain-B (where our MOSS servers are). Domain-B trusts Domain-A, but not the other way around. All MOSS-related system accounts were originally on Domain-B... So, they would pass credentials on to the DC on Domain-A (because B trusts A), but could not get display names, email addresses, etc. from the DC (because A does not trust B).

So we had to create an account on Domain-A to query AD. We then set said account as the one that imports profiles:
  1. In Shared Services Administration, go to User profiles and properties -> Configure Profile Import.
  2. Under Default Access Account, we input credentials for our Domain-A account with read access on AD. Problem solved, on to the next one!

Friday, November 14, 2008

Display Names are Screwed!

One day, the display names on our production SharePoint server were all truncated to the first 3 characters! Egad! No one knows why or how this happened.

Tuesday, September 30, 2008

Disabling Impersonation Without Breaking Search Crawler

A common workaround for authentication issues on some SP apps is to disable impersonation in the Web.config file corresponding to the SP app's IIS site. Unfortunately, doing so makes the site inaccessible to the search crawler.

However, iamabadprogrammer has written a post describing a way to disable impersonation without disabling or breaking the search crawler. Here's how I made this work:
  1. In Central Administration, go to Application Management > Create or Extend Web Application > Extend Web Application
  2. Use the Web Application box to select the SP App that needs to have impersonation disabled and search enabled.
  3. Fill out the rest of the settings to your liking. Remember that the new IIS site you are creating need not be accessible by your users. Also remember to make this site not conflict with any existing sites (which can easily be achieved by using the randomly-generated port number).
  4. Jot down what's in the URL box under "Load Balanced URL". You'll need it later.
The crawler has a path to search, so let's now tell it to search the searchable IIS site and not the user-friendly site.
  1. Go to the admin portal for the SharedServices corresponding with the SP app.
  2. Click on Search Settings > Content Sources and Crawl Schedules
  3. Click on whatever content source that includes the SP app.
  4. In the Start Addresses box, replace the line(s) corresponding with the user-friendly IIS site with equivalent line(s) found on the new Searchable IIS site.
  5. Set the other settings as you like and click OK.
Now the search crawler should indeed successfully crawl the SP application, but search itself will yield no results! Why? Because, when you search "in this site", you are searching for things found under the site the user is actually using, whereas the crawler found things under the other site. Same content, different access paths.

So we need a way to tell the search engine that everything at the searchable site is really something at the user-friendly site. We do this with Server Name Mappings:
  1. Go to the admin portal for the SharedServices corresponding with the SP app.
  2. Go to Search Settings > Server Name Mappings > New Mapping
  3. In the "Address in index:" blank, input the address of the searchable IIS site (e.g. http://myServer:12345/)--i.e., the "Load Balanced URL" you wrote down earlier.
  4. In the "Address in search results" blank, input the address of the user-friendly IIS site (e.g. http://myServer:80/ or This would be the "Load Balanced URL" of the user-friendly site.
Please respond with any comments, corrections, or questions you may have.

Thursday, August 7, 2008

A code comparison: SendEmail activity vs. SPUtil.SendEmail

Although shorter is not necessarily faster or easier to understand (much less is shorter always better), the code length of a given way of tackling a problem is nonetheless a useful metric for how valuable it is. So today I shall compare code length of two means of sending email in a SP workflow.

1. Use a SendEmail activity.

We can actually do this a few different ways:

  1. Bind the activity's properties to variables in your workflow and set the values pseudo-statically:
    private mySendEmailActivity_From = "From Address";
    private mySendEmailActivity_To = "To Address";
    private mySendEmailActivity_Subject = "Subject";
    private mySendEmailActivity_Body = "Body";

  2. (Some say this causes runtime errors!) Just set the properties on the activity itself prior to running it:
    private void notifyHr_MethodInvoking(object sender, EventArgs e)
    mySendEmailActivity.To = "To Address";
    mySendEmailActivity.From = "From Address";
    mySendEmailActivity.Subject = "Subject";
    mySendEmailActivity.Body = "Body";
  3. Bind the properties statically AND set them dynamically, which would be about the length of the above two examples added together.

2. Code it yourself!

Many find it more reliable to insert a Code activity and bind it to the following procedure:
public void sendRejectionEmail()
SPUtility.SendEmail(workflowProperties.Web, true, false, workflowProperties.OriginatorEmail, "HR-1 Rejected!", "pwn'd by the approvers! Mwahahaaaa!!!");
That's just one long line of code.

So... using the above one-liner should not take any longer to write than using the SendEmail activity.

Wednesday, August 6, 2008

More e-mail blues

I have put off wrestling with the particularly pesky (buggy? broken? useless?) SendEmail activity today, and found (to make a long story short):
  1. There is no way on earth (it seems) to properly set the correlation token of a SendEmail activity nested within a custom composite activity. Thus, it is incompatible with the most important abstraction mechanism provided by WF, thus it is not useful for development of complex workflows.
  2. Both the SendEmail activity and the "ability" to notify users of tasks via email (taskProperties.SendEmailNotification == true) are known to throw the workflow-terminating error "The e-mail message cannot be sent. Make sure the outgoing e-mail settings for the server are configured correctly" on some servers with properly-configured email settings.
I found the aforementioned error message to be thrown by my workflow when:
(the workflow was coded to copy data from my initialization form to my task forms) and ( (the tasks were set to send e-mail notifications) or (my SendEmail activity nested within my custom sequential activity was Enabled) ).
If the workflow was _not_ coded to copy data my initialization form to my task forms:
  1. Upon building the project, I get the following warning:
    "Activity 'sendRejectionEmail' validation warning: The correlation may be uninitialized."
  2. Upon starting the SendEmail activity, the workflow crashes with error message:
I tried adding an OnWorkflowActivitated activity to my custom sequential activity, and I set it and the SendEmail activity to the same correlation token. This time,
  1. The aforementioned warning goes away! (Maybe I'm onto something!)
  2. The workflow hangs (never assigns the first task even), and one of the w3wp.exe processes takes up 25% system resources (all it can get in a single thread on a quad-core processor, as is the case with this server).
Between this post and my previous one, I think I have made a good case that one should _not_ expect the OOB SendEmail activity to work.

The e-mail message cannot be sent. Make sure the outgoing e-mail settings for the server are configured correctly.

When building a SharePoint workflow that sends email either via the SendEmail activity or the CreateTask activity (with taskProperties.SendEmailNotification = true), many workflows will receive the error message "The e-mail message cannot be sent. Make sure the outgoing e-mail settings for the server are configured correctly," even if email works elsewhere (e.g. outside the workflow).

I get this message for every task assigned in my workflow, but I know the "offending code" that causes this error. Generally, it is code that copies data from the filled-out initialization form into a task's ExtendedProperties (which will show up in the custom task form). (Also note that the custom Task form has all the same fields as the Initialization form, plus some. I copied and pasted the fields from the Initialization template to the Task template when desiging in InfoPath.)

Specifically, in an initialization routine called within onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e), I have several lines that look like the following:

taskProperties.ExtendedProperties["MyFieldName"] = parsedInitData.MyFieldName;
  • taskProperties is a new SPWorkflowTaskProperties object to which MyCustomActivity.CreateTask_TaskProperties is assigned.
  • parsedInitData contains the data placed in the Initialization form that kicks off the workflow.
  • MyFieldName is the name of a field in said initialization form template, thus also a form in parsedInitData.
SOOO... I find that others have suggested the following workarounds:
What workarounds work for you all, my readers (all zero of you)? I shall investigate what to do in my specific situation today.