| Subcribe via RSS

What to do if SharePoint throws “500 Internal Server Error”?

Mai 28th, 2009 | No Comments | Posted in SharePoint

Yesterday we added a new server to the SharePoint farm. When calling a site over that new server we got an 500 Internal Server Error. There was no entry in the EventLog and even no information in the server log files. We wer trying lots of things but nothing got better. So what could we do to get more information?

We changed the diagnostics logging level from Unexpected to Verbose:

Diagnostics Logging Level

(Central Administration > Operations > Diagnistic Logging)

Then we recalled the website and finally got an error in the log files:

"Cannot make a cache safe URL for "init.js", file not found.
Please verify that the file exists under the layouts directory."

The file is part of every language pack so in our case we did not install a language pack on the new server that is used by the site.

Although this is a simple option to get more error information it’s forgotten or overseen very often.

Alex
Tags: , , ,

SharePoint Groups vs. Active Directory Groups

Mai 19th, 2009 | 3 Comments | Posted in SharePoint

I’ve discussed this topic quite often during the last months. After those discussions I figured out that its more a question when to use what kind of group rather than what kind is better than the other. In this post I just write down some advantages and disadvantages of the group types and let you choose what kind fits better for your needs.

SharePoint Group Active Directory Group
plus Members of this group can be added/removed from within SharePoint. The permission to add or remove users from the group can be delegated to SharePoint users. plus Members of this group can be managed within Active Directory. Only Active Directory administrators have the permission to modify group memberships.
plus Members of this group can be visible to users. minus Members of this group are not visible to users.
minus Cannot contain another SharePoint group as member. plus Can contain another Active Directory Group.
plus Must have a unique name on site collection level. The name is the unique identifier of the group. minus Can cause serious problems in lage scale scenarios: A user might only be a member of 1024 Active Directory groups (recoursively). If this number is reached the user is no longer able to log on to Windows.
Read the Microsoft documentation for more information.
plus Can contain SharePoint users that do not exist in the Active Directory.
Tags: , , ,

Create SPFieldLookup programatically

Februar 21st, 2009 | 1 Comment | Posted in .NET, SharePoint

There aren’t many posts describing how to add a SPFieldLookup column to a list programatically. Here’s how I do it:

I have a list named “MyList” and I have a list named “MyLookupList”. I want to create a lookup column within “MyList” looking up values from “MyLookupList”.

SPList myList = web.Lists["MyList"];
SPList myLookupList = web.Lists["MyLookupList"];
 
myList.Fields.AddLookup("Lookup", myLookupList.ID, false);
SPFieldLookup fieldLookup = myList.Fields["Lookup"] as SPFieldLookup;
// Display Title Column in lookup field 
// (this is shown by default but this way you can change it)
fieldLookup.LookupField = 
  myLookupList.Fields[SPBuiltInFieldId.Title].InternalName;
fieldLookup.Update();
Tags:

Low budget SharePoint Services 3.0 Development Environment

Februar 9th, 2009 | No Comments | Posted in Uncategorized

I tried to set up a development environment for SharePoint programming at home on my machine and found out that this isn’t a trivial task if you don’t want to use evaluation software and do not want to invest more than 1000 $.

The big cost factors are operating system and devlopment environment:

  • Windows Web Server 2008
  • Visual Studio 2008 Standard
First I evaluated Windows Home Server but this edition would format all drives during setup. Not a good choice if you want to use multiple Windows Systems on the same machine.
I’m not sure if it’s possible to build SharePoint extensions with Visual Studio Express. As long as many Visual Studio extensions do not work with VS Express I’d prefer Visual Studio Standard (or higher).

All else needed for SharePoint Services development is available for free:
  • SharePoint Service 3.0
  • Visual Studio Extensions for SharePoint Services 1.2 or 1.3
  • Tools

Simplify reading and writing field values of type SPFieldUser, SPFieldUrl and SPFieldLookup using extension methods

Februar 8th, 2009 | 8 Comments | Posted in .NET, SharePoint

