Prexens team dev' corner

How-to: Replace ‘Submit for approval’ custom quick access button

Goal of this post

The goal of this post is to guide you through the different steps for replacing the ‘Submit for Approval’ quick access button in a WCM environment.


Problematic overview

‘Submit for Approval’ is a native button which aims at firing a ‘Parallel Approval’ workflow when Publishing features are activated  in a WCM web site:

 

Native Approval button

 

When deploying a custom approval workflow, you’ll probably need to overwrite the logic of this button to start your custom workflow correctly.
You would also be able to customize the icon and the text of your custom button to achieve something like this:

 

Custom approval button

 

Write a custom class CustomSubmitForApproval

 

1. Set pre-requisites:

 

  • First, add those tworeferences : Microsoft.SharePoint, Microsoft.SharePoint.Publishing
  • Create a public sealed class that inherits from:
    Microsoft.SharePoint.Publishing.WebControls.EditingMenuActions.ConsoleAction
  • Add a constant used as argument of post back events:
private const string PostBackEventArg = "submitForApproval";

 

2. Add a constructor and customize your custom quick access button:

public CustomSubmitForApprovalButton()
            : base()
{
      this.DisplayText = "Custom Submit for Approval";
      this.ImageUrl = "/_layouts/images/workflows.gif";
}

 

3. Override NavigateUrl property and fire post back events:

public override string NavigateUrl
{
     get
     {
          return String.Format("javascript:{0}", Page.ClientScript.GetPostBackEventReference(
this, CustomSubmitForApprovalButton.PostBackEventArg));
     }
}

 

4. Override RaisePostBackEvent method, and implement your redirection logic

 

In our case, we force the page check in and we redirect toward check-in page:

public override void RaisePostBackEvent(string eventArgument)
{
    try
    {
        if (eventArgument == CustomSubmitForApprovalButton.PostBackEventArg)
        {

            SPFile myFile = SPContext.Current.File;

            // Auto checkin page before redirecting to 'Publish Major Version'
            if (myFile.CheckOutStatus != SPFile.SPCheckOutStatus.None)
            {
                myFile.CheckIn("Default comment", SPCheckinType.MinorCheckIn);
            }

            string redirectUrl = String.Format(
                    "{0}_layouts/Checkin.aspx?FileName={1}&Publish=true&List={2}&Source={3}",
                    SPContext.Current.Web.ServerRelativeUrl,
                    SPContext.Current.ListItemServerRelativeUrl,
                    Convert.ToString(SPContext.Current.List.ID),
                    Page.Request.Url.OriginalString);

            SPUtility.Redirect(redirectUrl, SPRedirectFlags.Default, HttpContext.Current);
        }

    }
    catch (Exception ex)
    {
        ShowError(ex, new ConsoleError(ex.Message));
    }
}

 

You can override some properties to control what is the minimum level of permission required to access the feature, and what are the states causing your button to appear in the page editing toolbar. In samples below, users have to be granted the permission of adding items and page has to be a publishing page waiting for approval.

 

 

5. Override SPBasePermissions UserRights property

public override SPBasePermissions UserRights
{
    get { return SPBasePermissions.AddListItems; }
}
6. Override AuthoringStates RequiredStates property
public override AuthoringStates RequiredStates
{
    get
    {
        return AuthoringStates.IsPublishingPageTrue |
            AuthoringStates.SaveConflictExistsFalse |
            AuthoringStates.IsMajorVersionFalse |
            AuthoringStates.IsApprovalWorkflowConfiguredTrue |
            AuthoringStates.InEditModeFalse |
            AuthoringStates.IsApprovalWorkflowRunningFalse;
    }
}


 

Overwrite CustomQuickAccess.xml file in master pages gallery


When your custom class is compiled and deployed on your SharePoint server, you will have to overwrite CustomQuickAccess.xml located into MOSS master pages gallery.


In a live scenario, we recommend to deploy this file through activation event of a custom feature. Simply make sure not to override an already existing file.
In our sample scenario, we are going to edit this file directly from SharePoint Designer :


1. Open CustomQuickAccess.xml from /_catalogs/masterpage/Editing Menu


 

2. Add a reference to our custom class (mind to adjust assembly reference with your own assembly qualified name):

<references>
 <reference TagPrefix="Prexens"
      assembly="Prexens.SharePoint.CustomQuickAccessButton, 
Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5"
namespace="Prexens.SharePoint.CustomQuickAccessButton" /> </references>

