SPServices Stories #22 : SPServices SharePoint Attachments in Internet Explorer 9

This entry is part 21 of 21 in the series SPServices Stories

Introduction

This is another story I ran across on Twitter and wanted to add to the SPServices Stories series. I wrote about my own experience getting attachment uploading to work in my post Uploading Attachments to SharePoint Lists Using SPServices. My cheat in that case was to change the DOCTYPE to HTML5 in a SharePoint 2010 master page. It was a cheat in the sense that it broke a lot of basic SharePoint functioning. Luckily I could get away with it in that particular case.

In this story, Jason Williams (@JayWll) explains how he got uploading to work with the fileReader polyfill. As much as all of us would like to be working with the latest and greatest technologies, in reality most organizations – especially larger ones – don’t update their tech very often. In Jason’s case he needed the upload capability to work with IE9, which doesn’t have a lot of the HTML5 spec implemented in it.

I’ve combined two of Jason’s posts here, SPServices addAttachment jquery example and SPServices SharePoint Attachments in Internet Explorer 9. Jason built on a post from Brendon Wilbore (@bjwildbore) to get things up and running. This is one of the great things about the SharePoint community: there are many people out there who are generous enough to post their work so that we can build upon it.

SPServices SharePoint Attachments in Internet Explorer 9

Brendon Wilbore:

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!


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>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
  <script type="text/javascript" src="js/jquery.FileReader.min.js"></script>
  <script type="text/javascript" src="js/jquery.SPServices-2013.01.min.js"></script>
  <script type="text/javascript">
    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');
          }
        })
      });
    });
  </script>
</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 if 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!

jQuery Library for SharePoint Web Services (SPServices) 2014.02 Released

SPServicesJust in time for the holiday gift-giving season, I’m releasing SPServices 2014.02. This is the second release in 2014 (which you should be able to glean from the release name). If you’re using an earlier version of SPServices, I strongly recommend an upgrade. Read on.

The most important change in this release is due to an egregious error on my part that goes way back to 2013.01. Paul Tavares (@paul_tavares) spotted quite a while back and I was just too dumb to realize how important it was to fix. The net effect of my mistake was that SPServices was always caching requests, regardless how you set cacheXML. On pages where we simply called SPServices a few times on page load, it usually wouldn’t make much of a difference, but a little more memory might be required for the browser. However, in things like Single Page Applications (SPAs) which use SPServices for the data transport layer (See my blog post series: Single-Page Applications (SPAs) in SharePoint Using SPServices), it could make a *huge* difference. It effectively meant that for every unique request, we were caching data whether we should or not. Over a long page lifespan, that could add up to a lot of memory eaten up. Bad news. All better now.

I found out over the last few months the insidious ” Required Field” issue that I thought I had fixed completely in 2014.01 wasn’t totally fixed. For folks on SharePoint 2010 who applied last December’s CU, the markup in the DOM was a little different than the fix I had put into 2014.01. (See: Office 365 Update Changes ‘Display Name’ on Required Fields) This *should* be fixed for everyone now. Please let me know if you see any issues going forward. And Microsoft, please don’t do that again.

There is a handful of new operations in this release, added due to user requests:

  • WebPartPages.SaveWebPart2
  • RecordsRepository.GetRecordRouting
  • RecordsRepository.GetRecordRoutingCollection
  • RecordsRepository.GetServerInfo
  • RecordsRepository.SubmitFile

Finally, I recently moved my SPServices development repository over to Github at sympmarc/SPServices. I’m still getting my feet wet with Github (See: SPServices and Github – This Time I Mean It), but I plan to have my most current work available there going forward. I’ll still be posting the releases over on the Codeplex site, and I’ll monitor the issues there as well. I’m hoping that by using Github, we’ll have more people contributing to SPServices. It’s always been a community-driven project, but I’d love to get more direct contributions.

This release will be, as all of the recent releases have been, available on cdnjs as soon as possible. If you’d like to serve up SPServices from a CDN, cdnjs is the place to get it. cdnjs hosts a lot of JavaScript libraries that don’t have enough coverage to interest the big boy CDNs, so check it out in any case.

Finally, I want to thank Paul Tavares again for his help with SPServices over the years and with this release in particular. Both he and Josh McCarty (@joshmcrty) have also been trying to help get me over onto Github for a long time, but my thick skull just hasn’t gotten it before. I’m using Jetbrains Webstorm as my IDE these days and the Github integration there has finally made Github make sense to me. As we say here in Massachusetts, “light dawns on Marblehead“.

As usual, there are also a number of additional changes to fix existing bugs or improve efficiency (yes, I’m still able to improve on my old code, and I expect that will continue).