To read or write values to fields of type SPFieldUser or SPFielLookup is an annoying task because you have to create field value objects bevore you can write your values into a field. The following post descripes how you can encapsulate those tasks to extension methods and slim your code to make it more readable.

SPFieldUser

The following code displays a couple of extension methods extending the SPListItem class for simple reading and writing access to fields of type SPFieldUser.

using System;
using System.Collections.Generic;
using Microsoft.SharePoint;
 
namespace ExtensionMethods
{
    public static class SPListItemExtensions
    {
        /// <summary>
        /// Returns the login name of an User-Field.
        /// </summary>
        public static string GetFieldValueUserLogin(this SPListItem item, 
          string fieldName)
        {
            if (item != null)
            {
                SPFieldUserValue userValue = 
                  new SPFieldUserValue(
                    item.Web, item[fieldName] as string);
                return userValue.User.LoginName;
            }
            else
            {
                return string.Empty;
            }
        }
 
        /// <summary>
        /// Sets the value of a User-Field to a login name.
        /// </summary>
        public static void SetFieldValueUser(this SPListItem item, 
          string fieldName, string loginName)
        {
            if (item != null)
            {
                item[fieldName] = item.Web.EnsureUser(loginName);
            }
        }
 
        /// <summary>
        /// Sets the value of a User-Field to an SPPrincipal 
        /// (SPGroup or SPUser).
        /// </summary>
        public static void SetFieldValueUser(this SPListItem item, 
          string fieldName, SPPrincipal principal)
        {
            if (item != null)
            {
                item[fieldName] = principal;
            }
        }
 
        public static void SetFieldValueUser(this SPListItem item, 
          string fieldName, IEnumerable<SPPrincipal> principals)
        {
            if (item != null)
            {
                SPFieldUserValueCollection fieldValues = 
                  new SPFieldUserValueCollection();
 
                foreach (SPPrincipal principal in principals)
                {
                    fieldValues.Add(
                      new SPFieldUserValue(
                        item.Web, principal.ID, principal.Name));
                }
                item[fieldName] = fieldValues;
            }
        }
 
        /// <summary>
        /// Sets the value of a multivalue User-Field to 
        /// a list of user names.
        /// </summary>
        public static void SetFieldValueUser(this SPListItem item, 
          string fieldName, IEnumerable<string> loginNames)
        {
            if (item != null)
            {
                SPFieldUserValueCollection fieldValues = 
                  new SPFieldUserValueCollection();
 
                foreach (string loginName in loginNames)
                {
                    SPUser user = item.Web.EnsureUser(loginName);
                    fieldValues.Add(
                      new SPFieldUserValue(
                        item.Web, user.ID, user.Name));
                }
 
                item[fieldName] = fieldValues;
            }
        }
    }
}

The following lines of code demonstrate how these extension methods can be used.

using ExtensionMethods;
 
SPList list = web.Lists["MyList"];
SPListItem item = list.Items[0];
 
// Get a user login of a SPFieldUser field
string userLogin = item.GetFieldValueUserLogin("Author");
 
// Set a SPFieldUser field to a user using the login
item.SetFieldValueUser("Person", "alexander.bruett");
 
// Set a SPFieldUser field that allows multiple values to a list of users
string[] persons = { "alexander.bruett", "paul.panzer" };
item.SetFieldValueUser("Mehrere Personen", persons);
 
// Set a SPFieldUser field to a SPUser
SPUser user = web.Users[1];
item.SetFieldValueUser("Person", user);
 
// Set a SPFieldUser field to a list of SPPrincipal
SPGroup group = web.Groups[0];
SPUser user = web.Users[0];
SPUser user2 = web.EnsureUser("alexander.bruett");
SPUser user3 = web.EnsureUser("Domain Users"); ;
SPPrincipal[] principals = { group, user, user2, user3 };
item.SetFieldValueUser("AssignedTo", principals);

Especially setting fields that allow multiple values now needs only a couple of code lines.

SPFieldLookup

The SPFieldLookup field is a little bit more complicated if you want to set the field only by the lookup string. You have to query the ID of the lookup value before you can set the field value. The following extension methods simplify this task.

