/**
* 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.components.link;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringEscapeUtils;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.DefaultComponentRenderer;
import org.olat.core.gui.components.form.flexible.impl.Form;
import org.olat.core.gui.components.form.flexible.impl.FormJSHelper;
import org.olat.core.gui.components.form.flexible.impl.NameValuePair;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.winmgr.AJAXFlags;
import org.olat.core.gui.render.RenderResult;
import org.olat.core.gui.render.Renderer;
import org.olat.core.gui.render.StringOutput;
import org.olat.core.gui.render.URLBuilder;
import org.olat.core.gui.translator.Translator;
import org.olat.core.util.StringHelper;
/**
* Description: Renders the link component depending of features and style.
* Use {@link LinkFactory} to create {@link Link} objects.
*
*/
public class LinkRenderer extends DefaultComponentRenderer {
private static final Pattern singleQuote = Pattern.compile("\'");
private static final Pattern doubleQutoe = Pattern.compile("\"");
@Override
public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator,
RenderResult renderResult, String[] args) {
Link link = (Link) source;
String command = link.getCommand();
AJAXFlags flags = renderer.getGlobalSettings().getAjaxFlags();
boolean iframePostEnabled = flags.isIframePostEnabled() && link.isAjaxEnabled() && link.getTarget() == null; // a link may force a non ajax-mode and a custom targ
int presentation = link.getPresentation();
/*
* START && beware! order of this if's are relevant
*/
boolean flexiformlink = (presentation - Link.FLEXIBLEFORMLNK) >= 0;
if (flexiformlink) {
presentation = presentation - Link.FLEXIBLEFORMLNK;
}
boolean nontranslated = (presentation - Link.NONTRANSLATED) >= 0;
if (nontranslated) {
presentation = presentation - Link.NONTRANSLATED;
}
/*
* END && beware! order of this if's are relevant
*/
StringBuilder cssSb = new StringBuilder("");
cssSb.append("class=\"");
if (!link.isEnabled()) {
cssSb.append(" o_disabled ");
}
if (link.isActive()) {
cssSb.append(" active ");
}
if (presentation == Link.BUTTON_XSMALL) {
cssSb.append("btn btn-xs ");
cssSb.append(link.isPrimary() ? "btn-primary" : "btn-default");
} else if (presentation == Link.BUTTON_SMALL) {
cssSb.append("btn btn-sm ");
cssSb.append(link.isPrimary() ? "btn-primary" : "btn-default");
} else if (presentation == Link.BUTTON) {
cssSb.append("btn ");
cssSb.append(link.isPrimary() ? "btn-primary" : "btn-default");
} else if (presentation == Link.BUTTON_LARGE) {
cssSb.append("btn btn-lg ");
cssSb.append(link.isPrimary() ? "btn-primary" : "btn-default");
} else if (presentation == Link.LINK_BACK) {
cssSb.append("o_link_back");
} else if (presentation == Link.TOOLENTRY_DEFAULT) {
cssSb.append("o_toolbox_link");
} else if (presentation == Link.TOOLENTRY_CLOSE) {
cssSb.append("o_toolbox_close");
} else if (presentation == Link.LINK_CUSTOM_CSS) {
String customCss = ( link.isEnabled() ? link.getCustomEnabledLinkCSS() : link.getCustomDisabledLinkCSS() );
cssSb.append( customCss == null ? "" : customCss );
}
if(StringHelper.containsNonWhitespace(link.getElementCssClass())) {
cssSb.append(" ").append(link.getElementCssClass());
}
cssSb.append("\"");
if (link.isEnabled()) {
// only set a target on an enabled link, target in span makes no sense
if (link.getTarget() != null){
cssSb.append(" target=\"").append(link.getTarget()).append("\"");
}
}
String elementId = link.getElementId();
// String buffer to gather all Javascript stuff with this link
// there is a var elementId = jQuery('#elementId');
// allowing to reference the link as an Ext.Element
// Optimize initial length based on heuristic measurements of extJsSb
StringBuilder jsSb = new StringBuilder(240);
boolean inForm = isInForm(args);
String i18n = link.getI18n();
String title = link.getTitle();
String customDisplayText = link.getCustomDisplayText();
// a form link can not have tooltips at the moment
// tooltip sets its own id into the <a> tag.
if (link.isEnabled()) {
sb.append("<p class='form-control-static'>", inForm)
.append("<a ")
// add layouting
.append(cssSb);
//REVIEW:pb elementId is not null if it is a form link
//the javascript handler and the link.registerForMousePositionEvent
//need also access to a created and id set. -> avoid "o_c"+link.getDispatchID()
if (elementId != null) {
sb.append(" id=\"").append(elementId).append("\" ");
}
String accessKey = link.getAccessKey();
if (accessKey != null) {
sb.append("accesskey=\"").append(accessKey).append("\" ");
}
if (flexiformlink) {
//no target if flexi form link! because target is set on
//post action of form
Form theForm = (Form)link.getInternalAttachedObject();
sb.append("href=\"javascript:")
.append(FormJSHelper.getJSFnCallFor(theForm, elementId, 1))
.append(";\" ");
if(link.isForceFlexiDirtyFormWarning()) {
sb.append("onclick=\"return o2cl_dirtyCheckOnly();\" ");
}
} else if(link.isPopup()) {
StringOutput href = new StringOutput();
LinkPopupSettings popup = link.getPopup();
ubu.buildURI(href, new String[] { VelocityContainer.COMMAND_ID }, new String[] { command }, null, AJAXFlags.MODE_NORMAL);
sb.append("href=\"javascript:;\" onclick=\"o_openPopUp('").append(href).append("','")
.append(popup.getTarget()).append("',").append(popup.getWidth())
.append(",").append(popup.getHeight()).append("); return false;\" ");
} else {
ubu.buildHrefAndOnclick(sb, null, iframePostEnabled, !link.isSuppressDirtyFormWarning(), true,
new NameValuePair(VelocityContainer.COMMAND_ID, command));
}
//tooltips
if(title != null) {
if (!link.isHasTooltip()) {
sb.append(" title=\"");
if (nontranslated){
sb.append(StringEscapeUtils.escapeHtml(title)).append("\"");
} else {
sb.append(StringEscapeUtils.escapeHtml(translator.translate(title))).append("\"");
}
}
//tooltips based on the extjs library, see webapp/static/js/ext*
if (link.isHasTooltip()) {
String text;
if (nontranslated) {
text = title;
} else {
text = translator.translate(title);
}
sb.append(" title=\"").append(StringEscapeUtils.escapeHtml(text)).append("\"");
}
}
sb.append(">");
// CSS icon
if (link.getIconLeftCSS() != null) {
sb.append("<i class='").append(link.getIconLeftCSS()).append("'");
sb.append("></i> "); // one space needed
} else if (presentation == Link.LINK_BACK) {
sb.append("<i class='o_icon o_icon_back'></i> "); // one space needed
}
sb.append("<span>"); // inner wrapper for layouting
if (customDisplayText != null) {
//link is nontranslated but has custom text
sb.append(customDisplayText);
} else if (nontranslated) {
if (i18n != null) {
// link name is not a i18n key
sb.append(i18n);
} else {
sb.append("");
}
} else {
// use translator
if(translator == null) {
sb.append("Ohoho");
} else {
sb.append(translator.translate(i18n));
}
}
sb.append("</span>");
// CSS icon
if (link.getIconRightCSS() != null) {
sb.append(" <i class='").append(link.getIconRightCSS()).append("'"); // one space needed
sb.append("></i> ");
}
if(link.getBadge() != null) {
renderer.render(link.getBadge(), sb, args);
}
sb.append("</a>").append("</p>", inForm);
//on click() is part of prototype.js
if(link.isRegisterForMousePositionEvent()) {
jsSb.append(elementId).append(".click(function(event) {")
.append(" jQuery('#").append(elementId).append("').each(function(index, el) {;")
.append(" var href = jQuery(el).attr('href');")
.append(" if(href.indexOf('x') == -1) jQuery(el).attr('href',href+'x'+event.pageX+'y'+event.pageY+'');")
.append(" });});");
}
/**
* TODO:gs:b may be usefull as well
* this binds the event to the function call as argument, usefull if event is needed
* Event.observe("id", "click", functionName.bindAsEventListener(this));
*/
if(link.getJavascriptHandlerFunction() != null) {
jsSb.append(elementId).append(".on('").append(link.getMouseEvent()).append("', ").append(link.getJavascriptHandlerFunction()).append(");");
}
/**
* Focus link so that it can be invoked using the enter key using a keyboard.
*/
if(link.isFocus()) {
jsSb.append(elementId).append(".focus();");
}
} else {
String text;
if (customDisplayText != null) {
//link is nontranslated but has custom text
text = customDisplayText;
} else if (nontranslated) {
// link name is not a i18n key
text = (i18n == null ? "" : i18n);
} else {
text = translator.translate(i18n);
}
sb.append("<a ");
if (elementId != null) sb.append(" id=\"").append(elementId).append("\" ");
String description = link.getTextReasonForDisabling();
// fallback to title
if (description == null) description = link.getTitle();
if (description != null) {
Matcher msq = singleQuote.matcher(description);
description = msq.replaceAll("'");
Matcher mdq = doubleQutoe.matcher(description);
description = mdq.replaceAll("\\\\\"");
sb.append(" title=\"").append(description).append("\" ");
}
sb.append(cssSb).append(" href='#' onclick='return false;'>");
// CSS icon
if (link.getIconLeftCSS() != null) {
sb.append("<i class='").append(link.getIconLeftCSS()).append("'");
sb.append("></i> "); // one space needed
}
sb.append("<span>").append(text).append("</span>");
// CSS icon
if (link.getIconRightCSS() != null) {
sb.append(" <i class='").append(link.getIconRightCSS()).append("'"); // one space needed
sb.append("></i> ");
}
sb.append("</a>");
}
if(link.getTarget() != null){
//if the link starts a download -> the o_afterserver is not called in
//non-ajax mode if a download is started.
//on click execute the "same" javascript as in o_ainvoke(r,true) for
//case 3:
jsSb.append("if (").append(elementId).append(") ")
.append(elementId).append(".click(function() {setTimeout(removeBusyAfterDownload,1200)});");
}
//disabled or not, all tags should be closed here
//now append all gathered javascript stuff if any
if(jsSb.length() > 0) {
// Execute code within an anonymous function (closure) to not leak
// variables to global scope (OLAT-5755)
sb.append(" <script type=\"text/javascript\">\n/* <![CDATA[ */\n")
.append("(function(){ var ").append(elementId).append(" = jQuery('#").append(elementId).append("');")
.append(jsSb).append("})();")
.append("\n/* ]]> */\n</script>");
}
}
private final boolean isInForm(String[] args) {
boolean embedded = false;
if(args != null && args.length > 0) {
for(String arg:args) {
if("form".equals(arg)) {
embedded = true;
}
}
}
return embedded;
}
}