View Javadoc
1   /**
2    * Copyright 2005-2015 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.kns.web.struts.action;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.struts.action.ActionForm;
21  import org.apache.struts.action.ActionForward;
22  import org.apache.struts.action.ActionMapping;
23  import org.apache.struts.action.RedirectingActionForward;
24  import org.kuali.rice.core.api.util.RiceConstants;
25  import org.kuali.rice.core.api.util.RiceKeyConstants;
26  import org.kuali.rice.kim.api.KimConstants;
27  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
28  import org.kuali.rice.kns.inquiry.Inquirable;
29  import org.kuali.rice.kns.util.WebUtils;
30  import org.kuali.rice.kns.web.struts.form.InquiryForm;
31  import org.kuali.rice.kns.web.ui.Field;
32  import org.kuali.rice.kns.web.ui.Row;
33  import org.kuali.rice.kns.web.ui.Section;
34  import org.kuali.rice.krad.bo.Attachment;
35  import org.kuali.rice.krad.bo.BusinessObject;
36  import org.kuali.rice.krad.bo.Exporter;
37  import org.kuali.rice.krad.bo.Note;
38  import org.kuali.rice.krad.bo.PersistableAttachment;
39  import org.kuali.rice.krad.bo.PersistableAttachmentList;
40  import org.kuali.rice.krad.datadictionary.BusinessObjectEntry;
41  import org.kuali.rice.krad.exception.AuthorizationException;
42  import org.kuali.rice.krad.service.KRADServiceLocator;
43  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
44  import org.kuali.rice.krad.service.ModuleService;
45  import org.kuali.rice.krad.service.NoteService;
46  import org.kuali.rice.krad.util.GlobalVariables;
47  import org.kuali.rice.krad.util.KRADConstants;
48  import org.kuali.rice.krad.util.KRADUtils;
49  
50  import javax.servlet.http.HttpServletRequest;
51  import javax.servlet.http.HttpServletResponse;
52  import java.io.ByteArrayOutputStream;
53  import java.io.IOException;
54  import java.lang.reflect.Method;
55  import java.util.Collections;
56  import java.util.List;
57  import java.util.Map;
58  
59  /**
60   * This class handles actions for inquiries of business objects.
61   *
62   * @deprecated Use {@link org.kuali.rice.krad.inquiry.InquiryController}.
63   */
64  @Deprecated
65  public class KualiInquiryAction extends KualiAction {
66      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiInquiryAction.class);
67      private NoteService noteService;
68  
69      @Override
70      protected void checkAuthorization(ActionForm form, String methodToCall) throws AuthorizationException {
71          if (!(form instanceof InquiryForm)) {
72              super.checkAuthorization(form, methodToCall);
73          } else {
74              try {
75              	if(!KRADConstants.DOWNLOAD_BO_ATTACHMENT_METHOD.equals(methodToCall)){
76              		Class businessObjectClass = Class.forName(((InquiryForm) form).getBusinessObjectClassName());
77              		if (!KimApiServiceLocator.getPermissionService().isAuthorizedByTemplate(
78                              GlobalVariables.getUserSession().getPrincipalId(), KRADConstants.KNS_NAMESPACE,
79                              KimConstants.PermissionTemplateNames.INQUIRE_INTO_RECORDS,
80                              KRADUtils.getNamespaceAndComponentSimpleName(businessObjectClass),
81                              Collections.<String, String>emptyMap())) {
82  
83              			throw new AuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), 
84                      		"inquire",
85                      		businessObjectClass.getSimpleName());
86              		}
87              	}
88              }
89              catch (ClassNotFoundException e) {
90              	LOG.warn("Unable to load BusinessObject class: " + ((InquiryForm) form).getBusinessObjectClassName(), e);
91                  super.checkAuthorization(form, methodToCall);
92              }
93          }
94      }
95  
96      @Override
97  	protected Map<String, String> getRoleQualification(ActionForm form,
98  			String methodToCall) {
99  		Map<String, String> roleQualification = super.getRoleQualification(
100 				form, methodToCall);
101 		if (form instanceof InquiryForm) {
102 			Map<String, String> primaryKeys = ((InquiryForm) form)
103 					.getInquiryPrimaryKeys();
104 			if (primaryKeys != null) {
105 				for (String keyName : primaryKeys.keySet()) {
106 					roleQualification.put(keyName, primaryKeys.get(keyName));					
107 				}
108 			}
109 		}
110 		return roleQualification;
111 	}
112 
113 	@Override
114     public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
115         request.setAttribute(KRADConstants.PARAM_MAINTENANCE_VIEW_MODE, KRADConstants.PARAM_MAINTENANCE_VIEW_MODE_INQUIRY);
116         return super.execute(mapping, form, request, response);
117     }
118 
119     /**
120      * Gets an inquirable impl from the impl service name parameter. Then calls lookup service to retrieve the record from the
121      * key/value parameters. Finally gets a list of Rows from the inquirable
122      */
123     public ActionForward start(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
124         InquiryForm inquiryForm = (InquiryForm) form;
125         if (inquiryForm.getBusinessObjectClassName() == null) {
126             LOG.error("Business object name not given.");
127             throw new RuntimeException("Business object name not given.");
128         }
129         
130         Class boClass = Class.forName(inquiryForm.getBusinessObjectClassName());
131         ModuleService responsibleModuleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(boClass);
132 		if(responsibleModuleService!=null && responsibleModuleService.isExternalizable(boClass)){
133 			String redirectUrl = responsibleModuleService.getExternalizableBusinessObjectInquiryUrl(boClass, (Map<String, String[]>) request.getParameterMap());
134 			ActionForward redirectingActionForward = new RedirectingActionForward(redirectUrl);
135 			redirectingActionForward.setModule("/");
136 			return redirectingActionForward;
137 		}
138 
139 		return continueWithInquiry(mapping, form, request, response);
140     }
141 
142 
143     /**
144      * Downloads the attachment to the user's browser
145      *
146      * @param mapping
147      * @param form
148      * @param request
149      * @param response
150      * @return ActionForward
151      * @throws Exception
152      */
153     public ActionForward downloadAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
154         InquiryForm inquiryForm = (InquiryForm) form;
155         int line = getSelectedLine(request);
156 
157         BusinessObject bo = retrieveBOFromInquirable(inquiryForm);
158         if (line < 0) {
159             if (bo instanceof PersistableAttachment) {
160                 PersistableAttachment attachment = (PersistableAttachment)bo;
161                 if (StringUtils.isNotBlank(attachment.getFileName())
162                         && attachment.getAttachmentContent() != null) {
163                     streamToResponse(attachment.getAttachmentContent(), attachment.getFileName(), attachment.getContentType(), response);
164                 }
165             }
166         } else {
167             if (bo instanceof PersistableAttachmentList) {
168                 PersistableAttachmentList<PersistableAttachment> attachmentsBo = (PersistableAttachmentList<PersistableAttachment>)bo;
169                 if (CollectionUtils.isEmpty(attachmentsBo.getAttachments())) {
170                     return null;
171                 }
172 
173                 List<? extends PersistableAttachment> attachments = attachmentsBo.getAttachments();
174                 if (CollectionUtils.isNotEmpty(attachments)
175                         && attachments.size() > line) {
176                     PersistableAttachment attachment = (PersistableAttachment)attachmentsBo.getAttachments().get(line);
177                     streamToResponse(attachment.getAttachmentContent(), attachment.getFileName(), attachment.getContentType(), response);
178                 }
179             }
180         }
181         return null;
182     }
183 
184     public ActionForward downloadCustomBOAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
185     	String fileName = request.getParameter(KRADConstants.BO_ATTACHMENT_FILE_NAME);
186 		String contentType = request.getParameter(KRADConstants.BO_ATTACHMENT_FILE_CONTENT_TYPE);
187 		String fileContentBoField = request.getParameter(KRADConstants.BO_ATTACHMENT_FILE_CONTENT_FIELD);
188     	//require certain request properties
189     	if (fileName != null
190     			&& contentType != null
191     			&& fileContentBoField != null) {
192     		//make sure user has authorization to download attachment
193     		checkAuthorization(form, findMethodToCall(form, request));
194     		
195     		fileName = StringUtils.replace(fileName, " ", "_");
196     		
197     		InquiryForm inquiryForm = (InquiryForm) form;
198         	BusinessObject bo = retrieveBOFromInquirable(inquiryForm);
199     		checkBO(bo);
200     		
201     		Class clazz = (bo.getClass());
202     		Method method = clazz.getMethod("get"+StringUtils.capitalize(fileContentBoField));
203     		byte[] fileContents = (byte[]) method.invoke(bo);
204     		streamToResponse(fileContents, fileName, contentType,response);
205     	} else {
206     		if (fileName == null) {
207     			LOG.error("Request Parameter \""+ KRADConstants.BO_ATTACHMENT_FILE_NAME + "\" not provided.");
208     		}
209     		if (contentType == null) {
210     			LOG.error("Request Parameter \""+ KRADConstants.BO_ATTACHMENT_FILE_CONTENT_TYPE + "\" not provided.");
211     		}
212     		if (fileContentBoField == null) {
213     			LOG.error("Request Parameter \""+ KRADConstants.BO_ATTACHMENT_FILE_CONTENT_FIELD + "\" not provided.");
214     		}
215     	}
216     	return null;
217     }
218     
219     
220     /**
221      * Downloads the selected attachment to the user's browser
222      *
223      * @param mapping
224      * @param form
225      * @param request
226      * @param response
227      * @return ActionForward
228      * @throws Exception
229      */
230     public ActionForward downloadBOAttachment(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
231         Long noteIdentifier = Long.valueOf(request.getParameter(KRADConstants.NOTE_IDENTIFIER));
232 
233         Note note = this.getNoteService().getNoteByNoteId(noteIdentifier);
234         if(note != null){
235         	Attachment attachment = note.getAttachment();
236         	if(attachment != null){
237         		//make sure attachment is setup with backwards reference to note (rather then doing this we could also just call the attachment service (with a new method that took in the note)
238         		attachment.setNote(note);
239         		WebUtils.saveMimeInputStreamAsFile(response, attachment.getAttachmentMimeTypeCode(), attachment.getAttachmentContents(), attachment.getAttachmentFileName(), attachment.getAttachmentFileSize().intValue());
240         	}
241         	return null;
242         }
243         
244         return mapping.findForward(RiceConstants.MAPPING_BASIC);
245     }
246     
247     public ActionForward continueWithInquiry(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
248     	InquiryForm inquiryForm = (InquiryForm) form;
249     	
250     	if (inquiryForm.getBusinessObjectClassName() == null) {
251     		LOG.error("Business object name not given.");
252     		throw new RuntimeException("Business object name not given.");
253     	}
254     	
255         BusinessObject bo = retrieveBOFromInquirable(inquiryForm);
256         checkBO(bo);
257         
258         populateSections(mapping, request, inquiryForm, bo);
259         
260         return mapping.findForward(RiceConstants.MAPPING_BASIC);
261     }
262     
263     /**
264      * Turns on (or off) the inactive record display for a maintenance collection.
265      */
266     public ActionForward toggleInactiveRecordDisplay(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
267         InquiryForm inquiryForm = (InquiryForm) form;
268         if (inquiryForm.getBusinessObjectClassName() == null) {
269             LOG.error("Business object name not given.");
270             throw new RuntimeException("Business object name not given.");
271         }
272         
273         BusinessObject bo = retrieveBOFromInquirable(inquiryForm);
274         checkBO(bo);
275         
276         Inquirable kualiInquirable = inquiryForm.getInquirable();
277         //////////////////////////////
278         String collectionName = extractCollectionName(request, KRADConstants.TOGGLE_INACTIVE_METHOD);
279         if (collectionName == null) {
280             LOG.error("Unable to get find collection name in request.");
281             throw new RuntimeException("Unable to get find collection class in request.");
282         }  
283         String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
284         boolean showInactive = Boolean.parseBoolean(StringUtils.substringBetween(parameterName, KRADConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, "."));
285         kualiInquirable.setShowInactiveRecords(collectionName, showInactive);
286         //////////////////////////////
287         
288         populateSections(mapping, request, inquiryForm, bo);
289 
290         // toggling the display to be visible again, re-open any previously closed inactive records
291         if (showInactive) {
292         	WebUtils.reopenInactiveRecords(inquiryForm.getSections(), inquiryForm.getTabStates(), collectionName);
293         }
294         
295         return mapping.findForward(RiceConstants.MAPPING_BASIC);
296     }
297 
298     @Override
299     public ActionForward toggleTab(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
300         InquiryForm inquiryForm = (InquiryForm) form;
301         if (inquiryForm.getBusinessObjectClassName() == null) {
302             LOG.error("Business object name not given.");
303             throw new RuntimeException("Business object name not given.");
304         }
305         
306         BusinessObject bo = retrieveBOFromInquirable(inquiryForm);
307         checkBO(bo);
308         
309         populateSections(mapping, request, inquiryForm, bo);
310         
311         Inquirable kualiInquirable = inquiryForm.getInquirable();
312         
313         return super.toggleTab(mapping, form, request, response);
314     }
315     
316     
317     
318     /**
319 	 * @see org.kuali.rice.krad.web.struts.action.KualiAction#hideAllTabs(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
320 	 */
321 	@Override
322 	public ActionForward hideAllTabs(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
323         // KULRICE-2852: Overrides hideAllTabs() so that it also calls the populateSections() method.
324 		InquiryForm inquiryForm = (InquiryForm) form;
325         if (inquiryForm.getBusinessObjectClassName() == null) {
326             LOG.error("Business object name not given.");
327             throw new RuntimeException("Business object name not given.");
328         }
329         
330         BusinessObject bo = retrieveBOFromInquirable(inquiryForm);
331         checkBO(bo);
332         
333         populateSections(mapping, request, inquiryForm, bo);
334 		
335 		return super.hideAllTabs(mapping, form, request, response);
336 	}
337 
338 	/**
339 	 * @see org.kuali.rice.krad.web.struts.action.KualiAction#showAllTabs(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
340 	 */
341 	@Override
342 	public ActionForward showAllTabs(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
343         // KULRICE-2852: Overrides showAllTabs() so that it also calls the populateSections() method.
344 		InquiryForm inquiryForm = (InquiryForm) form;
345         if (inquiryForm.getBusinessObjectClassName() == null) {
346             LOG.error("Business object name not given.");
347             throw new RuntimeException("Business object name not given.");
348         }
349         
350         BusinessObject bo = retrieveBOFromInquirable(inquiryForm);
351         checkBO(bo);
352         
353         populateSections(mapping, request, inquiryForm, bo);
354 		
355 		return super.showAllTabs(mapping, form, request, response);
356 	}
357 
358 	/**
359      * Handles exporting the BusinessObject for this Inquiry to XML if it has a custom XML exporter available.
360      */
361     public ActionForward export(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
362     	InquiryForm inquiryForm = (InquiryForm) form;
363     	if (inquiryForm.isCanExport()) {
364     		BusinessObject bo = retrieveBOFromInquirable(inquiryForm);
365     		checkBO(bo);
366     		if (bo != null) {
367 	    		BusinessObjectEntry businessObjectEntry = KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(inquiryForm.getBusinessObjectClassName());
368 	    		Class<? extends Exporter> exporterClass = businessObjectEntry.getExporterClass();
369 	    		if (exporterClass != null) {
370 	    			Exporter exporter = exporterClass.newInstance();
371 	        		response.setContentType(KRADConstants.XML_MIME_TYPE);
372 	        		response.setHeader("Content-disposition", "attachment; filename=export.xml");
373 	        		exporter.export(businessObjectEntry.getBusinessObjectClass(), Collections.singletonList(bo), KRADConstants.XML_FORMAT, response.getOutputStream());
374 	        	}
375     		} else {
376     			//show the empty section with error
377     			populateSections(mapping, request, inquiryForm, bo);
378     			return mapping.findForward(RiceConstants.MAPPING_BASIC); 
379     		}
380         }
381         
382         return null;
383     }
384 
385     /**
386      * Convert a Request into a Map<String,String>. Technically, Request parameters do not neatly translate into a Map of Strings,
387      * because a given parameter may legally appear more than once (so a Map of String[] would be more accurate.) This method should
388      * be safe for business objects, but may not be reliable for more general uses.
389      */
390     protected String extractCollectionName(HttpServletRequest request, String methodToCall) {
391         // collection name and underlying object type from request parameter
392         String parameterName = (String) request.getAttribute(KRADConstants.METHOD_TO_CALL_ATTRIBUTE);
393         String collectionName = null;
394         if (StringUtils.isNotBlank(parameterName)) {
395             collectionName = StringUtils.substringBetween(parameterName, methodToCall + ".", ".(");
396         }
397         return collectionName;
398     }
399     
400     protected BusinessObject retrieveBOFromInquirable(InquiryForm inquiryForm) {
401     	Inquirable kualiInquirable = inquiryForm.getInquirable();
402         // retrieve the business object
403         BusinessObject bo = kualiInquirable.getBusinessObject(inquiryForm.retrieveInquiryDecryptedPrimaryKeys());
404         if (bo == null) {
405             LOG.error("No records found in inquiry action.");
406             GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, RiceKeyConstants.ERROR_INQUIRY);
407         }
408         return bo;
409     }
410     
411     protected void populateSections(ActionMapping mapping, HttpServletRequest request, InquiryForm inquiryForm, BusinessObject bo) {
412     	Inquirable kualiInquirable = inquiryForm.getInquirable();
413     	
414     	if (bo != null) {
415     		// get list of populated sections for display
416     		List<Section> sections = kualiInquirable.getSections(bo);
417         	inquiryForm.setSections(sections);
418         	kualiInquirable.addAdditionalSections(sections, bo);
419     	} else {
420     		inquiryForm.setSections(getEmptySections(kualiInquirable.getTitle()));
421     	}
422 
423         request.setAttribute(KRADConstants.INQUIRABLE_ATTRIBUTE_NAME, kualiInquirable);
424     }
425     
426     /**
427     *
428     * Handy method to stream the byte array to response object
429     * @param attachmentDataSource
430     * @param response
431     * @throws Exception
432     */
433    protected void streamToResponse(byte[] fileContents, String fileName, String fileContentType,HttpServletResponse response) throws Exception{
434        ByteArrayOutputStream baos = null;
435        try{
436            baos = new ByteArrayOutputStream(fileContents.length);
437            baos.write(fileContents);
438            WebUtils.saveMimeOutputStreamAsFile(response, fileContentType, baos, fileName);
439        }finally{
440            try{
441                if(baos!=null){
442                    baos.close();
443                    baos = null;
444                }
445            }catch(IOException ioEx){
446                LOG.error("Error while downloading attachment");
447                throw new RuntimeException("IOException occurred while downloading attachment", ioEx);
448            }
449        }
450    }
451     /**
452      * Returns a section list with one empty section and one row.
453      * 
454      * @return list of sections
455      */
456     private List<Section> getEmptySections(String title) {
457     	final Row row = new Row(Collections.<Field>emptyList());
458     	
459     	final Section section = new Section(Collections.singletonList(row));
460 		section.setErrorKey("*");
461 		section.setSectionTitle(title != null ? title : "");
462 		section.setNumberOfColumns(0);
463 		
464 		return Collections.singletonList(section);
465     }
466     
467     /**
468      * throws an exception if BO fails the check.
469      * @param bo the BusinessObject to check.
470      * @throws UnsupportedOperationException if BO is null & no messages have been generated.
471      */
472     private void checkBO(BusinessObject bo) {
473         if (bo == null && GlobalVariables.getMessageMap().hasNoMessages()) {
474         	throw new UnsupportedOperationException("The record you have inquired on does not exist.");
475         }
476     }
477     
478     protected NoteService getNoteService() {
479 		if ( noteService == null ) {
480 			noteService = KRADServiceLocator.getNoteService();
481 		}
482 		return this.noteService;
483 	}
484 }