diff --git a/README.md b/README.md index 299b821..8ef5a3d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # open-JSD-7756 -JSD-7756 定时调度邮件推送折叠树默认不展开 \ No newline at end of file +JSD-7756 定时调度邮件推送折叠树默认不展开\ +免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\ +仅作为开发者学习参考使用!禁止用于任何商业用途!\ +为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。 \ No newline at end of file diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..e7bc0a4 --- /dev/null +++ b/plugin.xml @@ -0,0 +1,17 @@ + + com.fr.plugin.mail.fold + + yes + 1.2 + 10.0 + 2018-07-31 + fr.open + + --> + ]]> + + + + + \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/mail/fold/ExportUtil.java b/src/main/java/com/fr/plugin/mail/fold/ExportUtil.java new file mode 100644 index 0000000..07a9e24 --- /dev/null +++ b/src/main/java/com/fr/plugin/mail/fold/ExportUtil.java @@ -0,0 +1,269 @@ +package com.fr.plugin.mail.fold; + +import com.fr.base.DynamicUnitList; +import com.fr.cache.list.IntList; +import com.fr.form.ui.Widget; +import com.fr.main.workbook.ResultWorkBook; +import com.fr.report.ReportHelper; +import com.fr.report.cell.ResultCellElement; +import com.fr.report.cell.WidgetAttrElem; +import com.fr.report.elementcase.ElementCase; +import com.fr.report.report.Report; +import com.fr.report.web.button.form.TreeNodeToggleButton; +import com.fr.report.worksheet.AnalysisRWorkSheet; +import com.fr.script.Calculator; +import com.fr.stable.ViewActor; +import com.fr.stable.script.CalculatorProvider; +import com.fr.stable.web.SessionProvider; +import com.fr.third.v2.org.apache.poi.ss.usermodel.Sheet; +import com.fr.third.v2.org.apache.poi.ss.usermodel.Workbook; +import com.fr.third.v2.org.apache.poi.xssf.usermodel.XSSFRow; +import com.fr.web.core.ReportSessionIDInfor; +import com.fr.web.core.TreeHTMLWriter; +import com.fr.web.core.reportcase.WebElementReportCase; +import com.fr.web.output.html.chwriter.ViewCellWriter; +import com.fr.web.request.EmptyRepository; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.util.*; + +/** + * @author fr.open + * @date 2019/6/4 + */ +public class ExportUtil { + + public static void FoldTree(ResultWorkBook book, Workbook workbook, int i, ReportSessionIDInfor sessionProvider) throws CloneNotSupportedException { + book = sessionProvider.getWorkBookDefine().execute(book.getExecuteParameters(), new ViewActor()); + SessionRepository sessionRepository = new SessionRepository().buildSession(sessionProvider); + Sheet poiSheet = workbook.getSheetAt(i); + if (book.getReport(i) instanceof AnalysisRWorkSheet) { + AnalysisRWorkSheet sheet = (AnalysisRWorkSheet) book.getReport(i); + Calculator c = Calculator.createCalculator(); + c.setAttribute(Report.KEY, sheet); + TreeHTMLWriter htmlWriter = new TreeHTMLWriter(); + ViewCellWriter cellHtmlWriter = new ViewCellWriter(sessionRepository, i, sheet.getReportSettings(), true); + htmlWriter.writeReportToHtml(new WebElementReportCase(sheet, sessionRepository), i, cellHtmlWriter, new EmptyRepository(), ""); + cellHtmlWriter.dealWithAllTreeNodeRelation(c); + DynamicUnitList rowHeightList = (DynamicUnitList) ReportHelper.getRowHeightList((ElementCase) book.getReport(i)).clone(); + List spanList = new ArrayList(); + for (int j = 0; j < rowHeightList.size(); j++) { + if (rowHeightList.get(j).equal_zero()) { + spanList.add(j); + } + } + Map map = ExportUtil.generateNodeTree(sheet, spanList); + Map map1 = ExportUtil.generateNodeTree(sheet, new ArrayList()); + if (!map.isEmpty()) { + Iterator it = map.keySet().iterator(); + while (it.hasNext()) { + TreeNode node = map.get(it.next()); + if (node.getParent() == -1) { + foldRow(map, node.getSelf(), poiSheet); + } + } + } + + } + } + + public static class SessionRepository extends EmptyRepository { + private CalculatorProvider calculatorProvider = Calculator.createCalculator(); + + + public SessionRepository buildSession(ReportSessionIDInfor sessionIDInfor) { + calculatorProvider.setAttribute(SessionProvider.KEY, sessionIDInfor); + return this; + } + + @Override + public CalculatorProvider getCalculator() { + return calculatorProvider; + } + + @Override + public String getSessionID() { + return ((SessionProvider) calculatorProvider.getAttribute(SessionProvider.KEY)).getSessionID(); + } + } + + + private static void foldRow(Map map, int self, Sheet poiSheet) { + TreeNode node = map.get(self); + if (node == null) { + return; + } + int start = node.getSons().get(1); + int end = node.getSons().get(node.getSons().size() - 1); + for (int i = 1; i < node.getSons().size(); i++) { + int curStart = getStart(map, node.getSons().get(i)); + int curEnd = getEnd(map, node.getSons().get(i)); + if (curStart < start) { + start = curStart; + } + if (curEnd > end) { + end = curEnd; + } + foldRow(map, node.getSons().get(i), poiSheet); + } + + poiSheet.groupRow(start, end); + if (poiSheet.getRow(self) instanceof XSSFRow) { + for (int r = start; r <= end; r++) { + XSSFRow expand = (XSSFRow) poiSheet.getRow(r); + expand.getCTRow().setHidden(true); + } + XSSFRow xssfRow = (XSSFRow) poiSheet.getRow(self); + xssfRow.getCTRow().setCollapsed(true); + } + if (node.getParent() == -1) { + } + + } + + private static int getStart(Map map, int self) { + int start = self; + TreeNode node = map.get(self); + if (node == null) { + return start; + } + if (node.getSons() == null) { + return start; + } + for (int i = 1; i < node.getSons().size(); i++) { + int son = getStart(map, node.getSons().get(i)); + if (son < start) { + start = son; + } + } + return start; + + } + + private static int getEnd(Map map, int self) { + int end = self; + TreeNode node = map.get(self); + if (node == null) { + return end; + } + if (node.getSons() == null) { + return end; + } + for (int i = 1; i < node.getSons().size(); i++) { + int son = getEnd(map, node.getSons().get(i)); + if (son > end) { + end = son; + } + } + return end; + } + + public static ByteArrayInputStream parse(final OutputStream out) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos = (ByteArrayOutputStream) out; + final ByteArrayInputStream swapStream = new ByteArrayInputStream(baos.toByteArray()); + return swapStream; + } + + private static ResultCellElement findToggleCell(AnalysisRWorkSheet reportCase, int rowIndex) { + Iterator cellIterator = reportCase.getRow(rowIndex); + while (cellIterator.hasNext()) { + ResultCellElement tmpCell = (ResultCellElement) cellIterator.next(); + if (tmpCell instanceof WidgetAttrElem) { + if (((WidgetAttrElem) tmpCell).getWidget() instanceof TreeNodeToggleButton) { + return tmpCell; + } + } + } + return null; + } + + public static void buildNodeMap(AnalysisRWorkSheet reportCase, ResultCellElement cellElment, Map nodeMap, int parent, List spanList) { + if (cellElment == null) { + return; + } + TreeNodeToggleButton toggleButton = (TreeNodeToggleButton) ((WidgetAttrElem) cellElment).getWidget(); + int self = cellElment.getRow(); + IntList sonList = toggleButton.getRelativeIndexList(); + //折叠行 + if (sonList != null && sonList.size() > 1) { + if (sonList.get(0) == -1) { + nodeMap.put(span(self, spanList), new TreeNode(span(self, spanList), span(parent, spanList), span(sonList, spanList))); + int size = sonList.size(); + for (int i = 0; i < size; i++) { + buildNodeMap(reportCase, findToggleCell(reportCase, sonList.get(i)), nodeMap, self, spanList); + } + } + } + } + + private static IntList span(IntList list, List spanList) { + IntList res = new IntList(); + for (int i = 0; i < list.size(); i++) { + res.add(span(list.get(i), spanList)); + } + return res; + } + + private static int span(int row, List spanList) { + if (spanList.isEmpty()) { + return row; + } + for (int i = 0; i < spanList.size(); i++) { + if (row >= spanList.get(i) && (i == spanList.size() - 1 || row < spanList.get(i + 1))) { + return row - (i + 1); + } + } + return row; + } + + private static void setLevel(Map map) { + for (Integer key : map.keySet()) { + int level = getLevel(map, map.get(key)); + TreeNode node = map.get(key); + node.setLevel(level); + map.put(key, node); + } + } + + private static int getLevel(Map map, TreeNode treeNode) { + if (treeNode.getParent() == -1) { + return 0; + } + int level = 0; + int tmp = treeNode.getParent(); + while (map.get(tmp) != null) { + level++; + tmp = map.get(tmp).getParent(); + } + return level; + } + + public static Map generateNodeTree(AnalysisRWorkSheet resultWS, List spanList) { + int rowSize = resultWS.getRowCount(); + Map nodeMap = new HashMap(); + //遍历行 + for (int rowIndex = rowSize; rowIndex > -1; rowIndex--) { + ResultCellElement treeNodeCell = findToggleCell(resultWS, rowIndex); + if (treeNodeCell != null) { + Widget widget = ((WidgetAttrElem) treeNodeCell).getWidget(); + IntList sonList = ((TreeNodeToggleButton) widget).getRelativeIndexList(); + if (sonList != null && sonList.size() > 1) { + if (sonList.get(0) == -1) { + //折叠行 + if (nodeMap.containsKey(span(treeNodeCell.getRow(),spanList))) { + continue; + } + buildNodeMap(resultWS, treeNodeCell, nodeMap, -1, spanList); + setLevel(nodeMap); + } else { + //折叠列 暂不处理 + } + } + } + } + return nodeMap; + } +} diff --git a/src/main/java/com/fr/plugin/mail/fold/JoinedTag.java b/src/main/java/com/fr/plugin/mail/fold/JoinedTag.java new file mode 100644 index 0000000..d2889df --- /dev/null +++ b/src/main/java/com/fr/plugin/mail/fold/JoinedTag.java @@ -0,0 +1,38 @@ +package com.fr.plugin.mail.fold; + +import com.fr.stable.html.Tag; + +import java.util.List; + +/** + * @author fr.open + * @version 10.0 + * Created by fr.open on 2020/7/6 + */ +public class JoinedTag extends Tag { + + public JoinedTag(Tag originalTag) { + super.setTagName( originalTag.getTagName()); + super.setId(originalTag.getId()); + super.setClasses(originalTag.getClasses()); + super.setStyles(originalTag.getStyles()); + super.setAttributes(originalTag.getAttributes()); + super.setSubHtmlList(originalTag.getSubHtmlList()); + super.setSiblingHtmlList(originalTag.getSiblingHtmlList()); + } + + @Override + public StringBuffer getClassList() { + StringBuffer sb = new StringBuffer(); + List classes = super.getClasses(); + if (classes != null && classes.size() > 0) { + int size = classes.size(); + for (int i = 0; i < size - 1; i++) { + sb.append(classes.get(i)); + sb.append(' '); + } + sb.append(classes.get(size - 1)); + } + return new StringBuffer(sb.toString().replace(" ", "")); + } +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/mail/fold/MailFoldFormula.java b/src/main/java/com/fr/plugin/mail/fold/MailFoldFormula.java new file mode 100644 index 0000000..665d0b3 --- /dev/null +++ b/src/main/java/com/fr/plugin/mail/fold/MailFoldFormula.java @@ -0,0 +1,79 @@ +package com.fr.plugin.mail.fold; + +import com.fr.main.workbook.ResultWorkBook; +import com.fr.plugin.transform.FunctionRecorder; +import com.fr.report.cell.ResultCellElement; +import com.fr.report.cell.WidgetAttrElem; +import com.fr.report.web.button.form.TreeNodeToggleButton; +import com.fr.report.worksheet.AnalysisRWorkSheet; +import com.fr.schedule.base.bean.output.OutputEmail; +import com.fr.schedule.base.constant.ScheduleConstants; +import com.fr.schedule.extension.report.job.output.formula.EmailFormula; +import com.fr.schedule.extension.report.util.ScheduleParameterUtils; +import com.fr.schedule.feature.util.ScheduleUtils; +import com.fr.stable.unit.UNIT; +import com.fr.web.core.ReportSessionIDInfor; + +import java.util.*; + +/** + * @Author fr.open + * @Date 2021/5/8 + * @Description + **/ +@FunctionRecorder +public class MailFoldFormula extends EmailFormula { + @Override + public void dealWithFormulaParam(OutputEmail outputEmail, ResultWorkBook resultWorkBook, List> list) throws Exception { + ResultWorkBook fold = getFold(resultWorkBook, list.get(0)); + String contentText = ScheduleParameterUtils.dealWithParameter(outputEmail.getBodyContent(), list.get(0), fold); + super.dealWithFormulaParam(outputEmail, fold, list); + ReportSessionIDInfor sessionIdInfo = (new ReportSessionIDInfor(Collections.emptyMap())).buildResultWorkBook(fold); + boolean showTPL = outputEmail.isPreviewAttach();// 是否勾选正文 + boolean addLink = outputEmail.isAddLink();// 是否勾选链接 + String bodyContent = contentText; + bodyContent = showTPL ? bodyContent + "
" + TagUtils.exportTemplateAsHtml(null, sessionIdInfo) : bodyContent; + bodyContent = addLink ? bodyContent + "
" + ScheduleConstants.SCHEDULE_LINK + "" : bodyContent; + outputEmail.setBodyContent(bodyContent); + outputEmail.setBodyContentWithoutTPL(bodyContent); + } + + private ResultWorkBook getFold(ResultWorkBook book, Map param) { + if (param.containsKey("expand") && "false".equals(param.get("expand").toString())) { + return book; + } + for (int i = 0; i < book.getReportCount(); i++) { + if (book.getReport(i) instanceof AnalysisRWorkSheet) { + AnalysisRWorkSheet sheet = (AnalysisRWorkSheet) book.getReport(i); + Map map = ExportUtil.generateNodeTree(sheet, new ArrayList()); + for (int j = 0; j < sheet.getRowCount(); j++) { + if (findToggleCell(sheet, j)) { + if (map.get(j) == null || map.get(j).getParent() != -1) { + sheet.setRowHeight(j, UNIT.ZERO); + if (map.get(j) != null && map.get(j).getSons() != null) { + for (int k = 1; k < map.get(j).getSons().size(); k++) { + sheet.setRowHeight(map.get(j).getSons().get(k), UNIT.ZERO); + } + } + } + } + } + } + } + return book; + } + + private boolean findToggleCell(AnalysisRWorkSheet reportCase, int rowIndex) { + Iterator cellIterator = reportCase.getRow(rowIndex); + while (cellIterator.hasNext()) { + ResultCellElement tmpCell = (ResultCellElement) cellIterator.next(); + if (tmpCell instanceof WidgetAttrElem) { + if (((WidgetAttrElem) tmpCell).getWidget() instanceof TreeNodeToggleButton) { + return true; + } + } + } + return false; + } + +} diff --git a/src/main/java/com/fr/plugin/mail/fold/TagUtils.java b/src/main/java/com/fr/plugin/mail/fold/TagUtils.java new file mode 100644 index 0000000..1dda98c --- /dev/null +++ b/src/main/java/com/fr/plugin/mail/fold/TagUtils.java @@ -0,0 +1,202 @@ +package com.fr.plugin.mail.fold; + +import com.fr.base.Base64; +import com.fr.base.EmailAttachment; +import com.fr.page.web.EmailCssHolder; +import com.fr.stable.CommonCodeUtils; +import com.fr.stable.StringUtils; +import com.fr.stable.email.EmailAttachmentProvider; +import com.fr.stable.html.Tag; +import com.fr.web.core.ReportSessionIDInfor; +import com.fr.web.email.EmailPreviewContentReader; + +import javax.activation.DataHandler; +import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMultipart; +import javax.servlet.http.HttpServletRequest; +import java.util.*; + +/** + * @Author: fr.open + * @Date: 2020/8/11 16:36 + * 模板正文导出成html或者base64图片 + */ +public class TagUtils { + + public static String exportTemplateAsHtml(HttpServletRequest req, ReportSessionIDInfor sessionIDInfor) throws Exception { + Map cssMap = EmailCssHolder.getInstance().getCssMap("/com/fr/web/core/css/report.core.css"); +// Tag reportContentTag = MA.F().B(req, sessionIDInfor); + Tag reportContentTag = EmailPreviewContentReader.getInstance().getContentTag(req, sessionIDInfor); + Set classGroupSet = getAllClsGroup(reportContentTag); + StringBuffer finalcss = new StringBuffer(); + for (String clsGroup : classGroupSet) { + String[] claArr = clsGroup.split(" "); + String key = clsGroup.replace(" ", ""); + StringBuffer mergecss = new StringBuffer("{"); + for (String cls : claArr) { + String text = cssMap.get("." + cls); + if (StringUtils.isNotEmpty(text)) { + text = text.replace("*", ""); // outlook 网页端不兼容*号css样式,*是outlook全局样式 + /* if (!StringUtils.contains(text,"word-break")&&!StringUtils.contains(text,"word-wrap")){ + mergecss.append(text.substring(text.indexOf("{") + 1, text.lastIndexOf("}"))).append(";"); + }*/ + mergecss.append(text.substring(text.indexOf("{") + 1, text.lastIndexOf("}"))).append(";"); + } + } + if (!"{".equals(mergecss.toString())) { + mergecss.append("}"); + finalcss.append(".").append(key).append(" ").append(mergecss); + } + + } + finalcss.append("table.x-table{overflow:visible;line-height:1;}");//QQ邮箱样式影响 + finalcss.append("td{line-height:1 !important;}");//网易邮箱样式影响 + +// setTdNowrap(reportContentTag); + + Set allUndisplayTag = findAllUndisplayTag(reportContentTag); + String contetTag = reportContentTag.toString(); + for (Tag tag : allUndisplayTag) { + contetTag = contetTag.replace(tag.toString(), ""); // 把隐藏的tag不输出,outlook客户端不兼容display:none的style样式 + } + String bodyContent = "" + contetTag; + changeBase64ToPng(bodyContent); + return bodyContent; + } + + + private static Set getAllClsGroup(Tag tag) { + Set result = new HashSet(); + if (tag != null) { + String tagCls = tag.getClassList().toString(); + if (StringUtils.isNotEmpty(tagCls)) { + result.add(tagCls); + } + List subList = tag.getSubHtmlList(); + if (subList != null) { + for (int i = 0; i < subList.size(); i++) { + Object html = subList.get(i); + if (html instanceof Tag) { + result.addAll(getAllClsGroup((Tag) html)); + if ("td".equals(((Tag) html).getTagName())) { + html = new JoinedTag((Tag) html); + subList.set(i, html); + } + } + } + } + } + return result; + } + + + private static Set findAllUndisplayTag(Tag tag) { + Set result = new HashSet(); + if (tag != null) { + String tagStyle = tag.getStyleList().toString(); + if (StringUtils.contains(tagStyle, "display:none")) { + result.add(tag); + } + + List subList = tag.getSubHtmlList(); + if (subList != null) { + for (int i = 0; i < subList.size(); i++) { + Object html = subList.get(i); + if (html instanceof Tag) { + result.addAll(findAllUndisplayTag((Tag) html)); + } + } + } + } + return result; + } + + //((Tag) html).setAttributes((Map) new HashMap().put("nowrap","nowrap")); + private static void setTdNowrap(Tag tag) { + if (tag != null) { + if ("td".equals(tag.getTagName())) { + tag.setAttributes((Map) new HashMap().put("nowrap", "nowrap")); + } + List subList = tag.getSubHtmlList(); + if (subList != null) { + for (int i = 0; i < subList.size(); i++) { + Object html = subList.get(i); + if (html instanceof Tag) { + setTdNowrap((Tag) html); + } + } + } + } + } + + + + + public static EmailAttachment[] buildBase64ToPngEmailAttachment(String bodyContent) throws MessagingException { + ArrayList emailAttachmentsGroup = new ArrayList(); + String defaultName = "templateImage"; + String base64Tag = "data:image/png;base64,"; + MimeMultipart multipart = new MimeMultipart(); + if (bodyContent.contains(base64Tag)) { + MimeBodyPart htmlPart = new MimeBodyPart(); + while (bodyContent.contains(base64Tag)) { + int start = bodyContent.indexOf(base64Tag); + int end = bodyContent.indexOf(">", start); + String base64 = bodyContent.substring(start + base64Tag.length(), end); + String cidPrefix = defaultName+ UUID.randomUUID(); + bodyContent = bodyContent.substring(0, start) + "cid:" + cidPrefix + "\"" + bodyContent.substring(end); +// 跟代码看到com.fr.base.EmailManager.sendWithRecord里面,setFormat直接给的null,这里暂时先用null + String setFormat = null; + htmlPart.setContent(bodyContent, setFormat); + EmailAttachment emailAttachment = new EmailAttachment(Base64.decode(CommonCodeUtils.attributeHtmlDecode(base64)), cidPrefix); + emailAttachmentsGroup.add(emailAttachment); + } + } + + int size = emailAttachmentsGroup.size(); + EmailAttachment[] emailAttachments = new EmailAttachment[size]; + if (!emailAttachmentsGroup.isEmpty()){ + for (int i=0;i", start); + String base64 = bodyContent.substring(start + base64Tag.length(), end); + String cidPrefix = defaultName+ UUID.randomUUID(); + bodyContent = bodyContent.substring(0, start) + "cid:" + cidPrefix + "\"" + bodyContent.substring(end); +// 跟代码看到com.fr.base.EmailManager.sendWithRecord里面,setFormat直接给的null,这里暂时先用null + String setFormat = null; + htmlPart.setContent(bodyContent, setFormat); + + addImagePart(multipart, new EmailAttachment(Base64.decode(CommonCodeUtils.attributeHtmlDecode(base64)), cidPrefix), cidPrefix); + } + multipart.addBodyPart(htmlPart); + + } + return multipart; + } + + private static void addImagePart(MimeMultipart mimeMultipart, EmailAttachmentProvider provider, String filename) throws MessagingException { + MimeBodyPart mimeBodyPart = new MimeBodyPart(); + mimeBodyPart.setDataHandler(new DataHandler(provider.getByteArrayDataSource())); + mimeBodyPart.setHeader("Content-ID", "<" + filename + ">"); + mimeBodyPart.setFileName(filename + ".png"); + mimeBodyPart.setDisposition("inline"); + mimeMultipart.addBodyPart(mimeBodyPart); + } + +} \ No newline at end of file diff --git a/src/main/java/com/fr/plugin/mail/fold/TreeNode.java b/src/main/java/com/fr/plugin/mail/fold/TreeNode.java new file mode 100644 index 0000000..71b8300 --- /dev/null +++ b/src/main/java/com/fr/plugin/mail/fold/TreeNode.java @@ -0,0 +1,63 @@ +package com.fr.plugin.mail.fold; + +import com.fr.cache.list.IntList; + +/** + * @author fr.open + * @date 2019/6/3 + */ +public class TreeNode { + /** + * 自身所在行 + */ + private int self; + /** + * 父节点所在行 + */ + private int parent; + /** + * 子节点 + */ + + private IntList sons; + + private int level; + + public TreeNode(int self, int parent, IntList sons) { + this.self = self; + this.parent = parent; + this.sons = sons; + } + + public int getParent() { + return parent; + } + + public void setParent(int parent) { + this.parent = parent; + } + + public int getSelf() { + return self; + } + + public void setSelf(int self) { + this.self = self; + } + + public IntList getSons() { + return sons; + } + + public void setSons(IntList sons) { + this.sons = sons; + } + + public int getLevel() { + return level; + } + + public void setLevel(int level) { + this.level = level; + } +} diff --git a/教程.docx b/教程.docx new file mode 100644 index 0000000..b4fbce7 Binary files /dev/null and b/教程.docx differ