Blog

New Code Projects: Backblaze B2 Version Cleaner & VBA SharePoint List Library

Itā€™s been a while since Iā€™ve posted code of any description, but Iā€™ve been working on a couple of things recently that Iā€™m going to make publicly available on my GitLab page (and my mirror repository at code.jnf.me)

Backblaze B2 Version Cleaner

I wrote last week about transitioning my cloud backup to Backblazeā€™s B2 service, and I also mentioned a feature of it thatā€™s nice but also slightly problematic to me: it keeps an unlimited version history of all files.

Thatā€™s good, because it gives me the ability to go back in time should I ever need to, but over time the size of this version history will add up – and Iā€™m paying for that storage.

So, Iā€™ve written a script that will remove old versions once a newer version of the same file has reached a certain (configurable)Ā ā€œsafe age.ā€

For my purposes I use 30 days, so a month after Iā€™ve overwritten or deleted a file the old version is discarded. If I havenā€™t seen fit to roll back the clock before then my chance is gone.

Get the code here!

VBA SharePoint List Library

This one I created for work. Getting data from a SharePoint list into Excel is easy, but I needed to write Excel data to a list. I assumed thereā€™d be a VBA function that did this for me, but as it turns out I was mistaken – so I wrote one!

At the time of writing this is inĀ ā€œproof of conceptā€ stage. It works, but itā€™s too limited for primetime (it can only create new list items, not update existing ones, and each new item can only have a single field).

Out of necessity Iā€™ll be developing this one pretty quickly though, so check back regularly! Once itā€™s more complete Iā€™ll be opening it up to community contributions.

I have no plans to add functions that read from SharePoint to this library, but once I have the basic framework down that wouldnā€™t be too hard to add if youā€™re so inclined. Just make sure you contribute back!

Get the code here!

Blog

SharePoint on a Domain Controller Revisited

On Tuesday I wrote about installing SharePoint Foundation
2010
on my home windows server, which also acts as a domain controller, and I
concluded by saying that Iā€™d encountered performance issues as a result of that
(non-recommended) setup.

Turns out, the performance issues were a complete coincidence,
and everything is now running just fine.

The problem I was experiencing was that two of my three forward
DNS servers werenā€™t working correctly. Now that my
service provider
has corrected their issue, everything is great.

image

For a small setup like mine, Iā€™d say go ahead
and install SQL Server Express and SharePoint on the domain controller. It
works great!

Blog

Installing SharePoint Foundation on a Domain Controller

Itā€™s been a long time since I blogged about SharePoint, and thatā€™s
largely because I havenā€™t had a need to develop anything custom on top of the
platform for quite some time.

If youā€™ve been following along for a long time, you may
recall that back at the start of last year I installed
SharePoint foundation on a Windows 7 Virtual Machine
at home for testing
purposes and, while I didnā€™t blog about it explicitly, when I upgraded
my home server
last August I replaced that Windows 7 virtual machine (which
ran on my laptop) with an always-on Windows 2008 R2 VM, again running
SharePoint foundation.

As my home network continued to evolve I turned that Windows
Server VM into a domain controller, and this broke my SharePoint installation ā€“
but by then it wasnā€™t all that important and I didnā€™t need it for work anymore,
so I simply uninstalled it.

Recently, Iā€™ve been missing having SharePointā€™s
functionality at home. In particular, I wanted a shared calendar for Flo and I,
and a place for shared documents. We can achieve much of this with Google
calendar and our existing shared folders (and I already have a tool deployed that makes our network shares
available from outside our home network), but it all feels a little kludged together
and itā€™s lacking features like NTLM based SSO and an easy way to edit files
from the web-interface that SharePoint provides out of the box. I looked at a
couple of alternative
solutions and wasnā€™t satisfied.

Previously Iā€™d deployed SharePoint foundation in standalone
mode. This installs and runs all the required components on a single machine.
Itā€™s not recommended for a full-scale deployment, but itā€™s perfect for our home
network. The problem is that this simply isnā€™t an option if you install it on a
domain controller, and instead you have to install a server farm. In googling
around, the consensus online seemed to be that it wasnā€™t possible to install
SharePoint on a single server if that server was also acting as the domain
controller.

Not so.

It is possible, and in fact itā€™s pretty easy. I made a
couple of missteps by attempting to follow along with what some other people
had done, but the solution was actually extremely simple: first you need to
install SQL
Server 2008 R2 SP2 express
(and it has to be at least this version), then
you install SharePoint
Foundation 2010
. For all the discussion online, I actually didnā€™t have to do anything other than accept the default options to install SQL server. When I installed
SharePoint it doesnā€™t give me the option to install in Standalone mode, but I
simply pointed it to the SQL Server instance I had installed and that was all
there was to it.

That being said, things are not all that rosy. Just because
this setup is possible, doesnā€™t mean itā€™s recommended ā€“ and this is certainly
not Microsoftā€™s recommended way of doing things.

Microsoft advocate for a separation of server duties, and
having different, unrelated services running on different machines. Now that Iā€™ve
entirely eschewed that philosophy I can see why itā€™s important: SharePoint is
running well and performance is snappy, but general internet performance on our
home network has suffered, and I believe the fact my single Windows server is
also the DNS server for the network is the problem ā€“ DNS lookups are slow.

I may try and solve this by trying to install a slave DNS
server on the Linux server we have, but if not then I think SharePoint will
have to go away in the interests of DNS performance.