/// <summary>
/// Returns the value of a Lookup Field.
/// </summary>
private static string GetFieldValueLookup(this SPListItem item, 
    string fieldName)
{
    if (item != null)
    {
        SPFieldLookupValue lookupValue = 
            new SPFieldLookupValue(item[fieldName] as string);
        return lookupValue.LookupValue;
    }
    else
    {
        return string.Empty;
    }
}
 
/// <summary>
/// Returns the value of a Lookup-Field with multiple values.
/// </summary>
public static IEnumerable<string> GetFieldValueLookupCollection(
    this SPListItem item, string fieldName)
{
    List<string> result = new List<string>();
    if (item != null)
    {
        SPFieldLookupValueCollection values = 
            item[fieldName] as SPFieldLookupValueCollection;
 
        foreach (SPFieldLookupValue value in values)
        {
            result.Add(value.LookupValue);
        }
    }
    return result;
}
 
/// <summary>
/// Returns the SPFieldLookupValue instance of a lookup value. 
/// The ID value will be obtained using SPQuery.
/// </summary>
private static SPFieldLookupValue GetLookupValue(
    SPWeb web, SPFieldLookup field, string lookupValue)
{
    string queryFormat = 
        @"<Where>
            <Eq>
                <FieldRef Name='{0}' />
                <Value Type='Text'>{1}</Value>
            </Eq>
          </Where>";
 
    string queryText = 
        string.Format(queryFormat, field.LookupField, lookupValue);
    SPList lookupList = web.Lists[new Guid(field.LookupList)];
 
    SPListItemCollection lookupItems = 
        lookupList.GetItems(new SPQuery() { Query = queryText });
 
    if (lookupItems.Count > 0)
    {
        int lookupId = 
            Convert.ToInt32(lookupItems[0][SPBuiltInFieldId.ID]);
 
        return new SPFieldLookupValue(lookupId, lookupValue);
    }
    else
    {
        return null;
    }
}
 
/// <summary>
/// Sets the value of a Lookup-Field.
/// </summary>
public static void SetFieldValueLookup(
    this SPListItem item, string fieldName, string lookupValue)
{
    if (item != null)
    {
        SPFieldLookup field = 
            item.Fields.GetField(fieldName) as SPFieldLookup;
        item[fieldName] = GetLookupValue(item.Web, field, lookupValue);
    }
    else
    {
        item[fieldName] = null;
    }
}
 
/// <summary>
/// Set the values of a Lookup-Field with multiple values allowed.
/// </summary>
public static void SetFieldValueLookup(this SPListItem item, 
    string fieldName, IEnumerable<string> lookupValues)
{
    if (item != null)
    {
        SPFieldLookup field = 
            item.Fields.GetField(fieldName) as SPFieldLookup;
 
        SPFieldLookupValueCollection fieldValues = 
            new SPFieldLookupValueCollection();
 
        foreach (string lookupValue in lookupValues)
        {
            fieldValues.Add(
                GetLookupValue(item.Web, field, lookupValue));
        }
        item[fieldName] = fieldValues;
    }
}

That’s been a lot of code but take a look at the following lines of code. Especially the setting task becomes very easy now.

// Reads the value of a SPFieldLookup field
string value = item.GetFieldValueLookup("LookupFieldName");
 
// Read lookup values of a SPFieldLookup field 
// with multiple values allowed
IEnumerable<string> values = 
  item.GetFieldValueLookupCollection("MultiLookupFieldName");
 
// Set the value of a SPFieldLookup field
item.SetFieldValueLookup("LookupFieldName", "LookupValue");
 
//Set the value of a SPFieldLookup field with multiple values allowed
string[] values = {"Hamburg", "London" };
item.SetFieldValueLookup("MultiLookupFieldName", values);

SPFieldUrl

Setting and getting SPFieldUrl field values is very simple:

