package jnpf.bizcommon.onlyoffice.service.impl;
|
|
import jnpf.exception.DataException;
|
import org.springframework.stereotype.Component;
|
import org.springframework.util.StringUtils;
|
import org.w3c.dom.Element;
|
import org.w3c.dom.NodeList;
|
|
import javax.xml.XMLConstants;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.transform.OutputKeys;
|
import javax.xml.transform.Transformer;
|
import javax.xml.transform.TransformerFactory;
|
import javax.xml.transform.dom.DOMSource;
|
import javax.xml.transform.stream.StreamResult;
|
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayOutputStream;
|
import java.nio.charset.StandardCharsets;
|
import java.util.LinkedHashMap;
|
import java.util.Map;
|
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipOutputStream;
|
|
/** Canonicalizes qianpiye flow-version properties after DocumentServer saves a DOCX. */
|
@Component
|
public class DmsQianpiyeDocxPropertiesWriter {
|
static final String CUSTOM_PROPERTIES_PART = "docProps/custom.xml";
|
static final String NEW_FLOW_VERSION_PROPERTY = "DMS_NEW_FLOW_VERSION_ID";
|
static final String EDIT_FLOW_VERSION_PROPERTY = "DMS_EDIT_FLOW_VERSION_ID";
|
private static final String CUSTOM_PROPERTIES_NAMESPACE =
|
"http://schemas.openxmlformats.org/officeDocument/2006/custom-properties";
|
private static final String DOC_PROPERTIES_TYPES_NAMESPACE =
|
"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes";
|
private static final String FORMAT_ID = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}";
|
|
/** Keeps non-qianpiye and legacy documents intact; duplicate staged values use the last one. */
|
public byte[] upsertStagedFlowVersions(byte[] source) {
|
if (source == null || source.length == 0) return source;
|
try {
|
Map<String, byte[]> parts = readParts(source);
|
byte[] custom = parts.get(CUSTOM_PROPERTIES_PART);
|
if (custom == null) return source;
|
Map<String, String> versions = readFlowVersions(custom);
|
if (!StringUtils.hasText(versions.get(NEW_FLOW_VERSION_PROPERTY))
|
|| !StringUtils.hasText(versions.get(EDIT_FLOW_VERSION_PROPERTY))) return source;
|
parts.put(CUSTOM_PROPERTIES_PART, writeFlowVersions(custom, versions));
|
return writeParts(parts);
|
} catch (DataException e) {
|
throw e;
|
} catch (Exception e) {
|
throw new DataException("写入签批页流程版本属性失败: " + e.getMessage());
|
}
|
}
|
|
private Map<String, byte[]> readParts(byte[] source) throws Exception {
|
Map<String, byte[]> result = new LinkedHashMap<>();
|
try (ZipInputStream input = new ZipInputStream(new ByteArrayInputStream(source))) {
|
ZipEntry entry;
|
while ((entry = input.getNextEntry()) != null) {
|
if (entry.isDirectory()) continue;
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
byte[] buffer = new byte[8192];
|
for (int read; (read = input.read(buffer)) >= 0; ) output.write(buffer, 0, read);
|
result.put(entry.getName(), output.toByteArray());
|
}
|
}
|
return result;
|
}
|
|
private byte[] writeParts(Map<String, byte[]> parts) throws Exception {
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
try (ZipOutputStream zip = new ZipOutputStream(output)) {
|
for (Map.Entry<String, byte[]> part : parts.entrySet()) {
|
zip.putNextEntry(new ZipEntry(part.getKey()));
|
zip.write(part.getValue());
|
zip.closeEntry();
|
}
|
}
|
return output.toByteArray();
|
}
|
|
private Map<String, String> readFlowVersions(byte[] source) throws Exception {
|
org.w3c.dom.Document document = parse(source);
|
NodeList properties = document.getElementsByTagNameNS(CUSTOM_PROPERTIES_NAMESPACE, "property");
|
Map<String, String> result = new LinkedHashMap<>();
|
for (int index = 0; index < properties.getLength(); index++) {
|
Element property = (Element) properties.item(index);
|
String name = property.getAttribute("name");
|
if (NEW_FLOW_VERSION_PROPERTY.equals(name) || EDIT_FLOW_VERSION_PROPERTY.equals(name)) {
|
result.put(name, property.getTextContent().trim());
|
}
|
}
|
return result;
|
}
|
|
private byte[] writeFlowVersions(byte[] source, Map<String, String> versions) throws Exception {
|
org.w3c.dom.Document document = parse(source);
|
Element root = document.getDocumentElement();
|
NodeList properties = document.getElementsByTagNameNS(CUSTOM_PROPERTIES_NAMESPACE, "property");
|
int maxPid = 1;
|
for (int index = properties.getLength() - 1; index >= 0; index--) {
|
Element property = (Element) properties.item(index);
|
try { maxPid = Math.max(maxPid, Integer.parseInt(property.getAttribute("pid"))); }
|
catch (NumberFormatException ignored) { }
|
String name = property.getAttribute("name");
|
if (NEW_FLOW_VERSION_PROPERTY.equals(name) || EDIT_FLOW_VERSION_PROPERTY.equals(name)) root.removeChild(property);
|
}
|
appendProperty(document, root, maxPid + 1, NEW_FLOW_VERSION_PROPERTY, versions.get(NEW_FLOW_VERSION_PROPERTY));
|
appendProperty(document, root, maxPid + 2, EDIT_FLOW_VERSION_PROPERTY, versions.get(EDIT_FLOW_VERSION_PROPERTY));
|
TransformerFactory factory = TransformerFactory.newInstance();
|
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
try { factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); } catch (IllegalArgumentException ignored) { }
|
try { factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); } catch (IllegalArgumentException ignored) { }
|
Transformer transformer = factory.newTransformer();
|
transformer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());
|
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
|
ByteArrayOutputStream output = new ByteArrayOutputStream(source.length);
|
transformer.transform(new DOMSource(document), new StreamResult(output));
|
return output.toByteArray();
|
}
|
|
private void appendProperty(org.w3c.dom.Document document, Element root, int pid, String name, String value) {
|
Element property = document.createElementNS(CUSTOM_PROPERTIES_NAMESPACE, "property");
|
property.setAttribute("fmtid", FORMAT_ID);
|
property.setAttribute("pid", String.valueOf(pid));
|
property.setAttribute("name", name);
|
Element text = document.createElementNS(DOC_PROPERTIES_TYPES_NAMESPACE, "vt:lpwstr");
|
text.setTextContent(value);
|
property.appendChild(text);
|
root.appendChild(property);
|
}
|
|
private org.w3c.dom.Document parse(byte[] source) throws Exception {
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
factory.setNamespaceAware(true);
|
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
return factory.newDocumentBuilder().parse(new ByteArrayInputStream(source));
|
}
|
}
|