diff --git a/source/java/org/alfresco/repo/webdav/ActivityPostProducer.java b/source/java/org/alfresco/repo/webdav/ActivityPostProducer.java new file mode 100644 index 0000000000..cd4544a659 --- /dev/null +++ b/source/java/org/alfresco/repo/webdav/ActivityPostProducer.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.webdav; + + +/** + * WebDAVMethods that are able to post activity data must implement this interface + * in order that the CloudWebDAVServlet will supply the object with an + * ActivityPoster collaborator. + * + * @author Matt Ward + */ +public interface ActivityPostProducer +{ + void setActivityPoster(ActivityPoster activityPoster); +} diff --git a/source/java/org/alfresco/repo/webdav/ActivityPoster.java b/source/java/org/alfresco/repo/webdav/ActivityPoster.java new file mode 100644 index 0000000000..17d359441c --- /dev/null +++ b/source/java/org/alfresco/repo/webdav/ActivityPoster.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.webdav; + +import org.alfresco.service.cmr.model.FileInfo; + +/** + * WebDAV methods can ActivityPoster to create entries in the activity feed. + * + * @author Matt Ward + */ +public interface ActivityPoster +{ + void postFileAdded( + String siteId, + String tenantDomain, + FileInfo contentNodeInfo) throws WebDAVServerException; + + void postFileUpdated( + String siteId, + String tenantDomain, + FileInfo contentNodeInfo) throws WebDAVServerException; + + void postFileDeleted( + String siteId, + String tenantDomain, + String parentPath, + FileInfo contentNodeInfo) throws WebDAVServerException; +} diff --git a/source/java/org/alfresco/repo/webdav/ActivityPosterImpl.java b/source/java/org/alfresco/repo/webdav/ActivityPosterImpl.java new file mode 100644 index 0000000000..2088d313c2 --- /dev/null +++ b/source/java/org/alfresco/repo/webdav/ActivityPosterImpl.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.webdav; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.activities.ActivityService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.util.Pair; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * WebDAV methods may use an instance of this class to post activity data. + * + * @see ActivityPoster + * @author Matt Ward + */ +public class ActivityPosterImpl implements ActivityPoster +{ + private static final String FILE_ADDED = "org.alfresco.documentlibrary.file-added"; + private static final String FILE_UPDATED = "org.alfresco.documentlibrary.file-updated"; + private static final String FILE_DELETED = "org.alfresco.documentlibrary.file-deleted"; + private static final String APP_TOOL = "WebDAV"; + private final ActivityService activityService; + private final NodeService nodeService; + private final PersonService personService; + + + /** + * Constructor + * + * @param activityService + * @param nodeService + * @param personService + */ + public ActivityPosterImpl(ActivityService activityService, NodeService nodeService, PersonService personService) + { + this.activityService = activityService; + this.nodeService = nodeService; + this.personService = personService; + } + + + /** + * {@inheritDoc} + */ + @Override + public void postFileAdded( + String siteId, + String tenantDomain, + FileInfo contentNodeInfo) throws WebDAVServerException + { + postFileActivity(FILE_ADDED, siteId, tenantDomain, null, contentNodeInfo); + } + + /** + * {@inheritDoc} + */ + @Override + public void postFileUpdated( + String siteId, + String tenantDomain, + FileInfo contentNodeInfo) throws WebDAVServerException + { + postFileActivity(FILE_UPDATED, siteId, tenantDomain, null, contentNodeInfo); + } + + /** + * {@inheritDoc} + */ + @Override + public void postFileDeleted( + String siteId, + String tenantDomain, + String parentPath, + FileInfo contentNodeInfo) throws WebDAVServerException + { + postFileActivity(FILE_DELETED, siteId, tenantDomain, parentPath, contentNodeInfo); + } + + + private void postFileActivity( + String activityType, + String siteId, + String tenantDomain, + String parentPath, + FileInfo contentNodeInfo) throws WebDAVServerException + { + Pair personName = getPersonName(); + final String firstName = personName.getFirst(); + final String lastName = personName.getSecond(); + final String fileName = contentNodeInfo.getName(); + final NodeRef nodeRef = contentNodeInfo.getNodeRef(); + JSONObject json = createActivityJSON(tenantDomain, parentPath, nodeRef, firstName, lastName, fileName); + + activityService.postActivity( + activityType, + siteId, + APP_TOOL, + json.toString()); + } + + /** + * Create JSON suitable for create, modify or delete activity posts. Returns a new JSONObject + * containing appropriate key/value pairs. + * + * @param tenantDomain + * @param nodeRef + * @param firstName + * @param lastName + * @param fileName + * @throws WebDAVServerException + * @return JSONObject + */ + private JSONObject createActivityJSON( + String tenantDomain, + String parentPath, + NodeRef nodeRef, + String firstName, + String lastName, + String fileName) throws WebDAVServerException + { + JSONObject json = new JSONObject(); + try + { + json.put("nodeRef", nodeRef); + if (parentPath != null) + { + // Used for deleted files. + json.put("page", "documentlibrary?path=" + parentPath); + } + else + { + // Used for added or modified files. + json.put("page", "document-details?nodeRef=" + nodeRef); + } + json.put("title", fileName); + json.put("firstName", firstName); + json.put("lastName", lastName); + if (!tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) + { + // Only used in multi-tenant setups. + json.put("tenantDomain", tenantDomain); + } + } + catch (JSONException error) + { + throw new WebDAVServerException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + + return json; + } + + /** + * Creates the tuple (firstName, lastName) for the current user. + * + * @return Pair<String, String> + */ + private Pair getPersonName() + { + String firstName = ""; + String lastName = ""; + String userName = AuthenticationUtil.getFullyAuthenticatedUser(); + NodeRef person = personService.getPerson(userName); + if (person != null) + { + firstName = (String) nodeService.getProperty(person, ContentModel.PROP_FIRSTNAME); + lastName = (String) nodeService.getProperty(person, ContentModel.PROP_LASTNAME); + } + + return new Pair(firstName, lastName); + } +} diff --git a/source/java/org/alfresco/repo/webdav/DeleteMethod.java b/source/java/org/alfresco/repo/webdav/DeleteMethod.java index a38ac6869d..af1da7a626 100644 --- a/source/java/org/alfresco/repo/webdav/DeleteMethod.java +++ b/source/java/org/alfresco/repo/webdav/DeleteMethod.java @@ -27,14 +27,18 @@ import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.webdav.WebDavService; /** * Implements the WebDAV DELETE method * * @author gavinc */ -public class DeleteMethod extends WebDAVMethod +public class DeleteMethod extends WebDAVMethod implements ActivityPostProducer { + private ActivityPoster activityPoster; + /** * Default constructor */ @@ -100,8 +104,65 @@ public class DeleteMethod extends WebDAVMethod // ALF-7079 fix, working copies are not deleted at all if (!getNodeService().hasAspect(fileInfo.getNodeRef(), ContentModel.ASPECT_WORKING_COPY)) { - // delete it - fileFolderService.delete(fileInfo.getNodeRef()); + // As this content will be deleted, we need to extract some info before it's no longer available. + String siteId = getSiteId(); + NodeRef deletedNodeRef = fileInfo.getNodeRef(); + FileInfo parentFile = getDAVHelper().getParentNodeForPath(getRootNodeRef(), getPath(), getServletPath()); + boolean hidden = getNodeService().hasAspect(deletedNodeRef, ContentModel.ASPECT_HIDDEN); + // Delete it + fileFolderService.delete(deletedNodeRef); + // Don't post activity data for hidden files, resource forks etc. + if (!hidden) + { + postActivity(parentFile, fileInfo, siteId); + } } } + + + /** + * Create a deletion activity post. + * + * @param parent The FileInfo for the deleted file's parent. + * @param deletedFile The FileInfo for the deleted file. + * @throws WebDAVServerException + */ + private void postActivity(FileInfo parent, FileInfo deletedFile, String siteId) throws WebDAVServerException + { + WebDavService davService = getDAVHelper().getServiceRegistry().getWebDavService(); + if (!davService.activitiesEnabled()) + { + // Don't post activities if this behaviour is disabled. + return; + } + + String tenantDomain = getTenantDomain(); + + // Check there is enough information to publish site activity. + if (!siteId.equals(DEFAULT_SITE_ID)) + { + SiteService siteService = getServiceRegistry().getSiteService(); + NodeRef documentLibrary = siteService.getContainer(siteId, SiteService.DOCUMENT_LIBRARY); + String parentPath = "/"; + try + { + parentPath = getDAVHelper().getPathFromNode(documentLibrary, parent.getNodeRef()); + } + catch (FileNotFoundException error) + { + if (logger.isDebugEnabled()) + { + logger.debug("No " + SiteService.DOCUMENT_LIBRARY + " container found."); + } + } + + activityPoster.postFileDeleted(siteId, tenantDomain, parentPath, deletedFile); + } + } + + @Override + public void setActivityPoster(ActivityPoster activityPoster) + { + this.activityPoster = activityPoster; + } } diff --git a/source/java/org/alfresco/repo/webdav/PutMethod.java b/source/java/org/alfresco/repo/webdav/PutMethod.java index 183c7707bb..e4a75d371d 100644 --- a/source/java/org/alfresco/repo/webdav/PutMethod.java +++ b/source/java/org/alfresco/repo/webdav/PutMethod.java @@ -33,6 +33,7 @@ import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.webdav.WebDavService; import org.springframework.dao.ConcurrencyFailureException; /** @@ -40,7 +41,7 @@ import org.springframework.dao.ConcurrencyFailureException; * * @author Gavin Cornwell */ -public class PutMethod extends WebDAVMethod +public class PutMethod extends WebDAVMethod implements ActivityPostProducer { // Request parameters private String m_strContentType = null; @@ -49,6 +50,7 @@ public class PutMethod extends WebDAVMethod // Try to delete the node if the PUT fails private boolean noContent = false; private boolean created = false; + private ActivityPoster activityPoster; /** * Default constructor @@ -264,6 +266,8 @@ public class PutMethod extends WebDAVMethod } throw new WebDAVServerException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e); } + + postActivity(); } /** @@ -273,8 +277,62 @@ public class PutMethod extends WebDAVMethod * * @return true if the content was newly created, false if existing. */ - public boolean isCreated() + protected boolean isCreated() { return created; } + + /** + * Create an activity post. + * + * @throws WebDAVServerException + */ + private void postActivity() throws WebDAVServerException + { + WebDavService davService = getDAVHelper().getServiceRegistry().getWebDavService(); + if (!davService.activitiesEnabled()) + { + // Don't post activities if this behaviour is disabled. + return; + } + + String path = getPath(); + String siteId = getSiteId(); + String tenantDomain = getTenantDomain(); + + if (siteId.equals(DEFAULT_SITE_ID)) + { + // There is not enough information to publish site activity. + return; + } + + FileInfo contentNodeInfo = null; + try + { + contentNodeInfo = getDAVHelper().getNodeForPath(getRootNodeRef(), path, getServletPath()); + NodeRef nodeRef = contentNodeInfo.getNodeRef(); + // Don't post activity data for hidden files, resource forks etc. + if (!getNodeService().hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN)) + { + if (isCreated()) + { + activityPoster.postFileAdded(siteId, tenantDomain, contentNodeInfo); + } + else + { + activityPoster.postFileUpdated(siteId, tenantDomain, contentNodeInfo); + } + } + } + catch (FileNotFoundException error) + { + throw new WebDAVServerException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + @Override + public void setActivityPoster(ActivityPoster activityPoster) + { + this.activityPoster = activityPoster; + } } diff --git a/source/java/org/alfresco/repo/webdav/WebDAVHelper.java b/source/java/org/alfresco/repo/webdav/WebDAVHelper.java index ce1e90ad99..f9d9df33d3 100644 --- a/source/java/org/alfresco/repo/webdav/WebDAVHelper.java +++ b/source/java/org/alfresco/repo/webdav/WebDAVHelper.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.StringTokenizer; import org.alfresco.model.ContentModel; +import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -81,6 +82,7 @@ public class WebDAVHelper private ActionService m_actionService; private AuthenticationService m_authService; private PermissionService m_permissionService; + private TenantService m_tenantService; // Empty XML attribute list @@ -89,7 +91,7 @@ public class WebDAVHelper /** * Class constructor */ - protected WebDAVHelper(ServiceRegistry serviceRegistry, LockStore lockStore, AuthenticationService authService) + protected WebDAVHelper(ServiceRegistry serviceRegistry, LockStore lockStore, AuthenticationService authService, TenantService tenantService) { m_serviceRegistry = serviceRegistry; @@ -102,7 +104,7 @@ public class WebDAVHelper m_lockService = m_serviceRegistry.getLockService(); m_actionService = m_serviceRegistry.getActionService(); m_permissionService = m_serviceRegistry.getPermissionService(); - + m_tenantService = tenantService; m_authService = authService; m_lockStore = lockStore; @@ -202,6 +204,16 @@ public class WebDAVHelper return m_permissionService; } + /** + * Retrieve the {@link TenantService} held by the helper. + * + * @return TenantService + */ + public TenantService getTenantService() + { + return m_tenantService; + } + /** * @return Return the copy service */ diff --git a/source/java/org/alfresco/repo/webdav/WebDAVMethod.java b/source/java/org/alfresco/repo/webdav/WebDAVMethod.java index 683dae4c83..9e17eb8513 100644 --- a/source/java/org/alfresco/repo/webdav/WebDAVMethod.java +++ b/source/java/org/alfresco/repo/webdav/WebDAVMethod.java @@ -47,6 +47,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.ActionService; @@ -61,6 +62,8 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.TempFileProvider; @@ -83,6 +86,8 @@ import org.xml.sax.SAXException; */ public abstract class WebDAVMethod { + protected static final String DEFAULT_SITE_ID = ""; + // Log output protected static Log logger = LogFactory.getLog("org.alfresco.webdav.protocol"); @@ -140,6 +145,10 @@ public abstract class WebDAVMethod // request scope protected Map m_childToParent = new HashMap(); protected Map m_parentLockInfo = new HashMap(); + + private String siteId; + + private String tenantDomain; /** * Default constructor @@ -1498,5 +1507,67 @@ public abstract class WebDAVMethod } return sb.toString(); + } + + + /** + * Get the site ID (short-name) that the current request relates to. The site ID + * will be {@link DEFAULT_SITE_ID} if not specifically set. + * + * @return The site ID + */ + protected String getSiteId() + { + if (siteId == null) + { + siteId = determineSiteId(); + } + return siteId; + } + + protected String determineSiteId() + { + SiteService siteService = m_davHelper.getServiceRegistry().getSiteService(); + String siteId; + try + { + FileInfo fileInfo = m_davHelper.getNodeForPath( + getRootNodeRef(), + getPath(), + m_request.getServletPath()); + SiteInfo siteInfo = siteService.getSite(fileInfo.getNodeRef()); + siteId = siteInfo.getShortName(); + } + catch (FileNotFoundException error) + { + siteId = DEFAULT_SITE_ID; + } + return siteId; + } + + /** + * Get the tenant domain for the current user and request. The tenant domain + * will be {@link TenantService#DEFAULT_DOMAIN} if not specifically set. + * + * @return The tenant domain. + */ + protected String getTenantDomain() + { + if (tenantDomain == null) + { + tenantDomain = determineTenantDomain(); + } + return tenantDomain; + } + + protected String determineTenantDomain() + { + TenantService tenantService = m_davHelper.getTenantService(); + String tenantDomain = tenantService.getCurrentUserDomain(); + if (tenantDomain == null) + { + return TenantService.DEFAULT_DOMAIN; + } + return tenantDomain; } } diff --git a/source/java/org/alfresco/repo/webdav/WebDAVServlet.java b/source/java/org/alfresco/repo/webdav/WebDAVServlet.java index ed5e436e62..e880955a4e 100644 --- a/source/java/org/alfresco/repo/webdav/WebDAVServlet.java +++ b/source/java/org/alfresco/repo/webdav/WebDAVServlet.java @@ -34,11 +34,13 @@ import javax.transaction.UserTransaction; import org.alfresco.repo.security.authentication.AuthenticationContext; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.activities.ActivityService; 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.SearchService; import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.FileFilterMode; @@ -83,6 +85,8 @@ public class WebDAVServlet extends HttpServlet // WebDAV helper class private WebDAVHelper m_davHelper; + private ActivityPoster activityPoster; + private TenantService tenantService; /** * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, @@ -212,15 +216,21 @@ public class WebDAVServlet extends HttpServlet Class methodClass = m_davMethods.get(strHttpMethod); WebDAVMethod method = null; - if ( methodClass != null) + if (methodClass != null) { try { - // Create the handler method - + // Create the handler method method = methodClass.newInstance(); NodeRef rootNodeRef = m_rootNodes.getNodeForCurrentTenant(); method.setDetails(request, response, m_davHelper, rootNodeRef); + + // A very few WebDAV methods produce activity posts. + if (method instanceof ActivityPostProducer) + { + ActivityPostProducer activityPostProducer = (ActivityPostProducer) method; + activityPostProducer.setActivityPoster(activityPoster); + } } catch (Exception ex) { @@ -275,16 +285,21 @@ public class WebDAVServlet extends HttpServlet m_serviceRegistry = (ServiceRegistry)context.getBean(ServiceRegistry.SERVICE_REGISTRY); m_transactionService = m_serviceRegistry.getTransactionService(); - TenantService tenantService = (TenantService) context.getBean("tenantService"); + tenantService = (TenantService) context.getBean("tenantService"); AuthenticationService authService = (AuthenticationService) context.getBean("authenticationService"); NodeService nodeService = (NodeService) context.getBean("NodeService"); SearchService searchService = (SearchService) context.getBean("SearchService"); NamespaceService namespaceService = (NamespaceService) context.getBean("NamespaceService"); LockStoreFactory lockStoreFactory = (LockStoreFactory) context.getBean("webdavLockStoreFactory"); LockStore lockStore = lockStoreFactory.getLockStore(); + ActivityService activityService = (ActivityService) context.getBean("activityService"); + PersonService personService = m_serviceRegistry.getPersonService(); + + // Collaborator used by WebDAV methods to create activity posts. + activityPoster = new ActivityPosterImpl(activityService, nodeService, personService); // Create the WebDAV helper - m_davHelper = new WebDAVHelper(m_serviceRegistry, lockStore, authService); + m_davHelper = new WebDAVHelper(m_serviceRegistry, lockStore, authService, tenantService); // Initialize the root node