Or, maybe I just add a second physical server and move a few
of the VMs to that? Weā€™ll see.

Blog

SPServices SharePoint Attachments in Internet Explorer 9

A little over eight months ago I wrote a very brief post about using SPServices to add attachments to a SharePoint list. Full credit here goes to Brendan Wilbore who wrote the blog post that I linked to.

There was a problem, though ā€“ the solution relies on the fileReader JavaScript feature which requires Internet Explorer 10, and the default browser deployed within my organization is Internet Explorer 9. What we need is a fileReader alternative for older browsers. Thankfully, such a thing exists. Today Iā€™m going to post some example code that uses the fileReader polyfill and works in older browsers.

What You Need

The code has several pre-requisites. Youā€™ll need jQuery, jQuery UI, SPServices, SWFObject and the JavaScript and flash file that form the fileReader polyfill.

For the purposes of my demo I created a simple SharePoint list called ā€œFile Attachment Test.ā€ The list has a single field ā€“ title ā€“ and attachments to the list are enabled. Your list is probably named differently, so youā€™ll need to change the references in the code to reflect your list name.

The Code

<html>
<head>
   <meta charset="utf-8" />
   <title>File Attachment Test</title>
   http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
   http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js
   http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js
   http://js/jquery.FileReader.min.js
   http://js/jquery.SPServices-2013.01.min.js
   
      var selectedfile = false;

      $(document).ready(function() {
         $('input#itemfile').fileReader({filereader: 'js/filereader.swf'});

         $('input#itemfile').change(function(e) {
            selectedfile = e.target.files[0];

            $('span#filename').html(selectedfile.name);
            $('span#fileinput').hide();
         });

         $('input#createitem').click(function() {
            $().SPServices({
               operation: 'UpdateListItems',
               async: false,
               listName: 'File Attachment Test',
               batchCmd: 'New',
               webURL: '/demo',
               valuepairs: [
                  ['Title', $('input#itemtitle').val()]
               ],
               completefunc: function(xData, Status) {
                  if (Status == 'success' && $(xData.responseXML).find('ErrorCode').text() == '0x00000000') {
                     currentitem = $(xData.responseXML).SPFilterNode("z:row").attr("ows_ID");
                     alert('List item created with ID ' + currentitem);

                     if (selectedfile) {
                        filereader = new FileReader();
                        filereader.filename = selectedfile.name;

                        filereader.onload = function() {
                           data = filereader.result;
                           n = data.indexOf(';base64,') + 8;
                           data = data.substring(n);

                           $().SPServices({
                              operation: 'AddAttachment',
                              async: false,
                              listName: 'File Attachment Test',
                              listItemID: currentitem,
                              fileName: selectedfile.name,
                              attachment: data,
                              completefunc: function(xData, Status) {
                                 alert('File uploaded');
                              }
                           });
                        };

                        filereader.onabort = function() {
                           alert('Upload aborted');
                        };

                        filereader.onerror = function() {
                           alert('Upload error');
                        };

                        filereader.readAsDataURL(selectedfile);
                     }
                  } else alert('List item creation failed');
               }
            })
         });
      });
   
</head>
<body>
   <p>Title:<br><input type="text" id="itemtitle"></p>
   <p>File:<br><span id="fileinput"><input type="file" id="itemfile"></span><span id="filename"></span></p>
   <p><input type="button" id="createitem" value="Go!"></p>
</body>
</html>

Notes

The fileReader polyfill takes the file input box and puts the flash file on top of it, so that the file selection and upload is handled by flash instead of natively in the browser. I found that this fell apart of the file input box didnā€™t remain in the same place on the page. In other words, I had problems if I tried to use jQueryā€™s .show() and .hide() functions (or similar).

I solved this by putting the file selection form in a pop-up window. If the page you place your form on is static (i.e. nothing changes after the DOM is loaded) then you shouldnā€™t have this problem.

Enjoy!

Blog

Integrating Microsoft Office Functionality into Your SharePoint Apps

Have you ever come across this snippet of JavaScript before?

try {
   xhr = new XMLHttpRequest();
} catch(e) {
   xhr = new ActiveXObject(ā€œMicrosoft.XMLHTTPā€);
}

If you havenā€™t then not to worry, itā€™s not really the point of this post anyway ā€“ but for the uninitiated this is a snippet of JavaScript that prepares an object that will be used to make an ajax request. Ajax is the mechanism by which a webpage can load more data from the server even after the page itself has finished loading, and you find it all over the place.

The reason I bring it up is because of the tryā€¦ catch construct involved. Essentially it tries to execute the code between the first set of curly braces, and if that fails then it executes the code between the second set instead. In the huge majority of cases the first line of code executes successfully, so why is the second line necessary?

Microsoft. And more specifically, Internet Explorer 6.

For anybody who makes things that live on the web, supporting Internet Explorer 6 is an absolute chore. It does things differently to other (read ā€œstandards complaintā€) browsers, and you end up having to create everything twice and put a bunch of hacks in place to make your site work with it. The block of code above is a prime example: every other browser has the XMLHttpRequest object built into their JavaScript implementation, but for Microsoft you have to use an ActiveX object instead.

More recent versions of Internet Explorer are much better in this regard, and these days frameworks like jQuery take care of any little annoyances like this that remain so itā€™s not as big a deal as it was a decade ago, but thatā€™s not the point of this post either.