/// <summary>
/// Returns the value of an Url-Field.
/// </summary>
public static string GetFieldValueUrl(
    this SPListItem item, string fieldName)
{
    if (item != null)
    {
        SPFieldUrlValue urlValue = 
            new SPFieldUrlValue(item[fieldName] as string);
        return urlValue.Url;
    }
    else
    {
        return string.Empty;
    }
}
 
/// <summary>
/// Sets the value of an URL-Field.
/// </summary>
public static void SetFieldValueUrl(this SPListItem item, 
    string fieldName, string url, string description)
{
    if (item != null)
    {
        item[fieldName] = 
            new SPFieldUrlValue() 
                { 
                    Description = description, 
                    Url = url 
                };
    }
}

The following lines of code show how to get and set values of the SPFieldUrl field using the extension methods above:

// Get the url of a SPFieldUrl field
string url = item.GetFieldValueUrl("URL");
 
// Set the url and description of a SPFieldUrl field
item.SetFieldValueUrl("URL", "http://www.alexbruett.net", "Alex' Blog");

Conclusion

Using extension methods to simplify the access to complex and SharePoint list fields makes your code more readable and reduces the lines of code.

You could also use helper classes instead of extension methods but you will have to write more code to call a helper method instead of calling a extension method.

Tags: , , , , , , ,

Query whole site collection using SPSiteDataQuery

Februar 6th, 2009 | 1 Comment | Posted in .NET, SharePoint

Using the SPQuery class you can query one SharePoint list for items.

To set up a query across a whole site collection you can use the SPSiteDataQuery object.

The following method fetches all .doc files from all doclibs of the site collection and prints out a list of urls to those items.

public void TestSiteDataQuery()
{
  using (SPSite site = new SPSite("http://localhost"))
  {
    using (SPWeb web = site.OpenWeb("/"))
    {
      SPSiteDataQuery query = new SPSiteDataQuery();
 
      // Search in doclibs only
      query.Lists = "<Lists BaseType='1' />";
 
      // Only .doc files
      query.Query =
      @"<Where>
          <Eq>
            <FieldRef Name='DocIcon' />
            <Value Type='Computed'>doc</Value>
          </Eq>
        </Where>";
 
      // Select only needed columns: file reference
      query.ViewFields = "<FieldRef Name='FileRef' />";
 
      // Search in all webs of the site collection
      query.Webs = "<Webs Scope='SiteCollection' />";
 
      // Perform the query
      DataTable table = web.GetSiteData(query);
 
      // Generate an absolute url for each document
      foreach (DataRow row in table.Rows)
      {
        string relativeUrl = 
          row["FileRef"].ToString().Substring(
            row["FileRef"].ToString().IndexOf("#") + 1);
        string fullUrl = site.MakeFullUrl(relativeUrl);
 
        // Write urls to console
        Console.WriteLine(fullUrl);
      }
    }
  }
}
Tags: , , ,

Silverlight Host Web Part

Oktober 4th, 2008 | No Comments | Posted in SharePoint, Silverlight

Description
Lot’s of people start developing Silverlight applications these days and I asked myself: Why not host these applications in SharePoint?

I developed a very simple web parts that loads a silverlight application file (.xap) stored within a document library and runs it within its viewspace.

Requirements
Before you can deploy and use the web part in your SharePoint farm you have to complete the following tasks:

  • 1. Install Silverlight 2 Beta 2 on the server.
  • 2. Copy the file System.Web.Silverlight.dll to the GAC (The file is usually located in C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Server)
  • 3. Make neccessary adjustments to the web.config. This step is quite complex so I recommend you watch this screencast from Patrick Tisseghem and follow it step by step.
  • 4. Add this MIME type mapping to the IIS Website: .xap -> application/x-silverlight-2-b2

If you want to recompile the source code of this web part you have to install Visual Studio 2008 and the Silverlight Tools for VS 2008 and the Visual Studio Extensions for SharePoint 1.2.

Download, Deploy and Use

  • 1. Download the wsp file (setup.bat included) and install the web part.
  • 2. Download this sample .xap file that you can show within your silverlight web part.
  • 3. Ensure that the feature “Silverlight 2 Beta 2 Host Web Part Feature” is activated within the site collection.
  • 4. Upload the .xap file into a document library.
  • 5. Edit a page and add the web part “Silverlight 2 Beta 2 Host Web Part” to a web part zone.

  • 6. Now edit the web part properties and set the property “XAP URL” with the url to the xap file.

  • 7. Exit edit mode and refresh the page. The web part should show the silverlight application now.

Source Code

You can download the web part project here.

The first step is to ensure that a script manager exists on the page.

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    ScriptManager sm = ScriptManager.GetCurrent(this.Page);
    if (sm == null)
    {
        sm = new ScriptManager();
        Controls.AddAt(0, sm);
    }
}

The second step is to provide a web part property to consume a URL to the .xap file.

[WebBrowsable(true)]
[Personalizable(PersonalizationScope.Shared), Category("Silverlight")]
[FriendlyName("XAP URL")]
[Description("URL to XAP file")]
public string XapUrl
{
    get { return m_xapUrl; }
    set { m_xapUrl = value; }
}
protected string m_xapUrl = string.Empty;

The third step is to create a Silverlight control and load the Silverlight application into it:

protected override void CreateChildControls()
{
    base.CreateChildControls();
 
    try
    {
        if (!string.IsNullOrEmpty(XapUrl))
        {
            Silverlight ctrl = new Silverlight();
            ctrl.ID = this.ID + "_" + "SilverLightControl";
            ctrl.Source = XapUrl;
            ctrl.Width = new Unit(100, UnitType.Percentage);
            ctrl.Height = new Unit(m_controlHeight, 250);
            Controls.Add(ctrl);
        }
        else
        {
            Label lbl = new Label();
            lbl.Text = "Provide the url to a .xap file.";
            lbl.ID = this.ID + "_" + "LblMessage";
            Controls.Add(lbl);
        }
    }
    catch (Exception ex)
    {
        Label lbl = new Label();
        lbl.Text = string.Format("Error: " + ex.Message);
        lbl.ID = this.ID + "_" + "LblMessage";
        Controls.Add(lbl);                
    }
}

Have a nice Silverlight day,
Alex

Tags: , ,

Best practices: SharePoint Timer Jobs

August 18th, 2008 | 4 Comments | Posted in SharePoint

Part 1: Experiences and bad practices

I’ve developed a couple of SharePoint Timer jobs during the last months and gained a lot of experiences with deploying, configuring, debugging and updating them. Adding your code directly into a SharePoint timer job has many disadvantages:

Configuration

Timer jobs are run from the SharePoint Timer Service also known as owstimer.exe. You can place a owstimer.exe.config and add your configuration settings here. It serves as a shared app.config for all timer jobs. If you modify the configuration file you have to restart the timer job service to let the changes take effekt.

The config file is not designed to be used for timer job configuration so it doesn’t exist after you installed SharePoint. You have to create it on your own.

There are some alternative possibilities like custom defined xml files stored in a file at a defined location on the hard disc. Andrew Connel describes some ways to configure SharePoint timer jobs at MSDN. In my opinion none of them are simple to implement and nice to handle like a web.config or app.config.

Degbugging

To debug a timer job you have to attach the debugger to the owstimer.exe process, set breakpoint and wait until your timer job is executed. This could take minutes for each debugging session.

Deployment and redeployment

The deployment of a timer job is easy stuff if you have a visual studio template doing the dirty work for you. However, redeploying a solution means that you have to restart the SharePoint Timer Service and IIS what means session reset to all connected users.

Part 2: Extract code from timer job to web service

A default timer job is implemented like this:

Your code is inside the timer job and accessing your SharPoint using the API.

If you are going to write many lines of code your timer job you have a huge potential for error and you will need a couple of debugging sessions to fix all errors. Since debugging timer jobs take much time this doesn’t seem to be the best way implementing timer jobs.

Here’s my idea: Extract your code from the timer job into a web service.

