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