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.control;
017
018import java.io.Serializable;
019import java.util.Collections;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023
024import org.apache.commons.lang.StringUtils;
025import org.kuali.rice.kim.api.KimConstants;
026import org.kuali.rice.kim.api.identity.Person;
027import org.kuali.rice.kim.api.identity.PersonService;
028import org.kuali.rice.kim.api.identity.principal.Principal;
029import org.kuali.rice.kim.api.services.KimApiServiceLocator;
030import org.kuali.rice.krad.datadictionary.parse.BeanTag;
031import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
032import org.kuali.rice.krad.uif.UifConstants;
033import org.kuali.rice.krad.uif.component.MethodInvokerConfig;
034import org.kuali.rice.krad.uif.field.AttributeQuery;
035import org.kuali.rice.krad.uif.field.InputField;
036import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
037import org.kuali.rice.krad.uif.util.ComponentFactory;
038import org.kuali.rice.krad.uif.util.LifecycleElement;
039import org.kuali.rice.krad.uif.util.ScriptUtils;
040import org.kuali.rice.krad.uif.widget.QuickFinder;
041
042/**
043 * Represents a user control, which is a special control to handle the input of a Person.
044 *
045 * @author Kuali Rice Team (rice.collab@kuali.org)
046 */
047@BeanTag(name = "kimPersonControl", parent = "Uif-KimPersonControl")
048public class UserControl extends TextControlBase implements FilterableLookupCriteriaControl {
049    private static final long serialVersionUID = 7468340793076585869L;
050
051    private String principalIdPropertyName;
052    private String personNamePropertyName;
053    private String personObjectPropertyName;
054
055    public UserControl() {
056        super();
057    }
058
059    /**
060     * {@inheritDoc}
061     */
062    @Override
063    public void performApplyModel(Object model, LifecycleElement parent) {
064        super.performApplyModel(model, parent);
065
066        if (!(parent instanceof InputField)) {
067            return;
068        }
069
070        InputField field = (InputField) parent;
071        field.getAdditionalHiddenPropertyNames().add(principalIdPropertyName);
072
073        if (isRender() && !isHidden() && !Boolean.TRUE.equals(getReadOnly())) {
074            ViewLifecycle.getViewPostMetadata().addAccessibleBindingPath(principalIdPropertyName);
075        }
076
077        if (!Boolean.TRUE.equals(field.getReadOnly())) {
078            // add information fields
079            if (StringUtils.isNotBlank(personNamePropertyName)) {
080                field.getPropertyNamesForAdditionalDisplay().add(personNamePropertyName);
081            } else {
082                field.getPropertyNamesForAdditionalDisplay().add(
083                        personObjectPropertyName + "." + KimConstants.AttributeConstants.NAME);
084            }
085
086            // setup script to clear id field when name is modified
087            String idPropertyPath = field.getBindingInfo().getPropertyAdjustedBindingPath(principalIdPropertyName);
088            String onChangeScript = UifConstants.JsFunctions.SET_VALUE
089                    + "('"
090                    + ScriptUtils.escapeName(idPropertyPath)
091                    + "','');";
092
093            if (StringUtils.isNotBlank(getOnChangeScript())) {
094                onChangeScript = getOnChangeScript() + onChangeScript;
095            }
096            setOnChangeScript(onChangeScript);
097        }
098
099        if (Boolean.TRUE.equals(field.getReadOnly()) && StringUtils.isBlank(field.getReadOnlyDisplaySuffixPropertyName())) {
100            if (StringUtils.isNotBlank(personNamePropertyName)) {
101                field.setReadOnlyDisplaySuffixPropertyName(personNamePropertyName);
102            } else {
103                field.setReadOnlyDisplaySuffixPropertyName(
104                        personObjectPropertyName + "." + KimConstants.AttributeConstants.NAME);
105            }
106        }
107
108        // setup field query for displaying name
109        AttributeQuery attributeQuery = new AttributeQuery();
110
111        MethodInvokerConfig methodInvokerConfig = new MethodInvokerConfig();
112        PersonService personService = KimApiServiceLocator.getPersonService();
113        methodInvokerConfig.setTargetObject(personService);
114
115        attributeQuery.setQueryMethodInvokerConfig(methodInvokerConfig);
116        attributeQuery.setQueryMethodToCall("getPersonByPrincipalName");
117        attributeQuery.getQueryMethodArgumentFieldList().add(field.getPropertyName());
118        attributeQuery.getReturnFieldMapping().put(KimConstants.AttributeConstants.PRINCIPAL_ID,
119                principalIdPropertyName);
120
121        if (StringUtils.isNotBlank(personNamePropertyName)) {
122            attributeQuery.getReturnFieldMapping().put(KimConstants.AttributeConstants.NAME, personNamePropertyName);
123        } else {
124            attributeQuery.getReturnFieldMapping().put(KimConstants.AttributeConstants.NAME,
125                    personObjectPropertyName + "." + KimConstants.AttributeConstants.NAME);
126        }
127        field.setAttributeQuery(attributeQuery);
128
129        buildUserQuickfinder(model, field);
130    }
131
132    /**
133     * {@inheritDoc}
134     */
135    @Override
136    public Map<String, String> filterSearchCriteria(String propertyName, Map<String, String> searchCriteria,
137            FilterableLookupCriteriaControlPostData postData) {
138        Map<String, String> filteredSearchCriteria = new HashMap<String, String>(searchCriteria);
139
140        UserControlPostData userControlPostData = (UserControlPostData) postData;
141
142        // check valid principalName
143        // ToDo: move the principalId check and setting to the validation stage.  At that point the personName should
144        // be set as well or an error be displayed to the user that the principalName is invalid.
145        String principalName = searchCriteria.get(propertyName);
146        if (StringUtils.isNotBlank(principalName)) {
147            if (!StringUtils.contains(principalName, "*")) {
148                Principal principal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(
149                        principalName);
150                if (principal == null) {
151                    return null;
152                } else {
153                    filteredSearchCriteria.put(userControlPostData.getPrincipalIdPropertyName(),
154                            principal.getPrincipalId());
155                }
156            } else {
157                List<Person> people = KimApiServiceLocator.getPersonService().findPeople(Collections.singletonMap(
158                        KimConstants.AttributeConstants.PRINCIPAL_NAME, principalName));
159                if (people != null && people.size() == 0) {
160                    return null;
161                }
162            }
163        }
164
165        if (!StringUtils.contains(principalName, "*")) {
166            // filter
167            filteredSearchCriteria.remove(propertyName);
168            filteredSearchCriteria.remove(userControlPostData.getPersonNamePropertyName());
169        }
170
171        return filteredSearchCriteria;
172    }
173
174    /**
175     * Configures the field's quickfinder for a user lookup
176     *
177     * @param model object containing the view's data
178     * @param field field instance the quickfinder should be associated with
179     */
180    protected void buildUserQuickfinder(Object model, InputField field) {
181        QuickFinder quickFinder = field.getQuickfinder();
182
183        // don't build quickfinder if explicitly turned off
184        if (!field.isEnableAutoQuickfinder()) {
185            return;
186        }
187
188        if (quickFinder == null) {
189            quickFinder = ComponentFactory.getQuickFinder();
190            field.setQuickfinder(quickFinder);
191        }
192
193        if (StringUtils.isBlank(quickFinder.getDataObjectClassName())) {
194            quickFinder.setDataObjectClassName(Person.class.getName());
195        }
196
197        if (quickFinder.getFieldConversions().isEmpty()) {
198            quickFinder.getFieldConversions().put(KimConstants.AttributeConstants.PRINCIPAL_ID,
199                    principalIdPropertyName);
200
201            if (StringUtils.isNotBlank(personNamePropertyName)) {
202                quickFinder.getFieldConversions().put(KimConstants.AttributeConstants.NAME, personNamePropertyName);
203            } else {
204                quickFinder.getFieldConversions().put(KimConstants.AttributeConstants.NAME,
205                        personObjectPropertyName + "." + KimConstants.AttributeConstants.NAME);
206            }
207
208            quickFinder.getFieldConversions().put(KimConstants.AttributeConstants.PRINCIPAL_NAME,
209                    field.getPropertyName());
210        }
211    }
212
213    /**
214     * The name of the property on the parent object that holds the principal id
215     *
216     * @return principalIdPropertyName
217     */
218    @BeanTagAttribute
219    public String getPrincipalIdPropertyName() {
220        return principalIdPropertyName;
221    }
222
223    /**
224     * Setter for the name of the property on the parent object that holds the principal id
225     *
226     * @param principalIdPropertyName
227     */
228    public void setPrincipalIdPropertyName(String principalIdPropertyName) {
229        this.principalIdPropertyName = principalIdPropertyName;
230    }
231
232    /**
233     * The name of the property on the parent object that holds the person name
234     *
235     * @return personNamePropertyName
236     */
237    @BeanTagAttribute
238    public String getPersonNamePropertyName() {
239        return personNamePropertyName;
240    }
241
242    /**
243     * Setter for the name of the property on the parent object that holds the person name
244     *
245     * @param personNamePropertyName
246     */
247    public void setPersonNamePropertyName(String personNamePropertyName) {
248        this.personNamePropertyName = personNamePropertyName;
249    }
250
251    /**
252     * The name of the property on the parent object that holds the person object
253     *
254     * @return personObjectPropertyName
255     */
256    @BeanTagAttribute
257    public String getPersonObjectPropertyName() {
258        return personObjectPropertyName;
259    }
260
261    /**
262     * Setter for the name of the property on the parent object that holds the person object
263     *
264     * @param personObjectPropertyName
265     */
266    public void setPersonObjectPropertyName(String personObjectPropertyName) {
267        this.personObjectPropertyName = personObjectPropertyName;
268    }
269
270    /**
271     * {@inheritDoc}
272     */
273    @Override
274    public UserControlPostData getPostData(String propertyName) {
275        return new UserControlPostData(propertyName, this);
276    }
277
278    /**
279     * Holds post data for the {@link UserControl}.
280     */
281    public static class UserControlPostData implements FilterableLookupCriteriaControlPostData, Serializable {
282
283        private static final long serialVersionUID = 3895010942559014164L;
284
285        private String propertyName;
286
287        private String principalIdPropertyName;
288        private String personNamePropertyName;
289        private String personObjectPropertyName;
290
291        /**
292         * Constructs the post data from the property name and the {@link UserControl}.
293         *
294         * @param propertyName the name of the property to filter
295         * @param userControl the control to pull data from
296         */
297        public UserControlPostData(String propertyName, UserControl userControl) {
298            this.propertyName = propertyName;
299            this.principalIdPropertyName = userControl.getPrincipalIdPropertyName();
300            this.personNamePropertyName = userControl.getPersonNamePropertyName();
301            this.personObjectPropertyName = userControl.getPersonObjectPropertyName();
302        }
303
304        /**
305         * {@inheritDoc}
306         */
307        @Override
308        public Class<? extends FilterableLookupCriteriaControl> getControlClass() {
309            return UserControl.class;
310        }
311
312        /**
313         * {@inheritDoc}
314         */
315        @Override
316        public String getPropertyName() {
317            return propertyName;
318        }
319
320        /**
321         * @see UserControl#getPrincipalIdPropertyName()
322         */
323        public String getPrincipalIdPropertyName() {
324            return principalIdPropertyName;
325        }
326
327        /**
328         * @see UserControl#getPersonNamePropertyName()
329         */
330        public String getPersonNamePropertyName() {
331            return personNamePropertyName;
332        }
333
334        /**
335         * @see UserControl#getPersonObjectPropertyName()
336         */
337        public String getPersonObjectPropertyName() {
338            return personObjectPropertyName;
339        }
340
341    }
342
343}