How to send a file using glideajax? - javascript

I've been trying to get this to work for a while now. I have a UI action that includes a UI page through a GlideDialog. The UI page is just a form with a bunch of input (text type) and one file type. On click of submit button I am sending the form data as well as the file attachment via glideAjax,
var issueObj = {};
var ga = new GlideAjax(glideAjax);
var name = $j_jb('#name').val();
var address = $j_jb('#address').val();
var file = $j_jb('#jira_attachment')[0].files[0];
issueObj.name = name;
issueObj.address = address;
var IssueObjString = JSON.stringify(issueObj);
ga.addParam('sysparm_name','createIssue');
ga.addParam('sysparm_issueObj', IssueObjString);
ga.addParam('sysparm_attachment', file);
var that = this;
ga.getXML(function (response) {
var responseStatus = response.responseXML.documentElement.getAttribute("answer");
var DOMData = "";
if(responseStatus) {
that.displayMessage(jiraAlert['success-insertion']);
}
else {
that.displayMessage(jiraAlert['error-insertion']);
}
});
I have the corresponding script include method it calls here,
createIssue: function() {
var issueObj = this.getParameter("sysparm_issueObj");
var fileAttachment = this.getParameter("sysparm_attachment");
issueObj = JSON.parse(issueObj);
var fileName = issueObj.fileAttachment.name;
var fileType = issueObj.fileAttachment.type;*/
var gr = new GlideRecord('sample_table');
gr.newRecord();
gr.name = issueObj.name;
gr.address = issueObj.address;
insertRef = gr.insert();
var ga = new GlideSysAttachment();
ga.write(gr, fileAttachment.name, fileAttachment.type, fileAttachment);
}
The record gets generated by the attachment is corrupt,
I've hit a wall here, and don't know how to proceed further. Any help with this regard is highly appreciated!
Thanks,
Raskill

We cant send files directly, like how we can able to do it on php etc.
On ServiceNow you can use OOB widget Ticket Attachments widget-ticket-attachments
The alternative is you need to convert the file to base 64 and send that data to the server script and use.
Here is the code that I used in one place:
<label class="file-upload btn btn-primary">
${Browse for file} ...
<input type="file" id="fileToUpload" onchange="angular.element(this).scope().setFiles(this)"/>
</label>
Client:
$scope.setFiles = function(element) {
$scope.resumefiles = [];
$scope.$apply(function() {
// Turn the FileList object into an Array
for (var i = 0; i < element.files.length; i++) {
$scope.resumefiles.push(element.files[i]);
}
$scope.uploadResume($scope.resumefiles);
});
};
$scope.uploadResume = function(resumefiles){
var reader = new FileReader(), base64String = "";
reader.onloadend = function () {
base64String = reader.result.substr(reader.result.indexOf("base64,"),
reader.result.length-1).replace("base64,","");
$scope.data.fileData = base64String;
$scope.data.fileName = resumefiles[0].name;
$scope.data.fileType = resumefiles[0].type;
$scope.data.funcName = 'updateApplicantResume';
c.server.update().then(function(){
$scope.data.funcName = '';
$scope.resumefiles = [];
})
}
reader.readAsDataURL(resumefiles[0]);
}
Server script:
uploadAttachment: function(input) {
var attachment_sys_id = '';
var attachment = new GlideSysAttachment();
var gr = new GlideRecordSecure(input.table);
if (gr.get(input.applicantSysId)) {
attachment_sys_id = attachment.writeBase64(gr, input.fileName, input.fileType, input.fileData);
}
return attachment_sys_id;
},

Related

Downloading files in Blazor Webassembly when using navigation manager and needing to check user claims

