Prash's Blog

SharePoint SPItemEventReceiver ItemUpdating and ItemUpdated being called multiple times July 30, 2009

Filed under: SharePoint — prazjain @ 5:04 pm
Tags:

Problem : SPItemEventReceiver’s ItemUpdating and ItemUpdated methods are called twice! (to be precise)

And if you have Update() or SystemUpdate() call on the document / item, this leads to getting an exception message on screen about “XYZ has already been modified”. This happens due to race condition between the two threads when updating the document / item.

Background

In SharePoint, an event receiver can be hooked to a SPList to listen to any events occuring on the SPList.

This is how you do it :

SPWeb spWeb = GetSPWebInstance();
// assuming that you have a document library by name of "My List"
SPList sharedDocuments = web.Lists["My List"];
// attach a ItemUpdated event.
SPEventReceiverDefinition spEventReceiverDef = sharedDocuments.EventReceivers.Add();
spEventReceiverDef.Class = "Fully.Qualified.Class.Name";
spEventReceiverDef.Assembly = "AssemblyName, Version=x.x.x.x, Culture=neutral, PublicKeyToken=thePublicKeyToken"
spEventReceiverDef.Sequence = 5000; // put any number here
spEventReceiverDef.Type = SPEventReceiverType.ItemUpdated;
spEventReceiverDef.Update();

To attach an ItemUpdating event receiver, all you need to do it replace the type from SPEventReceiverType.ItemUpdated to SPEventReceiverType.ItemUpdating, and ofcourse the class / assembly information if you have the implementation in a different class / assembly.

ItemUpdating gets called synchronously just before an item is updated. And ItemUpdated gets called after the item is updated.

Cause

When force checkout is enabled on document library, this is how the document editing works :

When you checkout a document for editing, a local copy of the document is created that saves all the changes made by the user. At the time of checking-in the document, this local copy updates the original copy (even if there are no changes), then there is another request that does check-in on the document.

This is the reason why ItemUpdating and ItemUpdated are called twice (once for each step of check-in process).

Solution

A Property : “vti_sourcecontrolcheckedoutby” is provided that can be used to distinguish if the update is because of check-in or due to other reasons.

If this property exist in BeforeProperties and not in AfterProperties of SPItemEventProperties instance then it means that the callback is for item check-in event, else it is for event other than item check-in.

So if you have a piece of code that you want to be executed only when the check-in is done then enclose it in a conditional statement like this :

public override void ItemUpdated(SPItemEventProperties properties)
 {
// perform actions only if the update event is triggered by check-in
 if (properties.AfterProperties["vti_sourcecontrolcheckedoutby"] == null &&
 properties.BeforeProperties["vti_sourcecontrolcheckedoutby"] != null)
 {
// your code goes here
}
else
{
//ignore
}
}

Putting your code block in correct conditional section would make sure that your code gets executed for the check-in event callback.

 

Unable to preview / add SearchFacets webpart in SharePoint July 27, 2009

Filed under: SharePoint — prazjain @ 11:00 pm
Tags: ,

Recently when I tried to install the search facets webpart in my development environment, I was to find that it would not be as quick as I had thought it would be.

I followed all the steps in the guide but it would not allow me to preview the SearchFacets webpart (it allows me to preview the other two webparts). If I try to skip it, and go ahead with adding and using it, I just would not be able to add it!

I have raised this over here : http://facetedsearch.codeplex.com/Thread/View.aspx?ThreadId=62960&ANCHOR#Post216315 but still have to receive a reply that could possibly help me.

Apparently my colleague (whose VM I had copied over to my machine) was able to preview it and add it!

So given that we are both using the same VM copy, one is able to install and add it other one is not. Or maybe its some silly configuration I missed ?

In the mean time  while waiting for the reply, I have tried reinstalling my sql server and sharepoint server on the virtual machine. But it has not worked out yet.

