“Updates are currently disabled in GET requests” exception while provisioning multiple site collections

Scenario:

Recently i was working with creating site provisioning in SharePoint 2010. For that I have a list where user submits the request for the site that needs to be created (along with some required metadata). Then I have scheduled the timer job that picks up items from that list (based on the status), iterates through the items and provisions the site.

Here is the method in Timer Job that iterates through the items and creates site collections.

public static void ProvisionSite(String siteCollectionUrl)
{
   using (SPSite siteCollection = new SPSite(siteCollectionUrl))
   {
      SPList requestList = siteCollection.RootWeb.Lists.TryGetList("SiteRequests");
      if (requestList != null)
      {
        var query = new SPQuery();

        //Fetch the items that have Status=Requested
        query.Query = String.Format("{0}", "Requested");

        SPListItemCollection items = list.GetItems(query);

        if (items != null && items.Count > 0)
            ProcessRequest(items);
      }
   }
}

//Generates random 7-digit number
public static String GenerateSiteNumber()
{
  Random random = new Random();
  String siteNumber = random.Next(0000000, 9999999).ToString();
  return siteNumber;
}

public static void ProcessRequest(SPListItemCollection items)
{
   try
   {
      foreach (SPListItem item in items)
      {
        try
        {
           SPSite site = null;
           SPSecurity.RunWithElevatedPrivileges(delegate
           {
               SPWebApplication webApp = item.Web.Site.WebApplication;
               SPSiteCollection siteCollections = webApp.Sites;

              //Generate random number to use as site collection url
              String siteNumber = GenerateSiteNumber();
              String relativeUrl = "/sites/" + siteNumber;

              //Fail if site collection already exists
              if (SPSite.Exists(new Uri("http://sp2010demo/" + relativeUrl)))
                   throw new Exception("Site already exists");

               using (SPSite newSite = siteCollections.Add(relativeUrl, "My Site" + siteNumber, "Test Site collection", 1033, null, "dc\spadmin", "",       ""))
               {
                  site = newSite;
                  //.. code removed for clarity .....
               }
           });
        }
      }
   }
   catch
   { }
}

Problem:

As provisioning is done by the timer job Context is null (i.e., HttpContext.Current will be null). In my solution, I am simply iterating through the list items and creating the site collections. First request always works fine, site is created successfully but I am getting an exception as soon as the site for the next item is being provisioned. The exception that I get is the following:

“Updates are currently disabled in GET requests. To enable updates in GET requests, change the property AllowUnsafeUpdates on SPWeb.”

Now apparently, this exception is thrown when you perform any GET operation on Web and you haven’t set AllowUnsafeUpdates to “True” !! But I’m just creating a site collection, so I don’t have any SPWeb object where I should set this property ??

After investigating a little further (and with the help of ILSpy) it turned out that after first site collection is created, HttpContext.Current is set to the context of this newly created site collection and for the next item, Context is already set to the previous site collection and exception is thrown at SiteCollection.Add() method. The Add() method basically checks if HttpContext is already set, if yes then it uses the context and in my case this is wrong!

Solution

The solution is quite easy to fix.

After the completion of first iteration, set the HttpContext to null so that during the next request there is no context set.

Here is the line of code (finally block) that I added to my previous method.


public static void ProcessRequest(SPListItemCollection items)
{
 try
 {
   foreach (SPListItem item in items)
   {
     try
     {
         //... code removed for clarity --
     }
     finally
     {
        //Reset the Context
        System.Web.HttpContext.Current = null;
     }
   }
 }
 catch
 { }
}

Hope it helps.

Incorrect values for User Fields while copying list items from one site collection to another

Scenario

Recently, I was writting a piece of code to copy list item from a list in one site collection to the list in another site collection. I did the shallow copy (copying field by field).
Both the lists were created using same List definition so they had same fields and content types etc.
The item was successfully copied but then I noticed that the values of user fields were not correct.

Here is the pice of code that i used for copying the item


