Sunday, December 28, 2008

NullReferenceException on SPWorkflowTask.AlterTask ()

In a custom task edit form for a custom content type, the following code is used to update the task item:

Hashtable taskHash = new Hashtable();
taskHash["Status"] = "Approved";
taskHash["PercentComplete"] = "1";
SPWorkflowTask.AlterTask(this._taskItem, taskHash, true);

This works great most of the time, however, if the custom content type defines no fieldrefs, or more specifically, the content type XML does not have the node, as could be the case if only a custom form is included in the custom content type, the following exception would be thrown on the AlterTask line:

System.NullReferenceException was unhandled by user code
Message="Object reference not set to an instance of an object."
Source="Microsoft.SharePoint"

To fix this cryptic error, make sure that the node is in the content type definition XML, even when there're no child nodes.

Debug ASPX pages added to /LAYOUTS in SharePoint

How do you debug an ASPX page added to SharePoint's /LAYOUTS folder? There are many situations where you need to drop ASPX pages in the /LAYOUTs folder as part of a custom solution to some requriements. For example, an edit form for a custom content type, or a page for displaying data in a special way.

If the page fails, the exception is most likely logged in SharePoint log in the 12 hives, but wouldn't it be nice if you can step through the page in debug mode? All you need to do is to add the attribute debug="true" in the Page directive, set the breakpoint and attach the debugger to the process in Visual Studio, and voila!

Of course this is the standard way to debug ASP.NET pages. Only if you can remember it in the muddy water of SharePoint.

Thursday, November 6, 2008

WCF: file transfer error due to large size

Many people have bloged about a size issue during uploading file via WCF services. I want to detail the exact four scenarios that can occur:

By default, ASP.NET 2.0 has a default maximum request length of 4MB configured in machine.config. This value can be changed directly in machine.config or overridden in the service application's web.config:

WCF service binding definition also comtains a size def in bytes. So 4000000 is 4 MB.

There are four scenarios:
  1. if the size of the message is greater than BOTH values, this exception is thrown by the service:
    System.ServiceModel.CommunicationException: Maximum request length exceeded. ---> System.Web.HttpException: Maximum request length exceeded.

  2. if the size of the message is greater than httpRuntime.maxRequestLength but less than WCF's binding.maxReceivedMessageSize, the same exception above is thrown.

  3. if the size of the message is less than httpRuntime.maxRequestLength but greater than WCF's binding.maxReceivedMessageSize, the client receives a "The remote server returned an unexpected response: (400) Bad Request." excception. Nothing is logged on the server.

  4. if the size of the message is less than BOTH values, it works!
I used the term "size of the message" instead of size of the file above, as there is overhead when a file is packaged in service calls. For example, if the upper limit of file upload is 10 MB, both configurations should be like 12 MB. The actual wiggle room needed depends on the data.

Tuesday, July 15, 2008

Workflow updating current item - infinite loop

I added a dead simple workflow to a calendar list today to try something out. It copies the value from a hidden column (Email To) to a column I added (Attendees). I set the workflow to run when when a new item is added or an item is changed. I didn't think too much but during testing, the page becomes very sluggish and eventually it errors out:

"Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries. "

I eventually came across this post that's exactly the same as my problem. The direct cause of the error is that the CAML query statement gets really big. But why? Then it suddenly hit me, it's because that the workflow is set to run when an item is changed too. Since the workflow updates a column in the current list item, this creates an infinite loop where a new workflow instance is created after another.

This appears to me like a bug. There should be a difference between item changes inside a workflow vs outside of the workflow.

Tuesday, July 8, 2008

stsadm -o Deploysolution, to force or not to force?

There have been many discussions about this error that occurs when running stsadm -o deploysolution to deploy a solution:

The Execute method of job definition "SPSolutionDeploymentJobDefinition" (id "65274d73-2174-417e-b0fa-a63130953ee3") threw an exception. Failed to create feature receiver object from assembly "MyFeaturePack, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5", type "MyFeaturePack.FeatureReceiverApplyRootUI" for feature 0f9127e3-a930-41a3-8cda-21ded77d996d: System.ArgumentNullException: Value cannot be null.

Parameter name: type
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at Microsoft.SharePoint.Administration.SPFeatureDefinition.get_ReceiverObject()

Common causes of the problem include that (1) the assembly not being deployed to GAC, thereby having code access issue; (2) the signing key is somehow wrong; (3) the class name is not what's defined in feature.xml.

However, this error occurred for a different reason. Actually I still don't know the reason but managed to figure out a solution. Somehow adding the "-force" parameter on stsadm -o deploysolution made it to work for me:

stsadm -o deploysolution -url http://mossdev/ -name MyUI.wsp -immediate -allowgacdeployment -force

Maybe -force wipes out some caching somewhere. Oh well.

Tuesday, July 1, 2008

Custom theme in a feature does not get applied

People have written about deploying various customizations and custom functionalities as features via SharePoint Solution packages. I'm not going to go through it step by step here. As usual, I want to focus on the 'unusual' or 'gotcha' stuff when it comes to SharePoint.