The point is that ActiveX remains a part of Internet Explorer to this day, and despite the fact that many web programmers know it primarily as a result of the XMLHttp annoyance described above, it does have its uses. So letā€™s exploit it.

The Downside

If youā€™ve ever created web content then youā€™ll know the importance of standards compliance. Writing compliant markup and code helps to ensure that your site works in whatever browser software your end users happen to be using. Thatā€™s a good thing.

ActiveX exists only within Internet Explorer. If visitors to your SharePoint webapp are using another browser such as Chrome or Firefox then none of the following example code is going to work for them. The best weā€™ll be able to do is detect that it hasnā€™t worked and have our app react in an appropriate way, maybe with a warning message or something similar.

If we were creating something publicly accessible for consumption by a wide variety of internet users then this would be a deal breaker, but in the context of an app built atop SharePoint where all the users are within the four (physical or otherwise) walls of your organization? It might be fine. If your organization is like the one I work for then everybody is using Internet Explorer anyway, because thatā€™s the browser installed on your computer when IT deliver it, and getting them to install an alternative is like pulling teeth.

OK, So What is This ActiveX Thing?

Wikipedia sums it up pretty nicely, including condensing my previous three paragraphs down into a single sentence.

Many Microsoft Windows applications ā€” including many of those from Microsoft itself, such as Internet Explorer, Microsoft Office, Microsoft Visual Studio, and Windows Media Player ā€” use ActiveX controls to build their feature-set and also encapsulate their own functionality as ActiveX controls which can then be embedded into other applications. Internet Explorer also allows the embedding of ActiveX controls in web pages.

However, ActiveX will not work on all platforms, so using ActiveX controls to implement essential functionality of a web page restricts its usefulness.

In other words, one of the things we can use ActiveX for is to take the functionality of a Microsoft application and embed it into a web page. Iā€™m going to put together a fairly simple example, and Iā€™m going to use Excel. Letā€™s dive in!

The HTML

Iā€™m going to build a simple table in HTML. The data in the table could come from anywhere, such as a subset of some SharePoint-based dataset or other pulled together using some of the techniques weā€™ve looked at previously, or a script executed server-side if you have the ability to create such a thing. For the sake of simplicity though, Iā€™m just going to define in a static manner in my markup ā€“ and Iā€™m not going to worry about making it look good.

<table>
   <thead>
      <tr><th>Fruit</th><th>Qty</th></tr>
   </thead>
   <tbody>
      <tr><td>Bananas</td><td>5</td></tr>
      <tr><td>Apples</td><td>7</td></tr>
      <tr><td>Oranges</td><td>2</td></tr>
      <tr><td>Pears</td><td>3</td></tr>
   </tbody>
</table>

image

The Template

Next, Iā€™m going to define an excel template that weā€™ll use to place our data into. This could be as detailed or as simple as necessary, so for the purposes of example Iā€™ve gone with simple again. All Iā€™ve done in mine is put headings at the top of column A and B that match the headings in our HTML.

image

The JavaScript

OK, hereā€™s where the clever bit starts. The first thing weā€™re going to do is create an ActiveX object pointing to excel, and assign it to a variable so we can reference it again further along in the code.

var exApp = new ActiveXObject('Excel.Application');

Next weā€™re going to open a new document in excel, based upon our template. Doing this also returns an object that weā€™ll need again, so weā€™re going to assign this one to a variable too.

var exDoc = exApp.Workbooks.Add('http://192.168.1.102/web/template.xltx');

Itā€™s important to note here that we have to pass in an absolute reference to the template file ā€“ a relative reference is not sufficient because excel has no concept of the location of our webpage. In my test environment the SharePoint server is at 192.168.1.102, but this will undoubtedly be different for you.

At this point, excel is open and has our template loaded, so the next thing to do is iterate over the table in our HTML and plug the data into excel. In general, this is done with the following line of code:

exDoc.ActiveSheet.Cells(1, 1).Value = 'This is row 1, column 1!';

More specifically what weā€™re going to do is use our old friend jQuery to iterate over the table cells in our HTML page and put them into the right place in excel with the help of a couple of simple counter variables: one for the row weā€™re targeting, and one for the column. Donā€™t forget to include a reference to the jQuery library in the document <head> section.

r = 2;
$('table tbody tr').each(function() {
   c = 1;
   $(this).children().each(function() {
      exDoc.ActiveSheet.Cells(r, c).Value = $(this).html();
      c++;
   });
   r++;
});

While weā€™ve been preparing all this, the Excel window has been invisible to the user. The final step is to make it and its newly imported data appear.

exApp.Visible = true;

Done!

Putting it All Together

The full code of our HTML page is as follows:

<!DOCTYPE html>
<html>
<head>
   <title>Export to Excel Example</title>
   /web/js/jquery-1.11.0.min.js
   
      $(document).ready(function() {
         $('input#export').click(function() {
            var exApp = new ActiveXObject('Excel.Application');
            var exDoc = exApp.Workbooks.Add('http://192.168.1.102/web/template.xltx');

            r = 2;
            $('table tbody tr').each(function() {
               c = 1;
               $(this).children().each(function() {
                  exDoc.ActiveSheet.Cells(r, c).Value = $(this).html();
                  c++;
               });
               r++;
            });

            exApp.Visible = true;
         });
      });
   
