diff --git a/org.adempiere.base/META-INF/MANIFEST.MF b/org.adempiere.base/META-INF/MANIFEST.MF index b9bc48cda9..bb31d19ed9 100644 --- a/org.adempiere.base/META-INF/MANIFEST.MF +++ b/org.adempiere.base/META-INF/MANIFEST.MF @@ -66,6 +66,7 @@ Export-Package: bsh, org.idempiere.fa.service.api, org.idempiere.model, org.idempiere.print, + org.idempiere.print.renderer, org.idempiere.process Bundle-RequiredExecutionEnvironment: JavaSE-17 Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version>=17))" diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.CSVReportRenderer.xml b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.CSVReportRenderer.xml new file mode 100644 index 0000000000..c1646c7587 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.CSVReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.HTMLReportRenderer.xml b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.HTMLReportRenderer.xml new file mode 100644 index 0000000000..2b1cf24625 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.HTMLReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.PDFReportRenderer.xml b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.PDFReportRenderer.xml new file mode 100644 index 0000000000..5bfbed579e --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.PDFReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.PSReportRenderer.xml b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.PSReportRenderer.xml new file mode 100644 index 0000000000..abd191ce40 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.PSReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.SSVReportRenderer.xml b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.SSVReportRenderer.xml new file mode 100644 index 0000000000..6f843a055d --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.SSVReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.TabDelimitedReportRenderer.xml b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.TabDelimitedReportRenderer.xml new file mode 100644 index 0000000000..82794b497b --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.TabDelimitedReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.XLSReportRenderer.xml b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.XLSReportRenderer.xml new file mode 100644 index 0000000000..02dea96ca9 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.XLSReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.XLSXReportRenderer.xml b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.XLSXReportRenderer.xml new file mode 100644 index 0000000000..799d17c5f1 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.XLSXReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.XMLReportRenderer.xml b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.XMLReportRenderer.xml new file mode 100644 index 0000000000..aa185f1640 --- /dev/null +++ b/org.adempiere.base/OSGI-INF/org.idempiere.print.renderer.XMLReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.base/src/org/adempiere/base/Core.java b/org.adempiere.base/src/org/adempiere/base/Core.java index 96781f3dd3..b15eee40b4 100644 --- a/org.adempiere.base/src/org/adempiere/base/Core.java +++ b/org.adempiere.base/src/org/adempiere/base/Core.java @@ -67,6 +67,8 @@ import org.idempiere.fa.service.api.IDepreciationMethod; import org.idempiere.fa.service.api.IDepreciationMethodFactory; import org.idempiere.model.IMappedModelFactory; import org.idempiere.print.IPrintHeaderFooter; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; import org.idempiere.process.IMappedProcessFactory; /** @@ -1126,4 +1128,18 @@ public class Core { return myCreditManager; } // getCreditManager + + @SuppressWarnings("unchecked") + public static IReportRenderer getReportRenderer(String id) { + IReportRenderer renderer = null; + @SuppressWarnings("rawtypes") + List> rendererReferences = Service.locator().list(IReportRenderer.class).getServiceReferences(); + for(var holder : rendererReferences) { + renderer = holder.getService(); + if (renderer.getId().equals(id)) { + return renderer; + } + } + return null; + } } diff --git a/org.adempiere.base/src/org/adempiere/pdf/Document.java b/org.adempiere.base/src/org/adempiere/pdf/Document.java index d0453178ac..800d8e187a 100644 --- a/org.adempiere.base/src/org/adempiere/pdf/Document.java +++ b/org.adempiere.base/src/org/adempiere/pdf/Document.java @@ -47,7 +47,7 @@ public class Document { * @param pageable * @param output */ - private static void writePDF(Pageable pageable, OutputStream output) + public static void writePDF(Pageable pageable, OutputStream output) { try { final PageFormat pf = pageable.getPageFormat(0); diff --git a/org.adempiere.base/src/org/compiere/print/ReportEngine.java b/org.adempiere.base/src/org/compiere/print/ReportEngine.java index 6a814efa68..dd251fcecf 100644 --- a/org.adempiere.base/src/org/compiere/print/ReportEngine.java +++ b/org.adempiere.base/src/org/compiere/print/ReportEngine.java @@ -19,62 +19,34 @@ package org.compiere.print; import static org.compiere.model.SystemIDs.PROCESS_RPT_M_INVENTORY; import static org.compiere.model.SystemIDs.PROCESS_RPT_M_MOVEMENT; -import java.awt.Color; -import java.awt.Font; import java.awt.print.PrinterJob; import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.PrintWriter; import java.io.Writer; import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; import java.util.Properties; import java.util.logging.Level; -import javax.print.DocFlavor; -import javax.print.StreamPrintService; -import javax.print.StreamPrintServiceFactory; -import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.standard.Copies; import javax.print.attribute.standard.JobName; import javax.print.event.PrintServiceAttributeEvent; import javax.print.event.PrintServiceAttributeListener; -import javax.xml.transform.stream.StreamResult; import org.adempiere.exceptions.AdempiereException; -import org.adempiere.pdf.Document; -import org.adempiere.print.export.PrintDataExcelExporter; -import org.adempiere.print.export.PrintDataXLSXExporter; -import org.apache.ecs.MultiPartElement; -import org.apache.ecs.XhtmlDocument; -import org.apache.ecs.xhtml.a; -import org.apache.ecs.xhtml.script; -import org.apache.ecs.xhtml.span; -import org.apache.ecs.xhtml.style; -import org.apache.ecs.xhtml.table; -import org.apache.ecs.xhtml.tbody; -import org.apache.ecs.xhtml.td; -import org.apache.ecs.xhtml.th; -import org.apache.ecs.xhtml.thead; -import org.apache.ecs.xhtml.tr; import org.compiere.model.MClient; import org.compiere.model.MColumn; import org.compiere.model.MDunningRunEntry; @@ -90,15 +62,10 @@ import org.compiere.model.MProcess; import org.compiere.model.MProject; import org.compiere.model.MQuery; import org.compiere.model.MRfQResponse; -import org.compiere.model.MRole; -import org.compiere.model.MStyle; import org.compiere.model.MSysConfig; import org.compiere.model.MTable; import org.compiere.model.PO; import org.compiere.model.PrintInfo; -import org.compiere.model.X_AD_StyleLine; -import org.compiere.print.layout.InstanceAttributeColumn; -import org.compiere.print.layout.InstanceAttributeData; import org.compiere.print.layout.LayoutEngine; import org.compiere.print.layout.PrintDataEvaluatee; import org.compiere.process.ProcessInfo; @@ -107,18 +74,32 @@ import org.compiere.process.ServerProcessCtl; import org.compiere.tools.FileUtil; import org.compiere.util.CLogger; import org.compiere.util.DB; -import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Evaluator; import org.compiere.util.Ini; import org.compiere.util.Language; -import org.compiere.util.Msg; import org.compiere.util.Trx; import org.compiere.util.Util; import org.eevolution.model.MDDOrder; import org.eevolution.model.X_PP_Order; - -import com.googlecode.htmlcompressor.compressor.HtmlCompressor; +import org.idempiere.print.renderer.CSVReportRenderer; +import org.idempiere.print.renderer.CSVReportRendererConfiguration; +import org.idempiere.print.renderer.HTMLReportRenderer; +import org.idempiere.print.renderer.HTMLReportRendererConfiguration; +import org.idempiere.print.renderer.PDFReportRenderer; +import org.idempiere.print.renderer.PDFReportRendererConfiguration; +import org.idempiere.print.renderer.PSReportRenderer; +import org.idempiere.print.renderer.PSReportRendererConfiguration; +import org.idempiere.print.renderer.SSVReportRenderer; +import org.idempiere.print.renderer.SSVReportRendererConfiguration; +import org.idempiere.print.renderer.TabDelimitedReportRenderer; +import org.idempiere.print.renderer.TabDelimitedReportRendererConfiguration; +import org.idempiere.print.renderer.XLSReportRenderer; +import org.idempiere.print.renderer.XLSReportRendererConfiguration; +import org.idempiere.print.renderer.XLSXReportRenderer; +import org.idempiere.print.renderer.XLSXReportRendererConfiguration; +import org.idempiere.print.renderer.XMLReportRenderer; +import org.idempiere.print.renderer.XMLReportRendererConfiguration; /** * Report Engine. @@ -271,12 +252,6 @@ public class ReportEngine implements PrintServiceAttributeListener private boolean m_isReplaceTabContent = false; - /** - * store all column has same css rule into a list - * for IDEMPIERE-2640 - */ - private Map> mapCssInfo = new HashMap>(); - private List eventListeners = new ArrayList(); public void addEventListener(IReportEngineEventListener listener) @@ -614,7 +589,7 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) return m_printerName; } // getPrinterName - /************************************************************************** + /** * Create HTML File * @param file file * @param onlyTable if false create complete HTML document @@ -626,7 +601,7 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) return createHTML(file, onlyTable, language, null); } - /************************************************************************** + /** * Create HTML File * @param file file * @param onlyTable if false create complete HTML document @@ -691,487 +666,13 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) */ public boolean createHTML (Writer writer, boolean onlyTable, Language language, IHTMLExtension extension, boolean isExport) { - try - { - //collect column to print - List columns = new ArrayList<>(); - List asiElements = new ArrayList<>(); - int columnCount = 0; - for (int col = 0; col < m_printFormat.getItemCount(); col++) - { - MPrintFormatItem item = m_printFormat.getItem(col); - if (item.isPrinted()) - { - if (item.isTypeField() && item.isPrintInstanceAttributes()) - { - InstanceAttributeData asiElement = new InstanceAttributeData(item, columnCount); - asiElement.readAttributesData(m_printData); - asiElements.add(asiElement); - continue; - } - else - { - columns.add(item); - columnCount++; - } - } - } - if (asiElements.size() > 0) - { - int columnCreated = 0; - for(InstanceAttributeData data : asiElements) - { - List instanceColumns = data.getColumns(); - int index = data.getColumnIndex() + columnCreated; - for(InstanceAttributeColumn c : instanceColumns) - { - columns.add(index, c); - index++; - columnCreated++; - } - } - } - - String cssPrefix = extension != null ? extension.getClassPrefix() : null; - if (cssPrefix != null && cssPrefix.trim().length() == 0) - cssPrefix = null; - - table parameterTable = null; - if (!m_printFormat.isForm()) { - if (m_query != null && m_query.isActive()) { - int rows = m_query.getReportProcessQuery() != null ? m_query.getReportProcessQuery().getRestrictionCount() : m_query.getRestrictionCount(); - if (rows > 0) { - parameterTable = new table(); - if (cssPrefix != null) - parameterTable.setClass(cssPrefix + "-parameter-table"); - else - parameterTable.setClass("parameter-table"); - parameterTable.setNeedClosingTag(false); - } - } - } - - table table = new table(); - if (cssPrefix != null) - table.setClass(cssPrefix + "-table"); - // - // - table.setNeedClosingTag(false); - PrintWriter w = new PrintWriter(writer); - XhtmlDocument doc = null; - boolean minify = MSysConfig.getBooleanValue(MSysConfig.HTML_REPORT_MINIFY, true, Env.getAD_Client_ID(getCtx())); - - if (onlyTable) - w.print(compress(table.toString(), minify)); - else - { - doc = new XhtmlDocument(); - doc.getHtml().setNeedClosingTag(false); - doc.getBody().setNeedClosingTag(false); - doc.appendHead(""); - - if (extension != null && !Util.isEmpty(extension.getWebFontLinks(), true)) - { - doc.appendHead(extension.getWebFontLinks()); - } - - if (extension != null && extension.getStyleURL() != null) - { - // maybe cache style content with key is path - String pathStyleFile = extension.getFullPathStyle(); // creates a temp file - delete below - Path path = Paths.get(pathStyleFile); - List styleLines = Files.readAllLines(path, Ini.getCharset()); - Files.delete(path); // delete temp file - StringBuilder styleBuild = new StringBuilder(); - for (String styleLine : styleLines){ - styleBuild.append(styleLine); //.append("\n"); - } - appendInlineCss (doc, styleBuild); - } - if (extension != null && extension.getScriptURL() != null && !isExport) - { - script jslink = new script(); - jslink.setLanguage("javascript"); - jslink.setSrc(extension.getScriptURL()); - doc.appendHead(jslink); - } - - if (extension != null && !isExport){ - extension.setWebAttribute(doc.getBody()); - } - } - - if (doc != null) - { - //IDEMPIERE-4113 - mapCssInfo.clear(); - MPrintFormatItem item = null; - int printColIndex = -1; - for(int col = 0; col < columns.size(); col++) - { - Object colobj = columns.get(col); - if (colobj instanceof MPrintFormatItem) - item = (MPrintFormatItem) colobj; - else if (colobj instanceof InstanceAttributeColumn) - item = ((InstanceAttributeColumn) colobj).getPrintFormatItem(); - if(item != null) - { - printColIndex++; - addCssInfo(item, printColIndex); - } - }//IDEMPIERE-4113 - appendInlineCss(doc); - - StringBuilder styleBuild = new StringBuilder(); - MPrintTableFormat tf = m_printFormat.getTableFormat(); - CSSInfo cssInfo = new CSSInfo(tf.getPageHeader_Font(), tf.getPageHeaderFG_Color()); - if (cssPrefix != null) { - if (parameterTable != null) - styleBuild.append("."+cssPrefix + "-parameter-table th").append(cssInfo.getCssRule()); - styleBuild.append("."+cssPrefix + "-table th").append(cssInfo.getCssRule()); - } - else { - if (parameterTable != null) - styleBuild.append("parameter-table th").append(cssInfo.getCssRule()); - styleBuild.append("table th").append(cssInfo.getCssRule()); - } - - cssInfo = new CSSInfo(tf.getParameter_Font(), tf.getParameter_Color()); - styleBuild.append(".tr-parameter td").append(cssInfo.getCssRule()); - - cssInfo = new CSSInfo(tf.getFunct_Font(), tf.getFunctFG_Color()); - styleBuild.append(".tr-function td").append(cssInfo.getCssRule()); - - MPrintFont printFont = MPrintFont.get(m_printFormat.getAD_PrintFont_ID()); - Font base = printFont.getFont(); - Font newFont = new Font(base.getName(), Font.PLAIN, base.getSize()-1); - cssInfo = new CSSInfo(newFont, null); - styleBuild.append(".tr-level-1 td").append(cssInfo.getCssRule()); - - newFont = new Font(base.getName(), Font.PLAIN, base.getSize()-2); - cssInfo = new CSSInfo(newFont, null); - styleBuild.append(".tr-level-2 td").append(cssInfo.getCssRule()); - - styleBuild = new StringBuilder(styleBuild.toString().replaceAll(";", "!important;")); - appendInlineCss (doc, styleBuild); - - w.print(compress(doc.toString(), minify)); - - w.print("
"); - String paraWrapId = null; - if (parameterTable != null) { - paraWrapId = cssPrefix + "-para-table-wrap"; - w.print("
"); - w.print(""+Msg.getMsg(getCtx(), "Parameter")+""); - w.print(compress(parameterTable.toString(), minify)); - - tr tr = new tr(); - tr.setClass("tr-parameter"); - - MQuery query = m_query; - if (m_query.getReportProcessQuery() != null) - query = m_query.getReportProcessQuery(); - for (int r = 0; r < query.getRestrictionCount(); r++) - { - if (r > 0) { - tr = new tr(); - tr.setClass("tr-parameter"); - } - - td td = new td(); - tr.addElement(td); - td.addElement(query.getInfoName(r)); - - td = new td(); - tr.addElement(td); - td.addElement(query.getInfoOperator(r)); - - td = new td(); - tr.addElement(td); - td.addElement(query.getInfoDisplayAll(r)); - - w.print(compress(tr.toString(), minify)); - } - - w.print(""); - w.print("
"); - } - - StringBuilder tableWrapDiv = new StringBuilder(); - tableWrapDiv.append("
"); - - w.print(compress(tableWrapDiv.toString(), minify)); - w.print(compress(table.toString(), minify)); - } - - thead thead = new thead(); - tbody tbody = new tbody(); - tbody.setNeedClosingTag(false); - - Boolean [] colSuppressRepeats = m_layout == null || m_layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(m_printFormat):m_layout.colSuppressRepeats; - Object [] preValues = null; - if (colSuppressRepeats != null){ - preValues = new Object [colSuppressRepeats.length]; - } - - int printColIndex = -1; - HashMap suppressMap = new HashMap<>(); - - // for all rows (-1 = header row) - for (int row = -1; row < m_printData.getRowCount(); row++) - { - tr tr = new tr(); - if (row != -1) - { - m_printData.setRowIndex(row); - if (extension != null && !isExport) - { - extension.extendRowElement(tr, m_printData); - } - if (m_printData.isFunctionRow()) { - tr.setClass(cssPrefix + "-functionrow"); - } else if ( row < m_printData.getRowCount() && m_printData.isFunctionRow(row+1)) { - tr.setClass(cssPrefix + "-lastgrouprow"); - } - // add row to table body - //tbody.addElement(tr); - } else { - // add row to table header - thead.addElement(tr); - } - - printColIndex = -1; - // for all columns - for (int col = 0; col < columns.size(); col++) - { - Object colObj = columns.get(col); - MPrintFormatItem item = null; - InstanceAttributeColumn instanceAttributeColumn = null; - if (colObj instanceof MPrintFormatItem) - { - item = (MPrintFormatItem) colObj; - } - else if (colObj instanceof InstanceAttributeColumn) - { - instanceAttributeColumn = (InstanceAttributeColumn) colObj; - item = instanceAttributeColumn.getPrintFormatItem(); - } - if (item != null) - { - printColIndex++; - // header row - if (row == -1) - { - th th = new th(); - tr.addElement(th); - String columnHeader = instanceAttributeColumn != null ? instanceAttributeColumn.getName() : item.getPrintName(language); - th.addElement(Util.maskHTML(columnHeader)); - if (cssPrefix != null && instanceAttributeColumn == null) - { - MColumn column = MColumn.get(getCtx(), item.getAD_Column_ID()); - if (column != null && column.getAD_Column_ID() > 0) - { - if (DisplayType.isNumeric(column.getAD_Reference_ID())) - { - th.setClass(cssPrefix + "-number"); - } - } - } - if (item.isSuppressNull()) - { - suppressMap.put(printColIndex, th); - th.setID("report-th-"+printColIndex); - } - } - else - { - td td = new td(); - tr.addElement(td); - MStyle style = item.getAD_FieldStyle_ID() > 0 ? MStyle.get(Env.getCtx(), item.getAD_FieldStyle_ID()) : null; - Object obj = instanceAttributeColumn != null ? instanceAttributeColumn.getPrintDataElement(row) - : m_printData.getNodeByPrintFormatItemId(item.getAD_PrintFormatItem_ID()); - if (obj == null || !isDisplayPFItem(item)){ - td.addElement(" "); - if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ - preValues[printColIndex] = null; - } - if (item.isSuppressNull() && obj != null && suppressMap.containsKey(printColIndex)) - suppressMap.remove(printColIndex); - } - else if (obj instanceof PrintDataElement) - { - PrintDataElement pde = (PrintDataElement) obj; - String value = pde.getValueDisplay(language); // formatted - - if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ - if (value.equals(preValues[printColIndex])){ - td.addElement(" "); - continue; - }else{ - preValues[printColIndex] = value; - } - } - - if (item.isSuppressNull() && obj != null && suppressMap.containsKey(printColIndex)) - suppressMap.remove(printColIndex); - - if (pde.getColumnName().endsWith("_ID") && extension != null && !isExport) - { - boolean isZoom = false; - if (item.getColumnName().equals("Record_ID")) { - Object tablePDE = m_printData.getNode("AD_Table_ID"); - if (tablePDE != null && tablePDE instanceof PrintDataElement) { - int tableID = -1; - try { - tableID = Integer.parseInt(((PrintDataElement)tablePDE).getValueAsString()); - } catch (Exception e) { - tableID = -1; - } - if (tableID > 0) { - MTable mTable = MTable.get(getCtx(), tableID); - String tableName = mTable.getTableName(); - - value = getIdentifier(mTable, tableName, Integer.parseInt(value)); - - String foreignColumnName = tableName + "_ID"; - pde.setForeignColumnName(foreignColumnName); - isZoom = true; - } - } - } else { - isZoom = true; - } - if (isZoom) { - // check permission on the zoomed window - MTable mTable = MTable.get(getCtx(), pde.getForeignColumnName().substring(0, pde.getForeignColumnName().length()-3)); - int Record_ID = -1; - try { - Record_ID = Integer.parseInt(pde.getValueAsString()); - } catch (Exception e) { - Record_ID = -1; - } - Boolean canAccess = null; - if (Record_ID >= 0 && mTable != null) { - int AD_Window_ID = Env.getZoomWindowID(mTable.get_ID(), Record_ID); - canAccess = MRole.getDefault().getWindowAccess(AD_Window_ID); - } - if (canAccess == null) { - isZoom = false; - } - } - if (isZoom) { - //link for column - a href = new a("javascript:void(0)"); - href.setID(pde.getColumnName() + "_" + row + "_a"); - td.addElement(href); - href.addElement(Util.maskHTML(value)); - if (cssPrefix != null) - href.setClass(cssPrefix + "-href"); - // Set Style - if(style != null && style.isWrapWithSpan()) - setStyle(href, style); - else - setStyle(td, style); - extension.extendIDColumn(row, td, href, pde); - } else { - // Set Style - if(style != null && style.isWrapWithSpan()) { - span span = new span(); - setStyle(span, style); - span.addElement(Util.maskHTML(value)); - td.addElement(span); - } - else { - setStyle(td, style); - td.addElement(Util.maskHTML(value)); - } - } - - } - else - { - // Set Style - if(style != null && style.isWrapWithSpan()) { - span span = new span(); - setStyle(span, style); - span.addElement(Util.maskHTML(value)); - td.addElement(span); - } - else { - setStyle(td, style); - td.addElement(Util.maskHTML(value)); - } - } - if (cssPrefix != null) - { - if (DisplayType.isNumeric(pde.getDisplayType())) - td.setClass(cssPrefix + "-number"); - else if (DisplayType.isDate(pde.getDisplayType())) - td.setClass(cssPrefix + "-date"); - else - td.setClass(cssPrefix + "-text"); - } - } - else if (obj instanceof PrintData) - { - // ignore contained Data - } - else - log.log(Level.SEVERE, "Element not PrintData(Element) " + obj.getClass()); - } - } // printed - } // for all columns - - /* output table header */ - if (row == -1){ - w.print(compress(thead.toString(), minify)); - // output open of tbody - w.print(compress(tbody.toString(), minify)); - }else{ - // output row by row - w.print(compress(tr.toString(), minify)); - } - - } // for all rows - - w.print(""); - w.print(""); - if (suppressMap.size() > 0) - { - StringBuilder st = new StringBuilder(); - for(th t : suppressMap.values()) - { - if (st.length() > 0) - st.append(","); - st.append("#").append(t.getAttribute("id")); - } - st.append(" {\n\t\tdisplay:none;\n\t}"); - style styleTag = new style(); - styleTag.addElement(st.toString()); - w.print(compress(styleTag.toString(), minify)); - } - if (!onlyTable) - { - w.print("
"); - w.print("
"); - w.print(""); - w.print(""); - } - w.flush(); - w.close(); - } - catch (Exception e) - { - log.log(Level.SEVERE, "(w)", e); - throw new AdempiereException(e); - } + HTMLReportRendererConfiguration config = new HTMLReportRendererConfiguration() + .setOutputWriter(writer) + .setOnlyTable(onlyTable) + .setExport(isExport) + .setExtension(extension) + .setLanguage(language); + new HTMLReportRenderer().renderReport(this, config); return true; } // createHTML @@ -1182,7 +683,7 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) * @param recordID * @return String identifier */ - private String getIdentifier(MTable mTable, String tableName, int recordID) { + public String getIdentifier(MTable mTable, String tableName, int recordID) { ArrayList list = new ArrayList(); // get translation table - null if not exists MTable mTableTrl = MTable.get(getCtx(), tableName+"_Trl"); @@ -1259,22 +760,8 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) return tableName; } // addTrlSuffix - private String getCSSFontFamily(String fontFamily) { - if ("Dialog".equals(fontFamily) || "DialogInput".equals(fontFamily) || "Monospaced".equals(fontFamily)) - { - return "monospace"; - } else if ("SansSerif".equals(fontFamily)) - { - return "sans-serif"; - } else if ("Serif".equals(fontFamily)) - { - return "serif"; - } - return null; - } - - /************************************************************************** - * Create CSV File + /** + * Create delimited text file * @param file file * @param delimiter delimiter, e.g. comma, tab * @param language translation language @@ -1299,7 +786,7 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) } // createCSV /** - * Write CSV to writer + * Write delimited content to writer * @param writer writer * @param delimiter delimiter, e.g. comma, tab * @param language translation language @@ -1307,200 +794,35 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) */ public boolean createCSV (Writer writer, char delimiter, Language language) { - if (delimiter == 0) - delimiter = '\t'; - try - { - //collect columns to be printed - ArrayList columns = new ArrayList<>(); - List asiElements = new ArrayList<>(); - int columnCount = 0; - for (int col = 0; col < m_printFormat.getItemCount(); col++) - { - MPrintFormatItem item = m_printFormat.getItem(col); - if (item.isPrinted()) - { - if (item.isTypeField() && item.isPrintInstanceAttributes()) - { - InstanceAttributeData asiElement = new InstanceAttributeData(item, columnCount); - asiElement.readAttributesData(m_printData); - asiElements.add(asiElement); - continue; - } - else - { - columns.add(item); - columnCount++; - } - } + switch (delimiter) { + case ',' -> { + CSVReportRendererConfiguration config = new CSVReportRendererConfiguration().setLanguage(language).setOutputWriter(writer); + new CSVReportRenderer().renderReport(this, config); } - if (asiElements.size() > 0) - { - int columnCreated = 0; - for(InstanceAttributeData data : asiElements) - { - List instanceColumns = data.getColumns(); - int index = data.getColumnIndex() + columnCreated; - for(InstanceAttributeColumn c : instanceColumns) - { - columns.add(index, c); - index++; - columnCreated++; - } - } + case ';' -> { + SSVReportRendererConfiguration config = new SSVReportRendererConfiguration().setLanguage(language).setOutputWriter(writer); + new SSVReportRenderer().renderReport(this, config); } - - Boolean [] colSuppressRepeats = m_layout == null || m_layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(m_printFormat):m_layout.colSuppressRepeats; - Object [] preValues = null; - if (colSuppressRepeats != null){ - preValues = new Object [colSuppressRepeats.length]; + case '\t' -> { + TabDelimitedReportRendererConfiguration config = new TabDelimitedReportRendererConfiguration().setLanguage(language).setOutputWriter(writer); + new TabDelimitedReportRenderer().renderReport(this, config); } - int printColIndex = -1; - // for all rows (-1 = header row) - for (int row = -1; row < m_printData.getRowCount(); row++) - { - printColIndex = -1; - StringBuffer sb = new StringBuffer(); - if (row != -1) - m_printData.setRowIndex(row); - - // for all columns - boolean first = true; // first column to print - for (int col = 0; col < columns.size(); col++) - { - Object colObj = columns.get(col); - MPrintFormatItem item = null; - InstanceAttributeColumn iaColumn = null; - if (colObj instanceof InstanceAttributeColumn) - { - iaColumn = (InstanceAttributeColumn) colObj; - item = iaColumn.getPrintFormatItem(); - } - else if (colObj instanceof MPrintFormatItem) - { - item = (MPrintFormatItem)colObj; - } - if (item != null) - { - // column delimiter (comma or tab) - if (first) - first = false; - else - sb.append(delimiter); - // header row - if (row == -1) - { - String printName = iaColumn != null ? iaColumn.getName() : item.getPrintName(language); - createCSVvalue (sb, delimiter, printName); - } - else - { - printColIndex++; - Object obj = iaColumn != null ? iaColumn.getPrintDataElement(row) : m_printData.getNodeByPrintFormatItemId(item.getAD_PrintFormatItem_ID()); - String data = ""; - if (obj == null || !isDisplayPFItem(item)){ - if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ - preValues[printColIndex] = null; - } - } - else if (obj instanceof PrintDataElement) - { - PrintDataElement pde = (PrintDataElement)obj; - if (pde.isPKey()) - data = pde.getValueAsString(); - else - data = pde.getValueDisplay(language); // formatted - - if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ - if (data.equals(preValues[printColIndex])){ - continue; - }else{ - preValues[printColIndex] = data; - } - } - } - else if (obj instanceof PrintData) - { - } - else - log.log(Level.SEVERE, "Element not PrintData(Element) " + obj.getClass()); - createCSVvalue (sb, delimiter, data); - } - } // printed - } // for all columns - writer.write(sb.toString()); - writer.write(Env.NL); - } // for all rows - // - writer.flush(); - writer.close(); - } - catch (Exception e) - { - log.log(Level.SEVERE, "(w)", e); - return false; + default -> throw new IllegalArgumentException("Unexpected value: " + delimiter); } + return true; } // createCSV /** - * Add Content to CSV string. - * Encapsulate/mask content in " if required - * @param sb StringBuffer to add to - * @param delimiter delimiter - * @param content column value - */ - private void createCSVvalue (StringBuffer sb, char delimiter, String content) - { - // nothing to add - if (content == null || content.length() == 0) - return; - // - boolean needMask = false; - StringBuilder buff = new StringBuilder(); - char chars[] = content.toCharArray(); - for (int i = 0; i < chars.length; i++) - { - char c = chars[i]; - if (c == '"') - { - needMask = true; - buff.append(c); // repeat twice - } // mask if any control character - else if (!needMask && (c == delimiter || !Character.isLetterOrDigit(c))) - needMask = true; - buff.append(c); - } - - // Optionally mask value - if (needMask) - sb.append('"').append(buff).append('"'); - else - sb.append(buff); - } // addCSVColumnValue - - - /************************************************************************** * Create XML File * @param file file * @return true if success */ public boolean createXML (File file) { - try - { - Writer fw = new OutputStreamWriter(new FileOutputStream(file, false), Ini.getCharset()); // teo_sarca: save using adempiere charset [ 1658127 ] - return createXML (new BufferedWriter(fw)); - } - catch (FileNotFoundException fnfe) - { - log.log(Level.SEVERE, "(f) - " + fnfe.toString()); - } - catch (Exception e) - { - log.log(Level.SEVERE, "(f)", e); - } - return false; + XMLReportRendererConfiguration config = new XMLReportRendererConfiguration().setOutputFile(file); + new XMLReportRenderer().renderReport(this, config); + return true; } // createXML /** @@ -1510,22 +832,12 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) */ public boolean createXML (Writer writer) { - try - { - m_printData.createXML(new StreamResult(writer)); - writer.flush(); - writer.close(); - return true; - } - catch (Exception e) - { - log.log(Level.SEVERE, "(w)", e); - } - return false; + XMLReportRendererConfiguration config = new XMLReportRendererConfiguration().setOutputWriter(writer); + new XMLReportRenderer().renderReport(this, config); + return true; } // createXML - - /************************************************************************** + /** * Create PDF file. * (created in temporary storage) * @return PDF file @@ -1556,7 +868,7 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) return null; } // getPDF - /************************************************************************** + /** * Create HTML file. * (created in temporary storage) * @return HTML file @@ -1587,7 +899,7 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) return null; } // getHTML - /************************************************************************** + /** * Create CSV file. * (created in temporary storage) * @return CSV file @@ -1618,7 +930,7 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) return null; } // getCSV - /************************************************************************** + /** * Create XLS file. * (created in temporary storage) * @return XLS file @@ -1656,7 +968,7 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) } } // getXLS - /************************************************************************** + /** * Create XLSX file. * (created in temporary storage) * @return XLSX file @@ -1706,13 +1018,15 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) try { - if (file == null) + if (file == null) { file = FileUtil.createTempFile ("ReportEngine", ".pdf"); - fileName = file.getAbsolutePath(); + } else { + if (file.exists()) { + file.delete(); + file = new File(file.getAbsolutePath()); + } + } uri = file.toURI(); - if (file.exists()) - file.delete(); - } catch (Exception e) { @@ -1734,10 +1048,8 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) pi.setPDFFileName(fileName); ServerProcessCtl.process(pi, (m_trxName == null ? null : Trx.get(m_trxName, false))); } else { - if (m_layout == null) - layout (); - Document.getPDFAsFile(fileName, m_layout.getPageable(false)); - ArchiveEngine.get().archive(new File(fileName), m_info); + PDFReportRendererConfiguration config = new PDFReportRendererConfiguration().setOutputFile(file); + new PDFReportRenderer().renderReport(this, config); } } catch (Exception e) @@ -1770,17 +1082,10 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) */ public byte[] createPDFData () { - try - { - if (m_layout == null) - layout (); - return Document.getPDFAsArray(m_layout.getPageable(false)); - } - catch (Exception e) - { - log.log(Level.SEVERE, "PDF", e); - } - return null; + ByteArrayOutputStream os = new ByteArrayOutputStream(); + PDFReportRendererConfiguration config = new PDFReportRendererConfiguration().setOutputStream(os); + new PDFReportRenderer().renderReport(this, config); + return os.toByteArray(); } // createPDFData /************************************************************************** @@ -1790,19 +1095,9 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) */ public boolean createPS (File file) { - try - { - return createPS (new FileOutputStream(file)); - } - catch (FileNotFoundException fnfe) - { - log.log(Level.SEVERE, "(f) - " + fnfe.toString()); - } - catch (Exception e) - { - log.log(Level.SEVERE, "(f)", e); - } - return false; + PSReportRendererConfiguration config = new PSReportRendererConfiguration().setOutputFile(file); + new PSReportRenderer().renderReport(this, config); + return true; } // createPS /** @@ -1812,37 +1107,9 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) */ public boolean createPS (OutputStream os) { - try - { - String outputMimeType = DocFlavor.BYTE_ARRAY.POSTSCRIPT.getMimeType(); - DocFlavor docFlavor = DocFlavor.SERVICE_FORMATTED.PAGEABLE; - StreamPrintServiceFactory[] spsfactories = - StreamPrintServiceFactory.lookupStreamPrintServiceFactories(docFlavor, outputMimeType); - if (spsfactories.length == 0) - { - log.log(Level.SEVERE, "(fos) - No StreamPrintService"); - return false; - } - // just use first one - sun.print.PSStreamPrinterFactory - // System.out.println("- " + spsfactories[0]); - StreamPrintService sps = spsfactories[0].getPrintService(os); - // get format - if (m_layout == null) - layout(); - // print it - sps.createPrintJob().print(m_layout.getPageable(false), - new HashPrintRequestAttributeSet()); - // - os.flush(); - //following 2 line for backward compatibility - if (os instanceof FileOutputStream) - ((FileOutputStream)os).close(); - } - catch (Exception e) - { - log.log(Level.SEVERE, "(fos)", e); - } - return false; + PSReportRendererConfiguration config = new PSReportRendererConfiguration().setOutputStream(os); + new PSReportRenderer().renderReport(this, config); + return true; } // createPS /** @@ -1854,10 +1121,8 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) public void createXLS(File outFile, Language language) throws Exception { - Boolean [] colSuppressRepeats = m_layout == null || m_layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(m_printFormat):m_layout.colSuppressRepeats; - Map childFormats = m_layout != null ? m_layout.getChildPrintFormatDetails() : null; - PrintDataExcelExporter exp = new PrintDataExcelExporter(getPrintData(), getPrintFormat(), childFormats, colSuppressRepeats, m_query); - exp.export(outFile, language); + XLSReportRendererConfiguration config = new XLSReportRendererConfiguration().setOutputFile(outFile).setLanguage(language); + new XLSReportRenderer().renderReport(this, config); } /** @@ -1869,10 +1134,8 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) public void createXLSX(File outFile, Language language) throws Exception { - Boolean [] colSuppressRepeats = m_layout == null || m_layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(m_printFormat):m_layout.colSuppressRepeats; - Map childFormats = m_layout != null ? m_layout.getChildPrintFormatDetails() : null; - PrintDataXLSXExporter exp = new PrintDataXLSXExporter(getPrintData(), getPrintFormat(), childFormats, colSuppressRepeats, m_query); - exp.export(outFile, language); + XLSXReportRendererConfiguration config = new XLSXReportRendererConfiguration().setOutputFile(outFile).setLanguage(language); + new XLSXReportRenderer().renderReport(this, config); } /************************************************************************** @@ -2563,260 +1826,7 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) } return -1; } - - /** - * build css for table from mapCssInfo - * @param doc - */ - public void appendInlineCss (XhtmlDocument doc){ - StringBuilder buildCssInline = new StringBuilder(); - - // each entry is a css class - for (Entry> cssClassInfo : mapCssInfo.entrySet()){ - // each column is a css name. - for (int i = 0; i < cssClassInfo.getValue().size(); i++){ - if (i > 0) - buildCssInline.append (","); - - buildCssInline.append(cssClassInfo.getValue().get(i).getCssSelector()); - } - buildCssInline.append(cssClassInfo.getKey().getCssRule()); - buildCssInline.append("\n"); - } - - appendInlineCss (doc, buildCssInline); - } - - public void appendInlineCss (XhtmlDocument doc, StringBuilder buildCssInline){ - if (buildCssInline.length() > 0){ - buildCssInline.insert(0, ""); - doc.appendHead(buildCssInline.toString()); - } - } - - /** - * create css info from formatItem, add all column has same formatItem in a list - * @param formatItem - * @param index - */ - public void addCssInfo (MPrintFormatItem formatItem, int index){ - CSSInfo cadidateCss = new CSSInfo(formatItem); - if (mapCssInfo.containsKey(cadidateCss)){ - mapCssInfo.get(cadidateCss).add(new ColumnInfo(index, formatItem)); - }else{ - List newColumnList = new ArrayList(); - newColumnList.add(new ColumnInfo(index, formatItem)); - mapCssInfo.put(cadidateCss, newColumnList); - } - } - - /** - * Store info for make css rule - * @author hieplq - * - */ - public class CSSInfo { - private Font font; - private Color color; - private String cssStr; - public CSSInfo (MPrintFormatItem item){ - MPrintFont mPrintFont = null; - - if (item.getAD_PrintFont_ID() > 0) - { - mPrintFont = MPrintFont.get(item.getAD_PrintFont_ID()); - } - else if (m_printFormat.getAD_PrintFont_ID() > 0) - { - mPrintFont = MPrintFont.get(m_printFormat.getAD_PrintFont_ID()); - } - if (mPrintFont != null && mPrintFont.getAD_PrintFont_ID() > 0) - { - font = mPrintFont.getFont(); - } - - MPrintColor mPrintColor = null; - if (item.getAD_PrintColor_ID() > 0) - { - mPrintColor = MPrintColor.get(m_ctx, item.getAD_PrintColor_ID()); - } - else if (m_printFormat.getAD_PrintColor_ID() > 0) - { - mPrintColor = MPrintColor.get(m_ctx, m_printFormat.getAD_PrintColor_ID()); - } - if (mPrintColor != null && mPrintColor.getAD_PrintColor_ID() > 0) - { - color = mPrintColor.getColor(); - - } - } - - public CSSInfo (Font font, Color color) { - this.font = font; - this.color = color; - } - - /** - * sum hashCode of partial - */ - @Override - public int hashCode() { - return (color == null ? 0 : color.hashCode()) + (font == null ? 0 : font.hashCode()); - } - - /** - * equal only when same color and font - */ - @Override - public boolean equals(Object obj) { - if (obj == null || !(obj instanceof CSSInfo) || obj.hashCode() != this.hashCode()) - return false; - - CSSInfo compareObj = (CSSInfo)obj; - - return compareObj (compareObj.color, color) && compareObj (compareObj.font, font); - } - - /** - * compare two object equal when both is null or result of equal - * @param obj1 - * @param obj2 - * @return - */ - protected boolean compareObj(Object obj1, Object obj2) { - if (obj1 == null && obj2 != null) - return false; - - if (obj1 == null && obj2 == null){ - return true; - } - - return obj1.equals(obj2); - } - - /** - * append a css rule to css class - * @param cssBuild - * @param ruleName - * @param ruleValue - */ - protected void addCssRule(StringBuilder cssBuild, String ruleName, String ruleValue) { - cssBuild.append (ruleName); - cssBuild.append (":"); - cssBuild.append (ruleValue); - cssBuild.append (";"); - } - - /** - * build css rule - * @return - */ - public String getCssRule (){ - if (cssStr != null) - return cssStr; - - StringBuilder cssBuild = new StringBuilder(); - cssBuild.append ("{"); - - if (font != null){ - - String fontFamily = font.getFamily(); - fontFamily = getCSSFontFamily(fontFamily); - if (fontFamily != null){ - addCssRule(cssBuild, "font-family", fontFamily); - } - - if (font.isBold()) - { - addCssRule(cssBuild, "font-weight", "bold"); - } - - if (font.isItalic()) - { - addCssRule(cssBuild, "font-style", "italic"); - } - - int size = font.getSize(); - addCssRule(cssBuild, "font-size", size + "pt"); - } - - if (color != null) - { - cssBuild.append("color:rgb("); - cssBuild.append(color.getRed()); - cssBuild.append(","); - cssBuild.append(color.getGreen()); - cssBuild.append(","); - cssBuild.append(color.getBlue()); - cssBuild.append(");"); - } - cssBuild.append ("}"); - cssStr = cssBuild.toString(); - - return cssStr; - } - } - - /** - * store info of report column, - * now just use index to create css selector, but for later maybe will construct a complex class name - * @author hieplq - * - */ - public static class ColumnInfo { - protected static String CSS_SELECTOR_TEMPLATE = "table > tbody > tr > td:nth-child(%1$s)"; - int index = -1; - public ColumnInfo (int index, MPrintFormatItem formatItem){ - this.index = index; - - } - - public String getCssSelector(){ - return String.format(CSS_SELECTOR_TEMPLATE, index + 1); - } - } - - private boolean isDisplayPFItem(MPrintFormatItem item) - { - if(Util.isEmpty(item.getDisplayLogic())) - return true; - - return Evaluator.evaluateLogic(new PrintDataEvaluatee(null, m_printData), item.getDisplayLogic()); - } - - public String compress(String src, boolean minify) { - - if(minify) { - HtmlCompressor compressor = new HtmlCompressor(); - compressor.setEnabled(true); - compressor.setCompressCss(true); - compressor.setCompressJavaScript(true); - compressor.setRemoveComments(true); - compressor.setRemoveMultiSpaces(true); - compressor.setRemoveIntertagSpaces(true); -// compressor.setGenerateStatistics(false); -// compressor.setRemoveQuotes(false); -// compressor.setSimpleDoctype(false); -// compressor.setRemoveScriptAttributes(false); -// compressor.setRemoveStyleAttributes(false); -// compressor.setRemoveLinkAttributes(false); -// compressor.setRemoveFormAttributes(false); -// compressor.setRemoveInputAttributes(false); -// compressor.setSimpleBooleanAttributes(false); -// compressor.setRemoveJavaScriptProtocol(false); -// compressor.setRemoveHttpProtocol(false); -// compressor.setRemoveHttpsProtocol(false); -// compressor.setPreserveLineBreaks(false); - - return compressor.compress(src); - } - else { - return src; - } - } - public static void setDefaultReportTypeToPInstance(Properties ctx, MPInstance instance, int printFormatID) { if(Util.isEmpty(instance.getReportType())) { MPrintFormat pf = new MPrintFormat(ctx, printFormatID, null); @@ -2828,30 +1838,6 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) } } - private void setStyle(MultiPartElement element, MStyle style) { - if (style == null || style.getAD_Style_ID() == 0) - return; - - X_AD_StyleLine[] lines = style.getStyleLines(); - StringBuilder styleBuilder = new StringBuilder(); - for (X_AD_StyleLine line : lines) - { - String inlineStyle = line.getInlineStyle().trim(); - String displayLogic = line.getDisplayLogic(); - if (!Util.isEmpty(displayLogic)) - { - if (!Evaluator.evaluateLogic(new PrintDataEvaluatee(null, m_printData), displayLogic)) - continue; - } - if (styleBuilder.length() > 0 && !(styleBuilder.charAt(styleBuilder.length()-1)==';')) - styleBuilder.append("; "); - styleBuilder.append(inlineStyle); - } - if(styleBuilder.length() > 0) - element.setStyle(styleBuilder.toString()); - // - } - private ProcessInfo m_pi = null; /** @@ -2867,4 +1853,18 @@ queued-job-count = 0 (class javax.print.attribute.standard.QueuedJobCount) public ProcessInfo getProcessInfo() { return m_pi; } + + /** + * Evaluate display logic of a print format item + * @param printData data for display logic evaluation + * @param item print format item + * @return true if item has no display logic or display logic evaluate to true + */ + public static boolean isDisplayPFItem(PrintData printData, MPrintFormatItem item) + { + if(Util.isEmpty(item.getDisplayLogic())) + return true; + + return Evaluator.evaluateLogic(new PrintDataEvaluatee(null, printData), item.getDisplayLogic()); + } } // ReportEngine diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/CSVReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/CSVReportRenderer.java new file mode 100644 index 0000000000..243f7786ec --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/CSVReportRenderer.java @@ -0,0 +1,64 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.osgi.service.component.annotations.Component; + +import com.google.common.net.MediaType; + +/** + * Comma separated content rendering service for report engine + */ +@Component(service = IReportRenderer.class, immediate = true) +public class CSVReportRenderer extends DelimitedReportRenderer { + + public CSVReportRenderer() { + } + + @Override + public String getId() { + return CSVReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return Msg.getMsg(Env.getCtx(), "FileCSV"); + } + + @Override + public String getContentType() { + return MediaType.CSV_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return CSVReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public Class getConfigurationType() { + return CSVReportRendererConfiguration.class; + } + +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/CSVReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/CSVReportRendererConfiguration.java new file mode 100644 index 0000000000..74691cdcf0 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/CSVReportRendererConfiguration.java @@ -0,0 +1,63 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.io.Writer; + +import org.compiere.util.Language; + +/** + * Configuration for comma separated content renderer + */ +public class CSVReportRendererConfiguration extends DelimitedReportRendererConfiguration { + + public final static String ID = "CSV"; + public static final String FILE_EXTENSION = "csv"; + + public CSVReportRendererConfiguration() { + } + + @Override + public char getDelimiter() { + return ','; + } + + @Override + public CSVReportRendererConfiguration setOutputFile(File file) { + super.setOutputFile(file); + return this; + } + + @Override + public CSVReportRendererConfiguration setOutputWriter(Writer writer) { + super.setOutputWriter(writer); + return this; + } + + @Override + public CSVReportRendererConfiguration setLanguage(Language language) { + super.setLanguage(language); + return this; + } + +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/DelimitedReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/DelimitedReportRenderer.java new file mode 100644 index 0000000000..53738b36d7 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/DelimitedReportRenderer.java @@ -0,0 +1,262 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; + +import org.compiere.print.MPrintFormat; +import org.compiere.print.MPrintFormatItem; +import org.compiere.print.PrintData; +import org.compiere.print.PrintDataElement; +import org.compiere.print.ReportEngine; +import org.compiere.print.layout.InstanceAttributeColumn; +import org.compiere.print.layout.InstanceAttributeData; +import org.compiere.print.layout.LayoutEngine; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Ini; +import org.compiere.util.Language; + +/** + * Abstract base class for renderer that output delimited text content. + * @param Renderer configuration type + */ +public abstract class DelimitedReportRenderer implements IReportRenderer { + + private static final CLogger log = CLogger.getCLogger(DelimitedReportRenderer.class); + + public DelimitedReportRenderer() { + } + + @Override + public void renderReport(ReportEngine reportEngine, C configuration) { + try { + Writer fw = null; + if (configuration.getOutputFile() != null) + fw = new OutputStreamWriter(new FileOutputStream(configuration.getOutputFile(), false), Ini.getCharset()); + else + fw = configuration.getOutputWriter(); + createdDelimitedOutput(reportEngine, fw, configuration.getDelimiter(), configuration.getLanguage()); + } + catch (FileNotFoundException fnfe) { + log.log(Level.SEVERE, "(f) - " + fnfe.toString()); + } + catch (Exception e) { + log.log(Level.SEVERE, "(f)", e); + } + } + + @Override + public boolean isBinary() { + return false; + } + + /** + * Write delimited text to writer + * @param writer writer + * @param delimiter delimiter, e.g. comma, tab + * @param language translation language + * @return true if success + */ + private boolean createdDelimitedOutput (ReportEngine reportEngine, Writer writer, char delimiter, Language language) + { + MPrintFormat printFormat = reportEngine.getPrintFormat(); + PrintData printData = reportEngine.getPrintData(); + LayoutEngine layout = reportEngine.getLayout(); + if (delimiter == 0) + delimiter = '\t'; + try + { + //collect columns to be printed + ArrayList columns = new ArrayList<>(); + List asiElements = new ArrayList<>(); + int columnCount = 0; + for (int col = 0; col < printFormat.getItemCount(); col++) + { + MPrintFormatItem item = printFormat.getItem(col); + if (item.isPrinted()) + { + if (item.isTypeField() && item.isPrintInstanceAttributes()) + { + InstanceAttributeData asiElement = new InstanceAttributeData(item, columnCount); + asiElement.readAttributesData(printData); + asiElements.add(asiElement); + continue; + } + else + { + columns.add(item); + columnCount++; + } + } + } + if (asiElements.size() > 0) + { + int columnCreated = 0; + for(InstanceAttributeData data : asiElements) + { + List instanceColumns = data.getColumns(); + int index = data.getColumnIndex() + columnCreated; + for(InstanceAttributeColumn c : instanceColumns) + { + columns.add(index, c); + index++; + columnCreated++; + } + } + } + + Boolean [] colSuppressRepeats = layout == null || layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(printFormat):layout.colSuppressRepeats; + Object [] preValues = null; + if (colSuppressRepeats != null){ + preValues = new Object [colSuppressRepeats.length]; + } + int printColIndex = -1; + // for all rows (-1 = header row) + for (int row = -1; row < printData.getRowCount(); row++) + { + printColIndex = -1; + StringBuffer sb = new StringBuffer(); + if (row != -1) + printData.setRowIndex(row); + + // for all columns + boolean first = true; // first column to print + for (int col = 0; col < columns.size(); col++) + { + Object colObj = columns.get(col); + MPrintFormatItem item = null; + InstanceAttributeColumn iaColumn = null; + if (colObj instanceof InstanceAttributeColumn) + { + iaColumn = (InstanceAttributeColumn) colObj; + item = iaColumn.getPrintFormatItem(); + } + else if (colObj instanceof MPrintFormatItem) + { + item = (MPrintFormatItem)colObj; + } + if (item != null) + { + // column delimiter (comma or tab) + if (first) + first = false; + else + sb.append(delimiter); + // header row + if (row == -1) + { + String printName = iaColumn != null ? iaColumn.getName() : item.getPrintName(language); + addDelimitedValue (sb, delimiter, printName); + } + else + { + printColIndex++; + Object obj = iaColumn != null ? iaColumn.getPrintDataElement(row) : printData.getNodeByPrintFormatItemId(item.getAD_PrintFormatItem_ID()); + String data = ""; + if (obj == null || !ReportEngine.isDisplayPFItem(printData, item)){ + if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ + preValues[printColIndex] = null; + } + } + else if (obj instanceof PrintDataElement) + { + PrintDataElement pde = (PrintDataElement)obj; + if (pde.isPKey()) + data = pde.getValueAsString(); + else + data = pde.getValueDisplay(language); // formatted + + if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ + if (data.equals(preValues[printColIndex])){ + continue; + }else{ + preValues[printColIndex] = data; + } + } + } + else if (obj instanceof PrintData) + { + } + else + log.log(Level.SEVERE, "Element not PrintData(Element) " + obj.getClass()); + addDelimitedValue (sb, delimiter, data); + } + } // printed + } // for all columns + writer.write(sb.toString()); + writer.write(Env.NL); + } // for all rows + // + writer.flush(); + writer.close(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "(w)", e); + return false; + } + return true; + } // createCSV + + /** + * Add content to delimited string. + * Encapsulate/mask content in " if required + * @param sb StringBuffer to add to + * @param delimiter delimiter + * @param content column value + */ + private void addDelimitedValue (StringBuffer sb, char delimiter, String content) + { + // nothing to add + if (content == null || content.length() == 0) + return; + // + boolean needMask = false; + StringBuilder buff = new StringBuilder(); + char chars[] = content.toCharArray(); + for (int i = 0; i < chars.length; i++) + { + char c = chars[i]; + if (c == '"') + { + needMask = true; + buff.append(c); // repeat twice + } // mask if any control character + else if (!needMask && (c == delimiter || !Character.isLetterOrDigit(c))) + needMask = true; + buff.append(c); + } + + // Optionally mask value + if (needMask) + sb.append('"').append(buff).append('"'); + else + sb.append(buff); + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/DelimitedReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/DelimitedReportRendererConfiguration.java new file mode 100644 index 0000000000..76a137363d --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/DelimitedReportRendererConfiguration.java @@ -0,0 +1,71 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.io.Writer; + +import org.compiere.util.Language; + +/** + * Abstract base class for delimited content renderer configuration + */ +public abstract class DelimitedReportRendererConfiguration implements IReportRendererConfiguration { + + private File file; + private Writer writer; + private Language language; + + public DelimitedReportRendererConfiguration() { + } + + @Override + public File getOutputFile() { + return file; + } + + public DelimitedReportRendererConfiguration setOutputFile(File file) { + this.file = file; + return this; + } + + @Override + public Writer getOutputWriter() { + return writer; + } + + public DelimitedReportRendererConfiguration setOutputWriter(Writer writer) { + this.writer = writer; + return this; + } + + public DelimitedReportRendererConfiguration setLanguage(Language language) { + this.language = language; + return this; + } + + public Language getLanguage() { + return language; + } + + public abstract char getDelimiter(); +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/HTMLReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/HTMLReportRenderer.java new file mode 100644 index 0000000000..8d7af1b2da --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/HTMLReportRenderer.java @@ -0,0 +1,953 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.awt.Color; +import java.awt.Font; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; + +import org.adempiere.exceptions.AdempiereException; +import org.apache.ecs.MultiPartElement; +import org.apache.ecs.XhtmlDocument; +import org.apache.ecs.xhtml.a; +import org.apache.ecs.xhtml.script; +import org.apache.ecs.xhtml.span; +import org.apache.ecs.xhtml.style; +import org.apache.ecs.xhtml.table; +import org.apache.ecs.xhtml.tbody; +import org.apache.ecs.xhtml.td; +import org.apache.ecs.xhtml.th; +import org.apache.ecs.xhtml.thead; +import org.apache.ecs.xhtml.tr; +import org.compiere.model.MColumn; +import org.compiere.model.MQuery; +import org.compiere.model.MRole; +import org.compiere.model.MStyle; +import org.compiere.model.MSysConfig; +import org.compiere.model.MTable; +import org.compiere.model.X_AD_StyleLine; +import org.compiere.print.IHTMLExtension; +import org.compiere.print.MPrintColor; +import org.compiere.print.MPrintFont; +import org.compiere.print.MPrintFormat; +import org.compiere.print.MPrintFormatItem; +import org.compiere.print.MPrintTableFormat; +import org.compiere.print.PrintData; +import org.compiere.print.PrintDataElement; +import org.compiere.print.ReportEngine; +import org.compiere.print.layout.InstanceAttributeColumn; +import org.compiere.print.layout.InstanceAttributeData; +import org.compiere.print.layout.LayoutEngine; +import org.compiere.print.layout.PrintDataEvaluatee; +import org.compiere.util.CLogger; +import org.compiere.util.DisplayType; +import org.compiere.util.Env; +import org.compiere.util.Evaluator; +import org.compiere.util.Ini; +import org.compiere.util.Language; +import org.compiere.util.Msg; +import org.compiere.util.Util; +import org.osgi.service.component.annotations.Component; + +import com.google.common.net.MediaType; +import com.googlecode.htmlcompressor.compressor.HtmlCompressor; + +/** + * HTML content renderer service for report engine + */ +@Component(service = IReportRenderer.class, immediate = true) +public class HTMLReportRenderer implements IReportRenderer { + + private static final CLogger log = CLogger.getCLogger(HTMLReportRenderer.class); + + public HTMLReportRenderer() { + } + + @Override + public String getId() { + return HTMLReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return Msg.getMsg(Env.getCtx(), "FileHTML"); + } + + @Override + public String getContentType() { + return MediaType.HTML_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return HTMLReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public void renderReport(ReportEngine reportEngine, HTMLReportRendererConfiguration configuration) { + try + { + Language lang = configuration.getLanguage(); + if (lang == null) + lang = Language.getLoginLanguage(); + Writer fw = null; + if (configuration.getOutputFile() != null) + fw = new OutputStreamWriter(new FileOutputStream(configuration.getOutputFile(), false), Ini.getCharset()); + else + fw = configuration.getOutputWriter(); + createHTML (reportEngine, new BufferedWriter(fw), configuration.isOnlyTable(), lang, configuration.getExtension(), configuration.isExport()); + } + catch (FileNotFoundException fnfe) + { + log.log(Level.SEVERE, "(f) - " + fnfe.toString()); + } + catch (Exception e) + { + log.log(Level.SEVERE, "(f)", e); + throw new AdempiereException(e); + } + } + + @Override + public boolean isBinary() { + return false; + } + + /** + * Write HTML to writer + * @param reportEngine + * @param writer writer + * @param onlyTable if false create complete HTML document + * @param language optional language - if null numbers/dates are not formatted + * @param extension optional extension for html output + * @param isExport when isExport = true will don't embed resource dependent zk framework + */ + private void createHTML (ReportEngine reportEngine, Writer writer, boolean onlyTable, Language language, IHTMLExtension extension, boolean isExport) + { + MPrintFormat printFormat = reportEngine.getPrintFormat(); + PrintData printData = reportEngine.getPrintData(); + MQuery reportEngineQuery = reportEngine.getQuery(); + LayoutEngine layout = reportEngine.getLayout(); + Map> mapCssInfo = new HashMap>(); + try + { + //collect column to print + List columns = new ArrayList<>(); + List asiElements = new ArrayList<>(); + int columnCount = 0; + for (int col = 0; col < printFormat.getItemCount(); col++) + { + MPrintFormatItem item = printFormat.getItem(col); + if (item.isPrinted()) + { + if (item.isTypeField() && item.isPrintInstanceAttributes()) + { + InstanceAttributeData asiElement = new InstanceAttributeData(item, columnCount); + asiElement.readAttributesData(printData); + asiElements.add(asiElement); + continue; + } + else + { + columns.add(item); + columnCount++; + } + } + } + if (asiElements.size() > 0) + { + int columnCreated = 0; + for(InstanceAttributeData data : asiElements) + { + List instanceColumns = data.getColumns(); + int index = data.getColumnIndex() + columnCreated; + for(InstanceAttributeColumn c : instanceColumns) + { + columns.add(index, c); + index++; + columnCreated++; + } + } + } + + String cssPrefix = extension != null ? extension.getClassPrefix() : null; + if (cssPrefix != null && cssPrefix.trim().length() == 0) + cssPrefix = null; + + table parameterTable = null; + if (!printFormat.isForm()) { + if (reportEngineQuery != null && reportEngineQuery.isActive()) { + int rows = reportEngineQuery.getReportProcessQuery() != null ? reportEngineQuery.getReportProcessQuery().getRestrictionCount() : reportEngineQuery.getRestrictionCount(); + if (rows > 0) { + parameterTable = new table(); + if (cssPrefix != null) + parameterTable.setClass(cssPrefix + "-parameter-table"); + else + parameterTable.setClass("parameter-table"); + parameterTable.setNeedClosingTag(false); + } + } + } + + table table = new table(); + if (cssPrefix != null) + table.setClass(cssPrefix + "-table"); + // + // + table.setNeedClosingTag(false); + PrintWriter w = new PrintWriter(writer); + XhtmlDocument doc = null; + boolean minify = MSysConfig.getBooleanValue(MSysConfig.HTML_REPORT_MINIFY, true, Env.getAD_Client_ID(Env.getCtx())); + + if (onlyTable) + w.print(compress(table.toString(), minify)); + else + { + doc = new XhtmlDocument(); + doc.getHtml().setNeedClosingTag(false); + doc.getBody().setNeedClosingTag(false); + doc.appendHead(""); + + if (extension != null && !Util.isEmpty(extension.getWebFontLinks(), true)) + { + doc.appendHead(extension.getWebFontLinks()); + } + + if (extension != null && extension.getStyleURL() != null) + { + // maybe cache style content with key is path + String pathStyleFile = extension.getFullPathStyle(); // creates a temp file - delete below + Path path = Paths.get(pathStyleFile); + List styleLines = Files.readAllLines(path, Ini.getCharset()); + Files.delete(path); // delete temp file + StringBuilder styleBuild = new StringBuilder(); + for (String styleLine : styleLines){ + styleBuild.append(styleLine); //.append("\n"); + } + appendInlineCss (doc, styleBuild); + } + if (extension != null && extension.getScriptURL() != null && !isExport) + { + script jslink = new script(); + jslink.setLanguage("javascript"); + jslink.setSrc(extension.getScriptURL()); + doc.appendHead(jslink); + } + + if (extension != null && !isExport){ + extension.setWebAttribute(doc.getBody()); + } + } + + if (doc != null) + { + MPrintFormatItem item = null; + int printColIndex = -1; + for(int col = 0; col < columns.size(); col++) + { + Object colobj = columns.get(col); + if (colobj instanceof MPrintFormatItem) + item = (MPrintFormatItem) colobj; + else if (colobj instanceof InstanceAttributeColumn) + item = ((InstanceAttributeColumn) colobj).getPrintFormatItem(); + if(item != null) + { + printColIndex++; + addCssInfo(printFormat, item, printColIndex, mapCssInfo); + } + }//IDEMPIERE-4113 + appendInlineCss(doc, mapCssInfo); + + StringBuilder styleBuild = new StringBuilder(); + MPrintTableFormat tf = printFormat.getTableFormat(); + CSSInfo cssInfo = new CSSInfo(tf.getPageHeader_Font(), tf.getPageHeaderFG_Color()); + if (cssPrefix != null) { + if (parameterTable != null) + styleBuild.append("."+cssPrefix + "-parameter-table th").append(cssInfo.getCssRule()); + styleBuild.append("."+cssPrefix + "-table th").append(cssInfo.getCssRule()); + } + else { + if (parameterTable != null) + styleBuild.append("parameter-table th").append(cssInfo.getCssRule()); + styleBuild.append("table th").append(cssInfo.getCssRule()); + } + + cssInfo = new CSSInfo(tf.getParameter_Font(), tf.getParameter_Color()); + styleBuild.append(".tr-parameter td").append(cssInfo.getCssRule()); + + cssInfo = new CSSInfo(tf.getFunct_Font(), tf.getFunctFG_Color()); + styleBuild.append(".tr-function td").append(cssInfo.getCssRule()); + + MPrintFont printFont = MPrintFont.get(printFormat.getAD_PrintFont_ID()); + Font base = printFont.getFont(); + Font newFont = new Font(base.getName(), Font.PLAIN, base.getSize()-1); + cssInfo = new CSSInfo(newFont, null); + styleBuild.append(".tr-level-1 td").append(cssInfo.getCssRule()); + + newFont = new Font(base.getName(), Font.PLAIN, base.getSize()-2); + cssInfo = new CSSInfo(newFont, null); + styleBuild.append(".tr-level-2 td").append(cssInfo.getCssRule()); + + styleBuild = new StringBuilder(styleBuild.toString().replaceAll(";", "!important;")); + appendInlineCss (doc, styleBuild); + + w.print(compress(doc.toString(), minify)); + + w.print("
"); + String paraWrapId = null; + if (parameterTable != null) { + paraWrapId = cssPrefix + "-para-table-wrap"; + w.print("
"); + w.print(""+Msg.getMsg(Env.getCtx(), "Parameter")+""); + w.print(compress(parameterTable.toString(), minify)); + + tr tr = new tr(); + tr.setClass("tr-parameter"); + + MQuery query = reportEngineQuery; + if (reportEngineQuery.getReportProcessQuery() != null) + query = reportEngineQuery.getReportProcessQuery(); + for (int r = 0; r < query.getRestrictionCount(); r++) + { + if (r > 0) { + tr = new tr(); + tr.setClass("tr-parameter"); + } + + td td = new td(); + tr.addElement(td); + td.addElement(query.getInfoName(r)); + + td = new td(); + tr.addElement(td); + td.addElement(query.getInfoOperator(r)); + + td = new td(); + tr.addElement(td); + td.addElement(query.getInfoDisplayAll(r)); + + w.print(compress(tr.toString(), minify)); + } + + w.print(""); + w.print("
"); + } + + StringBuilder tableWrapDiv = new StringBuilder(); + tableWrapDiv.append("
"); + + w.print(compress(tableWrapDiv.toString(), minify)); + w.print(compress(table.toString(), minify)); + } + + thead thead = new thead(); + tbody tbody = new tbody(); + tbody.setNeedClosingTag(false); + + Boolean [] colSuppressRepeats = layout == null || layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(printFormat):layout.colSuppressRepeats; + Object [] preValues = null; + if (colSuppressRepeats != null){ + preValues = new Object [colSuppressRepeats.length]; + } + + int printColIndex = -1; + HashMap suppressMap = new HashMap<>(); + + // for all rows (-1 = header row) + for (int row = -1; row < printData.getRowCount(); row++) + { + tr tr = new tr(); + if (row != -1) + { + printData.setRowIndex(row); + if (extension != null && !isExport) + { + extension.extendRowElement(tr, printData); + } + if (printData.isFunctionRow()) { + tr.setClass(cssPrefix + "-functionrow"); + } else if ( row < printData.getRowCount() && printData.isFunctionRow(row+1)) { + tr.setClass(cssPrefix + "-lastgrouprow"); + } + // add row to table body + //tbody.addElement(tr); + } else { + // add row to table header + thead.addElement(tr); + } + + printColIndex = -1; + // for all columns + for (int col = 0; col < columns.size(); col++) + { + Object colObj = columns.get(col); + MPrintFormatItem item = null; + InstanceAttributeColumn instanceAttributeColumn = null; + if (colObj instanceof MPrintFormatItem) + { + item = (MPrintFormatItem) colObj; + } + else if (colObj instanceof InstanceAttributeColumn) + { + instanceAttributeColumn = (InstanceAttributeColumn) colObj; + item = instanceAttributeColumn.getPrintFormatItem(); + } + if (item != null) + { + printColIndex++; + // header row + if (row == -1) + { + th th = new th(); + tr.addElement(th); + String columnHeader = instanceAttributeColumn != null ? instanceAttributeColumn.getName() : item.getPrintName(language); + th.addElement(Util.maskHTML(columnHeader)); + if (cssPrefix != null && instanceAttributeColumn == null) + { + MColumn column = MColumn.get(Env.getCtx(), item.getAD_Column_ID()); + if (column != null && column.getAD_Column_ID() > 0) + { + if (DisplayType.isNumeric(column.getAD_Reference_ID())) + { + th.setClass(cssPrefix + "-number"); + } + } + } + if (item.isSuppressNull()) + { + suppressMap.put(printColIndex, th); + th.setID("report-th-"+printColIndex); + } + } + else + { + td td = new td(); + tr.addElement(td); + MStyle style = item.getAD_FieldStyle_ID() > 0 ? MStyle.get(Env.getCtx(), item.getAD_FieldStyle_ID()) : null; + Object obj = instanceAttributeColumn != null ? instanceAttributeColumn.getPrintDataElement(row) + : printData.getNodeByPrintFormatItemId(item.getAD_PrintFormatItem_ID()); + if (obj == null || !ReportEngine.isDisplayPFItem(printData, item)){ + td.addElement(" "); + if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ + preValues[printColIndex] = null; + } + if (item.isSuppressNull() && obj != null && suppressMap.containsKey(printColIndex)) + suppressMap.remove(printColIndex); + } + else if (obj instanceof PrintDataElement) + { + PrintDataElement pde = (PrintDataElement) obj; + String value = pde.getValueDisplay(language); // formatted + + if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ + if (value.equals(preValues[printColIndex])){ + td.addElement(" "); + continue; + }else{ + preValues[printColIndex] = value; + } + } + + if (item.isSuppressNull() && obj != null && suppressMap.containsKey(printColIndex)) + suppressMap.remove(printColIndex); + + if (pde.getColumnName().endsWith("_ID") && extension != null && !isExport) + { + boolean isZoom = false; + if (item.getColumnName().equals("Record_ID")) { + Object tablePDE = printData.getNode("AD_Table_ID"); + if (tablePDE != null && tablePDE instanceof PrintDataElement) { + int tableID = -1; + try { + tableID = Integer.parseInt(((PrintDataElement)tablePDE).getValueAsString()); + } catch (Exception e) { + tableID = -1; + } + if (tableID > 0) { + MTable mTable = MTable.get(Env.getCtx(), tableID); + String tableName = mTable.getTableName(); + + value = reportEngine.getIdentifier(mTable, tableName, Integer.parseInt(value)); + + String foreignColumnName = tableName + "_ID"; + pde.setForeignColumnName(foreignColumnName); + isZoom = true; + } + } + } else { + isZoom = true; + } + if (isZoom) { + // check permission on the zoomed window + MTable mTable = MTable.get(Env.getCtx(), pde.getForeignColumnName().substring(0, pde.getForeignColumnName().length()-3)); + int Record_ID = -1; + try { + Record_ID = Integer.parseInt(pde.getValueAsString()); + } catch (Exception e) { + Record_ID = -1; + } + Boolean canAccess = null; + if (Record_ID >= 0 && mTable != null) { + int AD_Window_ID = Env.getZoomWindowID(mTable.get_ID(), Record_ID); + canAccess = MRole.getDefault().getWindowAccess(AD_Window_ID); + } + if (canAccess == null) { + isZoom = false; + } + } + if (isZoom) { + //link for column + a href = new a("javascript:void(0)"); + href.setID(pde.getColumnName() + "_" + row + "_a"); + td.addElement(href); + href.addElement(Util.maskHTML(value)); + if (cssPrefix != null) + href.setClass(cssPrefix + "-href"); + // Set Style + if(style != null && style.isWrapWithSpan()) + setStyle(printData, href, style); + else + setStyle(printData, td, style); + extension.extendIDColumn(row, td, href, pde); + } else { + // Set Style + if(style != null && style.isWrapWithSpan()) { + span span = new span(); + setStyle(printData, span, style); + span.addElement(Util.maskHTML(value)); + td.addElement(span); + } + else { + setStyle(printData, td, style); + td.addElement(Util.maskHTML(value)); + } + } + + } + else + { + // Set Style + if(style != null && style.isWrapWithSpan()) { + span span = new span(); + setStyle(printData, span, style); + span.addElement(Util.maskHTML(value)); + td.addElement(span); + } + else { + setStyle(printData, td, style); + td.addElement(Util.maskHTML(value)); + } + } + if (cssPrefix != null) + { + if (DisplayType.isNumeric(pde.getDisplayType())) + td.setClass(cssPrefix + "-number"); + else if (DisplayType.isDate(pde.getDisplayType())) + td.setClass(cssPrefix + "-date"); + else + td.setClass(cssPrefix + "-text"); + } + } + else if (obj instanceof PrintData) + { + // ignore contained Data + } + else + log.log(Level.SEVERE, "Element not PrintData(Element) " + obj.getClass()); + } + } // printed + } // for all columns + + /* output table header */ + if (row == -1){ + w.print(compress(thead.toString(), minify)); + // output open of tbody + w.print(compress(tbody.toString(), minify)); + }else{ + // output row by row + w.print(compress(tr.toString(), minify)); + } + + } // for all rows + + w.print(""); + w.print(""); + if (suppressMap.size() > 0) + { + StringBuilder st = new StringBuilder(); + for(th t : suppressMap.values()) + { + if (st.length() > 0) + st.append(","); + st.append("#").append(t.getAttribute("id")); + } + st.append(" {\n\t\tdisplay:none;\n\t}"); + style styleTag = new style(); + styleTag.addElement(st.toString()); + w.print(compress(styleTag.toString(), minify)); + } + if (!onlyTable) + { + w.print("
"); + w.print("
"); + w.print(""); + w.print(""); + } + w.flush(); + w.close(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "(w)", e); + throw new AdempiereException(e); + } + } // createHTML + + /** + * Compress html content + * @param src + * @param minify + * @return compressed content + */ + public static String compress(String src, boolean minify) { + if(minify) { + HtmlCompressor compressor = new HtmlCompressor(); + compressor.setEnabled(true); + compressor.setCompressCss(true); + compressor.setCompressJavaScript(true); + compressor.setRemoveComments(true); + compressor.setRemoveMultiSpaces(true); + compressor.setRemoveIntertagSpaces(true); +// compressor.setGenerateStatistics(false); +// compressor.setRemoveQuotes(false); +// compressor.setSimpleDoctype(false); +// compressor.setRemoveScriptAttributes(false); +// compressor.setRemoveStyleAttributes(false); +// compressor.setRemoveLinkAttributes(false); +// compressor.setRemoveFormAttributes(false); +// compressor.setRemoveInputAttributes(false); +// compressor.setSimpleBooleanAttributes(false); +// compressor.setRemoveJavaScriptProtocol(false); +// compressor.setRemoveHttpProtocol(false); +// compressor.setRemoveHttpsProtocol(false); +// compressor.setPreserveLineBreaks(false); + + return compressor.compress(src); + } + else { + return src; + } + } + + /** + * Append css for table from mapCssInfo + * @param doc + */ + public static void appendInlineCss (XhtmlDocument doc, Map> mapCssInfo){ + StringBuilder buildCssInline = new StringBuilder(); + + // each entry is a css class + for (Entry> cssClassInfo : mapCssInfo.entrySet()){ + // each column is a css name. + for (int i = 0; i < cssClassInfo.getValue().size(); i++){ + if (i > 0) + buildCssInline.append (","); + + buildCssInline.append(cssClassInfo.getValue().get(i).getCssSelector()); + } + + buildCssInline.append(cssClassInfo.getKey().getCssRule()); + buildCssInline.append("\n"); + } + + appendInlineCss (doc, buildCssInline); + } + + /** + * Append inline css content + * @param doc + * @param buildCssInline + */ + public static void appendInlineCss (XhtmlDocument doc, StringBuilder buildCssInline){ + if (buildCssInline.length() > 0){ + buildCssInline.insert(0, ""); + doc.appendHead(buildCssInline.toString()); + } + } + + /** + * Create css info from formatItem, add all column has same CSSInfo in a list + * @param formatItem + * @param index + */ + public static void addCssInfo (MPrintFormat printFormat, MPrintFormatItem formatItem, int index, Map> mapCssInfo){ + CSSInfo cadidateCss = new CSSInfo(printFormat, formatItem); + if (mapCssInfo.containsKey(cadidateCss)) { + mapCssInfo.get(cadidateCss).add(new ColumnInfo(index, formatItem)); + } else { + List newColumnList = new ArrayList(); + newColumnList.add(new ColumnInfo(index, formatItem)); + mapCssInfo.put(cadidateCss, newColumnList); + } + } + + /** + * Store info for css rule + * @author hieplq + */ + public static class CSSInfo { + private Font font; + private Color color; + private String cssStr; + public CSSInfo (MPrintFormat printFormat, MPrintFormatItem item){ + MPrintFont mPrintFont = null; + + if (item.getAD_PrintFont_ID() > 0) + { + mPrintFont = MPrintFont.get(item.getAD_PrintFont_ID()); + } + else if (printFormat.getAD_PrintFont_ID() > 0) + { + mPrintFont = MPrintFont.get(printFormat.getAD_PrintFont_ID()); + } + if (mPrintFont != null && mPrintFont.getAD_PrintFont_ID() > 0) + { + font = mPrintFont.getFont(); + } + + MPrintColor mPrintColor = null; + if (item.getAD_PrintColor_ID() > 0) + { + mPrintColor = MPrintColor.get(Env.getCtx(), item.getAD_PrintColor_ID()); + } + else if (printFormat.getAD_PrintColor_ID() > 0) + { + mPrintColor = MPrintColor.get(Env.getCtx(), printFormat.getAD_PrintColor_ID()); + } + if (mPrintColor != null && mPrintColor.getAD_PrintColor_ID() > 0) + { + color = mPrintColor.getColor(); + + } + } + + public CSSInfo (Font font, Color color) { + this.font = font; + this.color = color; + } + + /** + * Sum hashCode of color and font + */ + @Override + public int hashCode() { + return (color == null ? 0 : color.hashCode()) + (font == null ? 0 : font.hashCode()); + } + + /** + * equal when same color and font + */ + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof CSSInfo) || obj.hashCode() != this.hashCode()) + return false; + + CSSInfo compareObj = (CSSInfo)obj; + + return compareObj (compareObj.color, color) && compareObj (compareObj.font, font); + } + + /** + * Compare two object equal when both is null or equal + * @param obj1 + * @param obj2 + * @return true if both is null or equal + */ + protected boolean compareObj(Object obj1, Object obj2) { + if (obj1 == null && obj2 != null) + return false; + + if (obj1 == null && obj2 == null){ + return true; + } + + return obj1.equals(obj2); + } + + /** + * Append a css rule to a css class + * @param cssBuild css class builder + * @param ruleName + * @param ruleValue + */ + protected void addCssRule(StringBuilder cssBuild, String ruleName, String ruleValue) { + cssBuild.append (ruleName); + cssBuild.append (":"); + cssBuild.append (ruleValue); + cssBuild.append (";"); + } + + /** + * Build css rule + * @return css rule + */ + public String getCssRule (){ + if (cssStr != null) + return cssStr; + + StringBuilder cssBuild = new StringBuilder(); + cssBuild.append ("{"); + + if (font != null){ + + String fontFamily = font.getFamily(); + fontFamily = getCSSFontFamily(fontFamily); + if (fontFamily != null){ + addCssRule(cssBuild, "font-family", fontFamily); + } + + if (font.isBold()) + { + addCssRule(cssBuild, "font-weight", "bold"); + } + + if (font.isItalic()) + { + addCssRule(cssBuild, "font-style", "italic"); + } + + int size = font.getSize(); + addCssRule(cssBuild, "font-size", size + "pt"); + } + + if (color != null) + { + cssBuild.append("color:rgb("); + cssBuild.append(color.getRed()); + cssBuild.append(","); + cssBuild.append(color.getGreen()); + cssBuild.append(","); + cssBuild.append(color.getBlue()); + cssBuild.append(");"); + } + cssBuild.append ("}"); + cssStr = cssBuild.toString(); + + return cssStr; + } + } + + /** + * Get CSS font family + * @param fontFamily Java font family + * @return CSS font family + */ + private static String getCSSFontFamily(String fontFamily) { + if ("Dialog".equals(fontFamily) || "DialogInput".equals(fontFamily) || "Monospaced".equals(fontFamily)) + { + return "monospace"; + } else if ("SansSerif".equals(fontFamily)) + { + return "sans-serif"; + } else if ("Serif".equals(fontFamily)) + { + return "serif"; + } + return null; + } + + /** + * Set inline style of element + * @param printData + * @param element + * @param style + */ + public static void setStyle(PrintData printData, MultiPartElement element, MStyle style) { + if (style == null || style.getAD_Style_ID() == 0) + return; + + X_AD_StyleLine[] lines = style.getStyleLines(); + StringBuilder styleBuilder = new StringBuilder(); + for (X_AD_StyleLine line : lines) + { + String inlineStyle = line.getInlineStyle().trim(); + String displayLogic = line.getDisplayLogic(); + if (!Util.isEmpty(displayLogic)) + { + if (!Evaluator.evaluateLogic(new PrintDataEvaluatee(null, printData), displayLogic)) + continue; + } + if (styleBuilder.length() > 0 && !(styleBuilder.charAt(styleBuilder.length()-1)==';')) + styleBuilder.append("; "); + styleBuilder.append(inlineStyle); + } + if(styleBuilder.length() > 0) + element.setStyle(styleBuilder.toString()); + // + } + + /** + * Store info of report column,
+ * now just use index to create css selector, but for later maybe will construct a complex class name + * @author hieplq + * + */ + public static class ColumnInfo { + protected static String CSS_SELECTOR_TEMPLATE = "table > tbody > tr > td:nth-child(%1$s)"; + int index = -1; + public ColumnInfo (int index, MPrintFormatItem formatItem){ + this.index = index; + + } + + public String getCssSelector(){ + return String.format(CSS_SELECTOR_TEMPLATE, index + 1); + } + } + + @Override + public Class getConfigurationType() { + return HTMLReportRendererConfiguration.class; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/HTMLReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/HTMLReportRendererConfiguration.java new file mode 100644 index 0000000000..dcef7d6e70 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/HTMLReportRendererConfiguration.java @@ -0,0 +1,143 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.io.Writer; + +import org.compiere.print.IHTMLExtension; +import org.compiere.util.Language; + +/** + * Configuration for HTML content renderer + */ +public class HTMLReportRendererConfiguration implements IReportRendererConfiguration { + + private boolean onlyTable; + private IHTMLExtension extension; + private boolean isExport; + private File file; + private Writer writer; + private Language language; + + public static final String ID = "HTML"; + public static final String FILE_EXTENSION = "html"; + + public HTMLReportRendererConfiguration() { + } + + /** + * Is only generate html table instead of html document + * @return true if only generate html table instead of html document + */ + public boolean isOnlyTable() { + return onlyTable; + } + + /** + * Set only generate html table or html document + * @param onlyTable + */ + public HTMLReportRendererConfiguration setOnlyTable(boolean onlyTable) { + this.onlyTable = onlyTable; + return this; + } + + /** + * Get HTML content extension + * @return HTML content extension + */ + public IHTMLExtension getExtension() { + return extension; + } + + /** + * Set HTML content extension + * @param extension + */ + public HTMLReportRendererConfiguration setExtension(IHTMLExtension extension) { + this.extension = extension; + return this; + } + + /** + * Is renderer support export + * @return true if renderer support export + */ + public boolean isExport() { + return isExport; + } + + /** + * Set is renderer support export + * @param isExport + */ + public HTMLReportRendererConfiguration setExport(boolean isExport) { + this.isExport = isExport; + return this; + } + + @Override + public File getOutputFile() { + return file; + } + + /** + * Set output file + * @param file + */ + public HTMLReportRendererConfiguration setOutputFile(File file) { + this.file = file; + return this; + } + + @Override + public Writer getOutputWriter() { + return writer; + } + + /** + * Set output writer + * @param writer + */ + public HTMLReportRendererConfiguration setOutputWriter(Writer writer) { + this.writer = writer; + return this; + } + + /** + * Set content language + * @param language + */ + public HTMLReportRendererConfiguration setLanguage(Language language) { + this.language = language; + return this; + } + + /** + * Get content language + * @return content language + */ + public Language getLanguage() { + return language; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/IReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/IReportRenderer.java new file mode 100644 index 0000000000..289dc36b79 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/IReportRenderer.java @@ -0,0 +1,74 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import org.compiere.print.ReportEngine; + +/** + * Content rendering service for report engine.
+ * Implementation must be thread safe. + * @param Renderer configuration type + */ +public interface IReportRenderer { + /** + * Get id of renderer + * @return renderer id (HTML, XLS, etc) + */ + String getId(); + + /** + * Get renderer name + * @return renderer name + */ + String getName(); + + /** + * Get MIME content type (text/html, etc) + * @return MIME content type + */ + String getContentType(); + + /** + * Get file extension (without dot) + * @return file extension + */ + String getFileExtension(); + + /** + * Render content for report engine + * @param reportEngine + * @param configuration + */ + void renderReport(ReportEngine reportEngine, T configuration); + + /** + * Is binary content + * @return true if content is binary + */ + boolean isBinary(); + + /** + * Get configuration type + * @return configuration type + */ + Class getConfigurationType (); +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/IReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/IReportRendererConfiguration.java new file mode 100644 index 0000000000..1fd4dfa5a6 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/IReportRendererConfiguration.java @@ -0,0 +1,57 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.io.OutputStream; +import java.io.Writer; + +/** + * Interface for report renderer configuration + */ +public interface IReportRendererConfiguration { + + /** + * Get output file + * @return output file + */ + default File getOutputFile() { + return null; + + } + + /** + * Get output stream + * @return output stream + */ + default OutputStream getOutputStream() { + return null; + } + + /** + * Get output writer + * @return output writer + */ + default Writer getOutputWriter() { + return null; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/PDFReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/PDFReportRenderer.java new file mode 100644 index 0000000000..6106ee36a6 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/PDFReportRenderer.java @@ -0,0 +1,100 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.logging.Level; + +import org.adempiere.exceptions.AdempiereException; +import org.adempiere.pdf.Document; +import org.compiere.print.ArchiveEngine; +import org.compiere.print.ReportEngine; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.osgi.service.component.annotations.Component; + +import com.google.common.net.MediaType; + +/** + * PDF content rendering service for report engine + */ +@Component(service = IReportRenderer.class, immediate = true) +public class PDFReportRenderer implements IReportRenderer { + + private static final CLogger log = CLogger.getCLogger(PDFReportRenderer.class); + + public PDFReportRenderer() { + } + + @Override + public String getId() { + return PDFReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return Msg.getMsg(Env.getCtx(), "FilePDF"); + } + + @Override + public String getContentType() { + return MediaType.PDF.toString(); + } + + @Override + public String getFileExtension() { + return PDFReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public void renderReport(ReportEngine reportEngine, PDFReportRendererConfiguration configuration) { + try + { + OutputStream os = null; + if (configuration.getOutputFile() != null) + os = new FileOutputStream(configuration.getOutputFile()); + else + os = configuration.getOutputStream(); + Document.writePDF(reportEngine.getLayout().getPageable(false), os); + if (configuration.getOutputFile() != null) + ArchiveEngine.get().archive(configuration.getOutputFile(), reportEngine.getPrintInfo()); + } + catch (Exception e) + { + log.log(Level.SEVERE, "PDF", e); + throw new AdempiereException(e); + } + } + + @Override + public boolean isBinary() { + return true; + } + + @Override + public Class getConfigurationType() { + return PDFReportRendererConfiguration.class; + } + +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/PDFReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/PDFReportRendererConfiguration.java new file mode 100644 index 0000000000..2ff49298a4 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/PDFReportRendererConfiguration.java @@ -0,0 +1,60 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.io.OutputStream; + +/** + * Configuration for PDF content renderer + */ +public class PDFReportRendererConfiguration implements IReportRendererConfiguration { + + public static final String ID = "PDF"; + public static final String FILE_EXTENSION = "pdf"; + + private File file; + private OutputStream outputStream; + + public PDFReportRendererConfiguration() { + } + + @Override + public File getOutputFile() { + return file; + } + + public PDFReportRendererConfiguration setOutputFile(File file) { + this.file = file; + return this; + } + + @Override + public OutputStream getOutputStream() { + return outputStream; + } + + public PDFReportRendererConfiguration setOutputStream(OutputStream os) { + this.outputStream = os; + return this; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/PSReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/PSReportRenderer.java new file mode 100644 index 0000000000..d7fde7c86b --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/PSReportRenderer.java @@ -0,0 +1,137 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.logging.Level; + +import javax.print.DocFlavor; +import javax.print.StreamPrintService; +import javax.print.StreamPrintServiceFactory; +import javax.print.attribute.HashPrintRequestAttributeSet; + +import org.adempiere.exceptions.AdempiereException; +import org.compiere.print.ReportEngine; +import org.compiere.print.layout.LayoutEngine; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.osgi.service.component.annotations.Component; + +import com.google.common.net.MediaType; + +/** + * Postscript content rendering service for report engine + */ +@Component(service = IReportRenderer.class, immediate = true) +public class PSReportRenderer implements IReportRenderer { + + private static final CLogger log = CLogger.getCLogger(PSReportRenderer.class); + + public PSReportRenderer() { + } + + @Override + public String getId() { + return PSReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return Msg.getMsg(Env.getCtx(), "FilePS"); + } + + @Override + public String getContentType() { + return MediaType.POSTSCRIPT.toString(); + } + + @Override + public String getFileExtension() { + return PSReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public void renderReport(ReportEngine reportEngine, PSReportRendererConfiguration configuration) { + try + { + OutputStream os = null; + if (configuration.getOutputFile() != null) + os = new FileOutputStream(configuration.getOutputFile()); + else + os = configuration.getOutputStream(); + createPS(reportEngine, os); + } + catch (Exception e) + { + log.log(Level.SEVERE, "PDF", e); + throw new AdempiereException(e); + } + } + + @Override + public boolean isBinary() { + return true; + } + + /** + * Write PostScript to output stream + * @param os output stream + */ + private void createPS (ReportEngine reportEngine, OutputStream os) + { + try + { + String outputMimeType = DocFlavor.BYTE_ARRAY.POSTSCRIPT.getMimeType(); + DocFlavor docFlavor = DocFlavor.SERVICE_FORMATTED.PAGEABLE; + StreamPrintServiceFactory[] spsfactories = + StreamPrintServiceFactory.lookupStreamPrintServiceFactories(docFlavor, outputMimeType); + if (spsfactories.length == 0) + { + log.log(Level.SEVERE, "(fos) - No StreamPrintService"); + return; + } + // just use first one - sun.print.PSStreamPrinterFactory + StreamPrintService sps = spsfactories[0].getPrintService(os); + // get format + LayoutEngine layout = reportEngine.getLayout(); + // print it + sps.createPrintJob().print(layout.getPageable(false), + new HashPrintRequestAttributeSet()); + // + os.flush(); + //following 2 line for backward compatibility + if (os instanceof FileOutputStream) + ((FileOutputStream)os).close(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "(fos)", e); + } + } // createPS + + @Override + public Class getConfigurationType() { + return PSReportRendererConfiguration.class; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/PSReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/PSReportRendererConfiguration.java new file mode 100644 index 0000000000..77decc8424 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/PSReportRendererConfiguration.java @@ -0,0 +1,68 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.io.OutputStream; + +/** + * Configuration for postscript content renderer + */ +public class PSReportRendererConfiguration implements IReportRendererConfiguration { + + public static final String ID = "PS"; + public static final String FILE_EXTENSION = "ps"; + + private File file; + private OutputStream outputStream; + + public PSReportRendererConfiguration() { + } + + @Override + public File getOutputFile() { + return file; + } + + /** + * Set output file + * @param file + */ + public PSReportRendererConfiguration setOutputFile(File file) { + this.file = file; + return this; + } + + @Override + public OutputStream getOutputStream() { + return outputStream; + } + + /** + * Set output stream + * @param os + */ + public PSReportRendererConfiguration setOutputStream(OutputStream os) { + this.outputStream = os; + return this; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/SSVReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/SSVReportRenderer.java new file mode 100644 index 0000000000..0b16375f30 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/SSVReportRenderer.java @@ -0,0 +1,61 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.osgi.service.component.annotations.Component; + +import com.google.common.net.MediaType; + +/** + * Semicolon separated content rendering service for report engine + */ +@Component(service = IReportRenderer.class, immediate = true) +public class SSVReportRenderer extends DelimitedReportRenderer { + + @Override + public String getId() { + return SSVReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return Msg.getMsg(Env.getCtx(), "FileSSV"); + } + + @Override + public String getContentType() { + return MediaType.CSV_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return SSVReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public Class getConfigurationType() { + return SSVReportRendererConfiguration.class; + } + +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/SSVReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/SSVReportRendererConfiguration.java new file mode 100644 index 0000000000..fd44a5ebda --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/SSVReportRendererConfiguration.java @@ -0,0 +1,62 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.io.Writer; + +import org.compiere.util.Language; + +/** + * Configuration for semicolon separated content renderer + */ +public class SSVReportRendererConfiguration extends DelimitedReportRendererConfiguration { + + public static final String ID = "SSV"; + public static final String FILE_EXTENSION = "ssv"; + + public SSVReportRendererConfiguration() { + } + + @Override + public char getDelimiter() { + return ';'; + } + + @Override + public SSVReportRendererConfiguration setOutputFile(File file) { + super.setOutputFile(file); + return this; + } + + @Override + public SSVReportRendererConfiguration setOutputWriter(Writer writer) { + super.setOutputWriter(writer); + return this; + } + + @Override + public SSVReportRendererConfiguration setLanguage(Language language) { + super.setLanguage(language); + return this; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/TabDelimitedReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/TabDelimitedReportRenderer.java new file mode 100644 index 0000000000..9dc0e26f02 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/TabDelimitedReportRenderer.java @@ -0,0 +1,64 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.osgi.service.component.annotations.Component; + +import com.google.common.net.MediaType; + +/** + * Tab delimited content rendering service for report engine + */ +@Component(service = IReportRenderer.class, immediate = true) +public class TabDelimitedReportRenderer extends DelimitedReportRenderer { + + public TabDelimitedReportRenderer() { + } + + @Override + public String getId() { + return TabDelimitedReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return Msg.getMsg(Env.getCtx(), "FileTXT"); + } + + @Override + public String getContentType() { + return MediaType.PLAIN_TEXT_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return TabDelimitedReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public Class getConfigurationType() { + return TabDelimitedReportRendererConfiguration.class; + } + +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/TabDelimitedReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/TabDelimitedReportRendererConfiguration.java new file mode 100644 index 0000000000..8152f48756 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/TabDelimitedReportRendererConfiguration.java @@ -0,0 +1,62 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.io.Writer; + +import org.compiere.util.Language; + +/** + * Configuration for tab delimited content renderer + */ +public class TabDelimitedReportRendererConfiguration extends DelimitedReportRendererConfiguration { + + public static final String ID = "TXT"; + public static final String FILE_EXTENSION = "txt"; + + public TabDelimitedReportRendererConfiguration() { + } + + @Override + public char getDelimiter() { + return '\t'; + } + + @Override + public TabDelimitedReportRendererConfiguration setOutputFile(File file) { + super.setOutputFile(file); + return this; + } + + @Override + public TabDelimitedReportRendererConfiguration setOutputWriter(Writer writer) { + super.setOutputWriter(writer); + return this; + } + + @Override + public TabDelimitedReportRendererConfiguration setLanguage(Language language) { + super.setLanguage(language); + return this; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/XLSReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/XLSReportRenderer.java new file mode 100644 index 0000000000..9667826a63 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/XLSReportRenderer.java @@ -0,0 +1,111 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.util.Map; +import java.util.logging.Level; + +import org.adempiere.exceptions.AdempiereException; +import org.adempiere.print.export.PrintDataExcelExporter; +import org.compiere.print.MPrintFormat; +import org.compiere.print.MPrintFormatItem; +import org.compiere.print.PrintData; +import org.compiere.print.ReportEngine; +import org.compiere.print.layout.LayoutEngine; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Language; +import org.compiere.util.Msg; +import org.osgi.service.component.annotations.Component; + +import com.google.common.net.MediaType; + +/** + * Excel (xls) content rendering service for report engine + */ +@Component(service = IReportRenderer.class, immediate = true) +public class XLSReportRenderer implements IReportRenderer { + + + private static final CLogger log = CLogger.getCLogger(XLSReportRenderer.class); + + public XLSReportRenderer() { + } + + @Override + public String getId() { + return XLSReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return Msg.getMsg(Env.getCtx(), "FileXLS"); + } + + @Override + public String getContentType() { + return MediaType.MICROSOFT_EXCEL.toString(); + } + + @Override + public String getFileExtension() { + return XLSReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public void renderReport(ReportEngine reportEngine, XLSReportRendererConfiguration configuration) { + try { + createXLS(reportEngine, configuration.getOutputFile(), configuration.getLanguage()); + } catch (Exception e) { + log.log(Level.SEVERE, e.getMessage(), e); + throw new AdempiereException(e); + } + } + + @Override + public boolean isBinary() { + return true; + } + + /** + * Create Excel file + * @param outFile output file + * @param language + * @throws Exception if error + */ + private void createXLS(ReportEngine reportEngine, File outFile, Language language) + throws Exception + { + LayoutEngine layout = reportEngine.getLayout(); + MPrintFormat printFormat = reportEngine.getPrintFormat(); + Boolean [] colSuppressRepeats = layout == null || layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(printFormat):layout.colSuppressRepeats; + Map childFormats = layout != null ? layout.getChildPrintFormatDetails() : null; + PrintDataExcelExporter exp = new PrintDataExcelExporter(reportEngine.getPrintData(), printFormat, childFormats, colSuppressRepeats, reportEngine.getQuery()); + exp.export(outFile, language); + } + + @Override + public Class getConfigurationType() { + return XLSReportRendererConfiguration.class; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/XLSReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/XLSReportRendererConfiguration.java new file mode 100644 index 0000000000..81a9b6d10a --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/XLSReportRendererConfiguration.java @@ -0,0 +1,72 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; + +import org.compiere.util.Language; + +/** + * Configuration for Excel (xls) content renderer + */ +public class XLSReportRendererConfiguration implements IReportRendererConfiguration { + + public static final String ID = "XLS"; + public static final String FILE_EXTENSION = "xls"; + + private File file; + private Language language; + + public XLSReportRendererConfiguration() { + } + + @Override + public File getOutputFile() { + return file; + } + + /** + * Set output file + * @param file + */ + public XLSReportRendererConfiguration setOutputFile(File file) { + this.file = file; + return this; + } + + /** + * Set content language + * @param language + */ + public XLSReportRendererConfiguration setLanguage(Language language) { + this.language = language; + return this; + } + + /** + * Get content language + * @return content language + */ + public Language getLanguage() { + return language; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/XLSXReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/XLSXReportRenderer.java new file mode 100644 index 0000000000..23e40f6ad0 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/XLSXReportRenderer.java @@ -0,0 +1,111 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.util.Map; +import java.util.logging.Level; + +import org.adempiere.exceptions.AdempiereException; +import org.adempiere.print.export.PrintDataXLSXExporter; +import org.compiere.print.MPrintFormat; +import org.compiere.print.MPrintFormatItem; +import org.compiere.print.PrintData; +import org.compiere.print.ReportEngine; +import org.compiere.print.layout.LayoutEngine; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Language; +import org.compiere.util.Msg; +import org.osgi.service.component.annotations.Component; + +import com.google.common.net.MediaType; + +/** + * Excel open xml (xlsx) content rendering service for report engine + */ +@Component(service = IReportRenderer.class, immediate = true) +public class XLSXReportRenderer implements IReportRenderer { + + private static final CLogger log = CLogger.getCLogger(XLSReportRenderer.class); + + public XLSXReportRenderer() { + } + + @Override + public String getId() { + return XLSXReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return Msg.getMsg(Env.getCtx(), "FileXLSX"); + } + + @Override + public String getContentType() { + return MediaType.OOXML_SHEET.toString(); + } + + @Override + public String getFileExtension() { + return XLSXReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public void renderReport(ReportEngine reportEngine, XLSXReportRendererConfiguration configuration) { + try { + createXLSX(reportEngine, configuration.getOutputFile(), configuration.getLanguage()); + } catch (Exception e) { + log.log(Level.SEVERE, e.getMessage(), e); + throw new AdempiereException(e); + } + } + + @Override + public boolean isBinary() { + return true; + } + + /** + * Create Excel open XML file + * @param reportEngine + * @param outFile output file + * @param language + * @throws Exception if error + */ + private void createXLSX(ReportEngine reportEngine, File outFile, Language language) + throws Exception + { + LayoutEngine layout = reportEngine.getLayout(); + MPrintFormat printFormat = reportEngine.getPrintFormat(); + Boolean [] colSuppressRepeats = layout == null || layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(printFormat):layout.colSuppressRepeats; + Map childFormats = layout != null ? layout.getChildPrintFormatDetails() : null; + PrintDataXLSXExporter exp = new PrintDataXLSXExporter(reportEngine.getPrintData(), printFormat, childFormats, colSuppressRepeats, reportEngine.getQuery()); + exp.export(outFile, language); + } + + @Override + public Class getConfigurationType() { + return XLSXReportRendererConfiguration.class; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/XLSXReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/XLSXReportRendererConfiguration.java new file mode 100644 index 0000000000..07e14541df --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/XLSXReportRendererConfiguration.java @@ -0,0 +1,72 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; + +import org.compiere.util.Language; + +/** + * Configuration for excel open xml (xlsx) content renderer + */ +public class XLSXReportRendererConfiguration implements IReportRendererConfiguration { + + public static final String ID = "XLSX"; + public static final String FILE_EXTENSION = "xlsx"; + + private File file; + private Language language; + + public XLSXReportRendererConfiguration() { + } + + @Override + public File getOutputFile() { + return file; + } + + /** + * Set output file + * @param file + */ + public XLSXReportRendererConfiguration setOutputFile(File file) { + this.file = file; + return this; + } + + /** + * Set content language + * @param language + */ + public XLSXReportRendererConfiguration setLanguage(Language language) { + this.language = language; + return this; + } + + /** + * Get content language + * @return content language + */ + public Language getLanguage() { + return language; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/XMLReportRenderer.java b/org.adempiere.base/src/org/idempiere/print/renderer/XMLReportRenderer.java new file mode 100644 index 0000000000..3c96b74177 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/XMLReportRenderer.java @@ -0,0 +1,118 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.logging.Level; + +import javax.xml.transform.stream.StreamResult; + +import org.compiere.print.ReportEngine; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Ini; +import org.compiere.util.Msg; +import org.osgi.service.component.annotations.Component; + +import com.google.common.net.MediaType; + +/** + * XML content rendering service for report engine + */ +@Component(service = IReportRenderer.class, immediate = true) +public class XMLReportRenderer implements IReportRenderer { + + private static final CLogger log = CLogger.getCLogger(XMLReportRenderer.class); + + public XMLReportRenderer() { + } + + @Override + public String getId() { + return XMLReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return Msg.getMsg(Env.getCtx(), "FileXML"); + } + + @Override + public String getContentType() { + return MediaType.XML_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return XMLReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public void renderReport(ReportEngine reportEngine, XMLReportRendererConfiguration configuration) { + try { + Writer fw = null; + if (configuration.getOutputFile() != null) + fw = new OutputStreamWriter(new FileOutputStream(configuration.getOutputFile(), false), Ini.getCharset()); + else + fw = configuration.getOutputWriter(); + createXML(reportEngine, fw); + } + catch (FileNotFoundException fnfe) { + log.log(Level.SEVERE, "(f) - " + fnfe.toString()); + } + catch (Exception e) { + log.log(Level.SEVERE, "(f)", e); + } + } + + @Override + public boolean isBinary() { + return false; + } + + /** + * Write XML content to writer + * @param reportEngine + * @param writer writer + */ + public void createXML (ReportEngine reportEngine, Writer writer) + { + try + { + reportEngine.getPrintData().createXML(new StreamResult(writer)); + writer.flush(); + writer.close(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "(w)", e); + } + } // createXML + + @Override + public Class getConfigurationType() { + return XMLReportRendererConfiguration.class; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/XMLReportRendererConfiguration.java b/org.adempiere.base/src/org/idempiere/print/renderer/XMLReportRendererConfiguration.java new file mode 100644 index 0000000000..ec81db797e --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/XMLReportRendererConfiguration.java @@ -0,0 +1,68 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.print.renderer; + +import java.io.File; +import java.io.Writer; + +/** + * Configuration for XML content renderer + */ +public class XMLReportRendererConfiguration implements IReportRendererConfiguration { + + public final static String ID = "XML"; + public static final String FILE_EXTENSION = "xml"; + + private File file; + private Writer writer; + + public XMLReportRendererConfiguration() { + } + + @Override + public File getOutputFile() { + return file; + } + + /** + * Set output file + * @param file + */ + public XMLReportRendererConfiguration setOutputFile(File file) { + this.file = file; + return this; + } + + @Override + public Writer getOutputWriter() { + return writer; + } + + /** + * Set output writer + * @param writer + */ + public XMLReportRendererConfiguration setOutputWriter(Writer writer) { + this.writer = writer; + return this; + } +} diff --git a/org.adempiere.base/src/org/idempiere/print/renderer/package-info.java b/org.adempiere.base/src/org/idempiere/print/renderer/package-info.java new file mode 100644 index 0000000000..186fe2335c --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/print/renderer/package-info.java @@ -0,0 +1,26 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ + +/** + * Provide interface and classes for report engine content rendering service + */ +package org.idempiere.print.renderer; \ No newline at end of file diff --git a/org.adempiere.server-feature/server.product.launch b/org.adempiere.server-feature/server.product.launch index 75227c70df..effea2a776 100644 --- a/org.adempiere.server-feature/server.product.launch +++ b/org.adempiere.server-feature/server.product.launch @@ -448,7 +448,9 @@ + + diff --git a/org.adempiere.ui.zk/META-INF/MANIFEST.MF b/org.adempiere.ui.zk/META-INF/MANIFEST.MF index ce3ee03f9a..192f45002e 100644 --- a/org.adempiere.ui.zk/META-INF/MANIFEST.MF +++ b/org.adempiere.ui.zk/META-INF/MANIFEST.MF @@ -115,6 +115,7 @@ Export-Package: fi.jawsy.jawwa.zk.atmosphere, org.adempiere.webui.window, org.idempiere.ui.zk.annotation, org.idempiere.ui.zk.media, + org.idempiere.ui.zk.report, org.idempiere.ui.zk.websocket, org.zkforge.ckez, org.zkforge.keylistener, diff --git a/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.CSVReportViewerRenderer.xml b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.CSVReportViewerRenderer.xml new file mode 100644 index 0000000000..78050b9430 --- /dev/null +++ b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.CSVReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.HTMLReportViewerRenderer.xml b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.HTMLReportViewerRenderer.xml new file mode 100644 index 0000000000..57c988c080 --- /dev/null +++ b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.HTMLReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.PDFReportViewerRenderer.xml b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.PDFReportViewerRenderer.xml new file mode 100644 index 0000000000..2ae6479699 --- /dev/null +++ b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.PDFReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.PSReportViewerRenderer.xml b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.PSReportViewerRenderer.xml new file mode 100644 index 0000000000..902c41abe3 --- /dev/null +++ b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.PSReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.SSVReportViewerRenderer.xml b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.SSVReportViewerRenderer.xml new file mode 100644 index 0000000000..720298f643 --- /dev/null +++ b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.SSVReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.TabDelimitedReportViewerRenderer.xml b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.TabDelimitedReportViewerRenderer.xml new file mode 100644 index 0000000000..df90a03eab --- /dev/null +++ b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.TabDelimitedReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.XLSReportViewerRenderer.xml b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.XLSReportViewerRenderer.xml new file mode 100644 index 0000000000..37db94c56e --- /dev/null +++ b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.XLSReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.XLSXReportViewerRenderer.xml b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.XLSXReportViewerRenderer.xml new file mode 100644 index 0000000000..1800ad035a --- /dev/null +++ b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.XLSXReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.XMLReportViewerRenderer.xml b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.XMLReportViewerRenderer.xml new file mode 100644 index 0000000000..8d8c5ee69f --- /dev/null +++ b/org.adempiere.ui.zk/OSGI-INF/org.idempiere.ui.zk.report.XMLReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/Extensions.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/Extensions.java index bf10054b7b..80ea7a50c1 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/Extensions.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/Extensions.java @@ -47,6 +47,7 @@ import org.compiere.model.MDashboardContent; import org.compiere.util.CCache; import org.idempiere.ui.zk.media.IMediaView; import org.idempiere.ui.zk.media.IMediaViewProvider; +import org.idempiere.ui.zk.report.IReportViewerRenderer; import org.zkoss.zk.ui.Component; /** @@ -390,4 +391,12 @@ public class Extensions { } return null; } // getADTabPanel + + /** + * @return list of {@link IReportViewerRenderer} + */ + public static List getReportViewerRenderers() { + List> references = Service.locator().list(IReportViewerRenderer.class, null, null).getServiceReferences(); + return references.stream().filter(e -> e.getService() != null).map(e -> e.getService()).collect(Collectors.toCollection(ArrayList::new)); + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WReportExportDialog.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WReportExportDialog.java index 2785304970..0ff45c2f00 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WReportExportDialog.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WReportExportDialog.java @@ -25,6 +25,8 @@ **********************************************************************/ package org.adempiere.webui.window; +import java.util.Arrays; +import java.util.Comparator; import java.util.logging.Level; import org.adempiere.webui.LayoutUtils; @@ -35,6 +37,7 @@ import org.adempiere.webui.component.Listbox; import org.adempiere.webui.component.Window; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.util.ZKUpdateUtil; +import org.adempiere.webui.window.IReportViewerExportSource.ExportFormat; import org.compiere.model.MSysConfig; import org.compiere.util.CLogger; import org.compiere.util.Env; @@ -82,27 +85,18 @@ public class WReportExportDialog extends Window implements EventListener cboType.setMold("select"); cboType.getItems().clear(); - cboType.appendItem("ps" + " - " + Msg.getMsg(Env.getCtx(), "FilePS"), "ps"); - cboType.appendItem("xml" + " - " + Msg.getMsg(Env.getCtx(), "FileXML"), "xml"); - cboType.appendItem("pdf" + " - " + Msg.getMsg(Env.getCtx(), "FilePDF"), "pdf"); - cboType.appendItem("html" + " - " + Msg.getMsg(Env.getCtx(), "FileHTML"), "html"); - cboType.appendItem("txt" + " - " + Msg.getMsg(Env.getCtx(), "FileTXT"), "txt"); - cboType.appendItem("ssv" + " - " + Msg.getMsg(Env.getCtx(), "FileSSV"), "ssv"); - cboType.appendItem("csv" + " - " + Msg.getMsg(Env.getCtx(), "FileCSV"), "csv"); - cboType.appendItem("xls" + " - " + Msg.getMsg(Env.getCtx(), "FileXLS"), "xls"); - cboType.appendItem("xlsx" + " - " + Msg.getMsg(Env.getCtx(), "FileXLSX"), "xlsx"); - - String contentType = viewer.getContentType(); - if (IReportViewerExportSource.PDF_MIME_TYPE.equals(contentType)) { - cboType.setSelectedIndex(2); - } else if (IReportViewerExportSource.HTML_MIME_TYPE.equals(contentType)) { - cboType.setSelectedIndex(3); - } else if (IReportViewerExportSource.EXCEL_MIME_TYPE.equals(contentType)) { - cboType.setSelectedIndex(7); - } else if (IReportViewerExportSource.CSV_MIME_TYPE.equals(contentType)) { - cboType.setSelectedIndex(6); - } else if (IReportViewerExportSource.EXCEL_XML_MIME_TYPE.equals(contentType)) { - cboType.setSelectedIndex(8); + ExportFormat[] exportFormats = viewer.getExportFormats(); + Arrays.sort(exportFormats, new Comparator() { + @Override + public int compare(ExportFormat ef0, ExportFormat ef1) { + return ef0.label.compareTo(ef1.label); + } + }); + for(ExportFormat exportFormat : exportFormats) { + ListItem item = cboType.appendItem(exportFormat.label, exportFormat); + if (viewer.getContentType().equals(exportFormat.contentType) && viewer.getFileExtension().equals(exportFormat.extension)) { + item.setSelected(true); + } } Hbox hb = new Hbox(); @@ -161,51 +155,11 @@ public class WReportExportDialog extends Window implements EventListener return; } - String ext = li.getValue().toString(); - if (ext.equals(IReportViewerExportSource.PDF_FILE_EXT)) - { - media = viewer.getMedia(IReportViewerExportSource.PDF_MIME_TYPE, IReportViewerExportSource.PDF_FILE_EXT); - } - else if (ext.equals(IReportViewerExportSource.POSTSCRIPT_FILE_EXT)) - { - media = viewer.getMedia(IReportViewerExportSource.POSTSCRIPT_MIME_TYPE, IReportViewerExportSource.POSTSCRIPT_FILE_EXT); - } - else if (ext.equals(IReportViewerExportSource.XML_FILE_EXT)) - { - media = viewer.getMedia(IReportViewerExportSource.XML_MIME_TYPE, IReportViewerExportSource.XML_FILE_EXT); - } - else if (ext.equals(IReportViewerExportSource.CSV_FILE_EXT)) - { - media = viewer.getMedia(IReportViewerExportSource.CSV_MIME_TYPE, IReportViewerExportSource.CSV_FILE_EXT); - } - else if (ext.equals(IReportViewerExportSource.SSV_FILE_EXT)) - { - media = viewer.getMedia(IReportViewerExportSource.CSV_MIME_TYPE, IReportViewerExportSource.SSV_FILE_EXT); - } - else if (ext.equals(IReportViewerExportSource.TEXT_FILE_EXT)) - { - media = viewer.getMedia(IReportViewerExportSource.TEXT_MIME_TYPE, IReportViewerExportSource.TEXT_FILE_EXT); - } - else if (ext.equals(IReportViewerExportSource.HTML_FILE_EXT) || ext.equals("htm")) - { - media = viewer.getMedia(IReportViewerExportSource.HTML_MIME_TYPE, IReportViewerExportSource.HTML_FILE_EXT); - } - else if (ext.equals(IReportViewerExportSource.EXCEL_XML_FILE_EXT)) - { - media = viewer.getMedia(IReportViewerExportSource.EXCEL_XML_MIME_TYPE, IReportViewerExportSource.EXCEL_XML_FILE_EXT); - } - else if (ext.equals(IReportViewerExportSource.EXCEL_FILE_EXT)) - { - media = viewer.getMedia(IReportViewerExportSource.EXCEL_MIME_TYPE, IReportViewerExportSource.EXCEL_FILE_EXT); - } - else - { - Dialog.error(-1, "FileInvalidExtension"); - return; - } + ExportFormat exportFormat = li.getValue(); + media = viewer.getMedia(exportFormat.contentType, exportFormat.extension); onClose(); - Filedownload.save(media, viewer.getReportName() + "." + ext); + Filedownload.save(media, viewer.getReportName() + "." + exportFormat.extension); } catch (Exception e) { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkJRViewer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkJRViewer.java index 87f4ae411b..db8be74326 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkJRViewer.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkJRViewer.java @@ -158,12 +158,12 @@ public class ZkJRViewer extends Window implements EventListener, ITabOnCl private boolean isUseEscForTabClosing = MSysConfig.getBooleanValue(MSysConfig.USE_ESC_FOR_TAB_CLOSING, false, Env.getAD_Client_ID(Env.getCtx())); private final ExportFormat[] exportFormats = new ExportFormat[] { - new ExportFormat(PDF_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FilePDF"), PDF_FILE_EXT, PDF_MIME_TYPE), - new ExportFormat(HTML_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileHTML"), HTML_FILE_EXT, HTML_MIME_TYPE), - new ExportFormat(CSV_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileCSV"), CSV_FILE_EXT, CSV_MIME_TYPE), - new ExportFormat(EXCEL_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileXLS"), EXCEL_FILE_EXT, EXCEL_MIME_TYPE), - new ExportFormat(EXCEL_XML_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileXLSX"), EXCEL_XML_FILE_EXT, EXCEL_XML_MIME_TYPE), - new ExportFormat(SSV_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileSSV"), SSV_FILE_EXT, CSV_MIME_TYPE) + new ExportFormat(Msg.getMsg(Env.getCtx(), "FilePDF"), PDF_FILE_EXT, PDF_MIME_TYPE), + new ExportFormat(Msg.getMsg(Env.getCtx(), "FileHTML"), HTML_FILE_EXT, HTML_MIME_TYPE), + new ExportFormat(Msg.getMsg(Env.getCtx(), "FileCSV"), CSV_FILE_EXT, CSV_MIME_TYPE), + new ExportFormat(Msg.getMsg(Env.getCtx(), "FileXLS"), EXCEL_FILE_EXT, EXCEL_MIME_TYPE), + new ExportFormat(Msg.getMsg(Env.getCtx(), "FileXLSX"), EXCEL_XML_FILE_EXT, EXCEL_XML_MIME_TYPE), + new ExportFormat(Msg.getMsg(Env.getCtx(), "FileSSV"), SSV_FILE_EXT, CSV_MIME_TYPE) }; private Center center; diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkReportViewer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkReportViewer.java index eddf6bd277..03ecb78c7e 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkReportViewer.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/ZkReportViewer.java @@ -16,20 +16,22 @@ *****************************************************************************/ package org.adempiere.webui.window; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.StringWriter; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Calendar; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; -import java.util.function.Supplier; +import java.util.TreeMap; import java.util.logging.Level; import javax.activation.FileDataSource; @@ -67,7 +69,6 @@ import org.adempiere.webui.event.DrillEvent.DrillData; import org.adempiere.webui.event.ZoomEvent; import org.adempiere.webui.panel.ADForm; import org.adempiere.webui.panel.StatusBarPanel; -import org.adempiere.webui.report.HTMLExtension; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.util.IServerPushCallback; @@ -103,8 +104,14 @@ import org.compiere.util.KeyNamePair; import org.compiere.util.Language; import org.compiere.util.Msg; import org.compiere.util.Util; +import org.idempiere.print.renderer.CSVReportRendererConfiguration; +import org.idempiere.print.renderer.HTMLReportRendererConfiguration; +import org.idempiere.print.renderer.PDFReportRendererConfiguration; +import org.idempiere.print.renderer.XLSReportRendererConfiguration; +import org.idempiere.print.renderer.XLSXReportRendererConfiguration; import org.idempiere.ui.zk.media.IMediaView; import org.idempiere.ui.zk.media.WMediaOptions; +import org.idempiere.ui.zk.report.IReportViewerRenderer; import org.zkoss.util.media.AMedia; import org.zkoss.util.media.Media; import org.zkoss.zk.au.out.AuScript; @@ -159,11 +166,11 @@ public class ZkReportViewer extends Window implements EventListener, IRep */ private static final long serialVersionUID = 3732290698059632847L; - protected static final String CSV_OUTPUT_TYPE = "CSV"; - protected static final String HTML_OUTPUT_TYPE = "HTML"; - protected static final String PDF_OUTPUT_TYPE = "PDF"; - protected static final String XLS_OUTPUT_TYPE = "XLS"; - protected static final String XLSX_OUTPUT_TYPE = "XLSX"; + protected static final String CSV_OUTPUT_TYPE = CSVReportRendererConfiguration.ID; + protected static final String HTML_OUTPUT_TYPE = HTMLReportRendererConfiguration.ID; + protected static final String PDF_OUTPUT_TYPE = PDFReportRendererConfiguration.ID; + protected static final String XLS_OUTPUT_TYPE = XLSReportRendererConfiguration.ID; + protected static final String XLSX_OUTPUT_TYPE = XLSXReportRendererConfiguration.ID; /** Window No */ protected int m_WindowNo = -1; @@ -227,20 +234,9 @@ public class ZkReportViewer extends Window implements EventListener, IRep protected Map uploadServicesMap = new HashMap<>(); /** Row count label */ private Label rowCount; - - private final ExportFormat[] exportFormats = new ExportFormat[] { - new ExportFormat(POSTSCRIPT_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FilePS"), POSTSCRIPT_FILE_EXT, POSTSCRIPT_MIME_TYPE), - new ExportFormat(XML_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileXML"), XML_FILE_EXT, XML_MIME_TYPE), - new ExportFormat(PDF_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FilePDF"), PDF_FILE_EXT, PDF_MIME_TYPE), - new ExportFormat(HTML_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileHTML"), HTML_FILE_EXT, HTML_MIME_TYPE), - new ExportFormat(TEXT_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileTXT"), TEXT_FILE_EXT, TEXT_MIME_TYPE), - new ExportFormat(SSV_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileSSV"), SSV_FILE_EXT, CSV_MIME_TYPE), - new ExportFormat(CSV_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileCSV"), CSV_FILE_EXT, CSV_MIME_TYPE), - new ExportFormat(EXCEL_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileXLS"), EXCEL_FILE_EXT, EXCEL_MIME_TYPE), - new ExportFormat(EXCEL_XML_FILE_EXT + " - " + Msg.getMsg(Env.getCtx(), "FileXLSX"), EXCEL_XML_FILE_EXT, EXCEL_XML_MIME_TYPE) - }; - private final Map> mediaSuppliers = new HashMap>(); + private final Map exportMap = new LinkedHashMap<>(); + private final Map rendererMap = new TreeMap<>(); private Center center; @@ -284,136 +280,34 @@ public class ZkReportViewer extends Window implements EventListener, IRep /** * @param contentType * @param fileExtension - * @return contentType + ; + fileExtension + * @return renderer id */ - private String toMediaType(String contentType, String fileExtension) { - return contentType + ";" + fileExtension; + private String toRendererId(String contentType, String fileExtension) { + for(Map.Entry entry : exportMap.entrySet()) { + if (entry.getKey().contentType.equals(contentType) && entry.getKey().extension.equals(fileExtension)) + return entry.getValue(); + } + return null; } /** * Create media supplier for supported format (pdf, html, etc) */ private void initMediaSuppliers() { - mediaSuppliers.put(toMediaType(PDF_MIME_TYPE, PDF_FILE_EXT), () -> { - try { - String path = System.getProperty("java.io.tmpdir"); - String prefix = makePrefix(m_reportEngine.getName()); - if (prefix.length() < 3) - prefix += "_".repeat(3-prefix.length()); - if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Path="+path + " Prefix="+prefix); - File file = FileUtil.createTempFile(prefix, "."+PDF_FILE_EXT, new File(path)); - m_reportEngine.createPDF(file); - return new AMedia(file.getName(), PDF_FILE_EXT, PDF_MIME_TYPE, file, true); - } catch (Exception e) { - if (e instanceof RuntimeException) - throw (RuntimeException)e; - else - throw new RuntimeException(e); + List renderers = Extensions.getReportViewerRenderers(); + Collections.sort(renderers, new Comparator() { + @Override + public int compare(IReportViewerRenderer r1, IReportViewerRenderer r2) { + return r1.getExportLabel().compareTo(r2.getExportLabel()); } }); - - mediaSuppliers.put(toMediaType(HTML_MIME_TYPE, HTML_FILE_EXT), () -> { - try { - String path = System.getProperty("java.io.tmpdir"); - String prefix = makePrefix(m_reportEngine.getName()); - if (prefix.length() < 3) - prefix += "_".repeat(3-prefix.length()); - if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Path="+path + " Prefix="+prefix); - File file = FileUtil.createTempFile(prefix, "."+HTML_FILE_EXT, new File(path)); - String contextPath = Executions.getCurrent().getContextPath(); - m_reportEngine.createHTML(file, false, m_reportEngine.getPrintFormat().getLanguage(), new HTMLExtension(contextPath, "rp", getUuid())); - return new AMedia(file.getName(), HTML_FILE_EXT, HTML_MIME_TYPE, file, false); - } catch (Exception e) { - if (e instanceof RuntimeException) - throw (RuntimeException)e; - else - throw new RuntimeException(e); + for(IReportViewerRenderer renderer : renderers) { + if (renderer.isExport()) { + ExportFormat exportFormat = new ExportFormat(renderer.getExportLabel(), renderer.getFileExtension(), renderer.getContentType()); + exportMap.put(exportFormat, renderer.getId()); } - }); - - mediaSuppliers.put(toMediaType(EXCEL_MIME_TYPE, EXCEL_FILE_EXT), () -> { - try { - String path = System.getProperty("java.io.tmpdir"); - String prefix = makePrefix(m_reportEngine.getName()); - if (prefix.length() < 3) - prefix += "_".repeat(3-prefix.length()); - if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Path="+path + " Prefix="+prefix); - File file = FileUtil.createTempFile(prefix, "."+EXCEL_FILE_EXT, new File(path)); - m_reportEngine.createXLS(file, m_reportEngine.getPrintFormat().getLanguage()); - return new AMedia(file.getName(), EXCEL_FILE_EXT, EXCEL_MIME_TYPE, file, true); - } catch (Exception e) { - if (e instanceof RuntimeException) - throw (RuntimeException)e; - else - throw new RuntimeException(e); - } - }); - - mediaSuppliers.put(toMediaType(CSV_MIME_TYPE, CSV_FILE_EXT), () -> { - try { - String path = System.getProperty("java.io.tmpdir"); - String prefix = makePrefix(m_reportEngine.getName()); - if (log.isLoggable(Level.FINE)) - { - log.log(Level.FINE, "Path="+path + " Prefix="+prefix); - } - File file = FileUtil.createTempFile(prefix, "."+CSV_FILE_EXT, new File(path)); - m_reportEngine.createCSV(file, ',', AEnv.getLanguage(Env.getCtx())); - return new AMedia(file.getName(), CSV_FILE_EXT, CSV_MIME_TYPE, file, false); - } catch (Exception e) { - if (e instanceof RuntimeException) - throw (RuntimeException)e; - else - throw new RuntimeException(e); - } - }); - - mediaSuppliers.put(toMediaType(EXCEL_XML_MIME_TYPE, EXCEL_XML_FILE_EXT), () -> { - try { - String path = System.getProperty("java.io.tmpdir"); - String prefix = makePrefix(m_reportEngine.getName()); - if (log.isLoggable(Level.FINE)) - { - log.log(Level.FINE, "Path=" + path + " Prefix=" + prefix); - } - File file = FileUtil.createTempFile(prefix, "."+EXCEL_XML_FILE_EXT, new File(path)); - m_reportEngine.createXLSX(file, m_reportEngine.getPrintFormat().getLanguage()); - return new AMedia(file.getName(), EXCEL_XML_FILE_EXT, EXCEL_XML_MIME_TYPE, file, true); - } catch (Exception e) { - if (e instanceof RuntimeException) - throw (RuntimeException)e; - else - throw new RuntimeException(e); - } - }); - - mediaSuppliers.put(toMediaType(POSTSCRIPT_MIME_TYPE, POSTSCRIPT_FILE_EXT), () -> { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - m_reportEngine.createPS(baos); - byte[] data = baos.toByteArray(); - return new AMedia(m_reportEngine.getName() + "."+POSTSCRIPT_FILE_EXT, POSTSCRIPT_FILE_EXT, POSTSCRIPT_MIME_TYPE, data); - }); - - mediaSuppliers.put(toMediaType(XML_MIME_TYPE, XML_FILE_EXT), () -> { - StringWriter sw = new StringWriter(); - m_reportEngine.createXML(sw); - String data = sw.getBuffer().toString(); - return new AMedia(m_reportEngine.getName() + "."+XML_FILE_EXT, XML_FILE_EXT, XML_MIME_TYPE, data); - }); - - mediaSuppliers.put(toMediaType(CSV_MIME_TYPE, SSV_FILE_EXT), () -> { - StringWriter sw = new StringWriter(); - m_reportEngine.createCSV(sw, ';', m_reportEngine.getPrintFormat().getLanguage()); - String data = sw.getBuffer().toString(); - return new AMedia(m_reportEngine.getName() + "."+SSV_FILE_EXT, SSV_FILE_EXT, CSV_MIME_TYPE, data); - }); - - mediaSuppliers.put(toMediaType(TEXT_MIME_TYPE, TEXT_FILE_EXT), () -> { - StringWriter sw = new StringWriter(); - m_reportEngine.createCSV(sw, '\t', m_reportEngine.getPrintFormat().getLanguage()); - String data = sw.getBuffer().toString(); - return new AMedia(m_reportEngine.getName() + "."+TEXT_FILE_EXT, TEXT_FILE_EXT, TEXT_MIME_TYPE, data); - }); + rendererMap.put(renderer.getId(), renderer); + } } @Override @@ -463,14 +357,11 @@ public class ZkReportViewer extends Window implements EventListener, IRep ZKUpdateUtil.setWidth(toolBar, "100%"); previewType.setMold("select"); - previewType.appendItem(HTML_OUTPUT_TYPE, HTML_OUTPUT_TYPE); - previewType.appendItem(PDF_OUTPUT_TYPE, PDF_OUTPUT_TYPE); - - if ( m_isCanExport ) - { - previewType.appendItem(XLS_OUTPUT_TYPE, XLS_OUTPUT_TYPE); - previewType.appendItem(CSV_OUTPUT_TYPE, CSV_OUTPUT_TYPE); - previewType.appendItem(XLSX_OUTPUT_TYPE, XLSX_OUTPUT_TYPE); + for(String id : rendererMap.keySet()) { + IReportViewerRenderer renderer = rendererMap.get(id); + if (!renderer.isPreview(m_isCanExport)) + continue; + previewType.appendItem(renderer.getPreviewLabel(), renderer.getId()); } toolBar.appendChild(previewType); @@ -478,41 +369,33 @@ public class ZkReportViewer extends Window implements EventListener, IRep toolBar.appendChild(new Separator("vertical")); - int pTypeIndex = 0; - + String type = null; if (m_reportEngine.getReportType() != null) { - if (m_reportEngine.getReportType().equals(PDF_OUTPUT_TYPE)) - pTypeIndex = 1; - else if (m_reportEngine.getReportType().equals(XLS_OUTPUT_TYPE) && m_isCanExport) - pTypeIndex = 2; - else if (m_reportEngine.getReportType().equals(CSV_OUTPUT_TYPE) && m_isCanExport) - pTypeIndex = 3; - else if (m_reportEngine.getReportType().equals(XLSX_OUTPUT_TYPE) && m_isCanExport) - pTypeIndex = 4; + type = m_reportEngine.getReportType(); } else { //set default type - String type = m_reportEngine.getPrintFormat().isForm() + type = m_reportEngine.getPrintFormat().isForm() // a42niem - provide explicit default and check on client/org specifics ? MSysConfig.getValue(MSysConfig.ZK_REPORT_FORM_OUTPUT_TYPE,PDF_OUTPUT_TYPE,Env.getAD_Client_ID(m_ctx),Env.getAD_Org_ID(m_ctx)) - : MSysConfig.getValue(MSysConfig.ZK_REPORT_TABLE_OUTPUT_TYPE,PDF_OUTPUT_TYPE,Env.getAD_Client_ID(m_ctx),Env.getAD_Org_ID(m_ctx)); - - if (HTML_OUTPUT_TYPE.equals(type)) { - pTypeIndex = 0; - } else if (PDF_OUTPUT_TYPE.equals(type)) { - pTypeIndex = 1; - } else if (XLS_OUTPUT_TYPE.equals(type) && m_isCanExport) { - pTypeIndex = 2; - } else if (CSV_OUTPUT_TYPE.equals(type) && m_isCanExport) { - pTypeIndex = 3; - } else if (XLSX_OUTPUT_TYPE.equals(type) && m_isCanExport) { - pTypeIndex = 4; - } + : MSysConfig.getValue(MSysConfig.ZK_REPORT_TABLE_OUTPUT_TYPE,PDF_OUTPUT_TYPE,Env.getAD_Client_ID(m_ctx),Env.getAD_Org_ID(m_ctx)); + } + + int defaultIndex = -1; + for(int i = 0; i < previewType.getItemCount(); i++) { + ListItem item = previewType.getItemAtIndex(i); + if (item.getValue().equals(type)) { + previewType.setSelectedIndex(i); + break; + } else if (item.getValue().equals("PDF")) { + defaultIndex = i; + } + } + if (previewType.getSelectedIndex() < 0) { + previewType.setSelectedIndex(defaultIndex >= 0 ? defaultIndex : 0); } - - previewType.setSelectedIndex(pTypeIndex); Vlayout toolbarPopupLayout = null; if (ClientInfo.maxWidth(ClientInfo.SMALL_WIDTH-1)) @@ -921,17 +804,7 @@ public class ZkReportViewer extends Window implements EventListener, IRep private void renderReport() { media = null; Listitem selected = previewType.getSelectedItem(); - if (selected == null || PDF_OUTPUT_TYPE.equals(selected.getValue())) { - new PDFRendererRunnable(this).run(); - } else if (HTML_OUTPUT_TYPE.equals(selected.getValue())) { - new HTMLRendererRunnable(this).run(); - } else if (XLS_OUTPUT_TYPE.equals(selected.getValue())) { - new XLSRendererRunnable(this).run(); - } else if (CSV_OUTPUT_TYPE.equals(selected.getValue())) { - new CSVRendererRunnable(this).run(); - } else if (XLSX_OUTPUT_TYPE.equals(selected.getValue())) { - new XLSXRendererRunnable(this).run(); - } + new RendererRunnable(this, selected.getValue()).run(); } /** @@ -1044,24 +917,6 @@ public class ZkReportViewer extends Window implements EventListener, IRep } } - /** - * Create file name prefix from name - * @param name - * @return file name prefix from name - */ - private String makePrefix(String name) { - StringBuilder prefix = new StringBuilder(); - char[] nameArray = name.toCharArray(); - for (char ch : nameArray) { - if (Character.isLetterOrDigit(ch)) { - prefix.append(ch); - } else { - prefix.append("_"); - } - } - return prefix.toString(); - } - /** * Dynamic Init */ @@ -1975,22 +1830,31 @@ public class ZkReportViewer extends Window implements EventListener, IRep }, new Event("onClearTabOnCloseHandler")); } - static class PDFRendererRunnable extends ZkContextRunnable implements IServerPushCallback { + private static class RendererRunnable extends ZkContextRunnable implements IServerPushCallback { private ZkReportViewer viewer; + private String rendererId; - public PDFRendererRunnable(ZkReportViewer viewer) { - super(); + public RendererRunnable(ZkReportViewer viewer, String rendererId) { this.viewer = viewer; + this.rendererId = rendererId; + } + + @Override + public void updateUI() { + viewer.onPreviewReport(); } @Override protected void doRun() { try { viewer.m_reportEngine.initName(); - if (!ArchiveEngine.isValid(viewer.m_reportEngine.getLayout())) - log.warning("Cannot archive Document"); - viewer.createNewMedia(PDF_MIME_TYPE, PDF_FILE_EXT); + List archiveList = Arrays.asList(PDF_OUTPUT_TYPE, HTML_OUTPUT_TYPE, XLS_OUTPUT_TYPE, XLSX_OUTPUT_TYPE); + if (archiveList.contains(rendererId)) { + if (!ArchiveEngine.isValid(viewer.m_reportEngine.getLayout())) + log.warning("Cannot archive Document"); + } + viewer.createNewMedia(rendererId); } catch (Exception e) { if (e instanceof RuntimeException) throw (RuntimeException)e; @@ -2003,183 +1867,33 @@ public class ZkReportViewer extends Window implements EventListener, IRep } } } - - @Override - public void updateUI() { - viewer.onPreviewReport(); - } } - static class HTMLRendererRunnable extends ZkContextRunnable implements IServerPushCallback { - - private ZkReportViewer viewer; - public HTMLRendererRunnable(ZkReportViewer viewer) { - super(); - this.viewer = viewer; - - } - - @Override - protected void doRun() { - try { - viewer.m_reportEngine.initName(); - if (!ArchiveEngine.isValid(viewer.m_reportEngine.getLayout())) - log.warning("Cannot archive Document"); - viewer.createNewMedia(HTML_MIME_TYPE, HTML_FILE_EXT); - } catch (Exception e) { - if (e instanceof RuntimeException) - throw (RuntimeException)e; - else - throw new RuntimeException(e); - } finally { - Desktop desktop = AEnv.getDesktop(); - if (desktop != null && desktop.isAlive()) { - new ServerPushTemplate(desktop).executeAsync(this); - } - } - } - - @Override - public void updateUI() { - viewer.onPreviewReport(); - } - } - - static class XLSRendererRunnable extends ZkContextRunnable implements IServerPushCallback { - - private ZkReportViewer viewer; - - public XLSRendererRunnable(ZkReportViewer viewer) { - super(); - this.viewer = viewer; - } - - @Override - protected void doRun() { - try { - viewer.m_reportEngine.initName(); - if (!ArchiveEngine.isValid(viewer.m_reportEngine.getLayout())) - log.warning("Cannot archive Document"); - viewer.createNewMedia(EXCEL_MIME_TYPE, EXCEL_FILE_EXT); - } catch (Exception e) { - if (e instanceof RuntimeException) - throw (RuntimeException)e; - else - throw new RuntimeException(e); - } finally { - Desktop desktop = AEnv.getDesktop(); - if (desktop != null && desktop.isAlive()) { - new ServerPushTemplate(desktop).executeAsync(this); - } - } - } - - @Override - public void updateUI() { - viewer.onPreviewReport(); - } - - } - - static class CSVRendererRunnable extends ZkContextRunnable implements IServerPushCallback { - - private ZkReportViewer viewer; - - public CSVRendererRunnable(ZkReportViewer viewer) { - super(); - this.viewer = viewer; - } - - @Override - protected void doRun() { - try { - viewer.m_reportEngine.initName(); - viewer.createNewMedia(CSV_MIME_TYPE,CSV_FILE_EXT); - } catch (Exception e) { - if (e instanceof RuntimeException) - throw (RuntimeException)e; - else - throw new RuntimeException(e); - } finally { - Desktop desktop = AEnv.getDesktop(); - if (desktop != null && desktop.isAlive()) { - new ServerPushTemplate(desktop).executeAsync(this); - } - } - } - - @Override - public void updateUI() { - viewer.onPreviewReport(); - } - - } - - protected static class XLSXRendererRunnable extends ZkContextRunnable implements IServerPushCallback - { - - private ZkReportViewer viewer; - - public XLSXRendererRunnable(ZkReportViewer viewer) - { - super(); - this.viewer = viewer; - } - - @Override - protected void doRun() - { - try - { - viewer.m_reportEngine.initName(); - if (!ArchiveEngine.isValid(viewer.m_reportEngine.getLayout())) - log.warning("Cannot archive Document"); - viewer.createNewMedia(EXCEL_XML_MIME_TYPE, EXCEL_XML_FILE_EXT); - } - catch (Exception e) - { - if (e instanceof RuntimeException) - throw (RuntimeException) e; - else - throw new RuntimeException(e); - } - finally - { - Desktop desktop = AEnv.getDesktop(); - if (desktop != null && desktop.isAlive()) - { - new ServerPushTemplate(desktop).executeAsync(this); - } - } - } - - @Override - public void updateUI() - { - viewer.onPreviewReport(); - } - - } - - private void createNewMedia(String contentType, String fileExtension) { + private void createNewMedia(String rendererId) { media = null; - media = getMedia(contentType, fileExtension); + media = getMedia(rendererId); } @Override public AMedia getMedia(String contentType, String fileExtension) { - if (media != null && media.getContentType().equals(contentType) && media.getFormat().equals(fileExtension)) - return media; + IReportViewerRenderer renderer = rendererMap.get(toRendererId(contentType, fileExtension)); - Supplier supplier = mediaSuppliers.get(toMediaType(contentType, fileExtension)); - return supplier != null ? supplier.get() : null; + if (renderer.isSameContentForExportAndPreview() && media != null + && media.getContentType().equals(contentType) && media.getFormat().equals(fileExtension)) + return media; + + return renderer != null ? renderer.renderMedia(this, true) : null; } + public AMedia getMedia(String rendererId) { + IReportViewerRenderer renderer = rendererMap.get(rendererId); + return renderer != null ? renderer.renderMedia(this, false) : null; + } @Override public ExportFormat[] getExportFormats() { - return exportFormats; + return exportMap.keySet().toArray(new ExportFormat[0]); } @Override @@ -2201,6 +1915,14 @@ public class ZkReportViewer extends Window implements EventListener, IRep public String getReportName() { return m_reportEngine.getName(); } + + /** + * Get report engine + * @return report engine + */ + public ReportEngine getReportEngine() { + return m_reportEngine; + } private void setupFindwindow(FindWindow findWindow) { findWindow.setTitle(null); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/media/Medias.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/media/Medias.java index 3fa2c43860..ac323bc9ec 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/media/Medias.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/media/Medias.java @@ -24,12 +24,21 @@ **********************************************************************/ package org.idempiere.ui.zk.media; +import org.idempiere.print.renderer.CSVReportRendererConfiguration; +import org.idempiere.print.renderer.HTMLReportRendererConfiguration; +import org.idempiere.print.renderer.PDFReportRendererConfiguration; +import org.idempiere.print.renderer.PSReportRendererConfiguration; +import org.idempiere.print.renderer.SSVReportRendererConfiguration; +import org.idempiere.print.renderer.TabDelimitedReportRendererConfiguration; +import org.idempiere.print.renderer.XLSReportRendererConfiguration; +import org.idempiere.print.renderer.XLSXReportRendererConfiguration; +import org.idempiere.print.renderer.XMLReportRendererConfiguration; + import com.google.common.net.MediaType; /** * Constants for media mime type and file extension * @author hengsin - * */ public final class Medias { @@ -42,15 +51,15 @@ public final class Medias { public static final String TEXT_MIME_TYPE = MediaType.PLAIN_TEXT_UTF_8.toString(); public static final String XML_MIME_TYPE = MediaType.XML_UTF_8.toString(); - public static final String CSV_FILE_EXT = "csv"; - public static final String SSV_FILE_EXT = "ssv"; - public static final String EXCEL_FILE_EXT = "xls"; - public static final String EXCEL_XML_FILE_EXT = "xlsx"; - public static final String HTML_FILE_EXT = "html"; - public static final String PDF_FILE_EXT = "pdf"; - public static final String POSTSCRIPT_FILE_EXT = "ps"; - public static final String TEXT_FILE_EXT = "txt"; - public static final String XML_FILE_EXT = "xml"; + public static final String CSV_FILE_EXT = CSVReportRendererConfiguration.FILE_EXTENSION; + public static final String SSV_FILE_EXT = SSVReportRendererConfiguration.FILE_EXTENSION; + public static final String EXCEL_FILE_EXT = XLSReportRendererConfiguration.FILE_EXTENSION; + public static final String EXCEL_XML_FILE_EXT = XLSXReportRendererConfiguration.FILE_EXTENSION; + public static final String HTML_FILE_EXT = HTMLReportRendererConfiguration.FILE_EXTENSION; + public static final String PDF_FILE_EXT = PDFReportRendererConfiguration.FILE_EXTENSION; + public static final String POSTSCRIPT_FILE_EXT = PSReportRendererConfiguration.FILE_EXTENSION; + public static final String TEXT_FILE_EXT = TabDelimitedReportRendererConfiguration.FILE_EXTENSION; + public static final String XML_FILE_EXT = XMLReportRendererConfiguration.FILE_EXTENSION; private Medias() { } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/CSVReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/CSVReportViewerRenderer.java new file mode 100644 index 0000000000..a00f763ace --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/CSVReportViewerRenderer.java @@ -0,0 +1,108 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import java.io.File; +import java.util.logging.Level; + +import org.adempiere.base.Core; +import org.adempiere.webui.apps.AEnv; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.tools.FileUtil; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.idempiere.print.renderer.CSVReportRendererConfiguration; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; + +import com.google.common.net.MediaType; + +/** + * Comma separated content rendering service for report viewer + */ +@Component(service = IReportViewerRenderer.class, immediate = true) +public class CSVReportViewerRenderer implements IReportViewerRenderer { + + private static final CLogger log = CLogger.getCLogger(CSVReportViewerRenderer.class); + + public CSVReportViewerRenderer() { + } + + @Override + public String getId() { + return CSVReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return Msg.getMsg(Env.getCtx(), "FileCSV"); + } + + @Override + public String getContentType() { + return MediaType.CSV_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return CSVReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return true; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return roleCanExport; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + ReportEngine reportEngine = viewer.getReportEngine(); + try { + String path = System.getProperty("java.io.tmpdir"); + String prefix = makePrefix(reportEngine.getName()); + if (log.isLoggable(Level.FINE)) + { + log.log(Level.FINE, "Path="+path + " Prefix="+prefix); + } + File file = FileUtil.createTempFile(prefix, "."+getFileExtension(), new File(path)); + IReportRenderer renderer = Core.getReportRenderer(getId()); + CSVReportRendererConfiguration config = new CSVReportRendererConfiguration() + .setLanguage(AEnv.getLanguage(Env.getCtx())) + .setOutputFile(file); + renderer.renderReport(reportEngine, config); + return new AMedia(file.getName(), getFileExtension(), getContentType(), file, false); + } catch (Exception e) { + if (e instanceof RuntimeException) + throw (RuntimeException)e; + else + throw new RuntimeException(e); + } + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/HTMLReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/HTMLReportViewerRenderer.java new file mode 100644 index 0000000000..58060493d9 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/HTMLReportViewerRenderer.java @@ -0,0 +1,116 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import java.io.File; +import java.util.logging.Level; + +import org.adempiere.base.Core; +import org.adempiere.webui.report.HTMLExtension; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.tools.FileUtil; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.idempiere.print.renderer.HTMLReportRendererConfiguration; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; +import org.zkoss.zk.ui.Executions; + +import com.google.common.net.MediaType; + +/** + * HTML content rendering service for report viewer + */ +@Component(service = IReportViewerRenderer.class, immediate = true) +public class HTMLReportViewerRenderer implements IReportViewerRenderer { + + private static final CLogger log = CLogger.getCLogger(HTMLReportViewerRenderer.class); + + public HTMLReportViewerRenderer() { + } + + @Override + public String getId() { + return HTMLReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return Msg.getMsg(Env.getCtx(), "FileHTML"); + } + + @Override + public String getContentType() { + return MediaType.HTML_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return HTMLReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return true; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return true; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + ReportEngine reportEngine = viewer.getReportEngine(); + try { + String path = System.getProperty("java.io.tmpdir"); + String prefix = makePrefix(reportEngine.getName()); + if (prefix.length() < 3) + prefix += "_".repeat(3-prefix.length()); + if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Path="+path + " Prefix="+prefix); + File file = FileUtil.createTempFile(prefix, "."+getFileExtension(), new File(path)); + String contextPath = Executions.getCurrent().getContextPath(); + IReportRenderer renderer = Core.getReportRenderer(getId()); + HTMLReportRendererConfiguration config = new HTMLReportRendererConfiguration() + .setLanguage(reportEngine.getPrintFormat().getLanguage()) + .setOutputFile(file) + .setExport(export) + .setExtension(new HTMLExtension(contextPath, "rp", viewer.getUuid())); + renderer.renderReport(reportEngine, config); + return new AMedia(file.getName(), getFileExtension(), getContentType(), file, false); + } catch (Exception e) { + if (e instanceof RuntimeException) + throw (RuntimeException)e; + else + throw new RuntimeException(e); + } + } + + @Override + public boolean isSameContentForExportAndPreview() { + return false; + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/IReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/IReportViewerRenderer.java new file mode 100644 index 0000000000..3e68716964 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/IReportViewerRenderer.java @@ -0,0 +1,111 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import org.adempiere.webui.window.ZkReportViewer; +import org.zkoss.util.media.AMedia; + +/** + * Service interface for rendering of report viewer content.
+ * Implementation must be thread safe. + */ +public interface IReportViewerRenderer { + + /** + * Get id of renderer + * @return renderer id (PDF, HTML, etc) + */ + String getId(); + + /** + * Renderer label for export output type selection + * @return label for export output type selection + */ + String getExportLabel(); + + /** + * Renderer label for preview output type selection + * @return label for preview output type selection + */ + default String getPreviewLabel() { + return getId(); + } + + /** + * Get MIME content type (text/html, etc) + * @return MIME content type + */ + String getContentType(); + + /** + * Output file extension (without dot) + * @return output file extension (html, csv, xls, etc) + */ + String getFileExtension(); + + /** + * Is renderer support export + * @return true if renderer support export + */ + boolean isExport(); + + /** + * Is renderer support preview + * @param roleCanExport true if role can export + * @return true if renderer support preview + */ + boolean isPreview(boolean roleCanExport); + + /** + * Render output media for report + * @param viewer + * @param export + * @return media + */ + AMedia renderMedia(ZkReportViewer viewer, boolean export); + + /** + * Is export and preview generate the exact same output + * @return true if export and preview generate the exact same output + */ + default boolean isSameContentForExportAndPreview() { + return true; + } + + /** + * Create file name prefix from name + * @param name + * @return file name prefix from name + */ + default String makePrefix(String name) { + StringBuilder prefix = new StringBuilder(); + char[] nameArray = name.toCharArray(); + for (char ch : nameArray) { + if (Character.isLetterOrDigit(ch)) { + prefix.append(ch); + } else { + prefix.append("_"); + } + } + return prefix.toString(); + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/PDFReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/PDFReportViewerRenderer.java new file mode 100644 index 0000000000..354c0228ea --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/PDFReportViewerRenderer.java @@ -0,0 +1,104 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import java.io.File; +import java.util.logging.Level; + +import org.adempiere.base.Core; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.tools.FileUtil; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.idempiere.print.renderer.PDFReportRendererConfiguration; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; + +import com.google.common.net.MediaType; + +/** + * PDF content rendering service for report viewer + */ +@Component(service = IReportViewerRenderer.class, immediate = true) +public class PDFReportViewerRenderer implements IReportViewerRenderer { + + private static final CLogger log = CLogger.getCLogger(PDFReportViewerRenderer.class); + + public PDFReportViewerRenderer() { + } + + @Override + public String getId() { + return PDFReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return Msg.getMsg(Env.getCtx(), "FilePDF"); + } + + @Override + public String getContentType() { + return MediaType.PDF.toString(); + } + + @Override + public String getFileExtension() { + return PDFReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return true; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return true; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + ReportEngine reportEngine = viewer.getReportEngine(); + try { + String path = System.getProperty("java.io.tmpdir"); + String prefix = makePrefix(reportEngine.getName()); + if (prefix.length() < 3) + prefix += "_".repeat(3-prefix.length()); + if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Path="+path + " Prefix="+prefix); + File file = FileUtil.createTempFile(prefix, "."+getFileExtension(), new File(path)); + IReportRenderer renderer = Core.getReportRenderer(getId()); + PDFReportRendererConfiguration config = new PDFReportRendererConfiguration().setOutputFile(file); + renderer.renderReport(reportEngine, config); + return new AMedia(file.getName(), getFileExtension(), getContentType(), file, true); + } catch (Exception e) { + if (e instanceof RuntimeException) + throw (RuntimeException)e; + else + throw new RuntimeException(e); + } + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/PSReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/PSReportViewerRenderer.java new file mode 100644 index 0000000000..985f293558 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/PSReportViewerRenderer.java @@ -0,0 +1,89 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import java.io.ByteArrayOutputStream; + +import org.adempiere.base.Core; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.idempiere.print.renderer.PSReportRendererConfiguration; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; + +import com.google.common.net.MediaType; + +/** + * Postscript content rendering service for report viewer + */ +@Component(service = IReportViewerRenderer.class, immediate = true) +public class PSReportViewerRenderer implements IReportViewerRenderer { + + public PSReportViewerRenderer() { + } + + @Override + public String getId() { + return PSReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return Msg.getMsg(Env.getCtx(), "FilePS"); + } + + @Override + public String getContentType() { + return MediaType.POSTSCRIPT.toString(); + } + + @Override + public String getFileExtension() { + return PSReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return true; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return false; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ReportEngine m_reportEngine = viewer.getReportEngine(); + m_reportEngine.createPS(baos); + IReportRenderer renderer = Core.getReportRenderer(getId()); + PSReportRendererConfiguration config = new PSReportRendererConfiguration().setOutputStream(baos); + renderer.renderReport(m_reportEngine, config); + byte[] data = baos.toByteArray(); + return new AMedia(m_reportEngine.getName() + "."+getFileExtension(), getFileExtension(), getContentType(), data); + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/SSVReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/SSVReportViewerRenderer.java new file mode 100644 index 0000000000..28a3cef62c --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/SSVReportViewerRenderer.java @@ -0,0 +1,91 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import java.io.StringWriter; + +import org.adempiere.base.Core; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.idempiere.print.renderer.SSVReportRendererConfiguration; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; + +import com.google.common.net.MediaType; + +/** + * Semicolon separated content rendering service for report viewer + */ +@Component(service = IReportViewerRenderer.class, immediate = true) +public class SSVReportViewerRenderer implements IReportViewerRenderer { + + public SSVReportViewerRenderer() { + } + + @Override + public String getId() { + return SSVReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return Msg.getMsg(Env.getCtx(), "FileSSV"); + } + + @Override + public String getContentType() { + return MediaType.CSV_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return SSVReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return true; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return false; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + StringWriter sw = new StringWriter(); + ReportEngine reportEngine = viewer.getReportEngine(); + IReportRenderer renderer = Core.getReportRenderer(getId()); + SSVReportRendererConfiguration config = new SSVReportRendererConfiguration() + .setLanguage(reportEngine.getPrintFormat().getLanguage()) + .setOutputWriter(sw); + renderer.renderReport(reportEngine, config); + String data = sw.getBuffer().toString(); + return new AMedia(reportEngine.getName() + "."+getFileExtension(), getFileExtension(), getContentType(), data); + } + +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/TabDelimitedReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/TabDelimitedReportViewerRenderer.java new file mode 100644 index 0000000000..696f062474 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/TabDelimitedReportViewerRenderer.java @@ -0,0 +1,91 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import java.io.StringWriter; + +import org.adempiere.base.Core; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.idempiere.print.renderer.TabDelimitedReportRendererConfiguration; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; + +import com.google.common.net.MediaType; + +/** + * Tab separated content rendering service for report viewer + */ +@Component(service = IReportViewerRenderer.class, immediate = true) +public class TabDelimitedReportViewerRenderer implements IReportViewerRenderer { + + public TabDelimitedReportViewerRenderer() { + } + + @Override + public String getId() { + return TabDelimitedReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return Msg.getMsg(Env.getCtx(), "FileTXT"); + } + + @Override + public String getContentType() { + return MediaType.PLAIN_TEXT_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return TabDelimitedReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return true; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return false; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + StringWriter sw = new StringWriter(); + ReportEngine reportEngine = viewer.getReportEngine(); + IReportRenderer renderer = Core.getReportRenderer(getId()); + TabDelimitedReportRendererConfiguration config = new TabDelimitedReportRendererConfiguration() + .setLanguage(reportEngine.getPrintFormat().getLanguage()) + .setOutputWriter(sw); + renderer.renderReport(reportEngine, config); + String data = sw.getBuffer().toString(); + return new AMedia(reportEngine.getName() + "."+getFileExtension(), getFileExtension(), getContentType(), data); + } + +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/XLSReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/XLSReportViewerRenderer.java new file mode 100644 index 0000000000..d95958b862 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/XLSReportViewerRenderer.java @@ -0,0 +1,106 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import java.io.File; +import java.util.logging.Level; + +import org.adempiere.base.Core; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.tools.FileUtil; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.idempiere.print.renderer.XLSReportRendererConfiguration; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; + +import com.google.common.net.MediaType; + +/** + * Excel (xls) content rendering service for report viewer + */ +@Component(service = IReportViewerRenderer.class, immediate = true) +public class XLSReportViewerRenderer implements IReportViewerRenderer { + + private static final CLogger log = CLogger.getCLogger(XLSReportViewerRenderer.class); + + public XLSReportViewerRenderer() { + } + + @Override + public String getId() { + return XLSReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return Msg.getMsg(Env.getCtx(), "FileXLS"); + } + + @Override + public String getContentType() { + return MediaType.MICROSOFT_EXCEL.toString(); + } + + @Override + public String getFileExtension() { + return XLSReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return false; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return roleCanExport; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + ReportEngine reportEngine = viewer.getReportEngine(); + try { + String path = System.getProperty("java.io.tmpdir"); + String prefix = makePrefix(reportEngine.getName()); + if (prefix.length() < 3) + prefix += "_".repeat(3-prefix.length()); + if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Path="+path + " Prefix="+prefix); + File file = FileUtil.createTempFile(prefix, "."+getFileExtension(), new File(path)); + IReportRenderer renderer = Core.getReportRenderer(getId()); + XLSReportRendererConfiguration config = new XLSReportRendererConfiguration() + .setLanguage(reportEngine.getPrintFormat().getLanguage()) + .setOutputFile(file); + renderer.renderReport(reportEngine, config); + return new AMedia(file.getName(), getFileExtension(), getContentType(), file, true); + } catch (Exception e) { + if (e instanceof RuntimeException) + throw (RuntimeException)e; + else + throw new RuntimeException(e); + } + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/XLSXReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/XLSXReportViewerRenderer.java new file mode 100644 index 0000000000..95d88cb410 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/XLSXReportViewerRenderer.java @@ -0,0 +1,107 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import java.io.File; +import java.util.logging.Level; + +import org.adempiere.base.Core; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.tools.FileUtil; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.idempiere.print.renderer.XLSXReportRendererConfiguration; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; + +import com.google.common.net.MediaType; + +/** + * Excel open xml (xlsx) content rendering service for report viewer + */ +@Component(service = IReportViewerRenderer.class, immediate = true) +public class XLSXReportViewerRenderer implements IReportViewerRenderer { + + private static final CLogger log = CLogger.getCLogger(XLSXReportViewerRenderer.class); + + public XLSXReportViewerRenderer() { + } + + @Override + public String getId() { + return XLSXReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return Msg.getMsg(Env.getCtx(), "FileXLSX"); + } + + @Override + public String getContentType() { + return MediaType.OOXML_SHEET.toString(); + } + + @Override + public String getFileExtension() { + return XLSXReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return false; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return roleCanExport; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + ReportEngine reportEngine = viewer.getReportEngine(); + try { + String path = System.getProperty("java.io.tmpdir"); + String prefix = makePrefix(reportEngine.getName()); + if (log.isLoggable(Level.FINE)) + { + log.log(Level.FINE, "Path=" + path + " Prefix=" + prefix); + } + File file = FileUtil.createTempFile(prefix, "."+getFileExtension(), new File(path)); + IReportRenderer renderer = Core.getReportRenderer(getId()); + XLSXReportRendererConfiguration config = new XLSXReportRendererConfiguration() + .setLanguage(reportEngine.getPrintFormat().getLanguage()) + .setOutputFile(file); + renderer.renderReport(reportEngine, config); + return new AMedia(file.getName(), getFileExtension(), getContentType(), file, true); + } catch (Exception e) { + if (e instanceof RuntimeException) + throw (RuntimeException)e; + else + throw new RuntimeException(e); + } + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/XMLReportViewerRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/XMLReportViewerRenderer.java new file mode 100644 index 0000000000..f88edb2e4d --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/XMLReportViewerRenderer.java @@ -0,0 +1,89 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.ui.zk.report; + +import java.io.StringWriter; + +import org.adempiere.base.Core; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.idempiere.print.renderer.XMLReportRendererConfiguration; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; + +import com.google.common.net.MediaType; + +/** + * XML content rendering service for report viewer + */ +@Component(service = IReportViewerRenderer.class, immediate = true) +public class XMLReportViewerRenderer implements IReportViewerRenderer { + + public XMLReportViewerRenderer() { + } + + @Override + public String getId() { + return XMLReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return Msg.getMsg(Env.getCtx(), "FileXML"); + } + + @Override + public String getContentType() { + return MediaType.XML_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return XMLReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return true; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return false; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + StringWriter sw = new StringWriter(); + ReportEngine reportEngine = viewer.getReportEngine(); + IReportRenderer renderer = Core.getReportRenderer(getId()); + XMLReportRendererConfiguration config = new XMLReportRendererConfiguration().setOutputWriter(sw); + renderer.renderReport(reportEngine, config); + String data = sw.getBuffer().toString(); + return new AMedia(reportEngine.getName() + "."+getFileExtension(), getFileExtension(), getContentType(), data); + } + +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/package-info.java b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/package-info.java new file mode 100644 index 0000000000..a6094eff21 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/idempiere/ui/zk/report/package-info.java @@ -0,0 +1,26 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ + +/** + * Provide interface and class for report viewer preview and export renderer + */ +package org.idempiere.ui.zk.report; \ No newline at end of file diff --git a/org.adempiere.ui.zk/WEB-INF/zk.xml b/org.adempiere.ui.zk/WEB-INF/zk.xml index defc063f5a..7ce03b4e16 100644 --- a/org.adempiere.ui.zk/WEB-INF/zk.xml +++ b/org.adempiere.ui.zk/WEB-INF/zk.xml @@ -139,4 +139,8 @@ org.zkoss.zk.ui.uuidRecycle.disabled true + + org.zkoss.zul.Filedownload.contentTypeAsIs + true + diff --git a/org.idempiere.zk.datatable/.classpath b/org.idempiere.zk.datatable/.classpath new file mode 100644 index 0000000000..8d8612144f --- /dev/null +++ b/org.idempiere.zk.datatable/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.idempiere.zk.datatable/.project b/org.idempiere.zk.datatable/.project new file mode 100644 index 0000000000..54804483eb --- /dev/null +++ b/org.idempiere.zk.datatable/.project @@ -0,0 +1,33 @@ + + + org.idempiere.zk.datatable + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.idempiere.zk.datatable/.settings/org.eclipse.core.resources.prefs b/org.idempiere.zk.datatable/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..99f26c0203 --- /dev/null +++ b/org.idempiere.zk.datatable/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/org.idempiere.zk.datatable/.settings/org.eclipse.pde.core.prefs b/org.idempiere.zk.datatable/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 0000000000..f29e940a00 --- /dev/null +++ b/org.idempiere.zk.datatable/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/org.idempiere.zk.datatable/META-INF/MANIFEST.MF b/org.idempiere.zk.datatable/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..9140f55ba6 --- /dev/null +++ b/org.idempiere.zk.datatable/META-INF/MANIFEST.MF @@ -0,0 +1,27 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Datatable Renderer for Report +Bundle-SymbolicName: org.idempiere.zk.datatable +Bundle-Version: 12.0.0.qualifier +Require-Bundle: org.adempiere.base;bundle-version="12.0.0", + zcommon;bundle-version="9.6.4", + zel;bundle-version="9.6.4", + zhtml;bundle-version="9.6.4", + zjavassist;bundle-version="9.6.4", + zk;bundle-version="9.6.4", + zkbind;bundle-version="9.6.4", + zkplus;bundle-version="9.6.4", + zkwebfragment;bundle-version="9.6.4", + zul;bundle-version="9.6.4", + zweb;bundle-version="9.6.4", + json;bundle-version="20190722.0.0", + org.apache.ecs;bundle-version="12.0.0" +Import-Package: com.google.common.net;version="30.1.1", + org.osgi.service.component.annotations;version="1.5.1" +Bundle-Vendor: idempiere.org +Fragment-Host: org.adempiere.ui.zk;bundle-version="11.0.0" +Automatic-Module-Name: org.idempiere.ui.zk.datatable +Bundle-RequiredExecutionEnvironment: JavaSE-17 +Service-Component: OSGI-INF/org.idempiere.zk.datatable.DatatableReportRenderer.xml, + OSGI-INF/org.idempiere.zk.datatable.DatatableReportViewerRenderer.xml +Bundle-ActivationPolicy: lazy diff --git a/org.idempiere.zk.datatable/OSGI-INF/org.idempiere.zk.datatable.DatatableReportRenderer.xml b/org.idempiere.zk.datatable/OSGI-INF/org.idempiere.zk.datatable.DatatableReportRenderer.xml new file mode 100644 index 0000000000..8b01431ece --- /dev/null +++ b/org.idempiere.zk.datatable/OSGI-INF/org.idempiere.zk.datatable.DatatableReportRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.idempiere.zk.datatable/OSGI-INF/org.idempiere.zk.datatable.DatatableReportViewerRenderer.xml b/org.idempiere.zk.datatable/OSGI-INF/org.idempiere.zk.datatable.DatatableReportViewerRenderer.xml new file mode 100644 index 0000000000..47cd93e2b2 --- /dev/null +++ b/org.idempiere.zk.datatable/OSGI-INF/org.idempiere.zk.datatable.DatatableReportViewerRenderer.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/org.idempiere.zk.datatable/build.properties b/org.idempiere.zk.datatable/build.properties new file mode 100644 index 0000000000..67e6565494 --- /dev/null +++ b/org.idempiere.zk.datatable/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = target/classes/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/ diff --git a/org.idempiere.zk.datatable/pom.xml b/org.idempiere.zk.datatable/pom.xml new file mode 100644 index 0000000000..4095cb7266 --- /dev/null +++ b/org.idempiere.zk.datatable/pom.xml @@ -0,0 +1,12 @@ + + 4.0.0 + + org.idempiere + org.idempiere.parent + ${revision} + ../org.idempiere.parent/pom.xml + + org.idempiere.zk.datatable + eclipse-plugin + diff --git a/org.idempiere.zk.datatable/src/metainfo/zk/lang-addon.xml b/org.idempiere.zk.datatable/src/metainfo/zk/lang-addon.xml new file mode 100644 index 0000000000..6eb17fa654 --- /dev/null +++ b/org.idempiere.zk.datatable/src/metainfo/zk/lang-addon.xml @@ -0,0 +1,7 @@ + + + + datatables + xul/html + + diff --git a/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DataTableOptions.java b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DataTableOptions.java new file mode 100644 index 0000000000..6d55b204fa --- /dev/null +++ b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DataTableOptions.java @@ -0,0 +1,510 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.zk.datatable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.TreeMap; + +import org.compiere.util.Language; +import org.compiere.util.Msg; +import org.compiere.util.Util; +import org.idempiere.zk.datatable.DatatableReportRenderer.FunctionTypes; +import org.json.JSONArray; +import org.json.JSONString; + +public class DataTableOptions { + + public class JSONFunction implements JSONString { + + private String string; + + public JSONFunction(String string) { + this.string = string; + } + + @Override + public String toJSONString() { + return string; + } + + } + + public final Integer DESC_OFFSET = 1000000; + + private HashMap> functionMap; + + private Integer printColLastIndex; + + private String locale; + + /** + * @param locale + */ + public DataTableOptions(String locale) { + functionMap = new HashMap<>(); + this.locale = locale; + } + + /** + * Get datatables settings + * @param path + * @return json datatables settings + */ + public String getDataTableOptions(String path) { + StringBuilder dataOptions = new StringBuilder(); + dataOptions.append(" { "); + dataOptions.append(" pageLength: ").append(250); + dataOptions.append(", lengthMenu: [ [250, 500, 1000, -1], [250, 500, 1000,\"").append(Msg.getMsg( Language.getAD_Language(locale), "All")).append("\" ] ] "); + dataOptions.append(", colReorder: ").append(true); + dataOptions.append(", responsive: ").append(true); + dataOptions.append(", ordering: ").append(true); + dataOptions.append(", search: { return: ").append(true).append(" }"); + dataOptions.append(", language: { url: '").append(path).append("DataTables/i18n/").append(this.locale).append(".json' }"); + dataOptions.append(", dom: \"lfrtip\" "); + + dataOptions.append(", initComplete:").append(" function () " + + "{ this.api().columns().every( function () " + + "{ var that = this; $('input', this.footer()).on('keyup change clear', function () " + + "{ if (that.search() !== this.value) " + + "{ that.search(this.value).draw();}});});}"); + + String orderBy = getOrderBy(); + + + if(orderBy != null) + dataOptions.append(", order: ").append(orderBy); + + String groupByDataSrc = getGroupDataSrc(); + if(groupByDataSrc != null) { + dataOptions.append(", rowGroup: { "); + dataOptions.append(" dataSrc: ").append(groupByDataSrc); + String aggregateFunctions = getRowGroupRendered(); + if(!Util.isEmpty(aggregateFunctions)) { + dataOptions.append(" , ").append(" endRender: ").append(aggregateFunctions); + dataOptions.append(" , ").append(" startRender: null "); + } + dataOptions.append(" } "); + } + + String footerFunction = getFootAggregateFunctions(); + if(!Util.isEmpty(footerFunction)) { + dataOptions.append(", footerCallback: ").append(footerFunction); + } + + dataOptions.append(" } "); + return dataOptions.toString(); + + } + + /** + * Get group rendering function + * @return group rendering function + */ + private String getRowGroupRendered() { + + ArrayList groupByList = functionMap.get(FunctionTypes.GROUP_BY); + Integer groupBy = groupByList.get(0); + + TreeMap groupRowNamesMap = new TreeMap<>(); + + StringBuilder retValue = new StringBuilder(); + retValue.append(" function ( rows, group ) { "); + // Prepare localization + retValue.append(" let locale = '" + locale + "'; "); + + String groupName = "groupBy_"+groupBy; + retValue.append(" let ").append(groupName); + retValue.append(" = group; "); + groupRowNamesMap.put(groupBy, new String[] { groupName, null }); + + + retValue.append(" let totalRows = []; "); + retValue.append(" let displayRows = rows.data(); "); + retValue.append(" rows.rows().data().filter(function(value, index) { ") + .append(" let groupValue = null; ") + .append(" if(value[").append(groupBy).append("].includes('') "); + Integer prevColIndex = 0; + for(Entry groupRowNameSet :groupRowNamesMap.entrySet()) { + Integer actualColIndex = groupRowNameSet.getKey(); + String[] varValues = groupRowNameSet.getValue(); + + + if(actualColIndex -prevColIndex > 0) { + retValue.append(".append( '"); + retValue.append("')"); + } + + if(varValues != null && varValues.length == 2) { + String varName = varValues[0]; + String varTotalName = varValues[1]; + + if(varName.contains("groupBy_")) + retValue.append(".append( ' "); + else + retValue.append(".append( ' "); + if(!Util.isEmpty(varName)) { + retValue.append("'+"+varName+"+'"); + } + + if(!Util.isEmpty(varTotalName)) { + retValue.append("
('+"+varTotalName+"+')"); + } + + if(varName.contains("groupBy_")) + retValue.append(" Σ "); + } + + retValue.append("' )"); + prevColIndex = varValues==null ? actualColIndex : actualColIndex+1; + } + + if(prevColIndex < (printColLastIndex+1)) { + retValue.append(".append( ' 1) + retValue.append("colspan=\"").append((printColLastIndex+1)-prevColIndex).append("\"/>"); + + retValue.append("') "); + } + retValue.append("; "); + retValue.append("return rowRender;"); + retValue.append(" } "); + + return retValue.toString(); + } + + /** + * Get table footer aggregate rendering function + * @return table footer aggregate rendering function + */ + private String getFootAggregateFunctions() { + + TreeMap groupRowNamesMap = new TreeMap<>(); + + StringBuilder retValue = new StringBuilder(); + retValue.append(" function ( tfoot, data, start, end, display ) { "); + retValue.append(" let api = this.api(); "); + // Prepare localization + retValue.append(" let locale = '" + locale + "'; "); + + ArrayList groupByList = functionMap.get(FunctionTypes.GROUP_BY); + if(groupByList != null) { + Integer groupBy = groupByList.get(0); + + String groupName = "groupBy_"+groupBy; + retValue.append(" let ").append(groupName); + retValue.append(" = ' Σ'; "); + groupRowNamesMap.put(groupBy, new String[] { groupName, null }); + } + + retValue.append(" let totalRows = data; "); + retValue.append(" let displayRows = api.rows({ page: 'current' }).data(); "); + setAggregateFunctions(retValue, groupRowNamesMap); + + for(Entry groupRowNameSet :groupRowNamesMap.entrySet()) { + Integer actualColIndex = groupRowNameSet.getKey(); + String[] varValues = groupRowNameSet.getValue(); + + if(varValues == null || varValues.length != 2) { + continue; + } + String varValue = varValues[0]; + String varTotalValue = varValues[1]; + + retValue.append(" $( api.column( "); + retValue.append(actualColIndex); + retValue.append(" ).footer() ).html(").append(varValue); + + if(!Util.isEmpty(varTotalValue)) { + retValue.append(" ).append('
') "); + retValue.append(".append( '(' + ").append(varTotalValue).append(" +')'"); + } + + retValue.append(" ) "); + + + if(varValue.contains("groupBy_")) + retValue.append(".addClass( 'datatable-text');"); + else + retValue.append(".addClass( 'datatable-number');"); + } + + retValue.append(" } "); + return retValue.toString(); + + } + + /** + * @param retValue + * @param groupRowNamesMap + */ + private void setAggregateFunctions(StringBuilder retValue, TreeMap groupRowNamesMap) { + + ArrayList averageList = functionMap.get(FunctionTypes.AVG); + ArrayList countList = functionMap.get(FunctionTypes.COUNT); + ArrayList deviationList = functionMap.get(FunctionTypes.DEVIATION); + ArrayList maxList = functionMap.get(FunctionTypes.MAX); + ArrayList minList = functionMap.get(FunctionTypes.MIN); + ArrayList sumList = functionMap.get(FunctionTypes.SUM); + ArrayList varianceList = functionMap.get(FunctionTypes.VARIANCE); + + if(averageList != null) { + for(Integer averageID:averageList) { + String varTotalName = "average_total_"+averageID; + retValue.append(" let ").append(varTotalName).append(" = new Intl.NumberFormat(locale).format(+(( totalRows.map(i => new NumberParser(locale).parse(i[").append(averageID).append("])).reduce( function (a, b) { return a + b*1; }, 0) "); + retValue.append(" / totalRows.length ).toFixed(4))); "); + + String varName = "average_"+averageID; + groupRowNamesMap.put(averageID, new String[] { varName, varTotalName }); + retValue.append(" let ").append(varName).append(" = new Intl.NumberFormat(locale).format(+(( displayRows.map(i => new NumberParser(locale).parse(i[").append(averageID).append("])).reduce( function (a, b) { return a + b*1; }, 0) "); + retValue.append(" / displayRows.length ).toFixed(4))); "); + } + } + + // Count List + if(countList != null) { + for(Integer countID:countList) { + String varTotalName = "count_total_"+countID; + retValue.append(" let ").append(varTotalName).append(" = new Intl.NumberFormat(locale).format(totalRows.length); "); + + String varName = "count_"+countID; + groupRowNamesMap.put(countID, new String[] { varName, varTotalName }); + retValue.append(" let ").append(varName).append(" = new Intl.NumberFormat(locale).format(displayRows.length); "); + } + } + + if(sumList != null) { + for(Integer sumID:sumList) { + String varTotalName = "sum_total_"+sumID; + retValue.append(" let ").append(varTotalName).append(" = new Intl.NumberFormat(locale).format(+((totalRows.map(i => new NumberParser(locale).parse(i[").append(sumID).append("])).reduce( function (a, b) { return a + b*1; }, 0)).toFixed(4))); "); + + String varName = "sum_"+sumID; + groupRowNamesMap.put(sumID, new String[] { varName, varTotalName }); + retValue.append(" let ").append(varName).append(" = new Intl.NumberFormat(locale).format(+((displayRows.map(i => new NumberParser(locale).parse(i[").append(sumID).append("])).reduce( function (a, b) { return a + b*1; }, 0)).toFixed(4))); "); + } + } + + if(maxList != null) { + for(Integer maxID:maxList) { + String varTotalName = "max_total_"+maxID; + retValue.append(" let ").append(varTotalName).append(" = new Intl.NumberFormat(locale).format(totalRows.map(i => new NumberParser(locale).parse(i[").append(maxID) + .append("])).reduce(function(a, b) { return Math.max(a, b);})); "); + + String varName = "max_"+maxID; + groupRowNamesMap.put(maxID, new String[] { varName, varTotalName }); + retValue.append(" let ").append(varName).append(" = new Intl.NumberFormat(locale).format(displayRows.map(i => new NumberParser(locale).parse(i[").append(maxID) + .append("])).reduce(function(a, b) { return Math.max(a, b);})); "); + } + } + + if(minList != null) { + for(Integer minID:minList) { + String varTotalName = "min_total_"+minID; + retValue.append(" let ").append(varTotalName).append(" = new Intl.NumberFormat(locale).format( totalRows.map(i => new NumberParser(locale).parse(i[").append(minID) + .append("])).reduce(function(a, b) { return Math.min(a, b);})); "); + + String varName = "min_"+minID; + groupRowNamesMap.put(minID, new String[] { varName, varTotalName }); + retValue.append(" let ").append(varName).append(" = new Intl.NumberFormat(locale).format( displayRows.map(i => new NumberParser(locale).parse(i[").append(minID) + .append("])).reduce(function(a, b) { return Math.min(a, b);})); "); + } + } + + if(varianceList != null) { + for(Integer varianceID:varianceList) { + String varTotalName = "variance_total_"+varianceID; + + retValue.append(" let ").append(varTotalName).append(" = totalRows.map((i) => { return { sum: new NumberParser(locale).parse(i[").append(varianceID) + .append("]), sumSquare: (new NumberParser(locale).parse(i[").append(varianceID).append("]) * new NumberParser(locale).parse(i[").append(varianceID).append("])) ") + .append(" }; } )"); + retValue.append(".reduce( function (a, b) { let c = { sum: a.sum + b.sum*1, sumSquare: a.sumSquare + b.sumSquare*1 }; return c; }, { sum: 0, sumSquare: 0 }); "); + + retValue.append(varTotalName).append(" = new Intl.NumberFormat(locale).format( +(((").append(varTotalName).append(".sumSquare - (( ").append(varTotalName).append(".sum * ").append(varTotalName).append(".sum ) / totalRows.length ))/ totalRows.length ).toFixed(4))); "); + + String varName = "variance_"+varianceID; + groupRowNamesMap.put(varianceID, new String[] { varName, varTotalName }); + + retValue.append(" let ").append(varName).append(" = displayRows.map((i) => { return { sum: new NumberParser(locale).parse(i[").append(varianceID) + .append("]), sumSquare: (new NumberParser(locale).parse(i[").append(varianceID).append("]) * new NumberParser(locale).parse(i[").append(varianceID).append("])) ") + .append(" }; } )"); + retValue.append(".reduce( function (a, b) { let c = { sum: a.sum + b.sum*1, sumSquare: a.sumSquare + b.sumSquare*1 }; return c; }, { sum: 0, sumSquare: 0 }); "); + + retValue.append(varName).append(" = new Intl.NumberFormat(locale).format( +(((").append(varName).append(".sumSquare - (( ").append(varName).append(".sum * ").append(varName).append(".sum ) / displayRows.length ))/ displayRows.length ).toFixed(4))); "); + } + } + + // Deviation + if(deviationList != null) { + for(Integer deviationID:deviationList) { + String varTotalName = "deviation_total_"+deviationID; + + retValue.append(" let ").append(varTotalName).append(" = totalRows.map((i) => { return { sum: new NumberParser(locale).parse(i[").append(deviationID) + .append("]), sumSquare: (new NumberParser(locale).parse(i[").append(deviationID).append("]) * new NumberParser(locale).parse(i[").append(deviationID).append("])) ") + .append(" }; } )"); + retValue.append(".reduce( function (a, b) { let c = { sum: a.sum + b.sum*1, sumSquare: a.sumSquare + b.sumSquare*1 }; return c; }, { sum: 0, sumSquare: 0 }); "); + + retValue.append(varTotalName).append(" = ((").append(varTotalName).append(".sumSquare - (( ").append(varTotalName).append(".sum * ").append(varTotalName).append(".sum ) / totalRows.length ))/ totalRows.length ); "); + retValue.append(varTotalName).append(" = new Intl.NumberFormat(locale).format(+(Math.sqrt(").append(varTotalName).append(").toFixed(4)));"); + + String varName = "deviation_"+deviationID; + groupRowNamesMap.put(deviationID, new String[] { varName, varTotalName }); + + retValue.append(" let ").append(varName).append(" = displayRows.map((i) => { return { sum: new NumberParser(locale).parse(i[").append(deviationID) + .append("]), sumSquare: (new NumberParser(locale).parse(i[").append(deviationID).append("]) * new NumberParser(locale).parse(i[").append(deviationID).append("])) ") + .append(" }; } )"); + retValue.append(".reduce( function (a, b) { let c = { sum: a.sum + b.sum*1, sumSquare: a.sumSquare + b.sumSquare*1 }; return c; }, { sum: 0, sumSquare: 0 }); "); + + retValue.append(varName).append(" = ((").append(varName).append(".sumSquare - (( ").append(varName).append(".sum * ").append(varName).append(".sum ) / displayRows.length ))/ displayRows.length ); "); + retValue.append(varName).append(" = new Intl.NumberFormat(locale).format(+(Math.sqrt(").append(varName).append(").toFixed(4)));"); + } + } + } + + /** + * @param type + * @param id + */ + public void addPrintColumnIndex(FunctionTypes type, Integer id) { + ArrayList list = functionMap.get(type); + if (list == null) { + list = new ArrayList<>(); + functionMap.put(type, list); + } + + list.add(id); + } + + /** + * Get group by json array + * @return group by json array + */ + private String getGroupDataSrc() { + StringBuilder retValue = new StringBuilder(); + ArrayList groupByList = functionMap.get(FunctionTypes.GROUP_BY); + if(groupByList == null) + return null; + + if(groupByList.size() > 1) + retValue.append(" [ "); + + for(int index = 0; index < groupByList.size() ; index++) { + Integer groupBy = groupByList.get(index); + StringBuilder function = new StringBuilder(); + function.append("function(row) { ") + .append(" if(row[").append(groupBy).append("].includes('
1) + retValue.append(" ] "); + + return retValue.toString(); + } + + /** + * Get order by json array + * @return order by json array + */ + private String getOrderBy() { + String orderBy = orderListToJson(functionMap.get(FunctionTypes.ORDER_BY)); + return orderBy; + } + + /** + * Get last print column index + * @return last print column index + */ + public Integer getPrintColLastIndex() { + return printColLastIndex; + } + + /** + * Set last print column index + * @param printColLastIndex + */ + public void setPrintColLastIndex(Integer printColLastIndex) { + this.printColLastIndex = printColLastIndex; + } + + /** + * Get locale + * @return locale + */ + public String getLocale() { + return locale; + } + + /** + * Convert orderByList to json array + * @param orderByList + * @return json array for orderByList + */ + private String orderListToJson(ArrayList orderByList) { + if(orderByList == null) + return null; + + JSONArray orderArray = null; + String orderString = "asc"; // asc / desc + + for(int orderBy: orderByList) { + if(orderArray == null) + orderArray = new JSONArray(); + + if(orderBy >= DESC_OFFSET) { + orderBy = orderBy - DESC_OFFSET; + orderString = "desc"; + } else { + orderString = "asc"; + } + JSONArray orderItem = new JSONArray(); + orderItem.put(orderBy); + orderItem.put(orderString); + orderArray.put(orderItem); + } + return orderArray.toString(); + + } + + + +} diff --git a/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DatatableReportRenderer.java b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DatatableReportRenderer.java new file mode 100644 index 0000000000..c6a058f2dd --- /dev/null +++ b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DatatableReportRenderer.java @@ -0,0 +1,875 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.zk.datatable; + +import java.awt.Font; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Writer; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +import org.adempiere.exceptions.AdempiereException; +import org.apache.ecs.XhtmlDocument; +import org.apache.ecs.xhtml.a; +import org.apache.ecs.xhtml.link; +import org.apache.ecs.xhtml.script; +import org.apache.ecs.xhtml.span; +import org.apache.ecs.xhtml.style; +import org.apache.ecs.xhtml.table; +import org.apache.ecs.xhtml.tbody; +import org.apache.ecs.xhtml.td; +import org.apache.ecs.xhtml.th; +import org.apache.ecs.xhtml.thead; +import org.apache.ecs.xhtml.tr; +import org.compiere.model.MColumn; +import org.compiere.model.MElement; +import org.compiere.model.MQuery; +import org.compiere.model.MRole; +import org.compiere.model.MStyle; +import org.compiere.model.MSysConfig; +import org.compiere.model.MTable; +import org.compiere.print.IHTMLExtension; +import org.compiere.print.MPrintFont; +import org.compiere.print.MPrintFormat; +import org.compiere.print.MPrintFormatItem; +import org.compiere.print.MPrintTableFormat; +import org.compiere.print.PrintData; +import org.compiere.print.PrintDataElement; +import org.compiere.print.ReportEngine; +import org.compiere.print.layout.InstanceAttributeColumn; +import org.compiere.print.layout.InstanceAttributeData; +import org.compiere.print.layout.LayoutEngine; +import org.compiere.util.CLogger; +import org.compiere.util.DisplayType; +import org.compiere.util.Env; +import org.compiere.util.Ini; +import org.compiere.util.Language; +import org.compiere.util.Msg; +import org.compiere.util.Util; +import org.idempiere.print.renderer.HTMLReportRenderer; +import org.idempiere.print.renderer.IReportRenderer; +import org.osgi.service.component.annotations.Component; +import org.zkoss.zk.ui.Executions; + +import com.google.common.net.MediaType; + +@Component(service = IReportRenderer.class, immediate = true) +public class DatatableReportRenderer implements IReportRenderer { + + private static final CLogger log = CLogger.getCLogger(DatatableReportRenderer.class); + private static final String JS_DATA_IDENTIFIER = "JS_DataTable"; + + protected enum FunctionTypes {SUM, COUNT, MIN, MAX, AVG, DEVIATION, VARIANCE, GROUP_BY, ORDER_BY}; + + public DatatableReportRenderer() { + } + + @Override + public String getId() { + return DatatableReportRendererConfiguration.ID; + } + + @Override + public String getName() { + return getId(); + } + + @Override + public String getContentType() { + return MediaType.HTML_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return DatatableReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public void renderReport(ReportEngine reportEngine, DatatableReportRendererConfiguration configuration) { + try + { + Language lang = configuration.getLanguage(); + if (lang == null) + lang = Language.getLoginLanguage(); + Writer fw = null; + if (configuration.getOutputFile() != null) + fw = new OutputStreamWriter(new FileOutputStream(configuration.getOutputFile(), false), Ini.getCharset()); + else + fw = configuration.getOutputWriter(); + createHTML (reportEngine, new BufferedWriter(fw), configuration.isOnlyTable(), lang, configuration.getExtension(), configuration.isExport()); + } + catch (FileNotFoundException fnfe) + { + log.log(Level.SEVERE, "(f) - " + fnfe.toString()); + } + catch (Exception e) + { + log.log(Level.SEVERE, "(f)", e); + throw new AdempiereException(e); + } + } + + @Override + public boolean isBinary() { + return false; + } + + @Override + public Class getConfigurationType() { + return DatatableReportRendererConfiguration.class; + } + + /** + * Write HTML to writer + * @param reportEngine + * @param writer writer + * @param onlyTable if false create complete HTML document + * @param language optional language - if null numbers/dates are not formatted + * @param extension optional extension for html output + * @param isExport when isExport = true will don't embed resource dependent zk framework + */ + private void createHTML (ReportEngine reportEngine, Writer writer, boolean onlyTable, Language language, IHTMLExtension extension, boolean isExport) + { + MPrintFormat printFormat = reportEngine.getPrintFormat(); + PrintData printData = reportEngine.getPrintData(); + MQuery reportEngineQuery = reportEngine.getQuery(); + LayoutEngine layout = reportEngine.getLayout(); + Map> mapCssInfo = new HashMap<>(); + try + { + DataTableOptions dataTableOptions = new DataTableOptions(language.getLanguageCode()); + //collect column to print + List columns = new ArrayList<>(); + List asiElements = new ArrayList<>(); + int columnCount = 0; + for (int col = 0; col < printFormat.getItemCount(); col++) + { + MPrintFormatItem item = printFormat.getItem(col); + if (item.isPrinted()) + { + if (item.isTypeField() && item.isPrintInstanceAttributes()) + { + InstanceAttributeData asiElement = new InstanceAttributeData(item, columnCount); + asiElement.readAttributesData(printData); + asiElements.add(asiElement); + continue; + } + else + { + columns.add(item); + columnCount++; + } + } + } + if (asiElements.size() > 0) + { + int columnCreated = 0; + for(InstanceAttributeData data : asiElements) + { + List instanceColumns = data.getColumns(); + int index = data.getColumnIndex() + columnCreated; + for(InstanceAttributeColumn c : instanceColumns) + { + columns.add(index, c); + index++; + columnCreated++; + } + } + } + + String cssPrefix = extension != null ? extension.getClassPrefix() : null; + if (cssPrefix != null && cssPrefix.trim().length() == 0) + cssPrefix = null; + + table parameterTable = null; + if (!printFormat.isForm()) { + if (reportEngineQuery != null && reportEngineQuery.isActive()) { + int rows = reportEngineQuery.getReportProcessQuery() != null ? reportEngineQuery.getReportProcessQuery().getRestrictionCount() : reportEngineQuery.getRestrictionCount(); + if (rows > 0) { + parameterTable = new table(); + if (cssPrefix != null) + parameterTable.setClass(cssPrefix + "-parameter-table"); + else + parameterTable.setClass("parameter-table"); + parameterTable.setNeedClosingTag(false); + } + } + } + + table table = new table(); + if (cssPrefix != null) { + table.setClass("cell-border compact hover " + cssPrefix + "-table"); + } + table.setID(JS_DATA_IDENTIFIER); + // + table.setNeedClosingTag(false); + PrintWriter w = new PrintWriter(writer); + XhtmlDocument doc = null; + boolean minify = MSysConfig.getBooleanValue(MSysConfig.HTML_REPORT_MINIFY, true, Env.getAD_Client_ID(Env.getCtx())); + + if (onlyTable) + { + w.print(HTMLReportRenderer.compress(table.toString(), minify)); + } + else + { + doc = new XhtmlDocument(); + doc.getHtml().setNeedClosingTag(false); + doc.getBody().setNeedClosingTag(false); + doc.appendHead(""); + + if (extension != null && !Util.isEmpty(extension.getWebFontLinks(), true)) + { + doc.appendHead(extension.getWebFontLinks()); + } + + if (extension != null && extension.getStyleURL() != null) + { + // maybe cache style content with key is path + String pathStyleFile = extension.getFullPathStyle(); // creates a temp file - delete below + Path path = Paths.get(pathStyleFile); + List styleLines = Files.readAllLines(path, Ini.getCharset()); + Files.delete(path); // delete temp file + StringBuilder styleBuild = new StringBuilder(); + for (String styleLine : styleLines){ + styleBuild.append(styleLine); //.append("\n"); + } + HTMLReportRenderer.appendInlineCss (doc, styleBuild); + } + if (extension != null && extension.getScriptURL() != null && !isExport) + { + script jslink = new script(); + jslink.setLanguage("javascript"); + jslink.setSrc(extension.getScriptURL()); + doc.appendHead(jslink); + } + + appendStyles(doc, isExport); + + appendScripts (doc, isExport); + + //FIXME Better implementation of Javascript + doc.appendHead(""); + doc.appendHead(""); + + if (extension != null && !isExport){ + extension.setWebAttribute(doc.getBody()); + } + } + + if (doc != null) + { + MPrintFormatItem item = null; + int printColIndex = -1; + for(int col = 0; col < columns.size(); col++) + { + Object colobj = columns.get(col); + if (colobj instanceof MPrintFormatItem) + item = (MPrintFormatItem) colobj; + else if (colobj instanceof InstanceAttributeColumn) + item = ((InstanceAttributeColumn) colobj).getPrintFormatItem(); + if(item != null) + { + printColIndex++; + HTMLReportRenderer.addCssInfo(printFormat, item, printColIndex, mapCssInfo); + } + } + HTMLReportRenderer.appendInlineCss(doc, mapCssInfo); + + StringBuilder styleBuild = new StringBuilder(); + MPrintTableFormat tf = printFormat.getTableFormat(); + HTMLReportRenderer.CSSInfo cssInfo = new HTMLReportRenderer.CSSInfo(tf.getPageHeader_Font(), tf.getPageHeaderFG_Color()); + if (cssPrefix != null) { + if (parameterTable != null) + styleBuild.append("."+cssPrefix + "-parameter-table th").append(cssInfo.getCssRule()); + styleBuild.append("."+cssPrefix + "-table th").append(cssInfo.getCssRule()); + } + else { + if (parameterTable != null) + styleBuild.append("parameter-table th").append(cssInfo.getCssRule()); + styleBuild.append("table th").append(cssInfo.getCssRule()); + } + + cssInfo = new HTMLReportRenderer.CSSInfo(tf.getParameter_Font(), tf.getParameter_Color()); + styleBuild.append(".tr-parameter td").append(cssInfo.getCssRule()); + + cssInfo = new HTMLReportRenderer.CSSInfo(tf.getFunct_Font(), tf.getFunctFG_Color()); + styleBuild.append(".tr-function td").append(cssInfo.getCssRule()); + + MPrintFont printFont = MPrintFont.get(printFormat.getAD_PrintFont_ID()); + Font base = printFont.getFont(); + Font newFont = new Font(base.getName(), Font.PLAIN, base.getSize()-1); + cssInfo = new HTMLReportRenderer.CSSInfo(newFont, null); + styleBuild.append(".tr-level-1 td").append(cssInfo.getCssRule()); + + newFont = new Font(base.getName(), Font.PLAIN, base.getSize()-2); + cssInfo = new HTMLReportRenderer.CSSInfo(newFont, null); + styleBuild.append(".tr-level-2 td").append(cssInfo.getCssRule()); + + styleBuild = new StringBuilder(styleBuild.toString().replaceAll(";", "!important;")); + HTMLReportRenderer.appendInlineCss (doc, styleBuild); + + w.print(doc.toString()); + + w.print("
"); + String paraWrapId = null; + if (parameterTable != null) { + paraWrapId = cssPrefix + "-para-table-wrap"; + w.print("
"); + w.print(""+Msg.getMsg(Env.getCtx(), "Parameter")+""); + w.print(HTMLReportRenderer.compress(parameterTable.toString(), minify)); + + tr tr = new tr(); + tr.setClass("tr-parameter"); + + MQuery query = reportEngineQuery; + if (reportEngineQuery.getReportProcessQuery() != null) + query = reportEngineQuery.getReportProcessQuery(); + for (int r = 0; r < query.getRestrictionCount(); r++) + { + if (r > 0) { + tr = new tr(); + tr.setClass("tr-parameter"); + } + + td td = new td(); + tr.addElement(td); + td.addElement(query.getInfoName(r)); + + td = new td(); + tr.addElement(td); + td.addElement(query.getInfoOperator(r)); + + td = new td(); + tr.addElement(td); + td.addElement(query.getInfoDisplayAll(r)); + + w.print(HTMLReportRenderer.compress(tr.toString(), minify)); + } + + w.print(""); + w.print("
"); + } + + StringBuilder tableWrapDiv = new StringBuilder(); + tableWrapDiv.append("
"); + + w.print(HTMLReportRenderer.compress(tableWrapDiv.toString(), minify)); + w.print(HTMLReportRenderer.compress(table.toString(), minify)); + } + + w.print(""); + tr tfoot = new tr(); + + for (int col = 0; col < printFormat.getItemCount(); col++) + { + MPrintFormatItem item = printFormat.getItem(col); + if (item.isPrinted()) + { + th th = new th(); + tfoot.addElement(th); + th.setTagText(item.getPrintName(language)); + } + } + tfoot.output(w); + w.print(""); + + thead thead = new thead(); + tbody tbody = new tbody(); + tbody.setNeedClosingTag(false); + + Boolean [] colSuppressRepeats = layout == null || layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(printFormat):layout.colSuppressRepeats; + Object [] preValues = null; + if (colSuppressRepeats != null){ + preValues = new Object [colSuppressRepeats.length]; + } + + int printColIndex = -1; + HashMap suppressMap = new HashMap<>(); + + // for all rows (-1 = header row) + for (int row = -1; row < printData.getRowCount(); row++) + { + tr tr = new tr(); + if (row != -1) + { + printData.setRowIndex(row); + if(printData.isFunctionRow()) + continue; + if (extension != null && !isExport) + { + extension.extendRowElement(tr, printData); + } + if (printData.isFunctionRow()) { + tr.setClass(cssPrefix + "-functionrow"); + } else if ( row < printData.getRowCount() && printData.isFunctionRow(row+1)) { + tr.setClass(cssPrefix + "-lastgrouprow"); + } + // add row to table body + //tbody.addElement(tr); + } else { + // add row to table header + thead.addElement(tr); + } + + printColIndex = -1; + // for all columns + for (int col = 0; col < columns.size(); col++) + { + Object colObj = columns.get(col); + MPrintFormatItem item = null; + InstanceAttributeColumn instanceAttributeColumn = null; + if (colObj instanceof MPrintFormatItem) + { + item = (MPrintFormatItem) colObj; + } + else if (colObj instanceof InstanceAttributeColumn) + { + instanceAttributeColumn = (InstanceAttributeColumn) colObj; + item = instanceAttributeColumn.getPrintFormatItem(); + } + if (item != null) + { + printColIndex++; + // header row + if (row == -1) + { + th th = new th(); + tr.addElement(th); + String columnHeader = instanceAttributeColumn != null ? instanceAttributeColumn.getName() : item.getPrintName(language); + th.addElement(Util.maskHTML(columnHeader)); + if (cssPrefix != null && instanceAttributeColumn == null) + { + MColumn column = MColumn.get(Env.getCtx(), item.getAD_Column_ID()); + if (column != null && column.getAD_Column_ID() > 0) + { + if (DisplayType.isNumeric(column.getAD_Reference_ID())) + { + th.setClass(cssPrefix + "-number"); + } + } + } + if (item.isSuppressNull()) + { + suppressMap.put(printColIndex, th); + th.setID("report-th-"+printColIndex); + } + // Calculate DateTables Options + if(isDataTableFunctionColumn(item)) { + if(item.isOrderBy()) + dataTableOptions.addPrintColumnIndex(FunctionTypes.ORDER_BY, item.isDesc() ? dataTableOptions.DESC_OFFSET + printColIndex : printColIndex); + if(item.isGroupBy()) + dataTableOptions.addPrintColumnIndex(FunctionTypes.GROUP_BY, printColIndex); + if(item.isSummarized()) + dataTableOptions.addPrintColumnIndex(FunctionTypes.SUM, printColIndex); + if(item.isCounted()) + dataTableOptions.addPrintColumnIndex(FunctionTypes.COUNT, printColIndex); + if(item.isMinCalc()) + dataTableOptions.addPrintColumnIndex(FunctionTypes.MIN, printColIndex); + if(item.isMaxCalc()) + dataTableOptions.addPrintColumnIndex(FunctionTypes.MAX, printColIndex); + if(item.isAveraged()) + dataTableOptions.addPrintColumnIndex(FunctionTypes.AVG, printColIndex); + if(item.isDeviationCalc()) + dataTableOptions.addPrintColumnIndex(FunctionTypes.DEVIATION, printColIndex); + if(item.isVarianceCalc()) + dataTableOptions.addPrintColumnIndex(FunctionTypes.VARIANCE, printColIndex); + } + + if(item.getAD_Column_ID() > 0) { + MColumn column = MColumn.get(item.getAD_Column_ID()); + String description = column.get_Translation(MElement.COLUMNNAME_Description,language.getAD_Language()); + if(!Util.isEmpty(description)) { + th.addAttribute("title", description); + } + } + } + else + { + td td = new td(); + tr.addElement(td); + MStyle style = item.getAD_FieldStyle_ID() > 0 ? MStyle.get(Env.getCtx(), item.getAD_FieldStyle_ID()) : null; + Object obj = instanceAttributeColumn != null ? instanceAttributeColumn.getPrintDataElement(row) + : printData.getNodeByPrintFormatItemId(item.getAD_PrintFormatItem_ID()); + if (obj == null || !ReportEngine.isDisplayPFItem(printData, item)){ + td.addElement(" "); + if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ + preValues[printColIndex] = null; + } + if (item.isSuppressNull() && obj != null && suppressMap.containsKey(printColIndex)) + suppressMap.remove(printColIndex); + } + else if (obj instanceof PrintDataElement) + { + PrintDataElement pde = (PrintDataElement) obj; + String value = pde.getValueDisplay(language); // formatted + + if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ + if (value.equals(preValues[printColIndex])){ + td.addElement(" "); + continue; + }else{ + preValues[printColIndex] = value; + } + } + + if (item.isSuppressNull() && obj != null && suppressMap.containsKey(printColIndex)) + suppressMap.remove(printColIndex); + + if (pde.getColumnName().endsWith("_ID") && extension != null && !isExport) + { + boolean isZoom = false; + if (item.getColumnName().equals("Record_ID")) { + Object tablePDE = printData.getNode("AD_Table_ID"); + if (tablePDE != null && tablePDE instanceof PrintDataElement) { + int tableID = -1; + try { + tableID = Integer.parseInt(((PrintDataElement)tablePDE).getValueAsString()); + } catch (Exception e) { + tableID = -1; + } + if (tableID > 0) { + MTable mTable = MTable.get(Env.getCtx(), tableID); + String tableName = mTable.getTableName(); + + value = reportEngine.getIdentifier(mTable, tableName, Integer.parseInt(value)); + + String foreignColumnName = tableName + "_ID"; + pde.setForeignColumnName(foreignColumnName); + isZoom = true; + } + } + } else { + isZoom = true; + } + if (isZoom) { + // check permission on the zoomed window + MTable mTable = MTable.get(Env.getCtx(), pde.getForeignColumnName().substring(0, pde.getForeignColumnName().length()-3)); + int Record_ID = -1; + try { + Record_ID = Integer.parseInt(pde.getValueAsString()); + } catch (Exception e) { + Record_ID = -1; + } + Boolean canAccess = null; + if (Record_ID >= 0 && mTable != null) { + int AD_Window_ID = Env.getZoomWindowID(mTable.get_ID(), Record_ID); + canAccess = MRole.getDefault().getWindowAccess(AD_Window_ID); + } + if (canAccess == null) { + isZoom = false; + } + } + if (isZoom) { + //link for column + a href = new a("javascript:void(0)"); + href.setID(pde.getColumnName() + "_" + row + "_a"); + td.addElement(href); + href.addElement(Util.maskHTML(value)); + if (cssPrefix != null) + href.setClass(cssPrefix + "-href"); + // Set Style + if(style != null && style.isWrapWithSpan()) + HTMLReportRenderer.setStyle(printData, href, style); + else + HTMLReportRenderer.setStyle(printData, td, style); + extension.extendIDColumn(row, td, href, pde); + } else { + // Set Style + if(style != null && style.isWrapWithSpan()) { + span span = new span(); + HTMLReportRenderer.setStyle(printData, span, style); + span.addElement(Util.maskHTML(value)); + td.addElement(span); + } + else { + HTMLReportRenderer.setStyle(printData, td, style); + td.addElement(Util.maskHTML(value)); + } + } + + } + else + { + // Set Style + if(style != null && style.isWrapWithSpan()) { + span span = new span(); + HTMLReportRenderer.setStyle(printData, span, style); + span.addElement(Util.maskHTML(value)); + td.addElement(span); + } + else { + HTMLReportRenderer.setStyle(printData, td, style); + td.addElement(Util.maskHTML(value)); + } + } + if (cssPrefix != null) + { + if (DisplayType.isNumeric(pde.getDisplayType())) + td.setClass(cssPrefix + "-number"); + else if (DisplayType.isDate(pde.getDisplayType())) + td.setClass(cssPrefix + "-date"); + else + td.setClass(cssPrefix + "-text"); + } + //just run with on record + if (row == 0) + HTMLReportRenderer.addCssInfo(printFormat, item, printColIndex, mapCssInfo); + } + else if (obj instanceof PrintData) + { + // ignore contained Data + } + else + log.log(Level.SEVERE, "Element not PrintData(Element) " + obj.getClass()); + } + } // printed + } // for all columns + + dataTableOptions.setPrintColLastIndex(printColIndex); + + /* output table header */ + if (row == -1){ + w.print(HTMLReportRenderer.compress(thead.toString(), minify)); + // output open of tbody + w.print(HTMLReportRenderer.compress(tbody.toString(), minify)); + }else{ + // output row by row + w.print(HTMLReportRenderer.compress(tr.toString(), minify)); + } + + } // for all rows + + w.print(""); + w.print(""); + if (suppressMap.size() > 0) + { + StringBuilder st = new StringBuilder(); + for(th t : suppressMap.values()) + { + if (st.length() > 0) + st.append(","); + st.append("#").append(t.getAttribute("id")); + } + st.append(" {\n\t\tdisplay:none;\n\t}"); + style styleTag = new style(); + styleTag.addElement(st.toString()); + w.print(HTMLReportRenderer.compress(styleTag.toString(), minify)); + } + if (!onlyTable) + { + w.print("
"); + w.print("
"); + String dataTableOptionString = dataTableOptions.getDataTableOptions("~./web/js"); + if( dataTableOptionString != null ) { + w.print(""); + } + w.print(""); + w.print(""); + } + w.flush(); + w.close(); + } + catch (Exception e) + { + log.log(Level.SEVERE, e.getMessage(), e); + throw new AdempiereException(e); + } + } // createHTML + + /** + * Is PrintFormat Item Function for DataTables + * @param item + * @return + */ + private boolean isDataTableFunctionColumn(MPrintFormatItem item) { + if(item.isOrderBy() + || item.isGroupBy() + || item.isSummarized() + || item.isCounted() + || item.isMinCalc() + || item.isAveraged() + || item.isDeviationCalc() + || item.isMaxCalc() + || item.isVarianceCalc()) + return true; + return false; + } + + /** + * If isExport, embed script content, otherwise embed script url + * @param doc + * @param isExport + * @throws IOException + * @throws URISyntaxException + */ + private void appendScripts (XhtmlDocument doc, boolean isExport) throws IOException, URISyntaxException{ + List urls = Arrays.asList("~./js/datatables/jquery.min.js","~./js/datatables/jquery.floatThead.min.js" + ,"~./js/datatables/datatables.js","~./js/datatables/jquery.dataTables.min.js","~./js/datatables/dataTables.rowGroup.min.js"); + if (isExport){ + // embed script by content + for (String extraScriptPath : urls){ + URL url = getClass().getResource(extraScriptPath.replace("~./", "/web/")); + appendInlineScriptContent (doc, readResource(url)); + } + } else { + // embed script by link + for (String extraScriptUrl : urls){ + embedScriptLink (doc, Executions.encodeURL(extraScriptUrl)); + } + } + } + + /** + * If isExport, embed css content, otherwise embed css url + * @param doc + * @param isExport + * @throws IOException + * @throws URISyntaxException + */ + private void appendStyles (XhtmlDocument doc, boolean isExport) throws IOException, URISyntaxException{ + List urls = Arrays.asList("~./js/datatables/datatables.css"); + if (isExport){ + // embed css by content + for (String extraStylePath : urls){ + URL url = getClass().getResource(extraStylePath.replace("~./", "/web/")); + HTMLReportRenderer.appendInlineCss(doc, readResource(url)); + } + } else { + // embed css by link + for (String extraStyleUrl : urls){ + embedStyleLink (doc, Executions.encodeURL(extraStyleUrl)); + } + } + } + + /** + * Read contents from URL + * @param url + * @return contents from URL + * @throws IOException + */ + private StringBuilder readResource(URL url) throws IOException { + StringBuilder builder = new StringBuilder(); + InputStream is = url.openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line); + } + reader.close(); + return builder; + } + + /** + * Embed script url into head tag + * @param doc + * @param scriptUrl + */ + protected void embedScriptLink (XhtmlDocument doc, String scriptUrl){ + script jslink = new script(); + jslink.setLanguage("javascript"); + jslink.setSrc(scriptUrl); + doc.appendHead(jslink); + } + + /** + * Embed css url into head tag + * @param doc + * @param scriptUrl + */ + protected void embedStyleLink (XhtmlDocument doc, String styleUrl){ + link csslink = new link(); + csslink.setType("text/css"); + csslink.setRel("stylesheet"); + csslink.setHref(styleUrl); + doc.appendHead(csslink); + } + + /** + * Embed script content into head tag + * @param doc + * @param buildScriptContent + */ + private void appendInlineScriptContent (XhtmlDocument doc, StringBuilder buildScriptContent){ + if (buildScriptContent.length() > 0){ + buildScriptContent.insert(0, ""); + doc.appendHead(buildScriptContent.toString()); + } + } +} diff --git a/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DatatableReportRendererConfiguration.java b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DatatableReportRendererConfiguration.java new file mode 100644 index 0000000000..5f6c676164 --- /dev/null +++ b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DatatableReportRendererConfiguration.java @@ -0,0 +1,141 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.zk.datatable; + +import java.io.File; +import java.io.Writer; + +import org.compiere.print.IHTMLExtension; +import org.compiere.util.Language; +import org.idempiere.print.renderer.IReportRendererConfiguration; + +public class DatatableReportRendererConfiguration implements IReportRendererConfiguration { + + public static final String ID = "Datatables"; + public static final String FILE_EXTENSION = "datatables.html"; + + private boolean onlyTable; + private IHTMLExtension extension; + private boolean isExport; + private File file; + private Writer writer; + private Language language; + + public DatatableReportRendererConfiguration() { + } + + /** + * Is only generate html table instead of full html content + * @return true if only generate html table + */ + public boolean isOnlyTable() { + return onlyTable; + } + + /** + * Set only generate html table instead of full html content + * @param onlyTable + */ + public DatatableReportRendererConfiguration setOnlyTable(boolean onlyTable) { + this.onlyTable = onlyTable; + return this; + } + + /** + * Get html extension + */ + public IHTMLExtension getExtension() { + return extension; + } + + /** + * Set html extension + * @param extension + */ + public DatatableReportRendererConfiguration setExtension(IHTMLExtension extension) { + this.extension = extension; + return this; + } + + /** + * Is for export or preview + * @return true for export + */ + public boolean isExport() { + return isExport; + } + + /** + * Set for export or preview + * @param isExport + */ + public DatatableReportRendererConfiguration setExport(boolean isExport) { + this.isExport = isExport; + return this; + } + + @Override + public File getOutputFile() { + return file; + } + + /** + * Set output file + * @param file + */ + public DatatableReportRendererConfiguration setOutputFile(File file) { + this.file = file; + return this; + } + + @Override + public Writer getOutputWriter() { + return writer; + } + + /** + * Set output writer + * @param writer + */ + public DatatableReportRendererConfiguration setOutputWriter(Writer writer) { + this.writer = writer; + return this; + } + + /** + * Set content language + * @param language + */ + public DatatableReportRendererConfiguration setLanguage(Language language) { + this.language = language; + return this; + } + + /** + * Get content language + * @return language + */ + public Language getLanguage() { + return language; + } + +} diff --git a/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DatatableReportViewerRenderer.java b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DatatableReportViewerRenderer.java new file mode 100644 index 0000000000..dbc7812510 --- /dev/null +++ b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/DatatableReportViewerRenderer.java @@ -0,0 +1,112 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ +package org.idempiere.zk.datatable; + +import java.io.File; +import java.util.logging.Level; + +import org.adempiere.base.Core; +import org.adempiere.webui.report.HTMLExtension; +import org.adempiere.webui.window.ZkReportViewer; +import org.compiere.print.ReportEngine; +import org.compiere.tools.FileUtil; +import org.compiere.util.CLogger; +import org.idempiere.print.renderer.IReportRenderer; +import org.idempiere.print.renderer.IReportRendererConfiguration; +import org.idempiere.ui.zk.report.IReportViewerRenderer; +import org.osgi.service.component.annotations.Component; +import org.zkoss.util.media.AMedia; +import org.zkoss.zk.ui.Executions; + +import com.google.common.net.MediaType; + +@Component(service = IReportViewerRenderer.class, immediate = true) +public class DatatableReportViewerRenderer implements IReportViewerRenderer { + + private static final CLogger log = CLogger.getCLogger(DatatableReportViewerRenderer.class); + + public DatatableReportViewerRenderer() { + } + + @Override + public String getId() { + return DatatableReportRendererConfiguration.ID; + } + + @Override + public String getExportLabel() { + return getId(); + } + + @Override + public String getContentType() { + return MediaType.HTML_UTF_8.toString(); + } + + @Override + public String getFileExtension() { + return DatatableReportRendererConfiguration.FILE_EXTENSION; + } + + @Override + public boolean isExport() { + return true; + } + + @Override + public boolean isPreview(boolean roleCanExport) { + return true; + } + + @Override + public AMedia renderMedia(ZkReportViewer viewer, boolean export) { + ReportEngine reportEngine = viewer.getReportEngine(); + try { + String path = System.getProperty("java.io.tmpdir"); + String prefix = makePrefix(reportEngine.getName()); + if (prefix.length() < 3) + prefix += "_".repeat(3-prefix.length()); + if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Path="+path + " Prefix="+prefix); + File file = FileUtil.createTempFile(prefix, "."+getFileExtension(), new File(path)); + String contextPath = Executions.getCurrent().getContextPath(); + IReportRenderer renderer = Core.getReportRenderer(getId()); + DatatableReportRendererConfiguration config = new DatatableReportRendererConfiguration() + .setLanguage(reportEngine.getPrintFormat().getLanguage()) + .setOutputFile(file) + .setExport(export) + .setExtension(new HTMLExtension(contextPath, "rp", viewer.getUuid())); + renderer.renderReport(reportEngine, config); + return new AMedia(file.getName(), getFileExtension(), getContentType(), file, false); + } catch (Exception e) { + if (e instanceof RuntimeException) + throw (RuntimeException)e; + else + throw new RuntimeException(e); + } + } + + @Override + public boolean isSameContentForExportAndPreview() { + return false; + } + +} diff --git a/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/package-info.java b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/package-info.java new file mode 100644 index 0000000000..f170d1cd3c --- /dev/null +++ b/org.idempiere.zk.datatable/src/org/idempiere/zk/datatable/package-info.java @@ -0,0 +1,26 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + **********************************************************************/ + +/** + * Provide classes for datatables report viewer renderer + */ +package org.idempiere.zk.datatable; \ No newline at end of file diff --git a/org.idempiere.zk.datatable/src/web/js/datatables/dataTables.rowGroup.min.js b/org.idempiere.zk.datatable/src/web/js/datatables/dataTables.rowGroup.min.js new file mode 100644 index 0000000000..d854113424 --- /dev/null +++ b/org.idempiere.zk.datatable/src/web/js/datatables/dataTables.rowGroup.min.js @@ -0,0 +1,28 @@ +/*! + SpryMedia Ltd. + + This source file is free software, available under the following license: + MIT license - http://datatables.net/license/mit + + This source file 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 license files for details. + + For details please refer to: http://www.datatables.net + RowGroup 1.2.0 + ©2017-2022 SpryMedia Ltd - datatables.net/license +*/ +var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(b,c,d){b instanceof String&&(b=String(b));for(var g=b.length,e=0;e").append(b("").attr("colspan",this._colspan()).attr("scope","row").append(a))).addClass(this.c.className).addClass(f).addClass("dtrg-level-"+h)}});k.defaults={className:"dtrg-group",dataSrc:0,emptyDataGroup:"No group",enable:!0,endClassName:"dtrg-end",endRender:null,startClassName:"dtrg-start",startRender:function(a,f){return f}};k.version="1.2.0";b.fn.dataTable.RowGroup= +k;b.fn.DataTable.RowGroup=k;e.Api.register("rowGroup()",function(){return this});e.Api.register("rowGroup().disable()",function(){return this.iterator("table",function(a){a.rowGroup&&a.rowGroup.enable(!1)})});e.Api.register("rowGroup().enable()",function(a){return this.iterator("table",function(f){f.rowGroup&&f.rowGroup.enable(a===g?!0:a)})});e.Api.register("rowGroup().enabled()",function(){var a=this.context;return a.length&&a[0].rowGroup?a[0].rowGroup.enabled():!1});e.Api.register("rowGroup().dataSrc()", +function(a){return a===g?this.context[0].rowGroup.dataSrc():this.iterator("table",function(f){f.rowGroup&&f.rowGroup.dataSrc(a)})});b(d).on("preInit.dt.dtrg",function(a,f,h){"dt"===a.namespace&&(a=f.oInit.rowGroup,h=e.defaults.rowGroup,a||h)&&(h=b.extend({},h,a),!1!==a&&new k(f,h))});return k}); diff --git a/org.idempiere.zk.datatable/src/web/js/datatables/datatables.css b/org.idempiere.zk.datatable/src/web/js/datatables/datatables.css new file mode 100644 index 0000000000..401040caee --- /dev/null +++ b/org.idempiere.zk.datatable/src/web/js/datatables/datatables.css @@ -0,0 +1,1579 @@ +/* + * This combined file was created by the DataTables downloader builder: + * https://datatables.net/download + * + * To rebuild or modify this file with the latest versions of the included + * software please visit: + * https://datatables.net/download/#dt/dt-1.12.1/b-2.2.3/b-colvis-2.2.3/cr-1.5.6/fh-3.2.4/sp-2.0.2 + * + * Included libraries: + * DataTables 1.12.1, Buttons 2.2.3, Column visibility 2.2.3, ColReorder 1.5.6, FixedHeader 3.2.4, SearchPanes 2.0.2 + */ + +@charset "UTF-8"; +table.dataTable td.dt-control { + text-align: center; + cursor: pointer; +} +table.dataTable td.dt-control:before { + height: 1em; + width: 1em; + margin-top: -9px; + display: inline-block; + color: white; + border: 0.15em solid white; + border-radius: 1em; + box-shadow: 0 0 0.2em #444; + box-sizing: content-box; + text-align: center; + text-indent: 0 !important; + font-family: "Courier New", Courier, monospace; + line-height: 1em; + content: "+"; + background-color: #31b131; +} +table.dataTable tr.dt-hasChild td.dt-control:before { + content: "-"; + background-color: #d33333; +} + +table.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled, +table.dataTable thead > tr > td.sorting, +table.dataTable thead > tr > td.sorting_asc, +table.dataTable thead > tr > td.sorting_desc, +table.dataTable thead > tr > td.sorting_asc_disabled, +table.dataTable thead > tr > td.sorting_desc_disabled { + cursor: pointer; + position: relative; + padding-right: 26px; +} +table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:after, +table.dataTable thead > tr > td.sorting:before, +table.dataTable thead > tr > td.sorting:after, +table.dataTable thead > tr > td.sorting_asc:before, +table.dataTable thead > tr > td.sorting_asc:after, +table.dataTable thead > tr > td.sorting_desc:before, +table.dataTable thead > tr > td.sorting_desc:after, +table.dataTable thead > tr > td.sorting_asc_disabled:before, +table.dataTable thead > tr > td.sorting_asc_disabled:after, +table.dataTable thead > tr > td.sorting_desc_disabled:before, +table.dataTable thead > tr > td.sorting_desc_disabled:after { + position: absolute; + display: block; + opacity: 0.125; + right: 10px; + line-height: 9px; + font-size: 0.9em; +} +table.dataTable thead > tr > th.sorting:before, table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:before, table.dataTable thead > tr > th.sorting_asc_disabled:before, table.dataTable thead > tr > th.sorting_desc_disabled:before, +table.dataTable thead > tr > td.sorting:before, +table.dataTable thead > tr > td.sorting_asc:before, +table.dataTable thead > tr > td.sorting_desc:before, +table.dataTable thead > tr > td.sorting_asc_disabled:before, +table.dataTable thead > tr > td.sorting_desc_disabled:before { + bottom: 50%; + content: "▴"; +} +table.dataTable thead > tr > th.sorting:after, table.dataTable thead > tr > th.sorting_asc:after, table.dataTable thead > tr > th.sorting_desc:after, table.dataTable thead > tr > th.sorting_asc_disabled:after, table.dataTable thead > tr > th.sorting_desc_disabled:after, +table.dataTable thead > tr > td.sorting:after, +table.dataTable thead > tr > td.sorting_asc:after, +table.dataTable thead > tr > td.sorting_desc:after, +table.dataTable thead > tr > td.sorting_asc_disabled:after, +table.dataTable thead > tr > td.sorting_desc_disabled:after { + top: 50%; + content: "▾"; +} +table.dataTable thead > tr > th.sorting_asc:before, table.dataTable thead > tr > th.sorting_desc:after, +table.dataTable thead > tr > td.sorting_asc:before, +table.dataTable thead > tr > td.sorting_desc:after { + opacity: 0.6; +} +table.dataTable thead > tr > th.sorting_desc_disabled:after, table.dataTable thead > tr > th.sorting_asc_disabled:before, +table.dataTable thead > tr > td.sorting_desc_disabled:after, +table.dataTable thead > tr > td.sorting_asc_disabled:before { + display: none; +} +table.dataTable thead > tr > th:active, +table.dataTable thead > tr > td:active { + outline: none; +} + +div.dataTables_scrollBody table.dataTable thead > tr > th:before, div.dataTables_scrollBody table.dataTable thead > tr > th:after, +div.dataTables_scrollBody table.dataTable thead > tr > td:before, +div.dataTables_scrollBody table.dataTable thead > tr > td:after { + display: none; +} + +div.dataTables_processing { + position: absolute; + top: 50%; + left: 50%; + width: 200px; + margin-left: -100px; + margin-top: -26px; + text-align: center; + padding: 2px; +} +div.dataTables_processing > div:last-child { + position: relative; + width: 80px; + height: 15px; + margin: 1em auto; +} +div.dataTables_processing > div:last-child > div { + position: absolute; + top: 0; + width: 13px; + height: 13px; + border-radius: 50%; + background: rgba(13, 110, 253, 0.9); + animation-timing-function: cubic-bezier(0, 1, 1, 0); +} +div.dataTables_processing > div:last-child > div:nth-child(1) { + left: 8px; + animation: datatables-loader-1 0.6s infinite; +} +div.dataTables_processing > div:last-child > div:nth-child(2) { + left: 8px; + animation: datatables-loader-2 0.6s infinite; +} +div.dataTables_processing > div:last-child > div:nth-child(3) { + left: 32px; + animation: datatables-loader-2 0.6s infinite; +} +div.dataTables_processing > div:last-child > div:nth-child(4) { + left: 56px; + animation: datatables-loader-3 0.6s infinite; +} + +@keyframes datatables-loader-1 { + 0% { + transform: scale(0); + } + 100% { + transform: scale(1); + } +} +@keyframes datatables-loader-3 { + 0% { + transform: scale(1); + } + 100% { + transform: scale(0); + } +} +@keyframes datatables-loader-2 { + 0% { + transform: translate(0, 0); + } + 100% { + transform: translate(24px, 0); + } +} +table.dataTable.nowrap th, table.dataTable.nowrap td { + white-space: nowrap; +} +table.dataTable th.dt-left, +table.dataTable td.dt-left { + text-align: left; +} +table.dataTable th.dt-center, +table.dataTable td.dt-center, +table.dataTable td.dataTables_empty { + text-align: center; +} +table.dataTable th.dt-right, +table.dataTable td.dt-right { + text-align: right; +} +table.dataTable th.dt-justify, +table.dataTable td.dt-justify { + text-align: justify; +} +table.dataTable th.dt-nowrap, +table.dataTable td.dt-nowrap { + white-space: nowrap; +} +table.dataTable thead th, +table.dataTable thead td, +table.dataTable tfoot th, +table.dataTable tfoot td { + text-align: left; +} +table.dataTable thead th.dt-head-left, +table.dataTable thead td.dt-head-left, +table.dataTable tfoot th.dt-head-left, +table.dataTable tfoot td.dt-head-left { + text-align: left; +} +table.dataTable thead th.dt-head-center, +table.dataTable thead td.dt-head-center, +table.dataTable tfoot th.dt-head-center, +table.dataTable tfoot td.dt-head-center { + text-align: center; +} +table.dataTable thead th.dt-head-right, +table.dataTable thead td.dt-head-right, +table.dataTable tfoot th.dt-head-right, +table.dataTable tfoot td.dt-head-right { + text-align: right; +} +table.dataTable thead th.dt-head-justify, +table.dataTable thead td.dt-head-justify, +table.dataTable tfoot th.dt-head-justify, +table.dataTable tfoot td.dt-head-justify { + text-align: justify; +} +table.dataTable thead th.dt-head-nowrap, +table.dataTable thead td.dt-head-nowrap, +table.dataTable tfoot th.dt-head-nowrap, +table.dataTable tfoot td.dt-head-nowrap { + white-space: nowrap; +} +table.dataTable tbody th.dt-body-left, +table.dataTable tbody td.dt-body-left { + text-align: left; +} +table.dataTable tbody th.dt-body-center, +table.dataTable tbody td.dt-body-center { + text-align: center; +} +table.dataTable tbody th.dt-body-right, +table.dataTable tbody td.dt-body-right { + text-align: right; +} +table.dataTable tbody th.dt-body-justify, +table.dataTable tbody td.dt-body-justify { + text-align: justify; +} +table.dataTable tbody th.dt-body-nowrap, +table.dataTable tbody td.dt-body-nowrap { + white-space: nowrap; +} + +/* + * Table styles + */ +table.dataTable { + width: 100%; + margin: 0 auto; + clear: both; + border-collapse: separate; + border-spacing: 0; + /* + * Header and footer styles + */ + /* + * Body styles + */ +} +table.dataTable thead th, +table.dataTable tfoot th { + font-weight: bold; +} +table.dataTable thead th, +table.dataTable thead td { + padding: 10px; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); +} +table.dataTable thead th:active, +table.dataTable thead td:active { + outline: none; +} +table.dataTable tfoot th, +table.dataTable tfoot td { + padding: 10px 10px 6px 10px; + border-top: 1px solid rgba(0, 0, 0, 0.3); +} +table.dataTable tbody tr { + background-color: transparent; +} +table.dataTable tbody tr.selected > * { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.9); + color: white; +} +table.dataTable tbody th, +table.dataTable tbody td { + padding: 8px 10px; +} +table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td { + border-top: 1px solid rgba(0, 0, 0, 0.15); +} +table.dataTable.row-border tbody tr:first-child th, +table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th, +table.dataTable.display tbody tr:first-child td { + border-top: none; +} +table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td { + border-top: 1px solid rgba(0, 0, 0, 0.15); + border-right: 1px solid rgba(0, 0, 0, 0.15); +} +table.dataTable.cell-border tbody tr th:first-child, +table.dataTable.cell-border tbody tr td:first-child { + border-left: 1px solid rgba(0, 0, 0, 0.15); +} +table.dataTable.cell-border tbody tr:first-child th, +table.dataTable.cell-border tbody tr:first-child td { + border-top: none; +} +table.dataTable.stripe > tbody > tr.odd > *, table.dataTable.display > tbody > tr.odd > * { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.023); +} +table.dataTable.stripe > tbody > tr.odd.selected > *, table.dataTable.display > tbody > tr.odd.selected > * { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.923); +} +table.dataTable.hover > tbody > tr:hover > *, table.dataTable.display > tbody > tr:hover > * { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.035); +} +table.dataTable.hover > tbody > tr.selected:hover > *, table.dataTable.display > tbody > tr.selected:hover > * { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.935); +} +table.dataTable.order-column > tbody tr > .sorting_1, +table.dataTable.order-column > tbody tr > .sorting_2, +table.dataTable.order-column > tbody tr > .sorting_3, table.dataTable.display > tbody tr > .sorting_1, +table.dataTable.display > tbody tr > .sorting_2, +table.dataTable.display > tbody tr > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.019); +} +table.dataTable.order-column > tbody tr.selected > .sorting_1, +table.dataTable.order-column > tbody tr.selected > .sorting_2, +table.dataTable.order-column > tbody tr.selected > .sorting_3, table.dataTable.display > tbody tr.selected > .sorting_1, +table.dataTable.display > tbody tr.selected > .sorting_2, +table.dataTable.display > tbody tr.selected > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.919); +} +table.dataTable.display > tbody > tr.odd > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.odd > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.054); +} +table.dataTable.display > tbody > tr.odd > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.odd > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.047); +} +table.dataTable.display > tbody > tr.odd > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.odd > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.039); +} +table.dataTable.display > tbody > tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.odd.selected > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.954); +} +table.dataTable.display > tbody > tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.odd.selected > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.947); +} +table.dataTable.display > tbody > tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.odd.selected > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.939); +} +table.dataTable.display > tbody > tr.even > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.019); +} +table.dataTable.display > tbody > tr.even > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.011); +} +table.dataTable.display > tbody > tr.even > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.even > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.003); +} +table.dataTable.display > tbody > tr.even.selected > .sorting_1, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.919); +} +table.dataTable.display > tbody > tr.even.selected > .sorting_2, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.911); +} +table.dataTable.display > tbody > tr.even.selected > .sorting_3, table.dataTable.order-column.stripe > tbody > tr.even.selected > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.903); +} +table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.082); +} +table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.074); +} +table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.062); +} +table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.982); +} +table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.974); +} +table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.962); +} +table.dataTable.no-footer { + border-bottom: 1px solid rgba(0, 0, 0, 0.3); +} +table.dataTable.compact thead th, +table.dataTable.compact thead td { + padding: 4px 17px; +} +table.dataTable.compact tfoot th, +table.dataTable.compact tfoot td { + padding: 4px; +} +table.dataTable.compact tbody th, +table.dataTable.compact tbody td { + padding: 4px; +} + +table.dataTable th, +table.dataTable td { + box-sizing: content-box; +} + +/* + * Control feature layout + */ +.dataTables_wrapper { + position: relative; + clear: both; +} +.dataTables_wrapper { + float: left; +} +.dataTables_wrapper .dataTables_length select { + border: 1px solid #aaa; + border-radius: 3px; + padding: 5px; + background-color: transparent; + padding: 4px; +} +.dataTables_wrapper .dataTables_filter { + float: right; + text-align: right; +} +.dataTables_wrapper .dataTables_filter input { + border: 1px solid #aaa; + border-radius: 3px; + padding: 5px; + background-color: transparent; + margin-left: 3px; +} +.dataTables_wrapper .dataTables_info { + clear: both; + float: left; + padding-top: 0.755em; +} +.dataTables_wrapper .dataTables_paginate { + float: right; + text-align: right; + padding-top: 0.25em; +} +.dataTables_wrapper .dataTables_paginate .paginate_button { + box-sizing: border-box; + display: inline-block; + min-width: 1.5em; + padding: 0.5em 1em; + margin-left: 2px; + text-align: center; + text-decoration: none !important; + cursor: pointer; + color: #333 !important; + border: 1px solid transparent; + border-radius: 2px; +} +.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover { + color: #333 !important; + border: 1px solid rgba(0, 0, 0, 0.3); + background-color: rgba(230, 230, 230, 0.1); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(230, 230, 230, 0.1)), color-stop(100%, rgba(0, 0, 0, 0.1))); + /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Chrome10+,Safari5.1+ */ + background: -moz-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* FF3.6+ */ + background: -ms-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* IE10+ */ + background: -o-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + /* W3C */ +} +.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active { + cursor: default; + color: #666 !important; + border: 1px solid transparent; + background: transparent; + box-shadow: none; +} +.dataTables_wrapper .dataTables_paginate .paginate_button:hover { + color: white !important; + border: 1px solid #111; + background-color: #585858; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111)); + /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #585858 0%, #111 100%); + /* Chrome10+,Safari5.1+ */ + background: -moz-linear-gradient(top, #585858 0%, #111 100%); + /* FF3.6+ */ + background: -ms-linear-gradient(top, #585858 0%, #111 100%); + /* IE10+ */ + background: -o-linear-gradient(top, #585858 0%, #111 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, #585858 0%, #111 100%); + /* W3C */ +} +.dataTables_wrapper .dataTables_paginate .paginate_button:active { + outline: none; + background-color: #2b2b2b; + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c)); + /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); + /* Chrome10+,Safari5.1+ */ + background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); + /* FF3.6+ */ + background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); + /* IE10+ */ + background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); + /* Opera 11.10+ */ + background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%); + /* W3C */ + box-shadow: inset 0 0 3px #111; +} +.dataTables_wrapper .dataTables_paginate .ellipsis { + padding: 0 1em; +} +.dataTables_wrapper .dataTables_length, +.dataTables_wrapper .dataTables_filter, +.dataTables_wrapper .dataTables_info, +.dataTables_wrapper .dataTables_processing, +.dataTables_wrapper .dataTables_paginate { + color: #333; +} +.dataTables_wrapper .dataTables_scroll { + clear: both; +} +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody { + -webkit-overflow-scrolling: touch; +} +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td { + vertical-align: middle; +} +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing { + height: 0; + overflow: hidden; + margin: 0 !important; + padding: 0 !important; +} +.dataTables_wrapper.no-footer .dataTables_scrollBody { + border-bottom: 1px solid rgba(0, 0, 0, 0.3); +} +.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable, +.dataTables_wrapper.no-footer div.dataTables_scrollBody > table { + border-bottom: none; +} +.dataTables_wrapper:after { + visibility: hidden; + display: block; + content: ""; + clear: both; + height: 0; +} + +@media screen and (max-width: 767px) { + .dataTables_wrapper .dataTables_info, +.dataTables_wrapper .dataTables_paginate { + float: none; + text-align: center; + } + .dataTables_wrapper .dataTables_paginate { + margin-top: 0.5em; + } +} +@media screen and (max-width: 640px) { + .dataTables_wrapper .dataTables_length, +.dataTables_wrapper .dataTables_filter { + float: none; + text-align: center; + } + .dataTables_wrapper .dataTables_filter { + margin-top: 0.5em; + } +} + + +@keyframes dtb-spinner { + 100% { + transform: rotate(360deg); + } +} +@-o-keyframes dtb-spinner { + 100% { + -o-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-ms-keyframes dtb-spinner { + 100% { + -ms-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-webkit-keyframes dtb-spinner { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@-moz-keyframes dtb-spinner { + 100% { + -moz-transform: rotate(360deg); + transform: rotate(360deg); + } +} +div.dataTables_wrapper { + position: relative; +} + +div.dt-buttons { + position: initial; +} + +div.dt-button-info { + position: fixed; + top: 50%; + left: 50%; + width: 400px; + margin-top: -100px; + margin-left: -200px; + background-color: white; + border: 2px solid #111; + box-shadow: 3px 4px 10px 1px rgba(0, 0, 0, 0.3); + border-radius: 3px; + text-align: center; + z-index: 21; +} +div.dt-button-info h2 { + padding: 0.5em; + margin: 0; + font-weight: normal; + border-bottom: 1px solid #ddd; + background-color: #f3f3f3; +} +div.dt-button-info > div { + padding: 1em; +} + +div.dtb-popover-close { + position: absolute; + top: 10px; + right: 10px; + width: 22px; + height: 22px; + border: 1px solid #eaeaea; + background-color: #f9f9f9; + text-align: center; + border-radius: 3px; + cursor: pointer; + z-index: 2003; +} + +button.dtb-hide-drop { + display: none !important; +} + +div.dt-button-collection-title { + text-align: center; + padding: 0.3em 0 0.5em; + margin-left: 0.5em; + margin-right: 0.5em; + font-size: 0.9em; +} + +div.dt-button-collection-title:empty { + display: none; +} + +span.dt-button-spacer { + display: inline-block; + margin: 0.5em; + white-space: nowrap; +} +span.dt-button-spacer.bar { + border-left: 1px solid rgba(0, 0, 0, 0.3); + vertical-align: middle; + padding-left: 0.5em; +} +span.dt-button-spacer.bar:empty { + height: 1em; + width: 1px; + padding-left: 0; +} + +div.dt-button-collection span.dt-button-spacer { + width: 100%; + font-size: 0.9em; + text-align: center; + margin: 0.5em 0; +} +div.dt-button-collection span.dt-button-spacer:empty { + height: 0; + width: 100%; +} +div.dt-button-collection span.dt-button-spacer.bar { + border-left: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.3); + padding-left: 0; +} + +button.dt-button, +div.dt-button, +a.dt-button, +input.dt-button { + position: relative; + display: inline-block; + box-sizing: border-box; + margin-left: 0.167em; + margin-right: 0.167em; + margin-bottom: 0.333em; + padding: 0.5em 1em; + border: 1px solid rgba(0, 0, 0, 0.3); + border-radius: 2px; + cursor: pointer; + font-size: 0.88em; + line-height: 1.6em; + color: black; + white-space: nowrap; + overflow: hidden; + background-color: rgba(0, 0, 0, 0.1); + /* Fallback */ + background: linear-gradient(to bottom, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="rgba(230, 230, 230, 0.1)", EndColorStr="rgba(0, 0, 0, 0.1)"); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + text-decoration: none; + outline: none; + text-overflow: ellipsis; +} +button.dt-button:first-child, +div.dt-button:first-child, +a.dt-button:first-child, +input.dt-button:first-child { + margin-left: 0; +} +button.dt-button.disabled, +div.dt-button.disabled, +a.dt-button.disabled, +input.dt-button.disabled { + cursor: default; + opacity: 0.4; +} +button.dt-button:active:not(.disabled), +div.dt-button:active:not(.disabled), +a.dt-button:active:not(.disabled), +input.dt-button:active:not(.disabled) { + background-color: rgba(0, 0, 0, 0.1); + /* Fallback */ + background: linear-gradient(to bottom, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="rgba(179, 179, 179, 0.1)", EndColorStr="rgba(0, 0, 0, 0.1)"); + box-shadow: inset 1px 1px 3px #999999; +} +button.dt-button:active:not(.disabled):hover:not(.disabled), +div.dt-button:active:not(.disabled):hover:not(.disabled), +a.dt-button:active:not(.disabled):hover:not(.disabled), +input.dt-button:active:not(.disabled):hover:not(.disabled) { + box-shadow: inset 1px 1px 3px #999999; + background-color: rgba(0, 0, 0, 0.1); + /* Fallback */ + background: linear-gradient(to bottom, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="rgba(128, 128, 128, 0.1)", EndColorStr="rgba(0, 0, 0, 0.1)"); +} +button.dt-button:hover, +div.dt-button:hover, +a.dt-button:hover, +input.dt-button:hover { + text-decoration: none; +} +button.dt-button:hover:not(.disabled), +div.dt-button:hover:not(.disabled), +a.dt-button:hover:not(.disabled), +input.dt-button:hover:not(.disabled) { + border: 1px solid #666; + background-color: rgba(0, 0, 0, 0.1); + /* Fallback */ + background: linear-gradient(to bottom, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="rgba(153, 153, 153, 0.1)", EndColorStr="rgba(0, 0, 0, 0.1)"); +} +button.dt-button:focus:not(.disabled), +div.dt-button:focus:not(.disabled), +a.dt-button:focus:not(.disabled), +input.dt-button:focus:not(.disabled) { + border: 1px solid #426c9e; + text-shadow: 0 1px 0 #c4def1; + outline: none; + background-color: #79ace9; + /* Fallback */ + background: linear-gradient(to bottom, #d1e2f7 0%, #79ace9 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="#d1e2f7", EndColorStr="#79ace9"); +} +button.dt-button.active:focus:not(.disabled), +div.dt-button.active:focus:not(.disabled), +a.dt-button.active:focus:not(.disabled), +input.dt-button.active:focus:not(.disabled) { + background: linear-gradient(to bottom, #d1e2f7 0%, #79ace9 100%) !important; +} +button.dt-button span.dt-down-arrow, +div.dt-button span.dt-down-arrow, +a.dt-button span.dt-down-arrow, +input.dt-button span.dt-down-arrow { + position: relative; + top: -2px; + color: rgba(70, 70, 70, 0.75); + font-size: 8px; + padding-left: 10px; + line-height: 1em; +} + +.dt-button embed { + outline: none; +} + +div.dt-buttons { + float: left; +} +div.dt-buttons.buttons-right { + float: right; +} + +div.dataTables_layout_cell div.dt-buttons { + float: none; +} +div.dataTables_layout_cell div.dt-buttons.buttons-right { + float: none; +} + +div.dt-btn-split-wrapper { + display: inline-block; +} + +div.dt-button-collection { + position: absolute; + top: 0; + left: 0; + width: 200px; + margin-top: 3px; + margin-bottom: 3px; + padding: 4px 4px 2px 4px; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.4); + background-color: white; + overflow: hidden; + z-index: 2002; + border-radius: 5px; + box-shadow: 3px 4px 10px 1px rgba(0, 0, 0, 0.3); + box-sizing: border-box; +} +div.dt-button-collection button.dt-button, +div.dt-button-collection div.dt-button, +div.dt-button-collection a.dt-button { + position: relative; + left: 0; + right: 0; + width: 100%; + display: block; + float: none; + margin: 4px 0 2px 0; +} +div.dt-button-collection button.dt-button:active:not(.disabled), div.dt-button-collection button.dt-button.active:not(.disabled), +div.dt-button-collection div.dt-button:active:not(.disabled), +div.dt-button-collection div.dt-button.active:not(.disabled), +div.dt-button-collection a.dt-button:active:not(.disabled), +div.dt-button-collection a.dt-button.active:not(.disabled) { + background-color: #dadada; + /* Fallback */ + background: linear-gradient(to bottom, #f0f0f0 0%, #dadada 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="#f0f0f0", EndColorStr="#dadada"); + box-shadow: inset 1px 1px 3px #666; +} +div.dt-button-collection button.dt-button:first-child, +div.dt-button-collection div.dt-button:first-child, +div.dt-button-collection a.dt-button:first-child { + margin-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +div.dt-button-collection button.dt-button:last-child, +div.dt-button-collection div.dt-button:last-child, +div.dt-button-collection a.dt-button:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; +} +div.dt-button-collection div.dt-btn-split-wrapper { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + align-content: flex-start; + align-items: stretch; + margin: 4px 0 2px 0; +} +div.dt-button-collection div.dt-btn-split-wrapper button.dt-button { + margin: 0; + display: inline-block; + width: 0; + flex-grow: 1; + flex-shrink: 0; + flex-basis: 50px; + border-radius: 0; +} +div.dt-button-collection div.dt-btn-split-wrapper button.dt-btn-split-drop { + min-width: 20px; + flex-grow: 0; + flex-shrink: 0; + flex-basis: 0; +} +div.dt-button-collection div.dt-btn-split-wrapper:first-child { + margin-top: 0; +} +div.dt-button-collection div.dt-btn-split-wrapper:first-child button.dt-button { + border-top-left-radius: 3px; +} +div.dt-button-collection div.dt-btn-split-wrapper:first-child button.dt-btn-split-drop { + border-top-right-radius: 3px; +} +div.dt-button-collection div.dt-btn-split-wrapper:last-child button.dt-button { + border-bottom-left-radius: 3px; +} +div.dt-button-collection div.dt-btn-split-wrapper:last-child button.dt-btn-split-drop { + border-bottom-right-radius: 3px; +} +div.dt-button-collection div.dt-btn-split-wrapper:active:not(.disabled) button.dt-button, div.dt-button-collection div.dt-btn-split-wrapper.active:not(.disabled) button.dt-button { + background-color: #dadada; + /* Fallback */ + background: linear-gradient(to bottom, #f0f0f0 0%, #dadada 100%); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr="#f0f0f0", EndColorStr="#dadada"); + box-shadow: inset 0px 0px 4px #666; +} +div.dt-button-collection div.dt-btn-split-wrapper:active:not(.disabled) button.dt-btn-split-drop, div.dt-button-collection div.dt-btn-split-wrapper.active:not(.disabled) button.dt-btn-split-drop { + box-shadow: none; +} +div.dt-button-collection.fixed .dt-button:first-child { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +div.dt-button-collection.fixed .dt-button:last-child { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +div.dt-button-collection.fixed { + position: fixed; + display: block; + top: 50%; + left: 50%; + margin-left: -75px; + border-radius: 5px; + background-color: white; +} +div.dt-button-collection.fixed.two-column { + margin-left: -200px; +} +div.dt-button-collection.fixed.three-column { + margin-left: -225px; +} +div.dt-button-collection.fixed.four-column { + margin-left: -300px; +} +div.dt-button-collection.fixed.columns { + margin-left: -409px; +} +@media screen and (max-width: 1024px) { + div.dt-button-collection.fixed.columns { + margin-left: -308px; + } +} +@media screen and (max-width: 640px) { + div.dt-button-collection.fixed.columns { + margin-left: -203px; + } +} +@media screen and (max-width: 460px) { + div.dt-button-collection.fixed.columns { + margin-left: -100px; + } +} +div.dt-button-collection.fixed > :last-child { + max-height: 100vh; + overflow: auto; +} +div.dt-button-collection.two-column > :last-child, div.dt-button-collection.three-column > :last-child, div.dt-button-collection.four-column > :last-child { + display: block !important; + -webkit-column-gap: 8px; + -moz-column-gap: 8px; + -ms-column-gap: 8px; + -o-column-gap: 8px; + column-gap: 8px; +} +div.dt-button-collection.two-column > :last-child > *, div.dt-button-collection.three-column > :last-child > *, div.dt-button-collection.four-column > :last-child > * { + -webkit-column-break-inside: avoid; + break-inside: avoid; +} +div.dt-button-collection.two-column { + width: 400px; +} +div.dt-button-collection.two-column > :last-child { + padding-bottom: 1px; + column-count: 2; +} +div.dt-button-collection.three-column { + width: 450px; +} +div.dt-button-collection.three-column > :last-child { + padding-bottom: 1px; + column-count: 3; +} +div.dt-button-collection.four-column { + width: 600px; +} +div.dt-button-collection.four-column > :last-child { + padding-bottom: 1px; + column-count: 4; +} +div.dt-button-collection .dt-button { + border-radius: 0; +} +div.dt-button-collection.columns { + width: auto; +} +div.dt-button-collection.columns > :last-child { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + align-items: center; + gap: 6px; + width: 818px; + padding-bottom: 1px; +} +div.dt-button-collection.columns > :last-child .dt-button { + min-width: 200px; + flex: 0 1; + margin: 0; +} +div.dt-button-collection.columns.dtb-b3 > :last-child, div.dt-button-collection.columns.dtb-b2 > :last-child, div.dt-button-collection.columns.dtb-b1 > :last-child { + justify-content: space-between; +} +div.dt-button-collection.columns.dtb-b3 .dt-button { + flex: 1 1 32%; +} +div.dt-button-collection.columns.dtb-b2 .dt-button { + flex: 1 1 48%; +} +div.dt-button-collection.columns.dtb-b1 .dt-button { + flex: 1 1 100%; +} +@media screen and (max-width: 1024px) { + div.dt-button-collection.columns > :last-child { + width: 612px; + } +} +@media screen and (max-width: 640px) { + div.dt-button-collection.columns > :last-child { + width: 406px; + } + div.dt-button-collection.columns.dtb-b3 .dt-button { + flex: 0 1 32%; + } +} +@media screen and (max-width: 460px) { + div.dt-button-collection.columns > :last-child { + width: 200px; + } +} + +div.dt-button-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + /* Fallback */ + background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + /* W3C Markup, IE10 Release Preview */ + z-index: 2001; +} + +@media screen and (max-width: 640px) { + div.dt-buttons { + float: none !important; + text-align: center; + } +} +button.dt-button.processing, +div.dt-button.processing, +a.dt-button.processing { + color: rgba(0, 0, 0, 0.2); +} +button.dt-button.processing:after, +div.dt-button.processing:after, +a.dt-button.processing:after { + position: absolute; + top: 50%; + left: 50%; + width: 16px; + height: 16px; + margin: -8px 0 0 -8px; + box-sizing: border-box; + display: block; + content: " "; + border: 2px solid #282828; + border-radius: 50%; + border-left-color: transparent; + border-right-color: transparent; + animation: dtb-spinner 1500ms infinite linear; + -o-animation: dtb-spinner 1500ms infinite linear; + -ms-animation: dtb-spinner 1500ms infinite linear; + -webkit-animation: dtb-spinner 1500ms infinite linear; + -moz-animation: dtb-spinner 1500ms infinite linear; +} + +button.dt-btn-split-drop { + margin-left: calc(-1px - 0.333em); + padding-bottom: calc(0.5em - 1px); + border-radius: 0px 1px 1px 0px; + color: rgba(70, 70, 70, 0.9); + border-left: none; +} +button.dt-btn-split-drop span.dt-btn-split-drop-arrow { + position: relative; + top: -1px; + left: -2px; + font-size: 8px; +} +button.dt-btn-split-drop:hover { + z-index: 2; +} + +button.buttons-split { + border-right: 1px solid rgba(70, 70, 70, 0); + border-radius: 1px 0px 0px 1px; +} + +button.dt-btn-split-drop-button { + background-color: white; +} +button.dt-btn-split-drop-button:hover { + background-color: white; +} + + +table.DTCR_clonedTable.dataTable { + position: absolute !important; + background-color: rgba(255, 255, 255, 0.7); + z-index: 202; +} + +div.DTCR_pointer { + width: 1px; + background-color: #0259C4; + z-index: 201; +} + + +table.fixedHeader-floating { + background-color: white; +} + +table.fixedHeader-floating.no-footer { + border-bottom-width: 0; +} + +table.fixedHeader-locked { + position: absolute !important; + background-color: white; +} + +@media print { + table.fixedHeader-floating { + display: none; + } +} + + +div.dtsp-topRow { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + border: 2px solid rgba(0, 0, 0, 0); + border-radius: 3px; + justify-content: space-around; + align-content: flex-start; + align-items: flex-start; + min-height: 37px; +} +div.dtsp-topRow input.dtsp-search { + text-overflow: ellipsis; + min-width: 50px; + flex-basis: 90px; + max-width: none; +} +div.dtsp-topRow input.dtsp-search::placeholder { + color: black; +} +div.dtsp-topRow div.dtsp-subRow1 { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + flex: 1 1 auto; +} +div.dtsp-topRow div.dtsp-subRow1 div.dtsp-searchCont { + position: relative; + width: 100%; +} +div.dtsp-topRow div.dtsp-subRow1 div.dtsp-searchCont input.dtsp-disabledButton { + padding-top: 10px; + padding-bottom: 10px; + background-color: transparent; +} +div.dtsp-topRow div.dtsp-subRow1 input { + padding-right: 2em; + width: 100% !important; + box-sizing: border-box; + font-size: 1em; +} +div.dtsp-topRow div.dtsp-subRow1 button.dtsp-searchIcon { + position: absolute; + top: 0; + right: 0; + bottom: 0; + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAABbmlDQ1BpY2MAACiRdZE7SwNBFIU/EyWikRRaiFhsoWKhEBREO42FTZAQFYza7G5eQhKX3QQJtoKNhWAh2vgq/AfaCrYKgqAIIhb+Al+NhPVOEkiQZJbZ+3FmzmXmDHjCGTPrtAYhm8vb0bmQthxb0Xzv+PHRxRSabjrWTCQSpun4eaRF1YdR1av5voajM55wTGhpF54wLTsvPC0c3sxbineFe8y0Hhc+ER6x5YDCt0o3KvymOFXhL8X2YnQWPKqnlqpjo47NtJ0VHhYeyGYKZvU86ib+RG5pQWqfzH4coswRQsOgwDoZ8oxKzUlmjX3Bsm+eDfGY8rcoYosjRVq8I6IWpGtCalL0hHwZiir3/3k6yfGxSnd/CNpeXfdzEHz7UNpz3d9T1y2dgfcFrnM1/4bkNPkt+l5NGziGwDZc3tQ04wCudqD32dJtvSx5ZXqSSfi4gK4YdN9Dx2olq+o650+wuCVPdAeHRzAk+wNrfw8JaBFXEnV+AAAACXBIWXMAAA9hAAAPYQGoP6dpAAABMUlEQVQoU6XRr0vDQRjH8akoM4iIjqGoOIZ5oIjB5XWxajaYDGLSIhhNYjcPRDSJwbQNw+L+BNGgYYo/5pT5/shz8vDlBgMPXux7z3N3z+25VOofYyCyd4ZYCavI4gXPsRp9LqiDdrEMH+8wv8Vh8gBfWclFPOEUN3hAHjlMoRa7wTzBS5xgKLFglPkZLjDic6HyDsEMNvGR2Nxifoci3tEI+X770JU0XmPXIlax+LTPh83fFox1X6kxyzdjm9UcdXi9S+Vti6svfyNULhNR9TVsYNhW6Ff9KKCNR7/Zv6eeaQ+6+qcdpu9BqGlp1HFgud+FYdzzUcUExu0Q/cdzHGEFetIlXKPjK/sbqYoOftMiS+j9jzEJPd1Wt+5+kdR/9EM9ucIC5jCbyPc01Q32kfsBppYz3hYFcCwAAAAASUVORK5CYII=") !important; + background-repeat: no-repeat; + background-position: center; + background-size: 12px; +} +div.dtsp-topRow div.dtsp-subRow2 { + white-space: nowrap; + flex: 0 0 auto; +} +div.dtsp-topRow button.dtsp-nameButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAABcGlDQ1BpY2MAACiRdZHNSwJBGMYftTDS8FCHkA57sOigIAXRMQzyYh3UIKvL7rirwe66zK6IdA26dBA6RF36OvQf1DXoWhAERRAR9B/0dQnZ3nEFJXSG2ffHs/O8zDwD+DM6M+yBJGCYDs+mU9JaYV0KviNMM4QoEjKzreXcUh59x88jfKI+JESv/vt6jlBRtRngGyKeYxZ3iBeIMzXHErxHPMbKcpH4hDjO6YDEt0JXPH4TXPL4SzDPZxcBv+gplbpY6WJW5gbxNHHM0KusfR5xk7BqruaoRmlNwEYWaaQgQUEVW9DhIEHVpMx6+5It3woq5GH0tVAHJ0cJZfLGSa1SV5WqRrpKU0dd5P4/T1ubnfG6h1PA4Kvrfk4CwX2g2XDd31PXbZ4BgRfg2uz4K5TT/DfpjY4WOwYiO8DlTUdTDoCrXWD82ZK53JICtPyaBnxcACMFYPQeGN7wsmr/x/kTkN+mJ7oDDo+AKdof2fwDCBRoDkL8UccAAAAJcEhZcwAAD2EAAA9hAag/p2kAAAK2SURBVFgJ7ZY9j41BFICvryCExrJBQ6HyEYVEIREaUZDQIRoR2ViJKCioxV+gkVXYTVZEQiEUhG2EQnxUCh0FKolY4ut5XnM2cyfva3Pt5m7EPcmzZ2bemTNnzjkzd1utnvQi0IvAfxiBy5z5FoxO89kPY+8mbMjtzs47RXs5/WVpbAG6bWExt5PuIibvhVkwmC+ck3eK9ln6/fAddFojYzBVuYSBpcnIEvRaqOw2RcaN18FPuJH0JvRUxbT3wWf4ltiKPgfVidWlbGZgPozDFfgAC+EA/K2EI4cwcAJ+gPaeQ+VQU2SOMMGcPgPl/m/V2p50rrbRsRgt9Iv5h6xtpP22Bz7Ce1C+gFFxfKzOmShcU+Qmyh2w3w8rIJfddHTck66EukL/xPhj+JM8rHNmFys0Pg4v0up3aFNlwR9NYyodd3OL/C64zpsymcTFcf6ElM4YzjAWKYrJkaq8kE/yUYNP4BoYvS1QRo+hNtF5xfkTUjoTheukSFFMjlTFm6PjceOca/SMpKfeCR1L6Uzk/y2WIkVhNFJlJAZhP+hYns7b9D3IPuhY5mYrIv8OrQJvR5NYyNaW4jsU8pSGNySiVx4o5tXq3JkoXE/mg5R/M8dGJCJpKhaDcjBRdbI/Rm8g69c122om33BHmj2CHoV5qa9jUXBraJ+G1fAVjIBO1klc87ro1K4JZ/K35SWW3TwcyDd6TecqnAEd8cGq2+w84xvBm1n3vS0izKkkwh5XNC/GmFPqqAtPF89AOScKuemaNzoTV1SD5dtSbmLf1/RV+tC0WTgcj6R7HEtrVGWaqu/lYDZ/2pvxQ/kIyw/gFByHC9AHw910hv1aUUumyd8yy0QfhmEkfiNod0Xusct68J1qc8Tdux0Z97Q+hsDb+AYGYEbF/4Guw2Q/qDPqZG/zXgT+3Qj8AtKnfWhFwmuAAAAAAElFTkSuQmCC") !important; + background-repeat: no-repeat; + background-position: center; + background-size: 23px; + vertical-align: bottom; +} +div.dtsp-topRow button.dtsp-countButton { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABcGlDQ1BpY2MAACiRdZHNSwJBGMYftTDS8FCHkA57sOigIAXRMQzyYh3UIKvL7rirwe66zK6IdA26dBA6RF36OvQf1DXoWhAERRAR9B/0dQnZ3nEFJXSG2ffHs/O8zDwD+DM6M+yBJGCYDs+mU9JaYV0KviNMM4QoEjKzreXcUh59x88jfKI+JESv/vt6jlBRtRngGyKeYxZ3iBeIMzXHErxHPMbKcpH4hDjO6YDEt0JXPH4TXPL4SzDPZxcBv+gplbpY6WJW5gbxNHHM0KusfR5xk7BqruaoRmlNwEYWaaQgQUEVW9DhIEHVpMx6+5It3woq5GH0tVAHJ0cJZfLGSa1SV5WqRrpKU0dd5P4/T1ubnfG6h1PA4Kvrfk4CwX2g2XDd31PXbZ4BgRfg2uz4K5TT/DfpjY4WOwYiO8DlTUdTDoCrXWD82ZK53JICtPyaBnxcACMFYPQeGN7wsmr/x/kTkN+mJ7oDDo+AKdof2fwDCBRoDkL8UccAAAAJcEhZcwAAD2EAAA9hAag/p2kAAAG5SURBVEgN3VU9LwVBFF0fiYhofUSlEQkKhU7z/oBCQkIiGr9BgUbhVzy9BAnhFyjV/AYFiU5ICM7ZN+c5Zud5dm3lJmfmzrkz9+7cu3c3y/6jjOBSF8CxXS7FmTkbwqIJjDpJvTcmsJ4K3KPZUpyZsx0sxoB9J6mnAkyC7wGuuCFIipNtEcpcWExgXpOBc78vgj6N+QO4NVsjwdFM59tUIDxDrHMBOeIQ34C5ZDregXuAQm4YcI68nN9B3wr2PcwPAIPkN2EqtJH6b+QZm1ajjTx7BqwAr26Lb+C2Kvpbt0Mb2HAJ7NrGFGfmXO3DeA4UshDfQAVmH0gaUFg852TTTDvlxwBlCtxy9zXyBhQFaq0wMmIdRebrfgosA3zb2hKnqG0oqchp4QbuR8X0TjzABhbdOT8jnQ/atcgqpnfwOA7yqZyTU587ZkIGdesLTt2EkynOnbreMUUKMI/dA4B/QVOcO13CQh+5wWCgDwo/75u59odB/wjmfhbgvACcAOyZPHihMWAoIwxyCLgf1oxfgjzVbgBXSTzIN+f0pg6s5DkcesLMRpsBrgE2XO3CN64JFP7JtUeKHX4CKtRRXFZ+7dEAAAAASUVORK5CYII=") !important; + background-repeat: no-repeat; + background-position: center; + background-size: 18px; + vertical-align: bottom; +} +div.dtsp-topRow button.dtsp-collapseButton span.dtsp-caret { + position: relative; + top: 2px; + display: inline-block; +} +div.dtsp-topRow button.dtsp-collapseButton.dtsp-rotated { + transform: rotate(180deg); +} + +div.dtsp-topRow.dtsp-bordered { + border: 2px solid #f0f0f0; + border-radius: 3px; +} + +div.dtsp-topRow.dtsp-bordered:hover { + background-color: #f0f0f0; + opacity: 0.6; + border: 2px solid #cfcfcf; + border-radius: 3px; + cursor: pointer !important; +} + +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane table thead th, +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane table thead td { + width: 100% !important; +} + +div.dt-button-collection { + z-index: 2002; +} + +div.dt-button-collection.dtb-collection-closeable div.dtsp-titleRow { + padding-right: 25px; +} + +div.dtsp-columns-1 { + max-width: 100%; + min-width: 100%; + margin: 0px !important; +} + +div.dtsp-columns-2 { + max-width: 49%; + min-width: 49%; + margin: 0px !important; +} + +div.dtsp-columns-3 { + max-width: 32%; + min-width: 32%; + margin: 0px !important; +} + +div.dtsp-columns-4 { + max-width: 24%; + min-width: 24%; + margin: 0px !important; +} + +div.dtsp-columns-5 { + max-width: 19%; + min-width: 19%; + margin: 0px !important; +} + +div.dtsp-columns-6 { + max-width: 16%; + min-width: 16%; + margin: 0px !important; +} + +div.dtsp-columns-7 { + max-width: 14%; + min-width: 14%; + margin: 0px !important; +} + +div.dtsp-columns-8 { + max-width: 12%; + min-width: 12%; + margin: 0px !important; +} + +div.dtsp-columns-9 { + max-width: 10.5%; + min-width: 10.5%; + margin: 0px !important; +} + +div.dtsp-narrow { + flex-direction: column !important; +} +div.dtsp-narrow div.dtsp-subRow1, +div.dtsp-narrow div.dtsp-subRow2 { + width: 100%; +} +div.dtsp-narrow div.dtsp-subRow2 button { + margin: 0 !important; + width: 25% !important; +} + +div.dt-button-collection { + float: none; +} + +div.dtsp-panesContainer { + margin-bottom: 1em; +} +div.dtsp-panesContainer div.dataTables_wrapper { + width: 100%; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_layout_cell { + padding: 0; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollHead { + display: none !important; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody { + background: white !important; + border-bottom: none; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody thead { + display: none; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody table { + table-layout: fixed; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody table tr > th, +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody table tr > td { + padding: 5px 10px; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody td.dtsp-nameColumn { + width: 100% !important; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody div.dtsp-nameCont { + width: 100%; + display: flex; + flex-direction: row; + justify-content: flex-start; + align-content: flex-start; + align-items: flex-start; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody div.dtsp-nameCont span.dtsp-name, +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody div.dtsp-nameCont span.dtsp-pill { + cursor: default; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody div.dtsp-nameCont span.dtsp-name { + text-overflow: ellipsis; + overflow: hidden; + display: inline-block; + vertical-align: middle; + white-space: nowrap; + flex-grow: 1; + text-align: left; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody div.dtsp-nameCont span.dtsp-pill { + display: inline-block; + background-color: #cfcfcf; + text-align: center; + border: 1px solid #cfcfcf; + border-radius: 10px; + width: auto; + min-width: 30px; + color: black; + font-size: 0.9em; + padding: 0 4px; +} +div.dtsp-panesContainer div.dataTables_wrapper div.dataTables_scrollBody div.dtsp-nameCont span.dtsp-pill:empty { + display: none; +} + +div.dtsp-panesContainer { + clear: both; + padding-left: 0; + padding-right: 0; + text-align: center; +} +div.dtsp-panesContainer div.dtsp-searchPanes { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + align-content: flex-start; + align-items: stretch; + clear: both; + text-align: left; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane { + flex-grow: 1; + flex-shrink: 0; + font-size: 0.9em; + margin-top: 15px !important; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane div.dataTables_wrapper { + flex: 1; + box-sizing: border-box; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane div.dataTables_wrapper div.dataTables_filter { + display: none; +} +div.dtsp-panesContainer div.dtsp-title { + float: left; + padding: 10px 0; +} +div.dtsp-panesContainer button.dtsp-clearAll, +div.dtsp-panesContainer button.dtsp-collapseAll, +div.dtsp-panesContainer button.dtsp-showAll { + float: right; + padding: 10px; +} + +div.dtsp-hidden { + display: none !important; +} + +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane div.dataTables_wrapper { + border: 2px solid #f0f0f0; + border-radius: 4px; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane div.dataTables_wrapper:hover { + border: 2px solid #cfcfcf; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane div.dtsp-selected { + border: 2px solid #3276b1; + border-radius: 4px; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane div.dtsp-selected:hover { + border: 2px solid #286092; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane div.dtsp-topRow div.dtsp-searchCont input.dtsp-search { + border: none; + padding-left: 3px; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane input.dtsp-paneInputButton, +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane button.dtsp-paneButton { + height: 35px; + width: 35px; + min-width: 0; + display: inline-block; + margin: 2px; + border: 0px solid transparent; + background-color: transparent; + margin-bottom: 0px; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane input.dtsp-paneInputButton:hover, +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane button.dtsp-paneButton:hover { + background-color: #f0f0f0; + border-radius: 2px; + cursor: pointer; +} +div.dtsp-panesContainer div.dtsp-searchPanes div.dtsp-searchPane button.dtsp-paneButton { + opacity: 0.6; +} +div.dtsp-panesContainer button.dtsp-clearAll, +div.dtsp-panesContainer button.dtsp-collapseAll, +div.dtsp-panesContainer button.dtsp-showAll { + border: 1px solid transparent; + background-color: transparent; +} +div.dtsp-panesContainer button.dtsp-clearAll:hover, +div.dtsp-panesContainer button.dtsp-collapseAll:hover, +div.dtsp-panesContainer button.dtsp-showAll:hover { + background-color: #f0f0f0; + border-radius: 2px; + cursor: pointer; +} +div.dtsp-panesContainer button.dtsp-disabledButton { + cursor: default !important; + color: #7c7c7c; +} +div.dtsp-panesContainer button.dtsp-disabledButton:hover { + background-color: transparent; +} +div.dtsp-panesContainer button.dtsp-disabledButton:focus { + outline: none; +} + +div.dtsp-topRow.dtsp-bordered:hover button.dtsp-disabledButton { + cursor: pointer !important; + pointer-events: none; +} +div.dtsp-topRow.dtsp-bordered:hover input.dtsp-paneInputButton { + pointer-events: none; +} + +@media screen and (max-width: 767px) { + div.dtsp-columns-4, +div.dtsp-columns-5, +div.dtsp-columns-6 { + max-width: 31% !important; + min-width: 31% !important; + } +} +@media screen and (max-width: 640px) { + div.dtsp-searchPanes { + flex-direction: column !important; + } + + div.dtsp-searchPane { + max-width: 98% !important; + min-width: 98% !important; + } +} + +tfoot input { + width: 100%; + padding: 3px; + box-sizing: border-box; + } +tfoot { + display: table-header-group; +} + +.dataTables_length { +float: right; +text-align: right; +} +.dataTables_filter, .dataTables_info { display: none; } diff --git a/org.idempiere.zk.datatable/src/web/js/datatables/datatables.js b/org.idempiere.zk.datatable/src/web/js/datatables/datatables.js new file mode 100644 index 0000000000..73aa34b030 --- /dev/null +++ b/org.idempiere.zk.datatable/src/web/js/datatables/datatables.js @@ -0,0 +1,24410 @@ +/* + * This combined file was created by the DataTables downloader builder: + * https://datatables.net/download + * + * To rebuild or modify this file with the latest versions of the included + * software please visit: + * https://datatables.net/download/#dt/dt-1.12.1/b-2.2.3/b-colvis-2.2.3/cr-1.5.6/fh-3.2.4/sp-2.0.2 + * + * Included libraries: + * DataTables 1.12.1, Buttons 2.2.3, Column visibility 2.2.3, ColReorder 1.5.6, FixedHeader 3.2.4, SearchPanes 2.0.2 + */ + +/*! DataTables 1.12.1 + * ©2008-2022 SpryMedia Ltd - datatables.net/license + */ + +/** + * @summary DataTables + * @description Paginate, search and order HTML tables + * @version 1.12.1 + * @author SpryMedia Ltd + * @contact www.datatables.net + * @copyright SpryMedia Ltd. + * + * This source file is free software, available under the following license: + * MIT license - http://datatables.net/license + * + * This source file 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 license files for details. + * + * For details please refer to: http://www.datatables.net + */ + +/*jslint evil: true, undef: true, browser: true */ +/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/ + +(function( factory ) { + "use strict"; + + if ( typeof define === 'function' && define.amd ) { + // AMD + define( ['jquery'], function ( $ ) { + return factory( $, window, document ); + } ); + } + else if ( typeof exports === 'object' ) { + // CommonJS + module.exports = function (root, $) { + if ( ! root ) { + // CommonJS environments without a window global must pass a + // root. This will give an error otherwise + root = window; + } + + if ( ! $ ) { + $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window + require('jquery') : + require('jquery')( root ); + } + + return factory( $, root, root.document ); + }; + } + else { + // Browser + window.DataTable = factory( jQuery, window, document ); + } +} +(function( $, window, document, undefined ) { + "use strict"; + + + var DataTable = function ( selector, options ) + { + // When creating with `new`, create a new DataTable, returning the API instance + if (this instanceof DataTable) { + return $(selector).DataTable(options); + } + else { + // Argument switching + options = selector; + } + + /** + * Perform a jQuery selector action on the table's TR elements (from the tbody) and + * return the resulting jQuery object. + * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on + * @param {object} [oOpts] Optional parameters for modifying the rows to be included + * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter + * criterion ("applied") or all TR elements (i.e. no filter). + * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. + * Can be either 'current', whereby the current sorting of the table is used, or + * 'original' whereby the original order the data was read into the table is used. + * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page + * ("current") or not ("all"). If 'current' is given, then order is assumed to be + * 'current' and filter is 'applied', regardless of what they might be given as. + * @returns {object} jQuery object, filtered by the given selector. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Highlight every second row + * oTable.$('tr:odd').css('backgroundColor', 'blue'); + * } ); + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Filter to rows with 'Webkit' in them, add a background colour and then + * // remove the filter, thus highlighting the 'Webkit' rows only. + * oTable.fnFilter('Webkit'); + * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue'); + * oTable.fnFilter(''); + * } ); + */ + this.$ = function ( sSelector, oOpts ) + { + return this.api(true).$( sSelector, oOpts ); + }; + + + /** + * Almost identical to $ in operation, but in this case returns the data for the matched + * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes + * rather than any descendants, so the data can be obtained for the row/cell. If matching + * rows are found, the data returned is the original data array/object that was used to + * create the row (or a generated array if from a DOM source). + * + * This method is often useful in-combination with $ where both functions are given the + * same parameters and the array indexes will match identically. + * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on + * @param {object} [oOpts] Optional parameters for modifying the rows to be included + * @param {string} [oOpts.filter=none] Select elements that meet the current filter + * criterion ("applied") or all elements (i.e. no filter). + * @param {string} [oOpts.order=current] Order of the data in the processed array. + * Can be either 'current', whereby the current sorting of the table is used, or + * 'original' whereby the original order the data was read into the table is used. + * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page + * ("current") or not ("all"). If 'current' is given, then order is assumed to be + * 'current' and filter is 'applied', regardless of what they might be given as. + * @returns {array} Data for the matched elements. If any elements, as a result of the + * selector, were not TR, TD or TH elements in the DataTable, they will have a null + * entry in the array. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Get the data from the first row in the table + * var data = oTable._('tr:first'); + * + * // Do something useful with the data + * alert( "First cell is: "+data[0] ); + * } ); + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Filter to 'Webkit' and get all data for + * oTable.fnFilter('Webkit'); + * var data = oTable._('tr', {"search": "applied"}); + * + * // Do something with the data + * alert( data.length+" rows matched the search" ); + * } ); + */ + this._ = function ( sSelector, oOpts ) + { + return this.api(true).rows( sSelector, oOpts ).data(); + }; + + + /** + * Create a DataTables Api instance, with the currently selected tables for + * the Api's context. + * @param {boolean} [traditional=false] Set the API instance's context to be + * only the table referred to by the `DataTable.ext.iApiIndex` option, as was + * used in the API presented by DataTables 1.9- (i.e. the traditional mode), + * or if all tables captured in the jQuery object should be used. + * @return {DataTables.Api} + */ + this.api = function ( traditional ) + { + return traditional ? + new _Api( + _fnSettingsFromNode( this[ _ext.iApiIndex ] ) + ) : + new _Api( this ); + }; + + + /** + * Add a single new row or multiple rows of data to the table. Please note + * that this is suitable for client-side processing only - if you are using + * server-side processing (i.e. "bServerSide": true), then to add data, you + * must add it to the data source, i.e. the server-side, through an Ajax call. + * @param {array|object} data The data to be added to the table. This can be: + *
    + *
  • 1D array of data - add a single row with the data provided
  • + *
  • 2D array of arrays - add multiple rows in a single call
  • + *
  • object - data object when using mData
  • + *
  • array of objects - multiple data objects when using mData
  • + *
+ * @param {bool} [redraw=true] redraw the table or not + * @returns {array} An array of integers, representing the list of indexes in + * aoData ({@link DataTable.models.oSettings}) that have been added to + * the table. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * // Global var for counter + * var giCount = 2; + * + * $(document).ready(function() { + * $('#example').dataTable(); + * } ); + * + * function fnClickAddRow() { + * $('#example').dataTable().fnAddData( [ + * giCount+".1", + * giCount+".2", + * giCount+".3", + * giCount+".4" ] + * ); + * + * giCount++; + * } + */ + this.fnAddData = function( data, redraw ) + { + var api = this.api( true ); + + /* Check if we want to add multiple rows or not */ + var rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ? + api.rows.add( data ) : + api.row.add( data ); + + if ( redraw === undefined || redraw ) { + api.draw(); + } + + return rows.flatten().toArray(); + }; + + + /** + * This function will make DataTables recalculate the column sizes, based on the data + * contained in the table and the sizes applied to the columns (in the DOM, CSS or + * through the sWidth parameter). This can be useful when the width of the table's + * parent element changes (for example a window resize). + * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable( { + * "sScrollY": "200px", + * "bPaginate": false + * } ); + * + * $(window).on('resize', function () { + * oTable.fnAdjustColumnSizing(); + * } ); + * } ); + */ + this.fnAdjustColumnSizing = function ( bRedraw ) + { + var api = this.api( true ).columns.adjust(); + var settings = api.settings()[0]; + var scroll = settings.oScroll; + + if ( bRedraw === undefined || bRedraw ) { + api.draw( false ); + } + else if ( scroll.sX !== "" || scroll.sY !== "" ) { + /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ + _fnScrollDraw( settings ); + } + }; + + + /** + * Quickly and simply clear a table + * @param {bool} [bRedraw=true] redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...) + * oTable.fnClearTable(); + * } ); + */ + this.fnClearTable = function( bRedraw ) + { + var api = this.api( true ).clear(); + + if ( bRedraw === undefined || bRedraw ) { + api.draw(); + } + }; + + + /** + * The exact opposite of 'opening' a row, this function will close any rows which + * are currently 'open'. + * @param {node} nTr the table row to 'close' + * @returns {int} 0 on success, or 1 if failed (can't find the row) + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnClose = function( nTr ) + { + this.api( true ).row( nTr ).child.hide(); + }; + + + /** + * Remove a row for the table + * @param {mixed} target The index of the row from aoData to be deleted, or + * the TR element you want to delete + * @param {function|null} [callBack] Callback function + * @param {bool} [redraw=true] Redraw the table or not + * @returns {array} The row that was deleted + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Immediately remove the first row + * oTable.fnDeleteRow( 0 ); + * } ); + */ + this.fnDeleteRow = function( target, callback, redraw ) + { + var api = this.api( true ); + var rows = api.rows( target ); + var settings = rows.settings()[0]; + var data = settings.aoData[ rows[0][0] ]; + + rows.remove(); + + if ( callback ) { + callback.call( this, settings, data ); + } + + if ( redraw === undefined || redraw ) { + api.draw(); + } + + return data; + }; + + + /** + * Restore the table to it's original state in the DOM by removing all of DataTables + * enhancements, alterations to the DOM structure of the table and event listeners. + * @param {boolean} [remove=false] Completely remove the table from the DOM + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * // This example is fairly pointless in reality, but shows how fnDestroy can be used + * var oTable = $('#example').dataTable(); + * oTable.fnDestroy(); + * } ); + */ + this.fnDestroy = function ( remove ) + { + this.api( true ).destroy( remove ); + }; + + + /** + * Redraw the table + * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Re-draw the table - you wouldn't want to do it here, but it's an example :-) + * oTable.fnDraw(); + * } ); + */ + this.fnDraw = function( complete ) + { + // Note that this isn't an exact match to the old call to _fnDraw - it takes + // into account the new data, but can hold position. + this.api( true ).draw( complete ); + }; + + + /** + * Filter the input based on data + * @param {string} sInput String to filter the table on + * @param {int|null} [iColumn] Column to limit filtering to + * @param {bool} [bRegex=false] Treat as regular expression or not + * @param {bool} [bSmart=true] Perform smart filtering or not + * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es) + * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false) + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Sometime later - filter... + * oTable.fnFilter( 'test string' ); + * } ); + */ + this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive ) + { + var api = this.api( true ); + + if ( iColumn === null || iColumn === undefined ) { + api.search( sInput, bRegex, bSmart, bCaseInsensitive ); + } + else { + api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); + } + + api.draw(); + }; + + + /** + * Get the data for the whole table, an individual row or an individual cell based on the + * provided parameters. + * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as + * a TR node then the data source for the whole row will be returned. If given as a + * TD/TH cell node then iCol will be automatically calculated and the data for the + * cell returned. If given as an integer, then this is treated as the aoData internal + * data index for the row (see fnGetPosition) and the data for that row used. + * @param {int} [col] Optional column index that you want the data of. + * @returns {array|object|string} If mRow is undefined, then the data for all rows is + * returned. If mRow is defined, just data for that row, and is iCol is + * defined, only data for the designated cell is returned. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * // Row data + * $(document).ready(function() { + * oTable = $('#example').dataTable(); + * + * oTable.$('tr').click( function () { + * var data = oTable.fnGetData( this ); + * // ... do something with the array / object of data for the row + * } ); + * } ); + * + * @example + * // Individual cell data + * $(document).ready(function() { + * oTable = $('#example').dataTable(); + * + * oTable.$('td').click( function () { + * var sData = oTable.fnGetData( this ); + * alert( 'The cell clicked on had the value of '+sData ); + * } ); + * } ); + */ + this.fnGetData = function( src, col ) + { + var api = this.api( true ); + + if ( src !== undefined ) { + var type = src.nodeName ? src.nodeName.toLowerCase() : ''; + + return col !== undefined || type == 'td' || type == 'th' ? + api.cell( src, col ).data() : + api.row( src ).data() || null; + } + + return api.data().toArray(); + }; + + + /** + * Get an array of the TR nodes that are used in the table's body. Note that you will + * typically want to use the '$' API method in preference to this as it is more + * flexible. + * @param {int} [iRow] Optional row index for the TR element you want + * @returns {array|node} If iRow is undefined, returns an array of all TR elements + * in the table's body, or iRow is defined, just the TR element requested. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Get the nodes from the table + * var nNodes = oTable.fnGetNodes( ); + * } ); + */ + this.fnGetNodes = function( iRow ) + { + var api = this.api( true ); + + return iRow !== undefined ? + api.row( iRow ).node() : + api.rows().nodes().flatten().toArray(); + }; + + + /** + * Get the array indexes of a particular cell from it's DOM element + * and column index including hidden columns + * @param {node} node this can either be a TR, TD or TH in the table's body + * @returns {int} If nNode is given as a TR, then a single index is returned, or + * if given as a cell, an array of [row index, column index (visible), + * column index (all)] is given. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * $('#example tbody td').click( function () { + * // Get the position of the current data from the node + * var aPos = oTable.fnGetPosition( this ); + * + * // Get the data array for this row + * var aData = oTable.fnGetData( aPos[0] ); + * + * // Update the data array and return the value + * aData[ aPos[1] ] = 'clicked'; + * this.innerHTML = 'clicked'; + * } ); + * + * // Init DataTables + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnGetPosition = function( node ) + { + var api = this.api( true ); + var nodeName = node.nodeName.toUpperCase(); + + if ( nodeName == 'TR' ) { + return api.row( node ).index(); + } + else if ( nodeName == 'TD' || nodeName == 'TH' ) { + var cell = api.cell( node ).index(); + + return [ + cell.row, + cell.columnVisible, + cell.column + ]; + } + return null; + }; + + + /** + * Check to see if a row is 'open' or not. + * @param {node} nTr the table row to check + * @returns {boolean} true if the row is currently open, false otherwise + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnIsOpen = function( nTr ) + { + return this.api( true ).row( nTr ).child.isShown(); + }; + + + /** + * This function will place a new row directly after a row which is currently + * on display on the page, with the HTML contents that is passed into the + * function. This can be used, for example, to ask for confirmation that a + * particular record should be deleted. + * @param {node} nTr The table row to 'open' + * @param {string|node|jQuery} mHtml The HTML to put into the row + * @param {string} sClass Class to give the new TD cell + * @returns {node} The row opened. Note that if the table row passed in as the + * first parameter, is not found in the table, this method will silently + * return. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnOpen = function( nTr, mHtml, sClass ) + { + return this.api( true ) + .row( nTr ) + .child( mHtml, sClass ) + .show() + .child()[0]; + }; + + + /** + * Change the pagination - provides the internal logic for pagination in a simple API + * function. With this function you can have a DataTables table go to the next, + * previous, first or last pages. + * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" + * or page number to jump to (integer), note that page 0 is the first page. + * @param {bool} [bRedraw=true] Redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * oTable.fnPageChange( 'next' ); + * } ); + */ + this.fnPageChange = function ( mAction, bRedraw ) + { + var api = this.api( true ).page( mAction ); + + if ( bRedraw === undefined || bRedraw ) { + api.draw(false); + } + }; + + + /** + * Show a particular column + * @param {int} iCol The column whose display should be changed + * @param {bool} bShow Show (true) or hide (false) the column + * @param {bool} [bRedraw=true] Redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Hide the second column after initialisation + * oTable.fnSetColumnVis( 1, false ); + * } ); + */ + this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) + { + var api = this.api( true ).column( iCol ).visible( bShow ); + + if ( bRedraw === undefined || bRedraw ) { + api.columns.adjust().draw(); + } + }; + + + /** + * Get the settings for a particular table for external manipulation + * @returns {object} DataTables settings object. See + * {@link DataTable.models.oSettings} + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * var oSettings = oTable.fnSettings(); + * + * // Show an example parameter from the settings + * alert( oSettings._iDisplayStart ); + * } ); + */ + this.fnSettings = function() + { + return _fnSettingsFromNode( this[_ext.iApiIndex] ); + }; + + + /** + * Sort the table by a particular column + * @param {int} iCol the data index to sort on. Note that this will not match the + * 'display index' if you have hidden data entries + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Sort immediately with columns 0 and 1 + * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); + * } ); + */ + this.fnSort = function( aaSort ) + { + this.api( true ).order( aaSort ).draw(); + }; + + + /** + * Attach a sort listener to an element for a given column + * @param {node} nNode the element to attach the sort listener to + * @param {int} iColumn the column that a click on this node will sort on + * @param {function} [fnCallback] callback function when sort is run + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Sort on column 1, when 'sorter' is clicked on + * oTable.fnSortListener( document.getElementById('sorter'), 1 ); + * } ); + */ + this.fnSortListener = function( nNode, iColumn, fnCallback ) + { + this.api( true ).order.listener( nNode, iColumn, fnCallback ); + }; + + + /** + * Update a table cell or row - this method will accept either a single value to + * update the cell with, an array of values with one element for each column or + * an object in the same format as the original data source. The function is + * self-referencing in order to make the multi column updates easier. + * @param {object|array|string} mData Data to update the cell/row with + * @param {node|int} mRow TR element you want to update or the aoData index + * @param {int} [iColumn] The column to update, give as null or undefined to + * update a whole row. + * @param {bool} [bRedraw=true] Redraw the table or not + * @param {bool} [bAction=true] Perform pre-draw actions or not + * @returns {int} 0 on success, 1 on error + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell + * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row + * } ); + */ + this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) + { + var api = this.api( true ); + + if ( iColumn === undefined || iColumn === null ) { + api.row( mRow ).data( mData ); + } + else { + api.cell( mRow, iColumn ).data( mData ); + } + + if ( bAction === undefined || bAction ) { + api.columns.adjust(); + } + + if ( bRedraw === undefined || bRedraw ) { + api.draw(); + } + return 0; + }; + + + /** + * Provide a common method for plug-ins to check the version of DataTables being used, in order + * to ensure compatibility. + * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the + * formats "X" and "X.Y" are also acceptable. + * @returns {boolean} true if this version of DataTables is greater or equal to the required + * version, or false if this version of DataTales is not suitable + * @method + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * alert( oTable.fnVersionCheck( '1.9.0' ) ); + * } ); + */ + this.fnVersionCheck = _ext.fnVersionCheck; + + + var _that = this; + var emptyInit = options === undefined; + var len = this.length; + + if ( emptyInit ) { + options = {}; + } + + this.oApi = this.internal = _ext.internal; + + // Extend with old style plug-in API methods + for ( var fn in DataTable.ext.internal ) { + if ( fn ) { + this[fn] = _fnExternApiFunc(fn); + } + } + + this.each(function() { + // For each initialisation we want to give it a clean initialisation + // object that can be bashed around + var o = {}; + var oInit = len > 1 ? // optimisation for single table case + _fnExtend( o, options, true ) : + options; + + /*global oInit,_that,emptyInit*/ + var i=0, iLen, j, jLen, k, kLen; + var sId = this.getAttribute( 'id' ); + var bInitHandedOff = false; + var defaults = DataTable.defaults; + var $this = $(this); + + + /* Sanity check */ + if ( this.nodeName.toLowerCase() != 'table' ) + { + _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); + return; + } + + /* Backwards compatibility for the defaults */ + _fnCompatOpts( defaults ); + _fnCompatCols( defaults.column ); + + /* Convert the camel-case defaults to Hungarian */ + _fnCamelToHungarian( defaults, defaults, true ); + _fnCamelToHungarian( defaults.column, defaults.column, true ); + + /* Setting up the initialisation object */ + _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); + + + + /* Check to see if we are re-initialising a table */ + var allSettings = DataTable.settings; + for ( i=0, iLen=allSettings.length ; i').appendTo($this); + } + oSettings.nTHead = thead[0]; + + var tbody = $this.children('tbody'); + if ( tbody.length === 0 ) { + tbody = $('').insertAfter(thead); + } + oSettings.nTBody = tbody[0]; + + var tfoot = $this.children('tfoot'); + if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) { + // If we are a scrolling table, and no footer has been given, then we need to create + // a tfoot element for the caption element to be appended to + tfoot = $('').appendTo($this); + } + + if ( tfoot.length === 0 || tfoot.children().length === 0 ) { + $this.addClass( oClasses.sNoFooter ); + } + else if ( tfoot.length > 0 ) { + oSettings.nTFoot = tfoot[0]; + _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); + } + + /* Check if there is data passing into the constructor */ + if ( oInit.aaData ) { + for ( i=0 ; i/g; + + // This is not strict ISO8601 - Date.parse() is quite lax, although + // implementations differ between browsers. + var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/; + + // Escape regular expression special characters + var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); + + // http://en.wikipedia.org/wiki/Foreign_exchange_market + // - \u20BD - Russian ruble. + // - \u20a9 - South Korean Won + // - \u20BA - Turkish Lira + // - \u20B9 - Indian Rupee + // - R - Brazil (R$) and South Africa + // - fr - Swiss Franc + // - kr - Swedish krona, Norwegian krone and Danish krone + // - \u2009 is thin space and \u202F is narrow no-break space, both used in many + // - Ƀ - Bitcoin + // - Ξ - Ethereum + // standards as thousands separators. + var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi; + + + var _empty = function ( d ) { + return !d || d === true || d === '-' ? true : false; + }; + + + var _intVal = function ( s ) { + var integer = parseInt( s, 10 ); + return !isNaN(integer) && isFinite(s) ? integer : null; + }; + + // Convert from a formatted number with characters other than `.` as the + // decimal place, to a Javascript number + var _numToDecimal = function ( num, decimalPoint ) { + // Cache created regular expressions for speed as this function is called often + if ( ! _re_dic[ decimalPoint ] ) { + _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); + } + return typeof num === 'string' && decimalPoint !== '.' ? + num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : + num; + }; + + + var _isNumber = function ( d, decimalPoint, formatted ) { + var strType = typeof d === 'string'; + + // If empty return immediately so there must be a number if it is a + // formatted string (this stops the string "k", or "kr", etc being detected + // as a formatted number for currency + if ( _empty( d ) ) { + return true; + } + + if ( decimalPoint && strType ) { + d = _numToDecimal( d, decimalPoint ); + } + + if ( formatted && strType ) { + d = d.replace( _re_formatted_numeric, '' ); + } + + return !isNaN( parseFloat(d) ) && isFinite( d ); + }; + + + // A string without HTML in it can be considered to be HTML still + var _isHtml = function ( d ) { + return _empty( d ) || typeof d === 'string'; + }; + + + var _htmlNumeric = function ( d, decimalPoint, formatted ) { + if ( _empty( d ) ) { + return true; + } + + var html = _isHtml( d ); + return ! html ? + null : + _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? + true : + null; + }; + + + var _pluck = function ( a, prop, prop2 ) { + var out = []; + var i=0, ien=a.length; + + // Could have the test in the loop for slightly smaller code, but speed + // is essential here + if ( prop2 !== undefined ) { + for ( ; i') + .css( { + position: 'fixed', + top: 0, + left: $(window).scrollLeft()*-1, // allow for scrolling + height: 1, + width: 1, + overflow: 'hidden' + } ) + .append( + $('
') + .css( { + position: 'absolute', + top: 1, + left: 1, + width: 100, + overflow: 'scroll' + } ) + .append( + $('
') + .css( { + width: '100%', + height: 10 + } ) + ) + ) + .appendTo( 'body' ); + + var outer = n.children(); + var inner = outer.children(); + + // Numbers below, in order, are: + // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth + // + // IE6 XP: 100 100 100 83 + // IE7 Vista: 100 100 100 83 + // IE 8+ Windows: 83 83 100 83 + // Evergreen Windows: 83 83 100 83 + // Evergreen Mac with scrollbars: 85 85 100 85 + // Evergreen Mac without scrollbars: 100 100 100 100 + + // Get scrollbar width + browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; + + // IE6/7 will oversize a width 100% element inside a scrolling element, to + // include the width of the scrollbar, while other browsers ensure the inner + // element is contained without forcing scrolling + browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100; + + // In rtl text layout, some browsers (most, but not all) will place the + // scrollbar on the left, rather than the right. + browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; + + // IE8- don't provide height and width for getBoundingClientRect + browser.bBounding = n[0].getBoundingClientRect().width ? true : false; + + n.remove(); + } + + $.extend( settings.oBrowser, DataTable.__browser ); + settings.oScroll.iBarWidth = DataTable.__browser.barWidth; + } + + + /** + * Array.prototype reduce[Right] method, used for browsers which don't support + * JS 1.6. Done this way to reduce code size, since we iterate either way + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnReduce ( that, fn, init, start, end, inc ) + { + var + i = start, + value, + isSet = false; + + if ( init !== undefined ) { + value = init; + isSet = true; + } + + while ( i !== end ) { + if ( ! that.hasOwnProperty(i) ) { + continue; + } + + value = isSet ? + fn( value, that[i], i, that ) : + that[i]; + + isSet = true; + i += inc; + } + + return value; + } + + /** + * Add a column to the list used for the table with default values + * @param {object} oSettings dataTables settings object + * @param {node} nTh The th element for this column + * @memberof DataTable#oApi + */ + function _fnAddColumn( oSettings, nTh ) + { + // Add column to aoColumns array + var oDefaults = DataTable.defaults.column; + var iCol = oSettings.aoColumns.length; + var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { + "nTh": nTh ? nTh : document.createElement('th'), + "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', + "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], + "mData": oDefaults.mData ? oDefaults.mData : iCol, + idx: iCol + } ); + oSettings.aoColumns.push( oCol ); + + // Add search object for column specific search. Note that the `searchCols[ iCol ]` + // passed into extend can be undefined. This allows the user to give a default + // with only some of the parameters defined, and also not give a default + var searchCols = oSettings.aoPreSearchCols; + searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); + + // Use the default column options function to initialise classes etc + _fnColumnOptions( oSettings, iCol, $(nTh).data() ); + } + + + /** + * Apply options for a column + * @param {object} oSettings dataTables settings object + * @param {int} iCol column index to consider + * @param {object} oOptions object with sType, bVisible and bSearchable etc + * @memberof DataTable#oApi + */ + function _fnColumnOptions( oSettings, iCol, oOptions ) + { + var oCol = oSettings.aoColumns[ iCol ]; + var oClasses = oSettings.oClasses; + var th = $(oCol.nTh); + + // Try to get width information from the DOM. We can't get it from CSS + // as we'd need to parse the CSS stylesheet. `width` option can override + if ( ! oCol.sWidthOrig ) { + // Width attribute + oCol.sWidthOrig = th.attr('width') || null; + + // Style attribute + var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/); + if ( t ) { + oCol.sWidthOrig = t[1]; + } + } + + /* User specified column options */ + if ( oOptions !== undefined && oOptions !== null ) + { + // Backwards compatibility + _fnCompatCols( oOptions ); + + // Map camel case parameters to their Hungarian counterparts + _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); + + /* Backwards compatibility for mDataProp */ + if ( oOptions.mDataProp !== undefined && !oOptions.mData ) + { + oOptions.mData = oOptions.mDataProp; + } + + if ( oOptions.sType ) + { + oCol._sManualType = oOptions.sType; + } + + // `class` is a reserved word in Javascript, so we need to provide + // the ability to use a valid name for the camel case input + if ( oOptions.className && ! oOptions.sClass ) + { + oOptions.sClass = oOptions.className; + } + if ( oOptions.sClass ) { + th.addClass( oOptions.sClass ); + } + + var origClass = oCol.sClass; + + $.extend( oCol, oOptions ); + _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); + + // Merge class from previously defined classes with this one, rather than just + // overwriting it in the extend above + if (origClass !== oCol.sClass) { + oCol.sClass = origClass + ' ' + oCol.sClass; + } + + /* iDataSort to be applied (backwards compatibility), but aDataSort will take + * priority if defined + */ + if ( oOptions.iDataSort !== undefined ) + { + oCol.aDataSort = [ oOptions.iDataSort ]; + } + _fnMap( oCol, oOptions, "aDataSort" ); + } + + /* Cache the data get and set functions for speed */ + var mDataSrc = oCol.mData; + var mData = _fnGetObjectDataFn( mDataSrc ); + var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; + + var attrTest = function( src ) { + return typeof src === 'string' && src.indexOf('@') !== -1; + }; + oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( + attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) + ); + oCol._setter = null; + + oCol.fnGetData = function (rowData, type, meta) { + var innerData = mData( rowData, type, undefined, meta ); + + return mRender && type ? + mRender( innerData, type, rowData, meta ) : + innerData; + }; + oCol.fnSetData = function ( rowData, val, meta ) { + return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); + }; + + // Indicate if DataTables should read DOM data as an object or array + // Used in _fnGetRowElements + if ( typeof mDataSrc !== 'number' ) { + oSettings._rowReadObject = true; + } + + /* Feature sorting overrides column specific when off */ + if ( !oSettings.oFeatures.bSort ) + { + oCol.bSortable = false; + th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called + } + + /* Check that the class assignment is correct for sorting */ + var bAsc = $.inArray('asc', oCol.asSorting) !== -1; + var bDesc = $.inArray('desc', oCol.asSorting) !== -1; + if ( !oCol.bSortable || (!bAsc && !bDesc) ) + { + oCol.sSortingClass = oClasses.sSortableNone; + oCol.sSortingClassJUI = ""; + } + else if ( bAsc && !bDesc ) + { + oCol.sSortingClass = oClasses.sSortableAsc; + oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; + } + else if ( !bAsc && bDesc ) + { + oCol.sSortingClass = oClasses.sSortableDesc; + oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; + } + else + { + oCol.sSortingClass = oClasses.sSortable; + oCol.sSortingClassJUI = oClasses.sSortJUI; + } + } + + + /** + * Adjust the table column widths for new data. Note: you would probably want to + * do a redraw after calling this function! + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnAdjustColumnSizing ( settings ) + { + /* Not interested in doing column width calculation if auto-width is disabled */ + if ( settings.oFeatures.bAutoWidth !== false ) + { + var columns = settings.aoColumns; + + _fnCalculateColumnWidths( settings ); + for ( var i=0 , iLen=columns.length ; i