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!

No comments:

Post a Comment