Wednesday, October 4, 2017

SharePoint 2013 Simple Document Sign Off (adapatable to SharePoint 2010)

What is it?

Simple document sign off is a series of SharePoint customizations that allow end users to sign off on documents that they have reviewed to allow formal tracking of document review. SharePoint allows approvals of documents from draft to a published state, but it does not natively allow sign off for end users on published documents.

There are many documents that may require a review and tracked sign off after they have been created or updated, including but not limited to:

  1. Employee Benefits
  2. Medical Testing Procedure Documents
  3. Key Business Procedures
  4. Contract Changes
  5. Equipment Usage Procedures
  6. Equipment Maintenance Procedures
  7. Emergency Protocols
  8. Changes to Invoicing Processes
The complete instructions on how to create this system are here:

Part 1Simple Document Sign Off - Background and Purpose
Part 2Simple Document Sign Off - System Overview
Part 3Simple Document Sign Off - Prerequisites and List/Library Setup
Part 4Simple Document Sign Off - Setting the Document ID
Part 5Simple Document Sign Off - The Sign Off Page
Part 6Simple Document Sign Off - Notification Workflow
Part 7Simple Document Sign Off - Sign Off Workflow
Part 8Simple Document Sign Off - Managing Document Sign Off

How does it work?

The document owner adds or updates a document in a document library. They then add in the users or group they want to notify that the document has been updated to the document properties, and click the "notify" button:

The end users then receive an email that gives them information about the document, a link to the document, and a link to the sign off page:

After reviewing the document the end user clicks the link to the sign off page and sees this:

After the user has registered their sign off, they can then review all of their sign off in the system:

Multiple user sign offs can be managed by the document owner in a central area. Who signed off on the document, the version signed off on, the date they signed off and the document name are all captured:


Multiple versions are managed through workflows configured in this system.

Wednesday, June 28, 2017

Display the current users Profile Image in SharePoint using EWS.

Introduction

If your IT department has configured it, you can retrieve the thumbnail images used in Outlook from the Exchange Server for your own purposes. This can be useful for customizing the look of various sites in SharePoint, such as employee dashboards.

This post describes how to retrieve and use the image, and how to handle the condition where the users email does not resolve to an image.


Retrieve the Image using EWS

The image is retrieved by using a URL of the following format:

https://Exchange Server/ews/Exchange.asmx/s/GetUserPhoto?email=email address&size=size code


Exchange Server = server that hosts exchange, URL provided by IT

email address = email of the user you are retrieving the image for

size = HR48x48 | HR64x64 | HR96X96 | HR120X120 | HR240X240 | HR360X360 | HR432X432 | HR504X504 | HR648X648

A sample url that would resolve to the user "Ian.mctavish@yourplace.ca" with a 64 x 64 thumbnail would be:

https://Exchange Server/ews/Exchange.asmx/s/GetUserPhoto?email=Ian.mctavish@yourplace.ca&size=HR64x64

Interesting, but how would we use this?

Using the Image on a Page

In its simplest state, you could include this as the url for an image and have it render on any page:

<img alt="Profile Image" src="https://Exchange Server/ews/Exchange.asmx/s/GetUserPhoto?email=Ian.mctavish@yourplace.ca&amp;size=HR64x64" />


This is great for displaying a specific user image on a page, but what if you want to display the current users image instead?

Displaying the Current Users Image


Download and reference JQuery

We can include the jQuery javascript libraries on our page, and use a couple of those functions to extract the image and set the value of the image on page load. You will first need to configure your SharePoint webpage to include the jQuery libraries by first downloading the libraries to your site assets folder, then referencing those libraries within your page:

<script type="text/javascript" src="/SiteAssets/jquery-1.11.3.js"></script>
<script type="text/javascript" src="/SiteAssets/jquery.SPServices-2014.02.js"></script>


Create Default Image

One of the things we need to anticipate is users who's email addresses do not resolve to images. We certainly don't want the dreaded "X" to appear when an image cannot be found. So we handle this by creating a generic profile image for all users and storing it within the SharePoint site. We will configure the page to first display this image, and then, if a profile image is found, then replace it with this image.

Set up the generic image (notice ID value) and put it on your SharePoint page:

<div id="UserImage"><img class="ihss-user-image" src="/Images/user.png" alt=""/></div>

This will resolve when the page loads to your generic user image.


Retrieve the Profile Image

Create a script section on your page and enter the following code:

