A number of customers have asked us to change the behavior of the Close button in an announcement. The default behavior is that when the user clicks the Close button, he is redirected to the default view of the list. Unless the querystring contains the ‘Source’ parameter. The user is then redirected to the value of that parameter. The question was if it is possible to make the Close button behave like the Back button of the browser. This is usefull when you aggregate announcements to multple places in your site collection(s)’. In this post I will describe how we implemented this change.
The basic idea is that we add a piece of JQuery script to the Dispform.aspx pages. This script changes the onclick of the button. To add this script to the pages, we add a custom control to the pages using the AdditionalPageHead control template. This is a custom user control that decides whether or not the script is added to the page. This way we only add the script to the pages that need it.
Step 1 – Register the control template
The first step is to create a new feature that will register our control:
<?xml version="1.0" encoding="utf-8" ?> <Feature Id="F88ED2E4-1318-4679-B0F4-38EECDB608F6" Title="Close Button behavior." Description="Adjust the behavior of the Close button on display pages." Version="1.0.0.0" Scope="Site" xmlns="http://schemas.microsoft.com/sharepoint/"> <ElementManifests> <ElementManifest Location="controls.xml" /> </ElementManifests> </Feature>
The contents of the controls.xml file:
<?xml version="1.0" encoding="utf-8" ?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Control Id="AdditionalPageHead" ControlSrc="~/_controltemplates/TST/CloseButton.ascx" /> </Elements>
Step 2 – Create the control template
Next thing to do is create the control that is loaded in the AdditionalPageHead. This control loads a custom control called ‘CloseButtonControl’:
<%@ Control Language="C#" ClassName="CloseButton" %> <%@ Register TagPrefix="TST" Namespace="TST.SharePoint.CloseButton" Assembly="TST.SharePoint.CloseButton, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2defc4f610a0b97" %> <TST:CloseButtonControl ID="CloseButtonControl1" ListTemplates="Announcements;Tasks" runat="server"> </TST:CloseButtonControl>
This custom user control has a property called ‘ListTemplates’. This is a semi-colon separated list of list template names that determines to which list templates the new behaviour is applied. In case we don’t want to implement this new behavior for all lists in the site collection, we can set the ListTemplates in our ASCX file. If we want the user to go back to the previous on every list item page, we set it to a ‘*’. This control property ‘ListTemplates’ in the ASCX file is not the best way to implement such configuration values. In a future post I will get back to this configuration topic.
Step 3 – Add the script to the page
Next thing to do is create the control that adds the script to the SharePoint pages that need it. The code snippet below show the code for this control. In the OnLoad our control checks if we are in a view item page (dispform.aspx). If that is the case, the template setting is validated. If the current request is the dispform page of one of the list templates that is setup in the ASCX file, the JQuery file and the script itself are added to the page. In this example the script will be added for Task and Announcement items.
namespace TST.SharePoint.CloseButton { public class CloseButtonControl : UserControl { private const string JQUERYREGISTRATION = "TST.SharePoint.CloseButton.JQuery"; private const string SCRIPTREGISTRATION = "TST.SharePoint.CloseButton.Script"; public string ListTemplates { get; set; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (String.IsNullOrEmpty(ListTemplates) || SPContext.Current.List == null || !Page.Request.Url.ToString().Contains(SPContext.Current.List.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl)) { return; } // Validate list template. string[] templates = ListTemplates.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string template in templates) { if (template=="*" || SPContext.Current.List.BaseTemplate.ToString() == template) { if (!Page.ClientScript.IsClientScriptIncludeRegistered(JQUERYREGISTRATION)) { Page.ClientScript.RegisterClientScriptInclude(typeof(CloseButtonControl), JQUERYREGISTRATION, "/_layouts/TST/jquery-1.3.2.min.js"); } if (!Page.ClientScript.IsStartupScriptRegistered(SCRIPTREGISTRATION)) { StringBuilder script = new StringBuilder(); script.Append("<script type=\"text/javascript\">"); script.Append("$(document).ready( function(){"); script.Append(" $(\"input[name$='GoBack']\").each(function() {"); script.Append(" var click = \"javascript:history.go(-1);\";"); script.Append(" this.onclick = new Function(click);"); script.Append(" });"); script.Append("}"); script.Append(");"); script.Append("</script>"); Page.ClientScript.RegisterStartupScript(typeof(CloseButtonControl), SCRIPTREGISTRATION, script.ToString()); } return; } } } } }
The JQuery script itself is very easy. It is a selector that finds the Close button and registers a new click function. The reason for registering this JQuery script from a usercontrol, is that we do not want every page to load the JQuery js file and run the script. Otherwise the solution would have been easier. You can register the JQuery file and the script directly in the ASCX file and you’re done.
Step 4 – Create the solution package
Last thing to do is create the DDF and manifest.xml files to create a solution package. The WSP file created from the DDF file below contains all necessary files.
; .OPTION EXPLICIT ; Generate errors .Set CabinetNameTemplate=TST.SharePoint.CloseButton_1.0.0.0.wsp .set DiskDirectoryTemplate=CDROM ; All cabinets go in a single directory .Set CompressionType=MSZIP;** All files are compressed in cabinet files .Set UniqueFiles="ON" .Set Cabinet=on .Set DiskDirectory1= manifest.xml manifest.xml ..\bin\release\TST.SharePoint.CloseButton.dll ..\12HIVE\TEMPLATE\LAYOUTS\TST\jquery-1.3.2.min.js LAYOUTS\TST\jquery-1.3.2.min.js ..\12HIVE\TEMPLATE\CONTROLTEMPLATES\TST\CloseButton.ascx CONTROLTEMPLATES\TST\CloseButton.ascx ..\12HIVE\TEMPLATE\FEATURES\TST.SharePoint.CloseButton\feature.xml TST.SharePoint.CloseButton\feature.xml ..\12HIVE\TEMPLATE\FEATURES\TST.SharePoint.CloseButton\controls.xml TST.SharePoint.CloseButton\controls.xml
These files are deployed to SharePoint using this manifest file:
<?xml version="1.0" encoding="utf-8" ?> <Solution ResetWebServer="TRUE" SolutionId="7EC65C73-E63F-433f-9695-B0DBD41B811D" xmlns="http://schemas.microsoft.com/sharepoint/"> <Assemblies> <Assembly DeploymentTarget="WebApplication" Location="TST.SharePoint.CloseButton.dll"> </Assembly> </Assemblies> <TemplateFiles> <TemplateFile Location="LAYOUTS\TST\jquery-1.3.2.min.js"/> <TemplateFile Location="CONTROLTEMPLATES\TST\CloseButton.ascx"/> </TemplateFiles> <FeatureManifests> <FeatureManifest Location="TST.SharePoint.CloseButton\feature.xml"/> </FeatureManifests> <CodeAccessSecurity> <PolicyItem> <PermissionSet class="NamedPermissionSet" version="1" Description="Allow access to TST.SharePoint.CloseButton"> <IPermission class="AspNetHostingPermission" version="1" Level="Minimal"/> <IPermission class="SecurityPermission" version="1" Flags="Execution" /> <IPermission class="Microsoft.SharePoint.Security.SharePointPermission, Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" version="1" Unrestricted="True" /> </PermissionSet> <Assemblies> <Assembly Name="TST.SharePoint.CloseButton" Version="1.0.0.0" PublicKeyBlob="0024000004800000blablabla_etc." /> </Assemblies> </PolicyItem> </CodeAccessSecurity> </Solution>
Step 5 – Test
After installing and deploying the WSP package, you can test the solution by activating the site collection feature. By de-activating the feature, the Close buttons in the lists return to the out of the box SharePoint behavior.