In this scenaro your timer job has just those lines of code needed to call the web service. The configuration parameters of this timer job is quite short - just the URL of your webservice. On single server farms you could reference your web service using “localhost:portxy” and do not need a configuration for this. If you need a configuration option you could chose on of the alternatives described by Andrew Connell at MSDN.

Using a web service has some big advantages:

Configuration file

You can deploy a web.config file and put as many configurations in it as you wish and do not have to worry about affecting other job configurations. If you modify the web.config file you do not have to restart any services or the IIS.

Deployment

You can deploy and redeploy your web service without having to restart the SharePoint timer service or IIS.

Testing

You can write your own test client to call your web service whenever you want. You don’t have to wait until the timer service runs your code.

External job engine

If you have an external job engine like Control-M you can forego timer jobs and let the external job engine call your web servce. In this case you do not need the owstimer.exe.config anymore.

Security considerations

A web service accessing the SharePoint with administration rights adds a point of attack. Only the persons or service accounts that use the web service should be allowed to call the service.

Tags:

Reorder SharePoint list fields from code

Juli 19th, 2008 | 1 Comment | Posted in SharePoint

There’s no function in the SharePoint API to reorder the fields of a SharePoint list. You have to make use of the ProcessBatchDate from the SPWeb class.

/// <summary>
/// This function reorders the fields in the specified list 
/// programmatically as specified by the firstFields parameter.
/// The fields not included in the firstFields list will be
/// added to the list.
/// </summary>
/// <param name="list">The SPList object to update</param>
/// <param name="firstFields">A generic list of SPField containing the
/// first fields in the order to be displayed.</param>
public void ReorderField(SPList list, List firstFields)
{
  List<SPField> fields = new List<SPField>();
 
  for (int i=0; i<firstFields.Count; i++)
  {
    fields.Add(firstFields[i]);
  }
 
  foreach (SPField field in list.Fields)
  {
    if (!fields.Contains(field))
    {
      fields.Add(field);
    }
  }
 
  StringBuilder sb = new StringBuilder();
 
  XmlTextWriter xmlWriter = new XmlTextWriter(new StringWriter(sb));
  xmlWriter.Formatting = Formatting.Indented;
 
  xmlWriter.WriteStartElement("Fields");
 
  for (int i=0; i&lt; fields.Count; i++)
  {
    xmlWriter.WriteStartElement("Field");
    xmlWriter.WriteAttributeString("Name", fields[i].InternalName);
    xmlWriter.WriteEndElement();
  }
 
  xmlWriter.WriteEndElement();
  xmlWriter.Flush();
 
  using (SPWeb web = list.ParentWeb)
  {
    ReorderFields(web, list, sb.ToString());
  }
}
 
/// <summary>  
/// This function reorders the fields in the specified list 
/// programmatically as specified by the xmlFieldsOrdered parameter  
/// </summary>  
/// <param name="web">The SPWeb object containing the list</param>
/// <param name="list">This function reorders the fields in the 
/// specified list</param>
/// <param name="xmlFieldsOrdered">A string in XML-format specifying the 
/// field order by the location within a xml-tree</param> 
private void ReorderFields(SPWeb web, SPList list,
  string xmlFieldsOrdered)
{
  try
  {
    string fpRPCMethod = @"<?xml version=""1.0"" encoding=""UTF-8""?>  
	<Method ID=""0,REORDERFIELDS"">  
	<SetList Scope=""Request"">{0}</SetList>  
	<SetVar Name=""Cmd"">REORDERFIELDS</SetVar>  
	<SetVar Name=""ReorderedFields"">{1}</SetVar>  
	<SetVar Name=""owshiddenversion"">{2}</SetVar>  
	</Method>";
 
      // relookup list version in order to be able to update it
      list = web.Lists[list.ID];
 
      int currentVersion = list.Version;
 
      string version = currentVersion.ToString();
      string RpcCall = string.Format(fpRPCMethod, list.ID,
        SPHttpUtility.HtmlEncode(xmlFieldsOrdered), version);
 
      web.AllowUnsafeUpdates = true;
      web.ProcessBatchData(RpcCall);
    }
    catch (System.Net.WebException err)
    {
      // TODO: Log this exception according your exception
      // handling policies.
      Console.WriteLine("WARNING:" + err.Message);
    }
}
Tags: , ,