$(document).ready(function() { 
  SetProfileImage();         
 });

  
 function SetProfileImage()
 {
  var thisUserEmail = $().SPServices.SPGetCurrentUser({
   fieldName: "Email",
   debug: false
  });  
  
var profileImagePath = 'https://exchange.yourweb.ca/ews/exchange.asmx/s/GetUserPhoto?email=' + thisUserEmail  + '&size=HR120x120';

     var img = new Image();
     img.onload = function() {
      //alert('image source: ' + img.src);
      //alert('image height: ' + img.height);
      var divHTML = "<img class=\"ihss-user-image\" src=\"" + profileImagePath + "\" />";
      //alert('image html: ' + divHTML);
      $('#UserImage').html(divHTML);
     }
     img.src = profileImagePath;
 }


The code functions as follows:

1. Once page has finished loading call set profile image
2. Retrieve current users email using SPServices.SPGetCurrentUser
3. Generate Exchange Server URL using email address
4. Configure onload function to update image to resolved profile path if found.

Leveraging the EWS to return user profile images is one way to display the current users Profile Image in SharePoint. The approach used here ensures that, if the user doesn't have an image, or the server is not responding, then the default profile image will be displayed instead.

Thursday, June 15, 2017

Expand the first group only on a SharePoint List View Web Part (SharePoint 2013)

If you add as SharePoint list view web part to a SharePoint page using SharePoint designer, you are given the ability to sort, group and filter the results of that web part for display.  If you choose to group by a specific element, then you can decide to either expand or contract all groups by default. Handy.


However, if you want to expand just the first grouping, and have the rest of the groups collapsed, there is no default setting to allow that to occur.  I have taken the original blog post posted here:


Original Blog Post


and have updated it for SharePoint 2013.


The original idea used here is sound, locate and click, using javascript and jquery, the plus sign located next to the first group.  However, using the web part ID did not work in my case, so I had to dig a bit deeper.


Here is the code I used to call the function:


<script type="text/javascript">


$(document).ready(function() {
   collapseGroups();
  });



And here is the slightly modified function:


function collapseGroups() {
   var closestElement = document.getElementById('group0');
   var myimg = closestElement.getElementsByTagName('img')[0];
   var mysrc = myimg.src;
   $(myimg).parent().click();
}

</script>




The challenging part of getting this working is identifying the document element that is closest to the + sign.  The original post used the name of the web part that hosts the list data, however, this did not work in my case.  Instead, it clicked the first columns sort header in the web part, and resorted the list by that column on page load!


So the approach is fine, click the + sign once you find it, we just need to find an element deeper in the list view web part.  Here is how I did it.


Navigate to your web part page and make note of the Group Header for your grouped column.  So if you were grouping by "Course Year", then "Course Year" would be what you are looking for.


Open the page in source view, and search for your grouping column name. When I searched for "Course Year" I got back html that looked like this:


<tr id="group0">
<td colspan="100" nowrap class="ms-gb">
<a href="javascript:" onclick="javascript:ExpCollGroup('0-1_', 'img_0-1_',event, true);return false;">
<img src="/_layouts/15/images/plus.gif" border="0" alt="expand" id="img_0-1_">&nbsp;Course Year</a>



You can see the plus image markup, plus javascript event associated with that markup that fires the button clicked event.  Unfortunately, between page refreshes, the image id changes, so I couldn't use that. 


However, the closest element with an ID was the table row (tr) that wrapped this line.  This tr has an ID of "group0".  I used this ID value in the collapse groups function, and it worked!  And it appears to be a more stable name than the ever changing values of the + image. 









Wednesday, March 22, 2017

Using SharePoint list images in Jquery getListItems

If you use JQuery's getListItems function to return an image from a SharePoint list, you will typically do something like this;


