Wednesday, February 15, 2012

Best practice to impersonate users for SharePoint API calls

We have a requirement to develop a event receiver to copy list items into another list. During the design, I’ve asked developer not only copy the list items, but also keep the created by and modified by same as source list so that the audit history will reflect the active users.

The way to implement is to impersonate the call. Here is simple example.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // Impersonate
            string siteStr = "http://sbx08/sites/Harry/";

            using (SPSite site = new SPSite(siteStr))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    // Impersonate to user “an/harryc”
                    SPUserToken utoken = web.AllUsers["na/harryc"].UserToken;

                // Open site as that user
                    using (SPSite newSite = new SPSite(siteStr, utoken))
                    {
                        using (SPWeb newWeb = newSite.OpenWeb())
                        {
                            Console.WriteLine(newWeb.CurrentUser.ToString());

                            // Impersonate code to interact with SharePoint
                        }
                    }

                }
            }
        }
    }
}


If users do not have the permission on the destination list, we have to use run with elevated privileges.

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


All these two are very common and I just want to keep the sample in my blog to train my new developers.

Thursday, February 9, 2012

Is there any way we could do only block the Web services calls for external programs but open to WebDav and SharePoint Designer on site level?

We have a production site with out of office calendar list. One designer workflow will send email notification for out of office approval if any calendar item created or modified. Today, all calendar items were updated at same time that trigger old pending workflows been send out to many people!

We looked at the logs and found all those entries are updated by one user name. The log also shows the is should caused by the web service call.

