Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

Category Archives: SharePoint 2010

Repost – Get an SPUser Object From a List or Library in SharePoint Using PowerShell (Brandon Atkinson)

This blog was written by someone else but appears to not be available right now.  I got this on cache.  I am re-posting it cause I had the hardest time tracking this down.  On reflection, it makes sense that you’d have to do it very similar to C# code, but I was stuck for a while.

THIS POST WAS WRITTEN BY BRANDON ATKINSON not me.  Following is the link, however it appears to not be working right now.  sharepointbrandon.com/category/powershell

I was looking this up for SharePoint 2010, however it most likely works the same in 2013.

Get an SPUser Object From a List or Library in SharePoint Using PowerShell

At some point in your journeys with PowerShell and SharePoint you’ll need to pull a user from a list/library column and get an email address or login name, etc.  If you’ve never done this before it can be a little frustrating as you have to jump through a couple of hoops.  Let’s take a look at simply querying the list.

Let’s assume we have a library with a Name column and a user column called “Owner”.  Now we can easily query the library and pull out the “Name” with something like this:

# $docLib is a reference to a library and $spQuery is an SPQuery object with a CAML query
$spListItems = $docLib.GetItems($spQuery)

# Loop through items and write out info
foreach ($item in $spListItems) {

    Write-Host $item.Name
}

In this example we simply query the library and loop through the results, writing out the ”Name”.  But what if we needed to also write out the email address of the user column.  Here is where the hoops come in, fortunately they are small.

You have to perform some casts in order to get the actual SPUser object.  So taking the previous example, we can re-write it like this:

# $docLib is a reference to a library and $spQuery is an SPQuery object with a CAML query
$spListItems = $docLib.GetItems($spQuery)

# Loop through items and write out info
foreach ($item in $spListItems) {

     Write-Host $item.Name

    # Get the SPUser object from the column
$spFieldUser = [Microsoft.SharePoint.SPFieldUser]$item.Fields.GetField(“Owner”);


$spFieldUserValue = [Microsoft.SharePoint.SPFieldUserValue]$spFieldUser.GetFieldValue($item[“Owner”].ToString());

$user = $spFieldUserValue.User;

   # Pull out the user email
Write-Host = $user.Email
}

In this example we first have to create an SPFieldUser object using $item.Fields.GetField(“Owner”).  Once we have that reference we can use the GetFieldValue method to return an SPFieldUserValue object, which will contain the actual SPUser object.  Now we have access to all the properties on the user like Email, DisplayName, LoginName, etc.

As you can see, its pretty easy to get to the user object in SharePoint via PowerShell.

Enjoy!

Problems publishing SPD 2010 workflows and InfoPath forms

So I have been working on a project on SharePoint 2010 that is basically a list where the input form has been customized using InfoPath.  We decided to go this route using a list instead of using a Forms library because of the capability in workflow to send dynamic emails to multiple people if and only if it comes from a Person or Group column.

The problem came when I got up around 75 columns.  Suddenly, I found that I could no longer publish the InfoPath form.  I got “The SOAP Message could not be parsed.”  If I removed a column or 2, or got rid of a rule, or a data connection, suddenly I could publish again.  But there seemed to be this magic, undefined breaking point that I couldn’t exceed before I’d start running into problems.

So now, I had to implement the SharePoint Designer 2010 workflow.  I knew I needed probably 3 workflows, one for create, and 2 for on change.  However, once I got past 2 or 3 IF statements, I could no longer publish the workflow either.  It would save, but on publish it would give me “Unexpected error on server associating the workflow.”  Now mind you, I did NOT have any Start Approval Process or similar actions – one of those content-type actions.  These were just simple conditions, emails, and setting of a few fields.

I found that I had to break my work up in to 16 workflows.  Each one started out with a condition to see if it should run.  This was getting ridiculous, and finally I got to a point where I couldn’t get any more granular and still it wouldn’t publish.  As suggested by some posts, I tried increasing $app.UserDefinedWorkflowMaximumComplexity in PowerShell (where $app is the SPWebApplication in question) and I also tried adding a timeout value to the  web.config.

 

So this whole time, I’m working from my contractor’s computer.  The SharePoint system in question was exposed over the internet using a UAG appliance for firewall and authentication.  Normally, this is not an issue, as I can usually get everything done I need to. On a whim I decided to open everything up from the customer’s computer, which incidentally has a VPN connection to their system, and voila, I can publish InfoPath forms and Workflows now with no issue.  <sigh>  It’s the little things that get you.

 