In the worse case scenario, I would have tried creating a new VM image altogether and do it from scratch, but without knowing why it happened I cannot be sure it would not happen next time.

If anyone had faced the same problem and was able to resolve it, do post your comments about it.

 

Using Virtual Earth / Bing Geocoding Webservice July 24, 2009

Filed under: Bing — prazjain @ 4:10 pm
Tags: , , , ,

Me being new in London, I took it upon myself to find pubs close to me. I created a database with location of pubs, now I had to do display them on map to find their proximity. As I just had the address of these pubs and not their geographic coordinates, I needed to convert address to geographic coordinates. And then I can use them on any of the various mapping platforms to display location on map.

I wrote a tool which would enrich the existing database of pubs with their respective geocodes. I am giving an overview of what / how I went ahead with doing that:

  1. Get a windows live id, if you do not already have one. You can get one here : https://accountservices.passport.net
  2. Get a license for using the Geocoding Webservice. You would need to register for Bing Maps Developer account. You can sign up here : https://mappoint-css.live.com/mwssignup/
  3. Now, I am assuming you already have a Visual Studio solution and project created where you are going to use this functionality, add a reference to this web service :

    https://staging.common.virtualearth.net/find-30/common.asmx, use this as URL for service and name it as ‘VEStagingToken’.

  4. Create a class GeoCodeFetcher that would hold all the logic for interacting with geocoding webservices.
  5. In your project create a reference to webservice VEGeocodingService pointing to this url: http://staging.dev.virtualearth.net/webservices/v1/geocodeservice/geocodeservice.svc?wsdl

PS : Bing limits you 30,000 transactions per day in the free developer account, which should be enough for any sample developer application. [Confirmed through offical sources and true as of July 2009]

For the rest, I have written commented code for your reading, this should be fairly self-explanatory.

Note: I could have refactored AddressToGeocode method a little bit to reduce the number of if/else conditions, but the aim of this post is to be make it more understandable for any new programmer who is using this service for the first time.

This Code sample is in C#, you can as well port this code to Java with minor syntax changes.


