Targeting a SharePoint Site in a Custom Action

In my initial attempt to create a custom action for a SharePoint list, I specified it as follows:

<CustomAction
    Id="SoftArtisans.Tutorial.MenuItemExcel"
    GroupId="ActionsMenu"
    Location="Microsoft.SharePoint.StandardMenu"
    RegistrationType="List"
    Sequence="1000"
    Title="Export to SoftArtisans OfficeWriter for Excel"
    ImageUrl="/_layouts/images/softartisanstutorial/actionicon_excel.gif">
    <UrlAction Url="/_layouts/SoftArtisansTutorial/ExcelWriter.aspx?ListId={ListId}"/>
</CustomAction>

WSS replaces the special {ListId} token with the actual id of the list. In the application page I tried to retrieve the list from which the action had originated as follows:

SPWeb web = SPContext.Current.Web;
string listId = Request.QueryString["ListId"];
SPList list = web.Lists[new Guid(listId)];

However, I found that SPContext.Current.Web always returned the root-level Web site, even if the action was activated from a child site. So I modified the URL of the action to include the URL of the site:

<UrlAction Url="/_layouts/SoftArtisansTutorial/ExcelWriter.aspx?ListId={ListId}&amp;SiteUrl={SiteUrl}" />

With this change, I was able to retrieve the Web site from the URL and the list within the Web site:

string relativeUrl = Regex.Replace(Request.QueryString["SiteUrl"], @"http://[^/]+", "");
using (SPWeb web = SPContext.Current.Site.OpenWeb(relativeUrl))
{
    string listId = Request.QueryString["ListId"];
    SPList list = web.Lists[new Guid(listId)];
    ...
}

Howver, this was not a good solution as it doesn’t take full advantage of the context information that SharePoint provides. The problem with the above approach is that the action URL is not site-specific; rather it always targets the root-level site. The solution is to include another special token in the action URL:

<UrlAction Url="~site/_layouts/SoftArtisansTutorial/ExcelWriter.aspx?ListId={ListId}"/>

WSS replaces the token ~site with the URL of the site, so that I could now access the actual site through the SPContext.Current.Web property.

Related posts: