Monday, December 1, 2014

Access Denied within SPSecurity.RunWithElevatedPrivileges

I never thought that SPSecurity.RunWithElevatedPrivileges gives an error "Access Denied". But yes, i see it in my below scenario.

I was doing an operation within an item event receiver which required higher level of access to update the web level information. Since this was to be executed irrespective of the access of the user logged in, i had the code to be executed within the SPSecurity.RunWithElevatedPrivileges. Unexpectedly the code was failing and throwing an error "Access Denied".

The below code executes well and fine for the users who has Full Control on a web but gives a "Access Denied' error for contributors or readers though the code is wrapped in SPSecurity.RunWithElevatedPrivileges.
public override void ItemCheckedIn(SPItemEventProperties properties)
{
            base.ItemCheckedIn(properties);
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                         SPWeb web = properties.Web;
                         var listItem = properties.ListItem;
                          if (!listItem.Title.Equals(web.Title))
                          {
                                    web.AllowUnsafeUpdates = true;
                                    web.Title = listItem.Title;
                                    web.Update();
                                    web.AllowUnsafeUpdates = false;
                           }
             }
}
In the above code web.Update() throws an exception "Access Denied" Though the code is executing under RunWithElevatedPrivileges. Because the code doesn't really execute under elevated privileges as the respective object SPWeb was not created within the SPSecurity.RunWithElevatedPrivileges but it is retrieved through the receiver context properties.Web, which is created before elevated privileges. Hence the elevated privileges cannot be applied to that object.

The solution for this problem is very simple. Create the objects which are being used to perform some operations with in the SPSecurity.RunWithElevatedPrivileges.
public override void ItemCheckedIn(SPItemEventProperties properties)
{
          base.ItemCheckedIn(properties);
          SPSecurity.RunWithElevatedPrivileges(delegate()
          {                 
                 var listItem = properties.ListItem;
                 using (SPSite site = new SPSite(properties.Site.ID))
                 {
                        using (SPWeb web = site.OpenWeb(properties.Web.ID))
                        {     
                               if (!listItem.Title.Equals(web.Title))
                              {
                                    web.AllowUnsafeUpdates = true;
                                    web.Title = listItem.Title;
                                    web.Update();
                                    web.AllowUnsafeUpdates = false;
                              }
                        }
                 }
           }
}
In this case created a SPWeb object with in elevated previliges though it is available as properties.Web.

Wednesday, November 19, 2014

PowerShell Updating the Page Layout of a Publishing Page

Here is the simple and efficient PS script to update the page layout of a page, if you have both page and layout urls.

While changing the page layout of a publishing page in SharePoint, make sure that the page is of the same content type that is associated with the page layout.
#Add sharepoint snapin
Remove-PSSnapin Microsoft.SharePoint.Powershell -ErrorAction SilentlyContinue
Add-PSSnapin Microsoft.SharePoint.Powershell

#Get publishing web, site and page
$spWeb = Get-SPWeb("http://sampleweb.com")
$pWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($spWeb)
$pSite = New-Object Microsoft.SharePoint.Publishing.PublishingSite($spWeb.Site);
$pubPage = $pWeb.GetPublishingPage("http://sampleweb.com/Pages/testpage.aspx") 

#Get page layout
$siteLayouts = $pSite.GetPageLayouts($false)
$myLayout = $siteLayouts["/_catalogs/masterpage/mylayout.aspx"] 

#Update page layout of a page
$pubPage.CheckOut() 
$pubPage.Layout = $myLayout 
$pubPage.ListItem.Update() 
$pubPage.CheckIn("Updated page layout via PowerShell")

$spWeb.Dispose()

Friday, December 13, 2013

How to Add User to Default SharePoint Groups Programatically

As every SharePoint developer or user know that when a new Site is created, Obviously 3 commonly used site specific groups will be created automatically along with some general groups.
<Site Name> Owners
<Site Name> Members
<Site Name> Visitors

To add an user to any of the above three groups of a site, we have a API method to get the groups associated with that site, programatically.
using (SPSite site = new SPSite("http://siteurl"))
{
    using (SPWeb web = site.RootWeb)
    {
        ////Get the site default groups
        SPGroup owernsGroup = web.AssociatedOwnerGroup;
        SPGroup membersGroup = web.AssociatedMemberGroup;
        SPGroup visitorsGroup = web.AssociatedVisitorGroup;

        ////Add user to groups
        SPUser user = web.EnsureUser("login");
        if (null != user)
        {
            owernsGroup.AddUser(user);
            membersGroup.AddUser(user);
            visitorsGroup.AddUser(user);
        }
    }
}

Also we can add user to other default groups like Approvers, Designers and Viewers etc., But there is no API property for web to get these groups. So we can get the group by Name.
if (web.GroupExists("Approvers"))
{
    ////Get approvers group
    SPGroup approversGroup = web.Groups["Approvers"];            
    if (null != approversGroup)
    {
        ////Add user to group
        SPUser user = web.EnsureUser("login");
        approversGroup.AddUser(user);
    }
}

Check User exists in SharePoint Group

I was looking for direct API method to check whether specified user exists in SharePoint group. Unfortunately there is no direct API check.

I found below single statement is efficient for this check. I made it a generic for re-usability.

SPGroup spGroup = web.Groups["Group Name"];
if (null != spGroup)
{
  SPUser spUser = web.EnsureUser("Login Account");
  if (null != spUser)
  {
    bool userExsists = spUser.Groups.Cast<SPGroup>().Any(g => g.Name.ToLower() == spGroup.Name.ToLower());
     if (!userExsists)
     {
        spGroup.AddUser(spUser);
     }
  }
}

Monday, January 28, 2013

Load SharePoint PowerShell Commandlets in the PowerShell ISE

We know that Windows PowerShell ISE is a nice editor for writing and executing PowerShell scripts. But for SharePoint Administrators/Developers none of the SharePoint PowerShell Command-lets are loaded by default, so need to Add the Snap-In explicitly. But I wanted to load the SharePoint snap-in to ISE one-time and use it further without loading again-and-again.

Execute the below PowerShell script in ISE to load the snap-in to ISE profile.
if (!(test-path $profile.AllUsersAllHosts)) 
{
   new-item -type file -path $profile.AllUsersAllHosts-force
}

$cmd = 'if((Get-PSSnapin | Where-Object {$_.Name -eq "Microsoft.SharePoint.PowerShell"}) -eq $null) { Add-PSSnapIn "Microsoft.SharePoint.Powershell" }'

out-file -FilePath $profile -InputObject $cmd -Append
Now close and open the ISE, It will load SharePoint snap-in automatically. Intellisense is available for all the SharePoint Command-lets.

Alternatively a single line of statement can be added to ISE to load the SharePoint SnapIn
Add-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue
Hope this saves your time...