Thursday, May 12, 2016

Making a shopping cart in SharePoint?!

So very recently my team had a requirement to build some sort of cart system so users within a SharePoint site could easily search for documents from disparate locations and add them to a cart to perform an action on them (like approvals and download).  This is quite a unique requirement so we hunkered down and started developing a solution.

To get started with this tutorial, you need some good SharePoint and jQuery knowledge as I cannot explain all the basics.  The first thing you need is a SharePoint search results page.  Go to the display template you will be using for the results and add a checkbox with basic html.  In the screenshot you will also notice we added additional document information that’s already being pulled back when a search query is issued. 

 

image

 

image 

We want to capture as much context about that document so when we add it to the cart the no additional information will need to be fetched.  So once you get your checkbox in place, go ahead and create a SharePoint list called “Doc Cart”.  We will be using this list to store all users cart information so it can be retained if they choose.  You will notice in my list I created the document name field as a URL field so I can store both the doc name and the url in one field that can be called.  I am storing the document icon so I can pull it back later as well. 

image

 

So back to the search results page.  By now you should have your checkboxes coming back from all search results because you selected the correct display template that you edited.  The next part you need to do is add a button somewhere on the page.  In my example, I put the button within the control template so its contained within the search results web part.

image

image

The function addDocument () will loop through all checked items and grab all the info from each document.  Make sure you understand DOM traversal in jQuery or this part can really hang you up.  This code should be very straight forward.  We are looping through all checked items, grabbing the data, setting the data within the data var for POST, and then making a POST to the doc list you created earlier.  When it POSTs, we will uncheck the documents and give the user a “toast” using the materalize plugin so the user knows it was added.  In our case we also have a nav item called “Document Cart” that we are updating to ensure its displaying the current document number in the cart.

image

function addDocument () {

    //only run on documents that are checked

$('.carts:checked').each(function(index,value){

    //keep looping while there are checked documents

    if ( $(this).is(':checked') ); {

        //sets TR background color to indicate item was added to cart

         $(this).parent().parent().css('background-color', '#CAE1FF');

            //grabs current web

            var currentURL = _spPageContextInfo.webAbsoluteUrl;

            //going up one web to location where doc list lives

            var desiredURL = currentURL.substring(0,currentURL.lastIndexOf("/"));

            //constructs REST url for POST

            var reqURL = desiredURL + "/_api/Web/Lists/GetByTitle(\'doc list\')/Items";

            //all vars are grabbing document context for POST

            var ownerNamevar = $(this).parent().parent().find(".document-owner").text();

            var docDate = $(this).parent().parent().find(".document-date").text();

            var docStatus = $(this).parent().parent().find(".document-status").text();

            var docRev = $(this).parent().parent().find(".document-revision").text();

            var docId = $(this).parent().parent().find(".doc-id").text();

            var docImg = $(this).parent().parent().find(".doc-id").find("img").attr("src");

            var docURL = $(this).parent().parent().find(".document-path").attr("href");

            var docName = $(this).parent().parent().find(".search-title").text();

            var documentIDvar = $(this).parent().parent().find(".document-itemID").text();

            //formatting data var for POST

            var data = {

                __metadata: { 'type': 'SP.Data.DoclistListItem' },

                Owner: ownerNamevar,

                dateondoc: docDate,

                Status: docStatus,

                Revision: docRev,

                docid: docId,

                img: docImg,

                DocumentID: documentIDvar,

            Document: {

                __metadata: { "type": "SP.FieldUrlValue" },

                Url: docURL,

                Description: docName,

                  },

                 };



            $.ajax({

                url: reqURL,

                type: "POST",

                headers: {

                    "accept": "application/json;odata=verbose",

                    "X-RequestDigest": $("#__REQUESTDIGEST").val(),

                    "content-Type": "application/json;odata=verbose"

                  },

                data: JSON.stringify(data),

                success: function (data) {

                            //updates users cart total after a document is added

                            userCartTotalSearch();

                  },

                error: function (error) {

                    console.log(JSON.stringify(error));

                  },

            });

                //unchecks the document

              $(this).prop('checked',false);

              //unchecks select all if checked

              $('.select-all').prop('checked', false);

              var allCheckBox = $(".carts");

              var count_checked = allCheckBox.filter(":checked").length;

              //creates add to cart toast for user

              Materialize.toast('Added To Cart', 4000);



}

});



}

 

