Enumerating User Profiles

I’ve been trying for a week to make a workflow activity that would parse all user profiles and find a user with a specific title. At first sight it’s a straightforward activity, instantiating a UserProfileManager from the current context and then doing a foreach loop.

Well, accessing user profiles requires an authorized user. In order to overcome this, in my activity I create a delegate that runs with elevated privileges (SPRunWithElevatedPrivileges method). I also instantiate a new SPSite context with the GUID of the SPSite already passed from SharePoint, because the original SPSite is associated with the caller identity, not the impersonated identity.

It didn’t work, either. I always got a “Access Denied: Only an administrator may enumerate through all user profiles.” error. Other people also got this error in the same circumstances (for example here). However, in my lab MOSS deployment (single-server) it worked OK. I was puzzled.

Thanks to my friend at Microsoft, Carlos, I was able to make it work.

The catch is that the impersonated user is the Application Pool identity. You have to assign to this account (NETWORK SERVICE in my case) two specific rights for Personalization Services Permissions (/ssp/admin/_layouts/ManageServicePermissions.aspx) at Shared Services administration page. Those rights are Use Personal Features and Manage User Profiles.

It worked!

Hide a field from NewForm.aspx

Updated (18/03/2008): There’s an extended custom web part made by Paul that does the same thing and much more. You can start reading from here.

I was faced with this problem lately: you have a optional field in your list that you’d like to leave hidden from the user (it’s used for workflow status keeping).

The first solution is to customize NewForm.aspx from SharePoint Designer and replace the default list form with Custom List Form, in which you can remove the fields you don’t need. The inconvenience is that you lose the ability to attach files to the list item. As I had to attach files to the list, this option was unacceptable.

Fortunately, there is another way to hide a field. You can customize NewForm.aspx and add a JavaScript code that hides the whole row the field is placed. I used the code snippet from SharePoint Designer blog and I came out with this small code block, to be placed on NewForm.aspx (or EditForm.aspx, as you wish).

You have to replace these fields (see the SPD blog entry for more information):

  • TAGNAME: HTML element that is being rendered (“SELECT“, “INPUT“…)
  • IDENTIFIER: SharePoint field type identifier (“TextField“, “DropDownChoice“…)
  • FIELD NAME: Display name of the field (e.g. “Status“, “Customer Name“…)

<script language=”javascript” type=”text/javascript”>

_spBodyOnLoadFunctionNames.push(“hideFields”);

function hideFields() {

var control = getTagFromIdentifierAndTitle(“TAGNAME”,“IDENTIFIER”,“FIELD NAME”);

control.parentNode.parentNode.parentNode.style.display=“none”;

}

function getTagFromIdentifierAndTitle(tagName, identifier, title) {

var len = identifier.length;

var tags = document.getElementsByTagName(tagName);

for (var i=0; i < tags.length; i++) {

var tempString = tags[i].id;

if (tags[i].title == title && (identifier == “” || tempString.indexOf(identifier) == tempString.length – len)) {

return tags[i];

}

}

return null;

}

</script>

Getting the SPUser out of SPFieldUser

SPFieldUser field type stores the username in “1#;User” fashion. This is a small annoyance when trying to access the user data, for instance when parsing “Assigned To” field of a Tasks list item.

This small amount of code will allow you to quickly get the underlying SPUser object. In this example, the SPFieldUser is the “Assigned To” column.

SPFieldUser assignedTo = (SPFieldUser)task.Fields[SPBuiltInFieldId.AssignedTo];

SPFieldUserValue user = (SPFieldUserValue) assignedTo.GetFieldValue(task[SPBuiltInFieldId.AssignedTo].ToString());

SPUser userObject = user.User;

An unknown error has ocurred – One solution

Today I was setting up a fresh virtual machine with MOSS 2007 and I met the dreaded “An unknown error has ocurred” message after the SharePoint Configuration Wizard finished.

I traced it in the Event Viewer and it was a repetitive error such as this one:

The application-specific permission settings do not grant Local Activation permission for the COM Server application with CLSID
{61738644-F196-11D0-9953-00C04FD919C1}
to the user NT AUTHORITYNETWORK SERVICE SID (S-1-5-20). This security permission can be modified using the Component Services administrative tool.