Getting started with custom SharePoint Event Receivers

Juli 18th, 2008 | 6 Comments | Posted in SharePoint

In this post I describe how to create a custom SharePoint Event Receiver using the Visual Studio Extensions for SharePoint Services.

Software Requirements

Windows Server 2003/2008
SharePoint Services 3.0/Server 2007
Visual Studio 2005/2008
Visual Studio Extensions für SharePoint Services

Download Visual Studio Extensions

Visual Studio 2005: Download page
Visual Studio 2008: Download page
Preconfigured Virtual Machine: Download page

Description
We will develop an event receiver that prevents documents from being deleted by cancelling all delete operations.

Step 1: Set up visual studio project
Open Visual Studio and create an „Empty“ SharePoint Project.

Now add a new item to the project called Event Receiver (Project -> Add new Item -> Event Receiver)

Pick document library.

Now your project should look like this:

Weg got a strong name key to sign the assembly that will be deployed to the global assembly cache (GAC), some SharePoint references and two code files and xml files.

The ItemEventReceiver class contains all events that will be fired when something happens to list items like adding, updating or deleting of files.
The ListEventReceiver class contains all events that will be fired when something happens to the list itself like adding or removing columns

Step 2: Add event code

We want to cancel delete operations so we have to implement an event in the ItemEventReceiver class. Open it and uncomment the ItemDeleting event.

///
/// Synchronous before event that occurs before an existing item 
/// is completely deleted.
///
/// A Microsoft.SharePoint.SPItemEventProperties object that 
/// represents properties of the event handler.
public override void ItemDeleting(SPItemEventProperties properties)
{
  properties.Cancel = true;
  properties.ErrorMessage = "Deleting of Items not allowed!";
}

Build the project to check for errors, there shouldn’t be any.

Step 3: Deploy

Visual Studio can deploy the event receiver for you. Just select Build –> Deploy from the menu.

What happen’s:
Visual Studio creates one feature for each of your event receivers and uses the SharePoint console to check for errors. If there are no errors visual studio creates a deployable solution package (wsp) and a setup.bat script that can be used to deploy and undeploy the solution. Finally visual studio executes the setup.bat.

Your event receiver is now deployed and activated on the root web site, not on sub sites.
If you want to test te event receiver on a sub site you have to activate it first: Navigate to Site Settings -> Site Feature and activate the feature MyEventReceiverItemEventReceiver.
(I’ll show how to change the receiver name later in this article.)

Step 4: Test

Pick an existing document library on the site or create a new one. Upload a document and try to delete it. You should receive a message like this:

If you deactivate the feature MyEventReceiverItemEventReceiver you will be able to delete the file.

Step 5: Feature description

To give the feature a more meaningful description we can modify the feature.xml. To find this file make invisible files visible to your solution explorer:

Discover the pkg folder and open the file MyEventReceiverItemEventReceiver\feature.xml

You can modify the Title attribute and add a Description attribute:

<?xml version="1.0" encoding="utf-8"?>
<Feature 
  Id="ddeefbb1-cff7-4198-9170-f1a477168e5e" 
  Title="File keeper" 
  Description="event receiver that prevents doclib files 
    from being deleted." 
  Scope="Web" 
  Version="1.0.0.0" 
  Hidden="FALSE" 
  DefaultResourceFile="core" 
  xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest 
      Location="MyEventReceiverItemEventReceiver\ItemEventReceiver.xml" />
  </ElementManifests>
</Feature>

Now rebuild and deploy your project. In your SharePoint site navigate to Site Settings -> Site features. Here you see the new description so your SharePoint administrators can understand what’s this feature is about.

The event receiver is attached to all document libraries of the site where the feature is activated.
If you want to attach the Event Receiver to one single list you have to code this with a custom
Feature Event Receiver or use this tool:

EventReceiver-Installer for SharePoint

Have fun,
Alex

Tags: ,