How to send a pdf file for ajax response using flask? - ajax

I am trying to display a pdf file in the browser without downloading it for the user. So currently I have a flask function that can generate a pdf file using FPDF module and I need it to be displayed to the user in a new tab.
The current flask function is
#app.route('/pdf/create', methods=['GET', 'POST'])
def create_pdf():
if not session.get('email'):
return redirect(url_for('login_page'))
email = session.get('email')
pdf = FPDF('P', 'mm', "A4")
pdf.add_page()
.... pdf making ...
pdf.output(f"pdf/mypdf.pdf")
return send_file(f"pdf/mypdf.pdf",
mimetype='application/pdf',
attachment_filename=f"My file.pdf",
as_attachment=True,
cache_timeout=-1)
I call the ajax function as
$("#viewFile").on("click",function(){
$.ajax({
url: "{{url_for('create_pdf')}}",
type: "get",
data: {text: mytext},
success: function(response) {
window.open(response);
},
error: function(xhr) {
alert("Error: Failed to save notes.");
}
});
});

With the parameter as_attachment you can specify whether the browser should save the file or offer it for display. If the value is set to False, the file should be displayed. (See documentation)
The following example also shows you how you can deliver the pdf document without saving it temporarily.
The use of AJAX is not necessary in this case. A simple anchor with a target should do the trick.
Flask (app.py)
from flask import (
Flask,
render_template,
send_file
)
from fpdf import FPDF
import io
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/pdf/create')
def create_pdf():
pdf = FPDF()
pdf.set_title('My Example PDF')
pdf.set_author('Artist Unknown')
pdf.set_subject('Creation of a PDF')
pdf.add_page()
pdf.set_font('Arial', 'B', 16)
pdf.cell(40, 10, 'Hello World')
stream = io.BytesIO(pdf.output(dest='S').encode('latin-1'))
return send_file(
stream,
mimetype='application/pdf',
attachment_filename='example.pdf',
as_attachment=False
)
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
Download
</body>
</html>

Related

Wagtail routable page view with ajax content loading on click