$().SPServices({
     operation: "GetListItems",
      async: false,
      listName: "leadsSelfContent",
      CAMLViewFields: "<ViewFields>

           <FieldRef Name='WebOptimizedImage' />
           ...
      </ViewFields>",
      CAMLQuery: "<Query>

                               ...
                             </Query>",
      completefunc: function (xData, Status) {
         $(xData.responseXML).SPFilterNode("z:row").each(function() {
           var ListItemImage = $(this).attr("ows_WebOptimizedImage");
            ....


This is standard JQuery get list items format.  However, what happens when you want to use that value?  You cannot set an image field directly to this value as it will not work.

Since SharePoint actually returns two values for the image, one being the URL, one being the description, we only want the URL.  So we need to extract it from the result returned from JQuery by using the following:


ListItemImage = $(this).attr("ows_WebOptimized").split(",")[0];


This extracts the URL, which we can then apply to our html field:


$("#PageImage").attr({src : ListItemImage});

Effecting this field in HTML:

<img class="main-picure" src="" id="PageImage"></img>


This works.  However, if we don't have an image in the associated list from SharePoint, then the entire script fails.  Why?  Because this line will throw an error when the field being returned is undefined:


ListItemImage = $(this).attr("ows_WebOptimized").split(",")[0];

So, we first need to test to see if a result has been returned for this field before returning, and capture the undefined value, as follows:

        
           if(typeof(ListItemImage ) === 'undefined')
           {
              ListItemImage  = "Empty";
           }
           else
           {
              ListItemImage  = $(this).attr("ows_WebOptimized").split(",")[0];
           }



Finally we test for the result before setting the html field:

if(ListItemAllLeadersimageField != "Empty")
           {
                 $("#PageImage").attr({src :ListItemImage });
            }



This way, if the field is empty, our script will still run.


The code in its entirety looks like this:




$().SPServices({
     operation: "GetListItems",
      async: false,
      listName: "leadsSelfContent",
      CAMLViewFields: "<ViewFields>

           <FieldRef Name='WebOptimizedImage' />
           ...
      </ViewFields>",
      CAMLQuery: "<Query>

                               ...
                             </Query>",
      completefunc: function (xData, Status) {
         $(xData.responseXML).SPFilterNode("z:row").each(function() {
           var ListItemImage = $(this).attr("ows_WebOptimizedImage");

           if(typeof(ListItemImage ) === 'undefined')
             {
                ListItemImage  = "Empty";
             }
           else
             {
                ListItemImage  = $(this).attr("ows_WebOptimized").split(",")[0];
             }
 
           if(ListItemAllLeadersimageField != "Empty")
             {
               $("#PageImage").attr({src :ListItemImage });
              }



This code will allow you to extract an image from a SharePoint list, test for an empty result, and then extract the URL from that result if not empty to set an html field.

Friday, March 17, 2017

Using SharePoint Picture Library thumbnail and web optimized images in other lists

Introduction



The post previous to this showed how to expose the thumbnail and web optimized images hidden from most users in a SharePoint Picture Library.


You could then use these images however you wish, to provide thumbnail views, use those thumbnails on pages, use the web optimized images wherever you would have used the original larger image, etc...


What if we want to take this to the next level?  What if we want to include these images in other lists so that users can select them, and we automatically have the web optimized and thumbnail versions there as well? 


If you add an image field to a SharePoint list, end users are expected to know how to add an image to a separate list repository, copy the URL of that picture, and paste it into the correct fields:




This process is a bit tougher than simply changing text or selecting an image.  What if we could also simplify the way users update the image?


So our goals here are:


1. Simplify user image selection process.
2. Make available thumbnail and web versions.




An Example



If we have a list that is "company announcements", where, every week, an new announcement and image is added, and this information is displayed as web part on the front page of the site.  This is a very simple implementation of SharePoint functionality, but it does run into two main challenges:


1. End users find the copying and pasting of an image url into an image field a more complex and error prone task.
2. End users sometimes add huge images to SharePoint, not knowing how to optimize them.


Also, if you have a list with image fields, views on that list that expose the field can be awkward and difficult to manage.  Replacing the image fields with the thumbnail versions makes the views appear much more manageable:




If we can simplify the way users select existing images in a list, and expose web optimized and thumbnail versions of those images, we can improve the user experience and leverage the optimized images for our own purposes.


Steps:



1. Create a picture library with the thumbnail and web optimized image paths exposed. This is described in the prior post.  For our purposes, the title field needs to be populated.  The simplest way to do this is make the title field mandatory, so that users adding the picture must add a title.  Or, you can create workflow that copies the picture name to the title whenever a new item is created.  Whichever works best for you.



2. Create a lookup field in your destination list to the picture library.  Include the thumbnail image URL and Web Image URL as returned fields as follows:






3. Create three new fields in your destination list.  They are Thumbnail (type picture), Web Optimized (type picture), and 'Prior Image Thumbnail Name' (type single line of text).


4. Open SharePoint designer and create a new workflow against your destination list. Set this workflow to run automatically whenever an item is created or changed.


5. Create two workflow variables, ThumbNailURL, and WebImageURL, both type string.


6. Create a condition on your workflow to only run if an image is added to the list:





7. Populate the local workflow variables with the lookup values extracted from the source list:







8.  Extract just the URL from the lookup values as they have additional text prepended to the front using the 'Extract Substring from Index of String' action:



9.  We only want to run this workflow if the picture has been updated.  So we test to see if the 'Prior Image Thumbnail Name' has been changed to detect this condition:






10.  Now we set both of the image type path values, and we set the thumbnail name for testing against as in step 9:




11.  The entire workflow should look like this:



Save and publish this workflow.  After ensuring the image exists in the picture library, add the picture using the title lookup on your destination list.  This workflow should autopopulate both the thumbnail and web optimized image fields for your usage.

Conclusion


You can now expose those images for your own usage from the list, such as thumbnails for views, or using in web parts on other pages.

In our company announcement example, we now have a simpler way for users to select an image once they have added it to the picture library (drop down instead of copying and pasting url), and we have the two additional image types generated.  The web optimized version would be used on the company announcement web part on the home page, rather than the image uploaded by the user.  This will not only make the page more efficient, but can take care of styling issues caused by importing huge images.





Wednesday, March 15, 2017

Exposing SharePoint Picture Library Thumbnail and Web Optimized Images

Introduction



The SharePoint picture library has some built in functions that can be very handy for usage in other areas of SharePoint.  Every image that is added to the library gets two versions created that are hidden from the end user.  One is a web optimized jpg image.  The other is a thumbnail image.


SharePoint uses these images for its own purposes, the web optimized images are used in publishing sites, and the thumbnails allow the picture library to have thumbnail based views.  The picture library is a useful tool for managing and exposing pictures.  By extracting the web optimized and thumbnail paths of these images, we can then use those paths for our own purposes where we need either the thumbnail or web optimized version.


This post shows how to pull the "hidden" paths to the images through a workflow and expose them for your own use.




Create a Picture Library



If you haven't created one already, you will need to first start with a SharePoint picture library.  These are available by default on Publishing sites, and on other sites where the "Team Collaboration" list feature is used.




You may have to deactivate and then reactivate this feature to get the picture library to show up.  Use "Site settings" -> "Add an App" and search for the picture library to find the icon below.


Add the picture library to your site.



Add two new fields to the Image Library



We need to fields to hold the image paths within our picture library.  Create two new columns, plain text.  One called WebImageURL.  And one called ThumbnailImageURL.





Create a Workflow to Populate Fields



Now that we have the fields available, we can populate them using a workflow.  Create a new workflow in SharePoint bound to this picture library.


Create a new "Action" step, "Set field in current item".  Choose "ThumbnailImageURL" as the field, and select "Current Item - Thumbnail URL" as the value:




Repeat the step for the web image url, setting field to WebImageURL and value to current item, Web Image URL.  Notice that the built in fields have spaces in their names. We are copying the info in those built in workflow fields over to our own fields to expose them.


One you are done your workflow should look like this:




Set this workflow to run automatically whenever an item is created.


Now, when you add an item to this list, the additional fields you created now expose the paths to the optimized images:



These paths can now be copied and pasted anywhere within SharePoint to use the optimized versions of the images.

Friday, February 24, 2017

Setting and Getting Checkbox Values using SPServices and JQuery

I was given a requirement for a project where a user was presented with two questions, each with a checkbox related.  The two questions were:


1. I have read and understood this document
2. This document does not apply to me


The user is only allowed to select one of these choices.  How do I support this requirement using a form with checkboxes and JQuery?  The following code meets these requirements, see comments for line by line explanations.


<script src="../SiteAssets/jquery-1.11.3.js" type="text/javascript"></script>
<script src="../SiteAssets/jquery.SPServices-2014.02.js" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
 


  $(document).ready(function () {
   alert('JQUERY LOADED');  //A test to ensure JQuery paths correct.  Comment out for prod
  
   $("input[type='checkbox']").click(function() {  // detect checkbox check
    var checkboxName = ($(this).attr('name'));  // get the name of the checkbox that was just clicked
   
    if (checkboxName == 'Checkbox1') {  // if the first checkbox is checked
     $('input[name=Checkbox2]').attr('checked', false);  // uncheck the second one
    }
    if (checkboxName == 'Checkbox2') {  // if the second checkbox is checked
     $('input[name=Checkbox1]').attr('checked', false);  // uncheck the first one
    }
   
   });

  });


</script>


<input name="Checkbox1" type="checkbox" />I have read and understood this document.<br/>
<input name="Checkbox2" type="checkbox" />This document does not apply to me.<br/>

Monday, January 30, 2017

Google Fonts not rendering correctly in IE 11

Problem

Google fonts provide a rich group of both unlicensed and licenced fonts ready for download and customization.  These fonts are used extensively throughout the web.


Their usage is simple, add the fonts header in your html code as follows:


<link href="https://fonts.googleapis.com/css?family=Roboto:400,100,100italic,300,300italic,400italic,700,500italic,500,700italic,900,900italic" rel="stylesheet" type="text/css"></link>


However, if you use IE 11, while the base font (Roboto) is downloaded, the bold weight and italic modifiers do not work.  So your site will look different in IE 11 than any other current browser.


Solution

It turns out that IE11 has a problem downloading everything after the first designation in the font family, so "Roboto:400" is downloaded in the above link, but everything else is missed.  The solution is to call each variation separately so that all components are downloaded, such as follows:

<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:400" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:100" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:100italic" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:300" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:300italic" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:400italic" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:700" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:500italic" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:700italic" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:900" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:900italic" />


I put both calls in the html head of the code as it seems to work best that way. Hopefully this will save you some time looking for the solution yourself.