http://fairulshahrizat.blogspot.com/2012/12/sharepoint-designer-2010-workflow-error.html

 

CAML vs LINQ to SharePoint 2010 – Performance

Summary

LINQ to SharePoint is really cool.  There’s lots of things you can do with it that you can’t do with CAML alone.  It’s much easier to read, to work with, to maintain, and because of the strongly typed objects, you find more of your bugs at compile time than at run time like you have to with CAML.

However, let me just say LINQ to SharePoint performance STINKS big time when you’re doing relatively simple comparisons that you could just as easily do in straight CAML.

My Test Case

My test case is a program that reads in information from 2 different stored procedures in SQL, separates out the data, loads them in to 4 SharePoint Lists, and looks for invalid corresponding data in another SharePoint list.  Basically 1 list is a master Valid Codes list, 1 list is a master Invalid Codes list, and the other 2 lists are distinct lists of categories and departments that relate to those codes, coming from the denormalized SQL queries.  Basically you first put all the valid codes in to the valid list, then you go through the invalid codes and see if there are matching codes in the valid codes list, and if so delete them, then check for corresponding data in another SharePoint list that has those code values in it, then put the invalid code in the invalid list.

The results

Running the program using straight CAML queries and no LINQ took 3 hours 11 minutes.

Running the program using all LINQ to SharePoint and no CAML took 9 hours 2 minutes.

 

I’m really bummed, I liked LINQ.  Now I’m not so sure.

Redesign your coded InfoPath 2010 Form to handle 1000’s of records

Scenario

Let’s say you have an InfoPath form with Code Behind where you want to have the user paste in thousands of Job Codes or Organization Codes or Cost Codes into a text box, and then they would press a button and it would validate those records – the Codes – against a System of Record (like a list in SharePoint) and then add the records into a Repeating Table in your InfoPath form.  You want to do this because your Codes have metadata associated with them that you either want to display and/or have the user update, and you want to have conditional formatting and buttons on each record to make it look and function the way you want it to.

You will quickly find that doing this in a Web Browser-enabled form is near to impossible, requires too many configuration settings to change in various places, and performs really sluggishly any time you take an action on the form like clicking a checkbox or pressing a button that only runs rules.  You’ll run into InfoPath settings in Central Admin that you have to configure, and you’ll run in to the dreaded 6-minute default timeout of IIS pages waiting for responses from the server.

The first step I took, on the advice of Doug my co-worker, Architect extraordinaire, was to offload all of the individual item processing – the validation against the SOR – off to a custom SOAP web service.  This did the heavy lifting – I passed in a comma-separated string of all of the Codes (which is why it had to be a SOAP service and not a REST service – too much data to pass in a URL), and then it validated the codes.  Finally, I had the web service create an XML Document with exactly the structure of my repeating table in InfoPath, fill it in with all of the Codes and their metadata, and pass that Outer XML as a string back to the form.  I used XPathNavigator to then insert the XML directly into the form.

What I found, through constructing a timing debug string, was that my Web Service call was taking 2 – 4 seconds.  Inserting the XML and waiting for the XML to render into HTML was taking 4 – 10 minutes, depending on how many codes I had.  It was completely unacceptable as I had clients who needed to put in 2000 codes.  My form got sluggish at 300 and outright unusable over 700.

Solution

So I went back to Doug, which is what I usually do when I’m stuck on a problem.  I explained where the bottleneck was and confirmed my suspicions by reading up on Performance and InfoPath on a series of 6 Microsoft blogs.  Doug said, since the bottleneck is the size of the XML in the form, you have to get the XML out of the form, into some other storage mechanism.  Allow the form to hold no more than 100 items at a time in the repeating table, and implement Paging of sorts to get at the other 1900 records.

