Let say I have a database with several products. I'm making a page to create printable export of that database. On the page the user can setup the export depending on his needs. For example, choosing what categories of products he want to exclude from it, how he want it to be sorted and so on.
He then click on a button making an ajax call with the chosen settings to a php script making the sql request and everything.
From there I would like to generate a temporary html page to visualize the result and open it in a new tab to be printed. But once it is done, I don't need it anymore. I could write the html file from my php script and make my ajax call to open the new file on another tab but that would create a new file everytime and take a lot of space.
What is the best way to achieve that ? The main problem is that my php script result in a very long string that can't be send in the url. tmpfile() don't allow me to open the created file in a new tab as it is deleted at the end of the script.
I tried using tempnam() like this :
$tmpfname = tempnam("../tmp/",$_SESSION['loggedUser']);
$handle = fopen($tmpfname, "w");
fwrite($handle, "long string being my html code");
fclose($handle);
$success = true;
$return_arr = array($success,$tmpfname);
And my ajax call :
$.post('../php/export_script.php',{:settings},function(data){
response = JSON.parse(data);
if (response[0] == true) {
var win = window.open('../landing.php?file='+response[1], '_blank');
if (win) {
win.focus();
} else {
alert("Browser blocked the opening of the file.");
}
}else{
alert(response[1]);
}
});
So that open the landing.php page that is like so :
<div id="content">
<?php
$handle = $_GET['handle'];
$fileData = #file_get_contents($handle);
echo $fileData
?>
</div>
But that open the new tab with the clear path of the file in the url like this
"https://mywebsite.com/landing.php?file=/home/myserver/myproject/tmp/userV4rIkE"
That looks unsafe to me, showing some clear server infos to the user. Is there a better way to achieve this ? I though about just creating a normal file in my tmp folder generating a random name with timestamp + user session code and setting up a way for my server to empty that folder every once in a while.
In the parent page, you can use javascript to create a new page, which gives you a way to write data into that new page - so just write the HTML from the parent into the new page. It does not need any URL at all (about:blank) should work fine.
const handle = window.open();
if (handle) {
// write HTML to child.
}
Related
I am working in Spring 3, Java, JSP, javascript, and jquery, using Ajax occasionally. I have server functions that generate a PDF; I have a new requirement to show a "preview" of a PDF document.
I have code that generates the PDF document, that works fine. I can show it by hacking my source to display it in the place we normally show the un-watermarked completed document, so I know the generation of the PDF is working.
What I now want to do is display that PDF in its own tab as the result of clicking on a button (or link) on our web page. There are a few restrictions:
I have a bunch of data to pass up to the controller from the web page, data that it needs to generate the PDF. We have code that does this through a POST method, and use Ajax to post the necessary data.
It would be inconvenient for the PDF to show up in the same window as the button clicked to show the PDF; a popup asking if the user wants to download or view elsewhere is fine. The users aren't sophisticated enough to depend on theiri knowledge of the 'back' button here. So we want the PDF to show up elsewhere, preferably on another tab in the window but another entire window would be ok.
I have the following in my controller at the moment:
response.setContentLength(pdfGenerated.length);
response.setContentType("application/pdf");
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setHeader("Content-Disposition", "attachment;filename=\"Preview.pdf\"");
ServletOutputStream out = response.getOutputStream();
out.write(pdfGenerated); // (encodedPdf);
out.flush();
out.close();
The ajax call looks like this:
$("#generatePDFPreview").live("click", function() {
var gridData = getCorrectedGridData();
var valid = validateContractContent(gridData);
if (valid) {
// the call below saves the contract data and then generates its PDF
$.ajax({
url: getModelObject("generatePDFPreviewURL")
,type:'POST'
,data: {'editedContents':JSON.stringify(gridData)}
,datatype: "application/pdf"
,async: false
,success: function(data) {
if(data != null && data.length>0 && data != "Error") {
//data must be contract id...use it to build the complete URL.
//window.location.href = getModelObject("deliveryScreenURL") + data;
window.open("data:application/pdf;base64, " + data);
} else {
alert("PDF preview not generated...Data returned is not ok. Please try again or contact Sales Support.");
}
}
});
}
});
I have tried different things here; I have left off the 'success' function entirely; I have tried encoding the data (base64) and returning that, and using data:application/pdf, etc., but that failed -- I have some evidence that the PDF data was too long for this, but am not sure (it was 85k-90k, the URL string stopped at something like 32784).
I am not worried about whether my user has the PDF reader installed. They must have it installed to use this and other parts of the application.
It is frustrating to be so close; all evidence is that we have most of the pieces in place, it's should just be a matter of telling the browser that we want it to use the PDF Reader to handle these bytes.
Can someone point us to a method, or point out what's wrong with what we've got now?
I'm developing application using AngularJS. Everything seems to be nice until I meet something that leads me to headache: SEO.
From many references, I found out that AJAX content crawled and indexed by Google bot or Bing bot 'is not that easy' since the crawlers don't render Javascript.
Currently I need a solution using PHP. I use PHP Slim Framework so my main file is index.php which contains function to echo the content of my index.html. My question is:
Is it possible to make a snapshot of rendered Javascript in HTML?
My strategy is:
If the request query string contains _escaped_fragment_, the application will generate a snapshot and give that snapshot as response instead of the exact file.
Any help would be appreciated. Thanks.
After plenty of times searching and researching, I finally managed to solve my problem by mixing PHP with PhantomJS (version 2.0). I use exec() function in PHP to run phantomJS and create Javascript file to take get the content of the targeted URL. Here are the snippets:
index.php
// Let's assume that you have a bin folder under your root folder directory which contains phantomjs.exe and content.js
$script = __DIR__ ."/bin/content.js";
$target = "http://www.kincir.com"; // target URL
$cmd = __DIR__."/bin/phantomjs.exe $script $target";
exec($cmd, $output);
return implode("", $output);
content.js
var webPage = require('webpage');
var system = require('system');
var page = webPage.create();
var url = system.args[1]; // This will get the second argument from $cmd, in this example, it will be the value of $target on index.php which is "http://www.kincir.com"
page.open(url, function (status) {
page.onLoadFinished = function () { // Make sure to return the content of the page once the page is finish loaded
var content = page.content;
console.log(content);
phantom.exit();
};
});
I recently published a project that gives PHP access to a browser. Get it here: https://github.com/merlinthemagic/MTS. It also relies on PhantomJS.
After downloading and setup you would simply use the following code:
$myUrl = "http://www.example.com";
$windowObj = \MTS\Factories::getDevices()->getLocalHost()->getBrowser('phantomjs')->getNewWindow($myUrl);
//now you can either retrive the DOM and parse it, like this:
$domData = $windowObj->getDom();
//this project also lets you manipulate the live page. Click, fill forms, submit etc.
Prerequisites
I have a Website, that displays a page with an input and a button. On the other end is a server that exposes a very basic HTTP API. The API is called like this:
http://127.0.0.1/api/arg1/arg2/arg3
where argX are the arguments. It returns raw HTML. This HTML code needs to be inserted into the Website (another domain). There is a
<div id="container5"></div>
on the website. The HTML needs to be inserted into this container. The code returned by the API is specifically made to be inserted into this container, as it uses CSS classes and scripts from the website, i.e.: the code is not valid for it self.
The Goal
Here is what I have: I've got the API to return what I want, and I got a small JavaScript to run on the website to change the contents of the container:
var element = document.getElementById("container5");
element.innerHTML = "New Contents";
This works so far. Now I need a way to get the HTML from the API to the page. By reading numerous SO questions, it quickly became clear that reading HTML from another URL is close to impossible in JavaScript, due to security constraints.
Is there an easy way to do this with JavaScript or do I need rethink the whole process somehow? One last constraint on my side is that I can only insert JS into the website, I can't - for example - upload a new file to the server.
Edit 1: Workaround!
I solved this for me by using a PHP intermediate file on the requesting server:
<?php
echo file_get_contents('http://example.com');
?>
This will generate a site using the HTML content of any URL. Now the requesting site can read this by using JavaScript:
var getHTML = function ( url, callback ) {
// Feature detection
if ( !window.XMLHttpRequest ) return;
// Create new request
var xhr = new XMLHttpRequest();
// Setup callback
xhr.onload = function() {
if ( callback && typeof( callback ) === 'function' ) {
callback( this.responseXML );
}
}
// Get the HTML
xhr.open( 'GET', url );
xhr.responseType = 'document';
xhr.send();
};
This modifies any element:
var element = document.getElementById("resultpage");
getHTML( 'http://localserver.org/test.php', function (response) {
element.innerHTML = response.documentElement.innerHTML;
});
Checkout CORS https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
also JSONP in same article.
I'm currently writing a Chrome Extension that takes user selected data and passes it to another window. Aside from the manifest.json files, I have a background.js and index.php file.
The background.js file successfully grabs the user selected data, opens a new broswer window, and then passes the user selected data to the index.php file located on a server via $_GET. This works great until you exceed a certain character limit in which case you get an error saying the url is too long. I was wondering if there was anyway to send the data from the background.js file to the new window using $_POST or any other method that would allow larger data selections? Or could I just pass the user selected data to the new window first, and then have that window access the server?
background.js
function getClickHandler() {
return function(info, tab) {
var tx = info.selectionText;
tx = encodeURIComponent(tx);
// Create a new window to the info page.
var url = 'http://192.168.0.22?tx=' + tx;
chrome.windows.create({url: url, width: 500, height: 760 });
};
};
chrome.contextMenus.create({
"title" : "Store",
"type" : "normal",
"contexts" : ["selection"],
"onclick" : getClickHandler()
});
index.php
<?php
$text = $_GET['tx'];
print ($text);
?>
Potentially you could create a form dynamically and then set an input to the value you're trying to send as a query-string-parameter.
Something like:
var $form = $('<form action="' + url + '" method="POST" target="_blank" />'),
$input = $('<input name="tx" type="text" />').val(tx);
$form.append($input).trigger("submit");
Without running the code I'm not sure if the submit will function without adding the form to the DOM, if not then you'll need to append the form to the DOM and then trigger submit, after which you probably want to remove the form from the DOM.
I say potentially because I'm not familiar with writing Chrome Extensions but I've done this as part of a normal web-app before and it worked.
For current non-IE browsers (Chrome, Firefox, Opera, Safari), I would like to send a PDF document to the printer given a URL to that PDF.
To avoid superfluous windows popping up, I am presently doing this with an <iframe> but I would like to close the iframe after the printing is completed, otherwise some browsers will pop up a dialog when one tries to leave the page.
Here is what I have come up with so far (using lodash and jQuery for simplicity):
var _print_url, _remove_iframe, _set_print;
_print_url = function(src_url) {
$("<iframe src='" + src_url + "' type='application/pdf'>").css({
visibility: 'hidden',
position: 'fixed',
right: '0',
bottom: '0'
}).on('load', _set_print).appendTo(document.body);
};
_remove_iframe = function(iframe) {
return $(iframe).parent().find(iframe).detach();
};
_set_print = function() {
this.contentWindow.print();
/*
* Where we could do #contentWindow.close() with a window, we must remove the
* print iframe from the DOM. We have to engage in some async madness
* it seems, for no apparent reason other than this won't work
* synchronously (#cw.print above must be async, it seems) - even though
* window.close() appears to work synchronously.
*/
_.delay(_.partial(_remove_iframe, this), 100);
};
Sometimes it seems with Google Chrome the print-dialog ends up showing the PDF correctly, but when one selects the printer and confirms the intention to print it will actually send the contents of the frame's parent page to the printer instead of the PDF itself.
There is a link suggestions on the Mozilla page but this document seems obsolete at the moment. The best example I could find was by reverse-engineering the Amazon Web Services print dialog for invoices, but that opens a window.
One alternative I have considered is Google Cloud Print, but obviously this requires the installation of extra software or configuration of a Google Account, neither of which I would wish to impose on users unless necessary.
Are there other examples of how one might print a PDF given a URL, particularly with Javascript and without otherwise superfluous additional browser add-ons or artefacts such as windows popping up?
-- NOTE whit this approach you will never see the popup blocker --
I run in a similar problem wiht an ajax application a couple month ago, the problem was I faced was I need to create the pdf file and store before to send it to print, what I did is the following:
I didnt use iframes. This application works with php TCPDF for creating the pdf and jquery and underscore for the templating system.
you can see the demo video at http://www.screenr.com/Ur07 (2:18 min)
Send the information via JSON (ajax) To achieve this because I faced the problem that everything was in ajax, so I couldn't make post with the information, so what I did is to append a hidden form to the DOM and then with the target="_blank" make the post to a new window (which you will close a the end of the process).
HTML hidden virtual form
<form method='<%= method %>'
action="<%= action %>"
name="<%= name %>"
id="<%= id %>"
target="_blank">
<input type='hidden' name='json' id='<%= valueId %>' />
</form>
Javascript
function makePost(){
var _t = _.template(_JS_Templates["print.form.hidden"]); //this the html of the form
var o = {
method : "POST",
action : js_WEB_ + "/print.php",
name : "print_virtual_form",
id : "print_virtual_form_id",
valueId : "print_virtual_value"
}
var form = _t(o);
$(".warp").append(form); //appending the form to the dom
var json = {
data : data // some data
}
$("#print_virtual_value").val(JSON.stringify(json)); //assing to the hidden input the value and stringify the json
$("#print_virtual_form_id").submit(); //make the post submmitting the form
$("#print_virtual_form_id").remove(); //removing the "virtual hidden form"
}
2.- when you receive the json in my case was using php you create whatever you have to create, I did it in this way you can see the php code in this example (is my answer)
Generate PDF using TCPDF on ajax call
3.- And finally in this is where the cool parts come is the response, in this part, I use the php file to write a javascript code saying that have to close the parent windows and report the json answer to an specific function, is kind of callback.
switch($_SERVER['REQUEST_METHOD']){
case "GET":
echo "Denied";
break;
case "POST":
if(isset($_POST["json"])){
if(!empty($_POST["json"])){
$json = $_POST["json"];
$hash = PDF::makePDF($json);
echo "<script>
function init() {
//calling callback
//returning control to the previous browser
window.opener.someclass.finish('".$hash."');
window.close();
}
window.onload = init;
</script>";
}else{
echo "not json detected";
}
}else{
echo "not json detected";
}
break;
4.- and for the end with the control in you window browser you can do.
var someobject = (function(name){
//private
var finish(data){
//do whatever you want with the json data
}
//public
var $r = {};
$r.finish = function(data){
finish(data); //you will pass the data to the finish function and that is..!! voila!!
}
return $r;
})("someobject");
I know is not exactly what you ask but is another approach to the same problem while I think this is little more complex, I can guarantee works in a lot of browser and the users will love the way you do it. They will never see what is happening and they will download the file just how the expect to do it, clicking a link and the saving the file.
Just my two cents and happy coding.