1 /**
2 * Copyright 2005-2012 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.web.form;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.codehaus.jackson.map.ObjectMapper;
20 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
21 import org.kuali.rice.krad.uif.UifConstants;
22 import org.kuali.rice.krad.uif.UifParameters;
23 import org.kuali.rice.krad.uif.view.History;
24 import org.kuali.rice.krad.uif.view.View;
25 import org.kuali.rice.krad.uif.service.ViewService;
26 import org.kuali.rice.krad.uif.view.ViewIndex;
27 import org.kuali.rice.krad.uif.view.ViewModel;
28 import org.kuali.rice.krad.util.KRADUtils;
29 import org.springframework.web.multipart.MultipartFile;
30 import org.kuali.rice.krad.uif.UifConstants.ViewType;
31
32 import javax.servlet.http.HttpServletRequest;
33 import java.io.IOException;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Properties;
39 import java.util.Set;
40 import java.util.UUID;
41
42 /**
43 * Base form class for views within the KRAD User Interface Framework
44 *
45 * <p>
46 * Holds properties necessary to determine the <code>View</code> instance that
47 * will be used to render the UI
48 * </p>
49 *
50 * @author Kuali Rice Team (rice.collab@kuali.org)
51 */
52 public class UifFormBase implements ViewModel {
53 private static final long serialVersionUID = 8432543267099454434L;
54
55 // current view
56 protected String viewId;
57 protected String viewName;
58 protected ViewType viewTypeName;
59 protected String pageId;
60 protected String methodToCall;
61 protected String formKey;
62 protected String jumpToId;
63 protected String jumpToName;
64 protected String focusId;
65 protected String formPostUrl;
66
67 protected boolean defaultsApplied;
68 protected boolean skipViewInit;
69
70 protected View view;
71 protected View postedView;
72
73 protected Map<String, String> viewRequestParameters;
74 protected List<String> readOnlyFieldsList;
75
76 protected Map<String, Object> newCollectionLines;
77 protected Map<String, String> actionParameters;
78 protected Map<String, Object> clientStateForSyncing;
79 protected Map<String, Set<String>> selectedCollectionLines;
80
81 protected MultipartFile attachmentFile;
82
83 // navigation
84 protected String returnLocation;
85 protected String returnFormKey;
86
87 protected History formHistory;
88
89 protected boolean renderFullView;
90 protected boolean validateDirty;
91
92 public UifFormBase() {
93 formKey = generateFormKey();
94 renderFullView = true;
95 defaultsApplied = false;
96 skipViewInit = false;
97
98 formHistory = new History();
99
100 readOnlyFieldsList = new ArrayList<String>();
101 viewRequestParameters = new HashMap<String, String>();
102 newCollectionLines = new HashMap<String, Object>();
103 actionParameters = new HashMap<String, String>();
104 clientStateForSyncing = new HashMap<String, Object>();
105 selectedCollectionLines = new HashMap<String, Set<String>>();
106 }
107
108 /**
109 * Creates the unique id used to store this "conversation" in the session.
110 * The default method generates a java UUID.
111 *
112 * @return
113 */
114 protected String generateFormKey() {
115 return UUID.randomUUID().toString();
116 }
117
118 /**
119 * Called after Spring binds the request to the form and before the
120 * controller method is invoked.
121 *
122 * @param request - request object containing the query parameters
123 */
124 public void postBind(HttpServletRequest request) {
125 // default form post URL to request URL
126 formPostUrl = request.getRequestURL().toString();
127
128 // get any sent client view state and parse into map
129 if (request.getParameterMap().containsKey(UifParameters.CLIENT_VIEW_STATE)) {
130 String clientStateJSON = request.getParameter(UifParameters.CLIENT_VIEW_STATE);
131 if (StringUtils.isNotBlank(clientStateJSON)) {
132 // change single quotes to double quotes (necessary because the reverse was done for sending)
133 clientStateJSON = StringUtils.replace(clientStateJSON, "'", "\"");
134
135 ObjectMapper mapper = new ObjectMapper();
136 try {
137 clientStateForSyncing = mapper.readValue(clientStateJSON, Map.class);
138 } catch (IOException e) {
139 throw new RuntimeException("Unable to decode client side state JSON", e);
140 }
141 }
142 }
143
144 // populate read only fields list
145 if (request.getParameter(UifParameters.READ_ONLY_FIELDS) != null) {
146 String readOnlyFields = request.getParameter(UifParameters.READ_ONLY_FIELDS);
147 setReadOnlyFieldsList(KRADUtils.convertStringParameterToList(readOnlyFields));
148 }
149
150 // reset skip view init parameter if not passed
151 if (!request.getParameterMap().containsKey(UifParameters.SKIP_VIEW_INIT)) {
152 skipViewInit = false;
153 }
154 }
155
156 /**
157 * @see org.kuali.rice.krad.uif.view.ViewModel#getViewId()
158 */
159 public String getViewId() {
160 return this.viewId;
161 }
162
163 /**
164 * @see org.kuali.rice.krad.uif.view.ViewModel#setViewId(java.lang.String)
165 */
166 public void setViewId(String viewId) {
167 this.viewId = viewId;
168 }
169
170 /**
171 * @see org.kuali.rice.krad.uif.view.ViewModel#getViewName()
172 */
173 public String getViewName() {
174 return this.viewName;
175 }
176
177 /**
178 * @see org.kuali.rice.krad.uif.view.ViewModel#setViewName(java.lang.String)
179 */
180 public void setViewName(String viewName) {
181 this.viewName = viewName;
182 }
183
184 /**
185 * @see org.kuali.rice.krad.uif.view.ViewModel#getViewTypeName()
186 */
187 public ViewType getViewTypeName() {
188 return this.viewTypeName;
189 }
190
191 /**
192 * @see org.kuali.rice.krad.uif.view.ViewModel#setViewTypeName(org.kuali.rice.krad.uif.UifConstants.ViewType)
193 */
194 public void setViewTypeName(ViewType viewTypeName) {
195 this.viewTypeName = viewTypeName;
196 }
197
198 /**
199 * @see org.kuali.rice.krad.uif.view.ViewModel#getPageId()
200 */
201 public String getPageId() {
202 return this.pageId;
203 }
204
205 /**
206 * @see org.kuali.rice.krad.uif.view.ViewModel#setPageId(java.lang.String)
207 */
208 public void setPageId(String pageId) {
209 this.pageId = pageId;
210 }
211
212 /**
213 * @see org.kuali.rice.krad.uif.view.ViewModel#getFormPostUrl()
214 */
215 public String getFormPostUrl() {
216 return this.formPostUrl;
217 }
218
219 /**
220 * @see org.kuali.rice.krad.uif.view.ViewModel#setFormPostUrl(java.lang.String)
221 */
222 public void setFormPostUrl(String formPostUrl) {
223 this.formPostUrl = formPostUrl;
224 }
225
226 public String getReturnLocation() {
227 return this.returnLocation;
228 }
229
230 public void setReturnLocation(String returnLocation) {
231 this.returnLocation = returnLocation;
232 }
233
234 public String getReturnFormKey() {
235 return this.returnFormKey;
236 }
237
238 public void setReturnFormKey(String returnFormKey) {
239 this.returnFormKey = returnFormKey;
240 }
241
242 /**
243 * Identifies the controller method that should be invoked to fulfill a
244 * request. The value will be matched up against the 'params' setting on the
245 * <code>RequestMapping</code> annotation for the controller method
246 *
247 * @return String method to call
248 */
249 public String getMethodToCall() {
250 return this.methodToCall;
251 }
252
253 /**
254 * Setter for the method to call
255 *
256 * @param methodToCall
257 */
258 public void setMethodToCall(String methodToCall) {
259 this.methodToCall = methodToCall;
260 }
261
262 /**
263 * @see org.kuali.rice.krad.uif.view.ViewModel#getViewRequestParameters()
264 */
265 public Map<String, String> getViewRequestParameters() {
266 return this.viewRequestParameters;
267 }
268
269 /**
270 * @see org.kuali.rice.krad.uif.view.ViewModel#setViewRequestParameters(java.util.Map<java.lang.String,java.lang.String>)
271 */
272 public void setViewRequestParameters(Map<String, String> viewRequestParameters) {
273 this.viewRequestParameters = viewRequestParameters;
274 }
275
276 /**
277 * @see org.kuali.rice.krad.uif.view.ViewModel#getReadOnlyFieldsList()
278 */
279 public List<String> getReadOnlyFieldsList() {
280 return readOnlyFieldsList;
281 }
282
283 /**
284 * @see org.kuali.rice.krad.uif.view.ViewModel#setReadOnlyFieldsList(java.util.List<java.lang.String>)
285 */
286 public void setReadOnlyFieldsList(List<String> readOnlyFieldsList) {
287 this.readOnlyFieldsList = readOnlyFieldsList;
288 }
289
290 /**
291 * @see org.kuali.rice.krad.uif.view.ViewModel#getNewCollectionLines()
292 */
293 public Map<String, Object> getNewCollectionLines() {
294 return this.newCollectionLines;
295 }
296
297 /**
298 * @see org.kuali.rice.krad.uif.view.ViewModel#setNewCollectionLines(java.util.Map<java.lang.String,java.lang.Object>)
299 */
300 public void setNewCollectionLines(Map<String, Object> newCollectionLines) {
301 this.newCollectionLines = newCollectionLines;
302 }
303
304 /**
305 * @see org.kuali.rice.krad.uif.view.ViewModel#getActionParameters()
306 */
307 public Map<String, String> getActionParameters() {
308 return this.actionParameters;
309 }
310
311 /**
312 * Returns the action parameters map as a <code>Properties</code> instance
313 *
314 * @return Properties action parameters
315 */
316 public Properties getActionParametersAsProperties() {
317 return KRADUtils.convertMapToProperties(actionParameters);
318 }
319
320 /**
321 * @see org.kuali.rice.krad.uif.view.ViewModel#setActionParameters(java.util.Map<java.lang.String,java.lang.String>)
322 */
323 public void setActionParameters(Map<String, String> actionParameters) {
324 this.actionParameters = actionParameters;
325 }
326
327 /**
328 * Retrieves the value for the given action parameter, or empty string if
329 * not found
330 *
331 * @param actionParameterName - name of the action parameter to retrieve value for
332 * @return String parameter value or empty string
333 */
334 public String getActionParamaterValue(String actionParameterName) {
335 if ((actionParameters != null) && actionParameters.containsKey(actionParameterName)) {
336 return actionParameters.get(actionParameterName);
337 }
338
339 return "";
340 }
341
342 /**
343 * Returns the action event that was sent in the action parameters (if any)
344 *
345 * <p>
346 * The action event is a special action parameter that can be sent to indicate a type of action being taken. This
347 * can be looked at by the view or components to render differently
348 * </p>
349 *
350 * TODO: make sure action parameters are getting reinitialized on each request
351 *
352 * @return String action event name or blank if action event was not sent
353 */
354 public String getActionEvent() {
355 if ((actionParameters != null) && actionParameters.containsKey(UifConstants.UrlParams.ACTION_EVENT)) {
356 return actionParameters.get(UifConstants.UrlParams.ACTION_EVENT);
357 }
358
359 return "";
360 }
361
362 /**
363 * @see org.kuali.rice.krad.uif.view.ViewModel#getClientStateForSyncing()
364 */
365 public Map<String, Object> getClientStateForSyncing() {
366 return clientStateForSyncing;
367 }
368
369 /**
370 * @see org.kuali.rice.krad.uif.view.ViewModel#getSelectedCollectionLines()
371 */
372 public Map<String, Set<String>> getSelectedCollectionLines() {
373 return selectedCollectionLines;
374 }
375
376 /**
377 * @see org.kuali.rice.krad.uif.view.ViewModel#setSelectedCollectionLines(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)
378 */
379 public void setSelectedCollectionLines(Map<String, Set<String>> selectedCollectionLines) {
380 this.selectedCollectionLines = selectedCollectionLines;
381 }
382
383 /**
384 * Key string that identifies the form instance in session storage
385 *
386 * <p>
387 * When the view is posted, the previous form instance is retrieved and then
388 * populated from the request parameters. This key string is retrieve the
389 * session form from the session service
390 * </p>
391 *
392 * @return String form session key
393 */
394 public String getFormKey() {
395 return this.formKey;
396 }
397
398 /**
399 * Setter for the form's session key
400 *
401 * @param formKey
402 */
403 public void setFormKey(String formKey) {
404 this.formKey = formKey;
405 }
406
407 /**
408 * @see org.kuali.rice.krad.uif.view.ViewModel#isDefaultsApplied()
409 */
410 public boolean isDefaultsApplied() {
411 return this.defaultsApplied;
412 }
413
414 /**
415 * @see org.kuali.rice.krad.uif.view.ViewModel#setDefaultsApplied(boolean)
416 */
417 public void setDefaultsApplied(boolean defaultsApplied) {
418 this.defaultsApplied = defaultsApplied;
419 }
420
421 /**
422 * Indicates whether a new view is being initialized or the call is refresh (or query) call
423 *
424 * @return boolean true if view initialization was skipped, false if new view is being created
425 */
426 public boolean isSkipViewInit() {
427 return skipViewInit;
428 }
429
430 /**
431 * Setter for the skip view initialization flag
432 *
433 * @param skipViewInit
434 */
435 public void setSkipViewInit(boolean skipViewInit) {
436 this.skipViewInit = skipViewInit;
437 }
438
439 /**
440 * Holder for files that are attached through the view
441 *
442 * @return MultipartFile representing the attachment
443 */
444 public MultipartFile getAttachmentFile() {
445 return this.attachmentFile;
446 }
447
448 /**
449 * Setter for the form's attachment file
450 *
451 * @param attachmentFile
452 */
453 public void setAttachmentFile(MultipartFile attachmentFile) {
454 this.attachmentFile = attachmentFile;
455 }
456
457 /**
458 * @return the renderFullView
459 */
460 public boolean isRenderFullView() {
461 return this.renderFullView;
462 }
463
464 /**
465 * @param renderFullView
466 */
467 public void setRenderFullView(boolean renderFullView) {
468 this.renderFullView = renderFullView;
469 }
470
471 /**
472 * @see org.kuali.rice.krad.uif.view.ViewModel#getView()
473 */
474 public View getView() {
475 return this.view;
476 }
477
478 /**
479 * @see org.kuali.rice.krad.uif.view.ViewModel#setView(org.kuali.rice.krad.uif.view.View)
480 */
481 public void setView(View view) {
482 this.view = view;
483 }
484
485 /**
486 * @see org.kuali.rice.krad.uif.view.ViewModel#getPostedView()
487 */
488 public View getPostedView() {
489 return this.postedView;
490 }
491
492 /**
493 * @see org.kuali.rice.krad.uif.view.ViewModel#setPostedView(org.kuali.rice.krad.uif.view.View)
494 */
495 public void setPostedView(View postedView) {
496 this.postedView = postedView;
497 }
498
499 /**
500 * Instance of the <code>ViewService</code> that can be used to retrieve
501 * <code>View</code> instances
502 *
503 * @return ViewService implementation
504 */
505 protected ViewService getViewService() {
506 return KRADServiceLocatorWeb.getViewService();
507 }
508
509 /**
510 * The jumpToId for this form, the element with this id will be jumped to automatically
511 * when the form is loaded in the view.
512 * Using "TOP" or "BOTTOM" will jump to the top or the bottom of the resulting page.
513 * jumpToId always takes precedence over jumpToName, if set.
514 *
515 * @return the jumpToId
516 */
517 public String getJumpToId() {
518 return this.jumpToId;
519 }
520
521 /**
522 * @param jumpToId the jumpToId to set
523 */
524 public void setJumpToId(String jumpToId) {
525 this.jumpToId = jumpToId;
526 }
527
528 /**
529 * The jumpToName for this form, the element with this name will be jumped to automatically
530 * when the form is loaded in the view.
531 * WARNING: jumpToId always takes precedence over jumpToName, if set.
532 *
533 * @return the jumpToName
534 */
535 public String getJumpToName() {
536 return this.jumpToName;
537 }
538
539 /**
540 * @param jumpToName the jumpToName to set
541 */
542 public void setJumpToName(String jumpToName) {
543 this.jumpToName = jumpToName;
544 }
545
546 /**
547 * Field to place focus on when the page loads
548 * An empty focusId will result in focusing on the first visible input element by default.
549 *
550 * @return the focusId
551 */
552 public String getFocusId() {
553 return this.focusId;
554 }
555
556 /**
557 * @param focusId the focusId to set
558 */
559 public void setFocusId(String focusId) {
560 this.focusId = focusId;
561 }
562
563 /**
564 * History parameter representing the History of views that have come before the
565 * viewing of the current view
566 *
567 * <p>
568 * Used for breadcrumb widget generation on the view and also for navigating back
569 * to previous or hub locations
570 * </p>
571 *
572 * @return History instance giving current history
573 */
574 public History getFormHistory() {
575 return formHistory;
576 }
577
578 /**
579 * Setter for the current History object
580 *
581 * @param history the history to set
582 */
583 public void setFormHistory(History history) {
584 this.formHistory = history;
585 }
586
587 /**
588 * Indicates whether the form should be validated for dirtyness
589 *
590 * <p>
591 * For FormView, it's necessary to validate when the user tries to navigate out of the form. If set, all the
592 * InputFields will be validated on refresh, navigate, cancel or close Action or on form
593 * unload and if dirty, displays a message and user can decide whether to continue with
594 * the action or stay on the form
595 * </p>
596 *
597 * @return boolean true if dirty validation should be enabled
598 */
599 public boolean isValidateDirty() {
600 return this.validateDirty;
601 }
602
603 /**
604 * Setter for dirty validation indicator
605 *
606 * @param validateDirty
607 */
608 public void setValidateDirty(boolean validateDirty) {
609 this.validateDirty = validateDirty;
610 }
611
612 }