Sharing to the Point

This was a triumph.
I'm making a note here: HUGE SUCCESS.

Search This Blog

Thursday, April 9, 2015

How to prevent users from deleting other users their attachments from items in SharePoint 2013

I have been inactive for quite a long time. It was a busy period at work, and I've been learning to program server-side as well. But recently, I wrote another piece of code in JavaScript, and I'd like to share this with you!

This script will do the following:
  • When the user edits an existing item, check if the item has attachments;
  • If the item has one or more attachments, get the author of each attachment;
  • Check for each attachment if the current user is the author. If the current user is not the author, disable the option to delete the attachment and put the text in grey.


Why is this useful? Because if you have multiple users working on one list item (in my case, a request form that requires multiple users to add an attachment), they won't accidentally delete each others attachments.

I added the script to an existing script, named "scripts.js" which is located in my "Style Library" folder. The "scripts.js" file is referenced in my master page, so that it will run on every page.

if ((document.referrer == "" || document.referrer == null) 
   && window.location.pathname.toLowerCase().indexOf("editform.aspx") > -1) {
   // These two lines are required, without it my code won't run.
   SP.SOD.executeFunc('SP.js', 'SP.ClientContext');
   SP.SOD.executeFunc('sp.runtime.js');
 
   var currentItemID;
   var attachmentAuthor = [];
   var itemArray = [];
 
   $(document).ready(function(){  
      // Get ID of the current item.
      currentItemID = window.location.href.toLowerCase();
      currentItemID = currentItemID.substring(currentItemID.toLowerCase().indexOf("?id=") + 4);
      // Remove the line below in case the URL of your item is 
      // not shown as a modal dialog.
      currentItemID = currentItemID.substring(0, currentItemID.toLowerCase().indexOf("&isdlg"));
  
      // Save the ID of the current item in the session 
      // (not necessary, but I prefer it this way)
      sessionStorage.setItem("SessionItemCurrentItemID", currentItemID);
 
      // Get attachments of current item.
      var url = "/_api/Web/Lists/getByTitle('" + "stbz" + "')/Items(" + currentItemID + ")/AttachmentFiles",
         qs = "?$select=ID,Author/Title,*&$expand=Author,AttachmentFiles",
         siteUrl = "https://path-to-your-site.com";
      $.ajax( {
         url : siteUrl + url + qs,
         type : 'GET',
         headers : {
            'accept' : 'application/json;odata=verbose',
            'content-type' : 'application/json;odata=verbose'
         },
         success : successHandler,
         fail : failHandler
      });  
 
      function successHandler(data) {
         if (data) {
         // If the item has attachments, then run this function.
            $.each(data.d.results, function() {
               getWebProperties(sessionStorage.getItem("SessionItemCurrentItemID"));
            });
         }
      }
 
      function failHandler(data, errCode, errMessage) { 
         console.log('Error: ' + errMessage); 
      }
   
      function getWebProperties(itemID) {
         var attachmentFiles;
         var ctx = new SP.ClientContext.get_current();
         var web = ctx.get_web();
         var attachmentFolder = web.getFolderByServerRelativeUrl('Lists/stbz/Attachments/' + itemID);
         attachmentFiles = attachmentFolder.get_files();
         ctx.load(attachmentFiles);
         ctx.executeQueryAsync(function(){
            // I can't remember what the $2_1 was again, but anyway...
            for (var j = 0; j < attachmentFiles["$2_1"].length; j++) {
               var author = attachmentFiles.itemAt(j).get_author(); 
               attachmentAuthor.push([attachmentFiles.itemAt(j).get_name(),author]);
   
               // You'll need to load the author along with the title parameter,
               // in order to be able to fetch the name of the author later.
               ctx.load(author, 'Title');
               ctx.executeQueryAsync(function(){}, function(err) {});
            }
            // Loop is not necesarrily required, but you will need to set a
            // timeout. Comes in handy when you have a lot of attachments.
            checkAttachmentsLoop();
         }, function(err) {});
      }
   
      function checkAttachmentsLoop() {
         setTimeout(function(){
            if (attachmentAuthor.length) {
               checkAttachments();
            }
            else {
               checkAttachmentsLoop();
            }
         },100);
      }
   
      function checkAttachments() {
         for (var h = 0; h < attachmentAuthor.length; h++) {
            // if you log attachmentAuthor[h][0] to the concole, you'll get 
            // the name of the attachment.
            // if you log attachmentAuthor[h][1].get_title()) to the console,
            // you'll get the name of the author.
    
            var currentAuthor = attachmentAuthor[h][1].get_title();
            // If the current user is not the author of the current attachment,
            // then we disable the ability to delete the attachment from the item.
            if (currentAuthor != sessionStorage.getItem("sessionItemUserName")) {
               var tr = document.getElementById("idAttachmentsTable").getElementsByTagName("tr");
               for (var i = 0; i < tr.length; i++) {
                  var currentTR = $(tr)[i];
                  var anchors = $(currentTR).find("a")[0];
                  var deletes = $(currentTR).find("a")[1];
                  if (anchors.innerHTML == attachmentAuthor[h][0]) {
                     $(currentTR).attr("disabled", "disabled");
                     $(anchors).css("color", "#b1b1b1");
                     $(anchors).removeAttr("href");
                     $(anchors).removeAttr("onclick");
                     $(deletes).css("text-decoration", "line-through");
                     $(deletes).css("color", "#b1b1b1");
                     $(deletes).removeAttr("href");
                     $(deletes).removeAttr("onclick");
                  }
               }
            }
         }
      }
   }
}
else {
   if (sessionStorage.getItem("SessionItemCurrentItemID") != null) {
      sessionStorage.removeItem("SessionItemCurrentItemID");
   }
}

And there you have it! I know, I didn't format the code that nicely... And I mix up jQuery and JavaScript quite often... But anyway, it is readable.

In case you would like to see an example of the code in action, here it is:

As you can see, the first attachment is one I added. I am the author of that attachment. And thus, I can also decide whether or not I'm going to delete it. The second attachment however, was not uploaded to the item by me. I am not its author. Therefore, I am not allowed to delete it.

If you have any questions, feel free to ask!

Monday, November 3, 2014

IT'S OVER NINE THOUSAAAAAAAAAAND!!!!

Actually, it's already over ten thousand by now, but I just couldn't let a title like that slip away! :D

It has been quite a while since I made a new post. Fear not, I will post something SharePoint-related soon. My work has the priority here, but if I can find some spare time then I will continue writing on the new post. Sneak peek: calendar overlays combined with checkboxes!

Let's give you guys a short update about my project, to inform you about what it is exactly that I'm so busy with.
Currently, I'm mainly creating and improving workflows using Nintex. Also in general I'm improving some of my scripts and the CSS of my project, along with some extra's to make it more user friendly. Most of my work goes to those workflows, I need to test them over and over again to make sure there is absolutely no error at all, since it is my intention to make them work properly from the start and then never have to change them again (unless there are major changes required). And other than that, I'm now also learing VBA.

Hopefully I'll be able to finish these workflows soon and when I have some spare time left I'll come back to entertain you all with a new post. Stay tuned!