You can see the full list of enhancements and improvements on the download page. Note the link to the Issue Tracker items for this release. For posterity, here are links to the release notes showing all the fixes and improvements from the Issue Tracker on Codeplex.

New Functionality

Issue Tracker Item Function Description

 

New Operations

Web Service Operation Options MSDN Documentation Issue Tracker Item
WebPartPages DeleteWebPart pageUrl, storageKey, storage WebPartPagesWebService.DeleteWebPart Method 10273
WebPartPages SaveWebPart2 pageUrl, storageKey, webPartXml, storage, allowTypeChange WebPartPagesWebService.SaveWebPart2 Method 10273
ALPHA6 RecordsRepository GetRecordRouting recordRouting RecordsRepository.GetRecordRouting Method 10257
RecordsRepository GetRecordRoutingCollection NA RecordsRepository.GetRecordRoutingCollection Method 10257
RecordsRepository GetServerInfo NA RecordsRepository.GetServerInfo Method 10257
RecordsRepository SubmitFile fileToSubmit, properties, recordRouting, sourceUrl, userName RecordsRepository.GetRecordRouting Method 10257

 

Bug Fixes and Efficiency

Issue Tracker Item Function Description
10267 $().SPServices.SPComplexToSimpleDropdown Possible bug in SPComplexToSimpleDropdown
10253 $().SPServices.SPFindMMSPicker SPFindMMSPicker missing from 2014.01
10279 $().SPServices.SPGetListItemsJson SPGetListItemsJson Doesn’t Handle Attachments Correctly
10284 $().SPServices.SPFilterDropdown SPFilterDropdown Not filtering dropdown column.
10272 $().SPServices.SPGetCurrentSite $().SPServices.SPGetCurrentSite Documentation
ALPHA5 10277 $().SPServices.SPGetCurrentUser fix for SPGetCurrentUser at webUrl /
10248 $().SPServices.SPGetCurrentUser Trouble with SPSservices and Sharepoint Form
10265 $().SPServices.SPGetCurrentUser $().SPServices.SPGetCurrentUser isn’t returning ID
10254 $().SPServices.SPDisplayRelatedInfo SPDisplayRelatedInfo – DIV named improperly with Required field
10256 $().SPServices.SPCascadeDropdowns SPCascadeDropdowns Required Lookup issue
10262 $().SPServices.SPComplexToSimpleDropdown SPComplexToSimpleDropdown – required fields get renamed without the ‘Required Field’ appended to the field name
10146 $().SPServices ResolvePrincipals with addToUserInfoList=true requires SOAPAction
10271 $().SPServices.SPGetListItemsJson SPGetListItemsJson not using defaults.webURL
10283 $().SPServices.SPGetListItemsJson CAMLViewName not working?
ALPHA7 10182 $().SPServices async on version 2013.01 and caching
10298 $().SPServices GetTermSets argument names are slightly wrong
10299 $().SPServices WebUrl not working

Simple Rules for SharePoint Permissions

PermissionsI get questions all the time about how to set up permissions in SharePoint.

Permissions are hard. It’s not just you. And if you don’t do them right, they turn into a tangled ball of string in a drawer that no one can ever get untangled.

Here are some of my rules of thumb. They are intentionally broad brush and some of them may not apply directly to you, yadda, yadda. But time and time again, these rules seem to work.

KISS

First and foremost, keep it simple. Complexity is your enemy. The site topology and list structures should be driven partly as a way to keep “keep it simple” true.

Open Is Good

Collaboration is hard when permissions are tight. Yes, you’ll want to lock things down for some content. That’s normal. But if you create separate Site Collections for everything – Site Collections are a permissions barrier – then you will find that your collaborative goals may not come true.

Highest: Best

Apply permissions at the highest level and only break inheritance when you need to. Permissions can be applied at the Site, List/Library, and item levels. Whenever possible, you want to avoid item-level permissions. There are performance concerns, but they are tiny compared to the administrative nightmare they turn into.

Out of the Box

Use the out of the box permissions unless you need something else. You know what these are:

  • Read
  • Contribute
  • Full Control

90+% of the time, those three permissions levels cover things. Most of the other out of the box permission levels are too esoteric to be useful (e.g., Design).

Groups, Not Individuals

Always use permissions groups, never individuals. For instance, we should have an HR Department permission group and not just give permissions to a person directly. This is even true if there is just one person in a group. If someone leaves the organization, you simply swap them out of groups for their replacement and you’re all set.

Distribute

