always i use el expressions like this;
<h:outputText value="#{bean.value}" escape="true" />;
and i cannot escape from xml in input fields:
<h:inputText value="#{bean.value}" />
is there a way to totally escape xml in facelets.
for instance a context parameter;
<context-param>
<param-name>facelets.ESCAPE_XML</param-name>
<param-value>false</param-value>
</context-param>
Override the renderer for <h:outputText> and comment out the part where it escapes the text. Then register your renderer in your faces.config.xml.
Of course, this will only work if you use that tag. It won't work if you just output an expression eg #{bean.value}.
Personally, I'd rather stick with having to add the escape attribute.
Both the h:outputText and h:inputText by default already escapes XML entities. You can't even turn it off in h:inputText as you could do in h:outputText. Your problem lies somewhere else. Maybe your understanding/definition of "escape XML" is wrong. Also, your <context-param> example suggests that you want to disable XML escaping. You can't do that for h:inputText because your webapp would then be prone for XSS attacks. You don't want to have that.
Have not tried it but you could maybe use a custom converter like the one bellow(Converts \n to <br/>)
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import org.apache.commons.lang.StringUtils;
public class BreakLineConverter implements Converter {
/**
* No conversion required
*/
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return value;
}
/**
* Converts All \r \n \r\n into break
*/
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (null==value || StringUtils.isEmpty((String)value))
return "";
String val=value.toString();
//This will take care of Windows and *nix based line separators
return val.replaceAll("\r\n", "<br />").replaceAll("\r", "<br />").replaceAll("\n", "<br />");
}
}
Register converter in faces-config.xml
<converter>
<description>Converts data to be displayed in web format
</description>
<converter-id>BreakLineConverter</converter-id>
<converter-class>comp.web.converter.BreakLineConverter</converter-class>
</converter>
Related
In my faces-config.xml I have set
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
Thus an arbitrary input component with no input places a null into the bean property bound to the value="#{myBean.someDate}" attribute and renders an empty component when getting null from that bean property.
While this is generally the wanted behavior for the application, I have a particular component where I want to replace null with a custom non null value.
Is there a general good way to achieve this?
I have tried this for the Primefaces (6.2) calendar with Mojarra 2.3.8:
<h:form id="myForm">
<p:calendar value="#{myBean.someDate}" pattern="MM/dd/yyyy"
converter="mySpecialDateConverter" locale="en" />
<p:commandButton value="send" process="#form" update="#form" />
<hr />
<h:outputText value="#{myBean.someDate}" />
</h:form>
I tried it using a converter that while rendering the component returns an empty String in getAsString() when my custom value is received from #{myBean.someDate}, or spits out my custom value in getAsObject() when null or pure emptyness is submitted:
import java.util.Date;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.DateTimeConverter;
import javax.faces.convert.FacesConverter;
#FacesConverter("mySpecialDateConverter")
public class MyConverter extends DateTimeConverter {
private static final Date PAST = new Date(0);
public MyConverter() {
setPattern("MM/dd/yyyy");
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (PAST.equals(value)) {
return "";
}
return super.getAsString(context, component, value);
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (null == value || "".equals(value.trim())) {
return PAST;
}
return super.getAsObject(context, component, value);
}
}
Rendering an empty calendar component when the bean property equals PAST works as expected. But submitting the calender without input sets the property to null because Primefaces CalendarRenderer simply does not consult the Converter on blank value submission:
package org.primefaces.component.calendar;
public class CalendarRenderer extends InputRenderer {
// ...
public Object getConvertedValue(FacesContext context, UIComponent component, Object value) throws ConverterException {
Calendar calendar = (Calendar) component;
String submittedValue = (String) value;
SimpleDateFormat format = null;
if (isValueBlank(submittedValue)) {
return null;
}
//Delegate to user supplied converter if defined
try {
Converter converter = calendar.getConverter();
if (converter != null) {
return converter.getAsObject(context, calendar, submittedValue);
}
}
// ...
}
// ...
}
This is partially ok, as the JavaDoc on Converter.getAsObject() says:
/**
* #return <code>null</code> if the value to convert is <code>null</code>,
* otherwise the result of the conversion
*/
Such there is no reason for Primefaces to resolve the converter on empty input. Would be different imho if we'd set javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL = false. Then I'd consider above behavior not correct as "" is not null.
For completeness this is my bean aka myBean:
import java.io.Serializable;
import java.util.Date;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
#Named
#ViewScoped
public class MyBean implements Serializable {
private static final long serialVersionUID = 1L;
private Date someDate;
public Date getSomeDate() {
return someDate;
}
public void setSomeDate(Date someDate) {
this.someDate = someDate;
}
}
I don't want to make changes to that bean as in real application the Calendars' value is bound to persisted entity property values. I also don't want the persistence layer do the job.
The very background:
I want to prevent null values in some database DATE fields. In case of periods for example I want the start default to some far away past date not relevant in application context, for end of that period I want a date in the far future not relevant in application context and both without to bother the user which dates they are by default. These default dates help filtering entities for which the current Date is in or out of that period, while no input by the user is assumed to be virtually the period for ever.
JSF components will not call the converter on a null value or anything they considers as null - this is true for the default components in JSF as well. So in this regard I guess PrimeFaces is behaving as expected and this is the correct outcome. Here are two ways of circumventing this that instantly come to mind;
Use OmniFaces
As described in JSF converter with null value, OmniFaces o:param will call your converter regardless of a null value - you can use it to pass parameters to components. In your specific case you should be able to do something similar to this;
<h:form id="myForm">
<p:calendar value="#{myBean.someDate}" pattern="MM/dd/yyyy" locale="en" />
<p:commandButton value="send" process="#form" update="#form">
<o:param name="date" value="#{myBean.someDateConversion}"
converter="mySpecialDateConverter" />
</p:commandButton>
<hr />
<h:outputText value="#{myBean.someDate}" />
</h:form>
Use a custom renderer
The second option is to use a custom renderer and just modify the behaviour of the component to always call the converter, as described in this answer - JSF Custom Converter not called on null value.
I have two buttons, in one I need <f:convertDateTime> to work but in another I need to disable <f:convertDateTime> on button click.
I tried the attributes rendered and disabled, but it didn't work, which was my mistake as it is not available as per the API docs.
Also, is there a way to override the class javax.faces.converter.DateTimeConverter such that whenever f:convertDateTime is triggered my class will be called?
I tried the attributes rendered and disabled, but it didn't work, which was my mistake as it is not available as per the API docs.
Indeed, this behavior is not supported. However, as to a possible solution, you basically already gave the answer yourself:
Also, is there a way to override the class javax.faces.converter.DateTimeConverter such that whenever f:convertDateTime is triggered my class will be called?
That is possible and will also solve your initial problem. Just register it as <converter> in faces-config.xml on exactly the same <converter-id> as <f:convertDateTime>.
<converter>
<converter-id>javax.faces.DateTime</converter-id>
<converter-class>com.example.YourDateTimeConverter</converter-class>
</converter>
Therein you could do additional conditional checking, such as checking if a certain button is pressed, or if a certain request parameter is present or absent. If you'd like to continue the default <f:convertDateTime> job, just delegate to super provided that your converter extends from DateTimeConverter.
E.g. in getAsObject():
public class YourDateTimeConverter extends DateTimeConverter {
#Override
public void getAsObject(FacesContext context, UIComponent component, String submittedValue) {
// ...
if (yourCondition) {
// Do your preferred way of conversion here.
// ...
return yourConvertedDateTime;
} else {
// Do nothing. Just let default f:convertDateTime do its job.
return super.getAsObject(context, component, submittedValue);
}
}
// ...
}
I'm guessing you have some text displayed based on a button click (correct me if I'm wrong). So it should be something like that:
<h:commandButton value="Convert" action="#{bean.doBtnConvert}" />
<h:commandButton value="Don't convert" action="#{bean.doBtnDontConvert}" />
<h:panelGroup id="pgText">
<h:outputText value="#{bean.someDateTime}" rendered="#{bean.convert}">
<f:convertDateTime pattern="dd.MM.yyyy HH:mm:ss" />
</h:outputText>
<h:outputText value="#{bean.someDateTime}" rendered="#{not bean.convert}"> />
</h:panelGroup>
And in bean you have the following field and methods:
private Date someDate;
private boolean convert;
public String doBtnConvert(){
setConvert(true);
String viewId = FacesContext.getCurrentInstance().getViewRoot().getViewId();
return viewId + "?faces-redirect=true";
}
public String doBtnDontConvert(){
setConvert(false);
String viewId = FacesContext.getCurrentInstance().getViewRoot().getViewId();
return viewId + "?faces-redirect=true";
}
// getter and setter for 'someDate' and 'convert' fields
I asked about pass through attributes in a different question and found I could create a custom renderer for the <p:autocomplete> component but the problem is my custom renderer would be used for every p:autocomplete in my project (site-wide). Therefore I have elected to create a custom component which extends org.primefaces.component.autocomplete.AutoComplete and adds the necessary attributes to the text box.
My initial thought was to add a constructor but it doesn't seem to work because the attribute map is null at this point:
#FacesComponent("com.mycomponents.SiteSearch")
public class SiteSearch extends AutoComplete {
public SiteSearch() {
Map<String,Object> attrs = getAttributes();
attrs.put("x-webkit-speech", null);
attrs.put("x-webkit-grammer", "builtin:search");
attrs.put("onwebkitspeechchange", "this.form.submit();");
attrs.put("placeholder", "Enter a Search Term");
}
}
My other thought was leave this custom component empty (empty class) and then specify a custom renderer that extends org.primefaces.component.autocomplete.AutoCompleteRenderer and modify the attributes there.
After all is said and done, I just need a way to keep these attributes separate to this one text box so just putting a custom renderer on the p:autoComplete is not going to work (unless maybe I can use renderType= attribute for this one p:autoComplete?).
If you need a specific component which uses a different renderer than <p:autoComplete> then you really can't go around creating a custom component with its own family and component type. You can still just extend the PrimeFaces AutoComplete (and its renderer) to save some boilerplate code.
In the custom component, you need to provide getters for those attributes. You could as good specify setters as well, this way you can always override the default values from in the view side. Those getters/setters should in turn delegate to StateHelper.
There's only a little problem with x-webkit-* attributes. The - is an illegal character in Java identifiers. So you have to rename the getters/setters and change the renderer somewhat as the standard renderer relies on the component property name being exactly the same as the tag attribute name. Update: I understand that x-webkit-speech should just be rendered as is (so, no getter/setter necessary) and that x-webkit-grammer is actually a typo, it should be x-webkit-grammar.
Here's how the SiteSearch component can look like:
#FacesComponent(SiteSearch.COMPONENT_TYPE)
public class SiteSearch extends AutoComplete {
public static final String COMPONENT_FAMILY = "com.example";
public static final String COMPONENT_TYPE = "com.example.SiteSearch";
private enum PropertyKeys {
grammar, onspeechchange, placeholder
}
#Override
public String getFamily() {
return COMPONENT_FAMILY;
}
#Override
public String getRendererType() {
return SiteSearchRenderer.RENDERER_TYPE;
}
public String getGrammar() {
return (String) getStateHelper().eval(PropertyKeys.grammar, "builtin:search");
}
public void setGrammar(String grammar) {
getStateHelper().put(PropertyKeys.grammar, grammar);
}
public String getOnspeechchange() {
return (String) getStateHelper().eval(PropertyKeys.onspeechchange, "submit()");
}
public void setOnspeechchange(String onspeechchange) {
getStateHelper().put(PropertyKeys.onspeechchange, onspeechchange);
}
public String getPlaceholder() {
return (String) getStateHelper().eval(PropertyKeys.placeholder, "Enter a Search Term");
}
public void setPlaceholder(String placeholder) {
getStateHelper().put(PropertyKeys.placeholder, placeholder);
}
}
Please note that the getters have all default values specified. If the eval() returns null, then the default value will be returned instead. I have also neutralized the attribute names somewhat so that it can be reused for any future non-webkit browsers by just modifying the renderer accordingly.
And here's how the SiteSearchRenderer renderer should look like for the above component:
#FacesRenderer(
componentFamily=SiteSearch.COMPONENT_FAMILY,
rendererType=SiteSearchRenderer.RENDERER_TYPE
)
public class SiteSearchRenderer extends AutoCompleteRenderer {
public static final String RENDERER_TYPE = "com.example.SiteSearchRenderer";
#Override
protected void renderPassThruAttributes(FacesContext facesContext, UIComponent component, String[] attrs) throws IOException {
ResponseWriter writer = facesContext.getResponseWriter();
writer.writeAttribute("x-webkit-speech", "x-webkit-speech", null);
writer.writeAttribute("x-webkit-grammar", component.getAttributes().get("grammar"), "grammar");
writer.writeAttribute("onwebkitspeechchange", component.getAttributes().get("onspeechchange"), "onspeechchange");
writer.writeAttribute("placeholder", component.getAttributes().get("placeholder"), "placeholder");
super.renderPassThruAttributes(facesContext, component, attrs);
}
}
To use it in the view, we of course need to register it as a tag. Create a /WEB-INF/my.taglib.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
version="2.0"
>
<namespace>http://example.com/ui</namespace>
<tag>
<tag-name>siteSearch</tag-name>
<component>
<component-type>com.example.SiteSearch</component-type>
<renderer-type>com.example.SiteSearchRenderer</renderer-type>
</component>
</tag>
</facelet-taglib>
Note that you don't need a <renderer> in faces-config.xml for this anymore. The #FacesRenderer annotation can just do its job on real custom components. So remove the <renderer> entry in faces-config.xml which you created based on your previous question.
Now tell JSF that you've got a custom taglib by the following context param in web.xml:
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>
Finally you can use it as follows:
<html ... xmlns:my="http://example.com/ui">
...
<my:siteSearch />
You can even specify additional attributes which will override the defaults set in the component:
<my:siteSearch grammar="builtin:language" onspeechchange="alert('peek-a-boo')" placeholder="Search" />
For IDE autocomplete on attributes, you'd need to specify every one as a separate <attribute> in the <tag> declaration in the my.taglib.xml.
I am a green bird in JSF, now I was puzzled by the text of it.
when I assign the value of the outputText with multi-spaces, the result shown in IE has only one space.
ex: the code is like
<h:outputText id="name" value="aa (multi-spaces here) bbb" ></h:outputText>
the result text shown in IE is "aa bbb"
can anyone tell me why the spaces disappear without trace?
This behaviour is defined by the HTML spec:
For all HTML elements except PRE, sequences of white space separate "words" (we use the term "word" here to mean "sequences of non-white space characters"). When formatting text, user agents should identify these words and lay them out according to the conventions of the particular written language (script) and target medium.
Note that if you are using XHTML, there are some differences in how attributes and the code point U+000C are handled.
For most text, sequences of white space are not rendered any differently from a single space.
Since this is for a outputText control, you can use a one-way converter for a no-break space solution:
package myconverters;
// imports
public class SpacePreserver implements Converter {
private static final char NO_BREAK_SPACE = '\u00A0';
public String getAsString(FacesContext context, UIComponent component,
Object value) {
if (component instanceof EditableValueHolder) {
throw new IllegalArgumentException(
"Cannot use SpacePreserver converter on editable controls.");
}
return value == null ? null : value.toString().replace(' ', NO_BREAK_SPACE);
}
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
throw new UnsupportedOperationException("Output converter only");
}
}
This can be defined (among other ways) using a faces-config.xml entry:
<converter>
<converter-id>spacePreserver</converter-id>
<converter-class>myconverters.SpacePreserver</converter-class>
</converter>
You can then add this to your output control:
<h:outputText id="text1" value="a b c" converter="spacePreserver" />
This code was tested using JSF 1.1 with a UTF-8 encoded JSP 2.0 view. Note that use of a no-break space will prohibit line-wrapping.
I want to embed a link in a JSF message, is this possible?
When I try it, the rendered html of the h:messages tag escapes the html characters. I tried setting the escape attribute of the h:messages tag to false, but that didn't help.
Unfortunately, this is not possible in the standard JSF implementation. The component and the renderer doesn't officially support this attribute. You can however homegrow a renderer which handles this.
Since this is a pretty common requirement/wish, I thought to take a look what's all possible.
First some background information: JSF by default uses ResponseWriter#writeText() to write the tag body, which escapes HTML by default. We'd like to let it use ResponseWriter#write() instead like as with <h:outputText escape="false" />. We'd like to extend the MessagesRenderer of the standard JSF implementation and override the encodeEnd() method accordingly. But since the MessagesRenderer#encodeEnd() contains pretty a lot of code (~180 lines) which we prefer not to copypaste to just change one or two lines after all, I found it better to replace the ResponseWriter with a custom implementation with help of ResponseWriterWrapper wherein the writeText() is been overriden to handle the escaping.
So, I ended up with this:
package com.example;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.context.ResponseWriterWrapper;
import javax.faces.render.FacesRenderer;
import com.sun.faces.renderkit.html_basic.MessagesRenderer;
#FacesRenderer(componentFamily="javax.faces.Messages", rendererType="javax.faces.Messages")
public class EscapableMessagesRenderer extends MessagesRenderer {
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
final ResponseWriter originalResponseWriter = context.getResponseWriter();
try {
context.setResponseWriter(new ResponseWriterWrapper() {
#Override
public ResponseWriter getWrapped() {
return originalResponseWriter;
}
#Override
public void writeText(Object text, UIComponent component, String property) throws IOException {
String string = String.valueOf(text);
String escape = (String) component.getAttributes().get("escape");
if (escape != null && !Boolean.valueOf(escape)) {
super.write(string);
} else {
super.writeText(string, component, property);
}
}
});
super.encodeEnd(context, component); // Now, render it!
} finally {
context.setResponseWriter(originalResponseWriter); // Restore original writer.
}
}
}
In spite of the #FacesRenderer annotation, it get overriden by the default MessagesRenderer implementation. I suspect here a bug, so I reported issue 1748. To get it to work anyway, we have to fall back to the faces-config.xml:
<render-kit>
<renderer>
<component-family>javax.faces.Messages</component-family>
<renderer-type>javax.faces.Messages</renderer-type>
<renderer-class>com.example.EscapableMessagesRenderer</renderer-class>
</renderer>
</render-kit>
Then, to trigger it, just do:
<h:messages escape="false" />
And it works! :)
Note: the above affects <h:messages> only. To do the same for <h:message>, just do the same, but replace anywhere "Messages" by "Message" (component family, renderer type and classnames).
The escape="false" attributed you need is provided by the OmniFaces <o:messages> component. The OmniFaces utility library is available for JSF 2.
I posted this solution mentioned by #BalusC's comment as an answer since this is the most straightforward solution.