02/09/2012 11:46:29.07 w3wp.exe (0x3A90)                       0x111C  SharePoint Foundation                 Logging Correlation Data                      xmnv     Medium               Name=Request (POST:http://serverURL:80/ourSIte/_vti_bin/lists.asmx)                2ae0784f-bd97-4098-9e15-d147a8b11ed4

Since any SharePoint users who have the permission could use web service calls and hit SharePoint server programmatically, we are concerned of DOA (Deny Of Access) and performance. We would like to have the capability to disable the SharePoint web service on the site level for external users besides SharePoint internal calls.

I did some search and found two possible ways that might block the web services on WebApp level. However, one option will block any of the web services,  SOAP, WebDav, SharePoint Designer. Another option will require Front server and TMG.

Is there any way we could do only block the Web services calls for external programs but open to WebDav and SharePoint Designer on site level?

I tried to access the admin web service (http://servername/SIteURL/_vti_admin/admin.asmx) and got the following error.  Is there anything we could do similar to other web services through global security group access?


Thanks.

Wednesday, February 8, 2012

LINQ could not access SharePoint 2010 managed metadate list columns

LINQ is a very simple and powerful way of querying data in the .Net programming language as we mentioned before. It allows the .Net language to query data natively against strongly typed classes. Yes against class with objects through DataContext generated by SPMetal.exe tool. SharePoint will convert the LINQ to CAML during the runtime. As a result, LINQ is an easy way comparing to older CAML to work with list data. Here is example to use LINQ to SharePoint 2010 with tricks and limitations.

1. Create a list named “City” with three columns “City”, “State”, and MMD that is managed meta column on site http://sbx08/sites/Harry/.

2. Create several entries and file in MMD columns as in the screen shot


3. Use SPMetal.exe located in ..\14\BIN to generate DataContext so you could use the objects in LINQ
C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\BIN>spmetal /web:http://sbx08/sites/Harry/ /namespace:HarrySite /code:HarrySite.cs /language:csharp /useremoteapieapi

spmetal /web:http://sbx08/sites/Harry/ /namespace:HarrySite /code:HarrySite.cs /language:csharp /useremoteapieapi

4. Follow the procedure we mentioned before to add DataContext class. In the DataContext class HarrySite.cs, you will find the attributes “City” and State” for City list. However, there is no MMD attribute generated for this managed meta column. See the following  SPMetal generated code for City list.

[Microsoft.SharePoint.Linq.ContentTypeAttribute(Name="Item", Id="0x01", List="City")]
       public partial class CityItem : Item {
             
              private string _state;
             
              private string _city;
             
              #region Extensibility Method Definitions
              partial void OnLoaded();
              partial void OnValidate();
              partial void OnCreated();
              #endregion
             
              public CityItem() {
                     this.OnCreated();
              }
             
              [Microsoft.SharePoint.Linq.ColumnAttribute(Name="State", Storage="_state", FieldType="Text")]
              public string State {
                     get {
                           return this._state;
                     }
                     set {
                           if ((value != this._state)) {
                                  this.OnPropertyChanging("State", this._state);
                                  this._state = value;
                                  this.OnPropertyChanged("State");
                           }
                     }
              }
             
              [Microsoft.SharePoint.Linq.ColumnAttribute(Name="City", Storage="_city", FieldType="Text")]
              public string City {
                     get {
                           return this._city;
                     }
                     set {
                           if ((value != this._city)) {
                                  this.OnPropertyChanging("City", this._city);
                                  this._city = value;
                                  this.OnPropertyChanged("City");
                           }
                     }
              }
       }

If you try to use the column MMD from DataContext, it will NOT find the reference. This seems to be a bug and it is similar to the issue REST web service could not retrieve data form managed metadata reported before.

We will work with Microsoft and keep you posted.

Procedures and tricks to use LINQ work with SharePoint 2010 list data

LINQ is a very simple and powerful way of querying data in the .Net programming language. It allows the .Net language to query data natively against strongly typed classes. Yes against class with objects through DataContext generated by SPMetal.exe tool. SharePoint will convert the LINQ to CAML during the runtime. As a result, LINQ is an easy way comparing to older CAML to work with list data. Here is example to use LINQ to SharePoint 2010 with tricks and limitations.

1. Create a list named “City” with two columns “City” and “State” on site http://sbx08/sites/Harry. You will see the list as in the screen shot.


2. Use SPMetal.exe located in ..\14\BIN to generate DataContext so you could use the objects in LINQ. The command you could use is:

spmetal /web:http://sbx08/sites/Harry/ /namespace:HarrySite /code:HarrySite.cs /language:csharp /useremoteapieapi

Please note we are using option  /useremoteapi if you are going across the wire from a non-SharePoint machine. It will add default credentials to the api call.

3. Create a console test application and add the following items to avoid compilation error.
  • Change platform target to "x64"
  • Change target framework to ".Net Framework 3.5"
  • Add existing item HarrySite.cs generated form SPMetal.exe to main class
  • Add Microsoft.SharePoint.Linq.dll reference
  • Add using HarrySite and using Microsoft.SharePoint.Kinq in main class
4. Use DataContext to query the "City" list data and insert one new City Chicago, Illinois to the list. Here is the code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using TeamSite100; 2 instances will cause issue
using HarrySite;
using Microsoft.SharePoint.Linq;

namespace SharePointLinq
{
    class Program
    {
        static void Main(string[] args)
        {
DataContext ctx = new DataContext("http://sbx08/sites/Harry/");
            // or HarryDataContext ctx = new HarryDataContext("http://sbx08/sites/Harry/");
            EntityList<CityItem> all = ctx.GetList<CityItem>("City");

            // var cities = from c in ctx.CityItem works also
            var cities = from c in all
                         where c.State == "Washington"
                         orderby c.State
                         select c;

               // Add an item to City list
               CityItem ct = new CityItem() { Title = "44", City = "Chicago", State = "Illilois" };
               all.InsertOnSubmit(ct);
               ctx.SubmitChanges();

            // Get each item from City list
            foreach (var city in cities)
            {
                Console.WriteLine(city.City + "," + city.State);
            }

            Console.ReadLine();


        }
    }
}

5. If you run the program, you will see one city added to the list and program will display the follow city.

Everything looks good and easy. There are some good bog you could refer to utilize LINQ.
However, there are some tricks you need to be aware of when you use LINQ to SharePoint. Here are the top three you need to keep in mind.
  1. Any list definition change such as column renamed, deleted, or type changed will require SPMetal tool to rebuild the DataContext class. Your implementation may require to change also.
  2. LINQ can not retrieve any managed metadata column values or look-up columns we will explain in next blog.
  3. SPMetal will not generate reverse lookup associations for any of the relationships based on the lookup column IF a site lookup column is used by more than one list.
  4. There is no UpdateOnSubmit() function so it is difficult to update the list item through LINQ. The functions InsertOnSubmit() and DeleteOnSubmit() could be used to add and delete list items.
Now w you understand the limitations of the LINQ, you should use this to work with the list that has no managed metadata columns or look-up columns and columns will maintain unchanged.