Now that you have that document info correctly posting to the list, you need to construct a page so the user can actually view the cart info.  Viewing this list data would be a bad way to display this to your user.  In a content editor you need to throw some html into a file and reference it.  We will be using basic table rows.

  <div id="#collapse1" class="panel-collapse collapse in">

      <div class="panel-body">

        <table class="table" id="doctableHeader">

          <thead>

            <tr>

              <th>DOCUMENTS</th>

              <th>OWNER</th>

              <th>DATE ON DOC</th>

              <th>STATUS</th>

              <th>REVISION</th>

              <th><a data-toggle="modal" data-target="#myModal" href="#">Remove ALL</a>   <span class="fa fa-times-circle"></th>

            </tr>

          </thead>

          <tbody class='cartitems'>



        </tbody>

</div>

</div>

the tbody class is the container we will use for appending all the document information to.  The code is pretty straight forward.  Honestly this would an ideal use for Angular so we don’t have to build all the html like I am doing but there is always 100 ways you can do things so here is how I did it.  notice on the url contruct I am filtering on the Created By column using the token _spPageContextInfo.userId.  This will only return items from the cart that user added.  We can use one list for many users this way.

function getCart () {

//url for GET

  var reqURL = _spPageContextInfo.webAbsoluteUrl + '/_api/web/lists/getbytitle(\'doc list\')/Items?$filter=AuthorId eq ' + _spPageContextInfo.userId

  console.log(reqURL);

  $.ajax({

    url: reqURL,

    type: "GET",

    headers: {

      "accept": "application/json;odata=verbose",

      "X-RequestDigest": $("#__REQUESTDIGEST").val(),

      "content-Type": "application/json;odata=verbose"

    },



    success: function (data) {

      //loop through results and build html

      jQuery(data.d.results).each(function (i) {

        //build html var

        var html = '<tr id="row'+this.Id+'"class="countme">'

        html += '<td style="display:none" id="doc-library-id">'+ this.DocumentID +'</td>'

        html += '<td><a href="'+ this.Document.Url +'"><span class="doc-id">' + this.docid + '</span><br>' + this.Document.Description + '</a></td>'

        html += '<td>' + this.Owner + '</td>'

        html += '<td>' + this.dateondoc + '</td>'

        html += '<td>' + this.Status + '</td>'

        html += '<td>' + this.Revision + '<img style="float: right" src="'+ this.img +'"></img></td>'

        html += '<td><a onclick="deleteMe ('+ this.Id + ')" href="#">Remove</a><span style="color: red;padding-left: 15px"class="fa fa-times-circle"></span></td>'

        html += '</tr>'

        $('.cartitems').append(html);



      });

      var html = '<div>'

      //build url for redirect button

      var redirect = _spPageContextInfo.webAbsoluteUrl + '/projectsearch/Pages/results.aspx'

      html += '<div style="float: right"><a href="'+ redirect +'"><span class="fa fa-angle-left"></span> ADD MORE DOCUMENTS</a></div>'

      html += '</div>'

      $('.panel-body').append(html);

      //display cart total

      var total = $(".countme").length;

      $("#doctotal").text(total);



    },

    error: function (error) {

      alert(JSON.stringify(error));

    }

  });



});

We you should end up with is something similar to the screenshot below. Your table will not look like mine because there is heavy styling on this.  What you will notice is that you can perform a lot of actions from here.  You can: remove cart items individually, remove all cart items, download a ZIP file of the cart, send an email with all the documents, and create an approval on the set.  Later blog posts will explain how you can accomplish this. 

 

image