Assuming you already have your custom theme, feature.xml, and the solution packaging files (manifest.xml and the ddf) ready, you can deploy the theme via the solution package, which adds your custom theme to /12/TEMPLATES/THEMES. Then you need to tell your site collection or web to actually use this new custom theme. This is typically accomplished in the FeatureReceiver class so when you activate the feature, the theme is applied. There are of course other ways to do this. For example, you can write a simple console application to run on the SharePoint WFE server to apply the theme.

SPWeb has a method ApplyTheme() that allows you to change the web theme in the code.

public override void FeatureActivated(SPFeatureReceiverProperties properties) {
SPSite site = properties.Feature.Parent as SPSite;
if (site == null) {
throw new SPException("This feature should only be activated on a Site Collection");
}
SPWeb rootweb = site.RootWeb;
rootweb.MasterUrl = "MyNewUI.master";
rootweb.ApplyTheme("MyCustomTheme");
rootweb.Update();
}

The code works without any error and the master page is correctly applied to the root web when you activate the feature. However, the custom theme is not applied. What is wrong?

The problem is with the order of the last two methods on SPWeb. It is necessary to call SPWeb.Update() to commit the changes when you assign new values to many of the pulbic properties of SPWeb. However, calling Update() AFTER ApplyTheme() somehow wipes out the change ApplyTheme() makes. So in order for this to work, there cannot be an Update() call after ApplyTheme() is called on the same instance of SPWeb. The above code would work perfectly by simply switch the order of the two method calls:

................
rootweb.Update();

rootweb.ApplyTheme("MyCustomTheme");
...............


Some folks have mentioned that in order to apply a custom theme from feature activation, there can not be an entry in \12\TEMPLATE\LAYOUTS\1033\SPTHEMES.XML for the theme. I found this to be not true. SPTHEMES.XML is for displaying the available themes on _layouts/themeweb.aspx for user to select. It has bearing on how a theme works or how it's applied. Therefore, you can either modify SPTHEMES.XML in your FeatureReceiver class or you don't have to. Adding an entry would allow user to see the theme applied listed on on _layouts/themeweb.aspx. That's all.


.

Friday, June 27, 2008

SPGridView Filtering

SPGridView is an amazing control in WSS 3.0's Microsoft.SharePoint.WebControls namespace. It is feature-packed but unfortunately, there is little documentation on how to use it beyond the vanilla data-binding. The post at Bob's SharePoit Bananza is perhaps the most helpful work so far. And here's another one with a bit more code.

I want to talk about a couple of quirks that took me forever to figure out as I was trying to get the filtering working in my own web part:


  • It is important to assign the SPGridView object a ID value. This is often overlooked when you create and add the instance in the backend code
    SPGridView grid = new SPGridView();
    grid.ID = "gvw1"; //or any string

    If the ID is not assigned, ASP.NET would assign one like "ctl00" automatically as it renders the control. However, this would result in the filter dropdown on the column header not working. Upon clicking, it gives a generic javacript error "'null' is null or not an object". Here's the difference in the onClick attribute in the HTML table tag generated:

    ID property not assigned:
    onclick="SPGridView_FilterPreMenuOpen('ctl00_PlaceHolderMain_ctl00', 'ctl00_PlaceHolderMain_ctl00_SPGridViewFilterMenuTemplate', 'ctl00_PlaceHolderMain_ctl00_ctl01_SPGridViewMenu0', 'Title', event);

    ID property = "gvw1":
    onclick="SPGridView_FilterPreMenuOpen('ctl00_PlaceHolderMain_gvw1', 'ctl00_PlaceHolderMain_gvw1_SPGridViewFilterMenuTemplate', 'ctl00_PlaceHolderMain_gvw1_ctl01_SPGridViewMenu1', 'Title', event);

    There is really no difference other than the ctl100 vs gvw1. However, it costed me a lot of hairs to figure out that it DOES make a difference!

  • The columns with the filter one can not have spaces in the column name. This is typically not an issue as the internal name of any field in SharePoint has no space (concatenated by the underline if the display name has spaces). The problem could arise when the data comes from a different source or is manipulated with new columns added in the fly. For exmaple:
    this.grid.FilterDataFields = "Service Department";
    this.grid.FilteredDataSourcePropertyName = "FilterExpression"; this.grid.FilteredDataSourcePropertyFormat = "{1} = '{0}'";

    "Service Department" is a new column I added to the DataTable behind the ObjectDataSource, and it is the column that I want to filter on (It's the first column in the data source so no commas needed). However, it gives this error when dropping down the filter and clicking an item in the dropdown:
    Syntax error: Missing operand after 'Department' operator. at System.Data.ExpressionParser.Parse() at System.Data.DataExpression..ctor(DataTable table, String expression, Type type) at System.Data.DataView.set_RowFilter(String value) at System.Web.UI.WebControls.FilteredDataSetHelper.CreateFilteredDataView(DataTable table, String sortExpression, String filterExpression, IDictionary filterParameters) at System.Web.UI.WebControls.ObjectDataSourceView.CreateFilteredDataView(DataTable dataTable, String sortExpression, String filterExpression) at System.Web.UI.WebControls.ObjectDataSourceView.ExecuteSelect(DataSourceSelectArguments arguments) at System.Web.UI.DataSourceView.Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback) at System.Web.UI.WebControls.DataBoundControl.PerformSelect() at System.Web.UI.WebControls.BaseDataBoundControl.DataBind() at System.Web.UI.WebControls.GridView.DataBind()

    Again not very indicative what the problem is. Well, the space in the column is the problem. Changing the column name to "Service_Department" or "ServiceDeparment" and the code above accordingly fixed the problem.