Wow, sounded like a great idea so I tried it, and so far it seems to be working.  Because I’m getting paid to do this, I can’t give you all of the code.  However, here’s the general steps you will need to take in order to accomplish this idea:

  1. Create a SOAP Web Service.  I had done this before and put it in ISAPI, but you have to do some extra manual work to get it to work there.  I found that if you add it directly to a subfolder under the Layouts directory, you can call it from there without any extra work.  The Web Service will need to have 4 methods or so:
    1. AddSQLDataGetPageXML(string identifier, int pageNum, string codes, string extraMetadataIfNecessary, string xmlChanges, string ns)
      This method is responsible for

      1. (a) getting existing data out of SQL for that identifier, or creating a new identifier if none exists;
      2. (b) copying the existing data, if any, into a  Generic List of custom objects representing your Code and its metadata;
      3. (c) processing  any changes that the customer has made to the repeating table of the 100 or so items that are showing (you need to capture your changes before blowing away and recreating the 100 items);
      4. (d) doing the actual Validation of the Code against your SOR;
      5. (e) adding the valid (and Invalid) codes to both the Generic List, as well as adding the record to the SQL table;
      6. (f) sorting your Generic List by RowNum or some kind of numbering identifier that you have added to the custom object;
      7. (g) iterating through the entire Generic List, gathering statistics of things like how many are invalid, how many total, etc., and taking only the ones in “that page” and turning them into an XML Element that you insert into a generic XmlDocument object that you created at the top of the method (using the ns parameter which is the namespace of the “my” prefix of your InfoPath form);
      8. (h) and finally returning a custom Return Object that holds both the statistics that you wanted to capture, as well as the XML which is the OuterXML of the xml document.
    2. GetPageXML(string identifier, int pageNum, string xmlChanges, int oldPageNum, string ns)
      This method is responsible for

      1. (a) getting existing data out of SQL for that identifier;
      2. (b) copying the existing data, if any, into a  Generic List of custom objects representing your Code and its metadata;
      3. (c) processing  any changes that the customer has made to the repeating table of the 100 or so items that are showing (you need to capture your changes before blowing away and recreating the 100 items) – NOTE HERE – this has to handle deletions too, so you need some special manipulation of comparing what is (or rather is NOT) in the XML string of changes, versus what IS in your Generic List that you read out of SQL;
      4. (d) sorting your Generic List by RowNum or some kind of numbering identifier that you have added to the custom object;
      5. (e) iterating through the entire Generic List, gathering statistics of things like how many are invalid, how many total, etc., and taking only the ones in “that page” and turning them into an XML Element that you insert into a generic XmlDocument object that you created at the top of the method (using the ns parameter which is the namespace of the “my” prefix of your InfoPath form);
      6. (f) and finally returning a custom Return Object that holds both the statistics that you wanted to capture, as well as the XML which is the OuterXML of the xml document.
    3. ProcessChanges(string identifier, string xmlChanges, string ns, int startItem, int stopItem, SqlConnection conn, List<YourCustomCodeObject> allCodesFromSQL)
      This is the method that you call from the first 2 methods to process your existing changes.  Remember, if the user changes something on the current page, and then you switch to another page, you need to make sure that you capture what has been changed on the current page.
    4. ReValidateCode(string code, string otherMetadataHere)
      This method is responsible for

      1. (a) reading in the code and finding it / validating in against your SOR;
      2. (b) creating a custom Return Class that can hold your Code’s data and metadata, and whether or not it’s valid;
      3. (c) returning the custom Return Class (your InfoPath form handles the changing of the repeating table item in the repeating table)
  2. Hook this up to your InfoPath Form
    1. There’s too much involved to go into here, but you need to basically do the following:
      1. Have a SOAP web service Data Connection, and methods to set values into and Execute that connection.
      2. Have a text box to paste the Codes into, and a method to take those codes, wrap them up and send it to the SOAP web service
      3. Have a repeating grid with all of your fields, metadata fields, buttons (such as ReValidate), and conditional formatting
      4. Have buttons for Previous and Next that have conditional formatting on them to only show if it is valid to show them (i.e. don’t show Previous if you’re on page 1)
      5. Have a MoveToPage method that gets called from the buttons and optionally some other places (like a delete all on this page kind of method)
  3. Create your Workflow (a custom Console Application probably) that handles the business processes of the form
    It will do the following:

    1. Loads in the InfoPath form, which you have created a class for using the XSD of the InfoPath Form (Export to Source Files to find the XSD)
    2. Checks to see if it’s already processed
    3. Looks for the Identifier in the Form that got stored when you were using the form
    4. Looks in SQL using the Identifier to get all the records
    5. Processes the records
    6. Deletes the records out of SQL

 

InfoPath 2010 – Registering of form templates deployed through object model is not supported

