Saturday, July 31, 2010

SP2010: Unable to publish workbook to Excel Services

While trying to publish a workbook to SharePoint 2010 to use with Excel Services, I ran into some problems. Specifically, it appeared that Excel wasn't able to access my Document library.

To publish my workbook, I followed instructions you might find anywhere on the web, using the Office 2010 Backstage to save to SharePoint. However, none of my libraries showed up by default. Not a problem! I can just type the URL into the address bar, right? Apparently Excel says that it "can't open this location using this program".

Generally problems like this are permissions related. Well, I was logged into the machine as my farm administrator, who has full rights to the Documents library. I was also able to click on files from SharePoint and have them open Excel automatically.

I decided to take a new approach. I opened the demo Excel workbook that came with the Business Intelligence Center template. I then made a small update and saved it back to SharePoint. Now when I opened Backstage to save to SharePoint, the Documents library showed up in the Recent Locations section.


I was hoping by doing this, I could trick Excel into finding the location. So I then tried to save my new workbook by just clicking on that recent location. However, Excel was still not able to do it, and told me it was unable to open my site:


After looking around on the web, I found a forum post that seemed promising, as I'm using a Windows Server 2008 R2 machine. I opened Server Manager and installed the Desktop Experience, which required me to install the Ink and Handwriting Services as a prerequisite. I was then prompted to reboot upon completion. After rebooting, I attempted to save my workbook to SharePoint again and it worked.

SP2010: Excel Services Error - Unable to Process the Request

I've been playing a lot with Excel Services for the last week and while it is nice, it is also temperamental. Most of this can be chalked up to inexperience on my part as I discover the closest to least privileges you can get for a SharePoint 2010 Excel Services Service Application, in light of the ever popular bug, "The workbook cannot be opened". However, I'm also convinced it's a little more particular than the previous version.

By default when I installed Excel Services using a PowerShell script, it had an entry for a Trusted File Location at "http://". After playing with the Business Intelligence Center template for a bit, I created my own workbook that had a PivotTable and a PivotChart that talked to my SSAS installation. Naturally I decided to update my Trusted File Location settings. So I deleted the default entry and created a more specific one that pointed directly to the Documents library that came with the template. Then I checked on the Excel example that came with the template and found that Excel Services was no longer working because it was "Unable to process the request".


Event Viewer showed nonstop critical errors and ULS had some gems in there pinning the blame on the Secure Store Service: "Request for security token failed with exception". I tried refreshing the key figuring that my WFE and App server were out of sync, but that didn't fix the problem. Lacking a better idea, I did an IIS reset on the WFE - and it started working again.

I'm not sure why it was unhappy, but at least it was only a 10 min troubleshooting span and a simple fix!

Monday, July 26, 2010

SharePoint 2010 - Error Removing Managed Account

Recently, while trying to experiment with Excel Services in SharePoint 2010, I decided to remove the service and do a reinstall with a PowerShell script. Since I like my scripts to create managed accounts as well, I removed the account that was running my service. Apparently, SharePoint didn't like this and I received an error stating that my SPManagedAccount could not be deleted because other objects depend on it when I loaded the Managed Accounts page in Central Administration. Well, not being able to load the page sort of limits my options for correcting the problem, doesn't it, Microsoft?


I double checked and the service application had been removed from SharePoint, the Excel Calculation Service had been stopped on the App Server, and the ApplicationPool had even been removed from IIS. Looking up the CorrelationID in the logs also didn't tell me very much.

I decided to pop open PowerShell and see what I could pull off. Turns out it was a rather simple fix. First, I looked up my Managed Account. I then tried to remove it and found out the dependency was an application pool. After removing the dependency I was about to remove the Managed Account! The PowerShell Commands to do this were:
  • Get-SPManagedAccount
  • Get-SPServiceApplicationPool
  • Remove-SPServiceApplicationPool
  • Remove-SPManagedAccount
Here is a screenshot of the PowerShell at work:

Thursday, June 17, 2010

SP2010 Dispose Patterns and Broken WebParts

Today I was building a web part for SharePoint 2010 and ran across an interesting problem. The web part was working fine for authenticated users, but was failing for anonymous users. My first thought was that Lockdown might have been the problem, but it turned out after looking through SharePoint log files that I was running into a dispose problem:

Trying to use an SPWeb object that has been closed or disposed and is no longer valid.

It's an interesting error message, considering that I was disposing of my object, but only after I'd finished with it. I believe the problem wasn't that my code was using a disposed SPWeb object, but rather that I had closed an SPWeb that SharePoint was relying upon.

Here was the original code, which, according to Best Practices, was closing objects that don't need to be closed:

using (SPSite site = SPContext.Current.Site)
{
    using (SPWeb web = site.RootWeb)
    {
        SPList myList = web.Lists["myList"];
        //code here to find a list item
    }
}

And the fix was as simple as changing it to this:

SPSite site = SPContext.Current.Site;
SPWeb web = site.RootWeb;
SPList myList = web.Lists["myList"];

