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 org.apache.commons.lang.StringUtils;
019import org.kuali.rice.krad.bo.DataObjectRelationship;
020import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
021import org.kuali.rice.krad.datadictionary.parse.BeanTag;
022import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
023import org.kuali.rice.krad.datadictionary.parse.BeanTags;
024import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
025import org.kuali.rice.krad.service.ModuleService;
026import org.kuali.rice.krad.uif.UifConstants;
027import org.kuali.rice.krad.uif.UifParameters;
028import org.kuali.rice.krad.uif.component.BindingInfo;
029import org.kuali.rice.krad.uif.component.Component;
030import org.kuali.rice.krad.uif.component.MethodInvokerConfig;
031import org.kuali.rice.krad.uif.container.CollectionGroup;
032import org.kuali.rice.krad.uif.container.DialogGroup;
033import org.kuali.rice.krad.uif.element.Action;
034import org.kuali.rice.krad.uif.field.FieldGroup;
035import org.kuali.rice.krad.uif.field.InputField;
036import org.kuali.rice.krad.uif.lifecycle.LifecycleEventListener;
037import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
038import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction;
039import org.kuali.rice.krad.uif.util.LifecycleElement;
040import org.kuali.rice.krad.uif.util.ViewModelUtils;
041import org.kuali.rice.krad.uif.view.View;
042import org.kuali.rice.krad.util.KRADConstants;
043import org.kuali.rice.krad.util.KRADUtils;
044import org.kuali.rice.krad.web.form.UifFormBase;
045
046import java.util.ArrayList;
047import java.util.HashMap;
048import java.util.List;
049import java.util.Map;
050
051/**
052 * Widget for navigating to a lookup from a field (called a quickfinder).
053 *
054 * @author Kuali Rice Team (rice.collab@kuali.org)
055 */
056@BeanTags({@BeanTag(name = "quickFinder", parent = "Uif-QuickFinder"),
057        @BeanTag(name = "quickFinderByScript", parent = "Uif-QuickFinderByScript"),
058        @BeanTag(name = "collectionQuickFinder", parent = "Uif-CollectionQuickFinder")})
059
060public class QuickFinder extends WidgetBase implements LifecycleEventListener {
061
062    private static final long serialVersionUID = 3302390972815386785L;
063
064    // lookup configuration
065    private String baseLookupUrl;
066    private String dataObjectClassName;
067    private String viewName;
068
069    private boolean returnByScript;
070    private String readOnlyLookupFields;
071    private String referencesToRefresh;
072    private String lookupCollectionName;
073    private String lookupCollectionId;
074
075    private Map<String, String> fieldConversions;
076    private Map<String, String> lookupParameters;
077    private Map<String, String> additionalLookupParameters;
078
079    private Action quickfinderAction;
080
081    private String lookupDialogId;
082    private boolean openInDialog;
083
084    // lookup view options
085    private Boolean renderReturnLink;
086    private Boolean renderResultActions;
087    private Boolean autoSearch;
088    private Boolean renderLookupCriteria;
089    private Boolean renderCriteriaActions;
090    private Boolean hideCriteriaOnSearch;
091    private Boolean renderMaintenanceLinks;
092    private Boolean multipleValuesSelect;
093
094    private String callbackMethodToCall;
095    private MethodInvokerConfig callbackMethod;
096    private Map<String, String> callbackContext;
097
098    public QuickFinder() {
099        super();
100
101        fieldConversions = new HashMap<String, String>();
102        lookupParameters = new HashMap<String, String>();
103        lookupDialogId = "";
104    }
105
106    /**
107     * The following initialization is performed:
108     *
109     * <ul>
110     * <li>Registers an event on the quickfinder action</li>
111     * </ul>
112     *
113     * {@inheritDoc}
114     */
115    @Override
116    public void performInitialization(Object model) {
117        super.performInitialization(model);
118
119        ViewLifecycle viewLifecycle = ViewLifecycle.getActiveLifecycle();
120        viewLifecycle.registerLifecycleCompleteListener(quickfinderAction, this);
121    }
122
123    /**
124     * Inherits readOnly from parent if not explicitly populated.
125     *
126     * {@inheritDoc}
127     */
128    @Override
129    public void afterEvaluateExpression() {
130        super.afterEvaluateExpression();
131
132        if (getReadOnly() == null) {
133            Component parent = ViewLifecycle.getPhase().getParent();
134            setReadOnly(parent == null ? null : parent.getReadOnly());
135        }
136    }
137
138    /**
139     * The following finalization is performed:
140     *
141     * <ul>
142     * <li>Sets up the quickfinder based on whether the parent is an input field or collection group</li>
143     * <li>Adds action parameters to the quickfinder action based on the quickfinder configuration</li>
144     * <li>Adds callback parameters to post data if present</li>
145     * </ul>
146     *
147     * {@inheritDoc}
148     */
149    @Override
150    public void performFinalize(Object model, LifecycleElement parent) {
151        super.performFinalize(model, parent);
152
153        if (parent instanceof Component && Boolean.TRUE.equals(((Component) parent).getReadOnly())) {
154            setRender(false);
155        }
156
157        if (!isRender()) {
158            return;
159        }
160
161        View view = ViewLifecycle.getActiveLifecycle().getView();
162
163        if (parent instanceof InputField) {
164            setupForInputField(view, model, (InputField) parent);
165
166            // add field conversions as accessible binding paths
167            if (isRender()) {
168                for (String toField : fieldConversions.values()) {
169                    ViewLifecycle.getViewPostMetadata().addAccessibleBindingPath(toField);
170                }
171            }
172        } else if (parent instanceof CollectionGroup) {
173            setupForCollectionGroup(view, model, (CollectionGroup) parent);
174        }
175
176        setupQuickfinderAction(view, model, parent);
177
178        addCallbackParametersIfPresent();
179    }
180
181    /**
182     * If quickfinder not manually configured attempts to find a relationship to build the quickfinder on, then also
183     * adjusts the path for any configured field conversions, lookup parameters, and refresh refreshes.
184     *
185     * @param view view instance the quickfinder is associated with
186     * @param model object containing the view data
187     * @param inputField input field instance the quickfinder should apply to
188     */
189    protected void setupForInputField(View view, Object model, InputField inputField) {
190        // if quickfinder class name not specified, attempt to find a relationship to build the quickfinder from
191        if (StringUtils.isBlank(dataObjectClassName)) {
192            DataObjectRelationship relationship = getRelationshipForField(view, model, inputField);
193
194            // if no relationship found cannot have a quickfinder
195            if (relationship == null) {
196                setRender(false);
197
198                return;
199            }
200
201            dataObjectClassName = relationship.getRelatedClass().getName();
202
203            if ((fieldConversions == null) || fieldConversions.isEmpty()) {
204                generateFieldConversions(relationship);
205            }
206
207            if ((lookupParameters == null) || lookupParameters.isEmpty()) {
208                generateLookupParameters(relationship);
209            }
210        }
211
212        // adjust paths based on associated attribute field
213        updateFieldConversions(inputField.getBindingInfo());
214        updateLookupParameters(inputField.getBindingInfo());
215        updateReferencesToRefresh(inputField.getBindingInfo());
216
217        // add the quickfinders action as an input field addon
218        inputField.addPostInputAddon(quickfinderAction);
219    }
220
221    /**
222     * Retrieves any {@link org.kuali.rice.krad.bo.DataObjectRelationship} that is associated with the given
223     * field and has a configured lookup view.
224     *
225     * @param view view instance the quickfinder is associated with
226     * @param model object containing the view data
227     * @param field input field instance the quickfinder should apply to
228     * @return data object relationship for the field, or null if one could not be found
229     */
230    protected DataObjectRelationship getRelationshipForField(View view, Object model, InputField field) {
231        String propertyName = field.getBindingInfo().getBindingName();
232
233        // get object instance and class for parent
234        Object parentObject = ViewModelUtils.getParentObjectForMetadata(view, model, field);
235        Class<?> parentObjectClass = null;
236        if (parentObject != null) {
237            parentObjectClass = parentObject.getClass();
238        }
239
240        // get relationship from metadata service
241        if (parentObjectClass != null) {
242            return KRADServiceLocatorWeb.getLegacyDataAdapter().getDataObjectRelationship(parentObject,
243                    parentObjectClass, propertyName, "", true, true, false);
244        }
245
246        return null;
247    }
248
249    /**
250     * Generates the lookup field conversions based on the references from the given relationship.
251     *
252     * @param relationship relationship field conversions will be generated from
253     */
254    protected void generateFieldConversions(DataObjectRelationship relationship) {
255        fieldConversions = new HashMap<String, String>();
256
257        for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
258            String fromField = entry.getValue();
259            String toField = entry.getKey();
260
261            fieldConversions.put(fromField, toField);
262        }
263    }
264
265    /**
266     * Generates the lookup parameters based on the references from the given relationship.
267     *
268     * @param relationship relationship lookup parameters will be generated from
269     */
270    protected void generateLookupParameters(DataObjectRelationship relationship) {
271        lookupParameters = new HashMap<String, String>();
272
273        for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
274            String fromField = entry.getKey();
275            String toField = entry.getValue();
276
277            if (relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey().equals(
278                    fromField)) {
279                lookupParameters.put(fromField, toField);
280            }
281        }
282    }
283
284    /**
285     * Adjusts the path on the field conversion to property to match the binding path prefix of the
286     * given {@link org.kuali.rice.krad.uif.component.BindingInfo}.
287     *
288     * @param bindingInfo binding info instance to copy binding path prefix from
289     */
290    protected void updateFieldConversions(BindingInfo bindingInfo) {
291        Map<String, String> adjustedFieldConversions = new HashMap<String, String>();
292        for (String fromField : fieldConversions.keySet()) {
293            String toField = fieldConversions.get(fromField);
294
295            if (!StringUtils.startsWith(toField, bindingInfo.getBindingPathPrefix())) {
296                String adjustedToFieldPath = bindingInfo.getPropertyAdjustedBindingPath(toField);
297                adjustedFieldConversions.put(fromField, adjustedToFieldPath);
298            } else {
299                adjustedFieldConversions.put(fromField, toField);
300            }
301        }
302
303        this.fieldConversions = adjustedFieldConversions;
304    }
305
306    /**
307     * Adjusts the path on the lookup parameter from property to match the binding path prefix of the
308     * given {@link org.kuali.rice.krad.uif.component.BindingInfo}.
309     *
310     * @param bindingInfo binding info instance to copy binding path prefix from
311     */
312    protected void updateLookupParameters(BindingInfo bindingInfo) {
313        Map<String, String> adjustedLookupParameters = new HashMap<String, String>();
314        for (String fromField : lookupParameters.keySet()) {
315            String toField = lookupParameters.get(fromField);
316            String adjustedFromFieldPath = bindingInfo.getPropertyAdjustedBindingPath(fromField);
317
318            adjustedLookupParameters.put(adjustedFromFieldPath, toField);
319        }
320
321        this.lookupParameters = adjustedLookupParameters;
322    }
323
324    /**
325     * Adjust the path on the referencesToRefresh parameter to match the binding path prefix of the
326     * given {@link org.kuali.rice.krad.uif.component.BindingInfo}.
327     *
328     * @param bindingInfo binding info instance to copy binding path prefix from
329     */
330    protected void updateReferencesToRefresh(BindingInfo bindingInfo) {
331        List<String> adjustedReferencesToRefresh = new ArrayList<String>();
332
333        if (referencesToRefresh == null) {
334            referencesToRefresh = new String();
335        }
336
337        for (String reference : StringUtils.split(referencesToRefresh, KRADConstants.REFERENCES_TO_REFRESH_SEPARATOR)) {
338            adjustedReferencesToRefresh.add(bindingInfo.getPropertyAdjustedBindingPath(reference));
339        }
340
341        this.referencesToRefresh = StringUtils.join(adjustedReferencesToRefresh,
342                KRADConstants.REFERENCES_TO_REFRESH_SEPARATOR);
343    }
344
345    /**
346     * Configures the quickfinder for the given collection group instance by setting the data object class,
347     * field conversions, and lookup collection name (if necessary).
348     *
349     * @param view view instance the quickfinder is associated with
350     * @param model object containing the view data
351     * @param collectionGroup collection group instance to build quickfinder for
352     */
353    protected void setupForCollectionGroup(View view, Object model, CollectionGroup collectionGroup) {
354        // check to see if data object class is configured for lookup, if so we will assume it should be enabled
355        // if not and the class configured for the collection group is lookupable, use that
356        if (StringUtils.isBlank(getDataObjectClassName())) {
357            Class<?> collectionObjectClass = collectionGroup.getCollectionObjectClass();
358
359            boolean isCollectionClassLookupable = KRADServiceLocatorWeb.getViewDictionaryService().isLookupable(
360                    collectionObjectClass);
361            if (isCollectionClassLookupable) {
362                setDataObjectClassName(collectionObjectClass.getName());
363
364                // use PK fields for collection class as default field conversions
365                if ((fieldConversions == null) || fieldConversions.isEmpty()) {
366                    List<String> collectionObjectPKFields =
367                            KRADServiceLocatorWeb.getLegacyDataAdapter().listPrimaryKeyFieldNames(
368                                    collectionObjectClass);
369
370                    fieldConversions = new HashMap<String, String>();
371                    for (String pkField : collectionObjectPKFields) {
372                        fieldConversions.put(pkField, pkField);
373                    }
374                }
375            } else {
376                // no available data object class to lookup so disable quickfinder
377                setRender(false);
378            }
379        }
380
381        // set the lookup return collection name to this collection path
382        if (isRender() && StringUtils.isBlank(getLookupCollectionName())) {
383            setLookupCollectionName(collectionGroup.getBindingInfo().getBindingPath());
384        }
385
386        if (isRender() && StringUtils.isBlank(getLookupCollectionId())) {
387            setLookupCollectionId(collectionGroup.getId());
388        }
389    }
390
391    /**
392     * Adjusts the id for the quickfinder action, and then adds action parameters for passing along the
393     * quickfinder configuration to the lookup view.
394     *
395     * @param view view instance the quickfinder is associated with
396     * @param model object containing the view data
397     * @param parent component instance the quickfinder is associated with
398     */
399    protected void setupQuickfinderAction(View view, Object model, LifecycleElement parent) {
400        quickfinderAction.setId(getId() + UifConstants.IdSuffixes.ACTION);
401
402        if (openInDialog) {
403            String lightboxScript = null;
404            String actionScript = quickfinderAction.getActionScript();
405            if (actionScript == null) {
406                actionScript = "";
407            }
408            String dialogId = quickfinderAction.getActionParameter(UifParameters.DIALOG_ID);
409
410            // if the dialog id is still blank, then look in the initial request parameters in case this was
411            // a request from a nested component (i.e., an action invoked as a result of another action)
412            if (StringUtils.isBlank(dialogId)) {
413                UifFormBase form = (UifFormBase) model;
414                Map<String, String[]> requestParameters = form.getInitialRequestParameters();
415                if (requestParameters != null) {
416                    String[] dialogIds = requestParameters.get(UifParameters.DIALOG_ID);
417                    if (dialogIds != null && dialogIds.length > 0) {
418                        dialogId = dialogIds[0];
419                    }
420                }
421            }
422
423            // set the quickfinder's dialog id when invoked from within a dialog
424            // this accounts for the quickfinder being on the dialog field in which case the dialog is the parent, and
425            // for the quickfinder being in a nested sub-collection in the dialog in which case the dialog is the
426            // quickfinder's parent's (the nested sub-collection) parent
427            if (StringUtils.isBlank(dialogId)) {
428                Object superParent = parent.getContext().get(UifConstants.ContextVariableNames.PARENT);
429                if (superParent != null) {
430                    if (superParent instanceof DialogGroup) {
431                        dialogId = ((DialogGroup) superParent).getId();
432                    } else if (superParent instanceof CollectionGroup) {
433                        CollectionGroup parentCollection = (CollectionGroup) superParent;
434                        superParent = parentCollection.getContext().get(UifConstants.ContextVariableNames.PARENT);
435                        if (superParent instanceof FieldGroup) {
436                            FieldGroup parentFieldGroup = (FieldGroup) superParent;
437                            superParent = parentFieldGroup.getContext().get(UifConstants.ContextVariableNames.PARENT);
438                            if (superParent instanceof DialogGroup) {
439                                dialogId = ((DialogGroup) superParent).getId();
440                            }
441                        }
442                    }
443                }
444            }
445
446            // let lookup know that it's been called from a dialog by passing its dialog id if present
447            if (StringUtils.isBlank(dialogId)) {
448                lightboxScript = UifConstants.JsFunctions.SHOW_LOOKUP_DIALOG + "(\"" + quickfinderAction.getId() + "\","
449                        + returnByScript + ",\"" + lookupDialogId + "\");";
450            } else {
451                lightboxScript = UifConstants.JsFunctions.SHOW_LOOKUP_DIALOG + "(\"" + quickfinderAction.getId() + "\","
452                        + returnByScript + ",\"" + lookupDialogId + "\",\"" + dialogId + "\");";
453
454                // also add the dialog id to the action parameters so that even other nested components have it
455                // no matter the depth of the nodes
456                quickfinderAction.addActionParameter(UifParameters.DIALOG_ID, dialogId);
457            }
458
459            quickfinderAction.setActionScript(actionScript + lightboxScript);
460        }
461
462        quickfinderAction.addActionParameter(UifParameters.BASE_LOOKUP_URL, baseLookupUrl);
463
464        Class dataObjectClass = getDataObjectClass(dataObjectClassName);
465        ModuleService responsibleModuleService =
466                KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(dataObjectClass);
467        if (responsibleModuleService != null && responsibleModuleService.isExternalizable(dataObjectClass)) {
468            if (ExternalizableBusinessObject.class.isAssignableFrom(dataObjectClass)) {
469                Class implementationClass = responsibleModuleService.getExternalizableBusinessObjectImplementation(
470                        dataObjectClass.asSubclass(ExternalizableBusinessObject.class));
471                if (implementationClass != null) {
472                    dataObjectClassName = implementationClass.getName();
473                }
474            }
475        }
476
477        quickfinderAction.addActionParameter(UifParameters.DATA_OBJECT_CLASS_NAME, dataObjectClassName);
478
479        if (!fieldConversions.isEmpty()) {
480            quickfinderAction.addActionParameter(UifParameters.CONVERSION_FIELDS, KRADUtils.buildMapParameterString(
481                    fieldConversions));
482        }
483
484        if (!lookupParameters.isEmpty()) {
485            quickfinderAction.addActionParameter(UifParameters.LOOKUP_PARAMETERS, KRADUtils.buildMapParameterString(
486                    lookupParameters));
487        }
488
489        addActionParameterIfNotNull(UifParameters.VIEW_NAME, viewName);
490        addActionParameterIfNotNull(UifParameters.READ_ONLY_FIELDS, readOnlyLookupFields);
491        addActionParameterIfNotNull(UifParameters.RENDER_RETURN_LINK, renderReturnLink);
492        addActionParameterIfNotNull(UifParameters.RENDER_RESULT_ACTIONS, renderResultActions);
493        addActionParameterIfNotNull(UifParameters.REFERENCES_TO_REFRESH, referencesToRefresh);
494        addActionParameterIfNotNull(UifParameters.AUTO_SEARCH, autoSearch);
495        addActionParameterIfNotNull(UifParameters.RENDER_LOOKUP_CRITERIA, renderLookupCriteria);
496        addActionParameterIfNotNull(UifParameters.RENDER_CRITERIA_ACTIONS, renderCriteriaActions);
497        addActionParameterIfNotNull(UifParameters.HIDE_CRITERIA_ON_SEARCH, hideCriteriaOnSearch);
498        addActionParameterIfNotNull(UifParameters.RENDER_MAINTENANCE_LINKS, renderMaintenanceLinks);
499        addActionParameterIfNotNull(UifParameters.MULTIPLE_VALUES_SELECT, multipleValuesSelect);
500        addActionParameterIfNotNull(UifParameters.LOOKUP_COLLECTION_NAME, lookupCollectionName);
501        addActionParameterIfNotNull(UifParameters.LOOKUP_COLLECTION_ID, lookupCollectionId);
502        addActionParameterIfNotNull(UifParameters.QUICKFINDER_ID, getId());
503
504        //insert additional lookup parameters.
505        if (additionalLookupParameters != null) {
506            //copy additional parameters to actionParameters
507            Map<String, String> actionParameters = quickfinderAction.getActionParameters();
508            actionParameters.putAll(additionalLookupParameters);
509            quickfinderAction.setActionParameters(actionParameters);
510        }
511
512       quickfinderAction.performFinalize(model, parent);
513    }
514
515    private Class<?> getDataObjectClass(String className) {
516        Class<?> dataObjectClass;
517
518        try {
519            dataObjectClass = Class.forName(className);
520        } catch (ClassNotFoundException e) {
521            throw new RuntimeException("Unable to get class for name: " + className, e);
522        }
523
524        return dataObjectClass;
525    }
526
527    /**
528     * Utility method to add an action parameter to the quickfinder action if the given parameter value
529     * is non blank.
530     *
531     * @param parameterName name of the parameter to add
532     * @param parameterValue value for the parameter to add
533     */
534    protected void addActionParameterIfNotNull(String parameterName, Object parameterValue) {
535        if ((parameterValue != null) && StringUtils.isNotBlank(parameterValue.toString())) {
536            quickfinderAction.addActionParameter(parameterName, parameterValue.toString());
537        }
538    }
539
540    /**
541     * Adds post context data for the quickfinder so when the lookup return occurs the focus and jump point
542     * of the quickfinder action can be retrieved.
543     *
544     * {@inheritDoc}
545     */
546    @Override
547    public void processEvent(ViewLifecycle.LifecycleEvent lifecycleEvent, View view, Object model,
548            LifecycleElement eventComponent) {
549        Action finalQuickfinderAction = (Action) eventComponent;
550
551        // add post metadata for focus point when the associated lookup returns
552        ViewLifecycle.getViewPostMetadata().addComponentPostData(this, UifConstants.PostMetadata.QUICKFINDER_FOCUS_ID,
553                finalQuickfinderAction.getFocusOnIdAfterSubmit());
554        ViewLifecycle.getViewPostMetadata().addComponentPostData(this, UifConstants.PostMetadata.QUICKFINDER_JUMP_TO_ID,
555                finalQuickfinderAction.getJumpToIdAfterSubmit());
556    }
557
558    /**
559     * Adds callback method and its parameters to post data so that when a refresh occurs it knows
560     * which view is returned from and possibly which collection line the quickfinder was on.
561     */
562    protected void addCallbackParametersIfPresent() {
563        if (StringUtils.isNotBlank(callbackMethodToCall)) {
564            ViewLifecycle.getViewPostMetadata().addComponentPostData(this,
565                    UifConstants.PostMetadata.QUICKFINDER_CALLBACK_METHOD_TO_CALL, callbackMethodToCall);
566        }
567
568        if (callbackMethod != null) {
569            ViewLifecycle.getViewPostMetadata().addComponentPostData(this,
570                    UifConstants.PostMetadata.QUICKFINDER_CALLBACK_METHOD, callbackMethod);
571        }
572
573        if (callbackContext != null && !callbackContext.isEmpty()) {
574            ViewLifecycle.getViewPostMetadata().addComponentPostData(this,
575                    UifConstants.PostMetadata.QUICKFINDER_CALLBACK_CONTEXT, callbackContext);
576        }
577    }
578
579    /**
580     * Returns the URL for the lookup for which parameters will be added.
581     *
582     * <p>The base URL includes the domain, context, and controller mapping for the lookup invocation. Parameters are
583     * then added based on configuration to complete the URL. This is generally defaulted to the application URL and
584     * internal KRAD servlet mapping, but can be changed to invoke another application such as the Rice standalone
585     * server</p>
586     *
587     * @return lookup base URL
588     */
589    @BeanTagAttribute
590    public String getBaseLookupUrl() {
591        return this.baseLookupUrl;
592    }
593
594    /**
595     * @see QuickFinder#getBaseLookupUrl()
596     */
597    public void setBaseLookupUrl(String baseLookupUrl) {
598        this.baseLookupUrl = baseLookupUrl;
599    }
600
601    /**
602     * Full class name the lookup should be provided for.
603     *
604     * <p>This is passed on to the lookup request for the data object the lookup should be rendered for. This is then
605     * used by the lookup framework to select the lookup view (if more than one lookup view exists for the same
606     * data object class name, the {@link #getViewName()} property should be specified to select the view to
607     * render).</p>
608     *
609     * @return lookup class name
610     */
611    @BeanTagAttribute
612    public String getDataObjectClassName() {
613        return this.dataObjectClassName;
614    }
615
616    /**
617     * @see QuickFinder#getDataObjectClassName()
618     */
619    public void setDataObjectClassName(String dataObjectClassName) {
620        this.dataObjectClassName = dataObjectClassName;
621    }
622
623    /**
624     * When multiple target lookup views exists for the same data object class, the view name can be set to
625     * determine which one to use.
626     *
627     * <p>When creating multiple lookup views for the same data object class, the view name can be specified for the
628     * different versions (for example 'simple' and 'advanced'). When multiple lookup views exist the view name must
629     * be sent with the data object class for the request. Note the view id can be alternatively used to uniquely
630     * identify the lookup view</p>
631     *
632     * @return String name of lookup view
633     */
634    @BeanTagAttribute
635    public String getViewName() {
636        return this.viewName;
637    }
638
639    /**
640     * @see QuickFinder#getViewName()
641     */
642    public void setViewName(String viewName) {
643        this.viewName = viewName;
644    }
645
646    /**
647     * Indicates whether the lookup return should occur through script, or by refresing the page (making server
648     * request).
649     *
650     * <p>For quickfinders that do not need any additional server side action, return through script can be
651     * much faster and prevents a page refresh.</p>
652     *
653     * @return boolean true if the return should occur through script, false if not (default)
654     */
655    @BeanTagAttribute
656    public boolean isReturnByScript() {
657        return returnByScript;
658    }
659
660    /**
661     * @see QuickFinder#isReturnByScript()
662     */
663    public void setReturnByScript(boolean returnByScript) {
664        this.returnByScript = returnByScript;
665    }
666
667    /**
668     * Comma delimited String of property names on the lookup view that should be read only.
669     *
670     * <p>When requesting a lookup view, property names for fields that are rendered as search criteria can be marked
671     * as read-only. This is usually done when a lookup parameter for that property is sent in and the user should
672     * not be allowed to change the value</p>
673     *
674     * @return property names (delimited by a comma) whose criteria fields should be read-only on the
675     * lookup view
676     */
677    @BeanTagAttribute
678    public String getReadOnlyLookupFields() {
679        return this.readOnlyLookupFields;
680    }
681
682    /**
683     * @see QuickFinder#setReadOnlyLookupFields(java.lang.String)
684     */
685    public void setReadOnlyLookupFields(String readOnlyLookupFields) {
686        this.readOnlyLookupFields = readOnlyLookupFields;
687    }
688
689    /**
690     * List of property names on the model that should be refreshed when the lookup returns.
691     *
692     * <p>Note this is only relevant when the return by script option is not enabled (meaning the server will be
693     * invoked
694     * on the lookup return call)</p>
695     *
696     * <p>When a lookup return call is made (to return a result value) the controller refresh method will be invoked.
697     * If
698     * refresh properties are configured, a call to refresh those references from the database will be made. This is
699     * useful if the lookup returns a foreign key field and the related record is needed.</p>
700     *
701     * @return list of property names to refresh
702     */
703    @BeanTagAttribute
704    public String getReferencesToRefresh() {
705        return this.referencesToRefresh;
706    }
707
708    /**
709     * @see QuickFinder#getReferencesToRefresh()
710     */
711    public void setReferencesToRefresh(String referencesToRefresh) {
712        this.referencesToRefresh = referencesToRefresh;
713    }
714
715    /**
716     * Map that determines what properties from a result lookup row (if selected) will be returned to properties on
717     * the calling view.
718     *
719     * <p>The purpose of using the lookup is to search for a particular value and return that value to the form being
720     * completed. In order for the lookup framework to return the field back to us, we must specify the name of the
721     * field on the data object class whose value we need, and the name of the field on the calling view. Furthermore,
722     * we can choose to have the lookup return additional fields that populate other form fields or informational
723     * properties (see ‘Field Queries and Informational Properties’). These pairs of fields are known as
724     * ‘field conversions’.</p>
725     *
726     * <p>The fieldConversions property is a Map. Each entry represents a field that will be returned back from the
727     * lookup, with the entry key being the field name on the data object class, and the entry value being the field
728     * name on the calling view. It is helpful to think of this as a from-to mapping. Pulling from the data object
729     * field (map key) to the calling view field (map value).</p>
730     *
731     * @return mapping of lookup data object property names to view property names
732     */
733    @BeanTagAttribute
734    public Map<String, String> getFieldConversions() {
735        return this.fieldConversions;
736    }
737
738    /**
739     * @see QuickFinder#getFieldConversions()
740     */
741    public void setFieldConversions(Map<String, String> fieldConversions) {
742        this.fieldConversions = fieldConversions;
743    }
744
745    /**
746     * Map that determines what properties from a calling view will be sent to properties on that are rendered
747     * for the lookup view's search fields (they can be hidden).
748     *
749     * <p> When invoking a lookup view, we can pre-populate search fields on the lookup view with data from the view
750     * that called the lookup. The user can then perform the search with these values, or (if edited is allowed or
751     * the fields are not hidden) change the passed in values. When the lookup is invoked, the values for the
752     * properties configured within the lookup parameters Map will be pulled and passed along as values for the
753     * lookup view properties</p>
754     *
755     * @return mapping of calling view properties to lookup view search fields
756     */
757    @BeanTagAttribute
758    public Map<String, String> getLookupParameters() {
759        return this.lookupParameters;
760    }
761
762    /**
763     * @see QuickFinder#getLookupParameters()
764     */
765    public void setLookupParameters(Map<String, String> lookupParameters) {
766        this.lookupParameters = lookupParameters;
767    }
768
769    /**
770     * Indicates whether the return links for lookup results should be rendered.
771     *
772     * <p>A lookup view can be invoked to allow the user to select a value (or set of values) to return back to the
773     * calling view. For single value lookups this is done with a return link that is rendered for each row. This
774     * return link can be disabled by setting this property to false</p>
775     *
776     * @return true if the return link should not be shown, false if it should be
777     */
778    @BeanTagAttribute
779    public Boolean getRenderReturnLink() {
780        return this.renderReturnLink;
781    }
782
783    /**
784     * @see QuickFinder#getRenderReturnLink()
785     */
786    public void setRenderReturnLink(Boolean renderReturnLink) {
787        this.renderReturnLink = renderReturnLink;
788    }
789
790    /**
791     * Indicates whether the maintenance actions (or others) are rendered on the invoked lookup view.
792     *
793     * <p>By default a lookup view will add an actions column for the result table that display maintenance links (in
794     * addition to a new link at the top of the page) if a maintenance action is available. Custom links can also be
795     * added to the action column as necessary. This flag can be set to true to suppress the rendering of the actions
796     * for the lookup call.</p>
797     *
798     * @return true if actions should be rendered, false if not
799     */
800    @BeanTagAttribute
801    public Boolean getRenderResultActions() {
802        return renderResultActions;
803    }
804
805    /**
806     * @see QuickFinder#getRenderResultActions()
807     */
808    public void setRenderResultActions(Boolean renderResultActions) {
809        this.renderResultActions = renderResultActions;
810    }
811
812    /**
813     * Indicates whether the search should be executed when first rendering the lookup view.
814     *
815     * <p>By default the lookup view is rendered, the user enters search values and executes the results. This flag can
816     * be set to true to indicate the search should be performed before showing the screen to the user. This is
817     * generally used when search criteria is being passed in as well</p>
818     *
819     * @return true if the search should be performed initially, false if not
820     */
821    @BeanTagAttribute
822    public Boolean getAutoSearch() {
823        return this.autoSearch;
824    }
825
826    /**
827     * @see org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute#name()
828     */
829    public void setAutoSearch(Boolean autoSearch) {
830        this.autoSearch = autoSearch;
831    }
832
833    /**
834     * Indicates whether the lookup criteria (search group) should be enabled on the invoked lookup view.
835     *
836     * <p> Setting the this to false will not display the lookup criteria but only the results. Therefore this is only
837     * useful when setting {@link #getAutoSearch()} to true and passing in criteria</p>
838     *
839     * @return true if lookup criteria should be displayed, false if not
840     */
841    @BeanTagAttribute
842    public Boolean getRenderLookupCriteria() {
843        return this.renderLookupCriteria;
844    }
845
846    /**
847     * @see QuickFinder#getRenderLookupCriteria()
848     */
849    public void setRenderLookupCriteria(Boolean renderLookupCriteria) {
850        this.renderLookupCriteria = renderLookupCriteria;
851    }
852
853    /**
854     * Indicates whether the criteria actions (footer) should be rendered on the invoked lookup view.
855     *
856     * @return boolean true if actions should be rendered (default), false if not
857     */
858    @BeanTagAttribute
859    public Boolean getRenderCriteriaActions() {
860        return this.renderCriteriaActions;
861    }
862
863    /**
864     * @see QuickFinder#getRenderCriteriaActions()
865     */
866    public void setRenderCriteriaActions(Boolean renderCriteriaActions) {
867        this.renderCriteriaActions = renderCriteriaActions;
868    }
869
870    /**
871     * Indicates whether the lookup criteria should be hidden when a search is executed.
872     *
873     * @return boolean true if criteria should be hidden, false if not
874     */
875    @BeanTagAttribute
876    public Boolean getHideCriteriaOnSearch() {
877        return hideCriteriaOnSearch;
878    }
879
880    /**
881     * @see QuickFinder#getHideCriteriaOnSearch()
882     */
883    public void setHideCriteriaOnSearch(Boolean hideCriteriaOnSearch) {
884        this.hideCriteriaOnSearch = hideCriteriaOnSearch;
885    }
886
887    /**
888     * Indicates whether the maintenance action links should be rendered for the invoked lookup view.
889     *
890     * <p>If a maintenance view exists for the data object associated with the lookup view, the framework will add
891     * links to initiate a new maintenance document. This flag can be used to disable the rendering of these links</p>
892     *
893     * <p> Note this serves similar purpose to {@link #getRenderResultActions()} but the intent is to only remove the
894     * maintenance links in this situation, not the complete actions column</p>
895     *
896     * @return true if maintenance links should be shown on the lookup view, false if not
897     */
898    @BeanTagAttribute
899    public Boolean getRenderMaintenanceLinks() {
900        return this.renderMaintenanceLinks;
901    }
902
903    /**
904     * @see QuickFinder#getRenderMaintenanceLinks()
905     */
906    public void setRenderMaintenanceLinks(Boolean renderMaintenanceLinks) {
907        this.renderMaintenanceLinks = renderMaintenanceLinks;
908    }
909
910    /**
911     * Action component that is used to rendered for the field for invoking the quickfinder action (bringing up the
912     * lookup).
913     *
914     * <p>Through the action configuration the image (or link, button) rendered for the quickfinder can be modified. In
915     * addition to other action component settings</p>
916     *
917     * @return Action instance rendered for quickfinder
918     */
919    @ViewLifecycleRestriction(exclude=UifConstants.ViewPhases.FINALIZE)
920    @BeanTagAttribute(type = BeanTagAttribute.AttributeType.BYTYPE)
921    public Action getQuickfinderAction() {
922        return this.quickfinderAction;
923    }
924
925    /**
926     * @see QuickFinder#getQuickfinderAction()
927     */
928    public void setQuickfinderAction(Action quickfinderAction) {
929        this.quickfinderAction = quickfinderAction;
930    }
931
932    /**
933     * The id of the DialogGroup to use when the openInDialog property is true.
934     *
935     * <p>The DialogGroup should only contain an iframe for its items.  When not set, a default dialog
936     * will be used.</p>
937     *
938     * @return the id of the dialog to use for this quickfinder
939     */
940    @BeanTagAttribute
941    public String getLookupDialogId() {
942        return lookupDialogId;
943    }
944
945    /**
946     * @see QuickFinder#getLookupDialogId()
947     */
948    public void setLookupDialogId(String lookupDialogId) {
949        this.lookupDialogId = lookupDialogId;
950    }
951
952    /**
953     * True if the quickfinder's lookup should be opened in a dialog; true is the default setting for the
954     * bean.
955     *
956     * @return true if the lookup should be opened in a dialog, false to open in a new window
957     */
958    @BeanTagAttribute
959    public boolean isOpenInDialog() {
960        return openInDialog;
961    }
962
963    /**
964     * @see QuickFinder#isOpenInDialog()
965     */
966    public void setOpenInDialog(boolean openInDialog) {
967        this.openInDialog = openInDialog;
968    }
969
970    /**
971     * Indicates whether the invoked lookup view should allow multiple values to be selected and returned.
972     *
973     * @return true if multi-value lookup should be requested, false for normal lookup
974     */
975    @BeanTagAttribute
976    public Boolean getMultipleValuesSelect() {
977        return multipleValuesSelect;
978    }
979
980    /**
981     * @see QuickFinder#getMultipleValuesSelect()
982     */
983    public void setMultipleValuesSelect(Boolean multipleValuesSelect) {
984        this.multipleValuesSelect = multipleValuesSelect;
985    }
986
987    /**
988     * For the case of multi-value lookup, indicates the collection that should be populated with
989     * the return results.
990     *
991     * <p>Note when the quickfinder is associated with a {@link CollectionGroup}, this property is
992     * set automatically from the collection name associated with the group</p>
993     *
994     * @return collection name (must be full binding path)
995     */
996    @BeanTagAttribute
997    public String getLookupCollectionName() {
998        return lookupCollectionName;
999    }
1000
1001    /**
1002     * @see QuickFinder#getLookupCollectionName()
1003     */
1004    public void setLookupCollectionName(String lookupCollectionName) {
1005        this.lookupCollectionName = lookupCollectionName;
1006    }
1007
1008    /**
1009     * For the case of multi-value lookup, indicates the collection id that should be populated with
1010     * the return results.
1011     *
1012     * <p>Note when the quickfinder is associated with a {@link CollectionGroup}, this property is
1013     * set automatically from the collection id associated with the group</p>
1014     *
1015     * @return collection id
1016     */
1017    @BeanTagAttribute
1018    public String getLookupCollectionId() {
1019        return lookupCollectionId;
1020    }
1021
1022    /**
1023     * @see QuickFinder#getLookupCollectionId()
1024     */
1025    public void setLookupCollectionId(String lookupCollectionId) {
1026        this.lookupCollectionId = lookupCollectionId;
1027    }
1028
1029    /**
1030     * The additional parameters that were passed to the quickFinder.
1031     *
1032     * @return additionalLookupParameters - map of additional lookup parameters
1033     */
1034    @BeanTagAttribute
1035    public Map<String, String> getAdditionalLookupParameters() {
1036        return additionalLookupParameters;
1037    }
1038
1039    /**
1040     * @see QuickFinder#getAdditionalLookupParameters()
1041     */
1042    public void setAdditionalLookupParameters(Map<String, String> additionalLookupParameters) {
1043        this.additionalLookupParameters = additionalLookupParameters;
1044    }
1045
1046    /**
1047     * The name of the callback method to invoke in the view helper service that checks
1048     * request parameters to indicate what view is being returned from.
1049     *
1050     * @return callbackMethodToCall - the name of the callback method
1051     */
1052    public String getCallbackMethodToCall() {
1053        return callbackMethodToCall;
1054    }
1055
1056    /**
1057     * @see QuickFinder#getCallbackMethodToCall()
1058     */
1059    public void setCallbackMethodToCall(String callbackMethodToCall) {
1060        this.callbackMethodToCall = callbackMethodToCall;
1061    }
1062
1063    /**
1064     * The specific method invoker to use to invoke the callback method to call.
1065     *
1066     * @return callbackMethod - the method invoker
1067     */
1068    public MethodInvokerConfig getCallbackMethod() {
1069        return callbackMethod;
1070    }
1071
1072    /**
1073     * @see QuickFinder#getCallbackMethod()
1074     */
1075    public void setCallbackMethod(MethodInvokerConfig callbackMethod) {
1076        this.callbackMethod = callbackMethod;
1077    }
1078
1079    /**
1080     * The context of parameters to be provided to the callback method to call.
1081     *
1082     * @return callbackContext - map of parameters
1083     */
1084    public Map<String, String> getCallbackContext() {
1085        return callbackContext;
1086    }
1087
1088    /**
1089     * @see QuickFinder#getCallbackContext()
1090     */
1091    public void setCallbackContext(Map<String, String> callbackContext) {
1092        this.callbackContext = callbackContext;
1093    }
1094}