Is there a way to add attachments to multiple records at once?

Marcel H_
Tera Guru

I've been reading related topics here about this type of thing, but it seems like most are related to bulk uploads of attachments out of one system and into ServiceNow, which is a bit different than the scenario that I'm trying to solve for.

In this case a group that works with records/data on a specific table regularly have documents that need to be uploaded to the records, but these documents many times will be summary reports that might be related to several of the records. They don't want to take the time to separate the data, and more importantly they don't want to have to add attachments to the records one at a time.

Is there a way to select multiple records, and then be able to select an attachment from the local computer that is then copied and attached to all the selected records? Could something like this be done with a UI Action and script?

3 REPLIES 3

rajneeshbaranwa
Giga Guru

You might have to create a UI Action on listview. In UI action write code to insert the row in sys_attachment table  in all selected records.

That makes sense, perhaps something similar to the UI Context Menu option for Update Selected? It seems like the script for this could be adapted to do the attachment insert maybe, since it's already handling the list of checked records and getting the sys_ids.

/**
 * Script executed on the Client for this menu action
 *
 * The following variables are available to the script:
 *    'g_list' the GlideList2 that the script is running against (only valid for List context menus)
 *    'g_fieldName' the name of the field that the context menu is running against (only valid for List context menus)
 *    'g_sysId' the sys_id of the row or form that the script is running against
 *    'rowSysId' is also set to the sys_id of the row to support legacy actions, but g_sysId is preferred
 */
runContextAction();

function runContextAction() {
   var keys = g_list.getChecked();
   if (keys == '') {
      var msg = new GwtMessage().getMessage('There are no rows selected');
      alert(msg);
      return;
   }
   
   var url = new GlideURL(g_list.tableName + '_update.do');
   url.addParam('sys_action', 'sysverb_multiple_update');
   url.addParam('sysparm_multiple', 'true');
   url.addParam('sysparm_nostack', 'yes');
   url.addParam('sysparm_checked_items', 'sys_idIN' + keys);
   url.addParam('sysparm_view', g_list.getView());
   
   window.location = url.getURL();
}

 

 

Pavel Zifcak
Mega Contributor

Yes, it is possible.

My first attempt was to send B64 encoded content of input of type file from UI page invoked within list UI action via glideAjax to the server and decoded add it to all selected records with GlideSysAttachment.write().

Well, it had worked, unfortunatelly only with files not bigger than roughly 1.5 MB - if bigger than this was selected, glideAjax was not able to send it to the server, does not matter if split to smaller chunks or not.

Then I discovered Create an Attachment UI Action. Solely not very helpful as it is mentioned to be used from the form, but experimentally I have found that

  • saveAttachment is available within the list as well
  • it creates the attachment(s) even for faked, nonexisting table_sys_id

Such created attachments can be copied to any record with copy(source_table, source_table_sys_id, target_table, target_table_sys_id) function available in GlideSysAttachment. 

I threw back all the gathering, encoding, sending, receiving, decoding, forwarding and writing stuff then and instead of that I (ab)used saveAttachment() invoked by button in my UI page:

UI page:

<input type="button" value="Manage Attachments" onClick="sajManageAttachments()" />
<input type="button" value="Set Status and Justification" onClick="fireProcessFormEvent()" />

 

UI page client script:

var attachmentFakeTableSysId = g_user.userName + "_" + Date.now();

function sajManageAttachments() {
	saveAttachment("sn_compliance_control", attachmentFakeTableSysId);
}
...
// on submit of my form from UI page 
function fireProcessFormEvent() {
	var gm = new GlideModal().get("sn_compliance_status_and_justification_gm");
	...
	gm.setPreference("attachmentFakeTableSysId", attachmentFakeTableSysId);
	gm.fireEvent('processForm');
	gm.destroy();
}

 

Within UI action the attachmentFakeTableSysId is forwarded as GA param:

var gm = new GlideModal("sn_compliance_status_and_justification_gm", false, 500);
gm.setTitle("Status and Justification");
gm.on("processForm", processForm);
gm.render();

function processForm() {
	...
	var attachmentFakeTableSysId = gm.getPreference("attachmentFakeTableSysId");
	...
	var ga = new GlideAjax("CSAS_GRC_ControlAjax"); 
	...
	ga.addParam("attachmentFakeTableSysId", attachmentFakeTableSysId);
	ga.addParam("selectedControlIds", selectedControlIds);
	ga.getXMLAnswer(_processAnswer);	
} 

 

And then extracted and used in attachment.copy():

var attachmentFakeTableSysId = this.getParameter("attachmentFakeTableSysId");	
...
attachmentResult = attachment.copy(sourceTableName, attachmentFakeTableSysId, targetTableName, controlGR.getUniqueValue());
gs.info("result of copying attachments '" + attachmentFakeTableSysId + "' is " + attachmentResult);