Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

Category Archives: Visual Studio 2010

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.

Advertisements

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.

Stupid Mistake – stored procedure expects parameter which was not supplied

I made a boneheaded mistake today and wanted to write it down so I don’t make the same boneheaded mistake again.

I was writing a program for gathering some custom metrics in SharePoint, to be stored in a custom SQL table.   I had written a stored procedure and properly passed in all parameters, or so I thought.

SqlCommand cmd = new SqlCommand("NameOfMyStoredProcedure", mainConnectionObj);

cmd.Parameters.Add(new SqlParameter("@TimeStamp", startTime));
cmd.Parameters.Add(new SqlParameter("@WebURL", web.Url));
... [more parameters here]

I kept getting “Stored Procedure NameOfMyStoredProcedure expects parameter @TimeStamp which was not supplied”.  I double-checked and triple-checked to make sure I had the syntax right.  I then went down the route of thinking that TimeStamp was a reserved word.  But then I tried something – I reversed my first 2 parameters, and sure enough, it was complaining about @WebURL this time.

Turns out it was extremely simple.  I just needed to specify the CommandType, telling it that it was indeed a stored procedure.

SqlCommand cmd = new SqlCommand("NameOfMyStoredProcedure", mainConnectionObj);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@TimeStamp", startTime));
cmd.Parameters.Add(new SqlParameter("@WebURL", web.Url));

That was it. It then worked like a charm!

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

SharePoint 2007-2010 Web Part Migration Planning

Well, I know I need to post the rest of my “Gantt Chart” blogs, but let me side track for a minute…

We are migrating one of our clients from MOSS 2007 to SharePoint 2010 and we wanted to know what web parts are being used on what sites.  I found a multi-step process you’ll have to go through, and it will take a while, but you CAN get a 70%-90% list.

GET THE WEB PARTS PER WEB

The first step is to use the stsadm command “enumallwebs”.  This apparently has a parameter/switch called “includewebparts” which apparently was added during an Octbver 09 Cumulative Update.

stsadm -o enumallwebs -includewebparts > MyOutputFileName.txt

This will construct for you an XML file that looks approximately like below:

<Databases>
  <Database SiteCount="2" Name="your_sharepoint_content_database_name" DataSource="server">
    <Site Id="12345678-716d-44a3-a973-fffffffffff" OwnerLogin="domain\spadmin" InSiteMap="True">
      <Webs Count="2">
        <Web Id="12345678-17d9-4870-8d94-fffffffffff" Url="/sites/test1" LanguageId="1031"
             TemplateName="STS#0" TemplateId="1">
          <WebParts>
            <WebPart Id="e60f6c95-e86c-4717-2c0d-6d8563c9caf7" Count="1" Status="Missing" />
            <WebPart Id="293e8d0e-486f-e21e-40e3-75bfb77202de" Count="103" Status="Missing" />
            <WebPart Id="b9a7f972-708a-cd77-4ffd-a235dfed5c38" Count="1" Status="Missing" />
            <WebPart Id="2242cce6-491a-657a-c8ee-b10a2a993eda" Count="182" Status="Missing" />
          </WebParts>
        </Web>
        <Web Id="12345678-3bda-4109-aacd-fffffffffff" Url="/sites/test1/subweb1" LanguageId="1031"
             TemplateName="STS#1" TemplateId="1">
          <WebParts>
            <WebPart Id="ce9aa113-48cf-ddee-0c03-597445e5b7ab" Count="1" Status="Missing" />
            <WebPart Id="293e8d0e-486f-e21e-40e3-75bfb77202de" Count="9" Status="Missing" />
            <WebPart Id="2242cce6-491a-657a-c8ee-b10a2a993eda" Count="7" Status="Missing" />
          </WebParts>
        </Web>
      </Webs>
    </Site>
    <Site Id="12345678-730c-46fd-a114-fffffffffff" OwnerLogin="domain\spadmin" InSiteMap="True">
      <Webs Count="1">
        <Web Id="12345678-7cd6-447d-8107-fffffffffff" Url="/sites/test2" LanguageId="1031"
             TemplateName="STS#0" TemplateId="1">
          <WebParts>
            <WebPart Id="d55b3b6b-6281-707b-73d0-0c49581475ad" Count="1" Status="Missing" />
            <WebPart Id="293e8d0e-486f-e21e-40e3-75bfb77202de" Count="83" Status="Missing" />
            <WebPart Id="9f030319-fa14-b625-4892-89f6f9f9d58b" Count="1" Status="Missing" />
            <WebPart Id="c9b34b5d-bf06-dc91-d23e-94ecad31cd0a" Count="2" Status="Missing" />
            <WebPart Id="2242cce6-491a-657a-c8ee-b10a2a993eda" Count="83" Status="Missing" />
            <WebPart Id="669602d9-e116-ccb8-eea3-e37ad589b14b" Count="1" Status="Missing" />
            <WebPart Id="f5897322-ddd4-c990-d012-f9d4fe2180ad" Count="2" Status="Missing" />
          </WebParts>
        </Web>
      </Webs>
    </Site>
  </Database>