In an earlier post, https://suehernandez.wordpress.com/2013/11/22/deploy-infopath-2010-with-code-as-solution-feature/, I explained how to implement an InfoPath 2010 form as a Feature in SharePoint 2010.

As I was trying to deploy this feature, I kept getting the error:

Registering of form templates deployed through object model is not supported

 

It turns out that what happened is that up until that point I had been deploying my InfoPath forms through Central Administration.  It’s easiest and usually quickest to do it this way (although once you deploy the same form like 50 times it starts getting really slow to deploy).

But I had not changed the “Identifier” per se of the form.  Let me show you a couple of pictures that shows what I’m talking about:

Form Template Properties buttonForm Template Properties dialog

Where it says “urn:schemas-microsofr-com….” that is the identifier of the form.  If you publish the form, and then copy it and change the name of it, it does NOT change the identifier.  So if you’ve already got that ID in Central Admin under Manage Form Templates, and then you try to deploy the same form as a feature, you get this error message.

One of the ways you can fix this is to either manually change this URN ID, or just open up the form in InfoPath Designer in design mode and do a Save-As.  It will automatically change your ID to match the new name of the form.

In the mean time, delete the form from Central Admin and try your Solution Deployment again.

Deploy InfoPath 2010 with Code as Solution-Feature

This one will be somewhat long of a post.   Here we will examine deploying an InfoPath 2010 form with CodeBehind as a custom Feature to SharePoint 2010.

  1. Change the properties of your form to Full Trust

    Full Trust

    Full Trust

  2. Publish your form to a SharePoint Server

    Publish Step 1

    Publish Step 1

  3. Choose your URL to the site where you’re going to be using the file.  Note that it will put this form in the Form Templates (url is FormServerTemplates) library at the top of your Site Collection

    Publish Step 2

    Publish Step 2

  4. Make sure that it is checked off to enable the form to be filled out by a browser, and choose Administrator Approved Template

    Publish Step 3

    Publish Step 3

  5. Choose the location on your desktop or local computer or shared drive where you want to save the Published form template to

    Publish Step 4

    Publish Step 4

  6. Create your Visual Studio solution.  Create it as a SharePoint 2010 Empty SharePoint Project

    Create new Empty SP Project

    Create new Empty SP Project

  7. Choose to deploy as Farm Solution

    Deploy as Farm Solution

    Deploy as Farm Solution

  8. Add a Feature to the project

    Add Feature

    Add Feature

  9. Rename the feature to something that makes more sense, so you can more easily find it later through PowerShell or in the 14 hive

    Rename Feature

    Rename Feature

  10. Open the feature in Visual Studio into Design view and set the properties of the feature at the top.

    Set Properties of Feature

    Set Properties of Feature

  11. Add a Module to the project

    Add Module

    Add Module

  12. Rename the module and remove the Sample.txt file

    Rename Module and remove Sample txt

    Rename Module and remove Sample txt

  13. Add your Module Files.  Here you will be adding the published form that you completed in Step 5 here, as well as the dll.  (You can get the .dll by going into design view in InfoPath, pressing Publish, and pressing Export Source Files – give it a folder to put the source files in, and find your .dll in there)

    Note that I have a little naming change here versus what I have in step 5.  I am showing you a “real” project file and had to blot out part of the name.  But this will be your published file – not the file you use to make the changes to the form.

    Add Module Files

    Add Module Files

  14. Open the Elements file in the NewFormModule.  You will have to make changes.  Here’s the file as it looks before:
    Elements Before

    Elements Before

    and here’s the file as it looks after:

    Elements After

    Elements After

  15. Open back up the Feature in Design mode, and press the button to switch it to Manifest mode.  Expand Edit Options and add the 2 properties shown below, with your Feature Name and your Published InfoPath form name as the values:

    Update the Manifest

    Update the Manifest

  16. Get the Properties window up for the Feature.  You will be adding a Receiver Assembly and a Receiver Class
    ReceiverAssembly=”Microsoft.Office.InfoPath.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”
    ReceiverClass=”Microsoft.Office.InfoPath.Server.Administration.XsnFeatureReceiver”

    Feature Props add Receiver info

    Feature Props add Receiver info

  17. OPTIONAL:  Add Activation dependency.  NOTE:  if you are going to mark your feature as Hidden=TRUE, you cannot add this dependency.  We will discuss later why you may want to make your feature Hidden.
    <ActivationDependency FeatureId=”C88C4FF1-DBF5-4649-AD9F-C6C426EBCBF5″/>

    Feature Activation Dependencies

    Feature Activation Dependencies

  18. Now in your solution explorer, you will want to highlight the Elements file and get the properties.  Clear out the Deployment Path and make sure it’s of type ElementManifest

    Elements Properties

    Elements Properties

  19. Next highlight the XSN published form and get its properties.  Clear the Deployment Path and make sure it is of type ElementFile

    Form Properties

    Form Properties

  20. Repeat the previous step for the DLL file
  21. Open up your Feature in Design mode just to look at it.  Your Manifest file should look similar to the following (if you added the Activation Dependency):

    Manifest After

    Manifest After

