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();
      }
   }
}