</head>
<body>
   <table>
      <thead>
         <tr><th>Fruit</th><th>Qty</th></tr>
      </thead>
      <tbody>
         <tr><td>Bananas</td><td>5</td></tr>
         <tr><td>Apples</td><td>7</td></tr>
         <tr><td>Oranges</td><td>2</td></tr>
         <tr><td>Pears</td><td>3</td></tr>
      </tbody>
   </table>

   <input type="button" id="export" value="Export to Excel">
</body>
</html>

Taking it Further

What Iā€™ve put together here is a pretty simple example, but hopefully you can see the value in some of the possibilities this opens up. Getting complex data from a webapp into Excel is actually fairly straightforward.

With a more detailed template to export data into you could prepare webapp data for analysis in excel, create charts, etc, etc.

Enjoy!

Blog

SharePoint Development: Lesson 4

Welcome back to my ongoing series on SharePoint development!

I promised last week that Iā€™d follow up lesson three within a week, so here we are! In that previous lesson everything we did was in relation to setting the stage for what weā€™re going to tackle today, so Iā€™ve got no doubt that youā€™ve been waiting with barely contained anticipation to get back to writing some code.

Because all the steps from last week were fairly standard SharePoint stuff, if youā€™re pretty familiar with the platform you may well have skimmed through it. Thatā€™s fine, but for everything we do today to be successful weā€™re going to need to make sure that:

  • Weā€™ve created and populated a list of shipping prices, with different shipping profiles (in our example, these different profiles are based on destination).
  • Uploaded the SPServices library minified javascript to our ā€œwebā€ document library.
  • Located and noted the GUID of the shipping prices list, so that we can programmatically reference it in our code.

With those steps done everything is in place, so letā€™s not waste any more time!

The HTML

The HTML portion of our code hasnā€™t changed very much since we wrote it way back in lesson one, and it doesnā€™t change much today either. That being said, we do have an additional option that forms part of our calculation this time around, so weā€™ll add a form element for that.

Destination province:
Please wait, loading data...

Item weight:
lbs

Shipping cost: $0

As you can see, weā€™ve added a drop-down menu that allows for the selection of the destination province. In the HTML it has a single entry in the list that says ā€œPlease wait, loading dataā€¦ā€ As you might anticipate, weā€™re going to remove this option later on and replace it with the actual choices, but itā€™s probably good practice to have something there because the SPServices library is going to read from our list with an AJAX HTTP request ā€“ in other words the page will load first (with our ā€œplease waitā€ message), and the data to populate the list will be loaded afterwards. Hopefully it will all happen so fast that nobody ever really sees the ā€œloadingā€ message, but you never know.

The JavaScript

OK, now weā€™re really going to start getting into some changes in functionality! First things first, though. We need to include the external libraries weā€™re going to be using. jQuery has been there from the start of our journey, but SPServices is new for today.

/web/js/jquery-1.11.0.min.js
/web/js/jquery.SPServices-2014.01.min.js

The other thing Iā€™m going to do at the beginning of my main block is declare a global variable in which to store list data. There are probably better approaches than this, but for the sake of keeping my code relatively simple this is one Iā€™m taking.

var shippingData = false;

Loading Data from our SharePoint List

Next is where the SPServices magic happens. Immediately inside our jQuery document ready function weā€™re going to call SPServices and grab the data from our list, putting the result of that request inside our shippingData variable. SPServices has many available options that get passed as an object to its main function and more details can be found in the documentation on their website. Like I said though, Iā€™m keeping things simple:

$().SPServices({
   operation: 'GetListItems',
   listName: '{4883AC18-E2A5-4EAF-8446-23B15B43861A}',
   completefunc: function(xData, Status) {
      if (Status == 'success' && ($(xData.responseXML).find('ErrorCode').text() == '0x00000000' || $(xData.responseXML).find('ErrorCode').text() == '')) {
         shippingData = $(xData.responseXML).SPFilterNode('z:row');
         populateDropdown();
      } else alert('Something went wrong!');
   }
});

Not bad, eh? Ten lines of code and weā€™ve read all the data from our SharePoint list. Letā€™s look at what weā€™ve done in a bit more detail.

The code weā€™ve written can be summarized as:

$().SPServices(obj);

All weā€™re doing is calling the SPServices plugin and passing in a javascript object that contains all the options it needs to understand what we want it to do. There are many options we could pass to it, and youā€™ll find more detailed documentation on the SPServices homepage. Iā€™ve kept things as simple as possible and passed in the bare minimum.

Operation: ā€˜GetListItemsā€™

In our example weā€™re dealing with items in a list. GetListItems is the operation we need to read data from a SharePoint list into our webapp. There are many other types of operation related to lists that SPServices could do for us ā€“ creating entirely new lists, adding or removing list fields, deleting lists, etc. Essentially almost anything you could do manually on SharePoint could be done programmatically with SPServices. If we wanted to write data back to a list then UpdateListItems would be the operation weā€™d use.

ListName: '{4883AC18-E2A5-4EAF-8446-23B15B43861A}'

The ListName parameter could take one of several formats. A simple string containing the name of the list will work, but to avoid any kind of confusion between similarly named lists my preference is to pass in the GUID of the list. Remember that the GUID of your list will be different to mine. SPServices can also take a WebURL parameter that tells it which site in your SharePoint collection the list can be found on, but since weā€™re using a GUID thatā€™s unique across all sites in the collection we donā€™t need that.