Wednesday, June 4, 2008

Another permission issue often found in SharePoint production env

Often in production deployment of SharePoint, the people and the accounts used to install and configure the SharePoint server farm itself are different from the ones responsible for further setting up the services on the server. For example, when using an administrator account that's not the one used to create the SSP to configure the services in the SSP, an error of "Access Denied" is encountered, even when the account is a server admin, farm admin, and the SSP site collection admin. What's missing is the "Personalization Services Permissions" right in the SSP (last item under User Profiles and My Sites). The user account must have all the permissions in this SharePoint category to perform operations like User Profile configuration and My Site Settings configuration.

STSADM command results in null reference error

When STSADM command returns immediately with "Object reference not set to an instance of an object” error, it is typically due to the inadequate permission the user account running the command has. The account should have the following:
  • belongs to the Administrators group on the server where the command is running.
  • is a farm administrator
  • has full access (sysadmin) to the SQL Server Instance (no need to have access to the server on which SQL Server runs on).

This problem often happens in production environments where probably no user accounts have the broad access levels needed as described above. Because user accounts are tightly managed and servers deployed in different silos and managed by different teams in many production environments.

Friday, May 2, 2008

Do not rename SharePoint Site Column "Title"

WSS 3.0 Site Column "Title" is the default site column on the content type Item. This column should not be renamed. Often it's tempting to rename it to something more relevant in a list, when the content type of the list is being edited:

However, since it's a system site column, changing it affects every list in the entire site collection even if the change was initiated in a subsite. What's worse, it cannot be changed back to the name back to "Title". it gives this error when you attempt to change it back:
“The Column name that you entered is already in use or reserved”
This is obviously a bug but the SharePoint team disagree to some extent. The solution is to program against the object model and update the name:
SPSite siteCollection = new SPSite("http://siteCollectionUrl");
SPField field = siteCollection.RootWeb.Fields[""];
field.Title = "Title";
field.Update();
This page discusses the issue in more detail and provides a small console application that runs the above code. http://sharepoint.microsoft.com/blogs/fromthefield/Lists/Posts/Post.aspx?ID=15

Wednesday, April 30, 2008

SharePoint ACL issue when adding custom images, CSS, script files

I just customized the master page of a MOSS 2007 site and also applied a custom theme that I created to it. It looked great (it's quoted from the customer, no kidding :). However, when a senior manager of the client decided to view it, the site just kept prompting for login continuously. He's not impressed. I knew he was able to view the site BEFORE any customization was applied. So how could an unghosted master page and a modified copy of an existing theme result in access issue?

It turned out that the new resources, a few image files and new CSS files, that I added for the customizations are the culprit. Or rather the ACLs on these new files. Several files added to the images folder and themes folder in the 12 hives didn't inherit the permissions from the parent folder for whatever reason (most other added files inherited fine). As soon as the ACLs on these files are set properly, the site stopped the endless login prompting.

Monday, April 7, 2008

Add Alternate Access Mapping for a WSS web application

You would think that the Alternate access mappings link under Global Configuration on the Operations page in Central Admin is the place to start with if you need to add an Alternate Access Mapping (AAM) for a web application. Nope. You start adding an AAM by extending the existing web application (Create or extend Web application link on the Application Management page). On the "Extend Web Application to Another IIS Web Site" page, make sure the existing web application is selected, and make sure select a value in the Zone dropdown box at bottom of the page that is different from the existing web application. Once you are done extending the web application. Go back to the Alternate Access Mappings page again and you will see the newly extended web application is there.

This TechNet article does an excellent job of walking through the step by step process of setting up an AAM (http://technet2.microsoft.com/windowsserver/WSS/en/library/c8ccffce-5162-46af-a3ef-1d7914e8efee1033.mspx?mfr=true).

WSS Create new site collections from Site Directory checkbox grayed out

I was trying to change my site creation from the default of creating subsites to creating site collections in WSS 3.0 today. It took some effort to figure out that you have to have a site directory site first even if you don't want to use the site directory. However, I still faced the problem that the option I really needed to change on the Site Directory Settings page was, errrr, grayed out:

It turned out that the Self-Service Site Management has to be turned on for the web application that this site collection sites on. It makes sense but it's just one of the many gotchas of SharePoint.


To turn on Self-Service Site Management, go to Central Admin -> Application Management -> Self-Service Site Management (under Application Security), and check On and Save. Then go back to the Site Directory Settings page of the site collection. The checkbox is enabled now.