Public static void Main(string[] args)
{
    using (SPSite sourceSite = new SPSite("http://sp2010demo/sites/source"))
    {
      using (SPWeb web = sourceSite .OpenWeb())
      {
          SPList sourceList= web.Lists.TryGetList("Source List");
          //fetch first item - for demonstration purpose
          SPListItem sourceItem = sourceList.Items[0];
          //instantiate destination site collection
          using (SPSite destinationSite = new SPSite("http://sp2010demo/sites/destination/"))
          {
             var destinationList = destinationSite.RootWeb.Lists.TryGetList("DestinationList");
             if (destinationList != null)
             {
                //Add new item
                SPListItem destinationItem = destinationList.Items.Add();
                //copy item
                CopyListItem(sourceItem, destinationItem);
             }
          }
      }
   }
}

public static void CopyListItem(SPListItem sourceItem, SPListItem destinationItem)
{
        foreach (SPField field in sourceItem.Fields)
        { 
          //Filter out readonly and attachment fields
          if ((!field.ReadOnlyField) && (field.InternalName != "Attachments"))
          {
                destinationItem[field.Title] = sourceItem[field.Title];
          }
        } 
        destinationItem.Update();
}

Apparently, it looked just fine and i used the same piece of code to copy the list items from one list to another within the same site collection and it worked just perfect as it should !

Problem

So, the problem was actually that when you move this item to a different site collection, users have to be present there in order for it to have the correct value.

If you look at the internal representation of the user field it is basically a look up value and the value is represented something like this: 1;#Sanjay Bhagia

