This blog post is a contribution from Balaji Sadasivan, an engineer with the SharePoint Developer Support team.
Issue
In order to audit workflows, we require to see the name of the workflow in the history list.
Cause
Currently the workflow history has no provision to show the workflow name.
Workaround
Create a new workflow history list from the schema of the original workflow history list. To the workflow history list add an ItemAdded even handler. In that write the flowing code:
SPListItem item = properties.ListItem;
SPSite site = new SPSite("http://<Servername>");
SPWeb web = site.RootWeb;
SPList destination = web.Lists["CustWFHistory"];//Custom workflow list
SPList wrkFlwList = web.Lists["MyList"];//The list where the workflow is located
string assocID = item["Workflow Association ID"].ToString();//Copying values from source workflow history
string templateID = item["Workflow Template ID"].ToString();
string listID = item["List ID"].ToString();
string primaryID = item["Primary Item ID"].ToString();
DateTime dateOccured = (DateTime)item["Date Occurred"];
Guid instance=new Guid(assocID);
SPWorkflowAssociationCollection WFCol = wrkFlwList.WorkflowAssociations;
SPListItem newItem = destination.Items.Add();
newItem["Workflow Association ID"] = assocID;//Adding them to destination
newItem["Workflow Template ID"] = templateID;
newItem["List ID"] = listID;
newItem["Primary Item ID"] = Convert.ToInt32(primaryID);
newItem["Date Occurred"] = dateOccured;
foreach (SPWorkflowAssociation wa in wrkFlwList.WorkflowAssociations)
{
string silID = wa.Id.ToString();
assocID = assocID.Replace("{", "");
assocID = assocID.Replace("}", "");
assocID = assocID.Trim();
if(silID==assocID)
newItem["Workflow name"] = wa.Name;//Getting the workflow name
}
newItem.Update();
site.Dispose();
The new list will show up with the workflow name, workflow association ID, List ID, date occurred. You can add fields like user ID etc. to the custom list query it from the workflow history list and add it.
Puzzle
In MOSS 2007 and SharePoint 2010 you can reroute the user to a custom page after they complete uploading documents to a document library to by changing the source parameter in the query string URL like what’s shown below:
http://[siteUrl]/_layouts/UploadEx.aspx?List=%7bD8358575-868E-47D6-A188-77607D7D4549%7d&RootFolder=&Source=http%3a%2f%2fbalaji-2srv%3a1983/SitePages/Home.aspx
However be sure to remove an additional parameter &IsDlg from the query string for this to work.
This also holds true for uploading multiple documents in SharePoint MOSS 2007. But in SharePoint 2010 we are using an ActiveX control that is not allowing users to be routed to a custom page after uploading multiple documents.
In the SharePoint 2010 UploadEx.aspx page calls the UploadCtl ActiveX and hardcodes the URL to the library URL followed by AllItems.aspx. Hence varying the source parameter doesn’t work in this scenario.
Solution
Create a custom upload page.
Here we pass the rerouting parameter to a web UploadCtl via a hidden field. Change the value of this parameter to whatever is required by you.
<input type="hidden" name="Confirmation-URL" id="Confirmation-URL" value="/_layouts/settings.aspx" />
You can rename this file as UploadEx.aspx after backing up the original, or provide an http module that will redirect the user to this page when UploadEx.aspx is called.
Full Code
Full code attached as a separate ASPX file. Download
This blog post is a contribution from Aaron Miao, an engineer with the SharePoint Developer Support team.
When developing a custom claims provider, you may need to retrieve user information from Active Directory (AD) with LDAP query programmatically. You can add custom attributes by extending the AD schema. Those custom attributes should be usually in global catalog.
Following is the code used to retrieve user information with LDAP query. This works fine if you retrieve the info from the same domain where the custom attributes reside. However it does not work if you retrieve the same user information from a user or machine that is in different domain.
DirectoryEntry root = new DirectoryEntry("LDAP://" + ldap);
root.RefreshCache();
DirectorySearcher ds = new DirectorySearcher(root);
ds.Filter = "samAccountName=" + samAccountName;
ds.SearchScope = SearchScope.Subtree;
SearchResult result = ds.FindOne();
The reason is that if one wants to access an AD custom attribute from a machine that is not part of the domain where the custom attributes reside, one needs to pass the fully qualified name of the object to access it. Otherwise the schema cache on the client machine is not properly refreshed even the schema.refresh() is called.
Changing the first line of above code like below should make it work.
DirectoryEntry root = new DirectoryEntry("LDAP://" + domain + "/" + ldap);
The above line generates something like:
LDAP://companydev.com/OU=55Users,DC=companydev,DC=com
Domain is needed to properly get the DirectoryEntry for the object to query from another domain. LDAP://OU=55Users,DC=companydev,DC=com only represents an object in same domain.
Note that you don't have to pass the full controller name like below:
LDAP:// devdc.companydev.com/OU=55Users,DC=companydev,DC=com
Notes: to AD/LDAP expert (which I am not), the information looks obvious. It took quite a while to figure out and find right information on this topic in SharePoint development space. So, I shared the information here