001/* 002 * (C) Copyright 2006-2014 Nuxeo SAS (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 * Nuxeo - initial API and implementation 016 * 017 * $Id$ 018 */ 019package org.nuxeo.ecm.platform.userworkspace.core.service; 020 021import java.io.Serializable; 022import java.security.Principal; 023import java.util.HashMap; 024import java.util.Map; 025 026import org.apache.commons.lang.StringUtils; 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.nuxeo.common.utils.IdUtils; 030import org.nuxeo.common.utils.Path; 031import org.nuxeo.ecm.core.api.CoreSession; 032import org.nuxeo.ecm.core.api.DocumentModel; 033import org.nuxeo.ecm.core.api.DocumentModelList; 034import org.nuxeo.ecm.core.api.DocumentNotFoundException; 035import org.nuxeo.ecm.core.api.NuxeoException; 036import org.nuxeo.ecm.core.api.NuxeoPrincipal; 037import org.nuxeo.ecm.core.api.PathRef; 038import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 039import org.nuxeo.ecm.core.api.event.CoreEventConstants; 040import org.nuxeo.ecm.core.api.pathsegment.PathSegmentService; 041import org.nuxeo.ecm.core.api.pathsegment.PathSegmentServiceDefault; 042import org.nuxeo.ecm.core.event.Event; 043import org.nuxeo.ecm.core.event.EventContext; 044import org.nuxeo.ecm.core.event.EventProducer; 045import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 046import org.nuxeo.ecm.core.event.impl.EventContextImpl; 047import org.nuxeo.ecm.platform.usermanager.UserAdapter; 048import org.nuxeo.ecm.platform.usermanager.UserManager; 049import org.nuxeo.ecm.platform.userworkspace.api.UserWorkspaceService; 050import org.nuxeo.ecm.platform.userworkspace.constants.UserWorkspaceConstants; 051import org.nuxeo.runtime.api.Framework; 052 053/** 054 * Abstract class holding most of the logic for using {@link UnrestrictedSessionRunner} while creating UserWorkspaces 055 * and associated resources 056 * 057 * @author tiry 058 * @since 5.9.5 059 */ 060public abstract class AbstractUserWorkspaceImpl implements UserWorkspaceService { 061 062 private static final Log log = LogFactory.getLog(DefaultUserWorkspaceServiceImpl.class); 063 064 private static final long serialVersionUID = 1L; 065 066 protected String targetDomainName; 067 068 public AbstractUserWorkspaceImpl() { 069 super(); 070 } 071 072 protected String getDomainName(CoreSession userCoreSession, DocumentModel currentDocument) { 073 if (targetDomainName == null) { 074 RootDomainFinder finder = new RootDomainFinder(userCoreSession); 075 finder.runUnrestricted(); 076 targetDomainName = finder.domaineName; 077 } 078 return targetDomainName; 079 } 080 081 protected String getUserWorkspaceNameForUser(String userName) { 082 PathSegmentService pss = Framework.getLocalService(PathSegmentService.class); 083 return IdUtils.generateId(userName, "-", false, pss.getMaxSize()); 084 } 085 086 protected String computePathUserWorkspaceRoot(CoreSession userCoreSession, String usedUsername, 087 DocumentModel currentDocument) { 088 String domainName = getDomainName(userCoreSession, currentDocument); 089 if (domainName == null) { 090 throw new NuxeoException("Unable to find root domain for UserWorkspace"); 091 } 092 Path path = new Path("/" + domainName); 093 path = path.append(UserWorkspaceConstants.USERS_PERSONAL_WORKSPACES_ROOT); 094 return path.toString(); 095 } 096 097 protected String computePathForUserWorkspace(CoreSession userCoreSession, String userName, 098 DocumentModel currentDocument) { 099 String rootPath = computePathUserWorkspaceRoot(userCoreSession, userName, currentDocument); 100 Path path = new Path(rootPath); 101 path = path.append(getUserWorkspaceNameForUser(userName)); 102 return path.toString(); 103 } 104 105 protected String computePathForUserWorkspaceCompat(CoreSession userCoreSession, String userName, 106 DocumentModel currentDocument) { 107 String rootPath = computePathUserWorkspaceRoot(userCoreSession, userName, currentDocument); 108 Path path = new Path(rootPath); 109 path = path.append(IdUtils.generateId(userName, "-", false, 30)); 110 return path.toString(); 111 } 112 113 @Override 114 public DocumentModel getCurrentUserPersonalWorkspace(String userName, DocumentModel currentDocument) 115 { 116 if (currentDocument == null) { 117 return null; 118 } 119 return getCurrentUserPersonalWorkspace(null, userName, currentDocument.getCoreSession(), currentDocument); 120 } 121 122 @Override 123 public DocumentModel getCurrentUserPersonalWorkspace(CoreSession userCoreSession, DocumentModel context) 124 { 125 return getCurrentUserPersonalWorkspace(userCoreSession.getPrincipal(), null, userCoreSession, context); 126 } 127 128 /** 129 * This method handles the UserWorkspace creation with a Principal or a username. At least one should be passed. If 130 * a principal is passed, the username is not taken into account. 131 * 132 * @since 5.7 "userWorkspaceCreated" is triggered 133 */ 134 protected DocumentModel getCurrentUserPersonalWorkspace(Principal principal, String userName, 135 CoreSession userCoreSession, DocumentModel context) { 136 if (principal == null && StringUtils.isEmpty(userName)) { 137 throw new NuxeoException("You should pass at least one principal or one username"); 138 } 139 140 String usedUsername; 141 if (principal instanceof NuxeoPrincipal) { 142 usedUsername = ((NuxeoPrincipal) principal).getActingUser(); 143 } else { 144 usedUsername = userName; 145 } 146 147 PathRef uwsDocRef = getExistingUserWorkspacePathRef(userCoreSession, usedUsername, context); 148 149 if (uwsDocRef == null) { 150 // do the creation 151 uwsDocRef = new PathRef(computePathForUserWorkspace(userCoreSession, usedUsername, context)); 152 PathRef rootRef = new PathRef(computePathUserWorkspaceRoot(userCoreSession, usedUsername, context)); 153 uwsDocRef = createUserWorkspace(rootRef, uwsDocRef, userCoreSession, principal, usedUsername); 154 } 155 156 // force Session synchro to process invalidation (in non JCA cases) 157 if (userCoreSession.getClass().getSimpleName().equals("LocalSession")) { 158 userCoreSession.save(); 159 } 160 161 return userCoreSession.getDocument(uwsDocRef); 162 } 163 164 protected PathRef getExistingUserWorkspacePathRef(CoreSession userCoreSession, String usedUsername, 165 DocumentModel context) { 166 PathRef uwsDocRef = new PathRef(computePathForUserWorkspace(userCoreSession, usedUsername, context)); 167 if (userCoreSession.exists(uwsDocRef)) { 168 return uwsDocRef; 169 } 170 // The document does not exist, try with the previous max size (30) 171 uwsDocRef = new PathRef(computePathForUserWorkspaceCompat(userCoreSession, usedUsername, context)); 172 if (userCoreSession.exists(uwsDocRef)) { 173 return uwsDocRef; 174 } 175 return null; 176 } 177 178 protected synchronized PathRef createUserWorkspace(PathRef rootRef, PathRef userWSRef, CoreSession userCoreSession, 179 Principal principal, String userName) { 180 181 UnrestrictedUWSCreator creator = new UnrestrictedUWSCreator(rootRef, userWSRef, userCoreSession, principal, 182 userName); 183 creator.runUnrestricted(); 184 userWSRef = creator.userWSRef; 185 return userWSRef; 186 } 187 188 @Override 189 public DocumentModel getUserPersonalWorkspace(NuxeoPrincipal principal, DocumentModel context) 190 { 191 return getCurrentUserPersonalWorkspace(principal, null, context.getCoreSession(), context); 192 } 193 194 @Override 195 public DocumentModel getUserPersonalWorkspace(String userName, DocumentModel context) { 196 UnrestrictedUserWorkspaceFinder finder = new UnrestrictedUserWorkspaceFinder(userName, context); 197 finder.runUnrestricted(); 198 return finder.getDetachedUserWorkspace(); 199 } 200 201 protected String buildUserWorkspaceTitle(Principal principal, String userName) { 202 if (userName == null) {// avoid looking for UserManager for nothing 203 return null; 204 } 205 // get the user service 206 UserManager userManager = Framework.getService(UserManager.class); 207 if (userManager == null) { 208 // for tests 209 return userName; 210 } 211 212 // Adapter userModel to get its fields (firstname, lastname) 213 DocumentModel userModel = userManager.getUserModel(userName); 214 if (userModel == null) { 215 return userName; 216 } 217 218 UserAdapter userAdapter = null; 219 userAdapter = userModel.getAdapter(UserAdapter.class); 220 221 if (userAdapter == null) { 222 return userName; 223 } 224 225 // compute the title 226 StringBuilder title = new StringBuilder(); 227 String firstName = userAdapter.getFirstName(); 228 if (firstName != null && firstName.trim().length() > 0) { 229 title.append(firstName); 230 } 231 232 String lastName = userAdapter.getLastName(); 233 if (lastName != null && lastName.trim().length() > 0) { 234 if (title.length() > 0) { 235 title.append(" "); 236 } 237 title.append(lastName); 238 } 239 240 if (title.length() > 0) { 241 return title.toString(); 242 } 243 244 return userName; 245 246 } 247 248 protected void notifyEvent(CoreSession coreSession, DocumentModel document, NuxeoPrincipal principal, 249 String eventId, Map<String, Serializable> properties) { 250 if (properties == null) { 251 properties = new HashMap<String, Serializable>(); 252 } 253 EventContext eventContext = null; 254 if (document != null) { 255 properties.put(CoreEventConstants.REPOSITORY_NAME, document.getRepositoryName()); 256 properties.put(CoreEventConstants.SESSION_ID, coreSession.getSessionId()); 257 properties.put(CoreEventConstants.DOC_LIFE_CYCLE, document.getCurrentLifeCycleState()); 258 eventContext = new DocumentEventContext(coreSession, principal, document); 259 } else { 260 eventContext = new EventContextImpl(coreSession, principal); 261 } 262 eventContext.setProperties(properties); 263 Event event = eventContext.newEvent(eventId); 264 Framework.getLocalService(EventProducer.class).fireEvent(event); 265 } 266 267 protected class UnrestrictedUWSCreator extends UnrestrictedSessionRunner { 268 269 PathRef rootRef; 270 271 PathRef userWSRef; 272 273 String userName; 274 275 Principal principal; 276 277 public UnrestrictedUWSCreator(PathRef rootRef, PathRef userWSRef, CoreSession userCoreSession, 278 Principal principal, String userName) { 279 super(userCoreSession); 280 this.rootRef = rootRef; 281 this.userWSRef = userWSRef; 282 this.userName = userName; 283 this.principal = principal; 284 } 285 286 @Override 287 public void run() { 288 289 // create root if needed 290 if (!session.exists(rootRef)) { 291 DocumentModel root; 292 try { 293 root = doCreateUserWorkspacesRoot(session, rootRef); 294 } catch (DocumentNotFoundException e) { 295 // domain may have been removed ! 296 targetDomainName = null; 297 rootRef = new PathRef(computePathUserWorkspaceRoot(session, userName, null)); 298 root = doCreateUserWorkspacesRoot(session, rootRef); 299 userWSRef = new PathRef(computePathForUserWorkspace(session, userName, null)); 300 } 301 assert (root.getPathAsString().equals(rootRef.toString())); 302 } 303 304 // create user WS if needed 305 if (!session.exists(userWSRef)) { 306 DocumentModel uw = doCreateUserWorkspace(session, userWSRef, principal, userName); 307 assert (uw.getPathAsString().equals(userWSRef.toString())); 308 } 309 310 session.save(); 311 } 312 313 } 314 315 protected class UnrestrictedUserWorkspaceFinder extends UnrestrictedSessionRunner { 316 317 protected DocumentModel userWorkspace; 318 319 protected String userName; 320 321 protected DocumentModel context; 322 323 protected UnrestrictedUserWorkspaceFinder(String userName, DocumentModel context) { 324 super(context.getCoreSession().getRepositoryName(), userName); 325 this.userName = userName; 326 this.context = context; 327 } 328 329 @Override 330 public void run() { 331 userWorkspace = getCurrentUserPersonalWorkspace(null, userName, session, context); 332 if (userWorkspace != null) { 333 userWorkspace.detach(true); 334 } 335 } 336 337 public DocumentModel getDetachedUserWorkspace() { 338 return userWorkspace; 339 } 340 } 341 342 protected class RootDomainFinder extends UnrestrictedSessionRunner { 343 344 public RootDomainFinder(CoreSession userCoreSession) { 345 super(userCoreSession); 346 } 347 348 protected String domaineName; 349 350 @Override 351 public void run() { 352 353 String targetName = getComponent().getTargetDomainName(); 354 PathRef ref = new PathRef("/" + targetName); 355 if (session.exists(ref)) { 356 domaineName = targetName; 357 return; 358 } 359 // configured domain does not exist !!! 360 DocumentModelList domains = session.query("select * from Domain order by dc:created"); 361 362 if (!domains.isEmpty()) { 363 domaineName = domains.get(0).getName(); 364 } 365 } 366 } 367 368 protected UserWorkspaceServiceImplComponent getComponent() { 369 return (UserWorkspaceServiceImplComponent) Framework.getRuntime().getComponent( 370 UserWorkspaceServiceImplComponent.NAME); 371 } 372 373 protected abstract DocumentModel doCreateUserWorkspacesRoot(CoreSession unrestrictedSession, PathRef rootRef); 374 375 protected abstract DocumentModel doCreateUserWorkspace(CoreSession unrestrictedSession, PathRef wsRef, 376 Principal principal, String userName); 377 378}