</Databases>

* You may have every web part in a “Missing” status – I think you need to run this command on a WFE that has ALL the features installed properly.  Please see one of the following posts for more information:

GET A LIST OF WEB PARTS AND THEIR ASSEMBLIES AND CLASSES

Well see that WebPart “ID” there?  That’s actually not an ID that you can use, even searching in the database, to find the name of the web part.  That is a made up GUID-that’s-not-a-GUID that just happens to “fit” inside a GUID.  More info in the next heading section.

For now, just go to your “Add a Web Part” page, or

https://yourserver.someurl.com/_layouts/newdwp.aspx

From here, you’re going to get a list which shows you 2 pieces of information you need: the full Class Name and the full Assembly Name. 

If you have Excel installed, you “should” be able to right-click inside the list and say “Export to Microsoft Excel”.  From Excel, go ahead and delete columns A and C (Blank and “File Name”).  Now we need to turn this file into an XML file so we can both crunch it through a program, as well as transform it using XSLT.  I personally had a hard time turning it into XML so what I did was I took this schema and saved it as .xml and opened it in Excel:

<WebPartTypeIDs>
   <WebPartTypeID>
      <AssemblyName>Assembly A</AssemblyName>
      <TypeName>Class A</TypeName>
      <GeneratedWebPartTypeID>GUID A</GeneratedWebPartTypeID>
   </WebPartTypeID>
   <WebPartTypeID>
      <AssemblyName>Assembly B</AssemblyName>
      <TypeName>Class B</TypeName>
      <GeneratedWebPartTypeID>GUID B</GeneratedWebPartTypeID>
   </WebPartTypeID>
</WebPartTypeIDs>

Then I copied and pasted everything from the Exported spreadsheet into this XML file.  I think you have to make sure that you have something in the GeneratedWebPartTypeID for at least one of the rows, in order to make sure it doesn’t get rid of that column when you save.

If you’re better at Excel (most will be) and you know an easier way, please let me know.

RUN THE OUTPUT THROUGH A PROGRAM

Next we have to get the generated ID.  Here’s the crazy part.  Here’s what Microsoft does:

  • Take the Assembly Name plus “|” plus the Class / Type Name and put it together in one string
  • Change the string to a Byte array
  • Run the Byte array through an MD5 Hash

So I wrote a little program to ingest in the xml file that we created through Excel.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Xml;
using System.Xml.Xsl;
using System.IO;

namespace GetWebPartTypeIDs
{
 public partial class Form1 : Form
 {
  public Form1()
  {
   InitializeComponent();
  }

  private void button1_Click(object sender, EventArgs e)
  {
   try
   {
    Cursor = Cursors.WaitCursor;
    XmlDocument xDoc = new XmlDocument();
    xDoc.Load(textBox1.Text);

    XmlNodeList nodes = xDoc.SelectNodes("//WebPartTypeID");
    foreach (XmlNode node in nodes)
    {
     string assemblyName = node.SelectSingleNode("AssemblyName").InnerText;
     string typeName = node.SelectSingleNode("TypeName").InnerText;

     string s = GenerateID(assemblyName, typeName);

     XmlNode idNode = node.SelectSingleNode("GeneratedWebPartTypeID");
     idNode.InnerText = s;
    }

    xDoc.Save(textBox1.Text);
   }
   catch (Exception ex)
   {
    richTextBox1.Text = ex.ToString();
   }
   finally
   {
    Cursor = Cursors.Default;
    richTextBox1.AppendText(Environment.NewLine + Environment.NewLine + "*************** DONE **************");
   }
  }

  private string GenerateID(string assemblyName, string typeName)
  {
   string s = assemblyName + "|" + typeName;
   byte[] bytes = Encoding.Unicode.GetBytes(s);
   byte[] b = new MD5CryptoServiceProvider().ComputeHash(bytes);
   return new Guid(b).ToString();
  }
 }
}

ADD THAT GENERATED XML OUTPUT TOGETHER WITH THE STSADM OUTPUT

So now, put the 2 XML’s together, with some made-up root, doesn’t matter what it is.

<DatabasesAndWebPartTypeIDs>
   <WebPartTypeIDs>
      ...
   </WebPartTypeIDs>
   <Databases>
      ...
   </Databases>
</DatabasesAndWebPartTypeIDs>

RUN IT THROUGH XSLT