Making the feature Hidden

The problem with adding the published file to Central Administration – Managed Form Templates is twofold:  (1) It puts in every version you create into the manifest; and (2) It makes the feature available to EVERY site collection in your Farm.  Similarly, if you don’t make the feature Hidden, all site collections will be able to see this feature.

Depending on your project, you may opt to hide the feature so that no one can turn it on, and only activate it to the Site Collection using PowerShell.

If you choose to make your feature hidden, you cannot have the Activation Dependency of the Enterprise Features.  Just realize this means is that if you don’t have Enterprise Features turned on, this Content Type will not work.

Here’s a picture of where you’d make the feature hidden – it’s just another one of the properties on the feature:

Is Hidden is True

Is Hidden is True

Deploy the feature, Activate it, and Use it

Once you deploy the solution package (.WSP file) and activate it to the site collection you’re going to use it in, it will now be a Content Type that you add to your Form Library.  Any fields which you promoted in the Publishing process will automatically be added to your Form Library and will sync to the data in the form.

References

I got bits and pieces from a lot of places, but here’s some main ones that I got a lot of detail from:

SPSecurity.RunWithElevatedPrivileges and Console Applications

Wow – I should have recognized this a LONG time ago.  But hey, I’m human.

When you use code such as the following:

SPSecurity.RunWithElevatedPrivileges(delegate()
{

     using (SPSite site = newSPSite(urlToSite))
{

         using (SPWeb web = site.OpenWeb())
{

             // Do something
         }
}
});

if you use that code in a Web Part, Application Page, or I think Event Handler – i.e. any code that has Context inside SharePoint – the code runs as expected and will elevate your permissions to the Application Pool account.

However, if you try to use that same code in your Console application, and let’s say you update a list item, and you run it either in debug mode or just “regularly” by double-clicking the exe file, you will notice that last modified by is YOU – NOT your Application Pool account.

This is because there’s no SharePoint context.

Instead, you can create a batch file (like if you have input parameters) and use the “RunAs” method that Windows gives you (example, in Windows Server 2008 R2 you have to hold down Shift and Right-click the batch file, in order to get the Run As option).  If you’re setting up the job as a scheduled task through Windows, you simply have to set the account to the Application Pool account and specify that it should run whether or not the account is logged in.

SharePoint Designer 2010: You do not have permission to do this operation

SharePoint Designer 2010:  You do not have permission to do this operation
–  or  –
Master Page Error:  The Master Page File ‘https://yourserver.com/yourPath/yourSC/_catalogs/masterpage/v4.master‘ cannot be loaded.  Attach a different Master Page, or correct the problem in Code view.

Environment Specifications: 

  • SharePoint Designer 2010. 
  • Windows XP OR Windows 7
  • All of the problem sites were sites that had been migrated from 2007
  • All of the users having problems had FULL CONTROL of the sites they were trying to get into
  • When using a VPN, we always get both errors
  • When outside of the network completely (our site is accessible on the internet with credentials) then we sometimes do not get the first error, but we can’t put the page into Design mode because of the second error.

So we figured out that it had something to do with permissions at the Master Page Gallery at the top of the site collection.  But what was wierd was that any new subsite we created did NOT have this problem.

Well, to make a long story short, we were right, and it’s a “feature”.  KB Article http://support.microsoft.com/kb/2592376 from Mirosoft explains that you have to be one of 3 different permissions, at the site collection level, in order to edit in designer:

  • Site Collection Administrator
  • Designer
  • Owner

