How to html escape variables in Thymeleaf? - thymeleaf

I would like to display several variables in a web page using Thymeleaf.
I have the code set as follows...
<span th:text="${foo.bar}" />
The problem is that when property foo.bar contains multiple spaces in a row, they are displayed as one (expected behaviour for HTML).
e.g. "hello world" => "hello world"
Is there a "Thymeleaf" way to HTML escape the value of the variable so that the value visually appears exactly as it should be?

I think the best way is to use HTML's way of accomplishing this: either using <pre> tags (<pre th:text="${foo.bar}" />), or else using the css white-space property and changing the style of your <span> tags that contain variables.
I guess you could also replace ' ' with (like this: <span th:text="${#strings.replace(foo.bar, ' ', ' ')}" />, but that would be my last option.

Related

How to get placeholder value using assert_selector in rails

How do I get the placeholder value from an input bar using assert_selector in rails ?
<input placeholder="hello world" value="<%= params[:query] %>" required>
I want to check is placeholder has hello in it
While the answer by Lam Phan does answer the question, resorting to writing XPath queries when CSS has what you need isn't usually the best idea from a readability/understandability perspective.
CSS has a number of attribute selectors, the most useful for testing usually being
= matching
*= containing
^= begins with
~= whitespace separated matching
You can see the full set at https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors
In this case that means you could do any of
assert_selector('input[placeholder*="hello"]') # contains 'hello'
assert_selector('input[placeholder~="hello"]') # contains the word 'hello'
assert_selector('input[placeholder^="hello"]') # starts with 'hello'
Another option would be take advantage of the fact that the Capybara :field selector has a placeholder filter - https://github.com/teamcapybara/capybara/blob/master/lib/capybara/selector.rb#L34 - which takes a regex - so you could do
assert_field(placeholder: /hello/)
which is the same as
assert_selector(:field, placeholder: /hello/)
you could use xpath and verify that there's an input element whose's placeholder contains 'hello'
assert_selector(:xpath, './/input[contains(#placeholder,"hello")]')
if you want to check exactly an input with id
assert_selector(:xpath, './/input[#id="input_id"][contains(#placeholder,"hello")]')
im not sure there's a helper method that check regex something like ~= /.*hello.*/, if you want to do that, you still have another choice
placeholder = find('#input_id')['placeholder'] # or use :xpath
assert_match /.*hello.*/, placeholder

Why this Xpath not working?

For example this HTML
<div>
<span></span> I want to find this <b>this works ok</b>.
</div>
I want to find a DIV with I want to find this in it and then grab the whole text inside that DIV including child elements
My XPATH, //*[contains(text(), 'I want to find this')] does not work at all.
If I do this //*[contains(text(), 'this works')] it works but I want to find any DIV based on I want to find this text
However, if I remove the <span></span> from that HTML, it works, why is that?
text() only gets the text before the first inner element. You can replace it with . to use the current node to search.
//div[contains(., 'I want to find this')]
This will search in a string concatenation of all text nodes inside the current node.
To grab all text you can use node.itertext() to iterate all inner texts if you are using lxml:
from lxml import etree
html = """
<div>
<span></span> I want to find this <b>this works ok</b>.
</div>
"""
root = etree.fromstring(html, etree.HTMLParser())
for div in root.xpath('//div[contains(., "I want to find this")]'):
print(''.join([x for x in div.itertext()]))
# => I want to find this this works ok.
Try using //*[text()=' I want to find this '] , this will select the div tag and then for text you can use the getText() method to get the text
You can try Replace text() with string():
//div[contains(string(), " I want to find this")]
Or, you can check that span's following text sibling contains the text:
//div[contains(span/following-sibling::text(), " I want to find this")]

Displaying user input html with newlines

I have comments section in my application where users enter input in a text area. I want to prevent the line breaks they enter but also display html as a string. For example, if comment.body is
Hello, this is the code: <a href='foo'>foo</a>
Bye
I want it to be displayed just as above. The same with anything else, including iframe tags.
The closest I got is:
= simple_format(comment.body)
but it sanitizes html code and it's not displayed. Example: foo <iframe>biz</iframe> bar is displayed as:
foo biz bar
What should I do to achieve what I want?
Just use it without any method, it will be rendered as plain text:
= comment.body
Using your second example, the output will be:
foo <iframe>biz</iframe> bar
To make \n behave as <br>, you can use CSS:
.add-line {
white-space: pre-wrap;
}
And use it in your view:
.add-line = comment.body
Using your first example:
comment.body = "Hello, this is the code: <a href='foo'>foo</a>\n\nBye"
The output will be:
Hello, this is the code: <a href='foo'>foo</a>
Bye
Having done something similar in the past, I think you must first understand why HTML is sanitized from user input.
Imagine I wrote the following into a field that accepted HTML and displays this to the front page.
<script>alert('Hello')</script>
The code would execute for anyone visiting the front-page and annoyingly trigger a JS alert for every visitor.
Maybe not much of an issue yet, but imagine I wrote some AJAX request that sent user session IDs to my own server. Now this is an issue... because people's sessions are being hijacked.
Furthermore, there is a full JavaScript based exploitation framework called BeEF that relies on this type of website exploit called Cross-site Scripting (XSS).
BeEF does extremely scary stuff and is worth taking a look at when considering user generated HTML.
http://guides.rubyonrails.org/security.html#cross-site-scripting-xss
So what to do? Well if you checked in your DB you'd see that the tags are actually being stored, but like you pointed out aren't displayed.
You could .html_safe the content, but again I strongly advise against this.
Maybe instead you should write an alternative .html_safe method yourself, something like html_safe_whitelisted_tags.
As for removing newlines, you say you want to display as is. So replacing /n with <br>, as pointed out by Michael, would be the solution for you.
comment.body.gsub('\n', '<br />').html_safe_whitelisted_tags
HTML safe allows the html in the comment to be used as html, but would skip the newlines, so doing a quick replace of \n with <br /> would cover the new lines
comment.body.gsub("\n", "<br />").html_safe
If you want the html to be displayed instead of rendered then checkout CGI::escapeHTML(), then do the gsub so that the <br /> does not get escaped.
CGI::escapeHTML(comment.body).gsub("\n", "<br />")

Angular expression inside of an inline html UI Bootsrap tooltip

I can't figure out the proper method to render angular expressions inside of uib-tooltip-html. The example used in the documentation: inline string
doesn't work for me in IE, Chrome or Firefox. Can anyone point me in the right direction? Thanks.
Here's an excerpt from the docs
uib-popover-html - Takes an expression that evaluates to an HTML string. Note that this HTML is not compiled. If compilation is required, please use the uib-popover-template attribute option instead. The user is responsible for ensuring the content is safe to put into the DOM!
The interesting part is
The user is responsible for ensuring the content is safe to put into the DOM!
Which basically means you should use the $sce service to sanitize you HTML and pass it as an expression at your controller
$scope.dynamicTooltipText = "MY SUPER DYNAMIC TEXT"
$scope.htmlPopover = $sce.trustAsHtml('static. ' + $scope.dynamicTooltipText + ' . <b>bold.</b>');
and then pass $htmlPopover to your uib-popover-html directive
<button uib-popover-html="htmlPopover" popover-placement="bottom" class="btn btn-default">
HTML Popover
</button>
Demo plunker

Escaping '&' character in thymeleaf

I need an image loaded onto a html img tag using thymeleaf. The problem is, the image itself is obtained from a url which takes in two parameters.
Sample:
<img src="/products/images?categoryId=1&image=1" />
The trouble is, the image parameter is generated dynamically and hence I need to use a thymeleaf expression there. Therefore I tried something like this:
<img th:src="#{'/products/images?categoryId=1&image=' + ${product.id}}" />
But when I run this, I get the following message:
Exception parsing document: template="product-list", line 104 - column 59
Which points to the location where the '&' symbol occurs. Now, I have tried using '& amp;' but then, the url becomes something like
/products/images?categoryId=1&image=1
Obviously, this is not going to work.
So how else do I make a valid link with two parameters using thymeleaf then?
This can easily done by Thymeleaf. Don't concatenate strings and
simply use #{'/products/images'(categoryId=1, image= ${product.id})}
See the documentation.
The way that you escape an ampersand & in any html attribute is &. Actually you should always escape ampersands in all html attributes whether you are using Thymeleaf or not.
See this question for more details and references:
Do I encode ampersands in <a href...>?

Resources