SPServices Stories #8 – CEWP, Nintex, jQuery, SPServices and the Client API
- SPServices Stories #1 – How to Start a Workflow on Multiple Items in a List
- SPServices Stories #2 – Charting List Data with HighCharts
- SPServices Stories #3 – AddWebPart Method of the WebPartPages Web Service
- SPServices Stories #4 – Using SPServices to Process Files from a Non-Microsoft Source
- SPServices Stories #5 – Gritter and Sharepoint: Integrate Nifty Notifications in Your Intranet!
- SPServices Stories #6 – Custom Quizzing System
- SPServices Stories #7 – Example Uses of SPServices, JavaScript and SharePoint
- SPServices Stories #8 – CEWP, Nintex, jQuery, SPServices and the Client API
- SPServices Stories #9: Developing with Client-side Technologies: jQuery, REST, SPServices and jsRender
- SPServices Stories #10 – jqGrid Implementation Using SPServices in SharePoint
- SPServices Stories #11 – Using SPServices and jQuery to Perform a Redirect from a SharePoint List NewForm to EditForm
- SPServices Stories #12 – SharePoint and jQuery SPServices in Education: A Case Study
- SPServices Stories #13: Durandal SP3: Developing SharePoint SPAs Made Easy
- SPServices Stories #14: Create a Slide Show in SharePoint 2010 using an Announcements List and SPServices
- SPServices Stories #15: Custom Client-side Calendar Query for Office 365 SharePoint Site Using SPServices jQuery Library
- SPServices Stories #17: Multiple Form Fields Autocomplete for SharePoint 2010/2013 using JavaScript
- SPServices Stories #18 – Retrieve Managed Metadata Using JavaScript and SPServices
- SPServices Stories #19 – Folders in SharePoint are as necessary as evil. Make the best of it using jQuery and SPServices.
- SPServices Stories #20 – Modify User Profile Properties on SharePoint Online 2013 using SPServices
- SPServices Stories #22 : SPServices SharePoint Attachments in Internet Explorer 9
- SPServices Stories #21 – Redirect If User Clicked a Button Previously
Introduction
I’ve been keeping a list of older posts about using SPServices that I’ve seen on the InterWebz for that day when I finally got around to doing something like SPServices Stories. This one is from Dan Stoll (@_danstoll) at Nintex, and was originally published on Dan’s blog back in February, 2012 as CEWP, Nintex, jQuery, SPServices and the Client API.
To me, the fact that someone at Nintex would choose to use SPServices to accomplish something with workflows is pretty telling, in a positive way. The folks over at Nintex know what they are doing, and if they choose to use SPServices as spackle to fill in some of the gaps in SharePoint, then it’s testament to the value of it.
In SPServices Stories #7, John Liu showed an example of starting a workflow with SPServices, and here Dan gives us some of the details under the covers.
CEWP, Nintex, jQuery, SPServices and the Client API
I had a requirement that required a not so difficult solution but tricky.. The first requirement was that a webpart had to be embedded in to a publishing page layout that was used over multiple sites in the farm. The next requirement was to only show this webpart to a select group of people.. Easy right ? Using audiences should work a treat…. Wrong. . Embedding the CEWP you can still put in the audience but it doesn’t work because it isn’t in a webpart zone… Ok, so let’s put a webpart zone in.. Pop in my custom CEWP webpart.. The audience set.. but still no joy.. Audience is working but because my webpart is in a webpart zone, it doesn’t show up.
So now I’m faced with .. “How do I get a default webpart to appear on 500 + pages that already exist?? ” Back to square one.. There has to be a way of telling a Div to hide if you aren’t part of a group of some kind. So let’s look at this.. Here is an extract of my page layout
<div class="certifiedPanel" style="display: none;"> <div class="article-before-wp"> <h3 class="ms-standardheader ms-WPTitle"> <span>Details</span> </h3> </div> <div class="article-meta article-wp"> <table style="width: 100%;"> <tbody> <tr> <th class="style1" style="width: 36%;">Contact:</th> <td id="pubContact"></td> </tr> <tr> <th class="style1" style="width: 36%;">Last Modified:</th> <td id="modifiedOn"></td> </tr> <tr style="display: none;"> <th style="width: 36%;">Last Review Date:</th> <td id="lastReviewedOn"></td> </tr> <tr> <th class="style1" style="width: 36%;">Next Review Date:</th> <td id="reviewedOn"></td> </tr> <tr> <th class="style1" style="width: 36%;">Certified Date:</th> <td id="certifiedOn"></td> </tr> <tr> <th class="style1" style="width: 36%;">HYway DOCID:</th> <td id="docId"></td> </tr> <tr> <th class="style1" style="width: 36%;">Reference ID:</th> <td id="refId"></td> </tr> </tbody> </table>Managers PanelNoneUse for formatted text, tables, and images.true2NormaltruetruetruetruetruetruetrueModeless <dir>Default</dir>Cannot import this Web Part.true00000000-0000-0000-0000-000000000000g_73d6de6b_445e_4d9a_8443_c05b20548336/OurProcesses/Documents/startworkflow.txt</div> <div class="article-after-wp"></div> </div>
You’ll see in the first line, I have included a “display:none” style on the DIV called “certifiedPanel” The rest of it, well the first half of the Certified Panel shows a few fields from the content type, eg Modified Date, ID etc etc.. The second half is the CEWP that has a text file as it’s source of content ‘startworkflow.txt’ .. This is where part 2 of the story begins..
The other requirements is that this “Panel” had to show indicators as to the phases of the document, it also had to have a link to the version history of the document and there also had to be a couple of workflows that could be executed against the page (content type) from this panel.. Firstly, let me show you the panel
The 3 indicators shown here, show the manager at a quick glance that the document isn’t certified (Yellow) because the review date has passed, it isn’t ready for Review either (Red) as the Contact hasn’t been filled in. The only parameter that is ok is that the Review date as been set with “something” (Green)
As you can see the document has been modified since being certified so this document is now no longer certified as changes may have been made to the content…
Ok so how did we do this.. I’ll post the full contents of the startworkflow.txt file and we can work from there. (the Startworkflow.txt) is used with the Content Editor Webpart to display on the page
[Ed: I’ve split the HTML and JavaScript sections for better readability.]
var $ = jQuery; $(document).ready(SetPageIndicators); function SetPageIndicators() { //get the certified and reviewed indicators var certifiedOn = new Date($("#certifiedOn").text()); var hasCertDate = !isNaN(certifiedOn); var modifiedOn = new Date($("#modifiedOn").text()); var hasModDate = !isNaN(modifiedOn); var reviewOn = new Date($("#reviewedOn").text()); var hasReview = !isNaN(reviewOn); var lastReviewedOn = new Date($("#lastReviewedOn").text()); var haslastReviewedOn = !isNaN(lastReviewedOn); var baseUrl = "/Style%20Library/hyway/Images/"; //set certified icon var certImg = $("#certImg"); if (!hasCertDate) certImg.attr("src", baseUrl + "statusRed.png"); if (hasCertDate) certImg.attr("src", baseUrl + "statusGreen.png"); if ((hasModDate && hasCertDate) && (modifiedOn.setDate(+1) > certifiedOn)) certImg.attr("src", baseUrl + "statusYellow.png"); //set lastReviewedOn icon var lastReviewedOnImg = $("#lastReviewedOnImg"); if (!haslastReviewedOn) lastReviewedOnImg.attr("src", baseUrl + "statusRed.png"); if (haslastReviewedOn) lastReviewedOnImg.attr("src", baseUrl + "statusGreen.png"); if (lastReviewedOn > certifiedOn) lastReviewedOnImg.attr("src", baseUrl + "statusYellow.png"); //set review icon var reviewImg = $("#reviewImg"); if (!hasReview) { reviewImg.attr("src", baseUrl + "statusRed.png"); } else { if (reviewOn > new Date()) { reviewImg.attr("src", baseUrl + "statusGreen.png"); } else { reviewImg.attr("src", baseUrl + "statusYellow.png"); } } //Hide the Panel from those who don't need it ExecuteOrDelayUntilScriptLoaded(function () { var ctx = SP.ClientContext.get_current(); var web = ctx.get_web(); // change ID based on // http://devserver/OurProcesses/_layouts/people.aspx?MembershipGroupId=551 // http://server/OurProcesses/_layouts/people.aspx?MembershipGroupId=752 var group = web.get_siteGroups().getById(551); ctx.load(group); var users = group.get_users(); ctx.load(users); var user = web.get_currentUser(); ctx.load(user); ctx.executeQueryAsync(Function.createDelegate(this, function () { // success for (var i = 0; i < users.get_count(); i++) { var u = users.get_item(i); //alert(u.get_loginName()); if (u.get_loginName() == user.get_loginName()) { //alert("found you"); $(".certifiedPanel").show(); } } }), Function.createDelegate(this, function () {})); }, "sp.js"); } function StartWorkflow(ItemURL, WorkflowName) { var waitDialog = SP.UI.ModalDialog.showWaitScreenWithNoClose('working on it….', 'Please wait while Gnomes get this sorted for you...', 76, 330); // start the workflow manually, then take the TemplateID from the URL of the Workflow starting form // this is no good since it changes when you republish workflow... // get TemplateID first for item. $().SPServices({ operation : "GetTemplatesForItem", item : ItemURL, async : false, completefunc : function (data, status) { var workflowTemplateID; if (status == "success") { $(data.responseXML).find("WorkflowTemplates > WorkflowTemplate").each(function (i, e) { // hard coded workflow name if ($(this).attr("Name") == WorkflowName) { var guid = $(this).find("WorkflowTemplateIdSet").attr("TemplateId"); if (guid != null) { workflowTemplateID = "{" + guid + "}"; } } }); } else { // error can't find template alert(status + " : " + data.responseText); return; } // start workflow now with obtained templateID. Note this must run within the completeFunc of the first webservice call $().SPServices({ operation : "StartWorkflow", item : ItemURL, templateId : workflowTemplateID, workflowParameters : "<root />", completefunc : function (data, status) { waitDialog.close(); if (status == "error") { alert(status + " : " + data.responseText); } else { document.location.reload(); } } }); } }); } function certImg_onclick() {} function ShowVersionHistory() { if (!_spPageContextInfo) { return; } var options = { tite : "Versions", url : _spPageContextInfo.webServerRelativeUrl + "/_layouts/Versions.aspx?list=" + _spPageContextInfo.pageListId + "&ID=" + _spPageContextInfo.pageItemId, allowMaximize : false, showClose : true, width : 800, height : 500, }; SP.UI.ModalDialog.showModalDialog(options); }
<table style="width: 100%"> <tr> <td> <img id="certImg" alt="Page Certified Indicator" src="" onclick="return certImg_onclick()" height="20px" width="20px" />Certified</td> <td> <a href="javascript:StartWorkflow(document.location, 'Super Stamp')"> Certify Process Page</a> </td> </tr> <tr> <td> <div> <img id="reviewImg" alt="Page Review Indicator" src="" height="20px" width="20px" />Reviewed</div> </td> <td> <a href="#" onclick="javascript:ShowVersionHistory();">View Modification History</a> </td> </tr> <tr> <td> <img id="lastReviewedOnImg" alt="Page Ready for Review Indicator" src="" onclick="return lastReviewedOnImg_onclick()" height="20px" width="20px" />Ready for Review</td> <td> <a href="javascript:StartWorkflow(document.location, 'ReadyForReview')"> Ready for Review</a> </td> </tr> </table>
As you can probably guess there is are a couple of parts to this txt file..
- The first being jquery switching the indicators depending on the rules set and the values of certain date fields.
- The second part is using the client API where we are setting the ID of the SP group (note you can’t have any nested SP or AD groups here) that has access to this panel.. This then sets the DIV certifiedPanel display to show.
- Part 3 is using SPServices http://spservices.codeplex.com/ to call Nintex Reusable Workflows that are bound to the custom content type that I am using for these Publishing Pages. As Nintex Workflow use the same infrastructure as SharePoint workflows, the rich feature set of SPServices, allow me to start my Nintex Workflows and a whole host of other things.
These workflows not only update the Page in question but also assign tasks to the review committee, and maintain a centralised list of all pages that are currently under review, / are due for review or don’t comply to the companies guidelines..
On approval of the task, the pages are certified within the companies “Processes” site. The JavaScript indicators reflect its new status, and the document is removed form the centralised management list.
I was thinking about a reverse strategy.
Placing the webpart on a blank layout page (no master page) then loading it in to the page. Then using $.ajax perform the post request on the webpart, and reloading it on response if it has a form.
I haven’t tested this theory yet, but I really like the ideas in this page.
The basic idea is taking the minimal download strategy to a new level and SPServices can really help with this in 2010.
I’ve implemented something similar in a layout page itself, but not actually embedding a control in a page using ajax.
It would be a very fast and lightweight loading sharepoint.