001    /**
002     * Copyright 2005-2014 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     */
016    package org.kuali.rice.krad.uif.control;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.kim.api.KimConstants;
020    import org.kuali.rice.kim.api.identity.Person;
021    import org.kuali.rice.kim.api.identity.PersonService;
022    import org.kuali.rice.kim.api.identity.principal.Principal;
023    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
024    import org.kuali.rice.krad.datadictionary.parse.BeanTag;
025    import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
026    import org.kuali.rice.krad.uif.UifConstants;
027    import org.kuali.rice.krad.uif.field.InputField;
028    import org.kuali.rice.krad.uif.util.ComponentFactory;
029    import org.kuali.rice.krad.uif.util.ScriptUtils;
030    import org.kuali.rice.krad.uif.view.View;
031    import org.kuali.rice.krad.uif.component.Component;
032    import org.kuali.rice.krad.uif.component.MethodInvokerConfig;
033    import org.kuali.rice.krad.uif.field.AttributeQuery;
034    import org.kuali.rice.krad.uif.widget.QuickFinder;
035    
036    import java.util.HashMap;
037    import java.util.Map;
038    
039    /**
040     * Represents a user control, which is a special control to handle the input of a Person
041     *
042     * @author Kuali Rice Team (rice.collab@kuali.org)
043     */
044    @BeanTag(name = "kimPersonControl-bean", parent = "Uif-KimPersonControl")
045    public class UserControl extends TextControl implements FilterableLookupCriteriaControl {
046        private static final long serialVersionUID = 7468340793076585869L;
047    
048        private String principalIdPropertyName;
049        private String personNamePropertyName;
050        private String personObjectPropertyName;
051    
052        public UserControl() {
053            super();
054        }
055    
056        /**
057         * @see org.kuali.rice.krad.uif.component.ComponentBase#performApplyModel(org.kuali.rice.krad.uif.view.View,
058         *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
059         */
060        @Override
061        public void performApplyModel(View view, Object model, Component parent) {
062            super.performApplyModel(view, model, parent);
063    
064            if (!(parent instanceof InputField)) {
065                return;
066            }
067    
068            InputField field = (InputField) parent;
069            field.getAdditionalHiddenPropertyNames().add(principalIdPropertyName);
070    
071            if (!field.isReadOnly()) {
072                // add information fields
073                if (StringUtils.isNotBlank(personNamePropertyName)) {
074                    field.getPropertyNamesForAdditionalDisplay().add(personNamePropertyName);
075                } else {
076                    field.getPropertyNamesForAdditionalDisplay().add(
077                            personObjectPropertyName + "." + KimConstants.AttributeConstants.NAME);
078                }
079    
080                // setup script to clear id field when name is modified
081                String idPropertyPath = field.getBindingInfo().getPropertyAdjustedBindingPath(principalIdPropertyName);
082                String onChangeScript = UifConstants.JsFunctions.SET_VALUE
083                        + "('"
084                        + ScriptUtils.escapeName(idPropertyPath)
085                        + "','');";
086    
087                if (StringUtils.isNotBlank(getOnChangeScript())) {
088                    onChangeScript = getOnChangeScript() + onChangeScript;
089                }
090                setOnChangeScript(onChangeScript);
091            }
092    
093            if (field.isReadOnly() && StringUtils.isBlank(field.getReadOnlyDisplaySuffixPropertyName())) {
094                if (StringUtils.isNotBlank(personNamePropertyName)) {
095                    field.setReadOnlyDisplaySuffixPropertyName(personNamePropertyName);
096                } else {
097                    field.setReadOnlyDisplaySuffixPropertyName(
098                            personObjectPropertyName + "." + KimConstants.AttributeConstants.NAME);
099                }
100            }
101    
102            // setup field query for displaying name
103            AttributeQuery attributeQuery = new AttributeQuery();
104    
105            MethodInvokerConfig methodInvokerConfig = new MethodInvokerConfig();
106            PersonService personService = KimApiServiceLocator.getPersonService();
107            methodInvokerConfig.setTargetObject(personService);
108    
109            attributeQuery.setQueryMethodInvokerConfig(methodInvokerConfig);
110            attributeQuery.setQueryMethodToCall("getPersonByPrincipalName");
111            attributeQuery.getQueryMethodArgumentFieldList().add(field.getPropertyName());
112            attributeQuery.getReturnFieldMapping().put(KimConstants.AttributeConstants.PRINCIPAL_ID,
113                    principalIdPropertyName);
114    
115            if (StringUtils.isNotBlank(personNamePropertyName)) {
116                attributeQuery.getReturnFieldMapping().put(KimConstants.AttributeConstants.NAME, personNamePropertyName);
117            } else {
118                attributeQuery.getReturnFieldMapping().put(KimConstants.AttributeConstants.NAME,
119                        personObjectPropertyName + "." + KimConstants.AttributeConstants.NAME);
120            }
121            field.setAttributeQuery(attributeQuery);
122    
123            buildUserQuickfinder(view, model, field);
124        }
125    
126        /**
127         * @see FilterableLookupCriteriaControl#filterSearchCriteria(String, java.util.Map)
128         */
129        @Override
130        public Map<String, String> filterSearchCriteria(String propertyName, Map<String, String> searchCriteria) {
131            Map<String, String> filteredSearchCriteria = new HashMap<String, String>(searchCriteria);
132    
133            // check valid principalName
134            // ToDo: move the principalId check and setting to the validation stage.  At that point the personName should
135            // be set as well or an error be displayed to the user that the principalName is invalid.
136            String principalName = searchCriteria.get(propertyName);
137            if (StringUtils.isNotBlank(principalName)) {
138                Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(principalName);
139                if (principal == null) {
140                    return null;
141                } else {
142                    filteredSearchCriteria.put(principalIdPropertyName, principal.getPrincipalId());
143                }
144            }
145    
146            // filter
147            filteredSearchCriteria.remove(propertyName);
148            filteredSearchCriteria.remove(personNamePropertyName);
149    
150            return filteredSearchCriteria;
151        }
152    
153        /**
154         * Configures the field's quickfinder for a user lookup
155         *
156         * @param view view instance that contains the field
157         * @param model object containing the view's data
158         * @param field field instance the quickfinder should be associated with
159         */
160        protected void buildUserQuickfinder(View view, Object model, InputField field) {
161            QuickFinder quickFinder = field.getQuickfinder();
162    
163            // if they explicitly turned off the quickfinder we will not build it
164            if ((quickFinder != null) && !quickFinder.isRender()) {
165                return;
166            }
167    
168            boolean quickfinderCreated = false;
169            if (quickFinder == null) {
170                quickFinder = ComponentFactory.getQuickFinder();
171                view.assignComponentIds(quickFinder);
172    
173                field.setQuickfinder(quickFinder);
174    
175                quickfinderCreated = true;
176            }
177    
178            if (StringUtils.isBlank(quickFinder.getDataObjectClassName())) {
179                quickFinder.setDataObjectClassName(Person.class.getName());
180            }
181    
182            if (quickFinder.getFieldConversions().isEmpty()) {
183                quickFinder.getFieldConversions().put(KimConstants.AttributeConstants.PRINCIPAL_ID,
184                        principalIdPropertyName);
185    
186                if (StringUtils.isNotBlank(personNamePropertyName)) {
187                    quickFinder.getFieldConversions().put(KimConstants.AttributeConstants.NAME, personNamePropertyName);
188                } else {
189                    quickFinder.getFieldConversions().put(KimConstants.AttributeConstants.NAME,
190                            personObjectPropertyName + "." + KimConstants.AttributeConstants.NAME);
191                }
192    
193                quickFinder.getFieldConversions().put(KimConstants.AttributeConstants.PRINCIPAL_NAME,
194                        field.getPropertyName());
195            }
196    
197            // if we created the quickfinder here it will have missed the initialize and apply model phase (it
198            // will be attached to the field for finalize)
199            if (quickfinderCreated) {
200                view.getViewHelperService().spawnSubLifecyle(view, model, quickFinder, field,
201                        UifConstants.ViewPhases.INITIALIZE, UifConstants.ViewPhases.APPLY_MODEL);
202            }
203        }
204    
205        /**
206         * The name of the property on the parent object that holds the principal id
207         *
208         * @return principalIdPropertyName
209         */
210        @BeanTagAttribute(name = "principalIdPropertyName")
211        public String getPrincipalIdPropertyName() {
212            return principalIdPropertyName;
213        }
214    
215        /**
216         * Setter for the name of the property on the parent object that holds the principal id
217         *
218         * @param principalIdPropertyName
219         */
220        public void setPrincipalIdPropertyName(String principalIdPropertyName) {
221            this.principalIdPropertyName = principalIdPropertyName;
222        }
223    
224        /**
225         * The name of the property on the parent object that holds the person name
226         *
227         * @return personNamePropertyName
228         */
229        @BeanTagAttribute(name = "personNamePropertyName")
230        public String getPersonNamePropertyName() {
231            return personNamePropertyName;
232        }
233    
234        /**
235         * Setter for the name of the property on the parent object that holds the person name
236         *
237         * @param personNamePropertyName
238         */
239        public void setPersonNamePropertyName(String personNamePropertyName) {
240            this.personNamePropertyName = personNamePropertyName;
241        }
242    
243        /**
244         * The name of the property on the parent object that holds the person object
245         *
246         * @return personObjectPropertyName
247         */
248        @BeanTagAttribute(name = "personObjectPropertyName")
249        public String getPersonObjectPropertyName() {
250            return personObjectPropertyName;
251        }
252    
253        /**
254         * Setter for the name of the property on the parent object that holds the person object
255         *
256         * @param personObjectPropertyName
257         */
258        public void setPersonObjectPropertyName(String personObjectPropertyName) {
259            this.personObjectPropertyName = personObjectPropertyName;
260        }
261    
262        /**
263         * @see org.kuali.rice.krad.uif.component.ComponentBase#copy()
264         */
265        @Override
266        protected <T> void copyProperties(T component) {
267            super.copyProperties(component);
268    
269            UserControl userControlCopy = (UserControl) component;
270    
271            userControlCopy.setPrincipalIdPropertyName(this.principalIdPropertyName);
272            userControlCopy.setPersonNamePropertyName(this.personNamePropertyName);
273            userControlCopy.setPersonObjectPropertyName(this.personObjectPropertyName);
274        }
275    }