Now we whittled it down to this – you don’t have to be in the “Designers” group at the top of the site collection – but if your configuration of the Master Pages Gallery was untouched, that WOULD work (because Designers were set as Design level in the Master Pages Gallery).  Basically the short of it is, you need to HAVE DESIGN OR GREATER PERMISSIONS ON THE MASTER PAGE BEING USED (or just the whole gallery) at the TOP OF THE SITE COLLECTION.

Turns out the reason for these varying behaviors is this:  In 2007, new sites would automatically inherit their master pages from their parents.  If you made no changes to that, it inherited right up to the top.  So it’s looking at the master page from the top, in those circumstances.  And as we’ve seen here, if you don’t have Design permissions to it, it doesn’t work.

In the case of a new 2010 site (just a regular Team Site), the new 2010 behavior is to use the Master Page in the gallery it has in its own site.  And since the user is an Administrator (Full Control) of that site, then no problem getting to the Master Page!

Just so you know, we did end up calling in a Microsoft Ticket on this one, to confirm our findings.  We knew at that point it had to be something about permissions at the top, but we didn’t know why.  They confirmed it, providing the KB article I mentioned earler, and the explanation you see in this article.

SharePoint 2010 Crashes after Service Pack 1 SP1 and August 2012 Cumulative Update CU

Well we just applied Service Pack 1 (SP1) to our SharePoint 2010 server – both foundation and server – and then the August 2012 Cumulative Update (CU) server only (that’s what we were told to do by Microsoft – your situation may differ).

When we tried to browse to the root web after all of this was done (we had to run a few -force commands using command line PSConfig.exe after the GUI failed), and our site gave us a NASTY 500 error.

I tried to go to Inetmgr (Internet Information Services Manager) and tried to take a look at where the log files are kept, I couldn’t remember.  So it pops up with this message saying there’s something wrong with our web.config – specifically the system.web/pages node was duplicated.

So I took a backup of the web.config and looked, and indeed it had a duplicate entry.  Now here’s the wierd part, here was the dup:

 <pages enableSessionState="false" enableViewState="true" enableViewStateMac="true" validateRequest="false" pageParserFilterType="Microsoft.SharePoint.ApplicationRuntime.SPPageParserFilter, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" asyncTimeout="7">
      <controls>
        <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=1.0.61025.0, 
Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      </controls>
      <namespaces>
        <remove namespace="System.Web.UI.WebControls.WebParts" />
      </namespaces>
      <tagMapping>
        <add tagType="System.Web.UI.WebControls.SqlDataSource, System.Web, Version=2.0.0.0, Culture=neutral, 
PublicKeyToken=b03f5f7f11d50a3a" mappedTagType="Microsoft.SharePoint.WebControls.SPSqlDataSource, 
Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      </tagMapping>
 </pages>

Be very careful that this is the one referencing the 12 version assembly – not the 14 version.  So I removed these lines and it now worked.

SharePoint 2010 custom Access Denied with Modal Dialog

There are lots of posts out there that explain to you how to link a custom Access Denied page to the system so that it displays your page instead of the OOB one.  Even a couple of posts on HOW to create that page.

In my case, I wanted to do this:

Custom Access Denied Page

I wanted to:

  • Use my own logo
  • Allow for signing in as a different user
  • List all of the Site Administrators for the site you’re trying to reach
  • Display a custom “Request Access” page in a Modal Dialog box

 This turned out to be a bit tricky.

Preparations

  • I used an application page in Visual Studio 2010, but instead of inheriting from LayoutsPageBase, I used UnsecuredLayoutsPageBase.  That way you don’t have to override the 4 methods to allow the user into the page.
  • I changed the master page reference to MasterPageFile=”~/_layouts/simple.master”

Use my own logo

I read that all you had to do was to override your logo in the PlaceHolderPageImage content placeholder.  So I dutifully put my image there, even calling it the same ID as the one it expected: <img id=”onetidtpweb1″ src=”/_layouts/images/Custom/Logo.png” alt=”Logo”/>.  However, that did not seem to work.  So I used JQuery to show my logo after-the-fact (it’s a workaround, not a true solution – don’t know why the placeholder didn’t work).

In PlaceHolderAdditionalPageHead, I had this:

