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.krad.uif.widget;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.CoreApiServiceLocator;
020import org.kuali.rice.core.web.format.Formatter;
021import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
022import org.kuali.rice.krad.service.KRADServiceLocator;
023import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
024import org.kuali.rice.krad.service.ModuleService;
025import org.kuali.rice.krad.uif.UifConstants;
026import org.kuali.rice.krad.uif.UifParameters;
027import org.kuali.rice.krad.uif.component.BindingInfo;
028import org.kuali.rice.krad.uif.component.Component;
029import org.kuali.rice.krad.uif.field.DataField;
030import org.kuali.rice.krad.uif.field.LinkField;
031import org.kuali.rice.krad.uif.util.LookupInquiryUtils;
032import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
033import org.kuali.rice.krad.uif.util.ViewModelUtils;
034import org.kuali.rice.krad.uif.view.View;
035import org.kuali.rice.krad.util.UrlFactory;
036
037import java.security.GeneralSecurityException;
038import java.util.HashMap;
039import java.util.List;
040import java.util.Map;
041import java.util.Map.Entry;
042import java.util.Properties;
043
044/**
045 * Widget for rendering an Inquiry link on a field's value
046 *
047 * @author Kuali Rice Team (rice.collab@kuali.org)
048 */
049public class Inquiry extends WidgetBase {
050    private static final long serialVersionUID = -2154388007867302901L;
051    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(Inquiry.class);
052
053    public static final String INQUIRY_TITLE_PREFIX = "title.inquiry.url.value.prependtext";
054
055    private String baseInquiryUrl;
056
057    private String dataObjectClassName;
058    private String viewName;
059
060    private Map<String, String> inquiryParameters;
061
062    private boolean forceInquiry;
063
064    private LinkField inquiryLinkField;
065
066    public Inquiry() {
067        super();
068
069        forceInquiry = false;
070        inquiryParameters = new HashMap<String, String>();
071    }
072
073    /**
074     * @see org.kuali.rice.krad.uif.widget.WidgetBase#performFinalize(org.kuali.rice.krad.uif.view.View,
075     *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
076     */
077    @Override
078    public void performFinalize(View view, Object model, Component parent) {
079        super.performFinalize(view, model, parent);
080
081        // only set inquiry if enabled
082        if (!isRender() || !isReadOnly()) {
083            return;
084        }
085
086        // set render to false until we find an inquiry class
087        setRender(false);
088
089        DataField field = (DataField) parent;
090
091        // check if field value is null, if so no inquiry
092        Object propertyValue = ObjectPropertyUtils.getPropertyValue(model, field.getBindingInfo().getBindingPath());
093        if ((propertyValue == null) || StringUtils.isBlank(propertyValue.toString())) {
094            return;
095        }
096
097        setupLink(view, model, field);
098    }
099
100    /**
101     * Get parent object and field name and build the inquiry link
102     * This was moved from the performFinalize because overlapping and to be used
103     * by DirectInquiry
104     *
105     * @param view - Container View
106     * @param model - model
107     * @param field - The parent Attribute field
108     */
109    public void setupLink(View view, Object model, DataField field) {
110        String propertyName = field.getBindingInfo().getBindingName();
111
112        // if class and parameters configured, build link from those
113        if (StringUtils.isNotBlank(getDataObjectClassName()) && (getInquiryParameters() != null) &&
114                !getInquiryParameters().isEmpty()) {
115            Class<?> inquiryObjectClass = null;
116            try {
117                inquiryObjectClass = Class.forName(getDataObjectClassName());
118            } catch (ClassNotFoundException e) {
119                LOG.error("Unable to get class for: " + getDataObjectClassName());
120                throw new RuntimeException(e);
121            }
122
123            updateInquiryParameters(field.getBindingInfo());
124
125            buildInquiryLink(model, propertyName, inquiryObjectClass, getInquiryParameters());
126        }
127        // get inquiry class and parameters from view helper
128        else {
129            // get parent object for inquiry metadata
130            Object parentObject = ViewModelUtils.getParentObjectForMetadata(view, model, field);
131            view.getViewHelperService().buildInquiryLink(parentObject, propertyName, this);
132        }
133    }
134
135    /**
136     * Adjusts the path on the inquiry parameter property to match the binding
137     * path prefix of the given <code>BindingInfo</code>
138     *
139     * @param bindingInfo - binding info instance to copy binding path prefix from
140     */
141    public void updateInquiryParameters(BindingInfo bindingInfo) {
142        Map<String, String> adjustedInquiryParameters = new HashMap<String, String>();
143        for (String fromField : inquiryParameters.keySet()) {
144            String toField = inquiryParameters.get(fromField);
145            String adjustedFromFieldPath = bindingInfo.getPropertyAdjustedBindingPath(fromField);
146
147            adjustedInquiryParameters.put(adjustedFromFieldPath, toField);
148        }
149
150        this.inquiryParameters = adjustedInquiryParameters;
151    }
152
153    /**
154     * Builds the inquiry link based on the given inquiry class and parameters
155     *
156     * @param dataObject - parent object that contains the data (used to pull inquiry
157     * parameters)
158     * @param propertyName - name of the property the inquiry is set on
159     * @param inquiryObjectClass - class of the object the inquiry should point to
160     * @param inquiryParms - map of key field mappings for the inquiry
161     */
162    public void buildInquiryLink(Object dataObject, String propertyName, Class<?> inquiryObjectClass,
163            Map<String, String> inquiryParms) {
164        Properties urlParameters = new Properties();
165
166        urlParameters.put(UifParameters.DATA_OBJECT_CLASS_NAME, inquiryObjectClass.getName());
167        urlParameters.put(UifParameters.METHOD_TO_CALL, UifConstants.MethodToCallNames.START);
168
169        for (Entry<String, String> inquiryParameter : inquiryParms.entrySet()) {
170            String parameterName = inquiryParameter.getKey();
171
172            Object parameterValue = ObjectPropertyUtils.getPropertyValue(dataObject, parameterName);
173
174            // TODO: need general format util that uses spring
175            if (parameterValue == null) {
176                parameterValue = "";
177            } else if (parameterValue instanceof java.sql.Date) {
178                if (Formatter.findFormatter(parameterValue.getClass()) != null) {
179                    Formatter formatter = Formatter.getFormatter(parameterValue.getClass());
180                    parameterValue = formatter.format(parameterValue);
181                }
182            } else {
183                parameterValue = parameterValue.toString();
184            }
185
186            // Encrypt value if it is a field that has restriction that prevents a value from being shown to
187            // user, because we don't want the browser history to store the restricted attributes value in the URL
188            if (KRADServiceLocatorWeb.getDataObjectAuthorizationService()
189                    .attributeValueNeedsToBeEncryptedOnFormsAndLinks(inquiryObjectClass, inquiryParameter.getValue())) {
190                try {
191                    if(CoreApiServiceLocator.getEncryptionService().isEnabled()) {
192                        parameterValue = CoreApiServiceLocator.getEncryptionService().encrypt(parameterValue);
193                    }
194                } catch (GeneralSecurityException e) {
195                    LOG.error("Exception while trying to encrypted value for inquiry framework.", e);
196                    throw new RuntimeException(e);
197                }
198            }
199
200            // add inquiry parameter to URL
201            urlParameters.put(inquiryParameter.getValue(), parameterValue);
202        }
203
204        // build inquiry URL
205        String inquiryUrl = "";
206
207        // check for EBOs for an alternate inquiry URL
208        ModuleService responsibleModuleService =
209                KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(inquiryObjectClass);
210        if (responsibleModuleService != null && responsibleModuleService.isExternalizable(inquiryObjectClass)) {
211            inquiryUrl = responsibleModuleService.getExternalizableDataObjectLookupUrl(inquiryObjectClass,
212                    urlParameters);
213        } else {
214            inquiryUrl = UrlFactory.parameterizeUrl(getBaseInquiryUrl(), urlParameters);
215        }
216
217        getInquiryLinkField().setHrefText(inquiryUrl);
218
219        // set inquiry title
220        String linkTitle = createTitleText(inquiryObjectClass);
221        linkTitle = LookupInquiryUtils.getLinkTitleText(linkTitle, inquiryObjectClass, getInquiryParameters());
222        getInquiryLinkField().setTitle(linkTitle);
223
224        setRender(true);
225    }
226
227    /**
228     * Gets text to prepend to the inquiry link title
229     *
230     * @param dataObjectClass - data object class being inquired into
231     * @return String title prepend text
232     */
233    public String createTitleText(Class<?> dataObjectClass) {
234        String titleText = "";
235
236        String titlePrefixProp =
237                KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString(INQUIRY_TITLE_PREFIX);
238        if (StringUtils.isNotBlank(titlePrefixProp)) {
239            titleText += titlePrefixProp + " ";
240        }
241
242        String objectLabel = KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary()
243                .getDataObjectEntry(dataObjectClass.getName()).getObjectLabel();
244        if (StringUtils.isNotBlank(objectLabel)) {
245            titleText += objectLabel + " ";
246        }
247
248        return titleText;
249    }
250
251    /**
252     * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
253     */
254    @Override
255    public List<Component> getComponentsForLifecycle() {
256        List<Component> components = super.getComponentsForLifecycle();
257
258        components.add(getInquiryLinkField());
259
260        return components;
261    }
262
263    public String getBaseInquiryUrl() {
264        return this.baseInquiryUrl;
265    }
266
267    public void setBaseInquiryUrl(String baseInquiryUrl) {
268        this.baseInquiryUrl = baseInquiryUrl;
269    }
270
271    public String getDataObjectClassName() {
272        return this.dataObjectClassName;
273    }
274
275    public void setDataObjectClassName(String dataObjectClassName) {
276        this.dataObjectClassName = dataObjectClassName;
277    }
278
279    public String getViewName() {
280        return this.viewName;
281    }
282
283    public void setViewName(String viewName) {
284        this.viewName = viewName;
285    }
286
287    public boolean isForceInquiry() {
288        return this.forceInquiry;
289    }
290
291    public void setForceInquiry(boolean forceInquiry) {
292        this.forceInquiry = forceInquiry;
293    }
294
295    public Map<String, String> getInquiryParameters() {
296        return this.inquiryParameters;
297    }
298
299    public void setInquiryParameters(Map<String, String> inquiryParameters) {
300        this.inquiryParameters = inquiryParameters;
301    }
302
303    public LinkField getInquiryLinkField() {
304        return this.inquiryLinkField;
305    }
306
307    public void setInquiryLinkField(LinkField inquiryLinkField) {
308        this.inquiryLinkField = inquiryLinkField;
309    }
310}