I have a Blazor WASM solution in which I am trying to build functionality for downloading data from a (Radzen) datagrid. I start off by calling an export service from the code of my component like so:
await _exportService.Export("expense-claims-admin", type, new Query()
{
OrderBy = grid.Query.OrderBy,
Filter = grid.Query.Filter,
Select = "CreateDate,User.FirstName,User.LastName,Status,ExpenseRecords.Count AS NumberOfExpenseRecords,MileageRecords.Count AS NumberOfMileageRecords,TotalAmount"
});
My export service then runs the following code:
public async Task Export(string table, string type, Query query = null)
{
_navigationManager.NavigateTo(query != null ? query.ToUrl($"/export/{table}/{type}") : $"/export/{table}/{type}", true);
}
This calls a controller in my server project, which runs a different action depending on the request URL. For example:
[HttpGet("/export/expense-claims-admin/excel")]
public IActionResult ExportExpenseClaimsToExcelAdmin()
{
var claimsPrincipal = User;
var companyId = claimsPrincipal.FindFirst(c => c.Type == "companyId");
if (companyId != null)
{
return ToExcel(ApplyQuery(context.expense_claims.Where(x => x.DeleteDate == null && x.CompanyId == int.Parse(companyId.Value)), Request.Query));
}
//TODO - Return something other than null
return null!;
}
This then calls one of two methods depending on whether I'm trying to export to Excel or CSV. Below is the ToExcel method that gets called above:
public FileStreamResult ToExcel(IQueryable query, string fileName = null)
{
var columns = GetProperties(query.ElementType);
var stream = new MemoryStream();
using (var document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook))
{
var workbookPart = document.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();
var workbookStylesPart = workbookPart.AddNewPart<WorkbookStylesPart>();
GenerateWorkbookStylesPartContent(workbookStylesPart);
var sheets = workbookPart.Workbook.AppendChild(new Sheets());
var sheet = new Sheet() { Id = workbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Sheet1" };
sheets.Append(sheet);
workbookPart.Workbook.Save();
var sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());
var headerRow = new Row();
foreach (var column in columns)
{
headerRow.Append(new Cell()
{
CellValue = new CellValue(column.Key),
DataType = new EnumValue<CellValues>(CellValues.String)
});
}
sheetData.AppendChild(headerRow);
foreach (var item in query)
{
var row = new Row();
foreach (var column in columns)
{
var value = GetValue(item, column.Key);
var stringValue = $"{value}".Trim();
var cell = new Cell();
var underlyingType = column.Value.IsGenericType &&
column.Value.GetGenericTypeDefinition() == typeof(Nullable<>) ?
Nullable.GetUnderlyingType(column.Value) : column.Value;
var typeCode = Type.GetTypeCode(underlyingType);
if (typeCode == TypeCode.DateTime)
{
if (!string.IsNullOrWhiteSpace(stringValue))
{
cell.CellValue = new CellValue() { Text = ((DateTime)value).ToOADate().ToString(System.Globalization.CultureInfo.InvariantCulture) };
cell.DataType = new EnumValue<CellValues>(CellValues.Number);
cell.StyleIndex = (UInt32Value)1U;
}
}
else if (typeCode == TypeCode.Boolean)
{
cell.CellValue = new CellValue(stringValue.ToLower());
cell.DataType = new EnumValue<CellValues>(CellValues.Boolean);
}
else if (IsNumeric(typeCode))
{
if (value != null)
{
stringValue = Convert.ToString(value, CultureInfo.InvariantCulture);
}
cell.CellValue = new CellValue(stringValue);
cell.DataType = new EnumValue<CellValues>(CellValues.Number);
}
else
{
cell.CellValue = new CellValue(stringValue);
cell.DataType = new EnumValue<CellValues>(CellValues.String);
}
row.Append(cell);
}
sheetData.AppendChild(row);
}
workbookPart.Workbook.Save();
}
if (stream?.Length > 0)
{
stream.Seek(0, SeekOrigin.Begin);
}
var result = new FileStreamResult(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
result.FileDownloadName = (!string.IsNullOrEmpty(fileName) ? fileName : "Export") + ".xlsx";
return result;
}
The issue I'm hitting is that the claims against the User object are never populated with the information that I need to do the checks in my export controller. I know this is because I'm calling the controller with navigation manager rather than going through a httpclient request so no authorisation token is passed, but if I use httpclient instead, the file is not downloaded by the browser.
I have tried using a combination of httpclient and javascript to get the file to download (code below) but this is returning a text file rather than an xlsx or csv file and the content is just gibberish (to me anyway).
Here's the code in my service if I use httpclient instead of navigation manager:
public async Task Export(string table, string type, Query query = null)
{
var response = await _client.GetAsync(query != null ? query.ToUrl($"/export/{table}/{type}") : $"/export/{table}/{type}");
var fileStream = await response.Content.ReadAsStreamAsync();
using var streamRef = new DotNetStreamReference(stream: fileStream);
await _js.InvokeVoidAsync("downloadFileFromStream", "Export", streamRef);
}
and here's the javascript code I'm using to download the file:
async function downloadFileFromStream(fileName, contentStreamReference) {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
triggerFileDownload(fileName, url);
URL.revokeObjectURL(url);
}
function triggerFileDownload(fileName, url) {
const anchorElement = document.createElement("a");
anchorElement.href = url;
if (fileName) {
anchorElement.download = fileName;
}
anchorElement.click();
anchorElement.remove();
}
I've read that using jsinterop to do file downloads is slow and that you're also limited by file size, so it seems like the best way to get this working would be to call the controller with navigation manager, but I just can't work out how to get those user claims if I do that and I really need to check those to make sure I'm returning the right data.

