Tuesday, May 22, 2012

Site Pages in Non-Team Sites

The problem:

I am creating a site from my site collection feature receiver; the site is not a team site, but it does need to have the Site Pages library. You might recall that on every team site this library is automatically provisioned, because of  the WikiPageHomePage feature; however, the problem is that this feature cannot be activated in non-team sites.

The solution:

Since I have to activate the feature in code, the best way to do it is to obtain the reference to the site that is being created and then call the EnsureSitePagesLibrary():

SPWeb web = ...
SPList list = web.Lists.EnsureSitePagesLibrary();

Note that, in a similar fashion, you can provision a Site Assets library in a non-team site - just call EnsureSiteAssetsLibrary(), rather than EnsureSitePagesLibrary().

Wednesday, May 9, 2012

Caching Web Part Content

The problem:

Here is a common scenario. You have a web part whose contents include a gridview, which is populated by going through all (or many) subsites and gathering the information from each of these sites. The gridview has to allow sorting and possibly even filtering.

The problem, though, is that sorting and filtering typically cause postback, which means you need to perform the expensive operation of loading the data again. If the data happens to be static (or relatively static), you wind up loading the same data over and over again, which results in performance deterioration.

The solution:

The solution depends on whether the data displayed the gridview is the same for every user.

a) If it is the same, then it will be worthwhile to use the Cache object and add the datatable (or any other data source object) to the cache if it is not already there. This will speed up the loading time for every user, since the cache will be populated only once. A couple of gotchas, though:

1) Make sure you place a lock around the code that accesses cache. Otherwise, if it takes 15 seconds to load the data table and many users are trying to access this page at the same time, then all of these users will be simultaneously trying to update the same cache object - and this would result in performance problems.

2) If the data occasionally changes, it would be wise to implement cache dependency, so that the cache is invalidated whenever the data changes.

b) If it is not the same, then use Session instead of Cache. But, make sure the session state is enabled. Another gotcha is that there is no expiration of the Session object, nor there is anything equivalent to cache dependency for Session objects - so you'll need to find some other way to clear the session object once the underlying data table changes.

Tuesday, May 8, 2012

Provisioning Site Pages

Today I'd like to talk about provisioning pages in a feature. Sometimes your customer wants to add some content to the pages you provision, and/or to have some web parts on them.. If that's the case, application pages will not do - they live on the file system (more specifically, LAYOUTS on the hive, or some subfolder of LAYOUTS), and you cannot add web parts to them.

Provisioning site pages using Visual Studio 2010 is a little harder than provisioning application pages - after all, there is no such an item template as "Site Page" or "Web Part Page." So, you will need to use a workaround.

First of all, you need to remember that when you provision files, you do so via modules. Luckily, there is such an item template. When you create a module, an Elements.xml file and a Sample.txt files are added to your Visual Studio 2010 project.

Rename Sample.txt to YourSitePage.aspx (or whatever name you'd like to use for your page), and add the following:


Note that in the PlaceHolderMain, I am creating a new web part zone, called MainWebPartZone (feel free to rename it if you don't like this name.) This is where the web part will live on this page.

Also add a class to your module and name it YourSitePage.aspx.cs.  This will be the code-behind class for your page.

The next step is configuring the module. If you want your user to treat your page as a library item, you need to provision it as ghostable in library. In the CDATA section, insert the full contents of the .webpart file (except the processing instruction!) In my example, the new page is provisioned in the Pages library.






Tuesday, April 10, 2012

Changing Toolbars For List Web Parts

The problem:

I need to always show the toolbar, MOSS 2007-style, on some of the list web parts on the site I am automatically provisioning. Unfortunately, there is no straightforward way to do it via the SharePoint API, for example through the SPWebPart or the SPView objects.

The solution:

I am not sure why Microsoft chose to omit this functionality from the object model, as this seems to be a pretty common requirement. But, it is possible to change the toolbar type if you use reflection. The code below does the trick. Note that I am passing "ShowToolbar" as a parameter to the "SetToolbarType" method.


Friday, March 30, 2012

Long Running Operations

The problem:

I have a long-running operation that is supposed to transfer to an error page in case of an error.

This code is perfectly valid. However, the error page (SPUtility.ErrorPage, which is just a synonym for error.aspx) it transfers to is too generic ("An error has occurred on the server") and cannot be customized with a more informative error message.


The solution:

You don't have to call the End() method for the long operation; if the result is erroneous, make a transfer to the error page using SPUtility.TransferToErrorPage, which takes an error message as a parameter. This code does the trick. Note also that it catches ThreadAbortException.

Thursday, March 1, 2012

Adding a Positive Integer Column To a List Via PowerShell

The problem:

I need to add a column to my list via PowerShell; this column must be a positive integer.

The solution:

Here is a simple script to accomplish this:

$site = Get-SPSite "http://my_site_collection"
$web = $site.OpenWeb("my_site")
$list = $web.Lists["my_list"]
$spFieldType = [Microsoft.SharePoint.SPFieldType]::Integer

#The new field is not required, so the value of the third parameter is 0
$spFieldName = "PositiveNo"
$list.Fields.Add($spFieldName, $spFieldType, 0)
$list.Update()

#Now that we created a new field, let's change it so that it has a minimal value of 1 and no decimal places
$spField = $list.Fields.GetField($spFieldName)
$spField.MinimumValue = 1
$spField.DisplayFormat = "0"
$spField.Update()

#Need to add the new column to the list
$views = $list.Views["All Items"]
$views.ViewFields.Add($spFieldName)
$views.Update()

Friday, February 24, 2012

Flickering Progress Box in Outlook 2010 While Opening a Task

The problem:

A workflow assigned me a task. I opened the task email in Outlook 2010 and then pushed the "Open This Task" button on the ribbon. All I got was a flickering progress box. The task was never opened and it took me quite a while to get rid of the flickering progress box.

The solution:

This seems like a security issue (and could have been handled more gracefully by Outlook.) While normally I don't advocate editing system files on the hive, this is the case when it is necessary. I opened the 14\LAYOUTS\FormsServer.aspx and added the following line right after the <body> tag:


<SharePoint:FormDigest runat="server" />

This creates a security validation for the FormsServer.aspx page and thus gets rid of the annoying flickering issue.