LogoLogo
AboutSupport
4.2
4.2
  • Unifi User Documentation
  • Install
    • Release Notes
      • Unifi 4.2 Release Notes
      • Unifi 4.1 Release Notes
      • Unifi 4.0 Release Notes
      • Unifi 3.1 Release Notes
      • Unifi 3.0 Release Notes
      • Unifi 2.2 Release Notes
      • Unifi 2.1 Release Notes
      • Unifi 2.0 Release Notes
    • Install or Upgrade
      • Global Utility
      • Hotfix
  • Configure
    • Integration Designer
    • Processes
    • Integrations
    • Connections
    • Messages
    • Message Scripts
    • Fields
    • Field Maps
    • Response Actions
    • Event Actions
    • Datasets
      • Create a New Dataset
      • Dataset Extras
    • Polling
      • Pollers
      • Poll Processors
    • Administration
      • Activity Logs
      • Data Stores
      • Properties
      • Scheduled Scripts
      • System Logs
    • Attachments
      • Extracting Attachments
      • Fetching Attachments
      • Embedded Attachments
    • Scripting
      • Snippets
      • Variables
    • Documentation
    • How to guides
      • How to Handle Attachments
        • Message
        • Scripted REST Resource
        • Test AddAttachment
      • How to Manually Close a Bond
      • How to Poll for Large Response Payloads
      • How to Setup an OAuth Connection
        • Identity Provider Instance
        • Identity Consumer Instance
        • OAuth Refresh Token Job
      • How to Setup Heartbeat Messages
  • Deploy
    • Package
    • Instance Clone
  • Operate
    • Bonding
      • Bonds
      • Bonded Attachments
    • Transport
      • Snapshots
      • Transactions
      • Stages
      • HTTP Requests
      • Dataset Requests
      • Poll Requests
    • Error Handling
      • Integration Pause and Resume
      • Integration Repair
      • Request Retry
      • Transaction & Request Replay
      • Transaction Ignore
  • Test
    • Overview
    • Integration Test
    • Test Scenario
    • Test Scenario Data
    • Test Result
    • Test Scenario Result
    • Generating Tests
    • Running Tests
    • Exploring Results
  • Integration Guides
    • Outbound Incident Guide
      • Getting Started
      • Process
      • Integration
      • Connection
      • Create Scenario
        • CreateIncidentResponse Message
        • CreateIncidentResponse Fields
        • CreateIncident Message
        • CreateIncident Fields
        • Trigger
        • Test CreateIncident
      • Update Scenario
        • Response Message
        • UpdateIncident Message
        • UpdateIncident Fields
        • Test UpdateIncident
      • Resolve Scenario
        • ResolveIncident Message
        • ResolveIncident Fields
        • Test ResolveIncident
      • Build - Integration Level
      • Conclusion
    • Bidirectional Asynchronous Incident Guide
      • Getting Started
      • Process
      • Web Service
      • Integration
      • Connection
      • Create Scenario
        • Response Message
        • CreateIncidentReceipt Message
        • CreateIncidentReceipt Fields
        • CreateIncident Message
        • CreateIncident Fields
        • Trigger
        • Test CreateIncident
      • Update Scenario
        • Receipt Message
        • UpdateIncident Message
        • UpdateIncident Fields
        • Test UpdateIncident
      • Resolve Scenario
        • ResolveIncident Message
        • ResolveIncident Fields
        • Test ResolveIncident
      • Build - Integration Level
      • Build the Other Half
        • Move the Integration
        • Reconfigure the Connections
      • Conclusion
    • Incident Update Poller Guide
      • Polling
        • Poll Processor
        • Poller
      • Inbound Message
        • UpdateIncidentInbound Message
        • UpdateIncidentInbound Fields
      • Message Identification
      • Bond Identification
        • Edit Incident Form
        • Edit CreateIncident Message
      • Test Update Poll
      • Conclusion
    • Incident Multiple Message Poller Guide
      • Polling
        • Poll Processor
        • Poller
      • Inbound Messages
        • ResolveIncidentInbound Message
        • ResolveIncidentInbound Fields
      • Testing
        • Test UpdateIncidentInbound
        • Test ResolveIncidentInbound
      • Conclusion
    • Incident Create Poller Guide
      • Polling
        • Connection Variables
        • Poll Processor
        • Poller
      • Messages
        • CreateIncidentInboundReceipt Message
        • CreateIncidentInboundReceipt Fields
        • CreateIncidentInbound Message
        • CreateIncidentInbound Fields
      • Build - Integration Level
      • Test Create Poll
      • Conclusion
    • Incident Parent and Child Poller Guide
      • Polling
        • Connection Variables
        • Child Poll Processor
        • Child Poller
        • Parent Poll Processor
        • Parent Poller
      • Inbound Messages
      • Testing
        • Test UpdateIncidentInbound
        • Test ResolveIncidentInbound
      • Conclusion
    • Incident Attachment Poller Guide
      • Polling
        • Connection Variables
        • Edit Endpoint URLs
        • Get Attachment Poll Processor
        • Get Attachment Poller
        • Select Attachments Poll Processor
        • Select Attachments Poller
        • Edit Child Poll Processor
        • Edit Child Update Poller
      • Messages
        • AddAttachmentInbound Message
      • Testing
        • Test Outbound Scenarios
        • Test CreateIncidentInbound
        • Test UpdateIncidentInbound
        • Test ResolveIncidentInbound
        • Test AddAttachmentInbound
      • Conclusion
  • Troubleshooting
    • Attachments
      • Inbound SOAP/Base64 attachments stopped working
      • New record attachments are not sent from Portal
      • Special characters in attachment file names
    • Datasets
    • Development
      • Bonding to existing records
      • Copying an existing Unifi trigger rule doesn't work
      • Duplicate messages being sent
      • Deleted records are not packaged
      • Multipart Form Data
      • Undefined error when building an integration
    • Diagnostic
    • Installation
      • Latest version of Unifi not accessible
    • Integration Responses
      • Transaction has been processed already
      • Initiating transaction not found for inbound receipt
      • Message has already been processed
      • Message ID not found
      • Message is not valid for this bond
      • Message name not recognised
      • No retry for requests with 401 response
      • Unable to identify message name from request
    • Other
      • Dynamic stage does not render
      • Duplicate bonds on Request integrations
    • Self-test
  • About
    • Quick Tour
    • Supported Features
    • Application Modules
    • Data Model
    • Transport Data Flow