///<summary>
/// Responsible for getting geocodes for a pub address. This class also holds logic
///for connecting to databases and saving data back to databases.
///</summary>
public class GeoCodeFetcher
{
#region [ Private Variables ]
///<summary>
/// Token that is to be sent everytime a GeocodeService call is made to VE to get geocodes for
/// a pub address.
///</summary>
private string _clientToken;

#endregion

#region [ Constructors ]
///<summary>
/// Gets a token from VE webserice for making Geocode service calls.
///</summary>

public GeoCodeFetcher()
{
string veAccountId = "VEAccountId";
string veAccountPwd = "VEAccountPwd";
// create the token once for all calls
CreateToken(veAccountId, veAccountPwd);
}

#endregion

#region [ Private Methods ]

///<summary>
/// Get a token for invoking VE services with max validity of 60 mins.
///</summary>
///<param></param>
///<param></param>
private void CreateToken(string username, string pwd)
{

CommonService commonService = new CommonService();
commonService.Url = "https://staging.common.virtualearth.net/find-30/common.asmx";
commonService.Credentials = new System.Net.NetworkCredential(username, pwd);
// create token specification
TokenSpecification tokenSpec = new TokenSpecification();
// some ip, this is used for tracking by VE services. Ideally should be IP of machine that is
// making service calls. But it would not fail the call if its any valid ip.
// putting in some value as the service call should not fail because of this

tokenSpec.ClientIPAddress = "x.x.x.x";
tokenSpec.TokenValidityDurationMinutes = 60; // 8 hrs is max limit.
_clientToken = commonService.GetClientToken(tokenSpec);

}

#endregion

#region [ Public Methods ]
///<summary>
/// This gets the address as argument and returns the latitude and longitude geocode for it.
///</summary>
///<param></param>
///<param></param>
///<param></param>
public void AddressToGeocode(string address, out decimal latitude, out decimal longitude)
{
string results = string.Empty;
// create a geocode request
GeocodeRequest geocodeRequest = new GeocodeRequest();
//Set the token received from VE service as credential token
geocodeRequest.Credentials = new VEGeocodingService.Credentials();
geocodeRequest.Credentials.Token = _clientToken;
// set the address as query
geocodeRequest.Query = address;
// we want only the top result so min confidence is high

ConfidenceFilter[] filters = new ConfidenceFilter[1];

filters[0] = new ConfidenceFilter();
filters[0].MinimumConfidence = VEGeocodingService.Confidence.High;
//set the geocode options
GeocodeOptions geocodeOptions = new GeocodeOptions();
geocodeOptions.Filters = filters;
geocodeRequest.Options = geocodeOptions;
GeocodeServiceClient geocodeServiceClient = new GeocodeServiceClient();
GeocodeResponse geocodeResponse = geocodeServiceClient.Geocode(geocodeRequest);

// if more than one result available then take the top result
if (geocodeResponse.Results.Length > 0)
{
latitude = (decimal)geocodeResponse.Results[0].Locations[0].Latitude;
longitude = (decimal)geocodeResponse.Results[0].Locations[0].Longitude;
}
else
{
// try search with medium confidence
filters[0].MinimumConfidence = VEGeocodingService.Confidence.Medium;
GeocodeResponse gcrMedium = geocodeServiceClient.Geocode(geocodeRequest);
 if (gcrMedium.Results.Length > 0)
{
latitude = (decimal)gcrMedium.Results[0].Locations[0].Latitude;
longitude = (decimal)gcrMedium.Results[0].Locations[0].Longitude;
}
else
{
filters[0].MinimumConfidence = VEGeocodingService.Confidence.Low;
GeocodeResponse gcrLow = geocodeServiceClient.Geocode(geocodeRequest);
if (gcrLow.Results.Length > 0)
{
latitude = (decimal)gcrLow.Results[0].Locations[0].Latitude;
longitude = (decimal)gcrLow.Results[0].Locations[0].Longitude;
}
 else
{
// throw an exception that geocodes could not be found for this pub
throw new Exception();
}
}
}
}
#endregion
}

These are all the helper methods you would need to actually interact with the services, and use them in your code whereever you need to geocode data.

Check the article on how to use the geocoded values to calculate route from point A to point B and more. How to use Routing Service.

 

Sharepoint IdentityNotMappedException Some or all identity references could not be translated July 16, 2009

Filed under: SharePoint — prazjain @ 8:15 pm
Tags:

Error : Failed to create sample data

Exception : An exception of type System.Security.Principal.IdentityNotMappedException was thrown.  Additional exception information: Some or all identity references could not be translated.

How I got this error

Today while working on sharepoint to get Search working, I had a suspicion that my sharepoint installation might have got corrupted.

( Reason behind that is I had to rename my VM machine after installing sharepoint. As my VM was a copy of my colleague’s VM so we had same DB server names and other things which could mess things up.

So I followed the steps from one of the websites and it all seemed fine, until today when I could see some places where machine name / user names  etc were messed up because of which I could not get the search working in sharepoint.
)

So that meant either I revert back to a previous snapshot on my VM (and install all softwares I had installed since then ) or try installing Sql Server and Sharepoint again.

I chose the second option.

Both installed fine, but when it came to Configuration Wizard of Sharepoint, it failed at Task 8 : Failed to create sample data.

Exception

An exception of type System.Security.Principal.IdentityNotMappedException was thrown.  Additional exception information: Some or all identity references could not be translated.
System.Security.Principal.IdentityNotMappedException: Some or all identity references could not be translated.

Resolution

After googling it for some, I did these changes :

1) Remove all websites that had anything to do with sharepoint on my developer machine.

2) Remove application pools that had anything to do with sharepoint.

3) Added my current user LMACHINE\Administrator to my sql server 2005 instance as sys admin.