Let site owners manage their own permissions if you can. SharePoint has a distributed permission model, and you want to let it work. That means that teams should be allowed to mess up their own permissions and external sharing and also be able to fix it. You simply can’t do it all. But you absolutely should be available to help if things get out of hand.

Finally…

Following these simple rules can save you so many headaches. If you’ve had SharePoint up and running for more than a few months, your ball of string is probably already a bit snarled. Don’t let it get much worse before you take a pass through and clean the permissions up. It only gets worse.

Find That Missing Web Part

Have you ever “lost” a Web Part on a SharePoint page? That happened to me (again) today. I tried and tried to figure out why I wasn’t seeing the contents of the Web Part. I knew it had some, and I knew what it should look like.

Where's my content?I’ve been bitten by this before, so I figured I’d write a note here to my future self in the hopes that I’m smart enough to search for “missing Web Part” and find this post. Of course, this bites me infrequently, so I never remember that it happened to me before. It’s gotten me in every version of SharePoint, too, I’ll bet.

It turns out that I had accidentally clicked on the Minimize option in the Web Part settings dropdown:

Minimize Web Part

You can set this back from the same dropdown menu, where Minimize will have been replace with Restore. You can also fix it in the Web Part Tool Pane.

Chrome State

It can actually be worse, though. There’s another place that you can do yourself in that’s even harder to spot. (I hit myself with this as a double whammy today.)

Right below the Chrome State above is the Chrome Type. The options there are generally “Default”, “None”, “Title and Border”, “Title Only”, and “Border Only”. If, like I did today, you accidentally select the “None” option, then your Web Part disappears altogether! With the branding I have on the site I’m working with today, the Web Part simply collapsed to a blue line.

Where's my Web Part?

At least I could tell it was there, but since it was a Content Search Web Part and I was working on the Display Templates, I assumed it was my code that was breaking it.

Live and learn, I guess. And always try to write future you a note so that you can fix it more easily the next time.

SPServices and Github – This Time I Mean It

SPServicesGitHub_LogoI’ve had some false starts moving SPServices to Git and/or Github over the last few years. If it weren’t for Josh McCarty’s (@joshmcrty) help on every release, I wouldn’t even have gotten SPServices onto cdnjs, since they use Github. (Yes, SPServices is available via CDN at cdnjs and has been for several years now.) I’m just tremendously behind the times.

So it’s only taken me about two years, but I’m really biting the bullet on Github this time. I’ve just read through a bunch of great suggestions I got when I abortively tried to move things to Github long ago and I’m curious given the amount of time that has passed those folks might do differently.

Here are my assumptions/preferences:

  • Simple, simple, simple
  • I’m using a public folder in my Dropbox as my “CDN” for development. I think Paul Tavares (@paul_tavares) knows where it is, but no one else does. This Dropbox-based CDN helps immensely for testing, since I can just point my script references there in all of my test environments. In case anyone is wondering, I’d love to use OneDrive (either flavor) for this, but it just doesn’t work the way it does redirects. I can get a clean URL from Dropbox that just plain works.
  • I’m leaning toward WebStorm for my IDE these days. (Where I can’t install it in client environments, I’ll still use SharePoint Designer and/or Sublime Text.) Webstorm has very robust integration with Github that even seems to make sense to me. I’ve got my Webstorm project embedded in the Dropbox CDN I mentioned above.
  • In case you’re wondering, I do probably 99% of the work on SPServices, so my ideas for version control have been extremely simple to date. SPServices is a one-file project and I make virtually all the changes to it. SPServices wouldn’t be what it is without excellent help and contributions from people like Josh McCarty and Paul Tavares; I couldn’t have gotten to this point without them. But as far as the actual edits and testing, it’s mostly me.
  • I’ll continue hosting the docs and downloads on Codeplex, at least for the foreseeable future. This makes sense because of the volume of documentation and the great discussions history that’s already there.

What I’m looking for is best (better) practices, build ideas, etc. SPServices will continue to live as long as people find it useful, and I want to keep building it and supporting it. That said, it’s my “side project” – something I do for fun and learning opportunities. So any ideas should be labor *saving*, not labor *producing*.

It’s my hope that – as they mentioned in the suggestions I linked to above – more people may decide to contribute with the move to Github. Who knows, maybe we can haul it into RESTland along with SharePoint 2013.

Today I posted the latest beta for the 2014.02 release. I expect to make it a stable release in about a week or so, since quite a few people have been downloading it and testing it as I’ve made changes over the last few months. I’ll write more about why you *really* should upgrade to this new version in an upcoming post. (Thanks yet again to Paul Tavares on this one.)

Thanks in advance for any ideas you can toss into the mix. Feel free to reply in the comments here or on the older thread in the Codeplex Discussions.