Powered by GitBook
On this page
  • Introduction
  • Use Case
  • Automatic Embedding
  • 1. Open the Field Map
  • 2. Embed Image Attachments Function
  • 3. Modify Journal Entries
  • 4. Build
  • Known Limitations
  • File size
  • Instance security
  • Alternative Approaches

Was this helpful?

Export as PDF
  1. Configure
  2. Attachments

Embedded Attachments

Introduction

Attachments in ServiceNow are normally attached to the records they belong to, making it easy to identify and share those attachments over an integration. Embedded attachments require a bit more effort to integrate since they can belong to other records, or in some cases, no record at all. They are typically found inside journal fields.

Use Case

The use case being looked at in this example is embedding attachments in the Additional Comments field using HTML inside [code] tags so lengthy comments such as operational checks can be shared with third parties directly from the instance.

To recreate this scenario:

  1. Open a record with a journal field, i.e. an Incident.

  2. From the More options selector, choose Email. The Compose Email window will open.

  3. Copy/paste or drag an image into the email composer. (In Windows, you can use Windows Key + Shift + S to take a screenshot and then Ctrl + V to paste it into the formatter).

  4. Click the Source code button to view the HTML source code.

  5. Select all the source code (Ctrl + A) and copy it (Ctrl + C).

  6. Back in your Incident, go to a journal field, e.g. Additional Comments and paste in the HTML.

  7. Wrap the comment in [code]...[/code] tags to tell ServiceNow to treat this comment as code.

  8. If the record and field is integrated, the field will be shared over the integration, but the other system will not have the attachment so the image will not show.

Note: [code] tags will only be rendered if the glide.ui.security.allow_codetag system property is enabled.

Automatic Embedding

We can allow the integration to share embedded attachments with some modification to the outbound mapping scripts.

In this example, we are sharing a journal field so will modify the Field Map for Journal fields. We want the attachment data to be automatically embedded as base64 instead of sending the sys_attachment reference.

1. Open the Field Map

Open Integration Designer and navigate to the Field Map you want to modify. Find the Source to Stage mapping script in theOutbound panel.

2. Embed Image Attachments Function

Copy the function below into your Source to Stage mapping script. This function accepts some text and updates all<img> tags that reference the sys_attachment table.

