/* * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Thomas Roger */ package org.nuxeo.ecm.platform.ui.web.component.message; import java.io.IOException; import java.util.Iterator; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.component.UIMessages; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.event.AbortProcessingException; import javax.faces.event.ComponentSystemEvent; import javax.faces.event.ComponentSystemEventListener; import javax.faces.event.ListenerFor; import javax.faces.event.PostAddToViewEvent; import org.apache.commons.lang.StringEscapeUtils; import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; import org.nuxeo.runtime.api.Framework; import com.sun.faces.renderkit.html_basic.MessagesRenderer; /** * Handles rendering of {@link javax.faces.application.FacesMessage} through jQuery Ambiance plugin. * * @since 5.7.3 */ @ListenerFor(systemEventClass = PostAddToViewEvent.class) public class NXMessagesRenderer extends MessagesRenderer implements ComponentSystemEventListener { public static final String RENDERER_TYPE = "javax.faces.NXMessages"; @Override public void encodeEnd(FacesContext context, UIComponent component) throws IOException { rendererParamsNotNull(context, component); if (!shouldEncode(component)) { return; } // If id is user specified, we must render boolean mustRender = shouldWriteIdAttribute(component); UIMessages messages = (UIMessages) component; ResponseWriter writer = context.getResponseWriter(); assert(writer != null); String clientId = ((UIMessages) component).getFor(); // if no clientId was included if (clientId == null) { // and the author explicitly only wants global messages if (messages.isGlobalOnly()) { // make it so only global messages get displayed. clientId = ""; } } // "for" attribute optional for Messages Iterator<?> messageIter = getMessageIter(context, clientId, component); assert(messageIter != null); if (!messageIter.hasNext()) { if (mustRender) { // no message to render, but must render anyway // but if we're writing the dev stage messages, // only write it if messages exist if ("javax_faces_developmentstage_messages".equals(component.getId())) { return; } writer.startElement("div", component); writeIdAttributeIfNecessary(context, writer, component); writer.endElement("div"); } // otherwise, return without rendering return; } boolean showDetail = messages.isShowDetail(); while (messageIter.hasNext()) { FacesMessage curMessage = (FacesMessage) messageIter.next(); if (curMessage.isRendered() && !messages.isRedisplay()) { continue; } curMessage.rendered(); // make sure we have a non-null value for summary and // detail. String summary = (null != (summary = curMessage.getSummary())) ? summary : ""; // Default to summary if we have no detail String detail = (null != (detail = curMessage.getDetail())) ? detail : summary; String severityStyleClass = null; String errorType = "default"; long timeout = 5; if (curMessage.getSeverity() == FacesMessage.SEVERITY_INFO) { severityStyleClass = (String) component.getAttributes().get("infoClass"); errorType = "info"; } else if (curMessage.getSeverity() == FacesMessage.SEVERITY_WARN) { severityStyleClass = (String) component.getAttributes().get("warnClass"); errorType = "warn"; } else if (curMessage.getSeverity() == FacesMessage.SEVERITY_ERROR) { severityStyleClass = (String) component.getAttributes().get("errorClass"); errorType = "error"; timeout = 0; } else if (curMessage.getSeverity() == FacesMessage.SEVERITY_FATAL) { severityStyleClass = (String) component.getAttributes().get("fatalClass"); errorType = "fatal"; timeout = 0; } // ensure message stays visible when running tests if (Framework.getProperty("org.nuxeo.ecm.tester.name") != null) { timeout = 0; } writer.startElement("script", messages); writer.writeAttribute("type", "text/javascript", null); String message = ""; if (showDetail) { message = detail; } String scriptContent = new StringBuilder().append("jQuery(document).ready(function() {\n") .append(" jQuery.ambiance({\n") .append(" message: \"") .append(StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeHtml(message))) .append("\",\n") .append(" title: \"") .append(StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeHtml(summary))) .append("\",\n") .append(" type: \"") .append(errorType) .append("\",\n") .append(" className: \"") .append(severityStyleClass) .append("\",\n") .append(" timeout: \"") .append(timeout) .append("\"") .append(" })\n") .append("});\n") .toString(); writer.writeText(scriptContent, null); writer.endElement("script"); } } /* * When this method is called, we know that there is a component with a script renderer somewhere in the view. We * need to make it so that when an element with a name given by the value of the optional "target" component * attribute is encountered, this component can be called upon to render itself. This method will add the component * (associated with this Renderer) to a facet in the view only if a "target" component attribute is set. */ public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { UIComponent component = event.getComponent(); if (ComponentUtils.isRelocated(component)) { return; } String target = verifyTarget((String) component.getAttributes().get("target")); if (target != null) { ComponentUtils.relocate(component, target, null); } } protected String verifyTarget(String toVerify) { return ComponentUtils.verifyTarget(toVerify, toVerify); } }