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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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);
+ }
+}