4) One of the application pools could not be deleted namely “Sharepoint – 80″, I investigated further and found that username was incorrect for this application pool. I corrected it and re-entered password.

Finally ran the Sharepoint Configuration wizard  again, and this time it worked !

What a relief.

The re-install of Sql Server 2005 and Sharepoint on my dev box, which according to estimates should have taken 30 mins, actually took 3 hours but how can you put that in a project plan.

 

Using SPFolder UniqueContentTypeOrder to Hide menu options July 15, 2009

Filed under: SharePoint — prazjain @ 5:41 pm
Tags: ,

I would explain the usage of this property by an example.
The example would be to hide a few items from the new menu button in menu bar.

I write a function that takes in a folder type and the list of content types that you want to hide from the

menu bar.

Now if you try to remove everything from the new menu button, you would get a runtime exception (just

like i got, so do not try that. Instead you might want to hide the new menu button!)

private void HideAllContentTypes(SPFolder rootFolder, IList<string> contentTypeNames)
 {
 IList<SPContentType> contentTypeCollection = rootFolder.ContentTypeOrder;

 List<SPContentType> contentTypes = new List<SPContentType>();

 foreach (SPContentType contentType in contentTypeCollection)
 {
 if (contentTypeNames.Contains(contentType.Name))
 continue;
 else
 contentTypes.Add(contentType);
 }

 rootFolder.UniqueContentTypeOrder = contentTypes;
 rootFolder.Update();
 }

Explanation of the code goes here :

1) IList<SPContentType> contentTypeCollection = rootFolder.ContentTypeOrder;
This step is important because you need to call SPFolder’s ContentTypeOrder property so that it initializes

internal storage structure and then when you later on call UniqueContentTypeOrder and set a collection on

it, you would not get a runtime exception.

2) Iterate through the SPContentType collection and create a new collection that does not include the

content types that you hate :)

3) Now you can assign SPFolder’s UniqueContentTypeOrder property with your new SPContentType

collection. Note you would get an runtime exception here if you tried to be smart and did not perform step 1.

4) Never forget to call Update() once you are done.

 

Defining Custom Folder content type and custom document content type July 14, 2009

Filed under: SharePoint — prazjain @ 10:49 pm
Tags: ,

A  Two Part Article Series

Part 1 : It would be about creating fields and custom content type for document and one

for folder. Then creating a feature to bind them together and activate on a site collection.

Part 2 : This would explain about you can associate those content types programmatically

with a document library.

Here we begin with Part 1

Note : Whereever you see Guids, replace them with new ones on your machine.

First we create a few fields that we can use in our custom content type

Create a file myfields.xml and paste this content in it :

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

<!-- first two fields are for creating custom document content type -->
<Field Type="Text" DisplayName="File Checksum" Required="FALSE" MaxLength="255" 

Group="MyGroup" ID="{01837735-8dec-4f20-b725-5d05e8747f60}" SourceID="{89369214-f359-419c

-80de-14763a3fd415}" StaticName="FileChecksum" Name="FileChecksum"  ShowInNewForm="FALSE" 

ShowInEditForm="FALSE"  ShowInFileDlg="FALSE"></Field>
<Field Type="Note" DisplayName="Information" Required="FALSE" NumLines="6" RichText="TRUE" 

RichTextMode="Compatible" AppendOnly="TRUE" Sortable="FALSE" Group="MyGroup" 

ID="{f48d6644-70ff-402c-847f-728f388d19b7}" SourceID="{89369214-f359-419c-80de-

14763a3fd415}" StaticName="Information" Name="Information"></Field>
 <!-- folder status field used by Custom Folder -->
 <Field Type="Choice" DisplayName="Status" Required="TRUE" Format="Dropdown" 

FillInChoice="FALSE" Group="MyGroup" ID="{61CA18AF-307D-4e08-9907-0274ECABEE56}" 

