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.krad.web.controller;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.krad.bo.Exporter;
21  import org.kuali.rice.krad.datadictionary.DataDictionary;
22  import org.kuali.rice.krad.datadictionary.DataObjectEntry;
23  import org.kuali.rice.krad.service.DataDictionaryService;
24  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
25  import org.kuali.rice.krad.uif.UifConstants;
26  import org.kuali.rice.krad.uif.UifParameters;
27  import org.kuali.rice.krad.uif.container.CollectionGroup;
28  import org.kuali.rice.krad.uif.layout.collections.TableExporter;
29  import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
30  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
31  import org.kuali.rice.krad.util.GlobalVariables;
32  import org.kuali.rice.krad.util.KRADConstants;
33  import org.kuali.rice.krad.web.form.InquiryForm;
34  import org.kuali.rice.krad.web.form.UifFormBase;
35  import org.springframework.beans.factory.annotation.Autowired;
36  import org.springframework.stereotype.Controller;
37  import org.springframework.validation.BindingResult;
38  import org.springframework.web.bind.annotation.ModelAttribute;
39  import org.springframework.web.bind.annotation.RequestMapping;
40  import org.springframework.web.bind.annotation.RequestMethod;
41  import org.springframework.web.bind.annotation.ResponseBody;
42  import org.springframework.web.servlet.ModelAndView;
43  
44  import javax.servlet.http.HttpServletRequest;
45  import javax.servlet.http.HttpServletResponse;
46  import java.util.Collections;
47  import java.util.List;
48  
49  /**
50   * Controller that handles table export requests.
51   *
52   * @author Kuali Rice Team (rice.collab@kuali.org)
53   */
54  @Controller
55  @RequestMapping(value = "/export")
56  public class UifExportController extends UifControllerBase {
57      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(UifExportController.class);
58  
59      @Autowired
60      protected HttpServletRequest request;
61  
62      /**
63       * Retrieves the session form for the form key request parameter so we can initialize a form instance of the
64       * same type the view was rendered with.
65       *
66       * {@inheritDoc}
67       */
68      @Override
69      protected UifFormBase createInitialForm() {
70          String formKey = request.getParameter(UifParameters.FORM_KEY);
71          if (StringUtils.isBlank(formKey)) {
72              throw new RuntimeException("Unable to create export form due to misssing form key parameter");
73          }
74  
75          UifFormBase sessionForm = GlobalVariables.getUifFormManager().getSessionForm(formKey);
76          if (sessionForm != null) {
77              try {
78                  return sessionForm.getClass().newInstance();
79              } catch (Exception e) {
80                  throw new RuntimeException("Cannot create export form instance from session form", e);
81              }
82          }
83  
84          return null;
85      }
86  
87      /**
88       * Generates exportable table data as CSV based on the rich table selected.
89       */
90      @MethodAccessible
91      @RequestMapping(method = RequestMethod.GET, params = "methodToCall=" + UifConstants.MethodToCallNames.TABLE_CSV,
92              produces = {"text/csv"})
93      @ResponseBody
94      public String tableCsvRetrieval(@ModelAttribute("KualiForm") UifFormBase form, HttpServletRequest request,
95              HttpServletResponse response) {
96          LOG.debug("processing csv table data request");
97  
98          return retrieveTableData(form, request, response);
99      }
100 
101     /**
102      * Generates exportable table data in xsl based on the rich table selected.
103      */
104     @MethodAccessible
105     @RequestMapping(method = RequestMethod.GET, params = "methodToCall=" + UifConstants.MethodToCallNames.TABLE_XLS,
106             produces = {"application/vnd.ms-excel"})
107     @ResponseBody
108     public String tableXlsRetrieval(@ModelAttribute("KualiForm") UifFormBase form, HttpServletRequest request,
109             HttpServletResponse response) {
110         LOG.debug("processing xls table data request");
111 
112         return retrieveTableData(form, request, response);
113     }
114 
115     /**
116      * Generates exportable table data based on the rich table selected.
117      */
118     @MethodAccessible
119     @RequestMapping(method = RequestMethod.GET, params = "methodToCall=" + UifConstants.MethodToCallNames.TABLE_XML,
120             produces = {"application/xml"})
121     @ResponseBody
122     public String tableXmlRetrieval(@ModelAttribute("KualiForm") UifFormBase form, HttpServletRequest request,
123             HttpServletResponse response) {
124         LOG.debug("processing xml table data request");
125 
126         return retrieveTableData(form, request, response);
127     }
128 
129     /**
130      * Handles exporting the dataObject for this Inquiry to XML if it has a custom XML exporter available.
131      *
132      * @param form   KualiForm
133      * @param result  interface that represents binding results
134      * @param request the http request that was made
135      * @param response the http response object
136      */
137     @RequestMapping(method = RequestMethod.GET, params = "methodToCall=" + UifConstants.MethodToCallNames.INQUIRY_XML,
138             produces = {"application/xml"})
139     @ResponseBody
140     public ModelAndView inquiryXmlRetrieval(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
141             HttpServletRequest request, HttpServletResponse response) throws Exception {
142         InquiryForm inquiryForm = (InquiryForm) form;
143         Object dataObject = inquiryForm.getDataObject();
144 
145         if (dataObject != null) {
146             applyCustomExport(Collections.singletonList(dataObject), inquiryForm.getDataObjectClassName(),
147                     KRADConstants.XML_FORMAT, response);
148         }
149 
150         return null;
151     }
152 
153 
154     /**
155      * Generates exportable table data based on the rich table selected.
156      *
157      * <p>First the lifecycle process is run to rebuild the collection group, then
158      * {@link org.kuali.rice.krad.uif.layout.collections.TableExporter} is invoked to build the export data from
159      * the collection.</p>
160      */
161     protected String retrieveTableData(@ModelAttribute("KualiForm") UifFormBase form, HttpServletRequest request,
162             HttpServletResponse response) {
163         LOG.debug("processing table data request");
164 
165         CollectionGroup collectionGroup = (CollectionGroup) ViewLifecycle.performComponentLifecycle(form.getView(),
166                 form, request, form.getViewPostMetadata(), form.getUpdateComponentId());
167 
168         List<Object> modelCollection = ObjectPropertyUtils.getPropertyValue(form,
169                 collectionGroup.getBindingInfo().getBindingPath());
170 
171 
172         Class<?> dataObjectClass = collectionGroup.getCollectionObjectClass();
173         String formatType = getValidatedFormatType(request.getParameter(UifParameters.FORMAT_TYPE));
174 
175         // set update none to prevent the lifecycle from being run after the controller finishes
176         form.setAjaxReturnType(UifConstants.AjaxReturnTypes.UPDATENONE.getKey());
177 
178         if (applyCustomExport(modelCollection, dataObjectClass.getName(), formatType, response)) {
179             return null;
180         }
181 
182         // generic export
183         return TableExporter.buildExportTableData(collectionGroup, form, formatType);
184     }
185 
186     /**
187      * Checks if a custom exporter can be applied.
188      *
189      * @param dataObjectEntry the data dictionary entry for the data object
190      *
191      * @return true if a custom exporter can be found, false otherwise
192      */
193     protected boolean canApplyCustomExport(DataObjectEntry dataObjectEntry) {
194         if (dataObjectEntry == null) {
195             return false;
196         }
197 
198         return dataObjectEntry.getExporterClass() != null;
199     }
200 
201     /**
202      * Applies custom export if an exporter class is defined. Will return false if no exporter class defined
203      * or if the dataObject collection is empty.
204      *
205      *
206      * @param dataObjectCollection
207      * @param dataObjectClassName
208      * @param formatType
209      *
210      * @param response  true if custom exporter applied else return false.
211      *
212      */
213     protected boolean applyCustomExport(List<Object> dataObjectCollection, String dataObjectClassName,
214             String formatType, HttpServletResponse response) {
215 
216         String contentType = getContentType(formatType);
217         setAttachmentResponseHeader(response, "export." + formatType, contentType);
218 
219 
220         // check for custom exporter class defined for the data object class
221         DataObjectEntry dataObjectEntry =
222                 KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDataObjectEntry(
223                         dataObjectClassName);
224 
225         // Return if no dataobject present to export
226         if (CollectionUtils.isEmpty(dataObjectCollection)) {
227             return false;
228         }
229 
230         // No custom exporter present
231         if (!canApplyCustomExport(dataObjectEntry)) {
232             return false;
233         }
234 
235         try {
236             Exporter exporter = dataObjectEntry.getExporterClass().newInstance();
237 
238             if (exporter.getSupportedFormats(dataObjectEntry.getDataObjectClass()).contains(formatType)) {
239                 exporter.export(dataObjectEntry.getDataObjectClass(), dataObjectCollection, formatType, response.getOutputStream());
240             }
241         } catch (Exception e) {
242             throw new RuntimeException("Cannot invoked custom exporter class", e);
243         }
244 
245         return false;
246     }
247 
248     /**
249      * Creates consistent setup of attachment response header.
250      *
251      * @param response http response object
252      * @param filename name of the return file
253      * @param contentType return content type
254      */
255     protected void setAttachmentResponseHeader(HttpServletResponse response, String filename, String contentType) {
256         response.setContentType(contentType);
257         response.setHeader("Content-disposition", "attachment; filename=\"" + filename + "\"");
258         response.setHeader("Expires", "0");
259         response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
260         response.setHeader("Pragma", "public");
261     }
262 
263     /**
264      * Reviews and returns a valid format type, defaults to csv.
265      *
266      * @param formatType format type to validate
267      * @return valid format type
268      */
269     protected String getValidatedFormatType(String formatType) {
270         if (KRADConstants.EXCEL_FORMAT.equals(formatType) || KRADConstants.XML_FORMAT.equals(formatType)) {
271             return formatType;
272         }
273 
274         return KRADConstants.CSV_FORMAT;
275     }
276 
277     /**
278      * Reviews and returns a valid content type, defaults to text/csv.
279      *
280      * @param formatType format type to return content type for
281      * @return valid content type
282      */
283     protected String getContentType(String formatType) {
284         if (KRADConstants.EXCEL_FORMAT.equals(formatType)) {
285             return KRADConstants.EXCEL_MIME_TYPE;
286         } else if (KRADConstants.XML_FORMAT.equals(formatType)) {
287             return KRADConstants.XML_MIME_TYPE;
288         } else {
289             return KRADConstants.CSV_MIME_TYPE;
290         }
291     }
292 }