diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.desc.xml new file mode 100644 index 0000000000..03894a1a8c --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.desc.xml @@ -0,0 +1,11 @@ + + DataLists - Download a Data List + Data Lists Component - Download a Data List + /slingshot/datalists/list/site/{site}/{container}/{list}/ + /slingshot/datalists/list/site/{site}/{container}/{list} + /slingshot/datalists/list/node/{store_type}/{store_id}/{id} + argument + user + required + internal + diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xls.ftl b/config/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xls.ftl new file mode 100644 index 0000000000..87cc72dbdb --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xls.ftl @@ -0,0 +1 @@ +<#-- Must not have a newline or any other contents! -->${writeExcel.write()} \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xlsx.ftl b/config/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xlsx.ftl new file mode 100644 index 0000000000..87cc72dbdb --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/datalists/list.get.xlsx.ftl @@ -0,0 +1 @@ +<#-- Must not have a newline or any other contents! -->${writeExcel.write()} \ No newline at end of file diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index f75b4ad36b..2fce714baa 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -1128,4 +1128,52 @@ + + + + + + + + + + + + + + + + dl:todoTitle,dl:todoDueDate,dl:todoPriority,dl:todoStatus,dl:todoNotes,dl:assignee,dl:attachments + + + cm:title,cm:description,dl:ganttStartDate,dl:ganttEndDate,dl:taskAssignee,dl:taskPriority,dl:taskStatus,dl:ganttPercentComplete,dl:taskComments,cm:attachments + + + cm:title,cm:description,dl:simpletaskDueDate,dl:simpletaskPriority,dl:simpletaskStatus,dl:simpletaskComments + + + dl:contactFirstName,dl:contactLastName,dl:contactEmail,dl:contactCompany,dl:contactJobTitle,dl:contactPhoneOffice,dl:contactPhoneMobile,dl:contactNotes + + + dl:issueID,cm:title,dl:issueAssignedTo,dl:issueStatus,dl:issuePriority,cm:description,dl:issueDueDate,dl:issueComments,cm:attachments + + + cm:title,cm:description,dl:eventLocation,dl:eventStartDate,dl:eventEndDate,dl:eventRegistrations,cm:attachments,dl:eventNote + + + cm:title,dl:locationAddress1,dl:locationAddress2,dl:locationAddress3,dl:locationZip,dl:locationState,dl:locationCountry,cm:description,cm:attachments + + + dl:meetingAgendaRef,cm:title,cm:description,dl:meetingAgendaTime,dl:meetingAgendaOwner,cm:attachments + + + dl:eventAgendaRef,dl:eventAgendaStartTime,dl:eventAgendaEndTime,dl:eventAgendaSessionName,dl:eventAgendaPresenter,dl:eventAgendaAudience,cm:attachments,dl:eventAgendaNotes + + + + + + diff --git a/source/java/org/alfresco/repo/web/scripts/DeclarativeSpreadsheetWebScript.java b/source/java/org/alfresco/repo/web/scripts/DeclarativeSpreadsheetWebScript.java new file mode 100644 index 0000000000..d569d4518d --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/DeclarativeSpreadsheetWebScript.java @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.CSVStrategy; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.Comment; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + + +/** + * Parent of Declarative Webscripts that generate Excel files, + * usually based on some sort of dictionary model. + * + * @author Nick Burch + */ +public abstract class DeclarativeSpreadsheetWebScript extends DeclarativeWebScript +{ + public static final String MODEL_CSV = "csv"; + public static final String MODEL_EXCEL = "excel"; + + protected DictionaryService dictionaryService; + protected String filenameBase; + + /** + * @param dictionaryService the DictionaryService to set + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * Identifies the resource for the webscript. + */ + protected abstract Object identifyResource(String format, WebScriptRequest req); + + /** + * If the format is requested as HTML, should an exception be raised, + * or should an HTML version be called? + */ + protected abstract boolean allowHtmlFallback(); + + /** + * Returns the QNames of the model properties to be output in + * the header, and if they're required or not + */ + protected abstract List> buildPropertiesForHeader(Object resource, String format, WebScriptRequest req); + + /** + * Populates the body of the Excel Workbook, once the header has been + * output. + * This is called if the format is .xls or .xlsx + */ + protected abstract void populateBody(Object resource, Workbook workbook, Sheet sheet, List properties) + throws IOException; + + /** + * Populates the body of the CSV file, once the header has been + * output. + * This is called if the format is .csv + */ + protected abstract void populateBody(Object resource, CSVPrinter csv, List properties) + throws IOException; + + /** + * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status) + */ + @Override + protected Map executeImpl(WebScriptRequest req, Status status) + { + Map model = new HashMap(); + model.put("success", Boolean.TRUE); + + // What format are they after? + String format = req.getFormat(); + if("csv".equals(format) || "xls".equals(format) || + "xlsx".equals(format) || "excel".equals(format)) + { + // Identify the thing to process + Object resource = identifyResource(format, req); + + // Generate the spreadsheet + try + { + generateSpreadsheet(resource, format, req, status, model); + return model; + } + catch(IOException e) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, + "Unable to generate template file", e); + } + } + + // If we get here, then it isn't a spreadsheet version + if(allowHtmlFallback()) + { + // There's some sort of help / upload form + return model; + } + else + { + throw new WebScriptException("Web Script format '" + format + "' is not supported"); + } + } + + /** + * Generates the spreadsheet, based on the properties in the header + * and a callback for the body. + */ + public void generateSpreadsheet(Object resource, String format, WebScriptRequest req, + Status status, Map model) throws IOException + { + Pattern qnameMunger = Pattern.compile("([A-Z][a-z]+)([A-Z].*)"); + + // Build up the details of the header + List> propertyDetails = buildPropertiesForHeader(resource, format, req); + String[] headings = new String[propertyDetails.size()]; + String[] descriptions = new String[propertyDetails.size()]; + boolean[] required = new boolean[propertyDetails.size()]; + for(int i=0; i property = propertyDetails.get(i); + if(property == null || property.getFirst() == null) + { + headings[i] = ""; + required[i] = false; + } + else + { + QName column = property.getFirst(); + required[i] = property.getSecond(); + + // Ask the dictionary service nicely for the details + PropertyDefinition pd = dictionaryService.getProperty(column); + if(pd != null && pd.getTitle() != null) + { + // Use the friendly titles, which may even be localised! + headings[i] = pd.getTitle(); + descriptions[i] = pd.getDescription(); + } + else + { + // Nothing friendly found, try to munge the raw qname into + // something we can show to a user... + String raw = column.getLocalName(); + raw = raw.substring(0, 1).toUpperCase() + raw.substring(1); + + Matcher m = qnameMunger.matcher(raw); + if(m.matches()) + { + headings[i] = m.group(1) + " " + m.group(2); + } + else + { + headings[i] = raw; + } + } + } + } + + // Build a list of just the properties + List properties = new ArrayList(propertyDetails.size()); + for(Pair p : propertyDetails) + { + QName qn = null; + if(p != null) + { + qn = p.getFirst(); + } + properties.add(qn); + } + + + // Output + if("csv".equals(format)) + { + StringWriter sw = new StringWriter(); + CSVPrinter csv = new CSVPrinter(sw, CSVStrategy.EXCEL_STRATEGY); + csv.println(headings); + + populateBody(resource, csv, properties); + + model.put(MODEL_CSV, sw.toString()); + } + else + { + Workbook wb; + if("xlsx".equals(format)) + { + wb = new XSSFWorkbook(); + // TODO Properties + } + else + { + wb = new HSSFWorkbook(); + // TODO Properties + } + + // Add our header row + Sheet sheet = wb.createSheet("Export"); + Row hr = sheet.createRow(0); + sheet.createFreezePane(0, 1); + + Font fb = wb.createFont(); + fb.setBoldweight(Font.BOLDWEIGHT_BOLD); + Font fi = wb.createFont(); + fi.setBoldweight(Font.BOLDWEIGHT_BOLD); + fi.setItalic(true); + + CellStyle csReq = wb.createCellStyle(); + csReq.setFont(fb); + CellStyle csOpt = wb.createCellStyle(); + csOpt.setFont(fi); + + // Populate the header + Drawing draw = null; + for(int i=0; i 0) + { + // Add a description for it too + if(draw == null) + { + draw = sheet.createDrawingPatriarch(); + } + ClientAnchor ca = wb.getCreationHelper().createClientAnchor(); + ca.setCol1(c.getColumnIndex()); + ca.setCol2(c.getColumnIndex()+1); + ca.setRow1(hr.getRowNum()); + ca.setRow2(hr.getRowNum()+2); + + Comment cmt = draw.createCellComment(ca); + cmt.setAuthor(""); + cmt.setString(wb.getCreationHelper().createRichTextString(descriptions[i])); + cmt.setVisible(false); + c.setCellComment(cmt); + } + } + + // Have the contents populated + populateBody(resource, wb, sheet, properties); + + // Save it for the template + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + wb.write(baos); + model.put(MODEL_EXCEL, baos.toByteArray()); + } + } + + @Override + protected Map createTemplateParameters(WebScriptRequest req, WebScriptResponse res, + Map customParams) + { + Map model = super.createTemplateParameters(req, res, customParams); + // We sometimes need to monkey around to do the binary output... + model.put("req", req); + model.put("res", res); + model.put("writeExcel", new WriteExcel(res, model, req.getFormat(), filenameBase)); + return model; + } + + public static class WriteExcel + { + private String format; + private String filenameBase; + private WebScriptResponse res; + private Map model; + private WriteExcel(WebScriptResponse res, Map model, String format, String filenameBase) + { + this.res = res; + this.model = model; + this.format = format; + } + public void write() throws IOException + { + String filename = filenameBase + "." + format; + + // If it isn't a CSV, reset so we can send binary + if(! "csv".equals(format)) + { + res.reset(); + } + + // Tell the browser it's a file download + res.addHeader("Content-Disposition", "attachment; filename=" + filename); + + // Now send that data + if("csv".equals(format)) + { + res.getWriter().append((String)model.get(MODEL_CSV)); + } + else + { + // Set the mimetype, as we've reset + if("xlsx".equals(format)) + { + res.setContentType(MimetypeMap.MIMETYPE_OPENXML_SPREADSHEET); + } else { + res.setContentType(MimetypeMap.MIMETYPE_EXCEL); + } + + // Send the raw excel bytes + byte[] excel = (byte[])model.get(MODEL_EXCEL); + res.getOutputStream().write(excel); + } + } + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/datalist/DataListDownloadWebScript.java b/source/java/org/alfresco/repo/web/scripts/datalist/DataListDownloadWebScript.java new file mode 100644 index 0000000000..99a72b9827 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/datalist/DataListDownloadWebScript.java @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.datalist; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.web.scripts.DeclarativeSpreadsheetWebScript; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.namespace.InvalidQNameException; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + + +/** + * Data List Download + * + * Exports the contents of a Data List as an Excel file + * + * @author Nick Burch + */ +public class DataListDownloadWebScript extends DeclarativeSpreadsheetWebScript + implements InitializingBean +{ + // Logger + private static final Log logger = LogFactory.getLog(DataListDownloadWebScript.class); + + private static final QName DATA_LIST_ITEM_TYPE = + QName.createQName(NamespaceService.DATALIST_MODEL_1_0_URI, "dataListItemType"); + + private NodeService nodeService; + private SiteService siteService; + private NamespaceService namespaceService; + private Map> modelOrder; + private Map rawModelOrder; + + public DataListDownloadWebScript() + { + this.filenameBase = "DataListExport"; + } + + /** + * @param nodeService + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param nodeService + */ + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + /** + * @param namespaceService + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public void setModelOrder(Map rawModelOrder) + { + this.rawModelOrder = rawModelOrder; + } + + + @Override + public void afterPropertiesSet() throws Exception { + modelOrder = new HashMap>(); + for(String key : rawModelOrder.keySet()) + { + QName model; + List order = new ArrayList(); + + try + { + model= QName.createQName(key, namespaceService); + } + catch(InvalidQNameException e) + { + logger.warn("Skipping invalid model type " + key); + continue; + } + + StringTokenizer st = new StringTokenizer(rawModelOrder.get(key), ","); + while(st.hasMoreTokens()) + { + order.add( QName.createQName(st.nextToken(), namespaceService) ); + } + modelOrder.put(model, order); + } + } + + /** + * Identify the datalist + */ + @Override + protected Object identifyResource(String format, WebScriptRequest req) { + // Try to find the datalist they requested + NodeRef list; + Map args = req.getServiceMatch().getTemplateVars(); + if(args.get("store_type") != null) + { + list = new NodeRef( + args.get("store_type"), + args.get("store_id"), + args.get("id") + ); + } + else + { + // Get the site + SiteInfo site = siteService.getSite(args.get("site")); + if(site == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Site not found with supplied name"); + } + + // Now find the data list container with in + NodeRef container = nodeService.getChildByName( + site.getNodeRef(), + ContentModel.ASSOC_CONTAINS, + args.get("container") + ); + if(container == null) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "Container not found within site"); + } + + // Now get the data list itself + list = nodeService.getChildByName( + container, + ContentModel.ASSOC_CONTAINS, + args.get("list") + ); + } + if(list == null || !nodeService.exists(list)) + { + throw new WebScriptException(Status.STATUS_NOT_FOUND, "The Data List could not be found"); + } + + return list; + } + + /** + * We don't have a HTML version + */ + @Override + protected boolean allowHtmlFallback() { + return false; + } + + /** + * Fetch the properties, in the requested order, from + * the data list definition + */ + @Override + protected List> buildPropertiesForHeader( + Object resource, String format, WebScriptRequest req) { + NodeRef list = (NodeRef)resource; + QName type = buildType(list); + + // Has the user given us rules for what to do + // with this type? + List props; + if(modelOrder.containsKey(type)) + { + props = modelOrder.get(type); + } + else + { + // We'll have to try to guess it for them + // For now, just use DataList properties for the type + TypeDefinition typeDef = dictionaryService.getType(type); + Map allProps = typeDef.getProperties(); + props = new ArrayList(); + + for(QName prop : allProps.keySet()) + { + if(NamespaceService.DATALIST_MODEL_1_0_URI.equals(prop.getNamespaceURI())) + { + props.add(prop); + } + } + } + + // Everything is required + List> properties = new ArrayList>(); + for(QName qname : props) + { + properties.add(new Pair(qname, true)); + } + return properties; + } + + private QName buildType(NodeRef list) + { + String typeS = (String)nodeService.getProperty(list, DATA_LIST_ITEM_TYPE); + if(! typeS.startsWith(NamespaceService.DATALIST_MODEL_PREFIX + ":")) + { + throw new WebScriptException(Status.STATUS_NOT_IMPLEMENTED, "Unexpected list type " + typeS); + } + QName type = QName.createQName(NamespaceService.DATALIST_MODEL_1_0_URI, typeS.substring(typeS.indexOf(':')+1)); + return type; + } + + private List getItems(NodeRef list) + { + Set typeSet = new HashSet(Arrays.asList(new QName[] { buildType(list) })); + + List items = new ArrayList(); + for(ChildAssociationRef ca : nodeService.getChildAssocs(list, typeSet)) + { + items.add(ca.getChildRef()); + } + return items; + } + + @Override + protected void populateBody(Object resource, CSVPrinter csv, + List properties) throws IOException { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "CSV not currently supported"); + } + + @Override + protected void populateBody(Object resource, Workbook workbook, + Sheet sheet, List properties) throws IOException { + NodeRef list = (NodeRef)resource; + List items = getItems(list); + + // Our various formats + DataFormat formatter = workbook.createDataFormat(); + + CellStyle styleInt = workbook.createCellStyle(); + styleInt.setDataFormat( formatter.getFormat("0") ); + CellStyle styleDate = workbook.createCellStyle(); + styleDate.setDataFormat( formatter.getFormat("yyyy-mm-dd") ); + CellStyle styleDouble = workbook.createCellStyle(); + styleDouble.setDataFormat( formatter.getFormat("General") ); + CellStyle styleNewLines = workbook.createCellStyle(); + styleNewLines.setWrapText(true); + + // Export the items + int rowNum = 1, colNum = 0; + for(NodeRef item : items) + { + Row r = sheet.createRow(rowNum); + + colNum = 0; + for(QName prop : properties) + { + Cell c = r.createCell(colNum); + + Serializable val = nodeService.getProperty(item, prop); + if(val == null) + { + // Is it an association, or just missing? + List assocs = nodeService.getTargetAssocs(item, prop); + if(assocs.size() > 0) + { + StringBuffer text = new StringBuffer(); + int lines = 1; + + for(AssociationRef ref : assocs) + { + NodeRef child = ref.getTargetRef(); + QName type = nodeService.getType(child); + if(ContentModel.TYPE_PERSON.equals(type)) + { + if(text.length() > 0) { + text.append('\n'); + lines++; + } + text.append(nodeService.getProperty( + child, ContentModel.PROP_USERNAME + )); + } + else if(ContentModel.TYPE_CONTENT.equals(type)) + { + // TODO Link to the content + if(text.length() > 0) { + text.append('\n'); + lines++; + } + text.append(nodeService.getProperty( + child, ContentModel.PROP_TITLE + )); + } + else + { + System.err.println("TODO: handle " + type + " for " + child); + } + } + + String v = text.toString(); + c.setCellValue( v ); + if(lines > 1) + { + c.setCellStyle(styleNewLines); + r.setHeightInPoints( lines*sheet.getDefaultRowHeightInPoints() ); + } + } + else + { + // This property isn't set + c.setCellType(Cell.CELL_TYPE_BLANK); + } + } + else + { + // Regular property, set + if(val instanceof String) + { + c.setCellValue((String)val); + } + else if(val instanceof Date) + { + c.setCellValue((Date)val); + c.setCellStyle(styleDate); + } + else if(val instanceof Integer || val instanceof Long) + { + double v = 0.0; + if(val instanceof Long) v = (double)(Long)val; + if(val instanceof Integer) v = (double)(Integer)val; + c.setCellValue(v); + c.setCellStyle(styleInt); + } + else if(val instanceof Float || val instanceof Double) + { + double v = 0.0; + if(val instanceof Float) v = (double)(Float)val; + if(val instanceof Double) v = (double)(Double)val; + c.setCellValue(v); + c.setCellStyle(styleDouble); + } + else + { + // TODO + System.err.println("TODO: handle " + val.getClass().getName() + " - " + val); + } + } + + colNum++; + } + + rowNum++; + } + + // Sensible column widths please! + colNum = 0; + for(QName prop : properties) + { + sheet.autoSizeColumn(colNum); + colNum++; + } + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/person/UserCSVUploadGet.java b/source/java/org/alfresco/repo/web/scripts/person/UserCSVUploadGet.java index 1ae0284ba3..a11e62728c 100644 --- a/source/java/org/alfresco/repo/web/scripts/person/UserCSVUploadGet.java +++ b/source/java/org/alfresco/repo/web/scripts/person/UserCSVUploadGet.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,36 +18,17 @@ */ package org.alfresco.repo.web.scripts.person; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.StringWriter; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.ArrayList; +import java.util.List; -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.repo.web.scripts.DeclarativeSpreadsheetWebScript; import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; import org.apache.commons.csv.CSVPrinter; -import org.apache.commons.csv.CSVStrategy; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.Comment; -import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.Font; -import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.springframework.extensions.webscripts.DeclarativeWebScript; -import org.springframework.extensions.webscripts.Status; -import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; /** @@ -55,247 +36,65 @@ import org.springframework.extensions.webscripts.WebScriptResponse; * users via a CSV. * * @author Nick Burch - * @since 3.5 */ -public class UserCSVUploadGet extends DeclarativeWebScript +public class UserCSVUploadGet extends DeclarativeSpreadsheetWebScript { - public static final String MODEL_CSV = "csv"; - public static final String MODEL_EXCEL = "excel"; - - private DictionaryService dictionaryService; - - /** - * @param dictionaryService the DictionaryService to set - */ - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - /** - * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.Status) - */ - @Override - protected Map executeImpl(WebScriptRequest req, Status status) - { - Map model = new HashMap(); - model.put("success", Boolean.TRUE); - - // What format are they after? - String format = req.getFormat(); - if ("csv".equals(format) || "xls".equals(format) || - "xlsx".equals(format) || "excel".equals(format)) - { - try - { - generateTemplateFile(format, req, status, model); - return model; - } - catch (IOException e) - { - throw new WebScriptException(Status.STATUS_BAD_REQUEST, - "Unable to generate template file", e); - } - } - - // Give them the help and the upload form - return model; - } - - /** - * Generates a template file to help the user figure out what to - * put into their CSV - */ - private void generateTemplateFile(String format, WebScriptRequest req, Status status, Map model) - throws IOException - { - Pattern p = Pattern.compile("([A-Z][a-z]+)([A-Z].*)"); - - String[] headings = new String[UserCSVUploadPost.COLUMNS.length]; - String[] descriptions = new String[UserCSVUploadPost.COLUMNS.length]; - for (int i=0; i 0) - { - // Add a description for it too - if (draw == null) - { - draw = sheet.createDrawingPatriarch(); - } - ClientAnchor ca = wb.getCreationHelper().createClientAnchor(); - ca.setCol1(c.getColumnIndex()); - ca.setCol2(c.getColumnIndex()+1); - ca.setRow1(hr.getRowNum()); - ca.setRow2(hr.getRowNum()+2); - - Comment cmt = draw.createCellComment(ca); - cmt.setAuthor(""); - cmt.setString(wb.getCreationHelper().createRichTextString(descriptions[i])); - cmt.setVisible(false); - c.setCellComment(cmt); - } - } - - // Add an empty data row - sheet.createRow(1); - - // Save it for the template - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - wb.write(baos); - model.put(MODEL_EXCEL, baos.toByteArray()); - } - } + public UserCSVUploadGet() + { + this.filenameBase = "ExampleUserUpload"; + } + /** + * We have a HTML version + */ @Override - protected Map createTemplateParameters(WebScriptRequest req, WebScriptResponse res, - Map customParams) - { - Map model = super.createTemplateParameters(req, res, customParams); - // We sometimes need to monkey around to do the binary output... - model.put("req", req); - model.put("res", res); - model.put("writeExcel", new WriteExcel(res, model, req.getFormat())); - return model; - } - - public static class WriteExcel - { - private String format; - private WebScriptResponse res; - private Map model; - - private WriteExcel(WebScriptResponse res, Map model, String format) - { - this.res = res; - this.model = model; - this.format = format; - } - - public void write() throws IOException - { - String filename = "ExampleUserUpload." + format; - - // If it isn't a CSV, reset so we can send binary - if (!"csv".equals(format)) - { - res.reset(); - } - - // Tell the browser it's a file download - res.addHeader("Content-Disposition", "attachment; filename=" + filename); - - // Now send that data - if ("csv".equals(format)) - { - res.getWriter().append((String)model.get(MODEL_CSV)); - } - else - { - // Set the mimetype, as we've reset - if ("xlsx".equals(format)) - { - res.setContentType(MimetypeMap.MIMETYPE_OPENXML_SPREADSHEET); - } - else - { - res.setContentType(MimetypeMap.MIMETYPE_EXCEL); - } - - // Send the raw excel bytes - byte[] excel = (byte[])model.get(MODEL_EXCEL); - res.getOutputStream().write(excel); - } - } - } -} \ No newline at end of file + protected boolean allowHtmlFallback() { + return true; + } + + /** + * We don't have a resource + */ + @Override + protected Object identifyResource(String format, WebScriptRequest req) { + return null; + } + + @Override + protected List> buildPropertiesForHeader( + Object resource, String format, WebScriptRequest req) { + List> properties = + new ArrayList>(UserCSVUploadPost.COLUMNS.length); + boolean required = true; + for(QName qname : UserCSVUploadPost.COLUMNS) + { + Pair p = null; + if(qname != null) + { + p = new Pair(qname, required); + } + else + { + required = false; + } + properties.add(p); + } + return properties; + } + + @Override + protected void populateBody(Object resource, CSVPrinter csv, List properties) throws IOException { + // Just a blank line is needed + csv.println(); + } + + @Override + protected void populateBody(Object resource, Workbook workbook, Sheet sheet, + List properties) throws IOException { + // Set the sheet name + workbook.setSheetName(0, "UsersToAdd"); + + // Add a blank line + sheet.createRow(1); + } +}