I am going to include 2 XSLT’s here.  The first one uses XSLT 2.0 so you might need to download the Saxon Parser (http://saxon.sourceforge.net/) if you don’t have XML Spy or if your file is too big for XML Spy to handle, which is what happened to me (250 MB xml file).

Here’s one that outputs an HTML file.  The nice thing about it is it goes “both ways” – it shows on the top which web parts are not being used and it shows on the bottom which web parts you couldn’t ID.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
 <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
 <xsl:template match="/">
   <html>
    <body>
     <h1>By Web Part Name - Unused are highlighted in Red</h1>
     <xsl:for-each select="//WebPartTypeID">
      <xsl:sort select="AssemblyName" />
      <xsl:sort select="TypeName" />
      <xsl:variable name="generatedId" select="GeneratedWebPartTypeID" />
      <xsl:variable name="count" select="count(//Databases//Web[WebParts/WebPart/@Id = $generatedId])" />
      <font face="Courier New" style="font-size:12px">
       <xsl:if test="number($count) = 0">
        <xsl:attribute name="color" select="'Red'" />
       </xsl:if>
       <xsl:value-of select="substring-before(AssemblyName, ',')" /> | <xsl:value-of select="TypeName" />:
<xsl:value-of select="$count" />
      </font><br/>
     </xsl:for-each>
     <hr/>
     <h1>By Web Part Type Id - Those with under 15 entries show the webs they're in</h1>
     <xsl:for-each-group select="//WebPart" group-by="@Id">
      <xsl:sort select="current-grouping-key()" />
      <xsl:variable name="count" select="count(//Web[WebParts/WebPart/@Id = current-grouping-key()])" />
      <font face="Courier New" style="font-size:12px">
       <xsl:value-of select="current-grouping-key()" />: <xsl:value-of select="$count" /> --&gt;
<font color="Red"><b><xsl:value-of
select="substring-before(//WebPartTypeID/AssemblyName[../GeneratedWebPartTypeID = current-grouping-key()], ',')"
 /></b> | <xsl:value-of
select="//WebPartTypeID/TypeName[../GeneratedWebPartTypeID = current-grouping-key()]" /></font>
      </font>
      <br/>
      <xsl:if test="number($count) &lt; 15">
       <xsl:for-each select="//Web/@Url[../WebParts/WebPart/@Id = current-grouping-key()]">
        <font face="Courier New" style="font-size:12px">..........<xsl:value-of select="." /></font><br/>
       </xsl:for-each>
      </xsl:if>
     </xsl:for-each-group>
    </body>
   </html>
 </xsl:template>
</xsl:stylesheet> 

The next XSLT is 1.0 compliant.  This one makes a “flat” xml file that you can then pull into Microsoft Access and do Pivot stuff on it to find out what web templates are being used, etc.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
 <xsl:template match="/">
  <ReportRoot>
   <xsl:for-each select="//Database">
    <xsl:variable name="db" select="@Name" />
    <xsl:for-each select="Site">
     <xsl:variable name="site" select="@Id" />
     <xsl:for-each select="Webs/Web">
      <xsl:variable name="webID" select="@Id" />
      <xsl:variable name="webURL" select="@Url" />
      <xsl:variable name="webTemplate" select="@TemplateName" />
      <xsl:variable name="webTemplateID" select="@TemplateId" />
      <xsl:for-each select="WebParts/WebPart">
       <xsl:variable name="webPartTypeID" select="@Id" />
       <xsl:variable name="nameNode" select="//WebPartTypeID[GeneratedWebPartTypeID = $webPartTypeID]" />
       <xsl:variable name="assembly" select="$nameNode/AssemblyName" />
       <xsl:variable name="class" select="$nameNode/TypeName" />
       <xsl:element name="WebPartType">
        <xsl:element name="WebPartTypeID">
         <xsl:value-of select="$webPartTypeID" />
        </xsl:element>
        <xsl:element name="AssemblyName">
         <xsl:value-of select="substring-before($assembly, ',')" />
        </xsl:element>
        <xsl:element name="FullAssemblyName">
         <xsl:value-of select="$assembly" />
        </xsl:element>       
        <xsl:element name="ClassName">
         <xsl:value-of select="$class" />
        </xsl:element>
        <xsl:element name="Database">
         <xsl:value-of select="$db" />
        </xsl:element>
        <xsl:element name="SiteCollection">
         <xsl:value-of select="$site" />
        </xsl:element>
        <xsl:element name="WebID">
         <xsl:value-of select="$webID" />
        </xsl:element>
        <xsl:element name="WebURL">
         <xsl:value-of select="$webURL" />
        </xsl:element>
        <xsl:element name="WebTemplateName">
         <xsl:value-of select="$webTemplate" />
        </xsl:element>
        <xsl:element name="WebTemplateID">
         <xsl:value-of select="$webTemplateID" />
        </xsl:element>
        <xsl:element name="WebCount">
         <xsl:value-of select="@Count" />
        </xsl:element>
       </xsl:element>
      </xsl:for-each>
     </xsl:for-each>
    </xsl:for-each>
   </xsl:for-each>
  </ReportRoot>
 </xsl:template> 
</xsl:stylesheet>

~Sue

MOSS SPSecurity.RunWithElevatedPrivileges Object Reference Not Set

MOSS (SharePoint 2007) SPSecurity.RunWithElevatedPrivileges Object Reference Not Set to an Instance of an Object.

There are a few post out there regarding permissions on the database, and permissions on a local account group; however I just barely found, and wanted to pass on –

If you’re using Visual Studio and you’re running in Debug mode, and you’re either getting the Object Reference on SPSecurity.RunWithElevatedPrivileges or you’re getting a File Not Found error (“Web application at … could not be found”) on new SPSite(webURL), then you might want to check your configuration options.  Are you running in x86 mode and your site is build on 64 bit? 

What I did was I changed my build configuration from x86 to “Any CPU” and that made both exceptions go away.

~Sue

SPListItem.DoesUserHavePermissions doesn’t work…or does it?

Don’t you hate when you make dumb – and upon later inspection, rather obvious – mistakes?

I was struggling (I’m too embarrassed to tell you how many hours) with the fact that even though I had broken permission inheritance, my code was always saying that the Current User had access to the list item – and full access at that (that should have been my first clue).

bool hasPermissions = false;
try
{
       //http://blogs.msdn.com/b/ryanrogers/archive/2004/07/15/184594.aspx
       site.CatchAccessDeniedException = false;

       hasPermissions = item.DoesUserHavePermissions(SPBasePermissions.ViewListItems);
}
catch (Exception ex2)
{
       hasPermissions = false;
}
finally
{
       site.CatchAccessDeniedException = true;
}

if (!hasPermissions)
{
       ShowError("User does not have permissions to list item");
       return;
}

So I thought the above code would work.  From everything I could see on the web, it SHOULD have worked.  So why didn’t it?

Turns out that I was running in Elevated Privileges mode, so the “Current User” was the system account.  Now mind you, that code above was NOT inside that Elevated Privileges wrapper – that’s what threw me I think.  But, what I did do (and I’m questioning now WHY did I do it this way) is that I had an SPWeb object variable that I set inside the RunWithElevatedPrivillges method.

SPList list = null;
SPWeb web = null;
SPSite site = null;
try
{
       SPSecurity.RunWithElevatedPrivileges(delegate()
       {
              using (SPSite secureSite = new SPSite(webURL))
              {
                     site = secureSite;
                     using (SPWeb secureWeb = secureSite.OpenWeb())
                     {
                           web = secureWeb;
                     }
              }
       });
}
catch
{
       ShowError("Could not retrieve web at '" + webURL + "'");
       return;
}

try
{
       list = web.Lists[listID];
}
catch
{
       ShowError("List ID '" + strListID + "' not found in web");
       return;
}

try
{
       SPListItem item = list.GetItemById(itemID);

So apparently the web object stayed elevated, thus the list was elevated, thus the List Item was elevated.  All I had to do was use the overload of the DoesUserHavePermissions method.  But that being said, I’m rethinking this whole, “pull it out of the elevated wrapper” thing I have going on here.  I’m marking this post as “Best Practices” as in what NOT to do :-).

hasPermissions = item.DoesUserHavePermissions(SPContext.Current.Web.CurrentUser, SPBasePermissions.ViewListItems);

Error occurred in deployment step ‘Activate Features’: Operation is not valid due to the current state of the object

SharePoint 2010 and Visual Studio 2010.

I added an Event Receiver to a new Visual Studio 2010 project.  Very simple.  It was of type EmailReceived but I don’t think that matters in this case.  I went to deploy the project and got the following error message:

“Error occurred in deployment step ‘Activate Features’: Operation is not valid due to the current state of the object”

Apparently reading up on many people’s posts this can be many things.  Turns out mine was because I did something so normal:  I renamed the namespace and the class name of the class.  Well apparently when I did that, I got out of sync with the automatically generated Elements.xml – which I couldnt’ find at first.

So pop open your “EventReceiver1” file in solution explorer – expand the files underneath it by pressing the + sign.  Double-click the Elements.xml file.  You will see something like this:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   <Receivers ListTemplateId="101">
      <Receiver>
         <Name>
EventReceiver1EmailReceived</Name>
         <Type>EmailReceived</Type>
         <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
         <Class>YourProjectNameHere.EventReceiver1.EventReceiver1</Class>
         <SequenceNumber>10000</SequenceNumber>
      </Receiver>
   </Receivers>
</Elements>

My problem was the <Class> element.  I had renamed my namespace to MyNamespace and my project to MyProjectName.  So my class should have been MyNamespace.MyProjectName.  That was it.

Easy