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}