package com.constellio.model.services.trash; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.records.wrappers.User; import com.constellio.model.entities.schemas.MetadataSchemaType; import com.constellio.model.entities.schemas.Schemas; import com.constellio.model.extensions.ModelLayerCollectionExtensions; import com.constellio.model.extensions.events.schemas.SchemaEvent; import com.constellio.model.services.factories.ModelLayerFactory; import com.constellio.model.services.records.RecordDeleteServicesRuntimeException.RecordServicesRuntimeException_CannotPhysicallyDeleteRecord_CannotSetNullOnRecords; import com.constellio.model.services.records.RecordPhysicalDeleteOptions; import com.constellio.model.services.records.RecordServices; import com.constellio.model.services.records.RecordServicesException; import com.constellio.model.services.records.RecordServicesRuntimeException; import com.constellio.model.services.records.RecordServicesRuntimeException.RecordServicesRuntimeException_CannotPhysicallyDeleteRecord; import com.constellio.model.services.search.SPEQueryResponse; import com.constellio.model.services.search.SearchServices; import com.constellio.model.services.search.query.logical.LogicalSearchQuery; import com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators; import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition; public class TrashServices { private static final Logger LOGGER = LoggerFactory.getLogger(TrashServices.class); private final ModelLayerFactory modelLayerFactory; private final String collection; private RecordServices recordServices; private final SearchServices searchServices; public TrashServices(ModelLayerFactory modelLayerFactory, String collection) { this.modelLayerFactory = modelLayerFactory; this.collection = collection; searchServices = modelLayerFactory.newSearchServices(); } public LogicalSearchQuery getTrashRecordsQueryForType(String selectedType, User currentUser) { MetadataSchemaType schema = modelLayerFactory .getMetadataSchemasManager().getSchemaTypes(collection).getSchemaType(selectedType); LogicalSearchCondition condition = from(schema).where(Schemas.LOGICALLY_DELETED_STATUS).isTrue(); return new LogicalSearchQuery(condition).filteredWithUserDelete(currentUser).sortDesc(Schemas.LOGICALLY_DELETED_ON); } public LogicalSearchQuery getTrashRecordsQueryForCollection(String collection, User currentUser) { List<MetadataSchemaType> trashSchemaList = getTrashSchemaTypes(collection); LogicalSearchCondition condition = from(trashSchemaList).where(Schemas.LOGICALLY_DELETED_STATUS).isTrue(); return new LogicalSearchQuery(condition).filteredWithUserDelete(currentUser).sortDesc(Schemas.LOGICALLY_DELETED_ON); } private List<MetadataSchemaType> getTrashSchemaTypes(String collection) { List<MetadataSchemaType> returnList = new ArrayList<>(); ModelLayerCollectionExtensions extension = modelLayerFactory.getExtensions() .forCollection(collection); List<MetadataSchemaType> allCollectionSchemaTypes = modelLayerFactory.getMetadataSchemasManager() .getSchemaTypes(collection).getSchemaTypes(); for (MetadataSchemaType schemaType : allCollectionSchemaTypes) { final String schemaTypeCode = schemaType.getCode(); SchemaEvent schemaEvent = new SchemaEvent() { @Override public String getSchemaCode() { return schemaTypeCode; } }; if (extension.isPutInTrashBeforePhysicalDelete(schemaEvent)) { returnList.add(schemaType); } } return returnList; } public List<String> restoreSelection(Set<String> selectedRecords, User currentUser) { List<String> returnList = new ArrayList<>(); for (String recordId : selectedRecords) { Record record = recordServices().getDocumentById(recordId); if (recordServices().isRestorable(record, currentUser)) { recordServices().restore(record, currentUser); } else { returnList.add(record.getTitle()); } } return returnList; } private RecordServices recordServices() { if (recordServices == null) { recordServices = modelLayerFactory.newRecordServices(); } return recordServices; } public Set<String> deleteSelection(Set<String> selectedRecords, User currentUser) { Set<String> returnSet = new HashSet<>(); for (String recordId : selectedRecords) { Record record; try { record = recordServices().getDocumentById(recordId); } catch (RecordServicesRuntimeException.NoSuchRecordWithId e) { // Already deleted, ignore continue; } try { boolean deleted = handleRecordPhysicalDelete(record, currentUser); if (!deleted) { returnSet.add(record.getTitle()); } } catch (Throwable e) { LOGGER.warn("record not deleted correctly from trash"); returnSet.add(recordId); } } return returnSet; } public Set<String> getTypesWithLogicallyDeletedRecords(String collection, User currentUser) { SearchServices searchServices = modelLayerFactory.newSearchServices(); LogicalSearchQuery query = getTrashRecordsQueryForCollection(collection, currentUser); query = query.addFieldFacet("schema_s").setNumberOfRows(0); SPEQueryResponse response = searchServices.query(query); List<String> schemasCodes = response.getFieldFacetValuesWithResults("schema_s"); Set<String> returnSet = new HashSet<>(); for (String schemaCode : schemasCodes) { String schemaType = StringUtils.substringBefore(schemaCode, "_"); returnSet.add(schemaType); } return returnSet; } public RecordsIdsAndTitles getRelatedRecords(String recordId, User user) { Record record = recordServices().getDocumentById(recordId, user); try { recordServices().physicallyDelete(record, user, new RecordPhysicalDeleteOptions().setMostReferencesToNull(true)); return new RecordsIdsAndTitles(); } catch (RecordServicesRuntimeException_CannotPhysicallyDeleteRecord_CannotSetNullOnRecords e) { return new RecordsIdsAndTitles(e.getRecordsIdsWithUnremovableReferences(), e.getRecordsTiltlesWithUnremovableReferences()); } } public LogicalSearchQuery getTrashRecordsQueryForCollectionDeletedBeforeDate(String collection, LocalDateTime deleteDate) { LogicalSearchCondition condition = LogicalSearchQueryOperators.from(getTrashSchemaTypes(collection)) .where(Schemas.LOGICALLY_DELETED_STATUS).isTrue() .andWhere(Schemas.LOGICALLY_DELETED_ON).isLessOrEqualThan(deleteDate); return new LogicalSearchQuery(condition).sortDesc(Schemas.LOGICALLY_DELETED_ON); } public boolean handleRecordPhysicalDelete(Record recordToDelete, User currentUser) { try { recordServices().physicallyDelete(recordToDelete, currentUser, new RecordPhysicalDeleteOptions().setMostReferencesToNull(true)); return true; } catch (RecordServicesRuntimeException_CannotPhysicallyDeleteRecord_CannotSetNullOnRecords e) { try { recordServices().add(recordToDelete.set(Schemas.ERROR_ON_PHYSICAL_DELETION, true)); } catch (RecordServicesException e2) { throw new RuntimeException(e2); } return false; } catch (RecordServicesRuntimeException_CannotPhysicallyDeleteRecord e) { e.printStackTrace(); return false; } } public long getLogicallyDeletedRecordsCount(String collection, User currentUser) { return searchServices.getResultsCount(getTrashRecordsQueryForCollection(collection, currentUser)); } public static class RecordsIdsAndTitles { final Set<String> recordsIds; final Set<String> recordsTitles; public RecordsIdsAndTitles(Set<String> recordsIds, Set<String> recordsTitles) { this.recordsIds = recordsIds; this.recordsTitles = recordsTitles; } public RecordsIdsAndTitles() { this.recordsTitles = new HashSet<>(); this.recordsIds = new HashSet<>(); } public Set<String> getRecordsIds() { return recordsIds; } public Set<String> getRecordsTitles() { return recordsTitles; } } }