- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 10-24-2019 10:13 AM
Pre-requisite to this article
In order to understand this article, I recommend you read fully and carefully lasse's article How to support copy / paste of pictures on Service Portal as well as the Script Include "XXX_Attachment" in the project Copy paste screenshot as attachment to any incident (or task).
You can also refer to my article How to support copy / paste of pictures on Service Portal using IE11.
General overview of the solution
You will face several problem trying to paste an image from the clipboard and you will solve them one by one:
- there is no standard zone to paste into: you will create and embed a Widget in the "SC Catalog Item" Portal Widget.
- you can't paste an image with IE11: in fact you can. You will use a "content editable" container and save the image with your own code.
- to create an attachment, you need a record ID and it has not been created yet: the Widget has generated an ID for the cart but you don't see it. You will read this ID and communicate it to the new Widget
So here are the steps to follow:
- modify the "SC Catalog Item" Portal Widget to broadcast the cart id.
- create the Paste Widget, which will listen for the cart id and save the image.
- embed the Paste Widget in the "SC Catalog Item" Portal Widget.
1. Modify the "SC Catalog Item" Portal Widget to broadcast the cart id
After having created a copy of the OOTB Widget, open the HTML template, locate the first container and add an init event. The code should now be something like this:
<div id="sc_cat_item" ng-if="::(data.recordFound && !data.not_for_mobile)" sn-atf-blacklist="IS_SERVICE_CATALOG"
ng-init="onLoad()">
Add the following code to the Client Script to broadcast the cart id to the embedded-to-be Widget:
$scope.onLoad = function() {
var table = ''+$scope.data._attachmentTable;
var id = ''+$scope.data._generatedItemGUID;
$timeout(function() {
$scope.$broadcast('catalog_widget.table.sys_id', id);
$scope.$broadcast('catalog_widget.table.name', table);
},500); // wait for the page to load
}
2. Create the Paste Widget
(sorry for the screenshots: I couldn't find how to correctly display the double braces on this forum)
Create a new Portal Widget
Create a new Portal Widget and name it as you wish. I named it "fuji_pastearea_for_ie11".
Create the HTML template:
Create the Client Script
Add the following code:
function spPasteAreaForIE11($scope, nowAttachmentHandler, $animate, $rootScope, cabrillo, $timeout, snRecordWatcher, spUtil, spAriaUtil, $http, $window, snAttachmentHandler, i18n) {
$scope.showLocationIcon = false;
$scope.msg = "";
$scope.isNative = cabrillo.isNative();
$scope.errorMessages = [];
var existingEntries = {};
var c = this;
var skipNextRecordWatchUpdate = false;
$scope.typing = [];
c.generatedItemGUID = null;
c.generatedItemTablename = null;
$scope.$on('catalog_widget.table.sys_id', function (e, id) {
c.generatedItemGUID = id;
});
$scope.$on('catalog_widget.table.name', function (e, name) {
c.generatedItemTablename = name;
});
$scope.addInformativeText = function (event) {
getPasteArea().innerHTML = c.data.pasteText;
}
$scope.removeInformativeText = function (event) {
getPasteArea().innerHTML = "";
}
function getPasteArea() {
return document.getElementById("pasteArea");
}
$scope.pasteIE = function (event) {
console.log('pasteIE');
event.preventDefault(); // prevent image from displaying in the paste area
if (typeof window.clipboardData != 'undefined') {
var clip = window.clipboardData;
if (clip) {
if (clip.files.length == 0) {
pastedNotImage();
} else if (clip.files[0].type.indexOf('image/') !== -1) {
//var url = URL.createObjectURL(clip.files[0]);
//var blob = url.substring(4);
var myFile = clip.files[0];
var reader = new window.FileReader();
reader.onloadend = function () {
var base64Image = reader.result;
attachClipboardData(base64Image);
};
reader.readAsDataURL(myFile);
} else {
pastedNotImage();
}
}
}
}
function pastedNotImage() {
alert(c.data.notImageText);
}
function attachClipboardData(data) { // date is 'date:image/png;base64,...'
console.log('attachClipboardData');
var temp = data.toString().replace(/data:/g, '').split(';');
var contentType = temp[0];
var fileName = 'Screenshot';
var fileType = contentType.split('/')[1];
var content = temp[1].toString().replace(/base64,/g, '');
c.data.action = "attachScreenshot";
c.data.attachment_contentType = contentType;
c.data.attachment_fileName = fileName;
c.data.attachment_fileType = fileType;
c.data.attachment_content = content;
c.data.attachment_generatedItemGUID = c.generatedItemGUID;
c.data.attachment_generatedItemTablename = c.generatedItemTablename;
c.server.update().then(function () {
c.data.action = undefined;
$rootScope.$broadcast("sp.attachments.update", $scope.data.sys_id);
spAriaUtil.sendLiveMessage($scope.data.attachAddedMsg);
});
}
}
Create the Server Script
The most important part here is the ""attachScreenshot" action : it will create an Attachment in "sys_attachment" table with "table_name" field set to "sc_cart_item" and the "table_sys_id" field set to the id sent by the "SC Catalog Item" Widget.
(function() {
gs.log('Server script');
data.maxAttachmentSize = parseInt(gs.getProperty("com.glide.attachment.max_size", 1024));
if (isNaN(data.maxAttachmentSize))
data.maxAttachmentSize = 24;
data.uploadingAttachmentMsg = gs.getMessage("Uploading attachment...");
data.sharingLocMsg = gs.getMessage("Sharing location...");
data.scanBarcodeMsg = gs.getMessage("Scan barcode");
data.checkInLocMsg = gs.getMessage("Check in location");
data.messagePostedMsg = gs.getMessage("Message has been sent");
data.viewMsg = gs.getMessage("View");
data.attachAddedMsg = gs.getMessage("Attachment added");
data.attachFailMsg = gs.getMessage("Failed to add attachment");
data.scanFailedMsg = gs.getMessage("File failed security scan");
data.sys_id = input.sys_id || options.sys_id || $sp.getParameter("sys_id");
data.table = input.table || options.table || 'sc_req_item';
// don't use options.title unless sys_id and table also come from options
if (options && options.sys_id && options.table)
data.ticketTitle = options.title;
data.placeholder = options.placeholder || gs.getMessage("Type your message here...");
data.placeholderNoEntries = options.placeholderNoEntries || gs.getMessage("Type your message here...");
data.btnLabel = options.btnLabel || gs.getMessage("Send");
data.includeExtended = options.includeExtended || false;
data.use_dynamic_placeholder = options.use_dynamic_placeholder;
data.isNewRecord = data.sys_id == -1 || gr.isNewRecord();
data.hideAttachmentBtn = options.hideAttachmentBtn;
data.pasteText = options.pasteText || gs.getMessage("Paste your screenshot here using CTRL+V...");
data.pasteTitle = options.pasteTitle || gs.getMessage("Paste image area");
data.notImageText = options.pastenotImageTextTitle || gs.getMessage("You are not pasting a screenshot");
data.attachmentTable = 'sc_cart_item';
var gr = new GlideRecord(data.table);
if (!gr.isValid()) {
gs.log('gr is not valid');
return;
}
data.table = gr.getRecordClassName(); // use actual table for the record
if (input) { // if we have input then we're saving
if (input.action == "attachScreenshot") {
var itemId = input.attachment_generatedItemGUID;
var tablename = input.attachment_generatedItemTablename;
var value = GlideStringUtil.base64DecodeAsBytes(input.attachment_content);
var attachment = new fujiAttachment();
var name = input.attachment_fileName + " " + gs.nowDateTime() + "." + input.attachment_fileType;
gs.log('Creating attachment with tablename='+tablename+', itemId='+itemId+
', name='+name+', input.attachment_contentType='+input.attachment_contentType);
var attID = attachment.write(tablename, itemId, name, input.attachment_contentType, value);
gs.log("Created sys_attachment with id "+attID);
}
}
data.canWrite = gr.canWrite();
data.canAttach = gs.hasRole(gs.getProperty("glide.attachment.role")) && GlideTableDescriptor.get(data.table).getED().getAttribute("no_attachment") != "true";
data.canRead = gr.canRead();
})()
3. create fujiAttachment Script Include
Use this code:
/*
* Adapted from https://developer.servicenow.com/app.do#!/share/contents/7877086_copy_paste_screenshot_as_attachment_to_any_incident_or_task?v=1.04&t=PRODUCT_DETAILS
*/
var fujiAttachment = Class.create();
fujiAttachment.prototype = Object.extendsObject(Attachment, {
attach: function() {
//gs.log("BEGIN attach");
if(this.target == null) {
//build target via gliderecord call
if(this.tablename == null || this.targetID == null)
return "Table name and/or target sys id are null. Please specify valid parameters.";
var targetRecord = new GlideRecord(this.tablename);
if(!targetRecord.get(this.targetID)) {
// return "Could not find a record in table '" + this.tablename + "' with sys_id '" + this.targetID + "'";
}
this.setTarget(targetRecord);
}
var sa = new GlideSysAttachment();
var attachmentId = sa.write(this.target, this.filename, this.contentType, this.value);
//gs.log("END attach");
if(attachmentId)
return attachmentId;
else
return "Attachment creation failed";
},
type: 'fujiAttachment'
});
4. embed the Paste Widget in the "SC Catalog Item" Portal Widget
Modify the HTML template
Add the following line to the HTML template where you want it (I put it under the attachments list) :
<sp-widget widget="data.widget_fujiPasteIE11" class="form-group m-b-xs" />
Modify the Server Script
Add the following line as the first line of the script :
data.widget_fujiPasteIE11 = $sp.getWidget('fuji_pastearea_for_ie11',null);
And ... that's it. It should be working.
- 3,553 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
I forgot a script include in my solution: fujiAttachment
Description: Handler for attachment processing
Modified from OOTB to return id of new attachment added instead of some text message
/*
* Adapted from https://developer.servicenow.com/app.do#!/share/contents/7877086_copy_paste_screenshot_as_attachment_to_any_incident_or_task?v=1.04&t=PRODUCT_DETAILS
*/
var fujiAttachment = Class.create();
fujiAttachment.prototype = Object.extendsObject(Attachment, {
attach: function() {
//gs.log("BEGIN attach");
if(this.target == null) {
//build target via gliderecord call
if(this.tablename == null || this.targetID == null)
return "Table name and/or target sys id are null. Please specify valid parameters.";
var targetRecord = new GlideRecord(this.tablename);
if(!targetRecord.get(this.targetID)) {
// return "Could not find a record in table '" + this.tablename + "' with sys_id '" + this.targetID + "'";
}
this.setTarget(targetRecord);
}
var sa = new GlideSysAttachment();
var attachmentId = sa.write(this.target, this.filename, this.contentType, this.value);
//gs.log("END attach");
if(attachmentId)
return attachmentId;
else
return "Attachment creation failed";
},
type: 'fujiAttachment'
});
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Christian,
1st of all, you are amazing.
I am able to get the results as expected on sc catalog item.
I forgot to mention that we are using sc catalog item deprecated and results are correctly displayed.
Now, image is being attached to the deprecated catalog item while composing; however, in order to display that screenshot i have to add another one using given attachment clip which then displays both in attachment list...
any idea whats difference between sc catalog item and deprecated one which would be causing this..?
Again thanks for your precious time..
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Added recordwatch in deprecated catalog item and now its working...
Thanks Christian for everything... 🙂
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi vicks,
I would say, without knowing the deprecated one, that the refresh attachment list is triggered in a different way. The non-deprecated one seems to use a Record Watcher.
Why are you using the old one ?
By the way, you were right: I had to make minor adjustments after posting this article. So I checked again and corrected the article. The crucial part is to grab the internal variables '$scope.data._generatedItemGUID' and '$scope.data._attachmentTable' from the Catalog Widget and pass them to the Attachment Widget so you can use them in the Server Script.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Well, i just started working on ServiceNow and the project i got has been using old catalog item because they have made many customizations on service portal before the new one came in.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Legacy code ... always a delicacy ...
Glad you were able to achieve your intended result 🙂