Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embedding a link (or other html) in a JSF message

Tags:

jsf

jsf-2

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.

like image 211
Brian Leathem Avatar asked Jul 28 '10 19:07

Brian Leathem


2 Answers

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).

like image 162
BalusC Avatar answered Oct 10 '22 04:10

BalusC


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.

like image 23
Adam Avatar answered Oct 10 '22 05:10

Adam