001/**
002 * Copyright 2005-2016 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.apache.log4j.Logger;
020import org.kuali.rice.core.api.CoreApiServiceLocator;
021import org.kuali.rice.core.api.exception.RiceRuntimeException;
022import org.kuali.rice.krad.service.ViewValidationService;
023import org.kuali.rice.krad.uif.UifConstants;
024import org.kuali.rice.krad.uif.UifParameters;
025import org.kuali.rice.krad.uif.component.Component;
026import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
027import org.kuali.rice.krad.uif.lifecycle.ViewPostMetadata;
028import org.kuali.rice.krad.uif.service.ViewService;
029import org.kuali.rice.krad.uif.util.ScriptUtils;
030import org.kuali.rice.krad.uif.util.UifRenderHelperMethods;
031import org.kuali.rice.krad.uif.view.MessageView;
032import org.kuali.rice.krad.uif.view.View;
033import org.kuali.rice.krad.util.KRADConstants;
034import org.kuali.rice.krad.util.KRADUtils;
035import org.kuali.rice.krad.util.UrlFactory;
036import org.kuali.rice.krad.web.form.UifFormBase;
037import org.kuali.rice.krad.web.service.ModelAndViewService;
038import org.springframework.web.servlet.ModelAndView;
039
040import javax.servlet.http.HttpServletRequest;
041import java.util.Map;
042import java.util.Properties;
043
044/**
045 * Default implementation of the model and view service.
046 *
047 * @author Kuali Rice Team (rice.collab@kuali.org)
048 */
049public class ModelAndViewServiceImpl implements ModelAndViewService {
050    private static final Logger LOG = Logger.getLogger(ModelAndViewServiceImpl.class);
051
052    private ViewService viewService;
053    private ViewValidationService viewValidationService;
054
055    /**
056     * Invokes {@link org.kuali.rice.krad.service.ViewValidationService} to validate the contents of the
057     * given form instance.
058     *
059     * {@inheritDoc}
060     */
061    @Override
062    public ModelAndView checkForm(UifFormBase form) {
063        getViewValidationService().validateViewSimulation(form);
064
065        return getModelAndView(form);
066    }
067
068    /**
069     * Builds the dialog group with the given id then creates the script for showing the dialog once the
070     * page reloads.
071     *
072     * {@inheritDoc}
073     */
074    @Override
075    public ModelAndView showDialog(String dialogId, boolean confirmation, UifFormBase form) {
076        if (form.isAjaxRequest()) {
077            form.setAjaxReturnType(UifConstants.AjaxReturnTypes.UPDATEDIALOG.getKey());
078            form.setUpdateComponentId(dialogId);
079        }
080
081        // run the lifecycle to build the dialog first
082        ModelAndView modelAndView = getModelAndView(form);
083        prepareView(form.getRequest(), modelAndView);
084
085        Component updateComponent;
086        if (form.isAjaxRequest()) {
087            updateComponent = form.getUpdateComponent();
088        } else {
089            updateComponent = form.getView();
090        }
091
092        // now add the script that will show the dialog to the on ready of the document
093        String showDialogScript = buildShowDialogScript(dialogId, confirmation, form);
094
095        String onReadyScript = ScriptUtils.appendScript(updateComponent.getOnDocumentReadyScript(), showDialogScript);
096        updateComponent.setOnDocumentReadyScript(onReadyScript);
097
098        form.getRequest().setAttribute(UifParameters.Attributes.VIEW_LIFECYCLE_COMPLETE, "true");
099
100        return modelAndView;
101    }
102
103    /**
104     * Builds a JavaScript string for invoking the showDialog method with the given dialog parameters.
105     *
106     * @param dialogId id for the dialog group to show
107     * @param confirmation whether the dialog should be shown as a confirmation
108     * @param form instance containing the model data
109     * @return String containing script
110     */
111    protected String buildShowDialogScript(String dialogId, boolean confirmation, UifFormBase form) {
112        StringBuilder showDialogScript = new StringBuilder();
113
114        showDialogScript.append(UifConstants.JsFunctions.SHOW_DIALOG);
115        showDialogScript.append("('");
116        showDialogScript.append(dialogId);
117        showDialogScript.append("', {responseHandler: ");
118        showDialogScript.append(UifConstants.JsFunctions.HANDLE_SERVER_DIALOG_RESPONSE);
119        showDialogScript.append(",responseEventData:{triggerActionId:'");
120        showDialogScript.append(form.getTriggerActionId());
121        showDialogScript.append("',confirmation:");
122        showDialogScript.append(confirmation);
123        showDialogScript.append("}});");
124
125        return showDialogScript.toString();
126    }
127
128    /**
129     * {@inheritDoc}
130     */
131    @Override
132    public ModelAndView performRedirect(UifFormBase form, String baseUrl, Properties urlParameters) {
133        String redirectUrl = UrlFactory.parameterizeUrl(baseUrl, urlParameters);
134
135        return performRedirect(form, redirectUrl);
136    }
137
138    /**
139     * {@inheritDoc}
140     */
141    @Override
142    public ModelAndView performRedirect(UifFormBase form, String redirectUrl) {
143        // indicate a redirect is occuring to prevent view processing down the line
144        form.setRequestRedirected(true);
145
146        // set the ajaxReturnType on the form this will override the return type requested by the client
147        form.setAjaxReturnType(UifConstants.AjaxReturnTypes.REDIRECT.getKey());
148
149        ModelAndView modelAndView;
150        if (form.isAjaxRequest()) {
151            modelAndView = getModelAndView(form, form.getPageId());
152            modelAndView.addObject("redirectUrl", redirectUrl);
153        } else {
154            modelAndView = new ModelAndView(UifConstants.REDIRECT_PREFIX + redirectUrl);
155        }
156
157        return modelAndView;
158    }
159
160    /**
161     * Retrieves an instance of the view with id {@link org.kuali.rice.krad.uif.UifConstants#MESSAGE_VIEW_ID}
162     * and sets the header and message from the given parameters.
163     *
164     * {@inheritDoc}
165     */
166    @Override
167    public ModelAndView getMessageView(UifFormBase form, String headerText, String messageText) {
168        MessageView messageView = (MessageView) getViewService().getViewById(UifConstants.MESSAGE_VIEW_ID);
169
170        messageView.setHeaderText(headerText);
171        messageView.setMessageText(messageText);
172
173        form.setViewId(UifConstants.MESSAGE_VIEW_ID);
174        form.setView(messageView);
175
176        return getModelAndView(form);
177    }
178
179    /**
180     * {@inheritDoc}
181     */
182    @Override
183    public ModelAndView getModelAndView(UifFormBase form) {
184        return getModelAndView(form, form.getPageId());
185    }
186
187    /**
188     * {@inheritDoc}
189     */
190    @Override
191    public ModelAndView getModelAndView(UifFormBase form, String pageId) {
192        if (StringUtils.isNotBlank(pageId)) {
193            form.setPageId(pageId);
194        }
195
196        ModelAndView modelAndView = new ModelAndView();
197        modelAndView.addObject(UifConstants.DEFAULT_MODEL_NAME, form);
198        modelAndView.setViewName(UifConstants.SPRING_VIEW_ID);
199
200        return modelAndView;
201    }
202
203    /**
204     * {@inheritDoc}
205     */
206    @Override
207    public ModelAndView getModelAndView(UifFormBase form, Map<String, Object> additionalViewAttributes) {
208        ModelAndView modelAndView = getModelAndView(form, form.getPageId());
209
210        if (additionalViewAttributes != null) {
211            for (Map.Entry<String, Object> additionalViewAttribute : additionalViewAttributes.entrySet()) {
212                modelAndView.getModelMap().put(additionalViewAttribute.getKey(), additionalViewAttribute.getValue());
213            }
214        }
215
216        return modelAndView;
217    }
218
219    /**
220     * {@inheritDoc}
221     */
222    @Override
223    public ModelAndView getModelAndViewWithInit(UifFormBase form, String viewId) {
224        form.setPageId(null);
225
226        return getModelAndViewWithInit(form, viewId, null);
227    }
228
229    /**
230     * {@inheritDoc}
231     */
232    @Override
233    public ModelAndView getModelAndViewWithInit(UifFormBase form, String viewId, String pageId) {
234        View view = getViewService().getViewById(viewId);
235
236        if (view == null) {
237            throw new RiceRuntimeException("No view was found with view id " + viewId);
238        }
239
240        form.setView(view);
241        form.setViewId(viewId);
242
243        return getModelAndView(form, pageId);
244    }
245
246    /**
247     * {@inheritDoc}
248     */
249    @Override
250    public void prepareView(HttpServletRequest request, ModelAndView modelAndView) {
251        if (modelAndView == null) {
252            return;
253        }
254
255        Object model = modelAndView.getModelMap().get(UifConstants.DEFAULT_MODEL_NAME);
256        if (!(model instanceof UifFormBase)) {
257            return;
258        }
259
260        UifFormBase form = (UifFormBase) model;
261
262        if (!form.isRequestRedirected()) {
263            invokeViewLifecycle(request, form);
264        }
265
266        // expose additional objects to the templates
267        modelAndView.addObject(UifParameters.REQUEST, request);
268        modelAndView.addObject(KRADConstants.USER_SESSION_KEY, request.getSession().getAttribute(
269                KRADConstants.USER_SESSION_KEY));
270
271        Map<String, String> properties = CoreApiServiceLocator.getKualiConfigurationService().getAllProperties();
272        modelAndView.addObject(UifParameters.CONFIG_PROPERTIES, properties);
273
274        modelAndView.addObject(UifParameters.RENDER_HELPER_METHODS, new UifRenderHelperMethods());
275    }
276
277    /**
278     * Prepares the {@link org.kuali.rice.krad.uif.view.View} instance contained on the form for rendering.
279     *
280     * @param request servlet request
281     * @param form form instance containing the data and view instance
282     */
283    protected void invokeViewLifecycle(HttpServletRequest request, UifFormBase form) {
284        // for component refreshes only lifecycle for component is performed
285        if (form.isUpdateComponentRequest() || form.isUpdateDialogRequest() || (form.isJsonRequest() && StringUtils
286                .isNotBlank(form.getUpdateComponentId()))) {
287            String refreshComponentId = form.getUpdateComponentId();
288
289            Component updateComponent = ViewLifecycle.performComponentLifecycle(form.getView(), form, request,
290                    form.getViewPostMetadata(), refreshComponentId);
291            form.setUpdateComponent(updateComponent);
292        } else {
293            // full view build
294            View view = form.getView();
295            if (view == null) {
296                LOG.warn("View in form was null: " + form);
297
298                if (!form.isJsonRequest()) {
299                    throw new IllegalStateException("View in form was null: " + form);
300                }
301                else {
302                    return;
303                }
304            }
305
306            Map<String, String> parameterMap = KRADUtils.translateRequestParameterMap(request.getParameterMap());
307            parameterMap.putAll(form.getViewRequestParameters());
308
309            // build view which will prepare for rendering
310            ViewPostMetadata postMetadata = ViewLifecycle.buildView(view, form, request, parameterMap);
311            form.setViewPostMetadata(postMetadata);
312
313            if (form.isUpdatePageRequest()) {
314                Component updateComponent = form.getView().getCurrentPage();
315                form.setUpdateComponent(updateComponent);
316            }
317
318            // update the page on the form to reflect the current page of the view
319            form.setPageId(view.getCurrentPageId());
320        }
321    }
322
323    protected ViewService getViewService() {
324        return viewService;
325    }
326
327    public void setViewService(ViewService viewService) {
328        this.viewService = viewService;
329    }
330
331    public ViewValidationService getViewValidationService() {
332        return viewValidationService;
333    }
334
335    public void setViewValidationService(ViewValidationService viewValidationService) {
336        this.viewValidationService = viewValidationService;
337    }
338}