I have a Suitelet rendering a PDF from various record types which do not currently have Advanced PDF functionality.
Everything works fine when I file.save() the finished PDF, but I would like the option to open the PDF in the browser without saving to the file cabinet first.
Currently, the action part of the script is this:
var templateFile = config.getValue({fieldId:'custrecord_extpdf_template_xml'});
var templateHTML = file.load({id: templateFile}).getContents();
var renderer = render.create();
renderer.templateContent = templateHTML;
renderer.addRecord('record',rec);
renderer.addRecord('config',config);
if (SAVETORECORD) {
var PDF = renderer.renderAsPdf();
PDF.folder = config.getValue({fieldId: 'custrecord_extpdf_temp_folder'});
PDF.name = param.rectype + param.id + '.pdf';
var fid = PDF.save();
var attachitem = record.attach({
record: { type: 'file', id: fid },
to: { type: param.rectype, id: param.id }
})
context.response.write(file.load({id:fid}).url )
}
else {
var PDF = renderer.renderAsPdf();
PDF.name = param.rectype + param.id + '.pdf';
context.response.writeFile(PDF,false)
}
When the variable SAVETORECORD is true, the PDF renders nicely, and opens in a new tab, attaches to the record, and is saved to the file cabinet.
However, when SAVETORECORD is false, a new window opens but is BLANK.
The Suitelet is called from a custom button trigger with the following code:
var response = https.get({url: suiteletURL });
if (response.body) window.open(response.body,'_blank');
window.location.reload(true);
I've tried context.response.writeFile(PDF,false) and context.response.writeFile(PDF,true) but get the same result.
What am I missing here?
After sleeping on it, I figured what I'd done wrong.
To save the PDF, the code stays as is.
To open in the browser without saving, I just needed to open the SuiteLet URL rather than the response.body from the button script:
if (SAVERECORD) {
var response = https.get({url: suiteletURL });
if (response.body) window.open(response.body,'_blank');
}
else {
window.open(suiteletURL,'_blank');
}
window.location.reload(true);
Related
I need to change the "title" for each document shown in ICN Viewer, dynamically, at runtime. I'll read the new viewer tab title from the document properties
ENVIRONMENT: ICN 2.0.3 CM8.5 WAS 8.5.5
CODE SO FAR:
I found a PARTIAL solution by hooking "ecm.model.desktop, onChange":
aspect.after(ecm.model.desktop, 'onChange', function() {
var contentViewer = dijit.byId('contentViewer');
if (contentViewer) {
var viewerTabTitleDef = new ViewerTabTitleDef ();
contentViewer.mainTabContainer.getChildren().forEach(function(child) {
viewerTabTitleDef.changeTitle(viewerTabTitleDef.self,
child.controlButton, child.contentViewerPane.viewerItem.item);
});
...
I was able to extend this for subsequent documents opened in the same viewer, and optimized by "removing()" the handler after this initial call. Here is the complete code:
var kill = aspect.after(ecm.model.desktop, 'onChange', function() {
var contentViewer = dijit.byId('contentViewer');
// "contentViewer" will be "unknown" unless viewer invoked
console.log('onChange: contentViewer', contentViewer);
if (contentViewer) {
console.log("new ViewerTabTitleDef()...");
kill.remove();
var viewerTabTitleDef = new ViewerTabTitleDef ();
contentViewer.mainTabContainer.getChildren().forEach(function(child) {
// For initially opened tabs
console.log('initially opened: child', child);
viewerTabTitleDef.changeTitle(viewerTabTitleDef.self, child.controlButton, child.contentViewerPane.viewerItem.item);
});
aspect.after(contentViewer.mainTabContainer, 'addChild', function(child) {
// For tabs added after the viewer was opened
console.log('subsequently opened: child', child);
viewerTabTitleDef.changeTitle(viewerTabTitleDef, child.controlButton, child.contentViewerPane.viewerItem.item);
}, true);
} // end if contentViewer
}); // end aspect.after(onChange desktop)
CURRENT PROBLEM:
Q: How can I change the label for a split tab (either vertical or horizontal)?
So far, I have NOT been able to find any event for any ICN/ECM widget or object variable that I can trigger on.
Thank you in advance!
===============================================
ADDENDUM:
Many thanks to Ivo Jonker, for his suggestion to modify the widget prototype's
"getHtmlName()" method. It worked!
Specifically:
I'm invoking this code from an ICN plugin. I set event handlers in my plugin's base .js file, but it actually gets invoked in the new, separate viewer window.
The original prototype looked like this:
getHtmlName: function() {
var methodName = "getHtmlName";
this.logEntry(methodName);
var displayName = this.item.getDisplayValue("{NAME}");
if (displayName == "") {
displayName = this.item.name;
}
var htmlName = entities.encode(displayName);
this.logExit(methodName);
return htmlName;
},
Per Ivo's suggestion, I overrode the prototype method like this:
myPluginDojo.viewerTabTitleDef = viewerTabTitleDef;
...
ecm.widget.viewer.model.ViewerItem.prototype.getHtmlName = function () {
console.log("NEW getHtmlName()...");
var displayName = myPluginDojo.viewerTabTitleDef.getTitle(this.item);
return displayName;
};
If i understand you correctly, you want to show a different tab-title (instead of the document title) in the navigator viewer whenever a doc is opened?
How about this:
Every document you open in the viewer is wrapped in a ecm.widget.viewer.model.ViewerItem which exposes the getHtmlName that returns the name used in the tab.
Your solution would be to implement your own getHtmlName.
Unfortunately though, the ViewerItem is constructed in the ecm.widget.viewer.ContentViewer#_open and then passed to the ecm.widget.viewer.ContentViewer#_openTab. So you'll either violate best practice by mingling with IBM private method's, or you'll go for a generic approach and just replace the ecm.widget.viewer.model.ViewerItem.prototype.getHtmlName
I'm trying to figure out how to make a Google Apps Script deployed as a web app download a PDF that's generated on a click. It almost works, but the resulting file isn't valid. I can't figure out if it's an encoding issue or something else.
In Apps Script the code looks simple:
function makePDF() {
...
var pdfBlob = doc.getAs('application/pdf');
return Utilities.base64Encode(pdfBlob.getBytes());
}
In the browser, there's a click handler:
function clickHandler(ev) {
ev.preventDefault();
google.script.run
.withSuccessHandler(function(data) {
var pdf = new Blob([window.atob(data)]);
var href = window.URL.createObjectURL(pdf);
var link = document.querySelector('#hiddenLink');
link.href = href;
link.click();
})
.makePDF();
}
Any suggestions?
Thanks!
I figured it out, so posting the answer if anyone else is trying to pass a PDF from Apps Script to the client javascript. It's all much simpler than I had made it.
Rather than messing around with base64 encodings, just pass back the bytes array:
function makePDF() {
...
var pdfBlob = DocumentApp.openById('1234').getAs('application/pdf');
return pdfBlob.getBytes();
}
Now, on the client side, construct a new Blob from an ArrayBuffer. That's easy too:
function clickHandler(ev) {
google.script.run
.withSuccessHandler(function(data) {
var arr = new Uint8Array(data);
var blob = new Blob([arr.buffer], {type: 'application/pdf'});
var obj_url = window.URL.createObjectURL(blob);
var hiddenLink = document.getElementById('hiddenPDFLink');
hiddenLink.setAttribute('href', obj_url);
hiddenLink.setAttribute('download', 'filename.pdf');
hiddenLink.click();
})
.makePDF();
}
And that's it! Hope someone else finds this helpful.
I assume that your makePDF function is doing some other stuffs/Calculation and at the end you need that document to be downloaded to local computer.
What you can do is inside success handler
var link = document.querySelector('#hiddenLink');
link.href = "https://docs.google.com/feeds/download/documents/export/Export?id=**TheIdOfDocumenToBeDownloaded**&exportFormat=pdf";
link.click();
It will then give you a prompt to save document on to local computer.
In my view, I generate a link with a username, acct number and date that is passed to the controller:
#Html.ActionLink(AccountGroup.AcctNum + " " + AccountGroup.DocDate.ToShortDateString(), "GetIndividualStatement", "Statements", new { statementDate = #AccountGroup.DocDate.ToShortDateString(), Userid = #ViewBag.UserID, acctNumber = AccountGroup.AcctNum.ToString() }, new { #class = "form-control, col-sm-6, medwidth" , target = "_blank" })
I have tried to use an AJAX link also:
#Ajax.ActionLink(AccountGroup.AcctNum + " " + AccountGroup.DocDate.ToShortDateString(), "GetIndividualStatement", "Statements", new { statementDate = #AccountGroup.DocDate.ToShortDateString(), Userid = #ViewBag.UserID, acctNumber = AccountGroup.AcctNum.ToString() }, new AjaxOptions { HttpMethod ="POST", UpdateTargetId = "detailsDiv" }, new { #class = "form-control, col-sm-6, medwidth" })
Then my controller takes that info generates an encrypted URL for the PDF (I have to create this on the spot in my controller, so I cannot just have a link in my view for the user to click (that would be too easy!). I need to open the URL which returns a PDF. I need to take that and open that PDF in a new tab (while keeping the original tab open). Here is my latest attempt, which opens in the same window, but how do I open in a new tab ?
WebClient client = new WebClient();
Byte[] buffer = client.DownloadData(path);
if (buffer != null)
{
Response.ContentType = "application/pdf";
Response.AddHeader("content-length", buffer.Length.ToString());
Response.BinaryWrite(buffer);
}
Thanks to all!
What you are looking for in the final HTML that you produce is something of the form below, where the link has the target attribute set to '_blank',
Open PDF
Get that right and you will get the behaviour you want.
If the issue is in the server-side code then when you solve it, edit your question to include the fix just so anyone else looking in from the future can see both sides of the story.
I'm writing a protractor script that need to upload a JPEG image. I could click on the upload button which opens up a windows file selector. But, then I need to write the path to a file in that File Selector dialog using protractor.
But, i have no idea how it works. I tried just typing the path using sendKeys and it doesn't work so far.
Anyone have an idea how to do this?
Thanks. :)
Try my answer in "How can I control the windows File Selector using protractor".
If you need a quick solution try the following solution:
// set file detector
var remote = require('../../node_modules/protractor/node_modules/selenium-webdriver/remote');
browser.setFileDetector(new remote.FileDetector());
var fileToUpload = '../sample.txt';
var absolutePath = path.resolve(__dirname, fileToUpload);
var fileElem = element(by.css('input[type="file"]'));
// Unhide file input
browser.executeScript("arguments[0].style.visibility = 'visible'; arguments[0].style.height = '1px'; arguments[0].style.width = '1px'; arguments[0].style.opacity = 1", fileElem.getWebElement());
fileElem.sendKeys(absolutePath);
// take a breath
browser.driver.sleep(100);
// click upload button
element(by.css('button[data-ng-click="uploadFile(file)"]')).click(); // does post request
[settingsEditProfile_page.settingsEditProfile_UploadImageButton()][1].isDisplayed().then(function () {
helperUtil.addStep("User redirected to Edit Profile page"); settingsEditProfile_page.settingsEditProfile_UploadImageButton().sendKeys(absolutePath).then(function () {
helperUtil.addStep("User clicked on upload button and uploaded new image");
browser.driver.sleep(3000);
settingsEditProfile_page.settingsEditProfile_Save().click().then(function () {
helperUtil.addStep("User clicked on SAVE button");
});
});
});
uploadFile: async (locator, filepath) => {
absolutePath = path.resolve(filepath);
click(locator);
element(by.css('input[type="file"]')).sendKeys(absolutePath);
await sleep(10000, "wait to close window");
await closePopup();
},
I am currently trying to resolve the following issue:
var fileName = "monthly_report.pdf"
var document = new Document();
//DO SOME STUFF WITH THE DOCUMENT
MemoryStream stream = new MemoryStream();
doc.Save(stream, SaveFormat.Pdf);
byte[] bytes = stream.GetBuffer();
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename="+fileName);
Response.BinaryWrite(bytes);
Response.End();
Based on this code I am trying to display an Aspose. Words document converted into an pdf in the browser / trying to create a download dialog in the browser for said document.
When I execute the action there is no error message. The contents of the pdf are then displayed within the response message of the chrome debugger. The response also holds the appropriate size ( 60kb for the pdf ). It simply does not start a download or displays the pdf in the browser and I wonder why that would be.
I also tried an alternative provided by Aspose:
var resp = System.Web.HttpContext.Current.Response;
resp.Clear();
// Create Memory Stream Object
MemoryStream stream = new MemoryStream();
doc.Save(stream, SaveFormat.Pdf);
doc.Save(resp, fileName, ContentDisposition.Attachment, SaveOptions.CreateSaveOptions(
SaveFormat.Pdf));
resp.End();
Which leads to the exact same result the pdf being displayed in the response not the browser itself.
The controller action executing this code is called by the ajax statement:
$("#btnReport").click(function () {
var datum = $("#hiddenDatum").val();
$.ajax({
type: "GET",
url: '#Url.Action("GenerateMonthlyReport", "Reporting")',
data: { datum: datum},
success: function (data) {
}
});
});
Any suggestions to what I am doing wrong would be highly appreciated.
Edit: My research indicates that the ajax call indeed does not work. How can i initiate the file download based on my controller logic?
You can't download a file directly using AJAX calls. You can do this though:
$.ajax({
type: "GET",
url: '#Url.Action("GenerateMonthlyReport", "Reporting")',
data: { datum: datum},
success: function (data) {
window.location.href = data.filePath; // i.e. '/downloads/file.pdf';
}
});
You cannot stream data back to the jQuery handler. Why not just make this a simple get request with a standard HTML anchor?
#Html.ActionLink("Generate Monthly Report", "GenerateMonthlyReport", "Reporting", new { datum = DateTime.Now.ToString("d", new CultureInfo("en-US")})