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.web.service.impl;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.krad.uif.UifConstants;
020import org.kuali.rice.krad.uif.UifParameters;
021import org.kuali.rice.krad.uif.component.MethodInvokerConfig;
022import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
023import org.kuali.rice.krad.util.GlobalVariables;
024import org.kuali.rice.krad.util.KRADConstants;
025import org.kuali.rice.krad.web.form.UifFormBase;
026import org.kuali.rice.krad.web.service.ModelAndViewService;
027import org.kuali.rice.krad.web.service.RefreshControllerService;
028import org.springframework.web.servlet.ModelAndView;
029import org.springframework.web.servlet.support.RequestContextUtils;
030
031import javax.servlet.http.HttpServletRequest;
032import java.util.Map;
033
034/**
035 * Default implementation of the refresh controller service.
036 *
037 * @author Kuali Rice Team (rice.collab@kuali.org)
038 */
039public class RefreshControllerServiceImpl implements RefreshControllerService {
040
041    private ModelAndViewService modelAndViewService;
042
043    /**
044     * Handles the refresh call by checking the request parameters and delegating out to helper methods.
045     *
046     * {@inheritDoc}
047     */
048    @Override
049    public ModelAndView refresh(UifFormBase form) {
050        HttpServletRequest request = form.getRequest();
051
052        if (request.getParameterMap().containsKey(UifParameters.MESSAGE_TO_DISPLAY)) {
053            String messageToDisplay = request.getParameter(UifParameters.MESSAGE_TO_DISPLAY);
054
055            if (StringUtils.isNotBlank(messageToDisplay)) {
056                GlobalVariables.getMessageMap().putErrorForSectionId(KRADConstants.GLOBAL_ERRORS, messageToDisplay);
057            }
058        }
059
060        if (request.getParameterMap().containsKey(UifParameters.REFRESH_STATUS)) {
061            String refreshStatus = request.getParameter(UifParameters.REFRESH_STATUS);
062
063            // if the return URL reported an error, do not continue with the refresh call
064            if (UifConstants.RefreshStatus.ERROR.equals(refreshStatus)) {
065                return getModelAndViewService().getModelAndView(form);
066            }
067        }
068
069        String refreshCallerType = "";
070        if (request.getParameterMap().containsKey(KRADConstants.REFRESH_CALLER_TYPE)) {
071            refreshCallerType = request.getParameter(KRADConstants.REFRESH_CALLER_TYPE);
072        }
073
074        if (StringUtils.equals(refreshCallerType, UifConstants.RefreshCallerTypes.MULTI_VALUE_LOOKUP)) {
075            processMultiValueReturn(form, request);
076        }
077
078        if (request.getParameterMap().containsKey(KRADConstants.REFERENCES_TO_REFRESH)) {
079            final String referencesToRefresh = request.getParameter(KRADConstants.REFERENCES_TO_REFRESH);
080
081            Runnable runnable = new Runnable() {
082                @Override
083                public void run() {
084                    ViewLifecycle.getHelper().refreshReferences(referencesToRefresh);
085                }
086            };
087
088            ViewLifecycle.encapsulateLifecycle(form.getView(), form, form.getViewPostMetadata(), null, request,
089                    runnable);
090        }
091
092        if (request.getParameterMap().containsKey(UifParameters.QUICKFINDER_ID)) {
093            String quickfinderId = request.getParameter(UifParameters.QUICKFINDER_ID);
094
095            setFocusJumpFromQuickfinder(form, quickfinderId);
096
097            invokeQuickfinderCallback(form, request, quickfinderId);
098        }
099
100        return getModelAndViewService().getModelAndView(form);
101    }
102
103    /**
104     * Handles the return from a multi-value lookup, processing any select line values and invoking the
105     * configured view helper service to create the lines for those values in the model collection.
106     *
107     * <p>There are two supported strategies for returning the selected lines. One, if the lookup view
108     * and the caller are within the same application container, Springs input flash map is used. If however,
109     * the lookup view is outside the caller, then just a standard request parameter is used.</p>
110     *
111     * @param form form instance containing the model data
112     * @param request http request object being handled
113     */
114    protected void processMultiValueReturn(final UifFormBase form, HttpServletRequest request) {
115        final String lookupCollectionId = request.getParameter(UifParameters.LOOKUP_COLLECTION_ID);
116
117        final String lookupCollectionName = request.getParameter(UifParameters.LOOKUP_COLLECTION_NAME);
118        if (StringUtils.isBlank(lookupCollectionName)) {
119            throw new RuntimeException("Lookup collection name is required for processing multi-value lookup results");
120        }
121
122        final String multiValueReturnFields = request.getParameter(UifParameters.MULIT_VALUE_RETURN_FILEDS);
123        String selectedLineValuesParam = request.getParameter(UifParameters.SELECTED_LINE_VALUES);
124
125        String flashMapSelectedLineValues = "";
126        if (RequestContextUtils.getInputFlashMap(request) != null) {
127            flashMapSelectedLineValues = (String) RequestContextUtils.getInputFlashMap(request).get(
128                    UifParameters.SELECTED_LINE_VALUES);
129        }
130
131        if (!StringUtils.isBlank(flashMapSelectedLineValues)) {
132            selectedLineValuesParam = flashMapSelectedLineValues;
133        }
134
135        final String selectedLineValues = selectedLineValuesParam;
136
137        Runnable runnable = new Runnable() {
138            @Override
139            public void run() {
140                // invoked view helper to populate the collection from lookup results
141                ViewLifecycle.getHelper().processMultipleValueLookupResults(form, lookupCollectionId,
142                        lookupCollectionName, multiValueReturnFields, selectedLineValues);
143            }
144        };
145
146        ViewLifecycle.encapsulateLifecycle(form.getView(), form, form.getViewPostMetadata(), null, request, runnable);
147    }
148
149    /**
150     * Retrieves the configured focus id and jump id for the quickfinder from the post metadata, and sets
151     * those values onto the form for the view rendering.
152     *
153     * @param form form instance containing the model data
154     * @param quickfinderId id for the quickfinder component that triggered the lookup we are
155     * returning from
156     */
157    protected void setFocusJumpFromQuickfinder(UifFormBase form, String quickfinderId) {
158        String focusId = (String) form.getViewPostMetadata().getComponentPostData(quickfinderId,
159                UifConstants.PostMetadata.QUICKFINDER_FOCUS_ID);
160        if (StringUtils.isNotBlank(focusId)) {
161            form.setFocusId(focusId);
162        }
163
164        String jumpToId = (String) form.getViewPostMetadata().getComponentPostData(quickfinderId,
165                UifConstants.PostMetadata.QUICKFINDER_JUMP_TO_ID);
166        if (StringUtils.isNotBlank(jumpToId)) {
167            form.setJumpToId(jumpToId);
168        }
169    }
170
171    /**
172     * Retrieves post metadata for the quickfinder component with the given id and if a callback method
173     * has been configured, invokes that callback method.
174     *
175     * @param form form instance containing the model data
176     * @param request http request object being handled
177     * @param quickfinderId id for the quickfinder component that triggered the lookup we are
178     * returning from
179     */
180    protected void invokeQuickfinderCallback(UifFormBase form, final HttpServletRequest request,
181            final String quickfinderId) {
182        String callbackMethodToCall = (String) form.getViewPostMetadata().getComponentPostData(quickfinderId,
183                UifConstants.PostMetadata.QUICKFINDER_CALLBACK_METHOD_TO_CALL);
184        MethodInvokerConfig callbackMethod = (MethodInvokerConfig) form.getViewPostMetadata().
185                getComponentPostData(quickfinderId, UifConstants.PostMetadata.QUICKFINDER_CALLBACK_METHOD);
186
187        if (StringUtils.isBlank(callbackMethodToCall) && (callbackMethod == null)) {
188            return;
189        }
190
191        if (callbackMethod == null) {
192            callbackMethod = new MethodInvokerConfig();
193        }
194
195        // get additional parameters to be passed to the callback method
196        Map<String, String> callbackContext = (Map<String, String>) form.getViewPostMetadata().
197                getComponentPostData(quickfinderId, UifConstants.PostMetadata.QUICKFINDER_CALLBACK_CONTEXT);
198
199        // if target class or object not set, use view helper service
200        if ((callbackMethod.getTargetClass() == null) && (callbackMethod.getTargetObject() == null)) {
201            callbackMethod.setTargetObject(form.getViewHelperService());
202        }
203
204        callbackMethod.setTargetMethod(callbackMethodToCall);
205
206        Object[] arguments = new Object[3];
207        arguments[0] = form;
208        arguments[1] = quickfinderId;
209        arguments[2] = callbackContext;
210        callbackMethod.setArguments(arguments);
211
212        final MethodInvokerConfig methodToInvoke = callbackMethod;
213
214        Runnable runnable = new Runnable() {
215            @Override
216            public void run() {
217                try {
218                    methodToInvoke.prepare();
219                    methodToInvoke.invoke();
220                } catch (Exception e) {
221                    throw new RuntimeException("Error invoking callback method for quickfinder: " + quickfinderId, e);
222                }
223            }
224        };
225
226        ViewLifecycle.encapsulateLifecycle(form.getView(), form, form.getViewPostMetadata(), null, request, runnable);
227    }
228
229    /**
230     * Instance of model and view service to use within the collection service.
231     *
232     * @return ModelAndViewService instance
233     */
234    protected ModelAndViewService getModelAndViewService() {
235        return modelAndViewService;
236    }
237
238    /**
239     * @see RefreshControllerServiceImpl#getModelAndViewService()
240     */
241    public void setModelAndViewService(ModelAndViewService modelAndViewService) {
242        this.modelAndViewService = modelAndViewService;
243    }
244}