/*
* (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* <a href="mailto:grenard@nuxeo.com">Guillaume</a>
*/
package org.nuxeo.ecm.collections.core.listener;
import static org.nuxeo.ecm.core.api.CoreSession.ALLOW_VERSION_WRITE;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.collections.api.CollectionConstants;
import org.nuxeo.ecm.collections.api.CollectionManager;
import org.nuxeo.ecm.collections.core.adapter.CollectionMember;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.DocumentRef;
import org.nuxeo.ecm.core.api.event.DocumentEventTypes;
import org.nuxeo.ecm.core.event.Event;
import org.nuxeo.ecm.core.event.EventContext;
import org.nuxeo.ecm.core.event.EventListener;
import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
import org.nuxeo.ecm.core.query.sql.NXQL;
import org.nuxeo.runtime.api.Framework;
/**
* Event handler to duplicate the collection members of a duplicated collection. The handler is synchronous because it
* is important to capture the collection member ids of the duplicated collection at the exact moment of duplication. We
* don't want to duplicate a collection member that was indeed added to the duplicated collection after the duplication.
* The handler will then launch asynchronous tasks to duplicate the collection members.
*
* @since 5.9.3
*/
public class DuplicatedCollectionListener implements EventListener {
private static final Log log = LogFactory.getLog(DuplicatedCollectionListener.class);
@Override
public void handleEvent(Event event) {
EventContext ctx = event.getContext();
if (!(ctx instanceof DocumentEventContext)) {
return;
}
final String eventId = event.getName();
final DocumentEventContext docCxt = (DocumentEventContext) event.getContext();
DocumentModel doc = null;
if (eventId.equals(DocumentEventTypes.DOCUMENT_CREATED_BY_COPY)) {
doc = docCxt.getSourceDocument();
} else if (eventId.equals(DocumentEventTypes.DOCUMENT_CHECKEDIN)) {
DocumentRef checkedInVersionRef = (DocumentRef) ctx.getProperties().get("checkedInVersionRef");
doc = ctx.getCoreSession().getDocument(checkedInVersionRef);
if (!doc.isVersion()) {
return;
}
} else {
return;
}
final CollectionManager collectionManager = Framework.getLocalService(CollectionManager.class);
if (collectionManager.isCollection(doc)) {
if (eventId.equals(DocumentEventTypes.DOCUMENT_CREATED_BY_COPY)) {
log.trace(String.format("Collection %s copied", doc.getId()));
} else if (eventId.equals(DocumentEventTypes.DOCUMENT_CHECKEDIN)) {
log.trace(String.format("Collection %s checked in", doc.getId()));
}
collectionManager.processCopiedCollection(doc);
}
if (collectionManager.isCollected(doc)) {
processCopiedMember(doc, ctx.getCoreSession());
}
if (doc.isFolder()) {
// We just copied a folder, maybe among the descendants there are collections that have been copied too
// proceed to the deep collection copy
int offset = 0;
DocumentModelList deepCopiedCollections;
CoreSession session = ctx.getCoreSession();
do {
deepCopiedCollections = session.query(
"SELECT * FROM Document WHERE ecm:mixinType = 'Collection' AND ecm:path STARTSWITH "
+ NXQL.escapeString(doc.getPathAsString()) + " ORDER BY ecm:uuid",
null, CollectionAsynchrnonousQuery.MAX_RESULT, offset, false);
offset += deepCopiedCollections.size();
for (DocumentModel deepCopiedCollection : deepCopiedCollections) {
collectionManager.processCopiedCollection(deepCopiedCollection);
}
} while (deepCopiedCollections.size() >= CollectionAsynchrnonousQuery.MAX_RESULT);
// Maybe among the descendants there are collection members that have been copied too
// Let's make sure they don't belong to their original document's collections
offset = 0;
DocumentModelList deepCopiedMembers;
do {
// CollectionMember is a dynamically added facet. Using it in where clause does not scale.
// Better check existence of collectionMember:collectionIds property to detect copied members
deepCopiedMembers = session.query(
"SELECT * FROM Document WHERE " + CollectionConstants.DOCUMENT_COLLECTION_IDS_PROPERTY_NAME
+ "/* IS NOT NULL AND ecm:path STARTSWITH " + NXQL.escapeString(doc.getPathAsString())
+ " ORDER BY ecm:uuid",
null, CollectionAsynchrnonousQuery.MAX_RESULT, offset, false);
offset += deepCopiedMembers.size();
for (DocumentModel deepCopiedMember : deepCopiedMembers) {
processCopiedMember(deepCopiedMember, session);
}
} while (deepCopiedMembers.size() >= CollectionAsynchrnonousQuery.MAX_RESULT);
}
}
/**
* @since 8.4
*/
private void processCopiedMember(DocumentModel doc, CoreSession session) {
if (!Framework.getLocalService(CollectionManager.class).isCollected(doc)) {
// should never happen but we may have dirty members which have no longer the CollectionMember facet but
// sill collectionMember:collectionIds valued
doc.setPropertyValue(CollectionConstants.DOCUMENT_COLLECTION_IDS_PROPERTY_NAME, null);
} else {
doc.getAdapter(CollectionMember.class).setCollectionIds(null);
}
if (doc.isVersion()) {
doc.putContextData(ALLOW_VERSION_WRITE, Boolean.TRUE);
}
doc = session.saveDocument(doc);
doc.removeFacet(CollectionConstants.COLLECTABLE_FACET);
doc = session.saveDocument(doc);
}
}