I have an app where i display contributor page with content like stories, videos and articles.
Stories, videos and articles are different page models.
template engine: Jinja
framework: Django
CMS: wagtail
I am able to load ajax content in to the tab successfully by creating separate view and corresponding url in urls.py with below ajax settings.
But if i try to use Wagtails routable page feature i get 404.
Ajax settings excerpt:
settings = jQuery.extend(true, {}, {
type: 'get',
dataType: 'json',
url: vUrl,
beforeSend: function (jqXHR, settings) {
console.log(`URL ${settings.url}`)
$tab.html('<br>');
$paneHeader.removeClass(css).addClass(css);
$el.trigger('tabsX:beforeSend', [jqXHR, settings]);
},
success: function (data, status, jqXHR) {
setTimeout(function () {
// $tab.html(data); //Use Json data instead of html
$tab.html(data.html);
$pane.tab('show');
$paneHeader.removeClass(css);
}, 300);
},
error: function (jqXHR, status, message) {
$paneHeader.removeClass(css);
},
}
#route(r'^/contributors/(?P<slug>[-\w]*)/articles')
def show_articles_contributed(self, request):
if request.is_ajax():
print("Inside AJAX")
articles = Contributor.get_articles_by_authors(self.id)
html = render_to_string("organisms/latest_articles.html", {"objects": articles})
return JsonResponse({"html": html}, safe=True)
else:
return super(ContributorPage, self).serve(request)
URL printed from console.log
URL /contributors/test-author/articles
Failed to load resource: the server responded with a status of 404 (Not Found)
class ContributorPage(RoutablePageMixin, PhoenixPage):
first_name = models.CharField(max_length=255, blank=True, default="")
last_name = models.CharField(max_length=255, blank=True, default="")
nickname = models.CharField(max_length=1024, blank=True, default="")
PhoenixPage is nothing but base page with metadata which actually uses base Wagtail Page class.
Any help/guidance would be greatly appreciated.
If question is not clear i can provide more info.
Thanks
I found a problem.
Posting a description for future visitors of this thread.
Ajax code was not an issue here but Wagtail route implementation was.
#route(r'^/contributors/(?P[-\w]*)/articles') <--This route is not correct for ContributorPage
ContributorPage gives URL like /contributor/author_name,
While author_name being ContributorPage slug
I just want to append /articles to it, to get route like /contributor/author_name/articles
so correct route for ContributorPage is
#route(/articles)
So now my main ContributorPage is live at /contributor/author_name
and
Ajax tabs are queried using route urls like,
/contributor/author_name/articles
/contributor/author_name/videos
/contributor/author_name/stories
with corresponding route like,
#route(/articles)
#route(/videos)
#route(/stories)

Django CSRF Token Missing Only in Production

I am getting a missing CSRF_Token error that only occurs in production mode on my server. However everything works great when I am running it from my computer terminal using the runserver command. I've read through many of the other questions pertaining to this with no luck. It seems that my case is slightly different than others, since it works locally but not in production.
I get the error when submitting an Ajax form that submits to the "submit" in views.py. Does anybody know what could be causing this? Also, looking at my cookies in Production mode, the CSRF_Token is not even there to begin with. Locally it is. Thanks for any help.
Here is my views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
return render(request, 'index.html')
def submit(request):
#Receive Request
inputone = request.POST['randominfo']
inputtwo = request.POST['randominfo2']
#Some more code here that setups response.
#Deleted since Im posting to StackOverflow
return response
Code Pertaining to the Ajax Submit
$(function () {
$.ajaxSetup({
headers: { "X-CSRFToken": getCookie("csrftoken") }
});
});
function getCookie(c_name)
{
if (document.cookie.length > 0)
{
c_start = document.cookie.indexOf(c_name + "=");
if (c_start != -1)
{
c_start = c_start + c_name.length + 1;
c_end = document.cookie.indexOf(";", c_start);
if (c_end == -1) c_end = document.cookie.length;
return unescape(document.cookie.substring(c_start,c_end));
}
}
return "";
}
function submitAjax(event){
$.ajax({
type:'POST',
url:'/submit/',
data:{
randominfo:document.getElementById('Random').innerHTML,
randominfo2:document.getElementById('Random2').innerHTML,
},
dateType: 'json',
success:function() {
# Url here
}
})
};
Solution that fixed this problem.
Adding "from django.views.decorators.csrf import ensure_csrf_cookie" in views.py and then "#ensure_csrf_cookie" above the view that returns the html file that contained the ajax form
The error ocurs because you are not setting the csrf token, to prevent this we have to check some details
First of all, you have to set the csrf token to your form, in your html you have to set some as follow:
<form id="id" name="form">
{% csrf_token %}
<!-- Form body here -->
</form>
Second the approach to set the csrf cookie to your request header is ok, i only suggest that instead you set your data field one by one, use method serialize of jquery
data: $("#your-form-id").serialize()
I would like to recommend you to read this post about ajax request with django that is very helpful
There are 2 things you can do:
1.) Submit a CSRF token in your ajax call. You have to use a getCookie() javascript function to get it. Luckily the django documentation has some code you can copy and paste.
javascript
$.ajax({
type:'POST',
url:'/submit/',
data:{
randominfo:document.getElementById('Random').innerHTML,
randominfo2:document.getElementById('Random2').innerHTML,
'csrfmiddlewaretoken': getCookie('csrftoken'), // add this
...
2.) Disable csrf for your /submit view. You can do this with a decorator. Note that this is less secure so make sure there's no confidential data.
views.py:
from django.views.decorators.csrf import csrf_exempt
...
#csrf_exempt
def your_submit_view(request):
#view code

How to use to decode data sent using AJAX in Ruby

I am very confused. I am sending my data using ajax after the information is introduced in one textarea. However, the info. is corrupted in this way: When writing pasá it would received in the server as pas%C3%A1
I already checked that pas%C3%A1 corresponds to UTF-8. URLEncode Code Chart
I also checked this link:
$('#form').serialize() messes up UTF-8 characters
but without success. This is for PHP.
Question: How can I decode UTF-8 in Ruby? or how can I avoid this corrupted data
I have defined this in my view:
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /
To get the info. from my view and to send it to the server I use:
$('textarea').change(function(e){
var pointer = $(this);
//Compare colors when focus is lost, if orange==orange, then send data
var color = pointer.css("background-color");
if (color == 'rgb(250, 187, 0)'){
var id = pointer.attr("id");
var getNumberFromId = id.substr(2,3);
var getLanguage = id.substr(0,2);
var getKey = $("#key"+getNumberFromId).html();
var getKey = getKey.replace(" ","");
console.log(getKey);
e.stopPropagation();
pointer.css('background-color','#8Fbc8F'); // Change field to green
toServer(pointer,getKey, getLanguage);
}
});
function toServer(pointer, key, language) {
$.ajax({
type: 'POST',
url: '/translator/translate/'+language+'/'+key,
data: {data:pointer.serialize()},
success: function(data){
}
});
}
If i understand correctly then you are trying to decode it at the server side. If that is the case then you could try something like this:
require 'cgi'
s = "pas%C3%A1"
string = CGI::unescape(s)
puts string # pasá

API request from front-end using sails.io.js

I have a basic front-end (html, css, jquery) and I'd like to use sails.io.js to communicate with an API server (developped with sails, with cors enabled). The API is running on localhost:10000 but it will be on an another domain than the one of the webclient later on.
Directly from jquery, I can issue some get request to this API and get the expected results.
When it comes to websocket, I have some problems...
In the index.html (just to test), I put the following:
<script src="js/sails.io.js"></script>
<script type="text/javascript">
io.sails.url('http://localhost:10000');
io.socket.get('/data', function serverResponded (body, sailsResponseObject) {
// body === sailsResponseObject.body
console.log('Sails responded with: ', body);
console.log('with headers: ', sailsResponseObject.headers);
console.log('and with status code: ', sailsResponseObject.statusCode);
});
</script>
But Chrome's developer tools tell me
ReferenceError: io is not defined
Any idea ?
UPDATE
I'm serving index.html with a web server (python -m SimpleHTTPServer)
I've installed sails.io.js using bower.
I've try to make this test as simple as possible:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<script src="bower_components/sails.io.js/dist/sails.io.js"></script>
<script src="index.js"></script>
</body>
</html>
index.js:
window.onload=function(){
io.sails.url = 'http://localhost:10000';
io.socket.get('http://localhost:10000/data', function (body, response) {
console.log('Sails responded with: ', body);
});
};
My sails (0.9.16) API is only returning a json object on the GET /data route.
I have implemented a dummy __getcookie function in the api:
'get /__getcookie': function(req, res, next){
res.json({ok: 123});
}
And commented the line 481 in interpret.js (Scott comments below).
I have also modify config/socket.js with:
authorization: false,
=> I can now get the result from the /data route of my API :)
But... on each request I have the following error:
error: Error: No valid session available from this socket.
First of all, sails.io.js includes the code for socket.io.js, so there is no need to try and include that separately. You should remove this line:
<script src="bower_components/socket.io/lib/socket.js"></script>
Next, if you're just loading index.html from disk (rather than serving it from a web server), you'll need to tell the Sails socket client what URL to connect to:
io.sails.url = 'http://localhost:10000';
Put this anywhere before you start making socket calls; the library is smart enough to wait until its connected before trying to make the calls. So, altogether:
window.onload=function(){
io.sails.url = 'http://localhost:10000';
io.socket.get('http://localhost:10000/data', function (body, sailsResponseObject) {
console.log('Sails responded with: ', body);
console.log('with headers: ', sailsResponseObject.headers);
console.log('and with status code: ', sailsResponseObject.statusCode);
});
};
should work. You should be able to see in the console whether or not the socket connected by looking for the "io.socket connected successfully." message.
did you try with a / in front of the src, like:
< script src="js/sails.io.js">
Do you have the sails.io.js in the /assets/js/ folder (sails 0.10) or in the /assets/linker/js folder (sails 0.9 and below).
Did sails lift copied that js file to .tmp/public/js folder?
Where is your index.html file located?

Ajax Django Login/Authentication without re-direct

I am trying to have an authentication set-up similar to that of StackOverflow, where the normal browsing is never affected unless there are some privileged actions which requires authentication (Do not bother users until then).
It should be as "Log In" if not logged in or "UserName" if logged in.
The relevant part of base.html (from fallr.net) (extended by index.html) looks like :
<script type="text/javascript">
//<![CDATA[
$(document).ready(function(){
var methods = {
forms : function(){
var login = function(){
var user = $(this).children('form').children('input[type="text"]').val();
var pass = $(this).children('form').children('input[type="password"]').val();
var dataString = '&username=' + $('input[name=username]').val() + '&password=' + $('input[name=password]').val();
if(user.length < 1 || pass.length < 1){
alert('Invalid!\nPlease fill all required forms');
} else {
alert('username: '+user+'\npassword: '+pass);
$.ajax({
type: "POST",
url: "/login",
dataType: "html",
data: {
username : user,
password : pass,
csrfmiddlewaretoken : '{{ csrf_token }}'
},
success: function(json){alert (json.server_response);},
error: function(xhr,errmsg,err) { alert(xhr.status + ": " + xhr.responseText); }
});
$.fallr('hide');
return false;
}
}
$.fallr('show', {
icon : 'secure',
width : '320px',
content : '<h4>Sign in</h4>'
+ '<form>'
+ '<input name="username" placeholder="username" type="text"/'+'>'
+ '<input name="password" placeholder="password" type="password"/'+'>'
+ '</form>',
buttons : {
button1 : {text: 'Submit', onclick: login},
button4 : {text: 'Cancel'}
}
});
}
};
//button trigger
$('a[href^="#fallr-"]').click(function(){
var id = $(this).attr('href').substring(7);
methods[id].apply(this,[this]);
return false;
});
// syntax highlighter
hljs.tabReplace = ' ';
hljs.initHighlightingOnLoad();
});
//]]>
</script>
The urls.py looks like :
from django.conf.urls import patterns, include, url
#from triplanner.views import *
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', main_page),
url(r'^login$',ajax_login),
url(r'^login/$','django.contrib.auth.views.login'),
url(r'^logout/$', logout_page),
# our application page
url(r'^account/',include('tripapp.urls')),
)
Also, '^login/$' is the previous implementation for learning which I want to replace with Ajax login.
And my views.py:
# -*- coding: utf-8 -*-
from django.contrib.auth import logout
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.contrib.auth import authenticate, login
#from django.http import HttpResponse
from django.template import RequestContext
#from django.utils import simplejson
def main_page(request):
return render_to_response('index.html', context_instance=RequestContext(request))
def logout_page(request):
"""
Log users out and redirect them to the main page
"""
logout(request)
return HttpResponseRedirect('/')
def ajax_login(request):
"""
This view logs a user in using the POST data.
"""
if request.method == 'POST':
print request.POST['username']
username = request.POST['username']
password = request.POST['password']
print username
print password
user = authenticate(username=username, password=password)
if (not user is None) and (user.is_active):
login(request, user)
response_dict = {}
response_dict.update({'server_response': username})
#return HttpResponse(simplejson.dumps(response_dict),mimetype='applicaion/javascript')
return render_to_response('index.html',{'username' : user}, context_instance=RequestContext(request))
# Set Session Expiry to 0 if user clicks "Remember Me"
#if not request.POST.get('rem', None):
# request.session.set_expiry(0)
#data = username
else:
return render_to_response('index.html', context_instance=RequestContext(request))
I am getting a 403 Error like "[20/Aug/2013 00:29:20] "POST / HTTP/1.1" 403 2294"
UPDATE NUMBER 1:
With the changed urls.py, views.py and javascript I am able to get a 200 response, but it gives alert window saying undefined and alerting me "Prevent this page from creatng dialog boxes"
The approach I use is to have a Tastypie api layer and require authentication for the APIs. If the API call fails because of authentication, the client can request the user to log-in via the ajax login method.
You can log-in a user via ajax using this gist
So, it looks like your current problem is with this: alert (json.server_response);. You may want to look into changing your $.ajax dataType parameter to json.
To quote the docs:
The type of data that you're expecting back from the server. If none is specified, jQuery will try to infer it based on the MIME type of the response (an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string). The available types (and the result passed as the first argument to your success callback) are:...
"html": Returns HTML as plain text; included script tags are evaluated when inserted in the DOM.
"json": Evaluates the response as JSON and returns a JavaScript object. The JSON data is parsed in a strict manner; any malformed JSON is rejected and a parse error is thrown. As of jQuery 1.9, an empty response is also rejected; the server should return a response of null or {} instead. (See json.org for more information on proper JSON formatting.)

Resources