<asp:Content ID=”Content2″ ContentPlaceHolderID=”PlaceHolderAdditionalPageHead” runat=”server”>
   <meta name=”Robots” content=”NOINDEX “/>
   <meta name=”SharePointError” content=”1″/>
   <script language=”javascript” src=”/_layouts/images/Custom/jquery-1.6.1.min.js” type=”text/javascript”></script>
   <script type=”text/javascript”>
            $(document).ready(function() {
                  $(‘div.s4-simple-iconcont > img’).attr(‘src’, ‘/_layouts/images/Custom/Logo.png’);
            });
   </script>
</asp:Content>

 Allow for signing in as a different user

Turns out that CloseConnection.aspx apparently silently redirects them to the Access Denied page?  Not sure exactly, but what I know is that if I hadn’t copied the code from SharePoint’s access denied page using Reflector, it wouldn’t sign me in as a different user.  So use reflector on AccessDenied, and copy CloseConnection(), Send403(), SendResponse(), IsBrowserRequest(), matchLegacyFrontPageUa(), HandleLoginAsAnotherUser(), and LogInAsAnotherUser() as well as some of thePage_Load code – the part that checks the query string and calls LogInAsAnotherUser or CloseConnection.

I also had to reconstruct the link that the user gets on the page.  I used the following code:

string urlForSignIn = web.ServerRelativeUrl;

if (!urlForSignIn.EndsWith(“/”))
{
      urlForSignIn += “/”;
}
urlForSignIn += “_layouts/closeConnection.aspx?loginasanotheruser=true”;
urlForSignIn = urlForSignIn.Replace(“/”, “\\u002f”);

lnkSignInAsDifferentUser.NavigateUrl = “javascript:LoginAsAnother(‘” + urlForSignIn + “‘, 1)”;

List all of the Site Administrators for the site you’re trying to reach

Immediately after the querystring inspection, create 2 variables to hold the Site Collection ID and the Web ID.  NOTE:  These are the only 2 things the code can get on these objects, if the user doesn’t have access to the site.  In other words, you can’t access SPWeb.Title – that throws the page off to a 403 Unauthorized page.  So you need to use SPSecurity.RunWithElevatedPrivileges and re-open your Site and Web objects securely, before you can get anything interesting.

When trying to get your admins, all I did was use foreach(SPUser in web.AllUsers) and then individually check web.DoesUserHavePermissions(user.LoginName, SPBasePermissions.FullMask).  Keep in mind:  you’re going to get service accounts and such when you loop through these users.  I basically ignored the service accounts I knew about, and limited the results to my Domain (i.e. to get rid of SHAREPOINT\SYSTEM).  I also did not take the account if it didn’t have an email address attached.

Display a custom “Request Access” page in a Modal Dialog box

So this one was wierd.  I had to use SP.UI.ModalDialog.showModalDialog(options) to pop up the Request Access window.  However, in order to call that function from your link, you have to include /_layouts/SP.UI.Dialog.js.  As it turns out, that in turn relies on a couple of other JS files.  But normally all you do is put the SP.UI.Dialog.js reference in a SharePoint:ScriptLink tag, and add a FormDigest tag as well, and then you can get to your modal dialog function.

However, it turns out BOTH the ScriptLink and the FormDigest tags actually throw the page into a 403 Forbidden state.  So I had to take those out.  But then the modal dialog didn’t work.  I tried to no avail to manually add a bunch of JS references to the page, but kept getting Script errors.

So I found an article about something else that led me to the solution: http://howtosharepoint.blogspot.com/2010/09/programatically-hide-show-status-bar.html.  Basically what he does is programmatically register the script links AFTER the UI is loaded, by adding this into the Page_Load function:

ScriptLink.RegisterScriptAfterUI(this.Page, “core.js”, true, false); 
ScriptLink.RegisterScriptAfterUI(this.Page, “CUI.js”, false, true); 
ScriptLink.RegisterScriptAfterUI(this.Page, “SP.js”, false, true);

This seemed to make the modal dialog errors go away, although periodically I am getting another script error I have to investigate.

Now keep in mind that the Request Access page has to also be based off of Simple.master.  So you’re going to get the same sections of the page, including the image, the header, and the “Go Back To Site” link at the bottom.  The good news is, though, that the Go Back to Site link just closes the modal dialog box.

And finally, when you’re done with your Request Access form and want to return to the Access Denied page, just add this to your code behind:

Page.ClientScript.RegisterClientScriptBlock(typeof(CustomRequestAccess), “closeDialogScript”, “SP.UI.ModalDialog.commonModalDialogClose(1, null);”, true);

 Cheers,

Sue