package fr.itldev.koya.action;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.Deflater;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.log4j.Logger;
import org.springframework.extensions.webscripts.WebScriptException;
import fr.itldev.koya.alfservice.KoyaActivityPoster;
import fr.itldev.koya.alfservice.KoyaNodeService;
import fr.itldev.koya.model.KoyaModel;
public class ZipContentActionExecuter extends ActionExecuterAbstractBase {
Logger logger = Logger.getLogger(ZipContentActionExecuter.class);
public static final String NAME = "koyaZip";
public static String PARAM_RESULT = "result";
public static String PARAM_ZIPNAME = "zipName";
public static String PARAM_COMPANYTMPZIPDIR = "companyTmpZipDir";
public static String PARAM_NODEREFS = "nodeRefs";
public static String PARAM_PDF = "pdf";
public static String PARAM_ASYNC = "async";
private NodeService nodeService;
private KoyaNodeService koyaNodeService;
private ContentService contentService;
private ActionService actionService;
private TransactionService transactionService;
private KoyaActivityPoster activityPoster;
private DictionaryService dictionaryService;
private NamespaceService namespaceService;
public void setNodeService(NodeService nodeService) {
this.nodeService = nodeService;
}
public void setKoyaNodeService(KoyaNodeService koyaNodeService) {
this.koyaNodeService = koyaNodeService;
}
public void setContentService(ContentService contentService) {
this.contentService = contentService;
}
public void setActionService(ActionService actionService) {
this.actionService = actionService;
}
public void setTransactionService(TransactionService transactionService) {
this.transactionService = transactionService;
}
public void setActivityPoster(KoyaActivityPoster activityPoster) {
this.activityPoster = activityPoster;
}
public void setDictionaryService(DictionaryService dictionaryService) {
this.dictionaryService = dictionaryService;
}
public void setNamespaceService(NamespaceService namespaceService) {
this.namespaceService = namespaceService;
}
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList) {
}
@Override
public void executeImpl(Action ruleAction, NodeRef actionedUponNodeRef) {
String zipName = (String) ruleAction.getParameterValue(PARAM_ZIPNAME);
NodeRef companyTmpZipDir = (NodeRef) ruleAction.getParameterValue(PARAM_COMPANYTMPZIPDIR);
Boolean pdf = (Boolean) ruleAction.getParameterValue(PARAM_PDF);
Boolean async = (Boolean) ruleAction.getParameterValue(PARAM_ASYNC);
List<NodeRef> nodeRefs = (List<NodeRef>) ruleAction.getParameterValue(PARAM_NODEREFS);
NodeRef zipNodeRef = null;
try {
final Map<QName, Serializable> properties = new HashMap<>();
zipName = koyaNodeService.getUniqueValidFileNameFromTitle(zipName);
properties.put(ContentModel.PROP_NAME, zipName);
ChildAssociationRef car = nodeService.createNode(companyTmpZipDir,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, zipName),
ContentModel.TYPE_CONTENT);
zipNodeRef = car.getChildRef();
Map<QName, Serializable> indexProp = new HashMap<>();
indexProp.put(ContentModel.PROP_IS_INDEXED, false);
indexProp.put(ContentModel.PROP_IS_CONTENT_INDEXED, false);
nodeService.addAspect(zipNodeRef, ContentModel.ASPECT_INDEX_CONTROL, indexProp);
nodeService.addAspect(zipNodeRef, KoyaModel.ASPECT_TEMPFILE, null);
ContentWriter contentWriter = contentService.getWriter(zipNodeRef,
ContentModel.PROP_CONTENT, true);
OutputStream os = contentWriter.getContentOutputStream();
CheckedOutputStream checksum = new CheckedOutputStream(os, new Adler32());
BufferedOutputStream buff = new BufferedOutputStream(checksum);
ZipArchiveOutputStream zipStream = new ZipArchiveOutputStream(buff);
// NOTE: This encoding allows us to workaround bug...
// http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4820807
zipStream.setEncoding("UTF-8");
zipStream.setMethod(ZipArchiveOutputStream.DEFLATED);
zipStream.setLevel(Deflater.BEST_COMPRESSION);
zipStream.setCreateUnicodeExtraFields(
ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS);
zipStream.setUseLanguageEncodingFlag(true);
zipStream.setFallbackToUTF8(true);
try {
addToZip(nodeRefs, pdf, zipStream, "");
} catch (IOException e) {
logger.error(e.getMessage(), e);
throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
} finally {
zipStream.close();
buff.close();
checksum.close();
os.close();
}
} catch (IOException | WebScriptException e) {
logger.error(e.getMessage(), e);
throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
}
if (!async) {
ruleAction.setParameterValue(PARAM_RESULT, zipNodeRef);
} else {
KoyaActivityPoster.KoyaActivityInfo info = activityPoster.getActivityInfo(zipNodeRef);
activityPoster.postDlFileAvailable(info,zipName);
}
}
private void addToZip(List<NodeRef> nodeRefs, Boolean pdf, ZipArchiveOutputStream out,
String path) throws IOException {
for (NodeRef nodeRef : nodeRefs) {
NodeRef nodeToAdd = nodeRef;
String nodeName = null;
if (pdf) {
Action pdfRenderAction = actionService.createAction(PdfRenderActionExecuter.NAME);
UserTransaction trx = transactionService.getNonPropagatingUserTransaction(false);
try {
trx.begin();
actionService.executeAction(pdfRenderAction, nodeRef);
trx.commit();
} catch (Throwable e) {
try {
trx.rollback();
} catch (IllegalStateException | SecurityException | SystemException e1) {
;
}
}
@SuppressWarnings("unchecked")
Map<String, Serializable> result = (Map<String, Serializable>) pdfRenderAction
.getParameterValue(PdfRenderActionExecuter.PARAM_RESULT);
nodeToAdd = new NodeRef(
result.get(PdfRenderActionExecuter.RESULT_PARAM_NODEREF).toString());
nodeName = (String) result.get(PdfRenderActionExecuter.RESULT_PARAM_TITLE);
}
addToZip(nodeToAdd, pdf, nodeName, out, path);
}
}
private void addToZip(NodeRef node, Boolean pdf, String nodeName, ZipArchiveOutputStream out,
String path) throws IOException {
QName nodeQnameType = this.nodeService.getType(node);
// Special case : links
if (this.dictionaryService.isSubClass(nodeQnameType, ApplicationModel.TYPE_FILELINK)) {
NodeRef linkDestinationNode = (NodeRef) nodeService.getProperty(node,
ContentModel.PROP_LINK_DESTINATION);
if (linkDestinationNode == null) {
return;
}
// Duplicate entry: check if link is not in the same space of the
// link destination
if (nodeService.getPrimaryParent(node).getParentRef()
.equals(nodeService.getPrimaryParent(linkDestinationNode).getParentRef())) {
return;
}
nodeQnameType = this.nodeService.getType(linkDestinationNode);
node = linkDestinationNode;
}
/**
* TODO test name/title export result.
*/
if (nodeName == null) {
nodeName = (String) nodeService.getProperty(node, ContentModel.PROP_TITLE);
}
nodeName = nodeName.replaceAll("([\\\"\\\\*\\\\\\>\\<\\?\\/\\:\\|]+)", "_");
// nodeName = noaccent ? unAccent(nodeName) : nodeName;
if (this.dictionaryService.isSubClass(nodeQnameType, ContentModel.TYPE_CONTENT)) {
ContentReader reader = contentService.getReader(node, ContentModel.PROP_CONTENT);
if (reader != null) {
InputStream is = null;
try {
is = reader.getContentInputStream();
} catch (ContentIOException cioex) {
logger.error("Failed to read node content while add to zip (ContentIOException) : Silently return " + node.toString());
logger.debug(cioex.toString());
return;
}
String filename = path.isEmpty() ? nodeName : path + '/' + nodeName;
ZipArchiveEntry entry = new ZipArchiveEntry(filename);
entry.setTime(((Date) nodeService.getProperty(node, ContentModel.PROP_MODIFIED))
.getTime());
entry.setSize(reader.getSize());
out.putArchiveEntry(entry);
try {
byte buffer[] = new byte[8192];
while (true) {
int nRead = is.read(buffer, 0, buffer.length);
if (nRead <= 0) {
break;
}
out.write(buffer, 0, nRead);
}
} catch (Exception exception) {
logger.error(exception.getMessage(), exception);
} finally {
is.close();
out.closeArchiveEntry();
}
} else {
logger.warn("Could not read : " + nodeName + "content");
}
} else if (this.dictionaryService.isSubClass(nodeQnameType, ContentModel.TYPE_FOLDER)
&& !this.dictionaryService.isSubClass(nodeQnameType,
ContentModel.TYPE_SYSTEM_FOLDER)) {
List<ChildAssociationRef> children = nodeService.getChildAssocs(node);
if (children.isEmpty()) {
String folderPath = path.isEmpty() ? nodeName + '/' : path + '/' + nodeName + '/';
out.putArchiveEntry(new ZipArchiveEntry(folderPath));
out.closeArchiveEntry();
} else {
List<NodeRef> nodeRefs = new ArrayList<>();
for (ChildAssociationRef childAssoc : children) {
NodeRef childNodeRef = childAssoc.getChildRef();
nodeRefs.add(childNodeRef);
}
addToZip(nodeRefs, pdf, out, path.isEmpty() ? nodeName : path + '/' + nodeName);
}
} else {
logger.info("Unmanaged type: " + nodeQnameType.getPrefixedQName(this.namespaceService)
+ ", filename: " + nodeName);
}
}
}