Session.Create(FlowFile) Transfers with no content

I am trying to build a Excecute Script Processor for Nifi.
It handles a JSON file, splits it and sends it to the next processor, which is an MongoDB writer.
The logic works so far. The main problem is, that I cannot get the processor to create and send a new FlowFile for each new JSON created out of the input JSON. I got it working a bit but unfortunately, all the FlowFiles come out empty. Is there something wrong with the flow (from creation of a new Flow to sending it)?
Here is a code snippet:
var flowFile = session.get();
if (flowFile != null) {
var StreamCallback = Java.type("org.apache.nifi.processor.io.StreamCallback");
var IOUtils = Java.type("org.apache.commons.io.IOUtils");
var StandardCharsets = Java.type("java.nio.charset.StandardCharsets");
try {
flowFile = session.write(flowFile,
new StreamCallback(function (inputStream, outputStream) {
var content = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
var json = JSON.parse(content);
var events = json["events"];
var mongoEvent = "";
var flowFileList = [];
for(var x = 0; x < json["events"].length; x++){
try{
var newFlowFile = session.create();
mongoEvent = constructJSONEvent(x, json); // Here we will receive our new JSON
outputStream.write(mongoEvent.getBytes(StandardCharsets.UTF_8));
session.transfer(newFlowFile, REL_SUCCESS);
}catch(e){
session.transfer(newFlowFile, REL_FAILURE);
}
}
}));
session.transfer(flowFile, REL_SUCCESS);
} catch(e) {
session.transfer(flowFile, REL_FAILURE);
}
}

Google Drive + Script throws permissions error even through I'm owner and granted permission

