/** * 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 org.olat.core.gui.UserRequest; import org.olat.core.gui.components.AbstractComponent; import org.olat.core.gui.components.ComponentEventListener; import org.olat.core.gui.components.ComponentRenderer; import org.olat.core.gui.components.badge.Badge; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Event; import org.olat.core.logging.AssertException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; /** * Description:<br> * Use @see {@link LinkFactory} to get Link objects. * <P> * Initial Date: July 06, 2006 <br> * * @author Alexander Schneider, Patrick Brunner */ public class Link extends AbstractComponent { private static final OLog log = Tracing.createLoggerFor(Link.class); //single renderer for all users, lazy creation upon first object creation of this class. private static final ComponentRenderer RENDERER = new LinkRenderer(); /** * each can be combined with {@link Link#NONTRANSLATED}<br> * can not be combined with each other! */ public static final int LINK_CUSTOM_CSS = 0; public static final int BUTTON_XSMALL = 1; public static final int BUTTON_SMALL = 2; public static final int BUTTON = 3; public static final int BUTTON_LARGE = 4; public static final int LINK_BACK = 5; public static final int LINK = 6; /** * to be refactored later into own components */ public static final int TOOLENTRY_DEFAULT = 7; public static final int TOOLENTRY_CLOSE = 8; /** * can be added to one of the following: * */ public static final int NONTRANSLATED = 16; public static final int FLEXIBLEFORMLNK = 32; private String command; private int presentation; private int presentationBeforeCustomCSS; private boolean primary; private boolean focus; private String i18n; private String title; private String elementId; private String textReasonForDisabling; private String customDisplayText; private String customEnabledLinkCSS; private String customDisabledLinkCSS; private String iconLeftCSS; private String iconRightCSS; private String target; private Object internalAttachedObj; private Object userObject; private String accessKey; private boolean active = false; private boolean ajaxEnabled = true; private boolean registerForMousePositionEvent = false; private MouseEvent mouseEvent; private String javascriptHandlerFunction; //x y coordinates of the mouse position when clicked the link, works only if enabled by registerForMousePositionEvent(true) private int offsetX = 0; private int offsetY = 0; private boolean hasTooltip; private boolean suppressDirtyFormWarning = false; private boolean forceFlexiDirtyFormWarning = false; private Badge badge; private LinkPopupSettings popup; /** * * @param name * @param command should not contain : * @param i18n * @param presentation * @param title */ protected Link(String name, String command, String i18n, int presentation, VelocityContainer vc, ComponentEventListener listeningController) { this(null, name, command, i18n, presentation, vc, listeningController); } /** * Same as but with fix ID * @param id A fix identifier for the link, must be unique or null * @param name * @param command * @param i18n * @param presentation * @param vc * @param listeningController */ protected Link(String id, String name, String command, String i18n, int presentation, VelocityContainer vc, ComponentEventListener listeningController) { this(id, name, command, i18n, presentation, null); if (listeningController == null) throw new AssertException("please provide a listening controller, listeningController is null at the moment"); addListener(listeningController); if (vc != null) vc.put(getComponentName(), this); setSpanAsDomReplaceable(true); } /** * the new flexible forms needs links where the VC and listener are unknown at construction time. * @param id A fix identifier for the link, must be unique or null * @param name * @param command * @param i18n * @param presentation * @param internalAttachedObj */ protected Link(String id, String name, String command, String i18n, int presentation, Object internalAttachedObj){ super(id, name); this.internalAttachedObj = internalAttachedObj; this.command = command; if ( this.command == null || this.command.equals("")) throw new AssertException("command string must be a valid string and not null"); this.i18n = i18n; this.presentation = presentation; this.presentationBeforeCustomCSS = presentation; if(log.isDebug()){ log.debug("***LINK_CREATED***" + " name: " + getComponentName() + " component: " + getComponentName() + " dispatchId: " + getDispatchID()); } // use span wrappers - if the custom layout needs div wrappers this flag has // to be set manually setSpanAsDomReplaceable(true); // Directly use the dispatch ID for DOM replacement to minimize DOM tree setElementId("o_c" + getDispatchID()); setDomReplacementWrapperRequired(false); } /** * @see org.olat.core.gui.components.Component#dispatchRequest(org.olat.core.gui.UserRequest) */ protected void doDispatchRequest(UserRequest ureq) { setDirty(true); String cmd = ureq.getParameter(VelocityContainer.COMMAND_ID); if(log.isDebug()){ log.debug("***LINK_CLICKED*** " + " dispatchID: " + ureq.getComponentID() + " commandID: " + cmd); } dispatch(ureq, command); } /** * @param ureq * @param command2 */ private void dispatch(UserRequest ureq, String cmd) { if(!command.equals(cmd)){ throw new AssertException("hack attempt! command does not match the one from the UserRequest! Command recieved: " + cmd + " expected: " + command); } if (registerForMousePositionEvent) setXYOffest(ureq); fireEvent(ureq, new Event(cmd)); } /** * @see org.olat.core.gui.components.Component#getHTMLRendererSingleton() */ public ComponentRenderer getHTMLRendererSingleton() { return RENDERER; } public String getCommand() { return command; } public String getI18n() { return i18n; } public boolean isPrimary() { return primary; } public void setPrimary(boolean isPrimary) { primary = isPrimary; } /** * Sets the focus in the DOM tree to this link element if possible. Note that only one * DOM element can have the focus, so this does not give any guarantee that the focus will * be on that element in call cases. * * @param focus true: element should have focus in DOM; false: no focused */ public void setFocus(boolean focus){ this.focus = focus; } /** * @return true: element should have focus in DOM; false: no focused */ public boolean isFocus(){ return focus; } public boolean isPopup() { return popup != null; } public void setPopup(boolean popup) { if(popup) { this.popup = new LinkPopupSettings(); } else { this.popup = null; } } public LinkPopupSettings getPopup() { return popup; } public void setPopup(LinkPopupSettings popup) { this.popup = popup; } public Badge getBadge() { return badge; } public void setBadge(String message, Badge.Level level) { if(badge == null) { badge = new Badge(getComponentName() + "_BADGE"); } badge.setMessage(message); badge.setLevel(level); setDirty(true); } public int getPresentation() { return presentation; } /** * The link title, text that shows up when mouse hovers over link. Not the * link text. <br> * If ((getPresentation() - Link.NONTRANSLATED) >= 0 ) the returned title is * already translated, otherwise an untranslated i18n key is returned * * @return */ public String getTitle() { return title; } Object getInternalAttachedObject(){ return internalAttachedObj; } MouseEvent getMouseEvent() { return mouseEvent; } String getJavascriptHandlerFunction() { return javascriptHandlerFunction; } boolean isRegisterForMousePositionEvent() { return registerForMousePositionEvent; } /** * Set an link title which gets displayed when hovering over the link. * <br> * If ((getPresentation() - Link.NONTRANSLATED) >= 0 ) the provided title is * already translated, otherwise its an untranslated i18n key * * @param the i18n key or the translated key depending on presentation mode */ public void setTitle(String i18nKey) { this.title = i18nKey; } /** * Only used in olat flexi form stuff * @return returns the custom setted element id */ protected String getElementId() { return this.elementId; } public boolean isHasTooltip() { return hasTooltip; } /** * @see org.olat.core.gui.components.Component#setEnabled(boolean) * @param true or false */ @Override public void setEnabled(boolean b){ super.setEnabled(b); setDirty(true); } protected String getTextReasonForDisabling() { return textReasonForDisabling; } public void setTextReasonForDisabling(String textReasonForDisabling) { this.textReasonForDisabling = textReasonForDisabling; } /** * @return the custom CSS class used for a disabled link */ public String getCustomDisabledLinkCSS() { //if (presentation % NONTRANSLATED != LINK_CUSTOM_CSS) { // throw new AssertException("Tried to get custom link CSS class but presentation mode is not set to LINK_CUSTOM_CSS"); //} return customDisabledLinkCSS; } /** * @param customDisabledLinkCSS the custom CSS class used for a disabled link */ public void setCustomDisabledLinkCSS(String customDisabledLinkCSS) { //if (presentation % NONTRANSLATED != LINK_CUSTOM_CSS) { // throw new AssertException("Tried to set custom link CSS class but presentation mode is not set to LINK_CUSTOM_CSS"); //} this.customDisabledLinkCSS = customDisabledLinkCSS; //check if it is a flexi.form link with custom css boolean flexiformlink = (presentation - Link.FLEXIBLEFORMLNK) >= 0; if (flexiformlink) { presentation = presentation - Link.FLEXIBLEFORMLNK; } boolean nontranslated = (presentation - Link.NONTRANSLATED) >= 0; if (nontranslated) { presentation = Link.NONTRANSLATED; } else { presentation = Link.LINK_CUSTOM_CSS; } //enable the flexi.form info again if(flexiformlink){ presentation += Link.FLEXIBLEFORMLNK; } setDirty(true); } /** * @return the custom CSS class used for a enabled link */ public String getCustomEnabledLinkCSS() { //if (presentation % NONTRANSLATED != LINK_CUSTOM_CSS) { // throw new AssertException("Tried to get custom link CSS class but presentation mode is not set to LINK_CUSTOM_CSS"); //} return customEnabledLinkCSS; } /** * @param customEnabledLinkCSS the custom CSS class used for a enabled link */ public void setCustomEnabledLinkCSS(String customEnabledLinkCSS) { //if (presentation % NONTRANSLATED != LINK_CUSTOM_CSS) { // throw new AssertException("Tried to set custom link CSS class but presentation mode is not set to LINK_CUSTOM_CSS"); //} this.customEnabledLinkCSS = customEnabledLinkCSS; //check if it is a flexi.form link with custom css boolean flexiformlink = (presentation - Link.FLEXIBLEFORMLNK) >= 0; if (flexiformlink) { presentation = presentation - Link.FLEXIBLEFORMLNK; } boolean nontranslated = (presentation - Link.NONTRANSLATED) >= 0; if (nontranslated) { presentation = Link.NONTRANSLATED; } else { presentation = Link.LINK_CUSTOM_CSS; } //enable the flexi.form info again if(flexiformlink){ presentation += Link.FLEXIBLEFORMLNK; } setDirty(true); } public void removeCSS(){ this.presentation = presentationBeforeCustomCSS; setDirty(true); } public String getTarget() { return target; } /** * allows setting an custom href target like "_blank" which * opens an link in a new window. * As ajax links never should open in a new window, the setTarget automatically disables * the ajax feature in the link. * @param target */ public void setTarget(String target) { this.target = target; } public Object getUserObject() { return userObject; } public void setUserObject(Object userObject) { this.userObject = userObject; } /* * this method should not be public, it is restricted to the link package. ID's must * be unique and follow the requirements for html id names. */ void setElementId(String elementID) { this.elementId = elementID; } protected String getAccessKey() { return accessKey; } /** * sets the accesskey, e.g. "5" -> Alt+5 then focusses on this link * @param accessKey */ public void setAccessKey(String accessKey) { this.accessKey = accessKey; } public void setAjaxEnabled(boolean ajaxEnabled) { this.ajaxEnabled = ajaxEnabled; } public boolean isAjaxEnabled() { return ajaxEnabled; } /** * When pressing this button, should the system prevent the check for any * unsubmitted forms? * * @param suppressDirtyFormWarning true: don't check for dirt forms; false: * check for dirty forms (default) */ public void setSuppressDirtyFormWarning(boolean suppressDirtyFormWarning) { this.suppressDirtyFormWarning = suppressDirtyFormWarning; } /** * @return true: don't check for dirt forms; false: check for dirty forms * (default) */ public boolean isSuppressDirtyFormWarning() { return suppressDirtyFormWarning; } /** * @return true if a flexi link must do an extra check of the dirtiness of its form. */ public boolean isForceFlexiDirtyFormWarning() { return forceFlexiDirtyFormWarning; } /** * * @param forceFlexiDirtyFormWarning true if the flexi link need to check if the form is dirty. */ public void setForceFlexiDirtyFormWarning(boolean forceFlexiDirtyFormWarning) { this.forceFlexiDirtyFormWarning = forceFlexiDirtyFormWarning; } /** * The custom display text or null if not set */ public String getCustomDisplayText() { return customDisplayText; } public void setCustomDisplayText(String customDisplayText) { this.customDisplayText = customDisplayText; setDirty(true); } /** * get the mouse position as event.command coded as x123y456 and appended to the UserRequest * catch it inside the event method with the ureq.getModuleUri() method.<br> * Uses prototype.js * @param b */ public void registerForMousePositionEvent(boolean b) { this.registerForMousePositionEvent = b; } /** * register a javascript function to an event of this link * TODO:gs:b may pass the event and the link element to the function as arguments * <br> * Uses prototype.js * @param event * @param handlerFunction: A javascript function name */ public void registerMouseEvent(MouseEvent event, String handlerFunction) { this.mouseEvent = event; this.javascriptHandlerFunction = handlerFunction; } /** * convenience method to set the x and y values you get by <code>link.registerForMousePositionEvent(true)</code> * to x and y * @param ureq * @param offsetX * @param offsetY */ public void setXYOffest(UserRequest ureq) { String xyOffset = ureq.getModuleURI(); if(xyOffset != null) { try { offsetX = Integer.parseInt(xyOffset.substring(1, xyOffset .indexOf("y"))); offsetY = Integer.parseInt(xyOffset.substring(xyOffset .indexOf("y") + 1, xyOffset.length())); } catch (NumberFormatException e) { offsetX = 0; offsetY = 0; } } } /** * valid events for the register mouse event stuff */ public enum MouseEvent { click, mousedown, mouseup, mouseover, mousemove, mouseout } /** * returs the mouse position when the link was clicked. * Only available if registerForMousePositionEvent is set true * @return offset x of mouse position */ public int getOffsetX() { return offsetX; } /** * returs the mouse position when the link was clicked. * Only available if registerForMousePositionEvent is set true * @return offset y of mouse position */ public int getOffsetY() { return offsetY; } /** * Sets a tooltip out off the text from the provided i18n key. Tooltips are fastser appearing than normal title tags * and can contain HTML tags. * @param sticky: sets the tooltip sticky, which means the user has to click the tip to disappear */ public void setTooltip(String tooltipI18nKey) { setTitle(tooltipI18nKey); this.hasTooltip = true; setDirty(true); } /** * @param iconCSS The CSS classes used as icons in the i element on the left hand side of the link text */ public void setIconLeftCSS(String iconCSS) { this.iconLeftCSS = iconCSS; setDirty(true); } /** * @return The icon CSS classes or NULL */ public String getIconLeftCSS() { return iconLeftCSS; } /** * @param iconCSS The CSS classes used as icons in the i element on the right hand side of the link text */ public void setIconRightCSS(String iconCSS) { this.iconRightCSS = iconCSS; setDirty(true); } /** * @return The icon CSS classes or NULL */ public String getIconRightCSS() { return iconRightCSS; } /** * Compare also with isEnabled(); * @return true if the link is active (only a rendering issue); false if link not active */ public boolean isActive() { return active; } /** * Compare also with setEnabled)= * @param isActive true: the link is currently active (only a rendering issue); false: the link is not active right now */ public void setActive(boolean isActive) { if (active == isActive) return; active = isActive; setDirty(true); } }