completefunc:Ā  function(xData, Status)

This is where the real magic happens. The completefunc parameter represents a callback function that SPServices executes once data has been loaded, and it takes two parameters: xData and Status.

Our completefunc does some basic error handling, and then calls another function to do the dirty work.

if (Status == 'success' && ($(xData.responseXML).find('ErrorCode').text() == '0x00000000' || $(xData.responseXML).find('ErrorCode').text() == ''))ā€¦

For the Status parameter thatā€™s passed in to completefunc, weā€™re looking for it to contain a value of ā€˜successā€™. We have to be a little careful about exactly what this means though: To SPServices success means that itā€™s passed a query to SharePoint and received a response. It doesnā€™t mean that our query was well formed, or that we received any useful data back. Basically youā€™ll always get a successful status unless SharePoint is down ā€“ in which case our webapp probably wouldnā€™t be available to users anyway.

To check that our query has truly been executed successfully by SharePoint, we look in the responseXML for an error code. Depending on the version of SharePoint weā€™re running, that will either be 0x00000000 or blank.

shippingData = $(xData.responseXML).SPFilterNode('z:row');

There are few ways we could approach what happens next. My goal was to keep my code as simple as possible so Iā€™m doing some things that may not be best practice. This is one of them: we take the data SPServices has returned and put it into a global variable.

The data SharePoint has passed back to us is in XML format and contains a wealth of information about our query, metadata about the response, and so on. We donā€™t really need any of this stuff ā€“ we just want the data itself ā€“ so SPServices has a function called SPFIlterNode that helps us filter the returned data down to what we actually care about. Weā€™re filtering here by z:row. Each z:row returned represents one entry from our SharePoint list.

populateDropdown();

Now that we have our data in a globally accessible variable, Iā€™m outsourcing the processing of it to another function: populateDropdown. That last step for our completefunc is to call this function.

Populating the Dropdown List

OK! So now we have the data we need loaded from our SharePoint list and resident in memory (in our shippingData global variable) so that we can manipulate it and our users can interact with it. The first step of this process is to populate the relevant options into the select box we created in our HTML. Iā€™m doing that (surprise surprise) with the populateDropdown function.

function populateDropdown() {
   $('select#destination option').remove();

   for (i = 0; i ' + $(shippingData[i]).attr('ows_Title') + '');
   }
}

If you recall, the dropdown list initially contains a single option with the text Please wait, loading dataā€¦. At this point in the story our data is loaded, so letā€™s get rid of that option first:

$('select#destination option').remove();

Done! Good. The next step is to loop through each of the items weā€™ve loaded into our shippingData variable, and add them as an option in the dropdown. We do this with a standard for loop:

