I am using SharePoint 2010 and I have a List which allows document attachments to be added. We are using custom forms for the new/Edit and View screens of the list Item. The problem is with the View Screen, users with only read access to the list item can open document attachments (specifically in Office 2013) and save a copy of that attachment with any changes they may have made back to the list item. We need to allow these users the ability to read the attachments, but not save changes back to the list item.
I am using the following code to make the attachment links which allow the users access to download and read the attachments on the list item.
StringBuilder AttachmentsText = new StringBuilder(); foreach (string FileName in li.Attachments) { AttachmentsText.AppendLine("<a onclick=\"" + CreateDocumentOnClickLink(li.Web.GetFile(li.Attachments.UrlPrefix + FileName), li) + " \" href=\"" + li.Attachments.UrlPrefix + FileName + "\" target=\"_blank\">" + FileName + "</a><br />"); } public string CreateDocumentOnClickLink(SPFile myfile, SPListItem li) { //string text = SPUtility.MapToControl(SPContext.Current.Web, myfile.Name, string.Empty); //string text2 = "0";// (myfile.Item.ParentList.DefaultItemOpen == DefaultItemOpen.Browser) ? "1" : "0"; ////SPFieldLookupValue sPFieldLookupValue = myfile.Item["CheckedOutUserId"] as SPFieldLookupValue; //string scriptLiteralToEncode = string.Empty;// (sPFieldLookupValue == null) ? string.Empty : sPFieldLookupValue.LookupValue; //string text3 = (SPContext.Current.Web.CurrentUser != null) ? SPContext.Current.Web.CurrentUser.ID.ToString(System.Globalization.CultureInfo.InvariantCulture) : string.Empty; //string text4 = "0";// myfile.Item.ParentList.ForceCheckout ? "1" : "0"; //return string.Format(System.Globalization.CultureInfo.InvariantCulture, "return DispEx(this,event,'{0}',{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}')", new object[] //{ // "TRUE", // "FALSE", // "FALSE", // text, // text2, // text, // string.Empty, // string.Empty, // SPHttpUtility.EcmaScriptStringLiteralEncode(scriptLiteralToEncode), // text3, // text4, // "0",//(string)myfile.Item["IsCheckedoutToLocal"], // "0x0000000000000000"//"0x7fffffffffffffff"//(string)myfile.Item["PermMask"] //}); //return "javscript:STSNavigate('" + li.Web.Url + "/_layouts/download.aspx?SourceUrl=" + li.Attachments.UrlPrefix + myfile.Name + "&Source=" + li.Attachments.UrlPrefix + "')"; return li.Web.Url + "/_layouts/download.aspx?SourceUrl=" + li.Attachments.UrlPrefix + myfile.Name + "&Source=" + li.Attachments.UrlPrefix; }
I have tried multiple different techniques to create the links that open the attachments in the hopes that it would keep the users from saving copies, but do far all have failed. You can see from the commented out code the different link types I have tried. It does keep them from saving changes to the existing attachment.
I have also tried using an Event Receiver to check when an attachment is added to a list item and check the security of the user that way.
public override void ItemAttachmentAdding(SPItemEventProperties properties) { try { base.ItemAttachmentAdding(properties); string POC = string.Empty; string ModifiedUser = string.Empty; if (AppSettings == null) { setGlobalVariables(properties); } SPSecurity.RunWithElevatedPrivileges(delegate() { POC = FormatPOC(properties, properties.ListItem, properties.ListItem["ASCPOC"].ToString()); ModifiedUser = FormatPOC(properties, properties.ListItem, properties.ListItem["Editor"].ToString()); COICTaskerHelper.WriteLog(AppSettings, properties.ListItem.Web.Url, ModifiedUser, " Made it into the ItemAttachmentAdding Event."); if (properties.ListItem["IsParent"] != null && Convert.ToBoolean(properties.ListItem["IsParent"].ToString()) == true) { if (!AJTrackerTools.IsCTBUser(AppSettings, properties, ModifiedUser)) //Not part of CTB for Parent List Item Cancel Attachment adding { COICTaskerHelper.WriteLog(AppSettings, properties.ListItem.Web.Url, ModifiedUser, " Canceling the Item Attachment because it is a parent tasker and user isnt in the CTB."); properties.Cancel = true; properties.ErrorMessage = "You do not have permissions to add an attachment to this item."; } } else { ArrayList ActionGroups = AJTrackerTools.GetActionGroups(AppSettings, properties, properties.ListItem); Boolean SaveAttachment = false; foreach (string ActionGroup in ActionGroups) { ArrayList UsersList = AJTrackerTools.GetUsersInGroup(AppSettings, properties, properties.ListItem, ActionGroup); foreach (SPUser u in UsersList) { if (!string.IsNullOrEmpty(u.LoginName.ToString())) { if (u.LoginName == ModifiedUser) { SaveAttachment = true; } } } } if (POC == ModifiedUser) SaveAttachment = true; if (SaveAttachment == false) { COICTaskerHelper.WriteLog(AppSettings, properties.ListItem.Web.Url, ModifiedUser, " Canceling the Item Attachment because it is a child tasker and user isnt in the Action group or a POC."); properties.Cancel = true; properties.ErrorMessage = "You do not have permissions to add an attachment to this item."; } } }); } catch (Exception ex) { if (AppSettings == null) { AppSettings.Add(new AppSetting("SP_LOGGING_ENABLED", "true")); } COICTaskerHelper.WriteLog(AppSettings, properties.ListItem.Web.Url, "ERROR", " ItemAttachmentAdding error: " + ex.Message); } }
The problem with this method is when adding an attachment it seems you have to make sure the Office document has to have the same content type to kick off the event. Also when opening up that same Office 2013 attachment you can still save a copy of the attachment without the event kicking off even if you use the attachment that is using the same content type. Any help on this issue would be greatly appreciated.
Thanks,
P.S. I have also tried a workflow, but when saving a copy of the Office 2013 attachment it still doesn't kick off the Item changed Event for the workflow to run and check the security of the user.
- Moved by Hemendra AgrawalMVP, Moderator Friday, May 15, 2015 5:46 AM