SourceID="{89369214-f359-419c-80de-14763a3fd415}" StaticName="CustomFolderStatus" 

Name="CustomFolderStatus">
 <Default>Not Started</Default>
 <CHOICES>
 <CHOICE>Not Started</CHOICE>
 <CHOICE>In Progress</CHOICE>
 <CHOICE>Completed</CHOICE>
 </CHOICES>
 </Field>
</Elements>

Now we create another file ctypesCustomDocument.xml and paste this content in it :

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

 <ContentType ID="0x010100F2895C5AACCB5A4599ACEF15F4E082D3" Name="My Custom Doc Type" 

Group="MyGroup" Version="1">
 <FieldRefs>
 <FieldRef ID="{f48d6644-70ff-402c-847f-728f388d19b7}" Name="Information" 

Required="FALSE"/>
 <FieldRef ID="{01837735-8dec-4f20-b725-5d05e8747f60}" Name="File Checksum" 

Required="FALSE"/>
 </FieldRefs>
 </ContentType>
</Elements>

Then create a file ctypesCustomFolderType.xml and paste this content in it :

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

 <ContentType ID="0x01200051F2CB82D79099439C4C3AF20A5517B5" Name="My Custom Folder Type" 

Group="MyGroup" Version="0">
 <FieldRefs>
 <FieldRef ID="{61CA18AF-307D-4e08-9907-0274ECABEE56}" Name="Status" Required="TRUE"  

/>
 </FieldRefs>
 <XmlDocuments>
 <XmlDocument 

NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
 <FormTemplates 

xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
 <Display>ListForm</Display>
 <Edit>ListForm</Edit>
 <New>ListForm</New>
 </FormTemplates>
 </XmlDocument>
 </XmlDocuments>
 </ContentType>
</Elements>

So just to re-iterate what we have done till here is :
Create a custom document type called “My Custom Doc Type” that includes two field

“Information” and “File Checksum”.
Create a custom folder content type called “My Custom Folder Type” this includes one field

“Status” which is a choice field.

Now we create a feature.xml file and put references to these three xml files in that one

file.

Copy this xml in feature.xml file :

<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
 Id="52143C47-C82C-40af-873C-6C9E52E9C73D"
 Title="Custom Content Types"
 Description="Installs Custom Content Types"
 Version="1.0.0.0"
 Scope="Site"
 DefaultResourceFile="core"
 Hidden="false"
 >
 <ElementManifests>
 <ElementManifest Location="myfields.xml"/>
 <ElementManifest Location="ctypesCustomDocument.xml"/>
 <ElementManifest Location="ctypesCustomFolderType.xml" />
 </ElementManifests>
</Feature>

Now copy all the 4 files we have created till now into 12-

hive/TEMPLATE/FEATURES/CustomContentTypes

Install the content types using this command (you would have to add stsadm to your path

variable for this to work)

stsadm -o installfeature -name CustomContentTypes -force

Once it is installed, we see how it can be used in the next article of this series.

 

Conditionally elevating security privileges to approve custom content type document July 14, 2009

Filed under: SharePoint — prazjain @ 9:48 pm
Tags: ,

In one of the posts before I had explained how to set content approval on a document library.

When you set content approval on that library, you would see that only when you are in admin / system account, that you can approve / reject documents else you cannot.

So here I would show you a scenario where you can elevate the privleges of the currently logged in user and approve / reject the document programmatically, depending on your business case.

Sample case : You have a Document Library “Custom List” which has documents of type “Custom Document”, now this “Custom Document” content type has some meta data associated with it i.e Document Type (which would be a kind of subtype to differentiate between different “Custom Document” files in the document library.
So based on this meta data / field of custom content type “Custom Document” we would decide if we want to automatically approve the document.

Here is the code that does the task :

1) To know how to enable content approval on a list programmatically, check the article here.

2) Now extend the class SPItemEventReceiver, to listen to the events the you are interested in handling.

