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