I'm not so sure this would have broken a SharePont 2007 site, but it's a good reminder to make sure you are following Best Practices for disposing objects!

Thursday, June 3, 2010

SharePoint 2010 Custom Error Messages for Public Facing Deployments

It's a fairly common requirement when building a public facing SharePoint site to make sure all pages share branding elements. This includes the SharePoint error pages. While working on a publishing site, you've probably encountered an article that touches on one of the error pages, but for some reason I haven't seen one that tries to cover all of them. So hopefully this will do the job!

Error Pages

If you are just looking to control the basic error pages, then look no further than the SPWebApplication.SPCustomPage enumeration, which provides a list of commonly updated pages. You use the SPWebApplication.UpdateMappedPage() method to set which pages SharePoint should serve up for each of the values within the enumeration. For a good example of this, see this post.

HTTP 404
But what about other HTTP status codes? Well the easiest would be the 404. SharePoint already has support for a custom 404 page, as long as it is pure HTML. The default 404 is located in 14\TEMPLATE\LAYOUTS\1033\sps404.html. The quickest way to override this is to use a feature to deploy your own HTML page into the same folder. A feature receiver can then update the SPWebApplication.FileNotFound property with a relative path to your file. There are just a couple points to keep in mind when you do this:
  1. Your custom page should be greater than 512 bytes. This is because the default feature in Internet Explorer to show "friendly" error messages will sometimes ignore pages smaller than this. You can read more about this problem at Microsoft Support.
  2. Your custom page, if encoded as UTF-8, should not have a BOM. Even if you save your file as UTF-8 without BOM, editing it later in Visual Studio will add the BOM back, so be careful. You can read more about that problem here.

If you'd like to have a custom 404 that performs server side code, such as to include web parts, you'll need to get a little more creative. There are two common ways to handle this. The first would be to have a static html page that performs a client side redirect with either a META tag or javascript. If you'd like to see that approach in action, here is an example.

A more robust solution would be to use an HttpModule. With the HttpModule, you have two more possibilities for how you serve your page. You can use a Response.Redirect, which is what the linked article suggests. The only downside to this is that the URL changes, which may or may not be desired. Alternatively, to keep the URL, you would need to use Response.Write.

HTTP 401
Now that you have custom error pages for the SPCustomPage enumered pages as well as a 404 page. But what about a 401? The simplest way you might think to do this would be to edit the web.config CustomErrors element. In any normal ASP.NET application, that would have been sufficient. Unfortunately it doesn't work in SharePoint.

Your next instinct might be to try IIS, as it provides the ability to set custom error pages. If you edit the property for the 401.2 status code and point it to a custom HTML page on your site, it may or may not work. In testing, it turns out that having Anonymous Access enabled in SharePoint (which then sets it in IIS) prevents a custom 401.2 page from being used. However, if Anonymous Access is disabled, such as with an Intranet site, then the custom page will show just fine. Since we are talking about a public facing deployment you are probably using the Publishing template, so you're going to need a different approach.

The trick for a custom 401 message when you have Anonymous Access enabled is using a custom http module, much like you could have done for the 404.

Here is an example:

public class CustomPageMappingsHttpModule : IHttpModule
{
 private const string Custom401File = "/_layouts/1033/Custom401_CSS.htm";

 private HttpApplication _application;

 public void Dispose()
 {
 }

 public void Init(HttpApplication context)
 {
  this._application = context;
  this._application.PreSendRequestHeaders += new EventHandler(_application_PreSendRequestHeaders);
 }

 protected void _application_PreSendRequestHeaders(object sender, EventArgs e)
 {
  HttpResponse response = this._application.Response;

  if (response.ContentType.Equals("text/html", StringComparison.CurrentCultureIgnoreCase))
  {
   string message = string.Empty;

   switch (response.StatusCode)
   {
    case 401:
     message = File.ReadAllText(this._application.Server.MapPath(Custom401File));
     this._application.Response.Clear();
     this._application.Response.Write(message);
     break;
   }
  }
 }
}

Once you hook that up in your web.config, go ahead and try to load http://YourSite/_layouts/settings.aspx. You should get the same authentication prompt you'd expect for an anonymous user, but if you hit cancel you'll now see your custom 401 message. Note that the way you register an HttpModule in 2010 is slightly different. If you add the module to the httpModules element in web.config, it may not work. In testing, I was only able to get my module to work by adding it to the modules element with the other SharePoint HttpModules.

As a final level of customization, you could consider having no prompt at all on your public site when users request restricted content. The way to do this is to extend the site and have a private site as well. Since we are just talking about two different web sites in IIS (with corresponding settings and web.config files), you can control their authentication and error settings independently while still serving up the same content. The public site could then have Anonymous Access enabled and Windows Authentication disabled. This removes the login prompt when a 401 occurs on the public site. The private site would have Anonymous Access disabled but Windows Authentication enabled, allowing users to log in and manage content as needed.

