001/** 002 * Copyright 2005-2015 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.kns.web.struts.action; 017 018import org.apache.commons.collections.CollectionUtils; 019import org.apache.commons.lang.StringUtils; 020import org.apache.struts.action.ActionForm; 021import org.apache.struts.action.ActionForward; 022import org.apache.struts.action.ActionMapping; 023import org.apache.struts.action.RedirectingActionForward; 024import org.kuali.rice.core.api.util.RiceConstants; 025import org.kuali.rice.core.api.util.RiceKeyConstants; 026import org.kuali.rice.kim.api.KimConstants; 027import org.kuali.rice.kim.api.services.KimApiServiceLocator; 028import org.kuali.rice.kns.inquiry.Inquirable; 029import org.kuali.rice.kns.util.WebUtils; 030import org.kuali.rice.kns.web.struts.form.InquiryForm; 031import org.kuali.rice.kns.web.ui.Field; 032import org.kuali.rice.kns.web.ui.Row; 033import org.kuali.rice.kns.web.ui.Section; 034import org.kuali.rice.krad.bo.Attachment; 035import org.kuali.rice.krad.bo.BusinessObject; 036import org.kuali.rice.krad.bo.Exporter; 037import org.kuali.rice.krad.bo.Note; 038import org.kuali.rice.krad.bo.PersistableAttachment; 039import org.kuali.rice.krad.bo.PersistableAttachmentList; 040import org.kuali.rice.krad.datadictionary.BusinessObjectEntry; 041import org.kuali.rice.krad.exception.AuthorizationException; 042import org.kuali.rice.krad.service.KRADServiceLocator; 043import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 044import org.kuali.rice.krad.service.ModuleService; 045import org.kuali.rice.krad.service.NoteService; 046import org.kuali.rice.krad.util.GlobalVariables; 047import org.kuali.rice.krad.util.KRADConstants; 048import org.kuali.rice.krad.util.KRADUtils; 049 050import javax.servlet.http.HttpServletRequest; 051import javax.servlet.http.HttpServletResponse; 052import java.io.ByteArrayOutputStream; 053import java.io.IOException; 054import java.lang.reflect.Method; 055import java.util.Collections; 056import java.util.List; 057import java.util.Map; 058 059/** 060 * This class handles actions for inquiries of business objects. 061 * 062 * @deprecated Use {@link org.kuali.rice.krad.inquiry.InquiryController}. 063 */ 064@Deprecated 065public class KualiInquiryAction extends KualiAction { 066 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiInquiryAction.class); 067 private NoteService noteService; 068 069 @Override 070 protected void checkAuthorization(ActionForm form, String methodToCall) throws AuthorizationException { 071 if (!(form instanceof InquiryForm)) { 072 super.checkAuthorization(form, methodToCall); 073 } else { 074 try { 075 if(!KRADConstants.DOWNLOAD_BO_ATTACHMENT_METHOD.equals(methodToCall)){ 076 Class businessObjectClass = Class.forName(((InquiryForm) form).getBusinessObjectClassName()); 077 if (!KimApiServiceLocator.getPermissionService().isAuthorizedByTemplate( 078 GlobalVariables.getUserSession().getPrincipalId(), KRADConstants.KNS_NAMESPACE, 079 KimConstants.PermissionTemplateNames.INQUIRE_INTO_RECORDS, 080 KRADUtils.getNamespaceAndComponentSimpleName(businessObjectClass), 081 Collections.<String, String>emptyMap())) { 082 083 throw new AuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), 084 "inquire", 085 businessObjectClass.getSimpleName()); 086 } 087 } 088 } 089 catch (ClassNotFoundException e) { 090 LOG.warn("Unable to load BusinessObject class: " + ((InquiryForm) form).getBusinessObjectClassName(), e); 091 super.checkAuthorization(form, methodToCall); 092 } 093 } 094 } 095 096 @Override 097 protected Map<String, String> getRoleQualification(ActionForm form, 098 String methodToCall) { 099 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}