/** * Koya is an alfresco module that provides a corporate orientated dataroom. * * Copyright (C) Itl Developpement 2014 * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 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 Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see `<http://www.gnu.org/licenses/>`. */ package fr.itldev.koya.alfservice; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.search.impl.lucene.LuceneUtils; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.Pair; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Transformer; import org.apache.log4j.Logger; import fr.itldev.koya.action.UpdateLastModificationDateActionExecuter; import fr.itldev.koya.alfservice.security.SpaceAclService; import fr.itldev.koya.exception.KoyaServiceException; import fr.itldev.koya.model.KoyaModel; import fr.itldev.koya.model.KoyaNode; import fr.itldev.koya.model.exceptions.KoyaErrorCodes; import fr.itldev.koya.model.impl.Company; import fr.itldev.koya.model.impl.Document; import fr.itldev.koya.model.impl.Dossier; import fr.itldev.koya.model.impl.Space; import fr.itldev.koya.model.impl.User; /** * Dossiers Handling Service * * */ public class DossierService { private final Logger logger = Logger.getLogger(this.getClass()); private NodeService nodeService; private KoyaNodeService koyaNodeService; protected SearchService searchService; private NamespacePrefixResolver prefixResolver; private TransactionService transactionService; private SpaceAclService spaceAclService; private KoyaContentService koyaContentService; private UserService userService; private AuthenticationService authenticationService; private OwnableService ownableService; private KoyaActivityPoster activityPoster; private ActionService actionService; // <editor-fold defaultstate="collapsed" desc="getters/setters"> public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } public void setKoyaNodeService(KoyaNodeService koyaNodeService) { this.koyaNodeService = koyaNodeService; } public void setSearchService(SearchService searchService) { this.searchService = searchService; } public void setPrefixResolver(NamespacePrefixResolver prefixResolver) { this.prefixResolver = prefixResolver; } public void setTransactionService(TransactionService transactionService) { this.transactionService = transactionService; } public void setSpaceAclService(SpaceAclService spaceAclService) { this.spaceAclService = spaceAclService; } public void setKoyaContentService(KoyaContentService koyaContentService) { this.koyaContentService = koyaContentService; } public void setUserService(UserService userService) { this.userService = userService; } public void setAuthenticationService( AuthenticationService authenticationService) { this.authenticationService = authenticationService; } public void setOwnableService(OwnableService ownableService) { this.ownableService = ownableService; } public void setActivityPoster(KoyaActivityPoster activityPoster) { this.activityPoster = activityPoster; } public void setActionService(ActionService actionService) { this.actionService = actionService; } // </editor-fold> /** * * @param title * @param parent * @param prop * @return * @throws KoyaServiceException */ public Dossier create(String title, NodeRef parent, Map<QName, String> prop) throws KoyaServiceException { // Dossier must have a title if (title == null || title.isEmpty()) { throw new KoyaServiceException(KoyaErrorCodes.DOSSIER_EMPTY_NAME); } // parent must be a Space if (!nodeService.getType(parent).equals(KoyaModel.TYPE_SPACE)) { throw new KoyaServiceException(KoyaErrorCodes.DOSSIER_NOT_IN_SPACE); } String name = koyaNodeService.getUniqueValidFileNameFromTitle(title); if (nodeService.getChildByName(parent, ContentModel.ASSOC_CONTAINS, name) != null) { throw new KoyaServiceException(KoyaErrorCodes.DOSSIER_NAME_EXISTS); } // build node properties final Map<QName, Serializable> properties = new HashMap<>(); properties.put(ContentModel.PROP_NAME, name); properties.put(ContentModel.PROP_TITLE, title); if (prop != null) { properties.putAll(prop); } ChildAssociationRef car = nodeService .createNode(parent, ContentModel.ASSOC_CONTAINS, QName .createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), KoyaModel.TYPE_DOSSIER, properties); Dossier created = koyaNodeService.getKoyaNode(car.getChildRef(), Dossier.class); // Add lastModified Aspect Map<QName, Serializable> props = new HashMap<>(); props.put(KoyaModel.PROP_LASTMODIFICATIONDATE, new Date()); nodeService.addAspect(created.getNodeRef(), KoyaModel.ASPECT_LASTMODIFIED, props); logger.info("[Koya] Dossier creation : " + created.getTitle() + " created (" + created.getNodeRef() + ")"); return created; } /** * * @param parent * @return * @throws KoyaServiceException */ public List<Dossier> list(NodeRef parent) throws KoyaServiceException { if (!nodeService.getType(parent).equals(KoyaModel.TYPE_SPACE)) { throw new KoyaServiceException( KoyaErrorCodes.DOSSIER_INVALID_PARENT_NODE); } List nodes = nodeService.getChildAssocs(parent, new HashSet<QName>() { { add(KoyaModel.TYPE_DOSSIER); } }); /** * transform List<ChildAssociationRef> to List<Dossier> */ CollectionUtils.transform(nodes, new Transformer() { @Override public Object transform(Object input) { try { return koyaNodeService.getKoyaNode( ((ChildAssociationRef) input).getChildRef(), Dossier.class); } catch (KoyaServiceException ex) { return null; } } }); return nodes; } /** * Get Dossier by reference * * @param company * @param reference * @return * @throws fr.itldev.koya.exception.KoyaServiceException */ public Dossier getDossier(final Company company, final String reference) { String luceneRequest = "TYPE:\"koya:dossier\" AND @koya\\:reference:\"" + reference + "\" AND PATH:\" /app:company_home/st:sites/cm:" + company.getName() + "/cm:documentLibrary/*/*\""; ResultSet rs = null; try { rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_LUCENE, luceneRequest); if (rs.length() == 1) { return koyaNodeService.getKoyaNode(rs.iterator().next() .getNodeRef(), Dossier.class); } if (rs.length() == 0) { return null; } if (rs.length() > 1) { logger.error(rs.length() + " dossiers match reference " + reference); } } finally { if (rs != null) { rs.close(); } } return null; } public List<Dossier> getInactiveDossier(final Space space, final Date inactiveFrom, final boolean notNotifiedOnly) throws KoyaServiceException { String luceneRequest = "+PATH:\"" + nodeService.getPath(space.getNodeRef()).toPrefixString( prefixResolver) + "/*\" +TYPE:\"koya:dossier\" +@koya\\:lastModificationDate:[MIN TO \"" + LuceneUtils.getLuceneDateString(inactiveFrom) + "\"]"; if (notNotifiedOnly) { luceneRequest += " +@koya\\:notified:false"; } ResultSet rs = null; try { rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_LUCENE, luceneRequest); List<Dossier> inactiveDossier = new ArrayList<>(rs.length()); for (NodeRef n : rs.getNodeRefs()) { inactiveDossier.add(koyaNodeService.getKoyaNode(n, Dossier.class)); } return inactiveDossier; } finally { if (rs != null) { rs.close(); } } } public void updateLastModificationDate(final Dossier d) { if (d == null) { return; } Action updateLastModificationDateAction = actionService.createAction(UpdateLastModificationDateActionExecuter.NAME); actionService.executeAction(updateLastModificationDateAction, d.getNodeRef(), false, true); } /* * ========== Site Consumer Upload in specific upload directory ========== */ public Map<String, String> uploadSiteConsumerDocument(Dossier dossier, String fileName, org.springframework.extensions.surf.util.Content content, String clientMessage) throws KoyaServiceException { NodeRef upDir = koyaNodeService.getPublicUploadFolder(dossier); if (upDir == null) { upDir = createPublicUploadFolder(dossier); } final NodeRef finalUpDir = upDir; String realFileName = fileName; boolean exists = true; int uniqueFileCounter = 0; while (exists) { // run as System to check existance of any file in dir (even other // users files) final String finalFileName = realFileName; exists = AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Boolean>() { @Override public Boolean doWork() throws Exception { return nodeService.getChildByName(finalUpDir, ContentModel.ASSOC_CONTAINS, finalFileName) != null; } }); if (exists) { // build new filename int dot = fileName.lastIndexOf('.'); realFileName = fileName.substring(0, dot) + "-" + ++uniqueFileCounter + fileName.substring(dot); } } Pair<NodeRef,Map<String, String>> uploadResult = koyaContentService.createContentNode( upDir, realFileName, null, content.getMimetype(), content.getEncoding(), content.getInputStream(), false); //===== permissions and ownable ownableService.setOwner(uploadResult.getFirst(), authenticationService.getCurrentUserName()); //no inherence spaceAclService.initConsumerUploadedDocument(dossier, uploadResult.getFirst()); Document d = koyaNodeService.getKoyaNode( new NodeRef(uploadResult.getSecond().get("nodeRef")), Document.class); User uploader = userService.getUserByUsername(authenticationService .getCurrentUserName()); activityPoster.postConsumerUpload(d,dossier, uploader); return uploadResult.getSecond(); } public List<Document> listSiteConsumerDocuments(final Dossier dossier) { NodeRef upDir = koyaNodeService.getPublicUploadFolder(dossier); if (upDir != null) { List<Document> docs = new ArrayList<>(); for (KoyaNode k : koyaNodeService.listChildrenPaginated(upDir, 0, Integer.MAX_VALUE, false, null, null, null).getFirst()) { if (Document.class.isAssignableFrom(k.getClass())) { docs.add((Document) k); } } return docs; } else { return new ArrayList<>(); } } private NodeRef createPublicUploadFolder(final Dossier dossier) { // create folder if not exists NodeRef upDir = AuthenticationUtil .runAsSystem(new AuthenticationUtil.RunAsWork<NodeRef>() { @Override public NodeRef doWork() throws Exception { Company c = koyaNodeService.getFirstParentOfType( dossier.getNodeRef(), Company.class); // create public client upload dir if not exists NodeRef companyClientUpDir = nodeService .getChildByName(c.getNodeRef(), ContentModel.ASSOC_CONTAINS, KoyaNodeService.SITECONSUMER_UPLOADDIR_NAME); if (companyClientUpDir == null) { final Map<QName, Serializable> properties = new HashMap<>(); properties.put(ContentModel.PROP_NAME, KoyaNodeService.SITECONSUMER_UPLOADDIR_NAME); properties.put(ContentModel.PROP_TITLE, KoyaNodeService.SITECONSUMER_UPLOADDIR_NAME); ChildAssociationRef car = nodeService.createNode( c.getNodeRef(), ContentModel.ASSOC_CONTAINS, QName.createQName( NamespaceService.CONTENT_MODEL_1_0_URI, KoyaNodeService.SITECONSUMER_UPLOADDIR_NAME), ContentModel.TYPE_FOLDER, properties); companyClientUpDir = car.getChildRef(); } // create dossier public upload client dir final Map<QName, Serializable> properties = new HashMap<>(); properties.put(ContentModel.PROP_NAME, koyaNodeService.publicUploadFolderName(dossier)); properties.put(ContentModel.PROP_TITLE, dossier.getName()); properties.put(KoyaModel.PROP_DOSSIERREF, dossier.getNodeRef()); ChildAssociationRef car = nodeService.createNode( companyClientUpDir, ContentModel.ASSOC_CONTAINS, QName.createQName( NamespaceService.CONTENT_MODEL_1_0_URI, koyaNodeService.publicUploadFolderName(dossier)), KoyaModel.TYPE_DOSSIERCLASSIFYFOLDER, properties); spaceAclService.initSingleDossierSiteConsumerUploadDirAcl( dossier, car.getChildRef()); return car.getChildRef(); } }); return upDir; } }