for (i = 0; i 

If youā€™re not familiar, this construct sets a variable i to zero, then loops through the following block of code multiple times, as long as i is less than shippingData.length, which is the number of items in our shippingData variable. On each iteration i is incremented by one (i++).

On each loop we add an item to our dropdown, using jQuery to append the relevant HTML. Based on the data that exists within our SharePoint list, we end up with these options in our dropdown:

Each $(shippingData[n]) has an attribute for each of the columns in our list. These attributes all have the prefix ows_, and, as mentioned in lesson 3, they are all referenced by the original name of that column (even if itā€™s been renamed since it was created). Thatā€™s why weā€™re using the attribute ows_Title to get at the data thatā€™s in our Province column: the column was called Title when the list was created, and we renamed it.

Performing the Calculation

With any luck everything in our story up to this point will have happened in a split second, but regardless weā€™re now ready for user input. The user selects the destination province, inputs the weight of the item being shipped, and hits the calculate button.

The calculation itself is really no different from the one we built in lesson 1, the difference being that our variables are defined by the list data rather than hardcoded in.

We still identify that the Calculate button has been pressed through the use of the jQuery $(ā€˜input#calculateā€™).click(function() {});, but our first step is now to set some variables based on whatever is selected in the destination province dropdown at the time.

bp = parseFloat($(shippingData[$('select#destination').val()]).attr('ows_BasePrice'));
bw = parseInt($(shippingData[$('select#destination').val()]).attr('ows_BaseWeight'));
ap = parseFloat($(shippingData[$('select#destination').val()]).attr('ows_AdditionalPrice'));
aw = parseInt($(shippingData[$('select#destination').val()]).attr('ows_AdditionalWeight'));

We set each of these variables by reading the relevant attribute (representing the column in our SharePoint list) from $(shippingData[n]), where n is the value of the destination dropdown, $('select#destinationā€™).val(). After that, itā€™s business as usual:

var shippingcost = bp;

if ($('input#itemweight').val() > bw) {
   shippingcost += (Math.ceil(($('input#itemweight').val() - bw) / aw) * ap);
}

$('span#shippingcost').html(shippingcost);

Putting it All Together

And weā€™re done! The completed code block ā€“ including all the javascript and HTML ā€“ that we copy and paste into our content editor webpart is as follows:

/web/js/jquery-1.11.0.min.js
/web/js/jquery.SPServices-2014.01.min.js

   var shippingData = false;

   $(document).ready(function() {
      $().SPServices({
         operation: 'GetListItems',
         listName: '{4883AC18-E2A5-4EAF-8446-23B15B43861A}',
         completefunc: function(xData, Status) {
            if (Status == 'success' && ($(xData.responseXML).find('ErrorCode').text() == '0x00000000' || $(xData.responseXML).find('ErrorCode').text() == '')) {
               shippingData = $(xData.responseXML).SPFilterNode('z:row');
               populateDropdown();
            } else alert('Something went wrong!');
         }
      });

      $('input#calculate').click(function() {
         bp = parseFloat($(shippingData[$('select#destination').val()]).attr('ows_BasePrice'));
         bw = parseInt($(shippingData[$('select#destination').val()]).attr('ows_BaseWeight'));
         ap = parseFloat($(shippingData[$('select#destination').val()]).attr('ows_AdditionalPrice'));
         aw = parseInt($(shippingData[$('select#destination').val()]).attr('ows_AdditionalWeight'));

         var shippingcost = bp;

         if ($('input#itemweight').val() > bw) {
            shippingcost += (Math.ceil(($('input#itemweight').val() - bw) / aw) * ap);
         }

         $('span#shippingcost').html(shippingcost);
      });
   });

   function populateDropdown() {
      $('select#destination option').remove();

      for (i = 0; i ' + $(shippingData[i]).attr('ows_Title') + '');
      }
   }


Destination province:
Please wait, loading data...

Item weight:
lbs

Shipping cost: $0

Taking it Further

The next steps with our shipping calculator app would be to add some additional error-checking and handling, and maybe amend the code to avoid using unnecessary global variables. Iā€™ve kept things as simple as possible here for the sake of example.

After that? As I mentioned earlier, thereā€™s a lot of cool stuff we can do with SPServices. Where you go from here is really up to you, but hopefully you can see some possibilities. Even with the basic building blocks of reading from and writing to lists, itā€™s possible to build some really cool stuff on top of SharePoint, possibly even taking the approach of using SharePoint as a database for a webapp that has its own look and feel.

Enjoy!

Blog

SharePoint Development: Lesson 3

Welcome back to my continuing series on SharePoint development!

If youā€™ve been following along with lesson one and lesson two then this post has probably been a long time coming, but weā€™re now at the point where weā€™ve built a pretty useful tool for calculating shipping costs, and weā€™ve integrated it into our existing SharePoint site to make it easily accessible to everyone in our team who might benefit from it.

Hereā€™s the thing, though. If your organization is anything like mine, then SharePoint is a tool that theyā€™ve made available to everybody. Youā€™re using it for some cool stuff, but writing code probably isnā€™t your day job ā€“ youā€™re the techy guy or gal in your group whoā€™s found an opportunity to make everybodyā€™s life a little easier with technology, and the beautiful part is that you can do it all without needing to engage your companyā€™s IT team (who are busy with large-scale projects involving a contribution to your companyā€™s bottom line, which your idea for a shipping calculator would need to be prioritized against).

Thereā€™s nothing wrong with any of that. Something like this really shouldnā€™t be a thing that your companyā€™s IT team get involved with in the exact same way that helping you craft an especially complex excel formula shouldnā€™t be a job for them either. If youā€™ve ever crafted an especially complex excel formula in a workbook thatā€™s shared throughout your group though then you may already have identified the downside to this approach: things change.

Our tool is built on a fixed model for calculating shipping costs of $19.99 for the first 20lbs and $3 for every 5lbs (or part thereof) over and above that. That rule is embedded within your code now, youā€™re the only one around with the necessary technical knowledge to update it when shipping costs change, and you have a day job to worry about too. If updating SharePoint tools is not how you like to spend your weekends, then we need a different approach.

What we need, then, is a solution where the average SharePoint user can make changes to key pieces of data, and our tool needs to be smart enough to read that data so that it can be used in calculations. And, while weā€™re at it, letā€™s expand the tool so that it can handle a few different shipping profiles (which could represent different couriers or, in our example, destinations).

Enter the SPServices library. SPServices is a jQuery plugin thatā€™s used to expose SharePoint data to our jQuery apps, including (amongst other functions) reading from and writing to SharePoint lists in SharePoint 2007, 2010 and 2013.

This is a significant step up from where we were at the end of lesson two (which is probably why Iā€™ve been procrastinating over writing it for so many weeks). Iā€™ve split it into parts. Today weā€™re going to set the stage and prepare our data, and this time next week (I promise!) weā€™re going to get our hands dirty with some code.

Nevertheless, both this post and its successor are probably going to be longer than those that have gone before, so be forewarned, go grab yourself a cup of coffee, and letā€™s dive in!

Creating the List

First things first, we need a list to hold our data. This list is where our less-technical colleagues will come when changes need to be made and weā€™ll keep it fairly straightforward.

Much like we did to create our document library in lesson one, go to Site Actions > View All Site Content and hit the Create button. This time weā€™re going to choose Custom List as the type of entity weā€™re going to create. We need to give the list a name, so letā€™s call it ā€œShipping Prices.ā€ For the time being weā€™ll leave the description blank, and weā€™ll hide our list from the Quick Launch bar. We can always change these options later.

image

When you hit the Create button the list will be created and will have a single column (ā€œTitleā€). We need to add a few more columns, so choose List Settings from the toolbar or the Actions menu (depending on your version of SharePoint). The first thing weā€™re going to do is rename the ā€œTitleā€ column to ā€œProvinceā€ by clicking it in the list, then weā€™re going to add four more columns by clicking the Create Column link and adding them one by one. Hereā€™s where we want to end up:

image

An Important Note Regarding Column Names

With this the basic framework for our data is in place. You may notice that none of our column names have spaces in them. Thatā€™s because SharePoint in the backend does strange things with spaces. As youā€™ll see later we can programmatically read from a column called ā€œBasePriceā€ by referring to it exactly in that way, whereas a column called ā€œBase Priceā€ would need to be referred to in our code as ā€œBase_x0020_Price.ā€

That being said, we can leverage a bit of trick here if want to improve readability for people who will interact with this list directly (our less-technical colleagues, remember). Behind the scenes (and in our code) SharePoint will always know the column by its original name, even if itā€™s subsequently been renamed. If we go back and spaces now, the internal name of the ā€œBasePriceā€ column will remain ā€œBasePrice,ā€ even if its display name is changed to ā€œBase Price.ā€

This is helpful in this scenario, but can easily be a bit of a gotcha ā€“ you need to remember the original name of all your columns, because thatā€™s how your code will reference them. Remember the ā€œTitleā€ column we renamed to ā€œProvince?ā€ Itā€™s still ā€œTitleā€ behind the scenes.

Populating the Data

There are many ways to get data into a SharePoint list, and Iā€™m not going to go into a great amount of detail here. You can add each item row by row with the built-in list forms, you can use SharePointā€™s datasheet view to edit many rows at once, or you can use a third party tool. Our example is probably a little basic to warrant breaking out a special third-party tool for, but nevertheless as you do more complex stuff in the future Iā€™m a fan of SharePoint List Item Editor. It does exactly what its name suggests, gives you a spreadsheet-like interface for editing items in SharePoint lists, and makes it easy to copy and paste many rows at once.

Regardless of how we do it, hereā€™s the data Iā€™m going to put into my list for the purposes of this example.

image

With this data in place youā€™re probably starting to get a sense of where things are going with this example. The BasePrice is the cost of shipping the first BaseWeight pounds, and the AdditionalPrice is the cost of each AdditionalWeight pounds or part thereof.

In many ways itā€™s no different from what weā€™d created by the end of lesson two, but with one critical difference ā€“ none of these variables are going to live in our code anymore. Theyā€™re all factored out into the list where theyā€™re easily editable when things need adjusting in the future.

image

Find The GUID of the List

Everything weā€™ve done so far is fairly standard SharePointy stuff, but youā€™ll notice weā€™ve had one eye on the end goal of programmatically interfacing with this data throughout. Finding the GUID of the list weā€™ve created is an important step in this process.

A GUID is a globally unique identifier, and every SharePoint list (or calendar, or document library, etc) on our SharePoint site collection has one. There are several ways we can connect our front-end custom interface to our back-end data, but the GUID is probably the most reliable because as the name implies, itā€™s globally unique. Itā€™s also not affected if our list gets renamed later.

There are several ways to find it, but my favourite is to use a simple tool I found for the purpose. Open the app and plug in the URL of your SharePoint site. Hit the Display List Titles and IDs button, and grab the relevant ID (including the opening and closing braces).

image

For me {4883AC18-E2A5-4EAF-8446-23B15B43861A} is what I need. For you it will be different. You may notice that the tool can also find the GUID of a list view. We donā€™t need this because weā€™re going to use the default view. Iā€™m not going to get into it right now, but if youā€™re unfamiliar SharePoint views define things like the sort order and filter thatā€™s applied to a list, and each list can have multiple views defined. If you want to programmatically access data thatā€™s filtered out of your default view then the simplest method is probably to create a new, unfiltered, public view and reference it by its GUID in your code. The documentation for SPServices will tell you more.

ā€¦and Talking of SPServices

Now is probably a good time to download the library and place the minified javascript file in the ā€œwebā€ document library we created in lesson one, in the ā€œjsā€ subfolder alongside the jQuery library thatā€™s already there. At the time of writing this file is called jquery.SPServices-2014.01.min.js.Ā With that our stage is set and weā€™re ready to rewrite the code in our content editor web part to interface with it, but that was a lot to take in so weā€™ll get to that next week.

Blog

SPServices addAttachment jQuery Example

Update: I’ve posted some example code that works in Internet Explorer 9!

If you’re having a few issues adding attachments via ajax and SPServices on SharePoint have a look over the code snippets below.

To upload a file to a list you need to make use of the fileReader javascript class, using the readAsDataURL method and stripping the first part off the dataurl to get the base64 component. Then submit this to SPServices.

I’ve been asked a few times to add the ability to upload attachments to SharePoint tools that I’ve created, and I’ve never been able to achieve it until I eventually came across this blog post last week.

If (like me) you’re developing in a front-end only way without any server-side programming then it seems like this is the way to upload files and attach them to SharePoint list items.

It relies on the javascript fileReader feature so your users will need a fairly modern browser… which is where I ran into trouble. The default browser deployed within my company is Internet Explorer 9, and that doesn’t have fileReader support.

With much work and even more googling I was able to get this technique to work in Internet Explorer 9. In the future I’ll write more about how I managed it, and how you can too!

SPServices addAttachment jQuery Example

Blog

SharePoint Development: Lesson 2

Welcome back to my series of posts on SharePoint development!

At the end of lesson one we’d used some basic HTML, javascript and jQuery skills to create a tool for calculating shipping costs, and we’re hosting the tool on our team SharePoint site so that everybody who needs it has access.

image

This is great and all, but as we noted – it doesn’t exactly feel like our tool is a part of SharePoint. That might be fine if we’d built something extremely complex where having it as a self-contained webapp of sorts made a lot of sense, but it seems wrong for our purposes.

What we need to do is somehow build the tool into the main page of our SharePoint site so our users don’t even have to think if they want to use it – it’s just right there waiting for them.

The Content Editor Web Part

Believe it or not, we laid the groundwork for this in lesson 1 even without knowing it. The final step last time was to add a content editor web part to our page with a link to the tool in it.

image

The content editor webpart is much more powerful than that though, and we’ve barely scratched the surface.Ā First, let’s take a closer look at what we have there already.

image

From the web part’s menu, choose the Edit Web Part option. An options pane will appear on the right of the screen.

If you’re using SharePoint 2007 there’s a button in the options pane called Source Editor, and this is where you’ll want to go. On SharePoint 2010 you’ll need to click into the web part first of all so that the ribbon appears at the top of the screen, then select HTML and Edit HTML Source from the Format Text ribbon.

Right now, my source looks like this:

ā€‹<a class="ms-rteFontSize-7" href="/web/shipping.aspx">Shipping Cost Calculator</a>

Simple enough, but hopefully you’re beginning to see where I’m going with this. The content of the web part is rendered inline as part of the HTML of the overall page, and we can put whatever we want in there. We can only edit this one snippet of the page where the web part lives, but that’s OK – it’s good enough.

Last time we included a reference to the jQuery library in the <head> section of the page, but does it actually need to be in the head? It may not be semantically great code, but we can put that reference anywhere. And once we have we’ll have all the power of jQuery at our disposal to manipulate the main page of the site however we see fit.

For now, let’s modify the code we used in lesson one to make it appropriate for inclusion in the middle of a page:

/web/js/jquery-1.11.0.min.js

   $(document).ready(function() {
      $('input#calculate').click(function() {
         var shippingcost = 19.99;
         
         if ($('input#itemweight').val() > 20) {
            shippingcost += (Math.ceil(($('input#itemweight').val() - 20) / 5) * 3);
         }
         
         $('span#shippingcost').html(shippingcost);
      });
   });

Item weight:
lbs

Shipping cost: $0

Paste that in to the content editor web part’s source, and save. On my installation of SharePoint 2010 a warning pops up telling me my HTML may have been edited. I don’t know why SharePoint feels the need to do this, but it doesn’t seem to matter.

Our shipping cost calculator is now looks like it’s really a part of our site homepage, and we’re done another quick lesson!

image

Taking it Further

What we’ve done here is great for our purposes, but we’ve actually opened up a world of extra possibility here.

As I noted, the HTML and javascript we’ve pasted into our content editor web part goes directly into the page, inline. In some respects that’s not the best – we have script tags in the middle of the page which isn’t really the correct approach, but in other ways it’s extremely powerful.

We can use our script to manipulate the page however we choose. If the approach we’ve chosen to take is to include our code in a web part then we probably don’t want to go nuts and change everything, but if you want to manipulate, say, the document’s title? Easy!

document.title = 'Site Home and Shipping Calculator'

If you want to include a custom CSS file? Done!

$("head").append("<link rel='stylesheet' href='/web/css/example.css' type='text/css' media='screen'>");

And if you have another web part that you want to use jQuery in, there’s no need to include a second reference to the library – one per page is all you need. You can easily have one content editor web part that influences another.

Conclusion

So, in lesson one we built a simple tool and now in lesson two we’ve integrated it right into our SharePoint page. There’s a lot you can do with this knowledge. In lesson three we’ll go deeper still though, and begin to use data from SharePoint lists in our tool with the help of the SPServices jQuery add-on.

Blog

SharePoint Development: Tools of the Trade

As I noted in my last post, lesson two of my SharePoint development series is still nothing more than an item in the “someday” section of my to-do list at the moment, but in the meantime I thought I’d share a few tools that I find invaluable when I’m doing SharePoint development work.

  • Notepad++
    Any good text editor with a focus on code will do, but Notepad++ is my tool of choice, not least because it’s available in a portable version that I can run on my work computer without having to ask our IT department to install anything for me (not that I would ever run unapproved software, obviously. I’m just saying you could).
  • SPServices jQuery Library
    I’ll be introducing this little gem in lesson 3, if/when I get around to writing it.
  • Find List and View GUIDs
    SharePoint lists and views all have unique IDs assigned to them by the system, and it’s useful (again, for reasons that will become clear later) to be able to find them. This simple tool does just that.
  • SharePoint List Item Editor
    Another simple but extremely useful tool. This one presents lists in a grid view which is great for mass-editing, copying and pasting from Excel, etc, etc.

Enjoy!

Update:

I’d previously also listed SharePoint CAML Query Helper in this list, with the caveat that I hadn’t actually tried it but screenshots I’d seen made me hopeful that it would make the process of building CAML queries (which are the method by which you can control what data from a list is returned to you when you’re using SPServices) easier.

It’s is a worthwhile tool to have in your toolbox and it’s great for testing CAML queries, but it didn’t make building them as easy as I’d hoped.

What I want is a tool that lets me select a list field, enter the criteria by which I want to filter on that field, and then spits out the CAML query that I need to use. If anybody knows of such a tool please let me know in the comments!