I'm trying to create a basic script on a 12-hour timer trigger that loops through each of my Google Calendars by their ICAL URL, and downloads the ICAL for a folder on my Google Drive (for backup purposes). It throws this error
"No item with the given ID could be found, or you do not have permission to access it. (line 23, file "Code")" (Line #23 is var folder... )
Running the script does download and save the ICAL file on the first run through the loop (and if I manually pass in each unique ICAL URL one at a time), but the error then terminates the loop. Seeing as how I've authorized access already and am the owner of everything here, I'm not sure what else I need.
var calendarsToSave = [
"https://calendar.google.com/calendar/ical/inXXXXXXX.com/privateXXXX/basic.ics",
"https://calendar.google.com/calendar/ical/XXXXX.com_XXXXXXup.calendar.google.com/private-XXXXXXX/basic.ics"
];
var folder = '123xxxxxxxxv789'; // my gdrive folder
function downloadFile(calendarURL,folder) {
var fileName = "";
var fileSize = 0;
for (var i = 0; i < calendarsToSave.length; i++) {
var calendarURL = calendarsToSave[i];
var response = UrlFetchApp.fetch(calendarURL, {muteHttpExceptions: true});
var rc = response.getResponseCode();
if (rc == 200) {
var fileBlob = response.getBlob()
var folder = DriveApp.getFolderById(folder); // << returns a permissions error thus terminating the for loop
var file = folder.createFile(fileBlob);
fileName = file.getName();
fileSize = file.getSize();
}
var fileInfo = { "rc":rc, "fileName":fileName, "fileSize":fileSize };
return fileInfo;
} // end for loop
}
Updated: You are also re-initializing a variable that already exists from the parameters and as a global variable so we can remove the parameter if you want to keep the global variable.
We can also move the place where you get the Google Folder object. It stays the same every time so we don't need to retrieve it again.
var calendarsToSave = [
"https://calendar.google.com/calendar/ical/inXXXXXXX.com/privateXXXX/basic.ics",
"https://calendar.google.com/calendar/ical/XXXXX.com_XXXXXXup.calendar.google.com/private-XXXXXXX/basic.ics"
];
var folder = '123xxxxxxxxv789'; // my gdrive folder
function downloadFile(calendarURL) {
var fileName = "";
var fileSize = 0;
var gfolder = DriveApp.getFolderById(folder);
for (var i = 0; i < calendarsToSave.length; i++) {
var calendarURL = calendarsToSave[i];
var response = UrlFetchApp.fetch(calendarURL, {muteHttpExceptions: true});
var rc = response.getResponseCode();
if (rc == 200) {
var fileBlob = response.getBlob()
var file = gfolder.createFile(fileBlob);
fileName = file.getName();
fileSize = file.getSize();
}
var fileInfo = { "rc":rc, "fileName":fileName, "fileSize":fileSize };
return fileInfo;
} // end for loop
}
Let see where that gets us.
Your "folder" variable is outside the function, causing the data to be inaccessible to the "downloadFile" function.
Google apps coding seems to require variables to be in a function to be defined. I would recommend moving both "calendarsToSave" and "folder" to the inside of "downloadFile"
Here is an example that will return your error:
var folder = '1HSFBPfPIsXWvFEb_AalFYalkPwrOAyxD';
function myFunction() {
var folder = DriveApp.getFolderById(folder);
var name = folder.getName();
Logger.log(name);
}
And here is one that will return the file name:
function myFunction() {
var folder = '1HSFBPfPIsXWvFEb_AalFYalkPwrOAyxD';
var folder = DriveApp.getFolderById(folder);
var name = folder.getName();
Logger.log(name);
}

Get String Value of Blob Passed to e.parameter in Apps Script

I'm using this code to get a blob passed to a function:
function submit(e){
var arrayBlob = e.parameter.arrayBlob;
Logger.log("arrayBlob #2 = " + arrayBlob.getDataAsString());
This is the error I get:
Execution failed: TypeError: Can not find getDataAsString function in
the Blob object.'arrayBlob'
How do I get the string value of this blob?
Here is my code:
function showList(folderID) {
var folder = DocsList.getFolderById(folderID);
var files = folder.getFiles();
var arrayList = [];
for (var file in files) {
file = files[file];
var thesesName = file.getName();
var thesesId = file.getId();
var thesesDoc = DocumentApp.openById(thesesId);
for (var child = 0; child < thesesDoc.getNumChildren(); child++){
var thesesFirstParagraph = thesesDoc.getChild(child);
var thesesType = thesesFirstParagraph.getText();
if (thesesType != ''){
var newArray = [thesesName, thesesType, thesesId];
arrayList.push(newArray);
break;
}
}
}
arrayList.sort();
var result = userProperties.getProperty('savedArray');
arrayList = JSON.stringify(arrayList);
var arrayBlob = Utilities.newBlob(arrayList);
Logger.log("arrayBlob #1 = " + arrayBlob.getDataAsString()); // Here it`s OK
var mydoc = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication().setWidth(550).setHeight(450);
var panel = app.createVerticalPanel()
.setId('panel');
panel.add(app.createHidden('arrayBlob', arrayBlob));
var label = app.createLabel("Selecione os itens desejados").setStyleAttribute("fontSize", 18);
app.add(label);
arrayList = JSON.parse(arrayList);
panel.add(app.createHidden('checkbox_total', arrayList.length));
for(var i = 0; i < arrayList.length; i++){
var checkbox = app.createCheckBox().setName('checkbox_isChecked_'+i).setText(arrayList[i][0]);
Logger.log("arrayList[i][0] = " + arrayList[i][0]);
Logger.log("arrayList[i] ====> " + arrayList[i]);
panel.add(checkbox);
}
var handler = app.createServerHandler('submit').addCallbackElement(panel);
panel.add(app.createButton('Submit', handler));
var scroll = app.createScrollPanel().setPixelSize(500, 400);
scroll.add(panel);
app.add(scroll);
mydoc.show(app);
}
function submit(e){
var arrayBlob = e.parameter.arrayBlob;
Logger.log("arrayBlob #2 = " + arrayBlob.getDataAsString());
// Continues...
}
I'd like the solution worked with more than one user simultaneous using the script.
Update:
Add a global variable OUTSIDE of any function:
var arrayBlob = Utilities.newBlob("dummy data");
function showList(folderID) {
Code here ....
};
Check that the code has access to the blob:
function submit(e){
Logger.log("arrayBlob.getDataAsString(): " + arrayBlob.getDataAsString());
//More Code . . .
}
This solution eliminates the need of embedding a hidden element in the dialog box with a value of the blob.
You won't need this line:
panel.add(app.createHidden('arrayBlob', arrayBlob));
There are other changes I'd make to the code, but I simply want to show the main issue.
Old Info:
In the function showList(), the method getDataAsString() works on the blob named arrayBlob.
Logger.log("arrayBlob #1 = " + arrayBlob.getDataAsString()); // Here it`s OK
In the function, submit(), the same method does not work.
var arrayBlob = e.parameter.arrayBlob;
In the function showList(), the code is assigning a newBlob to the variable arrayBlob. So arrayBlob is available to have the getDataAsString() method used on it.
var arrayBlob = Utilities.newBlob(arrayList);
In the function, submit(), you are trying to pass the arrayBlob blob variable into the submit() function, and reference it with e.parameter.
If you put a Logger.log() statement in the submit() function.
function submit(e){
Logger.log('e: ' + e);
Logger.log('e.parameter` + e.parameter);
var arrayBlob = e.parameter.arrayBlob;
Those Logger.log statements should show something in them. If there is nothing in e.parameter, then there is nothing for the .getDataAsString() to work on.
It looks like you are putting the arrayBlob into a hidden panel.
panel.add(app.createHidden('arrayBlob', arrayBlob));
But when the object is getting passed to the submit(e) function, the arrayBlob might not be getting put into that object.
So, what I'm saying is, the:
Logger.log("arrayBlob #2 = " + arrayBlob.getDataAsString());
Line may be perfectly good, but there is no arrayBlob there to work on. This hasn't fixed your problem, but do you think I'm understanding part of what is going on?
I'm not sure why you are using Blob's here at all, you could simply work with JSON instead.
However, if you have a reason to use Blobs, you can pass the JSON data through your form and create the Blob in your handler, as the modified code below does:
function showList(folderID) {
var folder = DocsList.getFolderById(folderID);
var files = folder.getFiles();
var arrayList = [];
for (var file in files) {
file = files[file];
var thesesName = file.getName();
var thesesId = file.getId();
var thesesDoc = DocumentApp.openById(thesesId);
for (var child = 0; child < thesesDoc.getNumChildren(); child++){
var thesesFirstParagraph = thesesDoc.getChild(child);
var thesesType = thesesFirstParagraph.getText();
if (thesesType != ''){
var newArray = [thesesName, thesesType, thesesId];
arrayList.push(newArray);
break;
}
}
}
arrayList.sort();
var result = UserProperties.getProperty('savedArray');
//get JSON data pass through form.
var arrayBlob = JSON.stringify(arrayList);
var mydoc = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication().setWidth(550).setHeight(450);
var panel = app.createVerticalPanel()
.setId('panel');
//include JSON Data in the form.
panel.add(app.createHidden('arrayBlob', arrayBlob));
var label = app.createLabel("Selecione os itens desejados").setStyleAttribute("fontSize", 18);
app.add(label);
panel.add(app.createHidden('checkbox_total', arrayList.length));
for(var i = 0; i < arrayList.length; i++){
var checkbox = app.createCheckBox().setName('checkbox_isChecked_'+i).setText(arrayList[i][0]);
Logger.log("arrayList[i][0] = " + arrayList[i][0]);
Logger.log("arrayList[i] ====> " + arrayList[i]);
panel.add(checkbox);
}
var handler = app.createServerHandler('submit').addCallbackElement(panel);
panel.add(app.createButton('Submit', handler));
var scroll = app.createScrollPanel().setPixelSize(500, 400);
scroll.add(panel);
app.add(scroll);
mydoc.show(app);
}
function submit(e){
var arrayBlob = Utilities.newBlob(e.parameter.arrayBlob);
Logger.log("arrayBlob #2 = " + arrayBlob.getDataAsString());
// Continues...
}
In the method you were using originally, the Blob itself was never included in the form, you were simply passing the string "Blob" around.
This is because the function createHidden(name, value); expects two strings as parameters, so it calls ".toString()" on the arrayBlob object, which returns the string "Blob".

JSON get data (swf) to HTML5

So my swf file request an url request (pr2hub.com/get_player_info_2.php?name=NAME)
And my swf use this datas to change movieclip's frames, colors.
I converted it to HTML5, but It's not work.
Original SWF is work:
http://tulyita.hu/games/pr2setspreview.html?users=sothal
HTML5 not work:
http://tulyita.hu/games/pr2setspreview.swf.html?users=sothal
The errors:
The ActionScript class JSON is not supported.
The ActionScript method JSON.parse() is not supported.
Can someone help this with me? AS3 Flash Code:
var loadedDataType:String;
var allowedToLoad:Boolean = false;
var pr2loaderInfo:String;
var pr2data:Object;
function loadPR2data():void
{
var urlRequest:URLRequest = new URLRequest("http://pr2hub.com/get_player_info_2.php?name=" + userName);
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, completeHandler);
urlLoader.load(urlRequest);
}
function completeHandler(event:Event):void
{
var loader:URLLoader = URLLoader(event.target);
pr2loaderInfo = loader.data;
pr2data = JSON.parse(loader.data);
loadRequest_function();
}
function loadRequest_function():void
{
loadHatFrame = pr2data.hat;
loadHatColor1 = pr2data.hatColor;
loadHatColor2 = pr2data.hatColor2;
loadHeadFrame = pr2data.head;
loadHeadColor1 = pr2data.headColor;
loadHeadColor2 = pr2data.headColor2;
loadBodyFrame = pr2data.body;
loadBodyColor1 = pr2data.bodyColor;
loadBodyColor2 = pr2data.bodyColor2;
loadFeetFrame = pr2data.feet;
loadFeetColor1 = pr2data.feetColor;
loadFeetColor2 = pr2data.feetColor2;
}
You need to import JSON first (add corelib.swc if not yet part of the project):
import com.adobe.serialization.json.JSON;
and then :
pr2data = JSON.decode( value );
You can find corelib here : https://github.com/mikechambers/as3corelib#readme

Categories

Resources