3) Here we would capture the ItemUpdated event, and if the status of document is pending then elevate the privileges and approve the document.

public class CustomDocItemEventReceiver : SPItemEventReceiver
{
public override void ItemUpdated(SPItemEventProperties properties)
{
SPListItem item = properties.ListItem;
// if its a folder then return
if (item.Folder != null)
return;
// here you can have your code update the meta data of the document
//run with elevated rights
// store url of the list item in a variable as we would need to get a new instance of
// this list item in elevated privilege
string url = item.ParentList.ParentWeb.ServerRelativeUrl + "/" + item.Url;
// store the webGuid as we would need to get new instance of this web in elevate privilege
Guid webGuid = item.Web.ID;
// check if the document is in Pending state
if (item.ModerationInformation.Status == SPModerationStatusType.Pending)
{
// put a conditional logic that check if the list item meets the condition of being
// auto-approved.
bool approvalRequired = Helper.IsApprovalRequired(item);
if (!approvalRequired)
{
// disable event firing as you do not want recursive item updates to be invoked.
base.DisableEventFiring();
SPSecurity.RunWithElevatedPrivileges(delegate()
{
// important thing to keep in mind here is that you would need to get new instances to
// SPSite and SPWeb objects because the security privileges have changed. Just trying to
// get a reference to SPSite and SPWeb from objects that were instantiated in lower
// privileges would not work.
using (SPSite site = new SPSite(item.Web.Site.ID))
{
using (SPWeb web = site.OpenWeb(webGuid))
{
SPListItem elevatedItem = null;
elevatedItem = web.GetListItem(url);
elevatedItem.File.Approve("Updated by the system on major version increase");
}
}
});
// do not forget to enable the event firing again :) 
base.EnableEventFiring();
}
}
}

}

This way a user with contribute permissions when updating the document can automatically approve the document.

 

Programmatically Enabling Content Management, Checkout before edit, Content Approval and Versioning on Documents July 10, 2009

Filed under: SharePoint — prazjain @ 9:53 pm
Tags: ,

In this blog I would be posting about how to programmatically enable Content Management on a document library, how to enable versioning on documents, and making sure that the user checks out a file before it is edited.

These were some of the tasks that I had to perform a few days back and being a sharepoint novice I spent quite some time finding how that could be done.

But to my surprise I found that each of these three can be enabled by setting a simple boolean property.

First get an instance of SPWeb object that you want to work on, then

1) Enabling Content Management on a document library :

// web is instance of SPWeb
SPList docList = web.Lists["Custom List"];
// set the bool property
docList.ContentTypesEnabled = true;
// now you are ready to add your custom content types
// first get instance to your custom content type
SPContentType customContentType = web.Site.RootWeb.ContentTypes["Custom Content Type"];
docList.ContentTypes.Add(customContentType);
// and never forget this
docList.Update();

2) Enabling Versioning on documents :

// get reference to the document library in which you want to enable versioning
// web is instance of SPWeb
SPList docList = web.Lists["Custom List"];
// enable both major and minor versioning flags like this (if you want both major and minor versioning)
docList.EnableVersioning = true;
docList.EnableMinorVersions = true;

// and do not forget this
docList.Update();

3) Enabling Content Approval on documents : (in a later post here I would explain how elevated privileges would need to be used to use content approval correctly)

// get reference to the document library in which you want to enable content approval
// web is instance of SPWeb
SPList docList = web.Lists["Custom List"];
// enable content approval like this
docList.EnableModeration = true;
// and do not forget this
docList.Update();

4) Forcing Checkout of Document before edit :

// get reference to the document library in which you want to enable versioning
// web is instance of SPWeb
SPList docList = web.Lists["Custom List"];
// enable force checkout before edit like this
docList.ForceCheckout = true;
// and do not forget this
docList.Update();
 

VS2008 Workflow extensions for SharePoint 2007 and 64 bit Windows 2003 server July 10, 2009

