Flask-WTF, unexpected output using macro use of {{ field |safe}} (HTML escaping) - macros

When the input is
When then output is
But when I change the input to
My output changes to
Please some body explain me why this is happening?
What's the role of {{ field |safe}}?
My App.py code -
class ContactForm(FlaskForm):
name = StringField("Name Of Student",validators = [InputRequired(message
= 'Name is missing'),Length(min=5,max=10,message="wrong")])
email = StringField("email",[validators.Email("Please enter your email
address.")])
#app.route('/form', methods = ['GET','POST'])
def index():
form = ContactForm()
if form.validate_on_submit():
return render_template('macro_output.html',form=form)
return render_template('main_form.html',form=form)
macro_form.html
{% macro render_output(field) %}
<p>
{{ field.label}}
{{ field |safe}}
</p>
{% endmacro %}
macro_output.html
{% from "macro_form.html" import render_field,
render_ErrorMessage,render_output %}
<html>
<body>
{{ render_output(form.name)}}
{{ render_output(form.name.data)}}
{{ render_output(form.email)}}
{{ render_output(form.email.data)}}
</body>
</html>

The jijna2 filter safe treats strings as, well, safe and does not do any pre-formatting by automatically escaping any characters that would otherwise be interpreted as code (in this case HTML code).
So if a variable, say, form.name.data = <html> then invoking {{ form.name.data | safe }} will embed an html tag (<html>) into your HTML, and I expect that the HTML browser parser simply ignores it as an error (you can check the rendered page source). But invoking {{ form.name.data }} jinja2 will escape the '<>' characters only 'html' will go into the HTML and therefore be rendered as text.
I would strongly advise against the use of the safe filter for user input, since an arbitrary user could inject code into your page, e.g. <script> ... </script>

Related

11ty - How to display post tags in my post.njk file?

I've used the 11ty/eleventy-base-blog template and have things working well - but I am trying to display the tags nicely within my post.njk file.
At the top of my post (.md) file I have this:
tags: ['Tag 1', 'Tag 2']
Within _includes/layouts/postslist.njk I'm using the following code:
{% for tag in post.data.tags %}
{%- if collections.tagList.indexOf(tag) != -1 -%}
{% set tagUrl %}/tags/{{ tag }}/{% endset %}
<span class="tag">{{ tag }}</span>
{%- endif -%}
{% endfor %}
This template is then used in the index.njk file (my homepage) using this code:
{% set postslist = collections.posts | head(-3) %}
{% set postslistCounter = collections.posts | length %}
{% include "postslist.njk" %}
The output of this is:
<span class="tag">Tag 1</span>
<span class="tag">Tag 2</span>
However, if I use {{ tags }} in my post.njk file I get the following output:
posts,Tag 1,Tag 2
I have also tried using the same code from the 'postslist.njk' file and putting it in my 'post.njk' file but that doesn't work.
How can I display the tags on my post in separated 'span' tags and also remove the 'posts' tag?
If you want me to open up my git repo then let me know.
Thanks!
Why is the posts tag in your tags?
In the starter template you're using (11ty/eleventy-base-blog), the posts/ directory includes a directory data file (posts.json), which gets applied too all files in that directory – i.e. to all posts. Since the .eleventy.js config file uses the Data Deep Merge option, the tags you set in each post get merged with the posts tag. This is why the tags of your post are ['posts', 'Tag 1', 'Tag 2'].
You can solve this in multiple ways. Either use two different frontmatter fields for collection and display purposes, or just filter out the posts tag when displaying the tags of a post.
How to output a list of tags?
If you just do {{ tags }}, you're telling Nunjucks to output an array, so it has to figure out how to convert an array to a string. Apparently, the default behaviour in this case is to simply join them with commas. You can make this more explicit (and include a space after the comma while you're at it):
{{ posts | join(', ') }}
Or, if you want to wrap the items in HTML tags, you can use a loop, optionally with a joiner:
{% set comma = joiner() %}
{% for tag in tags -%}
{% if tag !== 'posts' %}
{{ comma() }} <span class="tag">{{ tag }}</span>
{% endif %}
{%- endfor %}

How to get a new object after form submittig?

I'm trying flask nad there is a simple task to submit a form.
Page is showing a one picture and a form, if form was submitted correctly, picture should be changed, if not - be the same.
I can't understand the mechanism of how to show only one object on a page and get another after form submitting.
Tried to use iterator over the list of images is folder "static", but my implementation was not working correctly.
Please provide me a feedback how to do it in rigth way?
Now I have the simple view:
#app.route("/", methods=["GET", "POST"])
def start_view():
picture = None
form = InputForm(csrf_enabled=False)
if form.validate_on_submit():
picture = form.picture.data
form.picture.data = ""
return render_template('04-2.html', form=form, picture=picture)
class InputForm(Form):
picture = StringField('What is on a picture?', validators[DataRequired()])
submit = SubmitField('Submit')
And a simple template:
<body>
<form method="POST">
{{ form.picture.label }} {{ form.picture }}
{{ form.submit() }}
</form>
{% if form.errors %}
<span style="color: red">{{ form.error }}</span>
{% endif %}
</body>
Thank you!
Your form doesnt contain any pictures. It has a StringField and a SubmitField. If you want to see any image you need to have an <img> tag in your HTML pointing to the image location in your server
your view should look like:
from Flask import session
# in order to use sessions you have to use a secret key for your app
app.secret_key = 'some secret key'
#app.route("/", methods=["GET", "POST"])
def start_view():
img_list = ['filename1', 'filename2', 'filename3']
# if this is not the first form submission
if session.has_key('current'):
# if we reach the end of the list show the first image again
if int(session['current']) == len(img_list) - 1:
session['current'] = 0
# move to next image
else:
session['current'] = int(session['current']) + 1
else:
session['current'] = 0
picture = 'first_image_filename' # this should be the img on load
form = InputForm(csrf_enabled=False)
if form.validate_on_submit():
picture = img_list[int(session['current'])] # the filename of the next image
form.picture.data = ""
return render_template('04-2.html', form=form, picture=picture)
so template should look like:
<body>
<form method="POST">
{{ form.picture.label }} {{ form.picture }}
<img src="{{url_for('static', filename='img/' + picture)}}"
{{ form.submit() }}
</form>
{% if form.errors %}
<span style="color: red">{{ form.error }}</span>
{% endif %}
</body>

How to use existing widget form template blocks

I am creating custom Twig form template blocks to do some special rendering.
I have been unable, so far, to use some of the built-in form blocks. Specifically, and of the {{ *_widget() }} blocks.
Example:
Declare a new Twig function.
/* src/SiteBundle/Library/TwigExtension.php - Bundle-specific Twig extension */
...
public function getFunctions
{
$ret = [
new \Twig_SimpleFunction( 'wwui_myBlock', null, ['node_class'=>'Symfony\Bridge\Twig\Node\RenderBlockNode, 'is_safe'=>['html']] )
];
return $ret;
}
The extension is registered in the service.yml file and is obviously recognized since there are no errors about the wwui_myBlock function being undefined.
Define the Twig block.
{# src/SiteBundle/Resources/views/Form/fields.html.twig - Custom form blocks #}
{% block wwui_myBlock %}
<div class="formRow form-group-xs clearfix">
<div class="col-xs-1"></div>
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-xs-2' )|trim}) -%}
{{ form_label( form ) }}
<div class="col-xs-9">
{{ choice_widget( form ) }}
</div>
</div>
{% endblock wwui_myBlock %}
In the page template.
{# Page-specific template #}
...
{{ form_start( form ) }}
...
{{ wwui_myBlock( form.options ) }} {# options is a choice with radio buttons. #}
...
{{ form_end( form ) }}
When this page is rendered an exception is thrown:
The function "choice_widget" does not exist in SiteBundle:Form:fields.html.twig at line 62
My fields.html.twig file "uses" bootstrap_3_horizontal_layout.html.twig. That file "uses" bootstrap_3_layout.html.twig which, in turn, "uses" Twig's default form_div_layout.html.twig.
Why can't my custom block see choice_widget which is defined in form_div_layout.html.twig?
Turns out that the proper way to access such blocks is to use the block() function. so, instead of
{{ choice_widget( form ) }}
it's
{{ block( 'choice_widget', form ) }}

Symfony2 disable form label html escape

I have a form label printed like this:
{% set txt = 'Im OK with the terms and conditions' %}
{{ form_label(form.terms, txt) }}
But obviously this will escape the html tag and will print something like this:
<label>Im OK with the <a href="#">terms and conditions</a></label>
Is there a way to disable the html escaping for this label only?
I've already tried:
{{ form_label(form.terms, txt|raw) }}
...
{{ form_label(form.terms, txt)|raw }}
...
{% autoescape false %}
{{ form_label(form.terms, txt) }}
{% endautoescape %}
Any help would be greatly appreciated :)
One solution that works, but I'm not sure is the best one, is to set the label tag manually:
<label for="{{ form.terms.vars.id }}">{{ txt|raw }}</label>

Site-wide form with Symfony2?

I have simple form at all site pages: username, password, [Sign In]
I tried to make with with simple HTML, but I get
The CSRF token is invalid. Please try to resubmit the form
Idea to make form in each action seems bad. What is the good practice of doing site-wide forms?
You have to do it this way:
First, I guess you have some base template file like layout.html.twig and all other pages extend it. Eg:
// Resources/views/layout.html.twig
<doc ... bla blah>
<title>My site</title>
...(js, css)...
<body>
<div id="top">
{% render url("site_wide_form") %}
</div>
{% block content %}
{% endblock content %}
</body>
You need controller that will handle your form:
//Controller/SitewideController.php
/**
* #Route("/some/url/here", name="site_wide_form")
* #Template("yourbudle:folder:site_wide_form.html.twig")
*/
public function someAction()
{
..... your code for form, process submission etc ...
return ["form"=>$form->createView()] ;
}
and template file:
// site_wide_form.html.twig
<form action="{{ path("site_wide_form") }}" method="post">
{{ form_widget(form) }}
</form>
That's it. Read this to understand render tag: http://symfony.com/doc/2.2/book/templating.html#embedding-controllers
The CSRF token is missing. In your template (I assume you use Twig). You can tell Symfony2 to render all the remaining form elements using:
{{ form_rest(form) }}
Or you can render just the CSRF token:
{{ form_row(form._token) }}