001/**
002 * Copyright 2005-2018 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.uif.widget;
017
018import java.text.MessageFormat;
019
020import org.apache.commons.lang.StringUtils;
021import org.kuali.rice.core.api.CoreApiServiceLocator;
022import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
023import org.kuali.rice.coreservice.framework.parameter.ParameterService;
024import org.kuali.rice.krad.datadictionary.HelpDefinition;
025import org.kuali.rice.krad.datadictionary.parse.BeanTag;
026import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
027import org.kuali.rice.krad.datadictionary.uif.UifDictionaryBean;
028import org.kuali.rice.krad.uif.CssConstants;
029import org.kuali.rice.krad.uif.UifConstants;
030import org.kuali.rice.krad.uif.element.Action;
031import org.kuali.rice.krad.uif.field.InputField;
032import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
033import org.kuali.rice.krad.uif.util.ComponentFactory;
034import org.kuali.rice.krad.uif.util.LifecycleElement;
035import org.kuali.rice.krad.uif.view.ExpressionEvaluator;
036
037/**
038 * Widget that renders help on a component
039 *
040 * <p>
041 * If help URL is specified then display help icon and/or if help summary is specified then display help tooltip.
042 * </p>
043 *
044 * @author Kuali Rice Team (rice.collab@kuali.org)
045 */
046@BeanTag(name = "help", parent = "Uif-Help")
047public class Help extends WidgetBase {
048        private static final long serialVersionUID = -1514436681476297241L;
049
050    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(Help.class);
051
052    private Action helpAction;
053    private HelpDefinition helpDefinition;
054    private String externalHelpUrl;
055
056    private String tooltipHelpContent;
057
058    /**
059     * The following initialization is performed:
060     *
061     * <ul>
062     * <li>If help action not initialized and external help is configured, get the default
063     * help action component</li>
064     * </ul>
065     *
066     * {@inheritDoc}
067     */
068    @Override
069    public void performInitialization(Object model) {
070        super.performInitialization(model);
071
072        if (helpAction == null) {
073            if(hasValueOrPropertyExpression(externalHelpUrl, this, "externalHelpUrl") ||  (
074                    helpDefinition != null &&
075                            hasValueOrPropertyExpression(helpDefinition.getParameterName(), helpDefinition, "parameterName") &&
076                            hasValueOrPropertyExpression(helpDefinition.getParameterDetailType(), helpDefinition, "parameterDetailType")
077            )){
078                helpAction = ComponentFactory.getHelpAction();
079                helpAction.addDataAttribute(UifConstants.DataAttributes.ROLE, "help");
080            }
081        }
082        else{
083            helpAction.addDataAttribute(UifConstants.DataAttributes.ROLE, "help");
084        }
085    }
086
087    /**
088     * Helper function to check if value or property expression exists
089     *
090     * @param defaultValue
091     * @param dictionaryBean
092     * @param expressionName
093     * @return
094     */
095    private boolean hasValueOrPropertyExpression(String defaultValue, UifDictionaryBean dictionaryBean, String expressionName) {
096        return StringUtils.isNotBlank(defaultValue) || (dictionaryBean != null && dictionaryBean.getPropertyExpressions() != null &&
097                StringUtils.isNotBlank(dictionaryBean.getPropertyExpression(expressionName)));
098    }
099
100    /**
101     * Finalize the help widget for usage
102     *
103     * <p>
104     * In addition to the standard finalization the following tasks are performed:
105     * <li>Build the external help Url</li>
106     * <li>Set the javascript action which opens the external help in window</li>
107     * <li>Set render to false if help not configured</li>
108     * </p>
109     *
110     * {@inheritDoc}
111     */
112    @Override
113    public void performFinalize(Object model, LifecycleElement parent) {
114        super.performFinalize(model, parent);
115
116        buildExternalHelp(parent);
117        buildTooltipHelp(parent);
118
119        // if help is not configured don't render the component
120        if (StringUtils.isBlank(this.externalHelpUrl) && StringUtils.isBlank(this.tooltipHelpContent)) {
121            setRender(false);
122        }
123
124        // Change to icon only look and feel if not associated with an input
125        if (parent != null && !(parent instanceof InputField) && helpAction != null) {
126            helpAction.getLibraryCssClasses().remove(CssConstants.Classes.BTN);
127            helpAction.getLibraryCssClasses().remove(CssConstants.Classes.BTN_DEFAULT);
128            helpAction.getLibraryCssClasses().add(CssConstants.Classes.ICON_ONLY_BUTTON);
129        }
130    }
131
132    /**
133     * Build the external help
134     *
135     * <p>
136     * When the externalHelpUrl is blank and the helpDefinition is specified then the external help URL is
137     * looked up via the helpDefinition from the system parameters.  The namespace in the helpDefinition
138     * does not need to be specified and will default to the namespace of the view.
139     * </p>
140     *
141     * <p>
142     * Set the javascript action to open the external help in a window.
143     * </p>
144     *
145     * <p>
146     * Set the html title attribute of the help icon.
147     * </p>
148     *
149     * @param parent used to get the help title text used in the html title attribute of the help icon
150     */
151    protected void buildExternalHelp(LifecycleElement parent) {
152        if (StringUtils.isBlank(externalHelpUrl) && (helpDefinition != null)) {
153            if (StringUtils.isBlank(helpDefinition.getParameterNamespace())) {
154                helpDefinition.setParameterNamespace(ViewLifecycle.getView().getNamespaceCode());
155            }
156
157            String parameterNamespace = helpDefinition.getParameterNamespace();
158            String parameterDetailType = helpDefinition.getParameterDetailType();
159            String parameterName = helpDefinition.getParameterName();
160
161            ExpressionEvaluator expressionEvaluator = ViewLifecycle.getExpressionEvaluator();
162            if (parameterNamespace == null && helpDefinition.getPropertyExpression("parameterNamespace") != null) {
163                parameterNamespace = (String) expressionEvaluator.evaluateExpression(this.getContext(),
164                        helpDefinition.getPropertyExpression("parameterNamespace"));
165            }
166
167            if (parameterDetailType == null && helpDefinition.getPropertyExpression("parameterDetailType") != null) {
168                parameterDetailType = (String) expressionEvaluator.evaluateExpression(this.getContext(),
169                        helpDefinition.getPropertyExpression("parameterDetailType"));
170            }
171
172            if (parameterName == null && helpDefinition.getPropertyExpression("parameterName") != null) {
173                parameterName = (String) expressionEvaluator.evaluateExpression(this.getContext(),
174                        helpDefinition.getPropertyExpression("parameterName"));
175            }
176
177            if (StringUtils.isNotBlank(parameterNamespace)
178                    && StringUtils.isNotBlank(parameterDetailType)
179                    && StringUtils.isNotBlank(parameterName)) {
180                externalHelpUrl = getParameterService().getParameterValueAsFilteredString(
181                        parameterNamespace, parameterDetailType, parameterName);
182            }
183        }
184
185        if (StringUtils.isNotBlank(externalHelpUrl)) {
186            // set the javascript action for the external help
187            getHelpAction().setActionScript("openHelpWindow('" + externalHelpUrl + "')");
188
189            // set the alt and title attribute of the image
190            String helpTitle;
191
192            // make sure that we are the component's native help and not a misconfigured standalone help bean.
193            if ((parent instanceof Helpable) && (((Helpable) parent).getHelp() == this)) {
194                helpTitle = MessageFormat.format(
195                        CoreApiServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
196                                "help.icon.title.tag.with.field.label"), ((Helpable) parent).getHelpTitle());
197            } else {
198                helpTitle = CoreApiServiceLocator.getKualiConfigurationService().getPropertyValueAsString(
199                        "help.icon.title.tag");
200            }
201
202            getHelpAction().setTitle(helpTitle);
203        }
204    }
205
206    /**
207     * Build the tooltip help
208     *
209     * <p>
210     * The help tooltip is set on the component.  To use the help tooltip bean definition, the help's tooltip is used
211     * as and intermediary for setting up the tooltip widget and then copied to the component.
212     * </p>
213     *
214     * @param parent used for checking misconfigurations
215     */
216    protected void buildTooltipHelp(LifecycleElement parent) {
217        if (StringUtils.isNotBlank(tooltipHelpContent) && this.isRender()) {
218            // make sure that we are the component's native help and not a misconfigured standalone help bean.
219            if (this.getToolTip() != null && (parent instanceof Helpable) 
220                    && (((Helpable) parent).getHelp() == this)) {
221                this.getToolTip().setTooltipContent(tooltipHelpContent);
222                ((Helpable) parent).setTooltipOfComponent(this.getToolTip());
223            }
224        }
225    }
226
227    /**
228     * HelpActionField is used for rendering external help
229     *
230     * @return Action for external help
231     */
232    @BeanTagAttribute
233    public Action getHelpAction() {
234        return helpAction;
235    }
236
237    /**
238     * Setter for helpAction
239     *
240     * @param helpAction
241     */
242    public void setHelpAction(Action helpAction) {
243        this.helpAction = helpAction;
244    }
245
246    /**
247     * The help definition is used as the key to retrieve the external help Url from the parameter table of
248     * the database
249     *
250     * @return HelpDefinition
251     */
252    @BeanTagAttribute(type= BeanTagAttribute.AttributeType.DIRECTORBYTYPE)
253    public HelpDefinition getHelpDefinition() {
254        return helpDefinition;
255    }
256
257    /**
258     * Setter for the help definition of the database.
259     *
260     * @param helpDefinition
261     */
262    public void setHelpDefinition(HelpDefinition helpDefinition) {
263        this.helpDefinition = helpDefinition;
264    }
265
266    /**
267     * The external help Url
268     *
269     * <p>
270     * This should contain a valid URL.  When specified this URL takes precedence over the external help URL from
271     * the system parameters.
272     * </p>
273     *
274     * @return Url of the external help
275     */
276    @BeanTagAttribute
277    public String getExternalHelpUrl() {
278        return this.externalHelpUrl;
279    }
280
281    /**
282     * Setter for externalHelpUrl
283     *
284     * @param externalHelpUrl
285     */
286    public void setExternalHelpUrl(String externalHelpUrl) {
287        this.externalHelpUrl = externalHelpUrl;
288    }
289
290    /**
291     * TooltipHelpContent
292     *
293     * @return TooltipHelpContent
294     */
295    @BeanTagAttribute
296    public String getTooltipHelpContent() {
297        return this.tooltipHelpContent;
298    }
299
300    /**
301     * Setter for tooltipHelpContent
302     *
303     * @param tooltipHelpContent
304     */
305    public void setTooltipHelpContent(String tooltipHelpContent) {
306        this.tooltipHelpContent = tooltipHelpContent;
307    }
308
309    /**
310     * Retrieve the parameter service
311     *
312     * @return ParameterService
313     */
314    protected ParameterService getParameterService() {
315        return CoreFrameworkServiceLocator.getParameterService();
316    }
317}