( VS 2008 Workflow extensions for Sharepoint 2007 +  64 bit Windows 2003 server ) = MESS

Yes thats what I came to know today.

I have a VM installed that has 64 bit version of Win 2003 server installed, I got all my environment setup for the project on that VM. Now a few weeks into that project, when I am about to start the workflow bit on this SharePoint project, I find that the WF extensions installed on my machine are not working.

Error :

Object reference not set to an instance of an object.

As soon as I give the name of project (of Sharepoint 2007 Sequential Workflow ) and click OK. Boom. I see the error in dialog box. I click OK and go ahead but just not able to connect to my site in the next screen.

  1. I try by performing that same thing again, no, does not work.
  2. I restarted VS (its 2008), no, does not work.
  3. I restart the VM, no, does not work.

Only thing I can think of now, is probably my VS 2008 copy has gone corrupt.

So I try it out on my machine (not the VM, the one on which VM is installed), its Vista 32 bit box.

I see a different error. Do not remember the exact wording, it went something like this :

Error :

Cannot connect to a 64 bit Sharepoint server, please install 32 bit sharepoint
server.

I went through all articles I could go on internet to find the solution (if any).

Came across these information bits :

  • Its a known MS bug. Someone wrote in an article that it these sharepoint workflow extensions were never meant to be for 64 bit version, it was because some dev did not flipthe switch during the builds and the whole damn thing got release for 64 bit version too. So that means anyone trying to use them, would not be able to use them. (Do not waste any more time of yours in trying to use them on 64 bit, I have already wasted a lot today). A Bug has been raised and been close with remarks that it would be not fixed any time soon (as MS did not have enough resources to get that working!). So that means more wait till next release. More info here : http://social.msdn.microsoft.com/Forums/en-US/sharepointworkflow/thread/50bba408-608a-42be-9d67-c9f00dc89a03
  • Next option was to install Bamboo solution’s Helper file to get Sharepoint to run on Vista 32 bit box. I just did not want to do it, as this is some third party solution and not a standard one. If you think you would want to go ahead with it, try it out here http://community.bamboosolutions.com/media/p/193.aspx
  • Third option is to build a new Virtual Machine with everything 32 bit on it. I am planning to go ahead with this, as this seems least hassle of all.
 

Programmatically adding Custom folder content type and Custom document content type in SharePoint document library July 9, 2009

Filed under: SharePoint — prazjain @ 12:38 am
Tags: ,

Part 2 of the Two Part Article Series

In this post I would focus more on creating a custom document library and attaching the custom types to the list, Part 1 of the article explains creating those custom content types.

In this post I would be explaining how to add a custom type extending from Folder type in Sharepoint, to a custom list in my site.
And then I would be show how creation of that folder type can be allowed through the new button in menu, and hiding the other new buttons available there.like : Document, Folder

a) Create feature.xml and put it in 12 hive /TEMPLATE/FEATURES/MyFeature/feature.xml. Remember to change the GUID of feature below to a new GUID, and change the public key token of the assembly mentioned below in Feature tag to the token generated after signing your assembly.

<Feature Id="7a6dE9DB933A-DA89-4E86-A072-6CA8D24A68de"        
 Title="My Feature"        
 Description="Some description to go here"        
 Version="1.0.0.0"        
 Hidden="TRUE"        
 Scope="Web"        
 ReceiverAssembly="Geeks.SharePoint.SampleAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=78A0b8264297E9DB"
 ReceiverClass="Geeks.SharePoint.SampleAssembly.MyClass"        
 xmlns="http://schemas.microsoft.com/sharepoint/">
 <ElementManifests></ElementManifests>
</Feature>

b) Create a feature receiver event listener so that you can override the event callback and perform actions when the feature is being activated.

Create a project with name “Geeks.SharePoint.SampleAssembly”, and create a class MyClass in namespace “Geeks.SharePoint.SampleAssembly”. Ofcourse you would have to strong name this assembly and install it in GAC.

