/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.core.gui.render;
import org.olat.core.dispatcher.impl.StaticMediaDispatcher;
import org.olat.core.gui.GlobalSettings;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.ComponentCollection;
import org.olat.core.gui.components.ComponentRenderer;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.render.intercept.InterceptHandlerInstance;
import org.olat.core.gui.translator.Translator;
import org.olat.core.helpers.Settings;
import org.olat.core.logging.AssertException;
import org.olat.core.util.WebappHelper;
/**
* @author Felix Jost
*/
public class Renderer {
private final URLBuilder urlBuilder;
private final Translator translator;
private final ComponentCollection renderContainer;
private final RenderResult renderResult;
private final GlobalSettings globalSettings;
/**
* @param renderContainer is used as a starting node for searching a component
* @param translator the translator to be used to translate
* @param ubu
* @param renderResult
* @param globalSettings
* @return an instance of the renderer
*/
public static Renderer getInstance(ComponentCollection renderContainer, Translator translator, URLBuilder ubu, RenderResult renderResult,
GlobalSettings globalSettings) {
return new Renderer(renderContainer, translator, ubu, renderResult, globalSettings);
}
private Renderer(ComponentCollection renderContainer, Translator translator, URLBuilder ubu, RenderResult renderResult,
GlobalSettings globalSettings) {
this.renderContainer = renderContainer;
this.translator = translator;
this.urlBuilder = ubu;
this.renderResult = renderResult;
this.globalSettings = globalSettings;
}
/**
* Note: use only rarely - e.g. for static redirects to login screen. Renders
* a uri which is mounted to the webapp/ directory of your webapplication.
* <p>
* For static references (e.g. images which cannot be delivered using css):
* use renderStaticURI
*
* @param target
* @param URI e.g. myspecialdispatcher/somestuff
*/
public static void renderNormalURI(StringOutput target, String URI) {
String root = WebappHelper.getServletContextPath();
target.append(root); // e.g /olat
target.append("/");
target.append(URI);
}
/**
* Note: use only rarely - all non-generic js libs and css classes
* should be included using JsAndCssComponent, and all images
* should be referenced with the css background-image capability.
* <br>
* renders a uri which is mounted to the webapp/static/ directory of your webapplication.
*
* @param target
* @param URI e.g. img/specialimagenotpossiblewithcss.jpg
*/
public static void renderStaticURI(StringOutput target, String URI) {
// forward to static dispatcher that knows how to deliver the static files!
StaticMediaDispatcher.renderStaticURI(target, URI);
}
/**
* please do not use anymore!
* @param target
* @param command
*/
public void renderCommandURI(StringOutput target, String command) {
urlBuilder.buildURI(target, new String[] { VelocityContainer.COMMAND_ID }, new String[] { command });
}
/**
* renders the HTMLHeader-Part which this component comp needs. e.g. the
* richtext component needs some css and javascript libraries an
* velocity-container is a special case: it should collect the information
* from all the children that are visible since all could be renderer. since
* the actual rendering of a component depends on the page and is not know
* beforehand, we could include some css/js which turns out not to be needed
* in this request, but it is cached by the browser anyway, so it should not
* matter to much. a little advice if you want to do it perfectly : program
* the controller in such a way that they make a component invisible if not
* needed
*
* @param sb
* @param source
* @see org.olat.core.gui.render.ui.ComponentRenderer
*/
public void renderBodyOnLoadJSFunctionCall(StringOutput sb, Component source, RenderingState rstate) {
if (source != null && source.isVisible()) {
ComponentRenderer cr = findComponentRenderer(source);
cr.renderBodyOnLoadJSFunctionCall(this, sb, source, rstate);
}
}
/**
* to be called by VelocityRenderDecorator only
* @param sb
* @param source
*/
public void renderBodyOnLoadJSFunctionCall(StringOutput sb, Component source) {
RenderingState rstate = new RenderingState();
renderBodyOnLoadJSFunctionCall(sb, source, rstate);
}
/**
* @param sb
* @param source
*/
public void renderHeaderIncludes(StringOutput sb, Component source, RenderingState rstate) {
if (source != null && source.isVisible()) {
ComponentRenderer cr = findComponentRenderer(source);
URLBuilder cubu = urlBuilder.createCopyFor(source);
cr.renderHeaderIncludes(this, sb, source, cubu, translator, rstate);
}
}
public void renderHeaderIncludes(StringOutput sb, Component source) {
RenderingState rstate = new RenderingState();
renderHeaderIncludes(sb, source, rstate);
}
/**
* searches the rootcomponent of this renderer and all children recursively
* for a component by its name
*
* @param componentName
* @return
*/
public Component findComponent(String componentName) {
Component source = renderContainer.getComponent(componentName);
return source;
}
/**
* used by velocityrenderdecorator and method render(component) above
* @param source
* @param args
* @return
*/
public void render(Component source, StringOutput sb, String[] args) {
render(sb, source, args);
}
/**
* used by the renderer, and also by the panel and tabbedpane renderer to delegate rendering
* @param sb
* @param source
* @param args
*/
public void render(StringOutput sb, Component source, String[] args) {
GlobalSettings gset = getGlobalSettings();
boolean ajaxon = gset.getAjaxFlags().isIframePostEnabled();
// wrap with div's so javascript can replace this component by doing a document.getElementById(cid).innerHTML and so on.
boolean domReplaceable = source.isDomReplaceable();
boolean useSpan = source.getSpanAsDomReplaceable();
boolean domReplacementWrapperRequired = source.isDomReplacementWrapperRequired();
boolean forceDebugDivs = gset.isIdDivsForced();
if (source.isVisible()) {
int lev = renderResult.getNestedLevel();
if (lev > 60) throw new AssertException("components were nested more than 60 times, assuming endless loop bug: latest comp name: "+source.getComponentName());
Translator componentTranslator = source.getTranslator();
// for ajax mode: render surrounding divs or spans as a positional
// identifier for dom replacement
if (domReplaceable && domReplacementWrapperRequired && (ajaxon || forceDebugDivs)) {
if (useSpan) {
sb.append("<span id='o_c").append(source.getDispatchID());
} else {
sb.append("<div id='o_c").append(source.getDispatchID());
}
if(Settings.isDebuging()) {
//sb.append("' title='").append(source.getComponentName());
}
sb.append("'>");
}
ComponentRenderer cr = findComponentRenderer(source);
URLBuilder cubu = urlBuilder.createCopyFor(source);
renderResult.incNestedLevel();
// ---- for gui debug mode, direct the rendering to a special componentrenderer
InterceptHandlerInstance dhi = renderResult.getInterceptHandlerInstance();
if (dhi != null) {
cr = dhi.createInterceptComponentRenderer(cr);
}
try {
int preRenderLength = sb.length();
cr.render(this, sb, source, cubu, componentTranslator, renderResult, args);
if (preRenderLength == sb.length()) {
// Add bugfix for IE min-height on empty div problem: min-height does
// not get applied when div contains an empty comment.
// Affects IE6, IE7
sb.append("<!-- empty -->");
}
source.setDirty(false);
} catch (Exception e) {
// in order to produce a decent error msg, we need to postpone the
// exception
renderResult.setRenderExceptionInfo("exception while rendering component '" + source.getComponentName() + "' ("
+ source.getClass().getName() + ") " + source.getListenerInfo() + "<br />Message of exception: " + e.getMessage(), e);
}
renderResult.decNestedLevel();
// close div for the javascript dom replacement
if (ajaxon && domReplaceable && domReplacementWrapperRequired) {
if(useSpan){
sb.append("</span>");
} else{
sb.append("</div>");
}
}
} else { // not visible
if (domReplaceable && (ajaxon || forceDebugDivs)) {
// render empty div's (or spans in special cases) as a place holder since this component may
// be set to visible later on, and then needs to know its place in the
// browser dom tree
if (useSpan) {
sb.append("<span id=\"o_c").append(source.getDispatchID()).append("\"></span>");
} else {
// Add bugfix for IE min-height on empty div problem: min-height does
// not get applied when div contains an empty comment.
// Affects IE6, IE7
sb.append("<div id=\"o_c").append(source.getDispatchID()).append("\"><!-- empty --></div>");
}
}
}
}
private ComponentRenderer findComponentRenderer(Component toRender) {
return toRender.getHTMLRendererSingleton();
}
/**
* returns e.g. "o_c123" where 123 is the component id
* @param comp the component to get the id
*/
public static String getComponentPrefix(Component comp) {
String did = comp.getDispatchID();
return "o_c".concat(did);
}
/**
* @return URLBuilder
*/
public URLBuilder getUrlBuilder() {
return urlBuilder;
}
/**
* @return Translator
*/
public Translator getTranslator() {
return translator;
}
/**
* @return
*/
public GlobalSettings getGlobalSettings() {
return globalSettings;
}
/**
* @return the current uri prefix, e.g. /demo/go (=the prefix for the window just being rendered)
*/
public String getUriPrefix() {
return urlBuilder.getUriPrefix();
}
}