Custom Ribbon buttons to bulk publish, approve/reject, cancel approval and unpublish multiple items

————

UPDATE (2015-07-10): If you just want the WSPs, then they can be downloaded here for 2013.

UPDATE (2013-06-06): A SharePoint 2013 version of this solution is now available! Download it here. The 2010 version is still available for download.

UPDATE: There is a bug in the original code where the modal wait screen does not close properly in IE (when publishing items). It works fine with Chrome however.

I have updated the downloadable code with the fix. This now works with IE and Chrome. I’m pretty sure it works with Firefox also.
————

OOTB in SharePoint 2010 you can bulk approve/reject and unpublish multiple items using the Content and Structure tool (under Site Administration). There are a few problems with this however:

  • Obviously this is not intuitive
  • There is no bulk publish
  • There is no bulk cancel approval
  • Bulk approve/reject doesn’t seem to do anything when there is a content approval workflow running on the items (is this happening for you too?)

I went ahead and developed a couple of custom Ribbon buttons that allow you to bulk publish, approve/reject, cancel approval and unpublish multiple items. If an approval workflow is defined for the library, these actions start or stop the workflow appropriately.

The user gets a flyout menu as below:

For bulk publish and approve/reject, the user gets an OOTB-looking dialog that lists the selected items as below:

A result screen lists any item that could not be processed and the reason:

1. Download the source code

You can download the complete Visual Studio solution here.

2. Want to further improve the user experience in SharePoint?

Check out my other custom solutions here.

3. Key and interesting points

I did not go into details about the code for this solution because most of it is actually just straightforward SharePoint & ASP.NET.  I however have highlighted key and interesting points below.

General approach

The general approach is to extract the ID of the selected items, concatenate them into a delimited string, and then open an application page (in dialog mode) and pass the string as a query string parameter. The ID of the list is also passed to the application page. There is one application page for each of the action (publish, approve/reject, etc) and all of the processing is done in these application pages. When the application page closes, the list is refreshed.

Referencing JavaScript for the buttons

There are 2 CustomAction elements. One defines our custom buttons for the Ribbon, the other adds a reference to our JavaScript to the current page (i.e. the list view). This latter element looks like below. Location=”ScriptLink” is the bit that does the magic.

<CustomAction Id="BulkPublishingRibbonCustomActions.Script"
				  Location="ScriptLink"
				  ScriptSrc="BNH.SharePoint.BulkPublishing/BulkPublishing.js"/>

Enable/disable the buttons

The flyout button (the main one) should be disabled if no items are selected. This is straightforward. The child buttons however should be enabled/disabled depending on the settings of the list. Publish for example should be disabled if minor version is not enabled.

To get the settings of the list we need to make async calls using the SP JavaScript API. To make the EnabledScript for the button works with async however, we need to make use of the OOTB JavaScript method RefreshCommandUI(). This is described in this post: http://www.itidea.nl/index.php/using-async-call-to-enable-a-custom-ribbon-button.

The RefreshCommandUI() method essentially forces all the EnabledScript to be re-evaluated. However, it only seems to do it for the EnabledScript of the main button, not the child buttons. Furthermore, the EnabledScript of the main button is fired when the page loads or when items are selected. The EnabledScript of the child buttons are fired when the main button is clicked, i.e. when the flyout menu actually “flies out”.

The approach I have taken is to make the async calls in the EnabledScript of the main button to gather all the relevant settings and store them into variables. The EnabledScript of the child buttons simply check these variables.

Achieving the OOTB look and feel

If you have ever looked at the OOTB application pages, you’d noticed that special user controls are used to display each section on the page. Below for example is one section.

The two OOTB user controls used to create these sections are wssuc:InputFormSection and wssuc:InputFormControl. These controls are registered on the ASPX page as follow:

<%@ Register TagPrefix="wssuc" TagName="InputFormSection" Src="~/_controltemplates/InputFormSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="InputFormControl" Src="~/_controltemplates/InputFormControl.ascx" %>

The below markup demonstrates how to use these controls:

<wssuc:inputformsection id="formSectionCheckInComment" title="This is the bold heading on the left hand side" runat="server">
			<Template_Description>
<asp:Literal runat="server" text="This section is the explanatory text that appears beneath the bold heading on the left hand side."/>
			</Template_Description>
			<Template_InputFormControls>
<!-- This section contains the controls to appear on the right. -->
				<wssuc:InputFormControl LabelText="Label for the particular input field" runat="server">
					<Template_Control>