I looked up in the Registry and found out that the CLSID in the message identified one “IIS WAMREG admin service”. Then, I opened the Component Services, DCOM Config and under the properties of WAMREG service component I allowed NETWORK SERVICE account local launch and local activation permissions (on the Security tab).

The MOSS 2007 fresh welcome was just one IISRESET away!

Community Kit for SharePoint 2.0 Pre-Release

Exciting news! The CKS 2.0 has been pre-released. It’s just been a few months since the project started and we already have something tangible.

The features included in this pre-release are:

Enhanced Blog Edition Beta 1
Enhanced Wiki Edition Alpha
ChatterBox AJAX Beta
Tag Cloud

Download any of the components above and play with them (not in production environment, though)…and, above all, provide feedback to the team.

Full announcement can be found here.

How to Optimize SharePoint Designer’s Use of Content Types in a Workflow

I’m annoyed by many little details of the SharePoint Designer Workflow editor, but the main one is that it profligates in the creation of content types. It creates one content type for any custom form that you create when collecting data from a task. Moreover, it doesn’t give you the chance to reuse an existing type….every new task is a new content type in the Tasks list.

However, you CAN reuse existing content type for the Tasks list. You just have to create the first form as usual and then create a second form with a easy-to-find name such as “DUMMY”. Then, a little bit of manual work follows:

  • Open the XOML file of your workflow with right click, choosing “Open With” then “SharePoint Designer (Open As XML)
  • Find the first “CollectDataTask” action, such as this one

    <ns0:CollectDataTask x:Name=”ID15″ ContentTypeId=”0x01080100AC1C1A96057F4B4897ED6C99E92728F500A85399B392A23943B207F10B3F8BB318″ TaskId=”{ActivityBind ROOT,Path=taskAM}” Title=”CAF Approval 1″ InProperties=”{x:Null}” __Context=”{ActivityBind ROOT,Path=__context}” OutProperties=”{x:Null}” AssignedTo=”{ActivityBind ROOT,Path=userAM}” />

  • Copy the line after the x:Name field
  • Find the CollectDataTask with the Title=”DUMMY”
  • Replace the content of the line after the Name field with the first action copied data

That’s it! Now you can safely remove the DUMMY content type from the Task list and Site Content Types list.

SharePoint Hotfix is available

Microsoft released, at least, a quick-and-dirty hotfix that fixes up several issues with SharePoint Services 3.0.

In particular, I’ve found one of the issues to be very annoying. Namely, when you create a site and customize a list view to something other that the default view and then save the site as a template, these changes are not saved. So, when you create another site based on this template, the list view you customized reverts to the default view.

Among other issues, this is solved in the Hotfix that can be found on this address: http://support.microsoft.com/default.aspx?scid=kb;EN-US;934790

Well…now we have to wait for the SP1 to become available…..I hope it will be this year.

COM Again (0x81020037)

I found this “descriptive” error message in a fragment of MOSS 2007 code in a webpart that uploads and tags a document with appropriate metadata:

Microsoft.SharePoint.SPException: DOMAINuser has modified the file Sample.doc
System.Runtime.InteropServices.COMException (0x81020037)

The code 0x81020037 is “Save Conflict” error code in COM. The culprit:

SPListItem doc = list.Add();
doc[“Field1”] = “Value”;
doc[“Field2”] = “Value”;
doc.CheckIn(“Check-in Message”);
doc.Update(); <-- this is the line that throws the error I tried reversing CheckIn and Update methods, with no success. However, the following trick did the job: SPListItem doc = list.Add();
doc[“Field1”] = “Value”;
doc[“Field2”] = “Value”;
doc.CheckIn(“Check-in Message”);
doc = FindByTitle(doc.Title)
doc.Update();

FindByTitle is a custom method I wrote that finds a SPListItem in a SPList, performing a search on the Title field.

The explication: the underlying COM scaffolding doesn’t like the Update() method after CheckIn(). I had to “reacquire” the item and perform the Update() on this new reference to the same document.

Elementary, dear Watson….