/*
* (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* This library 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.
*
* Contributors:
* Thomas Roger <troger@nuxeo.com>
*/
package org.easysoa.registry.wall;
import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.*;
import static org.nuxeo.ecm.core.schema.FacetNames.HIDDEN_IN_NAVIGATION;
import static org.nuxeo.ecm.core.schema.FacetNames.SUPER_SPACE;
import static org.nuxeo.ecm.core.schema.FacetNames.SYSTEM_DOCUMENT;
import static org.nuxeo.ecm.platform.comment.api.CommentEvents.COMMENT_ADDED;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.activity.Activity;
import org.nuxeo.ecm.activity.ActivityBuilder;
import org.nuxeo.ecm.activity.ActivityHelper;
import org.nuxeo.ecm.activity.ActivityStreamService;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentRef;
import org.nuxeo.ecm.core.api.IdRef;
import org.nuxeo.ecm.core.api.SystemPrincipal;
import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner;
import org.nuxeo.ecm.core.event.Event;
import org.nuxeo.ecm.core.event.EventBundle;
import org.nuxeo.ecm.core.event.EventContext;
import org.nuxeo.ecm.core.event.PostCommitEventListener;
import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
import org.nuxeo.ecm.core.event.impl.ShallowDocumentModel;
import org.nuxeo.ecm.platform.publisher.api.PublishingEvent;
import org.nuxeo.runtime.api.Framework;
/**
* XXX TO BE REMOVED WHEN NXP-9077 BRANCH IS MERGED.
*/
public class WallStreamListener implements PostCommitEventListener {
private static final Log log = LogFactory.getLog(WallStreamListener.class);
public static final String WORKFLOW_STARTED_VERB = "workflowStarted";
@Override
public void handleEvent(EventBundle events) throws ClientException {
if (shouldHandle(events)) {
List<Event> filteredEvents = filterDuplicateEvents(events);
for (Event event : filteredEvents) {
handleEvent(event);
}
}
}
private boolean shouldHandle(EventBundle events) {
return events.containsEventName(DOCUMENT_CREATED)
|| events.containsEventName(DOCUMENT_UPDATED)
|| events.containsEventName(DOCUMENT_REMOVED)
|| events.containsEventName(COMMENT_ADDED)
|| events.containsEventName(DOCUMENT_RESTORED)
|| events.containsEventName(DOCUMENT_CHECKEDOUT)
|| events.containsEventName(PublishingEvent.documentPublished.name());
}
private List<Event> filterDuplicateEvents(EventBundle events) {
List<Event> filteredEvents = new ArrayList<Event>();
for (Event event : events) {
filteredEvents = removeEventIfExist(filteredEvents, event);
filteredEvents.add(event);
}
return filteredEvents;
}
private List<Event> removeEventIfExist(List<Event> events, Event event) {
EventContext eventContext = event.getContext();
if (eventContext instanceof DocumentEventContext) {
DocumentModel doc = ((DocumentEventContext) eventContext).getSourceDocument();
for (Iterator<Event> it = events.iterator(); it.hasNext();) {
Event filteredEvent = it.next();
EventContext filteredEventContext = filteredEvent.getContext();
if (filteredEventContext instanceof DocumentEventContext) {
DocumentModel filteredEventDoc = ((DocumentEventContext) filteredEventContext).getSourceDocument();
if (event.getName().equals(filteredEvent.getName())
&& doc.getRef().equals(filteredEventDoc.getRef())) {
it.remove();
break;
}
}
}
}
return events;
}
private void handleEvent(Event event) throws ClientException {
EventContext eventContext = event.getContext();
if (isSystemPrincipal(eventContext)) {
// do not log activity for system principal
return;
}
if (!(eventContext instanceof DocumentEventContext)) {
return;
}
DocumentEventContext docEventContext = (DocumentEventContext) eventContext;
CoreSession session = docEventContext.getCoreSession();
Principal principal = docEventContext.getPrincipal();
DocumentModel sourceDoc = docEventContext.getSourceDocument();
String eventName = event.getName();
if (isDocumentEvent(eventName) || isCommentEvent(eventName)) {
addDocumentActivities(session, sourceDoc, principal, eventName);
} else if (isRestoreEvent(eventName)) {
addRestoreActivities(session, sourceDoc, principal, eventName);
} else if (isPublishEvent(eventName)) {
addPublishActivities(session, sourceDoc, principal, eventName);
}
}
private boolean isSystemPrincipal(EventContext ctx) {
return ctx.getPrincipal() instanceof SystemPrincipal;
}
private boolean isDocumentEvent(String eventName) {
return DOCUMENT_CREATED.equals(eventName)
|| DOCUMENT_UPDATED.equals(eventName)
|| DOCUMENT_REMOVED.equals(eventName)
|| DOCUMENT_CHECKEDOUT.equals(eventName);
}
private boolean isCommentEvent(String eventName) {
return COMMENT_ADDED.equals(eventName);
}
private boolean isRestoreEvent(String eventName) {
return DOCUMENT_RESTORED.equals(eventName);
}
private boolean isPublishEvent(String eventName) {
return PublishingEvent.documentPublished.name().equals(eventName);
}
private void addDocumentActivities(CoreSession session, DocumentModel doc,
Principal principal, String eventName) throws ClientException {
if (doc instanceof ShallowDocumentModel
|| doc.hasFacet(HIDDEN_IN_NAVIGATION)
|| doc.hasFacet(SYSTEM_DOCUMENT) || doc.isProxy()
|| doc.isVersion()) {
// Not really interested in non live document or if document
// cannot be reconnected
// or if not visible
return;
}
Activity activity = newDocumentActivity(session, doc, principal,
eventName);
addActivitiesWithContext(activity, session, doc);
}
private Activity newDocumentActivity(CoreSession session,
DocumentModel doc, Principal principal, String eventName) {
return new ActivityBuilder().actor(
ActivityHelper.createUserActivityObject(principal)).displayActor(
ActivityHelper.generateDisplayName(principal)).verb(eventName).object(
ActivityHelper.createDocumentActivityObject(doc)).displayObject(
ActivityHelper.getDocumentTitle(doc)).target(
ActivityHelper.createDocumentActivityObject(
doc.getRepositoryName(), doc.getParentRef().toString())).displayTarget(
getDocumentTitle(session, doc.getParentRef())).build();
}
private void addActivitiesWithContext(Activity baseActivity,
CoreSession session, DocumentModel doc) throws ClientException {
List<Activity> activities = computeActivitiesWithContext(baseActivity,
session, doc);
activities.add(0, baseActivity);
ActivityStreamService activityStreamService = Framework.getLocalService(ActivityStreamService.class);
for (Activity activity : activities) {
activityStreamService.addActivity(activity);
}
}
private List<Activity> computeActivitiesWithContext(Activity activity,
CoreSession session, DocumentModel doc) throws ClientException {
List<Activity> activities = new ArrayList<Activity>();
for (DocumentRef ref : getParentSuperSpaceRefs(session, doc)) {
String context = ActivityHelper.createDocumentActivityObject(
session.getRepositoryName(), ref.toString());
activities.add(new ActivityBuilder(activity).context(context).build());
}
return activities;
}
private void addRestoreActivities(CoreSession session, DocumentModel doc,
Principal principal, String eventName) throws ClientException {
if (doc instanceof ShallowDocumentModel
|| doc.hasFacet(HIDDEN_IN_NAVIGATION)
|| doc.hasFacet(SYSTEM_DOCUMENT) || doc.isProxy()
|| doc.isVersion()) {
// Not really interested in non live document or if document
// cannot be reconnected
// or if not visible
return;
}
Activity activity = newRestoreActivity(doc, principal, eventName);
addActivitiesWithContext(activity, session, doc);
}
private Activity newRestoreActivity(DocumentModel doc, Principal principal,
String eventName) {
return new ActivityBuilder().actor(
ActivityHelper.createUserActivityObject(principal)).displayActor(
ActivityHelper.generateDisplayName(principal)).verb(eventName).object(
doc.getVersionLabel()).target(
ActivityHelper.createDocumentActivityObject(doc)).displayTarget(
ActivityHelper.getDocumentTitle(doc)).build();
}
private void addPublishActivities(CoreSession session, DocumentModel doc,
Principal principal, String eventName) throws ClientException {
if (doc.isProxy()) {
DocumentModel version = session.getSourceDocument(doc.getRef());
DocumentModel liveDoc = session.getSourceDocument(version.getRef());
Activity activity = newPublishActivity(session, doc, liveDoc,
principal, eventName);
addActivitiesWithContext(activity, session, doc);
}
}
private Activity newPublishActivity(CoreSession session,
DocumentModel proxy, DocumentModel liveDoc, Principal principal,
String eventName) {
return new ActivityBuilder().actor(
ActivityHelper.createUserActivityObject(principal)).displayActor(
ActivityHelper.generateDisplayName(principal)).verb(eventName).object(
ActivityHelper.createDocumentActivityObject(liveDoc)).displayObject(
ActivityHelper.getDocumentTitle(liveDoc)).target(
ActivityHelper.createDocumentActivityObject(
proxy.getRepositoryName(),
proxy.getParentRef().toString())).displayTarget(
getDocumentTitle(session, proxy.getParentRef())).build();
}
private String getDocumentTitle(CoreSession session, DocumentRef docRef) {
try {
DocumentModel doc = session.getDocument(docRef);
return ActivityHelper.getDocumentTitle(doc);
} catch (ClientException e) {
return docRef.toString();
}
}
private List<DocumentRef> getParentSuperSpaceRefs(CoreSession session,
final DocumentModel doc) throws ClientException {
final List<DocumentRef> parents = new ArrayList<DocumentRef>();
new UnrestrictedSessionRunner(session) {
@Override
public void run() throws ClientException {
List<DocumentModel> parentDocuments = session.getParentDocuments(doc.getRef());
for (DocumentModel parent : parentDocuments) {
if (parent.hasFacet(SUPER_SPACE)) {
parents.add(parent.getRef());
}
}
}
}.runUnrestricted();
return parents;
}
}