function embedImageAttachments(text, options) {

  function getAttachment(attachment_id) {
    var attachment = new GlideRecord('sys_attachment');
    return attachment.get(attachment_id) ? attachment : null;
  }

  function humanFileSize(size) {
    var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
    return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
  }

  function findImages(text) {
    var match_img = /<img\s+\w+=(\\?["'])[\s\S]+?\1\s*\/?>/;
    var tags = [];
    var start, end, match;
    while ((start = text.indexOf('<img')) > -1) {
      text = text.slice(start);
      match = text.match(match_img);
      if (!match) continue;
      end = match[0].length;
      tags.push({
        start: start,
        end: start + end,
        tag: text.slice(0, end + 1)
      });
      text = text.slice(end + 1);
    }
    return tags;
  }

  function embedImages(text) {
    var match_src = /src=\\?(["'])(\/?sys_attachment\.do\?(?:[\s\S]+&)*sys_id=([a-f0-9]{32})(?:&.+)*)\1/gm;
    var images = findImages(text);
    var output = '';
    var position = 0;
    if (!images.length) {
      return text;
    }
    images.forEach(function (image) {
      output += text.slice(position, image.start);
      output += image.tag.replace(match_src, replaceImageSrc);
      position = image.end + 1;
    });
    output += text.slice(position);
    return output;
  }

  function _log(text) {
    $stage.$embedded_attachment_logs.push(text);
    return text;
  }

  function replaceImageSrc(match, quote, src, attachment_id) {
    var attachment = getAttachment(attachment_id);
    var result = '';
    var data_uri;

    if (!attachment) {
      _log.warn(_log('Cannot embed image attachment ' + attachment_id + '; invalid attachment.'));
      return match;
    }

    _log('Embedding image attachment ' + attachment_id + '.');

    data_uri = 'data:' + attachment.content_type + ';base64,';
    $stage.$payload_size = $stage.$payload_size - src.length + data_uri.length + (attachment.size_bytes * 4/3);

    if ($stage.$payload_size > $stage.$payload_size_max) {
      error = 'Unable to embed image attachments; combined file size is too large to send ' +
            '(' + 
              humanFileSize($stage.$payload_size) + ' > ' +
              humanFileSize($stage.$payload_size_max) +
            ').';
      throw new Error(_log(error));
    }

    // this tag will be automatically replaced with base64 during send
    data_uri += '<x-attachment-data sys_id="' + attachment_id + '" />';

    result += match.replace(src, data_uri);

    // ensure file name is included
    if (match.indexOf('name=' + quote) == -1) {
      result += ' name=' + quote + attachment.file_name + quote;
    }
    return result;
  }

  // ensure objects are strings (e.g. GlideElement objects)
  text = String(text);

  // allow space for the actual message outside of the text with embedded attachments
  var payload_size_no_attachments = 5 * 1024;

  // track embedded attachment size as part of the full payload
  $stage.$payload_size = ($stage.$payload_size || payload_size_no_attachments) + text.length;

  // scoped string limit is 5MB
  $stage.$payload_size_max = $stage.$payload_size_max || (5 * 1024 * 1024);

  // make it easy to debug                                          
  $stage.$embedded_attachment_logs = $stage.$embedded_attachment_logs || [];       

  return embedImages(text);
}

3. Modify Journal Entries

The final step is to update the same Field Map to use the new embedImageAttachments() function that you've just added.

Add the code to the bottom of the same Source to Stage mapping script and modify as required.

This code is designed for the out of box Journal field map.

$stage.$[field.element].forEach(function (log) {
    log.text = embedImageAttachments(log.text);
});

4. Build

Build your integration to push the Field Map changes to all the messages. You can now test.

Known Limitations

File size

Embedding attachments in this way will mean the maximum string length imposed by ServiceNow of 5mb will be easy to reach. Since base64 is a 4/3 compression, you have a realistic limit of around 3mb of images which can be transferred this way in any single request.

Instance security

This approach is not considered secure since it is possible for malicious Javascript to be rendered in the [code] tags.

The glide.ui.security.allow_codetag system property is responsible for rendering [code] tags. This is typically disabled as part of Instance Security Hardening.

If the property is disabled then [code] tags will not be rendered. Both systems must have this property enabled for this solution to work.

Alternative Approaches

Some alternative approaches to embedding attachments in this way are:

  1. Create a document containing the images and upload it as an attachment on the record. If you are streaming attachments, you will be able to share a file up to 50mb in size.

  2. Upload the images separately as attachments and reference them in your comments. If you are streaming attachments, each image could be up to 50mb in size.

PreviousFetching AttachmentsNextScripting

Was this helpful?