<!-- Define the control(s) that represent your input field in this section -->
						<textarea id="txtComment" class="ms-long" rows="5" cols="45" title="<%$Resources:wss,checkin_checkincomment_title%>" name="CheckinDescription" runat="server"></textarea>
					</Template_Control>
				</wssuc:InputFormControl>
			</Template_InputFormControls>
		</wssuc:inputformsection>

There is also a special user control for the buttons at the end. This user control is wssuc:ButtonSection. This is registered as:

<%@ Register TagPrefix="wssuc" TagName="ButtonSection" Src="~/_controltemplates/ButtonSection.ascx" %>

This control renders a Cancel button by default (although you can switch this off), which will close the modal dialog if the page is being displayed in dialog mode. This user control can be used as follow:

<wssuc:ButtonSection id="buttonSectionDefault" runat="server">
			<template_buttons>
<!-- Define your additional buttons here. The default Cancel button will be rendered in addition to these. -->
				<asp:Button UseSubmitBehavior="false" runat="server" class="ms-ButtonHeightWidth buttonOK" Text="<%$Resources:wss,multipages_okbutton_text%>" id="btnOK" OnClick="btnOK_Click" accesskey="<%$Resources:wss,okbutton_accesskey%>"/>
			</template_buttons>
		</wssuc:ButtonSection>

There are 2 important things to remember when using these controls:

1. All the form sections should be wrapped in a table tag as follow:

<table class="ms-propertysheet" border="0" width="100%" cellspacing="0" cellpadding="0">
<!-- Form section controls go here -->
</table>

2. Controls in the template sections need to be server controls, i.e. runat=”server”, otherwise they won’t be rendered.

Displaying and closing wait dialog after button post back in dialog mode

When the OK button is clicked (while the page is being displayed in dialog mode) a time consuming operation is launched and I wanted to display a wait dialog. When the post back is completed (i.e. the time consuming operation has finished) I wanted to close the wait dialog but remain on the original page (in dialog mode) to display the result.

I used the OOTB SP.UI.ModalDialog.showWaitScreenWithNoClose() JavaScript method but had some problems closing the wait dialog properly. I solved this in the end and wrote about it here: How to close SharePoint modal wait screen after postBack when page is in dialog mode.

Making the custom buttons available for all libraries

CustomActions typically can be registered against a list template, list URL or content type. I wanted to make these buttons available for the Pages library, document libraries (OOTB and custom templates), master page gallery, etc. The only way this could be achieved is to register the CustomActions with a base content type, such as Document. This is because when the CustomActions are registered against a content type, it will also be displayed for content types inherited (directly or indirectly) from the registered content type.

Advertisements

About Bernado

Based in Australia, I am a freelance SharePoint and Dynamics CRM developer. I love developing innovative solutions that address business and everyday problems. Feel free to contact me if you think I can help you with your SharePoint or CRM implementation.
This entry was posted in Custom Actions, Ribbon, SharePoint 2010. Bookmark the permalink.

