001/**
002 * Copyright 2005-2016 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.kns.web.struts.action;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.struts.action.ActionForm;
020import org.apache.struts.action.ActionForward;
021import org.apache.struts.action.ActionMapping;
022import org.kuali.rice.core.api.CoreApiServiceLocator;
023import org.kuali.rice.core.api.config.property.ConfigContext;
024import org.kuali.rice.core.api.config.property.ConfigurationService;
025import org.kuali.rice.core.api.util.RiceConstants;
026import org.kuali.rice.core.api.util.RiceKeyConstants;
027import org.kuali.rice.coreservice.framework.parameter.ParameterService;
028import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
029import org.kuali.rice.kew.api.KewApiServiceLocator;
030import org.kuali.rice.kew.api.doctype.DocumentType;
031import org.kuali.rice.kns.datadictionary.BusinessObjectEntry;
032import org.kuali.rice.kns.datadictionary.HeaderNavigation;
033import org.kuali.rice.kns.datadictionary.KNSDocumentEntry;
034import org.kuali.rice.kns.datadictionary.LookupDefinition;
035import org.kuali.rice.kns.datadictionary.MaintainableFieldDefinition;
036import org.kuali.rice.kns.service.KNSServiceLocator;
037import org.kuali.rice.kns.service.MaintenanceDocumentDictionaryService;
038import org.kuali.rice.kns.util.WebUtils;
039import org.kuali.rice.kns.web.struts.form.KualiHelpForm;
040import org.kuali.rice.krad.datadictionary.AttributeDefinition;
041import org.kuali.rice.krad.datadictionary.DataDictionary;
042import org.kuali.rice.krad.datadictionary.DataDictionaryEntry;
043import org.kuali.rice.krad.datadictionary.HelpDefinition;
044import org.kuali.rice.krad.service.DataDictionaryService;
045import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
046import org.kuali.rice.krad.util.KRADConstants;
047
048import javax.servlet.http.HttpServletRequest;
049import javax.servlet.http.HttpServletResponse;
050
051/**
052 * This class handles requests for help text.
053 * 
054 * 
055 */
056public class KualiHelpAction extends KualiAction {
057    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiHelpAction.class);
058
059    private static final String VALIDATION_PATTERN_STRING = "ValidationPattern";
060    private static final String NO = "No";
061    private static final String YES = "Yes";
062    static final String DEFAULT_LOOKUP_HELP_TEXT_RESOURCE_KEY = "lookupHelpText";
063    
064    private static DataDictionaryService dataDictionaryService;
065    private static ConfigurationService kualiConfigurationService;
066    private static ParameterService parameterService;
067    private static MaintenanceDocumentDictionaryService maintenanceDocumentDictionaryService;
068
069    private DataDictionaryService getDataDictionaryService() {
070        if ( dataDictionaryService == null ) {
071            dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
072        }
073        return dataDictionaryService;
074    }
075    private ConfigurationService getConfigurationService() {
076        if ( kualiConfigurationService == null ) {
077            kualiConfigurationService = CoreApiServiceLocator.getKualiConfigurationService();
078        }
079        return kualiConfigurationService;
080    }
081    private ParameterService getParameterService() {
082        if ( parameterService == null ) {
083            parameterService = CoreFrameworkServiceLocator.getParameterService();
084        }
085        return parameterService;
086    }
087
088    private MaintenanceDocumentDictionaryService getMaintenanceDocumentDictionaryService() {
089        if ( maintenanceDocumentDictionaryService == null ) {
090            maintenanceDocumentDictionaryService = KNSServiceLocator.getMaintenanceDocumentDictionaryService();
091        }
092        return maintenanceDocumentDictionaryService;
093    }
094    
095    /**
096     * Convenience method for accessing <code>{@link DataDictionaryEntry}</code> for the given business object
097     * 
098     * @param businessObjectClassName
099     * @return DataDictionaryEntry
100     */
101    private DataDictionaryEntry getDataDictionaryEntry(String businessObjectClassName) {
102        return getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry(businessObjectClassName);
103    }
104
105    /**
106     * Convenience method for accessing the <code>{@link AttributeDefinition}</code> for a specific business object attribute
107     * defined in the DataDictionary.
108     * 
109     * @param businessObjectClassName
110     * @param attributeName
111     * @return AttributeDefinition
112     */
113    private AttributeDefinition getAttributeDefinition(String businessObjectClassName, String attributeName) throws ClassNotFoundException {
114        AttributeDefinition retval = null;
115
116        if (getDataDictionaryEntry(businessObjectClassName) != null) {
117            retval = getDataDictionaryEntry(businessObjectClassName).getAttributeDefinition(attributeName);
118        }
119        return retval;
120    }
121
122    /**
123     * @param attribute <code>{@link AttributeDefinition}</code>
124     * @return String
125     */
126    private String getAttributeMaxLength(AttributeDefinition attribute) throws Exception {
127        return attribute.getMaxLength().toString();
128    }
129
130    /**
131     * @param attribute <code>{@link AttributeDefinition}</code>
132     * @return String
133     */
134    private String getAttributeValidationPatternName(AttributeDefinition attribute) throws Exception {
135        String retval = new String();
136        if (attribute.getValidationPattern() != null) {
137            retval = attribute.getValidationPattern().getClass().getName();
138        }
139
140        if (retval.indexOf(".") > 0) {
141            retval = retval.substring(retval.lastIndexOf(".") + 1);
142        }
143        if (retval.endsWith(VALIDATION_PATTERN_STRING)) {
144            retval = retval.substring(0, retval.lastIndexOf(VALIDATION_PATTERN_STRING));
145        }
146
147        return retval;
148    }
149
150    /**
151     * Retrieves help information from the data dictionary for the business object attribute.
152     * 
153     * @return ActionForward
154     */
155    public ActionForward getAttributeHelpText(ActionMapping mapping, KualiHelpForm helpForm, HttpServletRequest request, HttpServletResponse response) throws Exception {
156
157        AttributeDefinition attribute;
158
159        if (StringUtils.isBlank(helpForm.getBusinessObjectClassName()) || StringUtils.isBlank(helpForm.getAttributeName())) {
160            throw new RuntimeException("Business object and attribute name not specified.");
161        }
162        attribute = getAttributeDefinition(helpForm.getBusinessObjectClassName(), helpForm.getAttributeName());
163
164        if ( LOG.isDebugEnabled() ) {
165            LOG.debug( "Request for help on: " + helpForm.getBusinessObjectClassName() + " -- " + helpForm.getAttributeName() );
166            LOG.debug( "  attribute: " + attribute );
167        }
168                
169        if (attribute == null || StringUtils.isBlank(attribute.getSummary())) {
170            helpForm.setResourceKey(RiceKeyConstants.MESSAGE_NO_HELP_TEXT);
171            return getResourceHelpText(mapping, helpForm, request, response);
172        }
173
174        boolean required = attribute.isRequired().booleanValue();
175        // KULRNE-4392 - pull the required attribute on BO maintenance documents from the document def rather than the BO
176        try {
177            Class boClass = Class.forName( helpForm.getBusinessObjectClassName() );
178            String docTypeName = getMaintenanceDocumentDictionaryService().getDocumentTypeName( boClass );
179            if (StringUtils.isNotBlank(docTypeName)) {
180                // maybe it's not a maint doc
181                MaintainableFieldDefinition field = getMaintenanceDocumentDictionaryService().getMaintainableField( docTypeName, helpForm.getAttributeName() );
182                if ( field != null ) {
183                    required = field.isRequired();
184                }
185            }
186            else {
187                if (log.isInfoEnabled()) {
188                    log.info("BO class " + boClass.getName() + " does not have a maint doc definition.  Defaulting to using DD for definition");
189                }
190            }
191        } catch ( ClassNotFoundException ex ) {
192            // do nothing
193            LOG.warn( "Unable to obtain maintainable field for BO property.", ex );
194        }
195        
196        helpForm.setHelpLabel(attribute.getLabel());
197        helpForm.setHelpSummary(attribute.getSummary());
198        helpForm.setHelpDescription(attribute.getDescription());
199        helpForm.setHelpRequired(required?YES:NO);
200        helpForm.setHelpMaxLength(getAttributeMaxLength(attribute));
201        helpForm.setValidationPatternName(getAttributeValidationPatternName(attribute));
202
203        return mapping.findForward(RiceConstants.MAPPING_BASIC);
204    }
205
206    /**
207     * Retrieves help information from the data dictionary for the business object attribute.
208     * 
209     * @return ActionForward
210     */
211    public ActionForward getAttributeHelpText(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
212        return getAttributeHelpText(mapping, (KualiHelpForm) form, request, response);
213    }
214
215    /**
216     * Retrieves help information from the data dictionary for the document type.
217     */
218    public ActionForward getDocumentHelpText(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
219        KualiHelpForm helpForm = (KualiHelpForm) form;
220
221        String documentTypeName = helpForm.getDocumentTypeName();
222
223        if (StringUtils.isBlank(documentTypeName)) {
224            throw new RuntimeException("Document type name not specified.");
225        }
226
227        DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary();
228        org.kuali.rice.krad.datadictionary.DocumentEntry entry = (org.kuali.rice.krad.datadictionary.DocumentEntry ) dataDictionary.getDocumentEntry(documentTypeName);
229
230        String label = "";
231        String summary = "";
232        String description = "";
233        HelpDefinition helpDefinition = null;
234        String apcHelpUrl = null;
235        if (entry != null) {
236            DocumentType docType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(entry.getDocumentTypeName());
237            label = docType.getLabel();
238            description = docType.getDescription();
239            if (StringUtils.isNotBlank(docType.getHelpDefinitionUrl())) {
240                apcHelpUrl = WebUtils.toAbsoluteURL(ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.EXTERNALIZABLE_HELP_URL_KEY), docType.getHelpDefinitionUrl());
241            }
242        }
243
244        if ( StringUtils.isNotBlank(apcHelpUrl) ) {
245            response.sendRedirect(apcHelpUrl);
246            return null;
247        }
248
249        helpForm.setHelpLabel(label);
250        helpForm.setHelpSummary(summary);
251        helpForm.setHelpDescription(description);
252        helpForm.setHelpDefinition(helpDefinition);
253
254        return mapping.findForward(RiceConstants.MAPPING_BASIC);
255    }
256
257    /**
258     * Retrieves help information from the data dictionary for the document type.
259     */
260    public ActionForward getBusinessObjectHelpText(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
261        KualiHelpForm helpForm = (KualiHelpForm) form;
262
263        String objectClassName = helpForm.getBusinessObjectClassName();
264
265        if (StringUtils.isBlank(objectClassName)) {
266            throw new RuntimeException("Document type name not specified.");
267        }
268
269        DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary();
270        BusinessObjectEntry entry = (BusinessObjectEntry) dataDictionary.getBusinessObjectEntry(objectClassName);
271
272        HelpDefinition helpDefinition = null;
273        String apcHelpUrl = null;
274        String label = "";
275        String objectDescription = "";
276        if (entry != null) {
277            helpDefinition = entry.getHelpDefinition();
278            label = entry.getObjectLabel();
279            objectDescription = entry.getObjectDescription();
280            if (null != helpDefinition && null != helpDefinition.getParameterNamespace() && null != helpDefinition.getParameterDetailType() && null != helpDefinition.getParameterName()) {
281                apcHelpUrl = getHelpUrl(helpDefinition.getParameterNamespace(), helpDefinition.getParameterDetailType(), helpDefinition.getParameterName());
282                }
283                }
284
285        if ( !StringUtils.isBlank(apcHelpUrl) ) {
286            response.sendRedirect(apcHelpUrl);
287            return null;
288        }
289        helpForm.setHelpLabel(label);
290        helpForm.setHelpDescription(objectDescription);
291
292        return mapping.findForward(RiceConstants.MAPPING_BASIC);
293    }
294    
295    /**
296     * Retrieves help information from the data dictionary for the document type.
297     */
298    public ActionForward getPageHelpText(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
299        KualiHelpForm helpForm = (KualiHelpForm) form;
300
301        String documentTypeName = helpForm.getDocumentTypeName();
302        String pageName = helpForm.getPageName();
303
304        if (StringUtils.isBlank(documentTypeName)) {
305            throw new RuntimeException("Document type name not specified.");
306        }
307        
308        if (StringUtils.isBlank(pageName)) {
309            throw new RuntimeException("Page name not specified.");
310        }
311
312        DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary();
313        KNSDocumentEntry entry = (KNSDocumentEntry) dataDictionary.getDocumentEntry(documentTypeName);
314
315        String apcHelpUrl = null;
316        String label = "";
317        String objectDescription = "";
318        if (entry != null) {
319            for ( HeaderNavigation headerNavigation : entry.getHeaderNavigationList() ) {
320                if (headerNavigation.getHeaderTabDisplayName().equals(pageName)) {
321                    HelpDefinition helpDefinition = headerNavigation.getHelpDefinition();
322                    if (null != helpDefinition && null != helpDefinition.getParameterNamespace() && null != helpDefinition.getParameterDetailType() && null != helpDefinition.getParameterName()) {
323                        apcHelpUrl = getHelpUrl(helpDefinition.getParameterNamespace(), helpDefinition.getParameterDetailType(), helpDefinition.getParameterName());
324                    }
325                }
326            }
327        }
328
329        if ( !StringUtils.isBlank(apcHelpUrl) ) {
330            response.sendRedirect(apcHelpUrl);
331            return null;
332        }
333        helpForm.setHelpLabel(pageName);
334        helpForm.setHelpDescription("No help content available.");
335
336        return mapping.findForward(RiceConstants.MAPPING_BASIC);
337    }
338    
339    /**
340     * Retrieves help content to link to based on security group/parameter
341     */
342    public ActionForward getStoredHelpUrl(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
343        KualiHelpForm helpForm = (KualiHelpForm) form;
344        
345        String helpParameterNamespace = helpForm.getHelpParameterNamespace();
346        String helpParameterDetailType = helpForm.getHelpParameterDetailType();
347        String helpParameterName = helpForm.getHelpParameterName();
348        
349        if (StringUtils.isBlank(helpParameterNamespace)) {
350            throw new RuntimeException("Parameter Namespace not specified.");
351        }
352        
353        if (StringUtils.isBlank(helpParameterDetailType)) {
354            throw new RuntimeException("Detail Type not specified.");
355        }
356
357        if (StringUtils.isBlank(helpParameterName)) {
358            throw new RuntimeException("Parameter Name not specified.");
359        }
360        
361        String apcHelpUrl = getHelpUrl(helpParameterNamespace, helpParameterDetailType, helpParameterName);
362        
363        if ( !StringUtils.isBlank(apcHelpUrl) ) {
364            response.sendRedirect(apcHelpUrl);
365            return null;
366        }
367        
368        helpForm.setHelpDescription("No help content available.");
369        return mapping.findForward(RiceConstants.MAPPING_BASIC);
370    }
371
372    /**
373     * Retrieves help information from resources by key.
374     */
375    public ActionForward getResourceHelpText(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
376        KualiHelpForm helpForm = (KualiHelpForm) form;
377
378        String resourceKey = helpForm.getResourceKey();
379        populateHelpFormForResourceText(helpForm, resourceKey);
380
381        return mapping.findForward(RiceConstants.MAPPING_BASIC);
382    }
383    
384    /**
385     * Utility method that populates a KualiHelpForm with the description from a given resource key
386     * @param helpForm the KualiHelpForm to populate with help text
387     * @param resourceKey the resource key to use as help text
388     */
389    protected void populateHelpFormForResourceText(KualiHelpForm helpForm, String resourceKey) {
390        if (StringUtils.isBlank(resourceKey)) {
391            throw new RuntimeException("Help resource key not specified.");
392        }
393
394        helpForm.setHelpLabel("");
395        helpForm.setHelpSummary("");
396        helpForm.setHelpDescription(getConfigurationService().getPropertyValueAsString(resourceKey));
397    }
398    
399    /**
400     * Retrieves help for a lookup
401     */
402    public ActionForward getLookupHelpText(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
403        KualiHelpForm helpForm = (KualiHelpForm) form;
404
405        // handle doc search custom help urls
406        if (!StringUtils.isEmpty(helpForm.getSearchDocumentTypeName())) {
407            DocumentType docType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(helpForm.getSearchDocumentTypeName());
408            if (docType != null && !StringUtils.isEmpty(docType.getDocSearchHelpUrl())) {
409                String docSearchHelpUrl = WebUtils.toAbsoluteURL(ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.EXTERNALIZABLE_HELP_URL_KEY), docType.getDocSearchHelpUrl());
410
411                if ( StringUtils.isNotBlank(docSearchHelpUrl) ) {
412                    response.sendRedirect(docSearchHelpUrl);
413                    return null;
414                }
415            }
416        }
417
418        final String lookupBusinessObjectClassName = helpForm.getLookupBusinessObjectClassName();
419        if (!StringUtils.isBlank(lookupBusinessObjectClassName)) {
420                final DataDictionary dataDictionary = getDataDictionaryService().getDataDictionary();
421                final BusinessObjectEntry entry = (BusinessObjectEntry) dataDictionary.getBusinessObjectEntry(lookupBusinessObjectClassName);
422                final LookupDefinition lookupDefinition = entry.getLookupDefinition();
423                
424                if (lookupDefinition != null) {
425                        if (lookupDefinition.getHelpDefinition() != null && !StringUtils.isBlank(lookupDefinition.getHelpDefinition().getParameterNamespace()) && !StringUtils.isBlank(lookupDefinition.getHelpDefinition().getParameterDetailType()) && !StringUtils.isBlank(lookupDefinition.getHelpDefinition().getParameterName())) {
426                                final String apcHelpUrl = getHelpUrl(lookupDefinition.getHelpDefinition().getParameterNamespace(), lookupDefinition.getHelpDefinition().getParameterDetailType(), lookupDefinition.getHelpDefinition().getParameterName());
427                        
428                        if ( !StringUtils.isBlank(apcHelpUrl) ) {
429                            response.sendRedirect(apcHelpUrl);
430                            return null;
431                        }
432                        } else if (!StringUtils.isBlank(lookupDefinition.getHelpUrl())) {
433                                final String apcHelpUrl = WebUtils.toAbsoluteURL(ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.EXTERNALIZABLE_HELP_URL_KEY), lookupDefinition.getHelpUrl());
434                                response.sendRedirect(apcHelpUrl);
435                                return null;
436                        }
437                }
438        }
439        
440        // still here?  guess we're defaulting...
441        populateHelpFormForResourceText(helpForm, getDefaultLookupHelpResourceKey());
442        return mapping.findForward(RiceConstants.MAPPING_BASIC);
443    }
444    
445    /**
446     * @return the key of the default lookup help resource text
447     */
448    protected String getDefaultLookupHelpResourceKey() {
449        return KualiHelpAction.DEFAULT_LOOKUP_HELP_TEXT_RESOURCE_KEY;
450    }
451
452    private String getHelpUrl(String parameterNamespace, String parameterDetailTypeCode, String parameterName) {
453        return WebUtils.toAbsoluteURL(getConfigurationService().getPropertyValueAsString(KRADConstants.EXTERNALIZABLE_HELP_URL_KEY), getParameterService().getParameterValueAsString(parameterNamespace, parameterDetailTypeCode, parameterName));
454    }    
455    
456    /**
457     * Retrieves help content to link to based on parameterNamespace and parameterName
458     */
459    public ActionForward getHelpUrlByNamespace(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
460        KualiHelpForm helpForm = (KualiHelpForm) form;
461       return getStoredHelpUrl(mapping, form, request, response); 
462    }
463}