001/* 002 * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * arussel 016 */ 017package org.nuxeo.ecm.platform.routing.core.impl; 018 019import java.util.ArrayList; 020import java.util.Calendar; 021import java.util.Date; 022import java.util.List; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.ecm.core.api.CoreSession; 027import org.nuxeo.ecm.core.api.DocumentModel; 028import org.nuxeo.ecm.core.api.DocumentModelList; 029import org.nuxeo.ecm.core.api.DocumentRef; 030import org.nuxeo.ecm.core.api.NuxeoPrincipal; 031import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 032import org.nuxeo.ecm.core.api.security.ACE; 033import org.nuxeo.ecm.core.api.security.ACL; 034import org.nuxeo.ecm.core.api.security.ACP; 035import org.nuxeo.ecm.core.api.security.SecurityConstants; 036import org.nuxeo.ecm.platform.routing.api.DocumentRoute; 037import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants; 038import org.nuxeo.ecm.platform.routing.api.DocumentRoutingPersister; 039import org.nuxeo.ecm.platform.routing.core.persistence.TreeHelper; 040import org.nuxeo.ecm.platform.usermanager.UserManager; 041import org.nuxeo.ecm.platform.userworkspace.api.UserWorkspaceService; 042import org.nuxeo.runtime.api.Framework; 043 044/** 045 * The default persister. It persists the {@link DocumentRoute} in a tree hierarchy ressembling the current date. New 046 * model created from instance are stored in the personal workspace of the user. 047 * 048 * @author arussel 049 */ 050public class DocumentRoutingTreePersister implements DocumentRoutingPersister { 051 052 private static final String DC_TITLE = "dc:title"; 053 054 protected static final Log log = LogFactory.getLog(DocumentRoutingTreePersister.class); 055 056 @Override 057 public DocumentModel getParentFolderForDocumentRouteInstance(DocumentModel document, CoreSession session) { 058 return TreeHelper.getOrCreateDateTreeFolder(session, getOrCreateRootOfDocumentRouteInstanceStructure(session), 059 new Date(), "HiddenFolder"); 060 } 061 062 @Override 063 public DocumentModel createDocumentRouteInstanceFromDocumentRouteModel(DocumentModel model, CoreSession session) { 064 DocumentModel parent = getParentFolderForDocumentRouteInstance(model, session); 065 DocumentModel result = session.copy(model.getRef(), parent.getRef(), null); 066 // copy now copies all the acls, and we don't need the readOnly 067 // policy applied on the model 068 // on the instance, too => removing acls 069 result = undoReadOnlySecurityPolicy(result, session); 070 // set initiator 071 NuxeoPrincipal principal = (NuxeoPrincipal) session.getPrincipal(); 072 String initiator = principal.getActingUser(); 073 result.setPropertyValue(DocumentRoutingConstants.INITIATOR, initiator); 074 // using the ref, the value of the attached document might not been 075 // saved on the model 076 result.setPropertyValue(DocumentRoutingConstants.ATTACHED_DOCUMENTS_PROPERTY_NAME, 077 model.getPropertyValue(DocumentRoutingConstants.ATTACHED_DOCUMENTS_PROPERTY_NAME)); 078 // reset creation date, used for workflow start time 079 result.setPropertyValue("dc:created", Calendar.getInstance()); 080 result.setPropertyValue(DocumentRoutingConstants.DOCUMENT_ROUTE_INSTANCE_MODEL_ID, model.getId()); 081 session.saveDocument(result); 082 return result; 083 } 084 085 @Override 086 public DocumentModel saveDocumentRouteInstanceAsNewModel(DocumentModel routeInstance, DocumentModel parentFolder, 087 String newName, CoreSession session) { 088 DocumentModel result = session.copy(routeInstance.getRef(), parentFolder.getRef(), newName); 089 return undoReadOnlySecurityPolicy(result, session); 090 } 091 092 @Override 093 public DocumentModel getOrCreateRootOfDocumentRouteInstanceStructure(CoreSession session) { 094 DocumentModel root = getDocumentRoutesStructure( 095 DocumentRoutingConstants.DOCUMENT_ROUTE_INSTANCES_ROOT_DOCUMENT_TYPE, session); 096 if (root == null) { 097 root = createDocumentRoutesStructure(DocumentRoutingConstants.DOCUMENT_ROUTE_INSTANCES_ROOT_DOCUMENT_TYPE, 098 DocumentRoutingConstants.DOCUMENT_ROUTE_INSTANCES_ROOT_ID, session); 099 } 100 return root; 101 } 102 103 /** 104 * Finds the first domain by name, and creates under it the root container for the structure containing the route 105 * instances. 106 */ 107 protected DocumentModel createDocumentRoutesStructure(String routeStructureDocType, String id, CoreSession session) 108 { 109 DocumentModel root = session.createDocumentModel(session.getRootDocument().getPathAsString(), id, 110 routeStructureDocType); 111 root.setPropertyValue(DC_TITLE, routeStructureDocType); 112 root = session.createDocument(root); 113 ACP acp = session.getACP(root.getRef()); 114 ACL acl = acp.getOrCreateACL(ACL.LOCAL_ACL); 115 acl.addAll(getACEs()); 116 session.setACP(root.getRef(), acp, true); 117 return root; 118 } 119 120 /** 121 * Create the rootModels under to root document. Grant READ to everyone on the root models ; workflow availability 122 * is specified on each route 123 * 124 * @param routeStructureDocType 125 * @param id 126 * @param session 127 * @return 128 */ 129 protected DocumentModel createModelsRoutesStructure(String routeStructureDocType, String id, CoreSession session) 130 { 131 DocumentModel rootModels = session.createDocumentModel("/", id, routeStructureDocType); 132 rootModels.setPropertyValue(DC_TITLE, routeStructureDocType); 133 rootModels = session.createDocument(rootModels); 134 ACP acp = session.getACP(rootModels.getRef()); 135 ACL acl = acp.getOrCreateACL(ACL.LOCAL_ACL); 136 acl.add(new ACE(SecurityConstants.EVERYONE, SecurityConstants.READ, true)); 137 session.setACP(rootModels.getRef(), acp, true); 138 return rootModels; 139 } 140 141 /** 142 * @return 143 */ 144 protected List<ACE> getACEs() { 145 List<ACE> aces = new ArrayList<ACE>(); 146 for (String group : getUserManager().getAdministratorsGroups()) { 147 aces.add(new ACE(group, SecurityConstants.EVERYTHING, true)); 148 } 149 aces.add(new ACE(DocumentRoutingConstants.ROUTE_MANAGERS_GROUP_NAME, SecurityConstants.READ_WRITE, true)); 150 aces.add(new ACE(SecurityConstants.EVERYONE, SecurityConstants.EVERYTHING, false)); 151 return aces; 152 } 153 154 protected UserManager getUserManager() { 155 return Framework.getService(UserManager.class); 156 } 157 158 protected DocumentModel getDocumentRoutesStructure(String type, CoreSession session) { 159 DocumentModelList res = session.query(String.format("SELECT * from %s", type)); 160 if (res == null || res.isEmpty()) { 161 return null; 162 } 163 if (res.size() > 1) { 164 if (log.isWarnEnabled()) { 165 log.warn("More han one DocumentRouteInstanceRoot found:"); 166 for (DocumentModel model : res) { 167 log.warn(" - " + model.getName() + ", " + model.getPathAsString()); 168 } 169 } 170 } 171 return res.get(0); 172 } 173 174 @Override 175 public DocumentModel getParentFolderForNewModel(CoreSession session, DocumentModel instance) { 176 UserWorkspaceService service = Framework.getService(UserWorkspaceService.class); 177 return service.getCurrentUserPersonalWorkspace(session, instance); 178 } 179 180 @Override 181 public String getNewModelName(DocumentModel instance) { 182 return "(COPY) " + instance.getPropertyValue("dc:title"); 183 } 184 185 protected DocumentModel undoReadOnlySecurityPolicy(DocumentModel instance, CoreSession session) 186 { 187 UndoReadOnlySecurityPolicy runner = new UndoReadOnlySecurityPolicy(session, instance.getRef()); 188 runner.runUnrestricted(); 189 return session.getDocument(runner.getInstanceRef()); 190 } 191 192 class UndoReadOnlySecurityPolicy extends UnrestrictedSessionRunner { 193 194 DocumentRef documentRef; 195 196 public UndoReadOnlySecurityPolicy(CoreSession session, DocumentRef documentRef) { 197 super(session); 198 this.documentRef = documentRef; 199 } 200 201 @Override 202 public void run() { 203 DocumentModel instance = session.getDocument(documentRef); 204 if (instance == null) { 205 return; 206 } 207 ACP acp = instance.getACP(); 208 // remove READ for everyone 209 ACL routingACL = acp.getOrCreateACL(DocumentRoutingConstants.DOCUMENT_ROUTING_ACL); 210 routingACL.remove(new ACE(SecurityConstants.EVERYONE, SecurityConstants.READ, true)); 211 // unblock rights inheritance 212 ACL localACL = acp.getOrCreateACL(ACL.LOCAL_ACL); 213 localACL.remove(new ACE(SecurityConstants.EVERYONE, SecurityConstants.EVERYTHING, false)); 214 instance.setACP(acp, true); 215 } 216 217 DocumentRef getInstanceRef() { 218 return documentRef; 219 } 220 } 221 222 @Override 223 public DocumentModel getParentFolderForDocumentRouteModels(CoreSession session) { 224 DocumentModel root = getDocumentRoutesStructure( 225 DocumentRoutingConstants.DOCUMENT_ROUTE_MODELS_ROOT_DOCUMENT_TYPE, session); 226 if (root == null) { 227 root = createModelsRoutesStructure(DocumentRoutingConstants.DOCUMENT_ROUTE_MODELS_ROOT_DOCUMENT_TYPE, 228 DocumentRoutingConstants.DOCUMENT_ROUTE_MODELS_ROOT_ID, session); 229 } 230 return root; 231 } 232}