The code provided below is fairly well commented so I hope you would not have problem understanding it.


namespace Geeks.SharePoint.SampleAssembly
{
 public class MyClass : SPFeatureReceiver
 {
 public override void FeatureActivated(SPFeatureReceiverProperties properties)
 {
 //get the current web
 SPWeb web = null;
 web = (SPWeb)properties.Feature.Parent;

 //add the document library
 AddDocumentLibs(web);

 //add the content types for the custom document library just created
 AddContentType(web);
 }

 public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
 {
 //not needed
 }

 public override void FeatureInstalled(SPFeatureReceiverProperties properties)
 {
 //not needed
 }

 public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
 {
 //not needed
 }
 private void AddDocumentLibs(SPWeb web)
 {
 Guid customListGuid = web.Lists.Add("My Custom List", "My Custom List Description", SPListTemplateType.DocumentLibrary);

 web.Update();

 SPList custList = web.Lists[customListGuid];

 // show the list in quick launch
 custList.OnQuickLaunch = true;
 custList.Update();

 // set both properties to true if you want both major and minor versioning to be turned on.
 custList.EnableVersioning = true;
 custList.EnableMinorVersions = true;
 custList.Update();
 }

 private void AddContentType(SPWeb web)
 {
 // get the custom list to which we would register content types
 SPList docList = web.Lists["My Custom List"];

 SPContentType customFolderType = web.Site.RootWeb.ContentTypes["My Custom Folder Type"];

 SPContentType customDocType = web.Site.RootWeb.ContentTypes["My Custom Doc Type"];

 SPContentType DefaultType = docList.ContentTypes["Document"];

 // set this property to allow management of content types programmatically.
 docList.ContentTypesEnabled = true;

 // register content type customDocType as instance of SPContentType
 docList.ContentTypes.Add(customDocType);

 // register content type customFolderType as instance of SPContentType
 docList.ContentTypes.Add(customFolderType);</pre>
 docList.Update();

 // just to show how you can hide the new Folder option from your menu in document list
 HideContentTypes(docList.RootFolder, "Folder");

 // remove the default Document that new menu allows to create, as we want only our custom doc to be created from new menu in this custom list.
 docList.ContentTypes.Delete(DefaultType.Id);

 docList.Update();

 // create a custom folder type programmatically in the document library when this feature is activated
 CreateFolder(docList.RootFolder, "My First Folder", customFolderType.Id);

 }

 // Creates a new folder in the specified root folder with the content type that matches the Id passed as parameter
 private void CreateFolder(SPFolder rootFolder, string folderName, SPContentTypeId contentTypeId)
 {
 SPFolder newFolder = rootFolder.SubFolders.Add(folderName);
 newFolder.Item.SystemUpdate();
 newFolder.Update();
 newFolder.Item[SPBuiltInFieldId.ContentTypeId] = contentTypeId;
 newFolder.Item.SystemUpdate();
 }

 // This method goes through the content types available for creation in the root folder. Then it goes and hides the content type
 // that is passed as argument by just creating a new collection that holds SPContentType and assigning that collection back to
 // root folder's UniqueContentTypeOrder. This is a special property that also allows changing the order of content types that appear in
 // the new menu option.
 private void HideContentTypes(SPFolder rootFolder, string contentTypeName)
 {
 IList<SPContentType> contentTypeCollection = rootFolder.ContentTypeOrder;

 List<SPContentType> contentTypes = new List<SPContentType>();

 foreach (SPContentType contentType in contentTypeCollection)
 {
 if (contentType.Name.Equals(contentTypeName))
 continue;
 else
 contentTypes.Add(contentType);
 }

 rootFolder.UniqueContentTypeOrder = contentTypes;
 rootFolder.Update();
 }

Hope that helps.

Want to go back to Part 1 ?

 

 
Follow

Get every new post delivered to your Inbox.