28 Responses to Custom Ribbon buttons to bulk publish, approve/reject, cancel approval and unpublish multiple items

  1. David says:

    This is awesome!!! I’ve been looking everywhere for something like this! Thank-you for sharing your work. I’m having problems getting it to work in my environment (SP 2010 Foundation + VS 2012) though. I installed the CKSDEV package (http://visualstudiogallery.msdn.microsoft.com/a346880f-2d29-47a6-84a2-f2d568dd6997/) and got it to deploy, but I get a parser error: “The expression prefix ‘SPUrl’ was not recognized. Please correct the prefix or register the prefix in the section of configuration” in /_layouts/BNH.SharePoint.BulkPublishing/Publish.aspx. I’m not familiar with using SPUrl like this. Can you help me figure it out?

    • Bernado says:

      Hi David, it appears that error is because the $SPUrl token is not available in Foundation (http://chayadigital.wordpress.com/2011/10/04/error-with-spurl-tokens/).

      In the Publish.aspx page, this token is being used for the CssRegistration (there is only 1 instance on that page). You could try updating that page and replace the token with the full URL to your site instead – at least to see if the problem goes away.

      Since you are using Foundation, which does not have some of the publishing functionalities, I am not sure what else will break.

  2. Anup says:

    Thank you very much for this post. This is what i have been searching for. One questions though: I deployed the solution and everything is working perfectly except Bulk Approve/Reject. When i click on Bulk Approve/Reject it displays the Server Error in ‘/’ Application message. I could not figure out what is wrong.

    • Bernado says:

      Hey Anup, it’s going to be hard to troubleshoot with that error message. Have you disabled customErrors in web.config? Also, did you modify the code or just deployed as is? And are you using Server or Foundation edition?

      • Anup says:

        I have not disable the customErrors in web.config. Also, i did not modify the code. I just deployed the code as is. I am using Server 2010. Thank you again!!

      • Bernado says:

        Hi Anup,

        The real error message is hidden when customErrors is enabled. You should disable customErrors in the web.config to help troubleshooting.

  3. liz says:

    Can you use this with SP2013? It is such a bear approving bulk documents!

  4. andorful says:

    Thanks for this code. I have searched online for something like this and I just found your solution. One question I have is when I do bulk Publish Major Version the result is not the same. First, there is no screen to enter the name of the person who should approve the documents and second, when i do go through the bulk Publish it changes the Approval Status from Draft to Rejected instantaneously.
    Is there anything I am missing or I can change in the code.
    This is what I am wanting to see, please check the 3rd image in this link
    https://www.nothingbutsharepoint.com/sites/eusp/Pages/SharePoint-2010-Document-Management-Part-6.aspx

    • Bernado says:

      I’m afraid the current code does not have the capability to display the initiation form (the 3rd image you are referring to in the link) prior to launching the workflow. Instead, the workflow is launched for each selected item using the default settings specified when the workflow was associated with the list or content type.

      Regarding the items becoming Rejected instantaneously, I suggest checking that the Approvers are specified in the default settings when the workflow was associated with the list or content type.

  5. dmarchal says:

    Hi Bernardo,
    thanks for this code.I’m using the 2013 solution and I come across 2 issues :
    – In the function SPQuery BulkPublishingContext::GetQueryForIDs(List itemIDs), I had to add the following code to allow the list to find the listItem by ID in subfolders and not in the rootfolder only:
    query.ViewAttributes = “Scope=\”RecursiveAll\””;
    – When the Bulk Publish/Unpublish Succeed, if I clic on the Close button on the Dialog, it throws the exceptions
    – “0x800a1391 – JavaScript runtime error: ‘IsSodLoaded’ is undefined” in the function LoadSodInternal(sod,bSync) of the init.debug.js page.
    – then “0x800a1391 – JavaScript runtime error: ‘ULSSendExceptionImpl’ is undefined” inthe function ULSOnError(msg, url, line) of the same page.
    At the same time, the Debug log got some Exceptions like
    – Exception was thrown at line 2279, column 14 in init.debug.js “0x800a1391 – JavaScript runtime error: ‘CreateObject’ is undefined”
    – Exception was thrown at line 5439, column 21 in init.debug.js “0x800a01ad – JavaScript runtime error: Automation server can’t create object”
    Following some msdn forums, it seems to be an IE internet security issue but I could not figure out what is wrong.

    • Bernado says:

      Hey there,

      Great catch regarding items in sub-folders, and thanks for posting back your solution :).

      Regarding the IE JS error, which version of IE are you using?

      • dmarchal says:

        My version of IE is 10.0.9200.16921

      • dmarchal says:

        Indeed, i had a RTM version of IE. These exceptions still persist with the update version. Now, the dialog closes and the current page is well refreshed.
        Thanks.

  6. Pingback: Thomas Troppers Blog | Sharepoint Bulk approval of items

  7. Pingback: Thomas Troppers Blog | Multiple approval of items ins SharePoint – other approaches

  8. Mike F says:

    You have done a great job producing a solution that should be OOTB. Problem is – I have half a dozen clients on Office365 SharePoint 2013 (cloud). Any chance of getting a sandbox solution file (wsp) that could be uploaded?

    • Bernado says:

      Hi Mike,

      That would be a fun exercise indeed, and one I’d like to take on in the future – perhaps reworking it as an app if possible. It won’t be anytime soon though unfortunately as I’m a little snowed under at the moment 😦

  9. Hello Bernardo, could you please update the download link for 2013? I would like to download it but the link is dead….

    Thanks!

  10. alexyshen says:

    Hi Bernado. Thank you so much for this solution. I’ve looked everywhere and yours is the only one I can find. Hey, can you please post a compiled version for 2013? I do not have Visual Studio and have not compiled solutions before. Thank you!!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s