————
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.
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?
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.
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.
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?
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!!
Hi Anup,
The real error message is hidden when customErrors is enabled. You should disable customErrors in the web.config to help troubleshooting.
Can you use this with SP2013? It is such a bear approving bulk documents!
Hi Liz, I haven’t had a chance to try this in SP2013. It’s on my to-do list :).
Actually Liz, I have gone ahead and converted this solution to SP2013 today. The link for the 2013 download is at the top of the post. Let me know if you run into any issues.
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
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.
Thanks for the explanation. Is there a way you can point me to the right direction so I can include the initiation form into this code?
Hi Bernardo
Would you be interested in being paid to implement this feature?
Hi Carter,
Appreciate the offer, but I’m afraid I don’t have capacity to do this right now. Would be a fun exercise though!
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.
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?
My version of IE is 10.0.9200.16921
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.
Pingback: Thomas Troppers Blog | Sharepoint Bulk approval of items
Pingback: Thomas Troppers Blog | Multiple approval of items ins SharePoint – other approaches
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?
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 😦
Hello Bernardo, could you please update the download link for 2013? I would like to download it but the link is dead….
Thanks!
Hi there,
I just tested the link and it is working fine. Can you please give it another go?
No, the same blank page with just the words “Previous fileNext file” at the top of the page… 😦 Could you please sent it to me directly? mazzeie@hotmail.com
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!!!
I have added a link to download the WSP at the top of the post :).
Bernado. You are absolutely awesome. Thank you!!!
Bernado, I’m rather new to the deploying of custom code in SharePoint… Can you explain the steps to deploy your code for a SharePoint list?? Many thanks!
Where is the link to download the 2010 version?
The 2010 version can be downloaded from here: https://www.box.com/s/083b07c27bfe3ae63f2a.