To share a quote from the most interesting man in the world... I don't code for Sharepoint often, but when I do, I prefer to know what the hell I am doing.
Update (06/06/2012): Before today the code that was here ran the possibility of generating a NullException error getting the current user. I have updated the code to resolve this based on information from Microsoft. For a descriptive addendum, see the update at the end of this post.
Recently I was tasked with customizing the Request Access page in Sharepoint 2010. Our group is small and our users not so saavy, so we simply wanted to list all the Site Owners on the Request Access page. This way end users would know who they needed to contact if they did not have access to a site. Slam dunk, right? Well, so I thought.
First off, I am by no means an expert Sharepoint or .NET developer. At the time of this writing, I have only developed a handful of Sharepoint applications and have under 6 months of experience. I have been a developer for a long time, but Sharepoint and .NET are not my native tongues. As such, I will explain as much as I understand and did to complete my tasks, but the why's of all of this will only be assumptions. Please feel free to educate me if I speak in error or you feel the need to clarify any of this.
I started out with a Google search, as all quests for knowledge usually do, and found a lot of links on how to do this. One of the most concise posts was by Anmol Rehan:
http://www.anmolrehan-sharepointconsultant.com/2011/08/how-to-use-custom-access-denied-page-in.html
One problem that is not well explained is that this creates a Site (Site Collection) scoped feature which will set the custom page. UpdateMappedPage() is not available to Sandboxed Solutions and must be a Farm solution, and sets the custom page at the WebApplication level. However, due to recent security fixes, you should get an error when trying to deploy or activate your solution.This is because webApp.Update() has to reach up into the WebApplication to work properly. As such, make sure you have this scoped at the WebApplication level and NOT the Site Collection level; Otherwise activating/deploying will give you an Access Denied error in your logs.
Another problem, my main problem and an issue with all the other information I found, is it only tell you how to configure Sharepoint to load a custom page. Anmol lists a simple 3 step process; however, step 2 was a lot more involved - at least for me - since this does not detail exactly on what you have to do to create these pages. I posted this question on Microsoft's forums (
my post), but - as seems to be usual - I owned my own thread and had to figure it out myself. As my post started to be the number 1 hit when searching for my problem, I figured I should document this for others that might run into the same issue. This seems fairly common, but maybe I am just so green I am the only one that needed help. Either way, I hope this helps someone.
So look at Anmol's post and get started, when you get to step 2 "Create your costom Access Denied Page in Layouts" this is what you do.
- Right-click on your project in the Solution Explorer and pull out the menu for Add then click Sharepoint "Layouts" Mapped Folder.
- Right-click on the folder automatically created under the Layouts folder in your project then pull out the Add menu, click New Item and then select Application Page.
- Copy the contents original page into your custom one (these are located in the hive under TEMPLATE/LAYOUTS/) and go crazy. Easy right?
Well, not really. What I found is that if I just built my page then I would either get a 403 Forbidden error or got redirected to the Access Denied page. These types of pages are considers
Safeguarded Application Pages, and appear to have some restrictions. First off, there can be issues with dynamic master pages and - I assume - other things that keep you from just laying into developing your custom page.
My first issue was I was really used to working with the code-behind; however, this only seemed to work if the Page directive Inherits itself. This also seemed to be the core reason for the 403 and Access Denied redirection. Once I changed this to inhert the original page - in my case Microsoft.SharePoint.ApplicationPages.RequestAccess - I could actually start seeing my changes. Of course, I could not use code-behind as any references to the page went all red and squiggly.
So you need to put all your functionality into the aspx page. Microsoft supported using code behind, but for these pages it seemed troublesome. This is a small bit of information, but this could have saved me a few days of work. I was never able to find any rules to what you could and could not do with these pages, and I would still love to know the hard facts. However, in the end I was able to get things working. We are still working around some issues related to using AD groups as Site Collection Administrators, which tends to list the AD group members as FullMask with the IsSiteAdmin equal to false, but I am hoping this is just an issue with our development environment.
To close up, this is the code I ended up using in my custom ReqAcc.aspx page:
Thanks to everyone for your support and help!
Update: For those that used the old version of the code (prior to 06/06/2012), here is the itemized changes:
- Replace SPUser reqUser = SPContext.Current.Web.CurrentUser; with string strUser = String.Format("{0}\\{1}", Environment.UserDomainName, Environment.UserName); and import the System namespace (<%@ Import Namespace="System" %>) You then need to convert this to a user object once you get into your RunWithElevatedPrivileges block with SPUser reqUser = thisroot.RootWeb.SiteUsers[strUser];. This is because the CurrentUser context was removed in these Safeguard Application pages as there was a bug allowing users to elevate privileges.
- I also now scope this as a WebApplication solution as restrictions were also put in place to disallow a Site Collection from updating the WebApplication. This makes sense, but limits us actually controlling this at a Site Collection.
- Added additional code I wrote for emailing site owners for access.