View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.view;
17  
18  import com.google.common.collect.Lists;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.mo.common.active.Inactivatable;
21  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
22  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
23  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
24  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
25  import org.kuali.rice.krad.uif.UifConstants.ViewType;
26  import org.kuali.rice.krad.uif.UifPropertyPaths;
27  import org.kuali.rice.krad.uif.container.CollectionGroup;
28  import org.kuali.rice.krad.uif.container.Group;
29  import org.kuali.rice.krad.uif.component.Component;
30  import org.kuali.rice.krad.uif.component.RequestParameter;
31  import org.kuali.rice.krad.uif.control.Control;
32  import org.kuali.rice.krad.uif.control.TextAreaControl;
33  import org.kuali.rice.krad.uif.control.TextControl;
34  import org.kuali.rice.krad.uif.element.Action;
35  import org.kuali.rice.krad.uif.element.Message;
36  import org.kuali.rice.krad.uif.field.FieldGroup;
37  import org.kuali.rice.krad.uif.field.InputField;
38  import org.kuali.rice.krad.uif.field.LookupInputField;
39  import org.kuali.rice.krad.uif.util.ComponentFactory;
40  import org.kuali.rice.krad.uif.util.ComponentUtils;
41  import org.kuali.rice.krad.util.KRADConstants;
42  import org.kuali.rice.krad.web.form.LookupForm;
43  
44  import java.util.ArrayList;
45  import java.util.Arrays;
46  import java.util.HashMap;
47  import java.util.List;
48  
49  /**
50   * View type for lookups
51   *
52   * <p>
53   * Supports doing a search against a data object class or performing a more advanced query. The view
54   * type is primarily made up of two groups, the search (or criteria) group and the results group. Many
55   * options are supported on the view to enable/disable certain features, like what actions are available
56   * on the search results.
57   * </p>
58   *
59   * <p>
60   * Works in conjunction with <code>LookupableImpl</code> which customizes the view and carries out the
61   * business functionality
62   * </p>
63   *
64   * @author Kuali Rice Team (rice.collab@kuali.org)
65   */
66  @BeanTag(name = "lookupView-bean", parent = "Uif-LookupView")
67  public class LookupView extends FormView {
68      private static final long serialVersionUID = 716926008488403616L;
69  
70      private Class<?> dataObjectClassName;
71  
72      private Group criteriaGroup;
73      private CollectionGroup resultsGroup;
74  
75      private List<Component> criteriaFields;
76      private List<Component> resultFields;
77      private List<String> defaultSortAttributeNames;
78  
79      protected boolean defaultSortAscending = true;
80  
81      @RequestParameter
82      private boolean hideReturnLinks = false;
83      @RequestParameter
84      private boolean suppressActions = false;
85      @RequestParameter
86      private boolean showMaintenanceLinks = false;
87      @RequestParameter
88      private boolean multipleValuesSelect = false;
89      @RequestParameter
90      private boolean renderLookupCriteria = true;
91      @RequestParameter
92      private boolean renderSearchButtons = true;
93      @RequestParameter
94      private boolean renderHeader = true;
95  
96      @RequestParameter
97      private String returnTarget;
98  
99      @RequestParameter
100     private boolean returnByScript;
101 
102     private boolean triggerOnChange;
103 
104     private Integer resultSetLimit = null;
105     private Integer multipleValuesSelectResultSetLimit = null;
106 
107     private String maintenanceUrlMapping;
108 
109     private FieldGroup rangeFieldGroupPrototype;
110 
111     private Message rangedToMessage;
112 
113     private boolean autoAddActiveCriteria;
114 
115     private List<String> additionalSecurePropertyNames;
116 
117     public LookupView() {
118         super();
119 
120         setViewTypeName(ViewType.LOOKUP);
121         setApplyDirtyCheck(false);
122         setTriggerOnChange(false);
123         setAutoAddActiveCriteria(true);
124         
125         additionalSecurePropertyNames = new ArrayList<String>();
126     }
127 
128     /**
129      * The following initialization is performed:
130      *
131      * <ul>
132      * <li>Set the abstractTypeClasses map for the lookup object path</li>
133      * </ul>
134      *
135      * @see org.kuali.rice.krad.uif.container.ContainerBase#performInitialization(org.kuali.rice.krad.uif.view.View,
136      *      java.lang.Object)
137      */
138     @Override
139     public void performInitialization(View view, Object model) {
140 
141         boolean isInactivatableClass = Inactivatable.class.isAssignableFrom(dataObjectClassName);
142 
143         if (autoAddActiveCriteria && isInactivatableClass) {
144             autoAddActiveCriteria();
145         }
146 
147         initializeGroups();
148 
149         // since we don't have these as prototypes need to assign ids here
150         view.assignComponentIds(getCriteriaGroup());
151         view.assignComponentIds(getResultsGroup());
152 
153         if (getItems().isEmpty()) {
154             setItems(Arrays.asList(getCriteriaGroup(), getResultsGroup()));
155         }
156 
157         super.performInitialization(view, model);
158 
159         // if this is a multi-value lookup, don't show return column
160         if (multipleValuesSelect) {
161             hideReturnLinks = true;
162         }
163 
164         getObjectPathToConcreteClassMapping().put(UifPropertyPaths.LOOKUP_CRITERIA, getDataObjectClassName());
165         if (StringUtils.isNotBlank(getDefaultBindingObjectPath())) {
166             getObjectPathToConcreteClassMapping().put(getDefaultBindingObjectPath(), getDataObjectClassName());
167         }
168     }
169 
170     /**
171      * Adds the 'active' property criteria to the criteria fields if the BO is inactivatable
172      */
173     private void autoAddActiveCriteria() {
174         boolean hasActiveCriteria = false;
175 
176         for (Component field : getCriteriaFields()) {
177             if (((InputField)field).getPropertyName().equals("active")) {
178                 hasActiveCriteria = true;
179             }
180         }
181 
182         if (!hasActiveCriteria) {
183             AttributeDefinition attributeDefinition = KRADServiceLocatorWeb.getDataDictionaryService().getAttributeDefinition(
184                     dataObjectClassName.getName(), "active");
185             LookupInputField activeField = new LookupInputField();
186 
187             if (attributeDefinition == null) {
188                 activeField = (LookupInputField)ComponentFactory.getNewComponentInstance("Uif-LookupActiveInputField");
189             }else{
190                 activeField = (LookupInputField)ComponentFactory.getNewComponentInstance("Uif-LookupCriteriaInputField");
191                 activeField.setPropertyName("active");
192                 activeField.copyFromAttributeDefinition(this, attributeDefinition);
193             }
194 
195             getCriteriaFields().add(activeField);
196         }
197     }
198 
199     protected void initializeGroups() {
200         if (renderLookupCriteria && (getCriteriaGroup() != null) && (getCriteriaGroup().getItems().isEmpty())) {
201             getCriteriaGroup().setItems(getCriteriaFields());
202         }
203 
204         if (getResultsGroup() != null) {
205             if ((getResultsGroup().getItems().isEmpty()) && (getResultFields() != null)) {
206                 getResultsGroup().setItems(getResultFields());
207             }
208             if (getResultsGroup().getCollectionObjectClass() == null) {
209                 getResultsGroup().setCollectionObjectClass(getDataObjectClassName());
210             }
211         }
212     }
213 
214     /**
215      * @see org.kuali.rice.krad.uif.container.ContainerBase#performApplyModel(View, Object,
216      * org.kuali.rice.krad.uif.component.Component)
217      */
218     @Override
219     public void performApplyModel(View view, Object model, Component parent) {
220         LookupForm lookupForm = (LookupForm) model;
221 
222         if (!renderSearchButtons) {
223             criteriaGroup.getFooter().setRender(false);
224         }
225 
226         if (!renderLookupCriteria) {
227             criteriaGroup.setRender(false);
228         }
229 
230         if (!renderHeader) {
231             getHeader().setRender(false);
232         }
233 
234         setupLookupCriteriaFields(view, model);
235 
236         // Get the search action button for trigger on change and trigger on enter
237         Group actionGroup = criteriaGroup.getFooter();
238         Action searchButton = findSearchButton(actionGroup.getItems());
239 
240         // Only add trigger on script if an action with methodToCall search exists
241         if (searchButton != null) {
242             String searchButtonId = searchButton.getId();
243 
244             for (Component criteriaField : criteriaGroup.getItems()) {
245                 addTriggerScripts(searchButtonId, criteriaField);
246             }
247         }
248 
249         super.performApplyModel(view, model, parent);
250     }
251 
252     /**
253      * @see org.kuali.rice.krad.uif.container.ContainerBase#performFinalize(org.kuali.rice.krad.uif.view.View,
254      *      Object, org.kuali.rice.krad.uif.component.Component)
255      */
256     @Override
257     public void performFinalize(View view, Object model, Component parent) {
258         super.performFinalize(view, model, parent);
259 
260         // force session persistence of criteria fields so we can validate the search input
261         List<InputField> fields = ComponentUtils.getComponentsOfTypeDeep(criteriaGroup, InputField.class);
262         for (InputField field : fields) {
263             field.setForceSessionPersistence(true);
264         }
265     }
266 
267     /**
268      * Adds an on change script to fields with the isTriggerOnChange set to true. Also prevents adds script to execute
269      * search on enter when focus is in a criteris field
270      *
271      * @param searchButtonId the id of the search button
272      * @param criteriaField that the script will be added to
273      */
274     private void addTriggerScripts(String searchButtonId, Component criteriaField) {
275         if (criteriaField instanceof LookupInputField) {
276 
277             criteriaField.setOnKeyPressScript("if(e.which == 13) { e.preventDefault();jQuery('#" + searchButtonId + "' ).click();}");
278 
279             if (isTriggerOnChange() || ((LookupInputField)criteriaField).isTriggerOnChange()) {
280                 criteriaField.setOnChangeScript("jQuery('#" + searchButtonId + "' ).click();");
281             }
282         }
283     }
284 
285     /**
286      * Finds an Action with the search methodToCall from a list of Actions
287      *
288      * @param componentList list of components
289      * @return the Action component with methodToCall of search
290      */
291     private Action findSearchButton(List<? extends Component> componentList) {
292         List<? extends Action> actionList = ComponentUtils.getComponentsOfType(componentList, Action.class);
293         for (Action action : actionList) {
294             String methodToCall = action.getMethodToCall();
295             if (methodToCall != null && methodToCall.equals("search")) {
296                 return action;
297             }
298         }
299         return null;
300     }
301 
302     /**
303      * Helper method to do any lookup specific changes to the criteria fields
304      */
305     private void setupLookupCriteriaFields(View view, Object model) {
306         HashMap<Integer, Component> dateRangeFieldMap = new HashMap<Integer, Component>();
307 
308         ExpressionEvaluator expressionEvaluator =
309                 view.getViewHelperService().getExpressionEvaluator();
310 
311         int rangeIndex = 0;
312         for (Component criteriaField : criteriaGroup.getItems()) {
313             // Set the max length on the controls to allow for wildcards
314             Control control = ((InputField)criteriaField).getControl();
315             if (control instanceof TextControl) {
316                 ((TextControl) control).setMaxLength(null);
317             } else if (control instanceof TextAreaControl) {
318                 ((TextAreaControl) control).setMaxLength(null);
319             }
320 
321             if (((LookupInputField)criteriaField).isRanged()) {
322                 // Create field group
323                 FieldGroup rangeFieldGroup = ComponentUtils.copy(rangeFieldGroupPrototype, criteriaField.getId());
324                 rangeFieldGroup.setLabel(((LookupInputField)criteriaField).getLabel());
325 
326                 // Evaluate and set the required property and reset the required message on the 'to' label
327                 expressionEvaluator.evaluatePropertyExpression(view, criteriaField.getContext(), criteriaField,
328                         "required", true);
329                 rangeFieldGroup.setRequired(criteriaField.getRequired());
330                 ((LookupInputField) criteriaField).getFieldLabel().setRequiredMessage(new Message());
331 
332                 // Evaluate and set the render property
333                 expressionEvaluator.evaluatePropertyExpression(view, criteriaField.getContext(), criteriaField,
334                         UifPropertyPaths.RENDER, true);
335                 rangeFieldGroup.setRender(criteriaField.isRender());
336 
337                 List<Component> fieldGroupItems = new ArrayList<Component>();
338 
339                 // Create a new from date field
340                 LookupInputField fromDate = (LookupInputField) ComponentUtils.copy(criteriaField,
341                         KRADConstants.LOOKUP_DEFAULT_RANGE_SEARCH_LOWER_BOUND_LABEL);
342                 fromDate.getBindingInfo().setBindingName(
343                         KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + fromDate.getPropertyName());
344                 fromDate.setPropertyName(
345                         KRADConstants.LOOKUP_RANGE_LOWER_BOUND_PROPERTY_PREFIX + fromDate.getPropertyName());
346 
347                 // Set the criteria fields labels
348                 fromDate.setLabel("");
349                 fromDate.getFieldLabel().setRenderColon(false);
350                 ((LookupInputField)criteriaField).getFieldLabel().setRender(false);
351 
352                 // Add the cirteria fields to the field group
353                 fieldGroupItems.add(fromDate);
354                 fieldGroupItems.add(rangedToMessage);
355                 fieldGroupItems.add(criteriaField);
356                 rangeFieldGroup.setItems(fieldGroupItems);
357 
358                 // Add fieldgroup to map with index as key
359                 dateRangeFieldMap.put(rangeIndex, rangeFieldGroup);
360             }
361 
362             rangeIndex++;
363         }
364 
365         // Replace original fields with range fieldgroups
366         List<Component> itemList = (List<Component>)criteriaGroup.getItems();
367         for (Integer index : dateRangeFieldMap.keySet()) {
368                 itemList.set(index, dateRangeFieldMap.get(index));
369         }
370 
371         criteriaGroup.setItems(itemList);
372     }
373 
374     /**
375      * @see org.kuali.rice.krad.uif.component.Component#getComponentPrototypes()
376      */
377     @Override
378     public List<Component> getComponentPrototypes() {
379         List<Component> components = super.getComponentPrototypes();
380 
381         components.add(rangeFieldGroupPrototype);
382         components.add(rangedToMessage);
383 
384         return components;
385     }
386 
387     public void applyConditionalLogicForFieldDisplay() {
388         // TODO: work into view lifecycle
389         //	    LookupViewHelperService lookupViewHelperService = (LookupViewHelperService) getViewHelperService();
390         //		Set<String> readOnlyFields = lookupViewHelperService.getConditionallyReadOnlyPropertyNames();
391         //		Set<String> requiredFields = lookupViewHelperService.getConditionallyRequiredPropertyNames();
392         //		Set<String> hiddenFields = lookupViewHelperService.getConditionallyHiddenPropertyNames();
393         //		if ( (readOnlyFields != null && !readOnlyFields.isEmpty()) ||
394         //			 (requiredFields != null && !requiredFields.isEmpty()) ||
395         //			 (hiddenFields != null && !hiddenFields.isEmpty())
396         //			) {
397         //			for (Field field : getResultsGroup().getItems()) {
398         //				if (InputField.class.isAssignableFrom(field.getClass())) {
399         //					InputField attributeField = (InputField) field;
400         //					if (readOnlyFields != null && readOnlyFields.contains(attributeField.getBindingInfo().getBindingName())) {
401         //						attributeField.setReadOnly(true);
402         //					}
403         //					if (requiredFields != null && requiredFields.contains(attributeField.getBindingInfo().getBindingName())) {
404         //						attributeField.setRequired(Boolean.TRUE);
405         //					}
406         //					if (hiddenFields != null && hiddenFields.contains(attributeField.getBindingInfo().getBindingName())) {
407         //						attributeField.setControl(LookupInquiryUtils.generateCustomLookupControlFromExisting(HiddenControl.class, null));
408         //					}
409         //				}
410         //	        }
411         //		}
412     }
413 
414     /**
415      * Class name for the object the lookup applies to
416      *
417      * <p>
418      * The object class name is used to pick up a dictionary entry which will
419      * feed the attribute field definitions and other configuration. In addition
420      * it is to configure the <code>Lookupable</code> which will carry out the
421      * lookup action
422      * </p>
423      *
424      * @return lookup data object class
425      */
426     @BeanTagAttribute(name="dataObjectClassName")
427     public Class<?> getDataObjectClassName() {
428         return this.dataObjectClassName;
429     }
430 
431     /**
432      * Setter for the object class name
433      *
434      * @param dataObjectClassName
435      */
436     public void setDataObjectClassName(Class<?> dataObjectClassName) {
437         this.dataObjectClassName = dataObjectClassName;
438     }
439 
440     /**
441      * @return the hideReturnLinks
442      */
443     @BeanTagAttribute(name="hideReturnLinks")
444     public boolean isHideReturnLinks() {
445         return this.hideReturnLinks;
446     }
447 
448     /**
449      * @param hideReturnLinks the hideReturnLinks to set
450      */
451     public void setHideReturnLinks(boolean hideReturnLinks) {
452         this.hideReturnLinks = hideReturnLinks;
453     }
454 
455     /**
456      * @return the suppressActions
457      */
458     @BeanTagAttribute(name="isSuppressActions")
459     public boolean isSuppressActions() {
460         return this.suppressActions;
461     }
462 
463     /**
464      * @param suppressActions the suppressActions to set
465      */
466     public void setSuppressActions(boolean suppressActions) {
467         this.suppressActions = suppressActions;
468     }
469 
470     /**
471      * @return the showMaintenanceLinks
472      */
473     @BeanTagAttribute(name="showMaintenanceLinks")
474     public boolean isShowMaintenanceLinks() {
475         return this.showMaintenanceLinks;
476     }
477 
478     /**
479      * @param showMaintenanceLinks the showMaintenanceLinks to set
480      */
481     public void setShowMaintenanceLinks(boolean showMaintenanceLinks) {
482         this.showMaintenanceLinks = showMaintenanceLinks;
483     }
484 
485     /**
486      * Indicates whether multiple values select should be enabled for the lookup
487      *
488      * <p>
489      * When set to true, the select field is enabled for the lookup results group that allows the user
490      * to select one or more rows for returning
491      * </p>
492      *
493      * @return true if multiple values should be enabled, false otherwise
494      */
495     @BeanTagAttribute(name="multipleValueSelect")
496     public boolean isMultipleValuesSelect() {
497         return multipleValuesSelect;
498     }
499 
500     /**
501      * Setter for the multiple values select indicator
502      *
503      * @param multipleValuesSelect
504      */
505     public void setMultipleValuesSelect(boolean multipleValuesSelect) {
506         this.multipleValuesSelect = multipleValuesSelect;
507     }
508 
509     @BeanTagAttribute(name="criteriaGroup",type = BeanTagAttribute.AttributeType.SINGLEBEAN)
510     public Group getCriteriaGroup() {
511         return this.criteriaGroup;
512     }
513 
514     public void setCriteriaGroup(Group criteriaGroup) {
515         this.criteriaGroup = criteriaGroup;
516     }
517 
518     @BeanTagAttribute(name="resultsGroup",type= BeanTagAttribute.AttributeType.SINGLEBEAN)
519     public CollectionGroup getResultsGroup() {
520         return this.resultsGroup;
521     }
522 
523     public void setResultsGroup(CollectionGroup resultsGroup) {
524         this.resultsGroup = resultsGroup;
525     }
526 
527     @BeanTagAttribute(name="criteriaFields",type= BeanTagAttribute.AttributeType.LISTBEAN)
528     public List<Component> getCriteriaFields() {
529         return this.criteriaFields;
530     }
531 
532     public void setCriteriaFields(List<Component> criteriaFields) {
533         this.criteriaFields = criteriaFields;
534     }
535 
536     @BeanTagAttribute(name="resultFields",type= BeanTagAttribute.AttributeType.LISTBEAN)
537     public List<Component> getResultFields() {
538         return this.resultFields;
539     }
540 
541     public void setResultFields(List<Component> resultFields) {
542         this.resultFields = resultFields;
543     }
544 
545     @BeanTagAttribute(name="defaultSortAttributeNames",type= BeanTagAttribute.AttributeType.LISTVALUE)
546     public List<String> getDefaultSortAttributeNames() {
547         return this.defaultSortAttributeNames;
548     }
549 
550     public void setDefaultSortAttributeNames(List<String> defaultSortAttributeNames) {
551         this.defaultSortAttributeNames = defaultSortAttributeNames;
552     }
553 
554     @BeanTagAttribute(name="defaultSortAscending")
555     public boolean isDefaultSortAscending() {
556         return this.defaultSortAscending;
557     }
558 
559     public void setDefaultSortAscending(boolean defaultSortAscending) {
560         this.defaultSortAscending = defaultSortAscending;
561     }
562 
563     /**
564      * Retrieves the maximum number of records that will be listed
565      * as a result of the lookup search
566      *
567      * @return Integer result set limit
568      */
569     @BeanTagAttribute(name="resultSetLimit")
570     public Integer getResultSetLimit() {
571         return resultSetLimit;
572     }
573 
574     /**
575      * Setter for the result list limit
576      *
577      * @param resultSetLimit Integer specifying limit
578      */
579     public void setResultSetLimit(Integer resultSetLimit) {
580         this.resultSetLimit = resultSetLimit;
581     }
582 
583     /**
584      * Indicates whether a result set limit has been specified for the
585      * view
586      *
587      * @return true if this instance has a result set limit
588      */
589     public boolean hasResultSetLimit() {
590         return (resultSetLimit != null);
591     }
592 
593     /**
594      * Retrieves the maximum number of records that will be listed
595      * as a result of the multiple values select lookup search
596      *
597      * @return multiple values select result set limit
598      */
599     @BeanTagAttribute(name="multipleValuesSelectResultSetLimit")
600     public Integer getMultipleValuesSelectResultSetLimit() {
601         return multipleValuesSelectResultSetLimit;
602     }
603 
604     /**
605      * Setter for the multiple values select result set limit
606      *
607      * @param multipleValuesSelectResultSetLimit Integer specifying limit
608      */
609     public void setMultipleValuesSelectResultSetLimit(Integer multipleValuesSelectResultSetLimit) {
610         this.multipleValuesSelectResultSetLimit = multipleValuesSelectResultSetLimit;
611     }
612 
613     /**
614      * Indicates whether a multiple values select result
615      * set limit has been specified for the view
616      *
617      * @return true if this instance has a multiple values select result set limit
618      */
619     public boolean hasMultipleValuesSelectResultSetLimit() {
620         return (multipleValuesSelectResultSetLimit != null);
621     }
622 
623     /**
624      * @param returnTarget the returnTarget to set
625      */
626     public void setReturnTarget(String returnTarget) {
627         this.returnTarget = returnTarget;
628     }
629 
630     /**
631      * @return the returnTarget
632      */
633     @BeanTagAttribute(name="returnTarget")
634     public String getReturnTarget() {
635         return returnTarget;
636     }
637 
638     /**
639      * @return the returnByScript
640      */
641     @BeanTagAttribute(name="returnByScript")
642     public boolean isReturnByScript() {
643         return returnByScript;
644     }
645 
646     /**
647      * Setter for the flag to indicate that lookups will return the value
648      * by script and not a post
649      *
650      * @param returnByScript the returnByScript flag
651      */
652     public void setReturnByScript(boolean returnByScript) {
653         this.returnByScript = returnByScript;
654     }
655 
656     /**
657      * String that maps to the maintenance controller for the maintenance document (if any) associated with the
658      * lookup data object class
659      *
660      * <p>
661      * Mapping will be used to build the maintenance action links (such as edit, copy, and new). If not given, the
662      * default maintenance mapping will be used
663      * </p>
664      *
665      * @return mapping string
666      */
667     @BeanTagAttribute(name="maintenanceUrlMapping")
668     public String getMaintenanceUrlMapping() {
669         return maintenanceUrlMapping;
670     }
671 
672     /**
673      * Setter for the URL mapping string that will be used to build up maintenance action URLs
674      *
675      * @param maintenanceUrlMapping
676      */
677     public void setMaintenanceUrlMapping(String maintenanceUrlMapping) {
678         this.maintenanceUrlMapping = maintenanceUrlMapping;
679     }
680 
681     /**
682      * Indicates that the action buttons like search in the criteria section should be rendered
683      *
684      * @return boolean
685      */
686     public boolean isRenderSearchButtons() {
687         return renderSearchButtons;
688     }
689 
690     /**
691      * Setter for the render search buttons flag
692      *
693      * @param renderSearchButtons
694      */
695     public void setRenderSearchButtons(boolean renderSearchButtons) {
696         this.renderSearchButtons = renderSearchButtons;
697     }
698 
699     /**
700      * Indicates whether the lookup criteria group should be rendered
701      *
702      * <p>
703      * Defaults to true. Can be set as bean property or passed as a request parameter in the lookup url.
704      * </p>
705      *
706      * @return boolean
707      */
708     public boolean isRenderLookupCriteria() {
709         return renderLookupCriteria;
710     }
711 
712     /**
713      * Setter for the lookup criteria group render flag
714      *
715      * @param renderLookupCriteria
716      */
717     public void setRenderLookupCriteria(boolean renderLookupCriteria) {
718         this.renderLookupCriteria = renderLookupCriteria;
719     }
720 
721     /**
722      * Indicates whether the lookup header should be rendered
723      *
724      * <p>
725      * Defaults to true. Can be set as bean property or passed as a request parameter in the lookup url.
726      * </p>
727      *
728      * @return boolean
729      */
730     public boolean isRenderHeader() {
731         return renderHeader;
732     }
733 
734     /**
735      * Setter for the header render flag
736      *
737      * @param renderHeader
738      */
739     public void setRenderHeader(boolean renderHeader) {
740         this.renderHeader = renderHeader;
741     }
742 
743     /**
744      * Indicates that the search must execute on changing of a value in all lookup input fields
745      *
746      * @return boolean
747      */
748     public boolean isTriggerOnChange() {
749         return triggerOnChange;
750     }
751 
752     /**
753      * Setter for the trigger search on change flag
754      *
755      * @param triggerOnChange
756      */
757     public void setTriggerOnChange(boolean triggerOnChange) {
758         this.triggerOnChange = triggerOnChange;
759     }
760 
761     /**
762      * The field group prototype that will be copied and used for range fields
763      *
764      * @return FieldGroup
765      */
766     public FieldGroup getRangeFieldGroupPrototype() {
767         return rangeFieldGroupPrototype;
768     }
769 
770     /**
771      * Setter for the range FieldGroup prototype
772      *
773      * @param rangeFieldGroupPrototype
774      */
775     public void setRangeFieldGroupPrototype(FieldGroup rangeFieldGroupPrototype) {
776         this.rangeFieldGroupPrototype = rangeFieldGroupPrototype;
777     }
778 
779     /**
780      * Indicates whether the 'active' criteria field must be added automatically for Inactivatable BO's
781      *
782      * @return boolean
783      */
784     public boolean isAutoAddActiveCriteria() {
785         return autoAddActiveCriteria;
786     }
787 
788     /**
789      * Setter for the flag that indicates whether the 'active' criteria field must be added automatically for
790      * Inactivatable BO's
791      *
792      * @param autoAddActiveCriteria
793      */
794     public void setAutoAddActiveCriteria(boolean autoAddActiveCriteria) {
795         this.autoAddActiveCriteria = autoAddActiveCriteria;
796     }
797 
798     /**
799      * List of secure property names that are in addition to the
800      * {@link org.kuali.rice.krad.uif.component.ComponentSecurity} or
801      * {@link org.kuali.rice.krad.datadictionary.AttributeSecurity} attributes.
802      *
803      * @return list of secure property names
804      */
805     @BeanTagAttribute(name = "additionalSecurePropertyNames", type = BeanTagAttribute.AttributeType.LISTVALUE)
806     public List<String> getAdditionalSecurePropertyNames() {
807         return additionalSecurePropertyNames;
808     }
809 
810     /**
811      * @see LookupView#getAdditionalSecurePropertyNames()
812      */
813     public void setAdditionalSecurePropertyNames(List<String> additionalSecurePropertyNames) {
814         this.additionalSecurePropertyNames = additionalSecurePropertyNames;
815     }
816 
817     /**
818      * The Message to render between the two range fields for ranged criteria fields
819      *
820      * @return
821      */
822     public Message getRangedToMessage() {
823         return rangedToMessage;
824     }
825 
826     /**
827      * Setter for the Message rendered between the two range fields for ranged criteria fields
828      *
829      * @param rangedToMessage
830      */
831     public void setRangedToMessage(Message rangedToMessage) {
832         this.rangedToMessage = rangedToMessage;
833     }
834 
835     /**
836      * @see org.kuali.rice.krad.uif.component.ComponentBase#copy()
837      */
838     @Override
839     protected <T> void copyProperties(T component) {
840         super.copyProperties(component);
841 
842         LookupView lookupViewCopy = (LookupView) component;
843 
844         if (this.dataObjectClassName != null) {
845             lookupViewCopy.setDataObjectClassName(this.getDataObjectClassName());
846         }
847 
848         if (this.criteriaGroup != null) {
849             lookupViewCopy.setCriteriaGroup((Group) this.getCriteriaGroup().copy());
850         }
851 
852         if (this.resultsGroup != null) {
853             lookupViewCopy.setResultsGroup((CollectionGroup) this.getResultsGroup().copy());
854         }
855 
856         if (this.criteriaFields != null) {
857             List<Component> criteriaFieldsCopy = Lists.newArrayListWithExpectedSize(criteriaFields.size());
858             for (Component criteriaField : criteriaFields) {
859                 criteriaFieldsCopy.add((Component) criteriaField.copy());
860             }
861             lookupViewCopy.setCriteriaFields(criteriaFieldsCopy);
862         }
863 
864         if (this.resultFields != null) {
865             List<Component> resultFieldsCopy = Lists.newArrayListWithExpectedSize(resultFields.size());
866             for (Component resultField : resultFields) {
867                 resultFieldsCopy.add((Component) resultField.copy());
868             }
869             lookupViewCopy.setResultFields(resultFieldsCopy);
870         }
871 
872         if (this.defaultSortAttributeNames != null) {
873             lookupViewCopy.setDefaultSortAttributeNames(new ArrayList<String>(defaultSortAttributeNames));
874         }
875 
876         lookupViewCopy.setDefaultSortAscending(this.isDefaultSortAscending());
877         lookupViewCopy.setHideReturnLinks(this.hideReturnLinks);
878         lookupViewCopy.setSuppressActions(this.suppressActions);
879         lookupViewCopy.setShowMaintenanceLinks(this.showMaintenanceLinks);
880         lookupViewCopy.setMaintenanceUrlMapping(this.maintenanceUrlMapping);
881         lookupViewCopy.setMultipleValuesSelect(this.multipleValuesSelect);
882         lookupViewCopy.setRenderLookupCriteria(this.renderLookupCriteria);
883         lookupViewCopy.setRenderSearchButtons(this.renderSearchButtons);
884         lookupViewCopy.setRenderHeader(this.renderHeader);
885         lookupViewCopy.setResultSetLimit(this.resultSetLimit);
886         lookupViewCopy.setReturnTarget(this.returnTarget);
887         lookupViewCopy.setTriggerOnChange(this.triggerOnChange);
888         lookupViewCopy.setResultSetLimit(this.resultSetLimit);
889         lookupViewCopy.setMultipleValuesSelectResultSetLimit(this.multipleValuesSelectResultSetLimit);
890         lookupViewCopy.setMaintenanceUrlMapping(this.maintenanceUrlMapping);
891 
892         if (this.rangeFieldGroupPrototype != null) {
893             lookupViewCopy.setRangeFieldGroupPrototype((FieldGroup) this.rangeFieldGroupPrototype.copy());
894         }
895 
896         if (this.rangedToMessage != null) {
897             lookupViewCopy.setRangedToMessage((Message) this.rangedToMessage.copy());
898         }
899 
900         lookupViewCopy.setAutoAddActiveCriteria(this.autoAddActiveCriteria);
901 
902         if (this.additionalSecurePropertyNames != null) {
903             lookupViewCopy.setAdditionalSecurePropertyNames(additionalSecurePropertyNames);
904         }
905     }
906 
907 }