Where first number is the user id in User Information List in the site collection that can be accessed (only if you are admin) by navigating to the following url in your site _catalogs/users/simple.aspx (e.g., http://sp2010demo/sites/source/_catalogs/users/simple.aspx).

So lets do some investigation.

I first open my source site collection i.e., http://sp2010demo/sites/source and navigate to my user information list by navigating to the following url http://sp2010demo/sites/source_catalogs/users/simple.aspx It shows all the users that have been added in my site

userinfosourcesite

Now, if you put the cursor on the user link, you can see the entire url in the status bar of your browser. At the end of the url you can see the querystring ID that has value 1 in this example.

Now lets navigate to the same user list in destination site collection by (http://sp2010demo/sites/destination/_catalog/users/simple.aspx).

Note: In my environment, I already have my user added in both the site collections, that is why I’m able to see my user at both the places but it is possible that you don’t see any user at both the places. User will be added automatically if you have logged in with that user on that site

userinfodestination

By observing the url of the same user we can see that the user id for this user in destination site collection is 10.

So if I’m copying the list item that has user field and that field has my user added (i.e., Sanjay Bhagia) the representation of that user in my source site collection will be 1;#Sanjay Bhagia but the representation of the same user in my destination site collection will be 10;#Sanjay Bhagia !! Hence, the copied item will not display the proper user at the destination list item.

Solution
It is quite easy to fix this issue, I basically fetched the value from user field and called SPWeb.EnsureUser() method to make sure the user is added in the User Information List in my destination site collection and updated the LookupId attribute of the SPUser object.

Here is the modified code:


public static void CopyListItem(SPListItem sourceItem, SPListItem destinationItem)
{
    foreach (SPField field in sourceItem.Fields)
    {
        //Filter out readonly and attachment fields
	if ((!field.ReadOnlyField) && (field.InternalName != "Attachments"))
        {
            //only for user type fields
            if (field.GetType() == typeof(SPFieldUser))
            {
               SPWeb destinationParentWeb = destinationItem.ParentList.ParentWeb;
               if (sourceItem[field.Title] != null)
               {
                 //for field can have multiple selection enabled otherwise you can user SPFieldUserValue class
                 SPFieldUserValueCollection users = new SPFieldUserValueCollection(sourceItem.ParentList.ParentWeb, sourceItem[field.Title].ToString());
                 foreach (SPFieldUserValue user in users)
                 {
                    //this LookupId is the number of user object in User Information List
                    user.LookupId = destinationParentWeb.EnsureUser(user.User.LoginName).ID;
                 }
                destinationItem[field.Title] = users;
               }
            }
            else
              destinationItem[field.Title] = sourceItem[field.Title];
                    }
        }
    destinationItem.Update();
}

So basically, I’m making sure that user object in destination site collection has the correct look up id, that’s it!

Hope it helps.

Creating loosely coupled and generic process steps implementation

Recently, during one of the SharePoint implementations we came across the requirement to build the workflow for site provisioning engine that we are developing.

One of the requirements of the process is to rollback everything that we create/modify if there is any problem/exception during the process. So for example, if we created the site collection and then in the next step process failed at setting up custom permissions, the process should be roll backed to the initial stage.

Normally, this is no big deal to implement everything as a standard workflow and you write a piece of code for different activities of the workflow (or use standard workflow activities from Visual Studio toolbox) but then an idea came up why not write an implementation of the process steps independent of the workflow in a nice and loosely couple way that should take care of roll back in a much flexible and nicer and cleaner way !

So in this post, I will try to describe the solution that I came up with and also implementation details.

So here is the implementation details:

First, I created an interface that contains the method declarations for Execute and RollBack methods


public interface IProcessStep
{
   void Execute(ProcessStepProperties properties);

   void RollBack(ProcessStepProperties properties);
}

As you see in the code above, I’m using parameter of type ProcessStepProperties. This is basically a custom class that I use as a data transfer object (DTO) across different steps and methods. It’s basically a collection (dictionary in my implementation) that contain key/value pairs. Here is the implementation:


public class ProcessStepProperties
{
   public Dictionary<String, Object> Keys = new Dictionary<String, Object>();

   public bool KeyExists(String key)
   {
     if (Keys.ContainsKey(key) && Keys[key] != null)
        return true;
     return false;
   }
}

Having interface ready, we write a class that defines one the process steps. This step is CreateSiteStep that creates the site collection. This class inherits from our interface IProcessStep and hence it provides the implementation of Execute and RollBack methods.


public class CreateSiteStep : IProcessStep
{
   //private method to check for preconditions for Execute method
   private bool PrequisitesPresent(ProcessStepProperties properties)
   {
      if (properties.KeyExists("SiteNumber") && properties.KeyExists("Title") && properties.KeyExists("TemplateName"))
         return true;
      return false;
   }

   public void Execute(ProcessStepProperties properties)
   {
      //Check if preconditions are met
      if (PrequisitesPresent(properties))
      {
         //Create site collection
         CreateSiteFromRequest(properties);

         //Update the property bag
         var spSite = properties.Keys["SPSite"] as SPSite;
         if (spSite != null)
         {
            properties.Keys["SiteUrl"] = spSite.Url;
         }
      }

      //Write checkpoint for rollback
      TransactionLogger.Log(this);
   }

   public void RollBack(ProcessStepProperties properties)
   {
        // Add Logging information  - ULS or event logger if appropriate
        SPSite site = properties.Keys["SPSite"] as SPSite;
        DeleteSite(site);
   }

Now there are couple of things that need to be discussed in the above code.

Firstly, as you see, I have created a private method to check pre-conditions before the Execute method is executed. This is totally upto the specific step to check if it requires any pre-conditions to be met or any values it requires before it proceeds.

Secondly, as i mentioned above I’m using ProcessProperties class (key/value collection) to transfer the state across methods and steps, I’m updating (adding) the value in the collection right after Site collection has been created. In this way, the next step (or rollback) step will have the updated information that might be useful for further execution of the process.

Thirdly and quite importantly there is a call to Log method of  TransactionLogger class.
TransactionLogger.Log(this);

This is very important. Again, TransactionLogger is my custom class that I’m using to log in the information for steps that have successfully been completed. This serves the purpose of check point for me that i use to roll back when anything fails. I’m using Stack as a data structure to push the steps (of type IProcessStep) whenever any step is successfully executed and if there is any failure during the execution, i simply pop the steps and invoke RollBack method until stack is empty – hence everything is cleaned up in case of failover.

Here is the implementation of TransactionLogger class.


public class TransactionLogger
{
  //to keep checkpoints
  private static readonly Stack TransactionStack = new Stack();

  //public property to check number of steps in the stack
  public static int LogCount
  {
      get { return TransactionStack.Count; }
  }

  private IProcessStep Pull()
  {
     return TransactionStack.Pop();
  }

  //Pushes the process step into the stack
  public static void Log(IProcessStep step)
  {
      TransactionStack.Push(step);
  }

  //Pops the step from stack and invoke rollback method for every step  until stack is empty
  public static void RollBack(ProcessStepProperties properties)
  {
      while (TransactionStack.Count > 0)
      {
          var step = TransactionStack.Pop();
          step.RollBack(properties);
      }
   }
}

So, basically the idea is that in case of any failure everything is rolled back and in the implementation above, TransactionLogger class doesn’t need to know the details about any ProcessStep class how to roll back because it is upto every step to roll back the things it has created. That is why TransactionLogger class adds IProcessStep object into the stack and that is our interface!
here is another class called SetSitePermissionGroupStep that basically adds custom permissions to the site collection that has been created in previous process step.


public class SetSitePermissionGroupStep : IProcessStep
{

   // .... code removed for clarity purpose ....

 public override void Execute(ProcessStepProperties properties)
 {
    if (PrequisitesPresent(properties))
    {
       AddAssociatedMemberGroups(properties, _key);
       TransactionLogger.Log(this);
    }
 }

 public override void RollBack(ProcessStepProperties properties)
 {
   //Roll back implementation
 }
}

So, as a recap, we defined an interface called IProcessStep that contains the declaration of Execute and RollBack methods.

Other classes (specific to process steps) inherit from IProcessStep and provide an implementation for Execute and RollBack methods. Execute method of every process step class also add check point after successful execution of the step using TransactionLogger.Log method.

Now having all this setup we need some sort of controller class that could use this implementation. This could be used from within the workflow as well but in my demonstration I’m using a class that implements the Queue and in that queue we can insert separate steps and then Process the queue in the end. Here is the implementation of my controller class.


public class TestProcessSteps
{
    private Queue _queue = new Queue();
    private ProcessStepProperties _properties = new ProcessStepProperties();

    public void Initialize(ProcessStepProperties properties)
    {
       _properties = properties;
    }
    public void Enqueue(IProcessStep step)
    {
       this._queue.Enqueue(step);
    }

    public void ProcessQueue()
    {
      try
      {
         while (_queue.Count > 0)
         {
            //pick item from the queue and invoke execute method
            var step = _queue.Dequeue();
            step.Execute(_properties);
         }
       }
       catch (Exception ex)
       {
          //roll back
          TransactionLogger.RollBack(_properties);
       }
    }
 }

If you notice above, if anything fails (i.e, exception occurs), RollBack method is invoked from TransactionLogger class (that basically pops up items from the stack and invoke rollback method on respective step).

Finally, here is how we test our implementation.


//Instantiate TestProcessStep class
TestProcessSteps testclient = new TestProcessSteps();

//setup properties
properties = InitializeProcessProperties(item);

//Invoke Initialize method on TestProcessSteps class (that basically initializes ProcessProperties)
testclient.Initialize(properties);

//Insert steps into Queue
testclient.Enqueue(new CreateSiteStep());
testclient.Enqueue(new SetSitePermissionGroupStep("MemberGroup"));
testclient.Enqueue(new SetSitePermissionGroupStep("OwnerGroup"));

//Process the Queue (that basically takes items from queue and invoke Execute methods on respective steps)
testclient.ProcessQueue();

Note that during the enqueue, I’m adding SetSitePermissionGroupStep step two times with different parameters – in this way you can actually define atomic steps also using the same class.

Hope it gives the general idea about my thought behind the implementation. Don’t hesitate to provide any feedback or issues if you find any in my solution.

Thanks for taking time to read this long post 🙂

Consuming Content Type Hub in custom web templates

Setting up content type hub in SharePoint 2010 is quite straightforward process. There are simple steps to follow and if followed in correct order you can get it working in no time. Content Type Hub relies on managed metadata service. Unless all web applications are sharing the same instance of managed metadata service application, they should be able to consume content types from the content type hub (a site collection marked as a hub and other web application are marked to consume from this location).

However, if you are doing the customization and you have some custom Web Templates that you use to create sites and subsites. And in those sites, you want to consume content types from content type hub, you might get a little bump in the road during the setup.

As I mentioned, Content Type Hub relies on Managed Matadata service, one feature TaxonomyFieldAdded (a hidden) feature is enabled on every site collection by default. This feature is not activated by default when you create web templates (please refer to this blog for further explanation of this: Web Templates and Content Type Publishing). So if you navigate to the Site Settings for any site that is created from custom web template, you will not see any link for Term store management or Content Type Publishing

sitesettings-featuredisabled

So for custom web templates, you are required to activate this feature.  Here is the PowerShell code for enabling this feature on a specific site collection.

$snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'}

if ($snapin -eq $null)
{
Write-Host "Loading SharePoint Powershell Snapin"
Add-PSSnapin "Microsoft.SharePoint.Powershell" -EA SilentlyContinue
}

#Enable <strong>TaxonomyFieldAdded </strong>feature on site collection
Enable-SPFeature –Identity 73EF14B1-13A9-416b-A9B5-ECECA2B0604C –url "http://portal.local.domain/sites/subsite1/"

Once feature is activated, you will see the links in Site Settings appear !
sitesettings-featureenabled

Now, you should be able to consume content types from content type hub as expected. You can activate this feature directly from your web template, add an entry for this feature in onet.xml file for your web template, in this way you don’t have to enable this feature manually.

</pre>
<SiteFeatures>
<!-- OOTB: Taxonomy -->
<Feature ID="73EF14B1-13A9-416b-A9B5-ECECA2B0604C" />
</SiteFeatures>
<pre>

Paged Custom Search Query using KeywordQuery

SharePoint Server Object Model provides two classes to perform custom search queries namely FullTextSqlQuery and KeywordQuery. Both of these classes inherit from a generic Query class (in  Microsoft.SharePoint.Search namespace).

You can perform search operations using both of these classes depending on your needs as FullTextSqlQuery provides more sophisticated queries using Sql syntax whereas KeywordQuery class provides simpler syntax to do the search.

However, the scenario I’m explaining in this post is relevant to both of these classes but i will use KeywordQuery class to demonstrate.

Scenario:

I want to write a custom people search web part that takes in the keywords entered by user and performs the search.

I’m using powershell to demonstrate here but this can be done in C# as well in similar fashion.

So, here is the function that performs the basic search


Function GetSearchResults([string]$siteCollectionUrl, [string]$keyword, [int]$sIndex, [int]$rLimit)
{
	#load the site and set the keyword query object
	$searchSite = Get-SPSite $siteCollectionUrl

	$keywordQuery = New-Object Microsoft.Office.Server.Search.Query.KeywordQuery $searchSite

	$keywordQuery.ResultTypes = [Microsoft.Office.Server.Search.Query.ResultType]::RelevantResults

  	#Write-Host "Start Index " $sIndex
	#Write-Host "Row Limit " $rLimit
	#Write-Host "Keyword " $keyword

	$keywordQuery.StartRow = $sIndex
	$keywordQuery.RowLimit = $rLimit
	$keywordQuery.HiddenConstraints = "scope:People"

	#Pass the query text - SharePoint
	$keywordQuery.QueryText = $keyword

	#query execution
	$results = $keywordQuery.Execute()
	return $results
}

KeywordQuery class has two properties (RowLimit and StartRow) that are of our interest here.
RowLimit specifies the number of items that you want the search to return when you execute the query
and StartRow gets or sets the first row of information from the search results. So basically with both of these properties you get the paged search result instead of getting entire data in one chunk !

Now, one of the obvious reasons to do this performance. But there is one more reason as well. RowLimit (default value is 50 if you don’t specify it yourself), even though can accept maximum 32-bit integer value theoretically, is set to some hardcoded limit of 10000. It means that if you set RowLimit to anything greater that 10000 you will get an exception when you execute the query.

The exception is something like this:

"Exception from HRESULT: 0x80040E01"

Here is the StackTrace from ULS log:

Log Query: More Information: Row could not be inserted into the rowset without exceeding provider's maximum number of active rows.

SearchServiceApplication::Execute--Exception: System.Runtime.InteropServices.COMException (0x80040E01): Exception from HRESULT: 0x80040E01
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at Microsoft.Office.Server.Search.Query.KeywordQueryInternal.Execute()
at Microsoft.Office.Server.Search.Query.QueryInternal.Execute(QueryProperties properties)
at Microsoft.Office.Server.Search.Administration.SearchServiceApplication.Execute(QueryProperties properties)

SearchServiceApplicationProxy::Execute--Error occured: System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]:
Exception from HRESULT: 0x80040E01 (Fault Detail is equal to An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true,
whose value is: System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x80040E01
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at Microsoft.Office.Server.Search.Query.KeywordQueryInternal.Execute()
at Microsoft.Office.Server.Search.Query.QueryInternal.Execute(QueryProperties properties)
at Microsoft.Office.Server.Search.Administration.SearchServiceApplication.Execute(QueryProperties properties)
at SyncInvokeExecute(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc) ...).

You can determine this limit by this method in your environment.

# Get the maximum results returned value set for KeywordQuery and FullTextQuery in current environment
Function GetMaxResultsReturned()
{
 # Get reference to Search Service Application
 $ssa = Get-SPEnterpriseSearchServiceApplication
 return $ssa.GetSetting('Config:qp_MaxResultsReturned')
}

This value is configured at Search Service Application level. Now this could be different for your environment but default is 10000. So if you set RowLimit to anything greater than this value (‘Config:qp_MaxResultsReturned’) you will get an exception.
You can modify this value if you want using this function

# Sets the maximum results returned value set for KeywordQuery and FullTextQuery in current environment
Function SetMaxResultsReturned($newLimit)
{
	$ssa = Get-SPEnterpriseSearchServiceApplication
	$ssa.UpdateSetting('Config:qp_MaxResultsReturned', $newLimit)
	$ssa.Update()
}

So, in order to avoid this exception, you should query to retrieve results in chunks and then merge them. In this way you don’t loose on performance and also you will get your results back without facing this exception.

Following, I’m calling the GetSearchResults function (defined above) to perform people search query by retrieving results in pages and merging them in a DataTable.


$startIndex = 0
$rowLimit = 50
$siteCollectionUrl = "<Url of the site collection>"
$searchKey = "s*"

$resultCollection = GetSearchResults $siteCollectionUrl $searchKey $startIndex $rowLimit
$hits = $resultCollection.TotalRows
Write-Host Total hits: $hits
$resultsDataTable = $resultCollection.Table

#Iterate through the rest of the pages to fetch items
while($resultCollection.TotalRows -gt $resultsDataTable.Rows.Count)
{
	$sIndex = $resultsDataTable.Rows.Count
	$resultCollection = GetSearchResults $searchKey $startIndex $rowLimit
        $resultsDataTable.Load($resultCollection, [System.Data.LoadOption]::PreserveChanges)
}

# display the results in table format for better view
$resultsDataTable | Format-Table -AutoSize -Property title , url

Hope it helps!

Update list items to trigger event handler through PowerShell

Scenario:
A SharePoint list has different content types associated with it and every content type has a field of type taxonomy (connected to managed metadata termstore). And that list has an event handler attached to it that is triggered on item add and item update. In that event handler, you push the change to user profile property (for respective user profile) that is also of type managed metadata. So basically when you add or update an item in the list, that value is written back in a user profile property.

Now, under normal circumstances everything should work fine as event handler will kick in and write back the values to user property. However, if you have to delete the user profile or due to different reasons, the values in user profile is wiped off etc, you need to ensure that the data already inserted in the list is also present in the user profile.

Solution:

For such scenarios, what we need to do is trigger the event handler on every list item so that the value is written back to user profile property. PowerShell comes to our rescue in such situation!

Here is the powerShell script for that


Function UpdateListItems($webAppUrl)
 {
  [Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges(
  {
     #Get Site Collections for respective web applications
     $sitecolls = Get-SPSite -WebApplication $webAppUrl

     Write-Host "Total number of site collections: " $sitecolls.Count

     #Iterate through every site collection to find the list at the root web
     foreach($siteCol in $sitecolls)
     {
        $site = Get-SPSite $siteCol.Url

        Write-Host "Site Collection: " $site.Url
        $rootSite = $site.RootWeb

        Write-Host $rootSite.Name

        #Retrieve the list
        $list = $rootSite.Lists["ListName"]

        if($list -ne $null)
        {

         # Fetch list items only for required content type (If this needs to execute for every content type,
         # just remove the filter from following statement, just write $listItems = $list.Items)
         $listItems = $list.Items | ?{$_.ContentType.Name -eq "ContentTypeName"}

         foreach($item in $listItems)
         {
           Write-Host $item["Field's Dislay Name"]

           #Update the item
           $item.Update()
         }
       }
     }
  })
 }

Now once the function is defined, call it for execution.


UpdateListItems "http://mysite.mydomain.com"

One thing to notice in the script above is that we are using RunWithElevatedPrivileges. My Colleague Matthias Einig has written an amazing post on his blog regarding this, do check it out.

Hope it helps.

Remove webpart from a page on Personal site using delegate control

Customizing Personal Sites (site collections for users in my site) could be little challenging at times. One reason being that personal site is provisioned automatically by SharePoint and there could be more than one point from which user can provision the site and there is no support to create your own web template or site definition, as we can do with every other type of site (as per my knowledge). Creating your own web template provides great flexibility and easiness to provision sites  having your own settings and all but lacking this feature for personal sites provide us more challenges, even for small customizations at times. In a recent project, one of the customer’s wish was to remove “Recent Blog Posts” web part from default page when user provisions the personal site.

Solution: After trying out different approaches the final solution turned out to create a delegate control that removes the web part from the page when site is provisioned and having this delegate control added on the site collection through feature stappling.

So, here is what I did to fulfill this requirement.

  1. Create a delegate control
  2. Here is the element file for delegate control

    
    <!--?xml version="1.0" encoding="utf-8"?-->
    
     <Control
     ControlAssembly="MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b848d3ec396f6039"
     ControlClass="MyAssembly.RemoveWebPartsFromPersonalSiteDefaultPage" Id="AdditionalPageHead" Sequence="50"
    
    
  3. Fetch the desired webpart and remove it

and in the code behind file for this delegate control add the functionality to fetch and remove the desired webpart (I’ve added this logic in Render method)



//... code removed

//Fetch Web Part title from SPCore resoruce file for current web language
 //It is important to fetch the localed based on current site's language not based on Browser locale or regional settings!
 var localizedRecentPostWebPartName = SPUtility.GetLocalizedString("$Resources:spscore,MySiteOnet_WebPart_Blog", "spscore", SPContext.Current.Web.Language);

//remove the webpart
 RemoveWebPartsFromWebPartPage(SPContext.Current.Web, "default.aspx", "MiddleRightZone", localizedRecentPostWebPartName);

 //add page redirectino logic otherwise it will be an infinite loop
 // ... code removed

Here is the definition of RemoveWebPartsFromWebPartPage method:



//... code removed

using (SP.SPLimitedWebPartManager webpartManager = web.GetLimitedWebPartManager(webPartPage.Url, Web.PersonalizationScope.Shared))
{
  var collection = webpartManager.WebParts;
  List webParts = new List();

  foreach (WebPart webPart in collection)
  {
    if (fromZoneId == webpartManager.GetZoneID(webPart))
    {
      if (webPart.DisplayTitle == webPartTitle)
      {
          webParts.Add(webPart);
      }
    }
  }

 // delete the web parts
 foreach (WebPart webPart in webParts)
 {
   webpartManager.DeleteWebPart(webPart);
 }
}

That’s it! that is the functionality that we need to remove the webpart. We created a delegate control that contains the code to remove the web part from desired page! Now comes the part where we need to execute this delegate. You can use different approaches depending on your requirement. For this demo, we create a simple site scoped feature and add this delegate as an item in that feature.

Build your solution, create the package and deploy it. Activate the feature. Having this feature activated, as soon as user navigates to the default page, our delegate control will kick in and looks for the desired web part and removes it!

Other approaches could be creating feature stappling mechanism that attaches our delegate control with desire web templates so that this control is attached to every site that is created.

Hope it helps!

Adding local variables on a page as a user control

Quite often you need to use both server side and client side approach to build SharePoint solution as both offer different level of flexibility and functionality. One of the benefits of using mixed approach is that you can utilize the strength of both approaches and solve the problems quite quickly and efficiently.

In this post I’m going to present a part of the solution where we had to cache one control during one of the recent projects.

It is a very common practice that we write separate .js file for our client side scripting and than we can add these file either on a page or on a master page that we can use later on in our controls underneath. This works totally fine but the only thing we need to be careful about external files is that we need to wait for them to load before we can use anything inside these files. That is also fine but in some situations but there might be a little delay until file is loaded and that delay might be of quite a significance.

To overcome this issue, we (Me and Magnus Hansson, who is the mastermind of this solution) recently came up with an approach where we actually created a user control and in that user control, we added some script to set some local variables that we needed to use in on our page.

Here is the markup of the user control (ascx control):

Markup:


<script type="text/javascript">
var Local = window.Local || {};
   Local.Variables = function () {
     return {
        currentRootWebUrl : '<asp:Literal runat="server" id="litRootWebUrl" />',
        currentWebUrl : '<asp:Literal runat="server" id="litCurrentWebUrl" />',
        currentUser : '<asp:Literal runat="server" id="litCurrentUser" />',
        currentLCID : '<asp:Literal runat="server" id="litCurrentLCID" />'
       }
} ();
</script>

and in the CodeBehind(.cs) we set these asp controls embedded in script above:


protected override void CreateChildControls()
{
      //Get current user's Web object and Root Web Object
      SPWeb currentWeb = SPContext.Current.Web;
      SPWeb currentRootWeb = currentWeb.Site.RootWeb;

      //Get Locale and user's login name
      litCurrentLCID.Text = SPContext.Current.Web.CurrencyLocaleID.ToString();
      litCurrentUser.Text = SPContext.Current.Web.CurrentUser.LoginName;
      litCurrentWebUrl.Text = currentWeb.Url;
      litRootWebUrl.Text = currentRootWeb.Url;

      base.CreateChildControls();
}

So what we have achieved so far is basically set of variables that are set from code behind in control. When executed, the output would be a JavaScript object named “Local” that has a public property called “Variables” that contains some more properties.

Now in order to see this, we need to add this control either on application page or master page (or any where you find it appropriate in your case).

We added in on Master Page as we needed to used this on a control inside master page. In order to add it on a master page, you can directly Register your user control on master page and then added reference to the control in header section or even create a delegate control to attach it with master page through feature.

Once, control is added, navigate to your site and see the browser output. You should find this section rendered in the view source:


var Local = window.Local || {};

Local.Variables = function () {

return {
    currentRootWebUrl : 'http://demo.local.com',
    currentWebUrl : 'http://demo.local.com/site/hr',
    currentUser : 'domain\\user1',
    currentLCID : '1033'
  }

} ();

Now as soon as DOM is loaded, you have your local variables present that you can use right away in your control further down in the DOM (or controls etc) without waiting for any other JavaScript file to load like following:


    var currentRootWebUrl = Local.Variables.currentRootWebUrl;
    var currentWebUrl = Local.Variables.currentWebUrl;

AllowEveryoneViewItems – Enable anonymous access to files in list

SharePoint provides anonymous access to sites and document libraries etc but for that you have to set up the policy at web application level and then for specific site or document library as per your needs. But at times you don’t want to allow anonymous access for entire web application or site collection but simply want to provide access to files within a specific document library without going through the trouble of configuring and managing permissions.

If you want to access documents within a document library or files in a list (as an attachment) there is one quick and easy way to do that.

There is a list property called AllowEveryoneViewItems that makes all documents within a document library anonymously accessible without dealing with permissions. There is no option in UI to set this up so you have to either do it through code, declaratively (list definition element.xml) or Powershell (depending on your situation and need).

Here is the PowerShell code for setting this property on any list or library


$web = Get-SPWeb -Identity "[site url]"
$list = $web.Lists.TryGetList("[list title]");
$list.AllowEveryoneViewItems = $true
$list.Update()

This is how you define in list definition xml:


<!--?xml version="1.0" encoding="utf-8"?-->

	<ListTemplate
		Name="[list name]"
		DisplayName="[list display name]"
		Description="[list description]"
		Type="10000"
		BaseType="0"
		Default="True"
		VersioningEnabled="TRUE"
		Hidden="FALSE"
                AllowEveryoneViewItems="TRUE"
		HiddenList="FALSE" />

And this is how you can do it using code


using (SPSite site = new SPSite("[site collection url]"))
{
   using (SPWeb web = site.OpenWeb())
   {
       SPList list = rootWeb.Lists.TryGetList("[list title]");
       if (list != null)
       {
          if (!list.AllowEveryoneViewItems)
          {
             list.AllowEveryoneViewItems = true;
             list.Update();
          }
       }
    }
}

It is important to note here that this property does not apply to all list items, but only to documents in document libraries or to attachments in list items. So you have to access this file directly for it to work ! (reference: SPList.AllowEveryoneViewItems property)

Here is the code snippet for accessing the file directly using HttpWebRequest


var fileContent = String.Empty;
//construct path to the navigation file
string fileUrl = "[full url of the file]";

//create webrequest to fetch this file!
var httpRequest = (HttpWebRequest)WebRequest.Create(fileUrl);
using (var webResponse = (HttpWebResponse)httpRequest.GetResponse())
{
   if (webResponse.StatusCode == HttpStatusCode.OK)
   {
      using (var responseStream = new StreamReader(webResponse.GetResponseStream()))
      {
          //if we get the response, read the content from file as string
          fileContent = responseStream.ReadToEnd();
      }
   }
}