3. Add a ConsoleNode that override native qaPublish Node ID:


<structure>
 <ConsoleNode 
Sequence="65"
  ConfigMenu="Replace"
  Action="Prexens:CustomSubmitForApprovalButton"
  ID="CustomQuickAccessButton_SubmitForApproval"
  ChangedNodeID="qaPublish" />
</structure>

 

4. Add two ConsoleNode that remove native Approve and Decline buttons (those two feature should probably be inconsistent with your custom publishing workflow):

<ConsoleNode ConfigMenu="Delete" ChangedNodeID="qaWorkflowDecline" />
<ConsoleNode ConfigMenu="Delete" ChangedNodeID="qaWorkflowApprove" />


5. Save, publish, and approve this file in master pages gallery


How to associate a worklow to a list programmatically

Goal of this post

This post is a good example of how to programmatically associate a Workflow to a list.
The workflow can be standard or designed with SharePoint Designer or Visual Studio.
As a workflow is using a standard tasks list and a specific workflow history list to run properly, the code will check and use or create them if necessary.
It will also take care of associating the workflow with a list.

Code example

We assume that the variable web is a SPWeb object containing the list we want to associate the workflow with.

1. Declare objects we are going to use

SPList myList = null;                               // List to associate workflow to
string myListName = null;                           // My list name
SPList historyList = null;                          // Workflow history list
SPList taskList = null;                             // Workflow tasks list
string workflowTemplateGuid = null;                 // Workflow template Guid
SPWorkflowTemplate workflowTemplate = null;         // Workflow template
SPWorkflowAssociation workflowAssociation = null;   // Workflow association
string workflowAssocName = null;                    // Workflow association name

2. Init

Be sure to use the internal name of the list. And set the workflow association name, this will appair as workflow name in SharePoint list settings.
myListName = "My list name";
workflowAssocName = "My Workflow";

3. Get Workflow template

If you want to use a custom workflow and know its template GUID, use the GUID string.
workflowTemplateGuid = "BAD855B1-32CE-4bf1-A29E-463678304E1A";
workflowTemplate = web.WorkflowTemplates[new Guid(workflowTemplateGuid)];
Instead you can get the workflow template GUID by using the GetTemplateByName method.
workflowTemplate = web.WorkflowTemplates.GetTemplateByName(
"Template name",
System.Globalization.CultureInfo.CurrentCulture);

4. Get or create workflow history and tasks lists.

The history list is dedicated to workflows and it's based on the WorkflowHistory list template. Most of times you'll have to create it.
// Try to get workflow history list
try
{
      historyList = web.Lists["Workflow History"];
}
catch (ArgumentException exc)
{
      // Create workflow history list
      Guid listGuid = web.Lists.Add("Workflow History", "", SPListTemplateType.WorkflowHistory);
      historyList = web.Lists[listGuid];
      historyList.Hidden = true;
      historyList.Update();
}
The tasks list is a common tasks list based on Tasks list template. If you want to create a specific tasks list for the workflow, don't use "Tasks" title for it. In the example we want to use a dedicated tasks list and will name it "Workflow Tasks".
// Try to get workflow tasks list
try
{
      taskList = web.Lists["Workflow Tasks"];
}
catch (ArgumentException exc)
{
      // Create workflow tasks list
      Guid listGuid = web.Lists.Add("Workflow Tasks", "", SPListTemplateType.Tasks);
      taskList = web.Lists[listGuid];
      taskList.Hidden = true;
      taskList.Update();
}

5. Create workflow association, configure it and associate it to the list.

// Allow unsafe updates on web
web.AllowUnsafeUpdates = true;
try { // Create workflow association workflowAssociation = SPWorkflowAssociation.CreateListAssociation(
workflowTemplate,
workflowAssocName, taskList, historyList); // Set workflow parameters workflowAssociation.AllowManual = false; workflowAssociation.AutoStartCreate = true; workflowAssociation.AutoStartChange = false; // Add workflow association to my list myList.AddWorkflowAssociation(workflowAssociation); // Enable workflow workflowAssociation.Enabled = true; } finally { web.AllowUnsafeUpdates = false; }

Categories
Links
  Prexens Web Site
  Charting for SharePoint
  Charting for SharePoint (Codeplex)
  Charting for SharePoint Starter kit

Microsoft Certified Partner