001/* 002 * (C) Copyright 2018-2019 Nuxeo (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 * Funsho David 018 */ 019 020package org.nuxeo.ecm.core.bulk.action; 021 022import static org.nuxeo.ecm.core.api.event.CoreEventConstants.REPOSITORY_NAME; 023import static org.nuxeo.ecm.core.api.event.DocumentEventCategories.EVENT_DOCUMENT_CATEGORY; 024import static org.nuxeo.ecm.core.api.trash.TrashService.DOCUMENT_TRASHED; 025import static org.nuxeo.ecm.core.api.trash.TrashService.DOCUMENT_UNTRASHED; 026import static org.nuxeo.ecm.core.bulk.BulkServiceImpl.STATUS_STREAM; 027import static org.nuxeo.ecm.core.query.sql.NXQL.ECM_NAME; 028import static org.nuxeo.ecm.core.query.sql.NXQL.ECM_PARENTID; 029import static org.nuxeo.ecm.core.query.sql.NXQL.ECM_UUID; 030import static org.nuxeo.ecm.core.query.sql.NXQL.NXQL; 031import static org.nuxeo.lib.stream.computation.AbstractComputation.INPUT_1; 032import static org.nuxeo.lib.stream.computation.AbstractComputation.OUTPUT_1; 033 034import java.io.Serializable; 035import java.util.ArrayList; 036import java.util.Arrays; 037import java.util.Collection; 038import java.util.HashSet; 039import java.util.List; 040import java.util.Map; 041import java.util.Set; 042 043import org.apache.logging.log4j.LogManager; 044import org.apache.logging.log4j.Logger; 045import org.nuxeo.ecm.core.api.CoreSession; 046import org.nuxeo.ecm.core.api.DocumentModelList; 047import org.nuxeo.ecm.core.api.DocumentRef; 048import org.nuxeo.ecm.core.api.IdRef; 049import org.nuxeo.ecm.core.api.IterableQueryResult; 050import org.nuxeo.ecm.core.api.NuxeoException; 051import org.nuxeo.ecm.core.api.PropertyException; 052import org.nuxeo.ecm.core.api.trash.TrashService; 053import org.nuxeo.ecm.core.bulk.action.computation.AbstractBulkComputation; 054import org.nuxeo.ecm.core.event.Event; 055import org.nuxeo.ecm.core.event.EventService; 056import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 057import org.nuxeo.lib.stream.computation.Topology; 058import org.nuxeo.runtime.api.Framework; 059import org.nuxeo.runtime.stream.StreamProcessorTopology; 060 061/** 062 * @since 10.3 063 */ 064public class TrashAction implements StreamProcessorTopology { 065 066 public static final String ACTION_NAME = "trash"; 067 068 public static final String ACTION_FULL_NAME = "bulk/" + ACTION_NAME; 069 070 public static final String PARAM_NAME = "value"; 071 072 public static final String PROXY_QUERY_TEMPLATE = "SELECT ecm:uuid FROM Document WHERE ecm:isProxy=1 AND ecm:uuid IN ('%s')"; 073 074 public static final String SYSPROP_QUERY_TEMPLATE = "SELECT ecm:uuid, ecm:name, ecm:parentId FROM Document WHERE ecm:isProxy=0 AND ecm:isTrashed=%s AND ecm:uuid IN ('%s')"; 075 076 @Override 077 public Topology getTopology(Map<String, String> options) { 078 return Topology.builder() 079 .addComputation(TrashComputation::new, 080 Arrays.asList(INPUT_1 + ":" + ACTION_FULL_NAME, OUTPUT_1 + ":" + STATUS_STREAM)) 081 .build(); 082 } 083 084 public static class TrashComputation extends AbstractBulkComputation { 085 086 private static final Logger log = LogManager.getLogger(TrashComputation.class); 087 088 public TrashComputation() { 089 super(ACTION_FULL_NAME); 090 } 091 092 @Override 093 protected void compute(CoreSession session, List<String> ids, Map<String, Serializable> properties) { 094 Boolean trashValue = (Boolean) properties.get(PARAM_NAME); 095 if (trashValue) { 096 removeProxies(session, ids); 097 } 098 setSystemProperty(session, ids, trashValue); 099 } 100 101 protected void removeProxies(CoreSession session, List<String> ids) { 102 Set<DocumentRef> proxies = new HashSet<>(); 103 String query = String.format(PROXY_QUERY_TEMPLATE, String.join("', '", ids)); 104 try (IterableQueryResult res = session.queryAndFetch(query, NXQL)) { 105 for (Map<String, Serializable> map : res) { 106 proxies.add(new IdRef((String) map.get(ECM_UUID))); 107 } 108 } 109 session.removeDocuments(proxies.toArray(new DocumentRef[0])); 110 try { 111 session.save(); 112 } catch (PropertyException e) { 113 // TODO send to error stream 114 log.warn("Cannot save session", e); 115 } 116 } 117 118 public void setSystemProperty(CoreSession session, List<String> ids, Boolean value) { 119 List<DocumentRef> updatedRefs = new ArrayList<>(ids.size()); 120 String query = String.format(SYSPROP_QUERY_TEMPLATE, value ? "0" : "1", String.join("', '", ids)); 121 try (IterableQueryResult res = session.queryAndFetch(query, NXQL)) { 122 TrashService trashService = Framework.getService(TrashService.class); 123 for (Map<String, Serializable> map : res) { 124 DocumentRef ref = new IdRef((String) map.get(ECM_UUID)); 125 try { 126 session.setDocumentSystemProp(ref, "isTrashed", value); 127 String docName = (String) map.get(ECM_NAME); 128 if (!value && trashService.isMangledName(docName)) { 129 DocumentRef parentRef = new IdRef((String) map.get(ECM_PARENTID)); 130 session.move(ref, parentRef, 131 trashService.unmangleName(session, parentRef, docName)); 132 } 133 updatedRefs.add(ref); 134 } catch (NuxeoException e) { 135 // TODO send to error stream 136 log.warn("Cannot set system property: isTrashed on: " + ref.toString(), e); 137 } 138 } 139 } 140 try { 141 session.save(); 142 if (!updatedRefs.isEmpty()) { 143 fireEvent(session, value ? DOCUMENT_TRASHED : DOCUMENT_UNTRASHED, updatedRefs); 144 } 145 } catch (PropertyException e) { 146 // TODO send to error stream 147 log.warn("Cannot save session", e); 148 } 149 } 150 151 protected void fireEvent(CoreSession session, String eventId, Collection<DocumentRef> refs) { 152 EventService eventService = Framework.getService(EventService.class); 153 DocumentModelList docs = session.getDocuments(refs.toArray(new DocumentRef[0])); 154 docs.forEach(d -> { 155 DocumentEventContext ctx = new DocumentEventContext(session, session.getPrincipal(), d); 156 ctx.setProperty(REPOSITORY_NAME, session.getRepositoryName()); 157 ctx.setCategory(EVENT_DOCUMENT_CATEGORY); 158 Event event = ctx.newEvent(eventId); 159 event.setImmediate(false); 160 event.setInline(false); 161 eventService.fireEvent(event); 162 }); 163 } 164 } 165}