…thoughts on ServiceNow and digital transformation

Post

Prevent deletion of attachments in ServiceNow


Without fail, users that are new to ServiceNow always ask about how to recover deleted attachments. Especially on attachment fields where the Delete button is so darn close to the Update button, a stray mouseclick can mean the end of the attachment. Unfortunately, out-of-box, attachments that are deleted by the user are truly deleted. “So you’re telling me that I can’t delete or edit Work Notes but I can delete attachments without any trace?” Yes. Kind of annoying.

To make matters worse, what regular users wouldn’t notice but admins might is that on attachment fields, the system deletes the attachment but leaves the sys_id of the deleted attachment in the field. That means if you have any scripts that check to see if there is an attachment present in the field, you can’t just check for a value in the field. You also have to check that the sys_id exists in the sys_attachment table.

The below Business Rule is an answer to this common pitfall. This is handy for HRSD where attachments usually have more business signficance than in ITSM but it could be used anywhere in the platform.

The purpose of this Business Rule is to prevent the deletion of attachments on specific tables. If the attachment being deleted is in an attachment field, the BR will delete the sys_id from the attachment_field (which should be out of the box functionality anyway, in my opinion) but leave the attachment record in the sys_attachment table. If the attachment being deleted is a regular attachment, the BR will change the table name in the sys_attachment record to ZZ_YY, effectively hiding it from the attachments section at the top of the form. In both cases, the attachment will be available in the activity formatter, in the entry from when the attachment was originally uploaded.

Here’s how the BR is configured, in this case it is only triggered for attachments in the Human Resources application

Here is the script in the Advanced tab

(function executeRule(current, previous /*null when async*/ ) { 
    if (current.table_name.getValue().slice(0, 5) == 'ZZ_YY') { 
       //if it is for an attachment field 
       removeAttachmentFromField(); 
   } else { 
       //otherwise, if it is just a regular attachment, prefix the table name to ZZ_YY, this will hide it in the attachments section  
       current.setValue('table_name', 'ZZ_YY' + current.table_name.getValue()); 
       current.update(); 
       current.setAbortAction(true); 
   } 
   //abort the delete action 
   current.setAbortAction(true); 
   

function removeAttachmentFromField() { 
       //Normally when an attachment is deleted from the attachment field, the actual attachment record is deleted but the field retains the sys_id of the delete attachment. This function removes the sys_id from the field. 
       //get the table name 
       var tableName = current.table_name.getDisplayValue().replace('ZZ_YY', ''); 
       //get the GR for the host record 
       var hostRecord = new GlideRecord(tableName); 
       hostRecord.get(current.table_sys_id); 
       //find all the attachment fields and look for one with the sys_id of the attachment 
       var dictGR = new GlideRecord('sys_dictionary'); 
       dictGR.addQuery('name', tableName); 
       dictGR.addQuery('internal_type', 'file_attachment'); 
       dictGR.query(); 
       while (dictGR.next()) { 
           if (hostRecord.getValue(dictGR.element.getValue()) == current.getUniqueValue()) { 
               hostRecord.setValue(dictGR.element.getValue(), ''); //remove the sys_id 
           } 
       } 
       hostRecord.setWorkflow(false); 
       hostRecord.update(); 
   } 
}
)(current, previous);