Quirks
Since you are probably using the Publishing template, it's worth mentioning the Lockdown feature. This feature stops users from being able to see your Form pages. It also locks down lists and libraries, such as the Style Library. So make sure if you use CSS in your custom error pages that you know whether you have lockdown enabled and plan locations for your assets appropriately.

Wednesday, May 26, 2010

SharePoint 2010 Content Deployment Jobs Missing Deployment Options Section

I've been working with Content Deployment jobs lately, and noticed that in 2010, the section on the Create Jobs page where you can specify whether to perform an incremental or full content deployment was missing. Just to make sure I wasn't crazy, I looked up the old screen from 2007, which looks like this:


As you can see, there is a section there for setting the types of content that are deployed. The first option, "Deploy only new, changed, or deleted content" is the incremental deployment. The second option, "Deploy all content, including content that has been deployed before", is the full deployment.

Now compare that screen to the 2010 equivalent:

For some reason, the Deployment Options section is missing. This is not gonna work. If you recall from 2007, doing a full deployment after the initial deployment can cause problems. See this article for more information about why.


So, how do we create an incremental job? The answer, courtesy of Becky Bertram, MVP:
You can't do it through Central Administration, but you can specify whether you want to do a full or incremental deployment using PowerShell. Go to your SharePoint PowerShell prompt and type get-help new-spcontentdeploymentjob -detailed and then take a look at the IncrementalEnabled parameter.

Here is a simple script to create an incremental content deployment job, assuming you have already defined a content deployment path of "Authoring to Production":

New-SPContentDeploymentJob -Name "Authoring to Production - Incremental" 
-SPContentDeploymentPath "Authoring to Production" -IncrementalEnabled:$true

Looking in Central Administration, you'll see your created job, but you still can't see if it's incremental or full. To do that, run Get-SPContentDeploymentJob "Authoring to Production - Incremental". You should notice the following line in your output:
  • ExportMethodType             : ExportChanges
That sounds awfully like an incremental doesn't it? Let's try setting the  IncrementalEnabled parameter to false and creating another job:
New-SPContentDeploymentJob -Name "Authoring to Production - Full" 
-SPContentDeploymentPath "Authoring to Production" -IncrementalEnabled:$false 
Now run Get-SPContentDeploymentJob "Authoring to Production - Full" and you should see a slightly different export method type:
  • ExportMethodType             : ExportAll
So, this means that your Content Deployment job is going to be incremental by default. The only way to get a full job is to create it via PowerShell. Given the limitations and caveats with full jobs, this seems like a good change.

Thursday, May 6, 2010

SP 2010 - Web Parts and Embedded Resources Not Working

I ran into a problem recently trying to use Embedded Resources with SharePoint 2010 web parts. I was using code I had directly used in non SharePoint ASP.Net server controls, which if you aren't familiar with, is pretty simple. I'll briefly describe how you'd use an embedded resource, then describe my problem and how I fixed it.

First, you need to just add your resource, which in my case was a stylesheet, to your project. Then, using the properties pane, set the Build Action to Embedded Resource. Once you've done that, you'll need to edit the AssemblyInfo.cs file within your project and enable the embedded resource.

[assembly: WebResource("MyAssembly.styles.MyStyleSheet.css", "text/css")]

Now you have a resource that will be embedded in your DLL. The path to that resource is specified in the WebResourceAttribute above. I usually compile and open my DLL in reflector at this point to confirm that the path I've picked is correct. Once it is, you can include your stylesheet like this:

string styleSheetUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(), "MyAssembly.styles.MyStyleSheet.css");
LiteralControl styleSheetLink = new LiteralControl(string.Format("LINK SYNTAX HERE - BLOGGER WONT LET ME EMBED THE HTML", styleSheetUrl));
                Page.Header.Controls.Add(styleSheetLink);

In theory, that should work, right? It works in server controls, and it's worked for me in older web parts for MOSS 2007. But, when I built my 2010 web part, it wasn't working.

I viewed source on my page and could see that the embedded resource was added to the markup. But when I tried to load that URL myself, I got an error, implying my resource wasn't there at all! I checked and rechecked my path inside the DLL. Everything was right.

But then I noticed something different about 2010 web parts. They use a LoadControl mechanism. I was accessing my embedded resources, using the above code, directly inside the webpart.ascx.cs, not the webpart.cs file. On a hunch, I moved the code into the webpart.cs file. Success!

This got me curious, so I looked at the source for the generated HTML and noticed that the generated URL for the embedded resource had changed. Apparently, the path is based somewhat on that first parameter to GetWebResourceUrl(), which is a type. Tinkering a little more, I learned that I could actually leverage the embedded resource from the webpart.ascx.cs by making a minor adjustment.

string styleSheetUrl = Page.ClientScript.GetWebResourceUrl(this.Parent.GetType(), "MyAssembly.styles.MyStyleSheet.css");

Notice that I'm not passing the type of the parent, which for our webpart.ascx.cs, is the wrapper class that calls LoadControl.