Circumvent Intelligent Advisor 50 MB Max File Attachment Limit
This is the B2B Service FAQ describing the 50 MB file attachment limit.
https://cx.rightnow.com/app/answers/detail/a_id/11619
Set Upload Control Custom Properties

Attribute | Values | Required | Notes |
name | xUpload | yes | this creates the custom upload |
max files | any number | no | only required if you want to set a max |
extensions allowed | .jpg,.jpeg | no | restrict to file type. Note the period before the extension is required. Comma separated values |
required | true or false | no | |
category | any string | yes | this creates the unique upload group. |
Save modal.css file to resources folder.
The modal.css is used to create a nice preview when clicking on the thumbnail image.
/* Style the Image Used to Trigger the Modal */
#myImg {
border-radius: 5px;
cursor: pointer;
transition: 0.3s;
}
#myImg:hover {opacity: 0.7;}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.9); /* Black w/ opacity */
}
/* Modal Content (Image) */
.modal-content {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
}
/* Caption of Modal Image (Image Text) - Same Width as the Image */
#caption {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
text-align: center;
color: #ccc;
padding: 10px 0;
height: 150px;
}
/* Add Animation - Zoom in the Modal */
.modal-content, #caption {
animation-name: zoom;
animation-duration: 0.6s;
}
@keyframes zoom {
from {transform:scale(0)}
to {transform:scale(1)}
}
/* The Close Button */
.close {
position: absolute;
top: 15px;
right: 35px;
color: #f1f1f1;
font-size: 40px;
font-weight: bold;
transition: 0.3s;
}
.close:hover,
.close:focus {
color: #bbb;
text-decoration: none;
cursor: pointer;
}
/* 100% Image Width on Smaller Screens */
@media only screen and (max-width: 700px){
.modal-content {
width: 100%;
}
}
Save fileUpload.css file to resources folder.
This styles your custom upload group
.uploadButton{
border: 2px dashed gray;
background: #F5F5F5;
width: 75px;
height: 75px;
vertical-align: top;
border-radius: 8px;
display: inline-block;
text-align: center;
line-height: 75px;
font-size:12px;
margin-right:20px;
cursor: pointer;
}
.preview {
vertical-align: top;
margin-right: 20px;
display: inline-block;
width:75px;
height:75px;
margin-right:20px;
}
.uploadNote{
height:70px;
display: inline-block;
width: calc(100% - 120px);
}
.uploadGroup{
padding: 10px;
border: solid 1px #808080;
margin-bottom: 20px;
border-radius: 5px;
min-width: 200px;
}
.deleteWrapper{
margin-top:5px;
}
.deleteWrapper svg{
margin-right:10px;
}
Add fileUpload.js Interview Extension to your resources folder.
This does all the magic to display and upload your files.
var appName = "OIA_File_Attach_Upload";
var appVersion = "1.0";
var isDevMode = false;
var isLocal = true; // don't use BUI extension code when debugging locally.
var currentWindow = window.location.href;
var baseURI = 'https://example.custhelp.com/';
var incidentId = null;
var uploadControl = {};
if(currentWindow.indexOf("localhost") == -1){
isLocal = false;
}
if(currentWindow.indexOf("--tst") == -1){
isDevMode = true;
}
if(isDevMode){
baseURI = 'https://example--tst1.custhelp.com/';
}
OraclePolicyAutomation.AddExtension({
customUpload: function (control) {
if (control.getProperty("name") == "xUpload") {
return {
mount: function (el) {
if(checkRequiredAttributes(control)){
addModalHTML(el);
if(isLocal){
makeUploadGroup(el, control);
} else{
init_bui(el, control);
}
}
},
unmount: function () {
// remove files from Interview so they are not uploaded when the interview is submitted;
var uploads = control.getUploads();
if(uploads.length){
for(var i in uploads){
if (uploads.hasOwnProperty(i)) {
control.removeUpload(i);
}
}
}
},
};
}
}
});
function init_bui(el, control){
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = baseURI + '/AgentWeb/module/extensibility/js/client/core/extension_loader.js';
script.onload = function() {
getGlobalContext().then(function () {
getExtensionProvider().then(function (extensionProvider) {
extensionProvider.registerWorkspaceExtension(function(workspaceRecord){
incidentId = workspaceRecord.getWorkspaceRecordId();
loadCategoryFiles(el, control);
});
});
});
};
document.head.appendChild(script);
}
function checkRequiredAttributes(control){
if(control.getProperty("category") == null){
alert('Attachment missing attribute: Category');
return false;
}
fileRequired = control.getProperty("required");
maxFiles = parseInt(control.getProperty("max files"));
// set attributes for the controller upload group
uploadControl[control.id] = {};
uploadControl[control.id].maxFiles = (maxFiles) ? maxFiles : 1000;
uploadControl[control.id].fileCount = 0;
uploadControl[control.id].fileRequired = (fileRequired == 'true') ? true : false;
uploadControl[control.id].category = control.getProperty("category");
uploadControl[control.id].filesUploaded = {};
return true;
}
// modal used to display preview of image
function addModalHTML(el){
var modal = document.createElement('div');
var span = document.createElement('span');
var img = document.createElement("img");
var caption = document.createElement('div');
modal.id = "modal";
modal.setAttribute("class", "modal");
modal.onclick = function () {
modal.style.display = "none";
};
span.setAttribute("class", "close");
span.innerHTML = '×';
img.id = 'modalImage';
img.setAttribute("class", "modal-content");
caption.id = 'caption';
modal.appendChild(span);
modal.appendChild(img);
modal.appendChild(caption);
el.appendChild(modal);
}
function loadCategoryFiles(el, control){
var getCurrentAttachments = new ORACLE_SERVICE_CLOUD.ExtensionPromise();
var category = uploadControl[control.id].category;
var query = "/connect/v1.4/queryResults?query=SELECT id, AttachmentNotes, FileAttachments.ID as fileId, FileAttachments.FileName from CO.FileAttachments where Category = '"+category+"' and Incident = " + incidentId;
var promiseArray = [];
getQueryResult(query, "getFileAttachments").then(function (queryResponse) {
getCurrentAttachments.resolve();
rows = queryResponse.items[0].rows;
for(var i in rows){
if (rows.hasOwnProperty(i)) {
let uploadGroupId = control.id + i;
let fileAttachId = rows[i][0];
uploadControl[control.id].fileCount++;
uploadControl[control.id].filesUploaded[uploadGroupId] = fileAttachId;
// div wrapper
div = document.createElement("div");
div.id = 'wrapper_' + uploadGroupId;
div.setAttribute("class", "uploadGroup");
// preview
fileName = rows[i][3];
fileId = rows[i][2];
fileNameParts = fileName.split('.');
fileExtension = fileNameParts[1];
if ( /\.(jpe?g|png|gif)$/i.test(fileName) ) {
// show image
preview = document.createElement("img");
preview.id = fileId;
preview.setAttribute("class", "preview");
preview.style.cursor = "pointer";
div.appendChild(preview);
let imageQuery = '/connect/v1.4/CO.FileAttachments/'+fileAttachId+'/FileAttachments/'+fileId+'/data';
let returnData = {};
returnData.fileId = fileId;
returnData.fileExtension = fileExtension;
promiseArray.push(getQueryResult(imageQuery, "getFileAttachments", returnData));
} else{
// show non-image box
preview = document.createElement("div");
preview.innerHTML = '<div class="opa-file-item"><div class="opa-file-preview"><div class="opa-file-item-file"><label>'+fileExtension+'</label></div></div></div>';
div.appendChild(preview);
}
// create textarea for the note field
attachmentNote = document.createElement("textarea");
note = rows[i][1];
attachmentNote.name = "uploadNote";
attachmentNote.id = "uploadNote";
attachmentNote.maxLength = "5000";
attachmentNote.setAttribute("class", "uploadNote");
attachmentNote.setAttribute("placeholder", "Add attachment note");
attachmentNote.value = note;
attachmentNote.onchange = function () {
saveNote(control, uploadGroupId, this.value);
};
div.appendChild(attachmentNote);
// delete button
deleteButton = makeDeleteButton(div, uploadGroupId, fileName, el, control);
// append
div.appendChild(deleteButton);
el.appendChild(div);
}
}
if(uploadControl[control.id].fileCount < uploadControl[control.id].maxFiles){
makeUploadGroup(el, control);
}
if(promiseArray.length){
Promise.all(promiseArray).then(function(results) {
for(var x in results){
if (results.hasOwnProperty(x)) {
let queryResults = results[x];
let thumbnail = document.getElementById(queryResults.returnData.fileId);
thumbnail.src = 'data:image/'+queryResults.returnData.fileExtension+';base64, ' + queryResults.data;
thumbnail.onclick = function () {
var modal = document.getElementById("modal");
var modalImg = document.getElementById("modalImage");
var captionText = document.getElementById("caption");
modal.style.display = "block";
modalImg.src = thumbnail.src;
captionText.innerHTML = fileName;
};
}
}
}).catch(function (errorMSG) {
console.log(errorMSG);
});
}
});
}
function makeUploadGroup(el, control){
var uploadButton, fileInput;
var uploadGroupId = control.id + Date.now(); // used to create a unique key value
// create DOM wrapper
div = document.createElement("div");
div.id = 'wrapper_' + uploadGroupId;
div.setAttribute("class", "uploadGroup");
// create "Add File" upload button
uploadButton = document.createElement("label");
uploadButton.innerHTML = 'Add File';
uploadButton.setAttribute("class", "uploadButton");
uploadButton.id = uploadGroupId;
// create input upload
fileInput = document.createElement("input");
fileInput.setAttribute("type", "file");
fileInput.setAttribute("id", "file-input");
fileInput.setAttribute("style", "display:none");
if(control.getProperty("extensions allowed") != ''){
fileInput.setAttribute("accept", control.getProperty("extensions allowed"));
}
fileInput.onchange = function (evt) {
let fileList = evt.target.files; // capture uploaded file
// create attachment note field and append to upload group
let attachmentNote = document.createElement("textarea");
attachmentNote.name = "uploadNote";
attachmentNote.id = "uploadNote";
attachmentNote.maxLength = "5000";
attachmentNote.setAttribute("class", "uploadNote");
attachmentNote.setAttribute("placeholder", "Add attachment note");
attachmentNote.onchange = function () {
saveNote(control, uploadGroupId, this.value);
};
div = document.getElementById('wrapper_' + uploadGroupId);
div.appendChild(attachmentNote);
// read the uploaded file
file = fileList[0]; // get the first and only element
if(uploadControl[control.id].fileRequired){
control.addFiles(evt.target.files); // add uploaded file to interview to
}
readImage(file, uploadGroupId, el, control);
// increment the total file count
uploadControl[control.id].fileCount++;
// add another upload group if the total uploads is less than the the defined max files
if(uploadControl[control.id].fileCount < uploadControl[control.id].maxFiles){
makeUploadGroup(el, control);
}
};
// append file upload button to file Input
uploadButton.appendChild(fileInput);
// append upload button to wrapper
div.appendChild(uploadButton);
// append wrapper to control element
el.appendChild(div);
}
function readImage(file, uploadGroupId, el, control) {
const reader = new FileReader();
reader.onload = function (event) {
file.dataURI = event.target.result;
file.textNote = null;
uploadButton = document.getElementById(uploadGroupId);
uploadButton.setAttribute("style", "display:none");
if ( /\.(jpe?g|png|gif)$/i.test(file.name) ) { // create preview for image files only
preview = document.createElement("img");
preview.src = file.dataURI;
preview.setAttribute("class", "preview");
preview.style.cursor = "pointer";
preview.onclick = function () {
var modal = document.getElementById("modal");
var modalImg = document.getElementById("modalImage");
var captionText = document.getElementById("caption");
modal.style.display = "block";
modalImg.src = file.dataURI;
captionText.innerHTML = file.name;
};
} else{
fileNameParts = file.name.split('.'); // create an array of the file name delimited by a period
preview = document.createElement("div");
preview.setAttribute("class", "preview");
preview.innerHTML = '<div class="opa-file-item"><div class="opa-file-preview"><div class="opa-file-item-file"><label>'+fileNameParts[1]+'</label></div></div></div>';
}
div = document.getElementById('wrapper_' + uploadGroupId);
div.insertBefore(preview, uploadButton);
deleteButton = makeDeleteButton(div, uploadGroupId, file.name, el, control);
div.appendChild(deleteButton);
saveRecordExternal(file, control, uploadGroupId);
};
reader.readAsDataURL(file);
}
function saveNote(control, uploadGroupId, note){
if(!isLocal){
var saveFileAttachmentNote = new ORACLE_SERVICE_CLOUD.ExtensionPromise();
var data = '{"AttachmentNotes": "'+note+'"}';
var query = "/connect/v1.4/CO.FileAttachments/" + uploadControl[control.id].filesUploaded[uploadGroupId];
var source = "save file note attachment";
patchQueryData(query, data, source).then(function () {
saveFileAttachmentNote.resolve();
}).catch(function (errorMSG) {
alert(errorMSG);
saveFileAttachmentNote.resolve();
});
}
}
function saveRecordExternal(file, control, uploadGroupId){
if(!isLocal){
var dataURIParts = file.dataURI.split(',');
var category = uploadControl[control.id].category;
var createFileAttachmentRecord = new ORACLE_SERVICE_CLOUD.ExtensionPromise();
var data = '{"Incident": {"id":'+incidentId+'}, "Category": "'+category+'","FileAttachments": {"fileName":"'+file.name+'", "data": "'+dataURIParts[1]+'"}}';
var query = "/connect/v1.4/CO.FileAttachments/";
var source = "add incident parts record";
postQueryData(query, data, source).then(function (jsonResponse) {
fileAttachId = jsonResponse.id;
uploadControl[control.id].filesUploaded[uploadGroupId] = fileAttachId;
createFileAttachmentRecord.resolve();
}).catch(function (errorMSG) {
alert(errorMSG);
createFileAttachmentRecord.resolve();
});
} else{
// add dummy file to for local debugging
uploadControl[control.id].filesUploaded[uploadGroupId] = Date.now();
}
}
function makeDeleteButton(uploadGroup, uploadGroupId, fileName, el, control){
var divWrapper = document.createElement("div");
var deleteButton = document.createElement("span");
var text = document.createTextNode(fileName);
divWrapper.setAttribute("class", "deleteWrapper");
deleteButton.setAttribute("role", "button");
deleteButton.setAttribute("title", "Delete");
deleteButton.style.cursor = "pointer";
deleteButton.innerHTML = '<svg aria-hidden="true" focusable="false" role="img" viewBox="0 0 448 512" style="width: 16px; height: 16px;"><path fill="currentColor" d="M432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16zM53.2 467a48 48 0 0 0 47.9 45h245.8a48 48 0 0 0 47.9-45L416 128H32z"></path></svg>';
deleteButton.onclick = function () {
// delete from B2B Service Object
if(!isLocal){
var query = "/connect/v1.4/CO.FileAttachments/" + uploadControl[control.id].filesUploaded[uploadGroupId];
deleteRecord(query, 'Delete File Attachment'); // remove from database
}
// remove from DOM
uploadGroup.parentNode.removeChild(uploadGroup);
// create another upload group if max count is not met
if(uploadControl[control.id].fileCount == uploadControl[control.id].maxFiles){
makeUploadGroup(el, control);
}
// decrement total file count
uploadControl[control.id].fileCount--;
delete uploadControl[control.id].filesUploaded[uploadGroupId]; // remove from JS local variable
};
divWrapper.appendChild(deleteButton);
divWrapper.appendChild(text);
return divWrapper;
}
alert(errorMSG);
createFileAttachmentRecord.resolve();
});
}
}
function makeDeleteButton(uploadGroup, category, photoGroupId, fileName){
var divWrapper = document.createElement("div");
var deleteButton = document.createElement("span");
var text = document.createTextNode(fileName);
divWrapper.setAttribute("class", "deleteWrapper");
deleteButton.setAttribute("role", "button");
deleteButton.setAttribute("title", "Delete");
deleteButton.style.cursor = "pointer";
deleteButton.innerHTML = '<svg aria-hidden="true" focusable="false" role="img" viewBox="0 0 448 512" style="width: 16px; height: 16px;"><path fill="currentColor" d="M432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16zM53.2 467a48 48 0 0 0 47.9 45h245.8a48 48 0 0 0 47.9-45L416 128H32z"></path></svg>';
deleteButton.onclick = function () {
fileCount--;
uploadGroup.parentNode.removeChild(uploadGroup); // remove from DOM
if(!isLocal){
var query = "/connect/v1.4/CO.FileAttachments/" + filesUploaded[photoGroupId];
deleteRecord(query, 'Delete File Attachment'); // remove from database
}
delete filesUploaded[photoGroupId]; // remove from JS local variable
};
divWrapper.appendChild(deleteButton);
divWrapper.appendChild(text);
return divWrapper;
}
Add aUtils.js file to your resources folder.
This allows the interview extension to interact with the B2B Service BUI Incident workspace and interact with the B2B service REST API
function getExtensionProvider() {
extensionProviderPromise = ORACLE_SERVICE_CLOUD.extension_loader.load(appName, appVersion);
return extensionProviderPromise;
}
function getGlobalContext() {
var globalContextPromise_1 = new ORACLE_SERVICE_CLOUD.ExtensionPromise();
globalContextPromise = globalContextPromise_1;
getExtensionProvider().then(function (extensionProvider) {
extensionProvider.getGlobalContext().then(function (globalContext) {
globalContextPromise_1.resolve(globalContext);
});
});
return globalContextPromise_1;
}
function getSessionToken() {
return new Promise(function (resolve, reject){
getGlobalContext().then(function (globalCtx) {
globalCtx.getSessionToken().then(function (sessionToken) {
tokenObj = {
sessionToken: sessionToken,
url: globalCtx.getInterfaceServiceUrl('REST'),
interfaceUrl: globalCtx.getInterfaceUrl(),
acctID: globalCtx.getAccountId().toString(),
login: globalCtx.login,
profile: globalCtx.profileName
};
resolve(tokenObj);
});
});
});
}
function getWorkspaceExtension() {
var workspaceExtensionPromise_1 = new ORACLE_SERVICE_CLOUD.ExtensionPromise();
workspaceExtensionPromise = workspaceExtensionPromise_1;
getExtensionProvider().then(function (extensionProvider) {
extensionProvider.registerWorkspaceExtension(function (WRecord) {
workspaceExtensionPromise_1.resolve(WRecord);
});
});
return workspaceExtensionPromise_1;
}
function getQueryResult(query, source, returnData)
{
return new Promise(function (resolve, reject) {
getSessionToken().then(function(sessionObj){
var xhr = new XMLHttpRequest();
xhr.open('GET', sessionObj.url + query);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('OSvC-CREST-Application-Context', 'BUI Add-In Utils');
xhr.setRequestHeader('Authorization', 'Session ' + sessionObj.sessionToken);
xhr.onload = function() {
if (xhr.status === 200) {
var jsonResponse = JSON.parse(xhr.responseText);
if(returnData){
jsonResponse.returnData = returnData;
}
resolve(jsonResponse);
}
else if (xhr.status !== 200) {
reject(xhr.status);
}
};
xhr.send();
});
});
}
function deleteRecord(query, source)
{
getSessionToken().then(function(sessionObj){
var xhr = new XMLHttpRequest();
xhr.open('DELETE', sessionObj.url + query);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('OSvC-CREST-Application-Context', 'BUI Add-In Utils');
xhr.setRequestHeader('Authorization', 'Session ' + sessionObj.sessionToken);
xhr.send();
});
queryResponse = new ORACLE_SERVICE_CLOUD.ExtensionPromise();
return queryResponse;
}
function postQueryData(query, data, source)
{
getSessionToken().then(function(sessionObj){
var xhr = new XMLHttpRequest();
xhr.open('POST', sessionObj.url + query);
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("OSvC-CREST-Application-Context", "SR Automation");
xhr.setRequestHeader('Authorization', 'Session ' + sessionObj.sessionToken);
xhr.onload = function() {
if (xhr.status === 200 || xhr.status === 201) {
var jsonResponse = {};
if(xhr.responseText != ""){
jsonResponse = JSON.parse(xhr.responseText);
}
queryResponse.resolve(jsonResponse);
}
else {
jsonResponseError = JSON.parse(xhr.responseText);
queryResponse.reject(xhr.status + ': ' + jsonResponseError.detail);
}
};
xhr.send(data);
});
queryResponse = new ORACLE_SERVICE_CLOUD.ExtensionPromise();
return queryResponse;
}
function patchQueryData(query, data, source)
{
getSessionToken().then(function(sessionObj){
var xhr = new XMLHttpRequest();
xhr.open('POST', sessionObj.url + query);
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("OSvC-CREST-Application-Context", "SR Automation");
xhr.setRequestHeader("X-HTTP-Method-Override", "PATCH");
xhr.setRequestHeader('Authorization', 'Session ' + sessionObj.sessionToken);
xhr.onload = function() {
if (xhr.status === 200 || xhr.status === 201) {
var jsonResponse = {};
if(xhr.responseText != ""){
jsonResponse = JSON.parse(xhr.responseText);
}
queryResponse.resolve(jsonResponse);
}
else {
jsonResponseError = JSON.parse(xhr.responseText);
queryResponse.reject(xhr.status + ': ' + jsonResponseError.detail);
}
};
xhr.send(data);
});
queryResponse = new ORACLE_SERVICE_CLOUD.ExtensionPromise();
return queryResponse;
}
Create Custom Object CO.FileAttachments to store your files.
