SharePoint Posts

  • Andreas Finne (gravatar)

    When developing using the SharePoint object model, certain operations has to be run with elevated privileges. For instance, setting information into the property bag of a site needs to be done with elevated privileges. This post shows how this is done, and also includes a helper method for making it easier and shorter to do this.

    There is a method called SPSecurity.RunWithElevatedPrivileges in the object model that takes a delegate as argument. The delegate is then run with more permissions. An important note regarding this: You must not use old references to SPWeb or SPSite instances inside your delegate, since they are “contaminated” with lower permissions. What you have to do is to create new SPSite and SPWeb instances using the Ids of the sites and webs you already have a reference to, like this:

    SPSecurity.RunWithElevatedPrivileges(delegate() 
    { 
        using (SPSite site = new SPSite(SPContext.Current.Site.ID)) 
        { 
            using (SPWeb elevatedWeb = site.OpenWeb(webId)) 
            { 
                //Your code here 
            } 
        } 
    });

    If you need to do these kind of operations in several locations, this is a lot of repeating code to include. A colleague of mine made a small helper method that does the setup for elevated privileges for you. Here’s the short code snippet:

    private delegate void CodeToRunElevated(SPWeb elevatedWeb);
    private static void RunElevated(Guid webId, CodeToRunElevated secureCode)
    {
        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
            using (SPSite site = new SPSite(SPContext.Current.Site.ID))
            {
                using (SPWeb elevatedWeb = site.OpenWeb(webId))
                {
                   secureCode(elevatedWeb);
                }
            }
        });
    }

    To invoke this, you call on the method with the Id of your web as one argument, and a delegate taking an SPWeb as the other. When the method is invoked, the SPWeb argument will be “uncontaminated” and pushed to elevated privileges. This example sets the name, title, and description of a site.

    RunElevated(web.ID, delegate(SPWeb elevatedWeb)
    {
        elevatedWeb.Name = siteTitle;
        elevatedWeb.Title = siteTitle;
        elevatedWeb.Description = siteDescription;
        elevatedWeb.Update();
    });
  • Andreas Finne (gravatar)

    In a previous SharePoint project, I implemented a few event handlers that get called whenever an item in a list gets changed (or added/deleted). There were a few problems along the way that took a bit of searching before I found solutions. This post documents a few of the things that are good to know.

    The event handlers that I implemented where all of the synchronous ItemUpdating kind. There are also asynchronous events, like for instance ItemUpdated, but for the scenario at hand, I wanted to have some special handling depending on certain values of an item.

    The event handler is of the form
    public override void ItemUpdating(SPItemEventProperties properties).

    The properties object contains the collections BeforeProperties and AfterProperties that helps you to find out what has changed for your item,  i.e. compare
    properties.BeforeProperties[<field name>].ToString() to
    properties.AfterProperties[<field name>].ToString(). You can also modify the value of the fields in AfterProperties to affect what is actually stored in the list.

    Here comes the first gotcha; the BeforeProperties are only populated for document libraries. If you have an event handler on a custom list, the BeforeProperties collection is empty. So in order to check the current value of a field, you have to use properties.ListItem[<field name>] instead.

    Second gotcha; be aware of the internal name of fields. They have a sneaky behaviour in this case. As usual you should use the internal name of the field instead of the display name when accessing the field, i.e. “Evaluation_x0020_Date” instead of “Evaluation Date”. However, you can do properties.ListItem[“Evaluation Date”] to get the current value of the field, BUT to get the new value from AfterProperties, you have to do properties.AfterProperties[“Evaluation_x0020_Date”]. Here comes the even sneakier behaviour; if you try to do properties.AfterProperties[“Evaluation Date”], it doesn’t fail, but returns null. Usually, if you try to access a field that does not exist, an exception is thrown. However, “Evaluation Date” passes as a valid field name, but the value cannot be found.

    The third problem I had was related to date fields. In my event handler, I wanted to make sure that a certain date field remained unchanged if some other condition was met. The easiest solution would of course be
    properties.AfterProperties[“Decision Date”] = properties.BeforeProperties[“Decision Date”]
    As already stated in the first gotcha, the BeforeProperties is not populated for lists, so the statement was revised to
    properties.AfterProperties[“Decision Date”] = properties.ListItem[“Decision Date”]

    Now, this is still too simple, if you read the gotcha number two, you know that we have to use the internal name. Taking this into account, the third revision of the line is
    properties.AfterProperties[“Decision_x0020_Date”] = properties.ListItem[“Decision_x0020_Date”]

    This still doesn’t work. Even though all the properties are strings, and it is the same field, the formats of the strings are somehow still incompatible. The data type in this case was a datetime. It turns out that the format that comes out of the ListItem is in the format the SharePoint site uses, while the AfterProperties need the date in ISO8601 format. The fourth and final iteration (that actually works) of the line ended up as
    properties.AfterProperties[“Decision_x0020_Date”] = Microsoft.SharePoint.Utilities.SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Parse(properties.ListItem[“Decision_x0020_Date”].ToString()))

    Short and concise.

  • Andreas&#32;Finne (gravatar)

    When creating custom webparts or other custom features in SharePoint that retrieves data from a SQL server, there are a few gotchas and pitfalls that you should be aware of. I spent a few hours battling a couple of problems, so I thought I’d document the things I found out here to spread the knowledge.

    I was creating a custom webpart that retrieved data from a SQL server. On my development machine, I used a test SQL user with username and password directly in the connection string in the web.config and things worked as they should. The problems started when I deployed my solution to the test server.

    On the test server, I changed the connection string to use integrated security instead, and when trying to access the page, I was greeted with an error message stating that I was not allowed to have a connection string with the words Integrated Security in it. This happens since SharePoint adds a tag mapping that replaces the normal SqlClient assembly with SharePoint’s own. Apparently that SqlClient does not support integrated security. So, what I had to do was to add the following line to my web.config in the tagMappings-section:

    <remove tagType=”System.Web.UI.WebControls.SqlDataSource, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a” />

    After I added this line, things worked ok when I tried the page on the test server. However, when I asked a colleague to try out the page from his machine, another issue arose. He was greeted with the message: Login failed for user ‘NT AUTHORITY\ANONYMOUS LOGON’. This happens since SharePoint by default tries to impersonate the user browsing the site, so when using integrated security (if your environment is set up for impersonation multi-hop) SharePoint actually connects to the database as you. If impersonation-forwarding is not supported, you are instead greeted with the previously mentioned login failed message.

    What I actually wanted to do in my webpart was to connect to the database as the SharePoint service user, not using impersonation. To fix this, I created two helper methods that turns off impersonation temporarily.

    private WindowsImpersonationContext ctx = null;
    
    public void UseAppPoolIdentity()
    {
      try
      {
        if (!WindowsIdentity.GetCurrent().IsSystem)
        {
          ctx = WindowsIdentity.Impersonate(System.IntPtr.Zero);
        }
      } catch { }
    }
    
    public void ReturnToImpersonatingCurrentUser()
    {
      try
      {
        if (ctx != null) { ctx.Undo(); }
      }
      catch { }
    }
     

    These calls are then placed around the code that should be run as the service user, for instance when retrieving information from the SQL database, like this:

    UseAppPoolIdentity();
    
    // Code to retrieve data
    
    ReturnToImpersonatingCurrentUser();
  • Andreas&#32;Finne (gravatar)

    When declaring a feature, you can add an <ActivationDependencies> section to your feature.xml file. Here you can list the features your own feature is depending on.

    According to the documentation on activation dependencies (http://msdn.microsoft.com/en-us/library/aa543162.aspx), the depending features get automatically activated if they are in the same scope: “If a Feature is dependent on another Feature at the same scope, and the second Feature is not activated when the first one is activated, Windows SharePoint Services activates the second Feature

    This is incorrect!

    The second feature is activated automatically only if it is hidden!

  • Mikael&#32;Riska (gravatar)

    I was recently contacted by one our customers with a request to change some permissions on some SharePoint sites. In normal cases this is something you can easily do through the user interface, only in this case they wanted to change the permissions on some 400 sites. And as you can probably imagine the permissions inheritance chain was broken or this would have been a trivial task. It is easy enough to programmatically loop through a bunch of SharePoint sites and set permissions but it felt overkill to fire up Visual Studio and create a throw-away console application that did this.

    No, this sounded to me like the kind of scripting tasks I remember from my old days on the IT admin side of the fence. I remember being blown away by the scripting capabilities provided by PowerShell when it was first introduced, but other than a few small tests on my own computer I never really had a good reason to use PowerShell in Real Life™ – until today, that is. I also remember reading about the possibility to use any .NET API available, surely this should be possible to pull of with a combination of PowerShell and the SharePoint API?

    Sure enough, a quick Google gave me a link to http://blogs.flexnetconsult.co.uk/colinbyrne/PermaLink,guid,1665700b-e0de-4b8a-bb1c-014d6fbcf2db.aspx where Colin Byrne explained how to import the SharePoint dll into PowerShell. That was all the help I needed since I already knew the SharePoint API and how to do what I wanted once I got at the correct objects. I immediately installed Windows Management Framework Core which contains PowerShell 2.0 and tested it out on my development server.

    [System.Reflection.Assembly]::Load(“Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”)
    $spsite=[Microsoft.SharePoint.SPSite](“http://localhost/demo“)
    $rootweb = $spsite.RootWeb
    $group = $rootweb.EnsureUser("ECTEST\domain users")
    $role = $rootweb.RoleDefinitions["Contribute"]
    
    foreach($web in $rootweb.Webs)
    {
        $assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($group)
        $assignment.RoleDefinitionBindings.Add($role)
        $web.RoleAssignments.Add($assignment);
        
        $web.dispose()
    }

    Since I knew the SharePoint API reasonably well it was an easy task to throw together this script and test it out. After some head scratching I was able to pinpoint a couple of typos in my script and it finally worked as planned. Since PowerShell is not compiled it is easy to introduce typos and difficult to find them. All in all, the task was finished in about the same time it took me to write this blog post, and I know I will certainly revisit PowerShell for similar jobs in the future.

  • Andreas&#32;Finne (gravatar)

    One of our SharePoint applications uses the property bag for storing information. During development and testing, we needed some easy way to set and change the information in the property bag. That is why this tool was created.

    It is a simple console application that should be run on the SharePoint server as a user with adequate rights. It is invoked as follows:

    PropertyBagAdmin <url> <property> <value>

    Where <url> is the URL of the site, <property> is the name of the property in the property bag, <value> is the value to set the property to.

    Note! If the property does not exist, it is created.

    Use this tool at your own risk. Don’t use it if you are not sure what the property bag is or why you would like to set values in it. We have used it a lot ourselves, and have not run into problems with it. In short: don’t blame us if you break your SharePoint server using this tool.

    Download PropertyBagAdmin.zip