package com.constellio.model.services.records; import static com.constellio.model.entities.security.global.AuthorizationDeleteRequest.authorizationDeleteRequest; import static com.constellio.model.services.records.RecordLogicalDeleteOptions.LogicallyDeleteTaxonomyRecordsBehavior.LOGICALLY_DELETE_THEM; import static com.constellio.model.services.records.RecordLogicalDeleteOptions.LogicallyDeleteTaxonomyRecordsBehavior.LOGICALLY_DELETE_THEM_ONLY_IF_PRINCIPAL_TAXONOMY; import static com.constellio.model.services.records.RecordPhysicalDeleteOptions.PhysicalDeleteTaxonomyRecordsBehavior.PHYSICALLY_DELETE_THEM; import static com.constellio.model.services.records.RecordPhysicalDeleteOptions.PhysicalDeleteTaxonomyRecordsBehavior.PHYSICALLY_DELETE_THEM_ONLY_IF_PRINCIPAL_TAXONOMY; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.fromAllSchemasIn; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.where; import static java.lang.Boolean.TRUE; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.solr.common.params.ModifiableSolrParams; import org.joda.time.LocalDateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.constellio.data.dao.dto.records.RecordDTO; import com.constellio.data.dao.dto.records.RecordsFlushing; import com.constellio.data.dao.dto.records.TransactionDTO; import com.constellio.data.dao.services.bigVault.RecordDaoException.OptimisticLocking; import com.constellio.data.dao.services.records.RecordDao; import com.constellio.data.utils.Factory; import com.constellio.data.utils.TimeProvider; import com.constellio.model.entities.Taxonomy; import com.constellio.model.entities.records.ActionExecutorInBatch; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.records.Transaction; import com.constellio.model.entities.records.wrappers.SolrAuthorizationDetails; import com.constellio.model.entities.records.wrappers.User; import com.constellio.model.entities.schemas.Metadata; import com.constellio.model.entities.schemas.MetadataSchema; import com.constellio.model.entities.schemas.MetadataSchemaType; import com.constellio.model.entities.schemas.MetadataSchemaTypes; import com.constellio.model.entities.schemas.MetadataValueType; import com.constellio.model.entities.schemas.Schemas; import com.constellio.model.extensions.events.records.RecordLogicalDeletionValidationEvent; import com.constellio.model.extensions.events.records.RecordPhysicalDeletionEvent; import com.constellio.model.extensions.events.records.RecordPhysicalDeletionValidationEvent; import com.constellio.model.services.contents.ContentManager; import com.constellio.model.services.contents.ContentModificationsBuilder; import com.constellio.model.services.extensions.ModelLayerExtensions; import com.constellio.model.services.factories.ModelLayerFactory; import com.constellio.model.services.records.RecordDeleteServicesRuntimeException.RecordDeleteServicesRuntimeException_CannotDeleteRecordWithUserFromOtherCollection; import com.constellio.model.services.records.RecordDeleteServicesRuntimeException.RecordDeleteServicesRuntimeException_CannotTotallyDeleteSchemaType; import com.constellio.model.services.records.RecordDeleteServicesRuntimeException.RecordDeleteServicesRuntimeException_RecordServicesErrorDuringOperation; import com.constellio.model.services.records.RecordDeleteServicesRuntimeException.RecordServicesRuntimeException_CannotPhysicallyDeleteRecord_CannotSetNullOnRecords; import com.constellio.model.services.records.RecordServicesException.ValidationException; import com.constellio.model.services.records.RecordServicesRuntimeException.RecordServicesRuntimeException_CannotLogicallyDeleteRecord; import com.constellio.model.services.records.RecordServicesRuntimeException.RecordServicesRuntimeException_CannotPhysicallyDeleteRecord; import com.constellio.model.services.records.RecordServicesRuntimeException.RecordServicesRuntimeException_CannotRestoreRecord; import com.constellio.model.services.records.preparation.RecordsToReindexResolver; import com.constellio.model.services.schemas.MetadataSchemaTypesAlteration; import com.constellio.model.services.schemas.MetadataSchemasManager; import com.constellio.model.services.schemas.SchemaUtils; import com.constellio.model.services.schemas.builders.MetadataBuilder; import com.constellio.model.services.schemas.builders.MetadataSchemaBuilder; import com.constellio.model.services.schemas.builders.MetadataSchemaTypeBuilder; import com.constellio.model.services.schemas.builders.MetadataSchemaTypesBuilder; import com.constellio.model.services.search.SearchServices; import com.constellio.model.services.search.StatusFilter; 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; import com.constellio.model.services.security.AuthorizationsServices; import com.constellio.model.services.taxonomies.TaxonomiesManager; public class RecordDeleteServices { private final Logger LOGGER = LoggerFactory.getLogger(RecordDeleteServices.class); private final RecordDao recordDao; private final SearchServices searchServices; private final RecordServices recordServices; private final AuthorizationsServices authorizationsServices; private final TaxonomiesManager taxonomiesManager; private final MetadataSchemasManager metadataSchemasManager; private final ContentManager contentManager; private final ModelLayerExtensions extensions; private final ModelLayerFactory modelLayerFactory; public RecordDeleteServices(RecordDao recordDao, ModelLayerFactory modelLayerFactory) { this.recordDao = recordDao; this.searchServices = modelLayerFactory.newSearchServices(); this.recordServices = modelLayerFactory.newRecordServices(); this.authorizationsServices = modelLayerFactory.newAuthorizationsServices(); this.taxonomiesManager = modelLayerFactory.getTaxonomiesManager(); this.metadataSchemasManager = modelLayerFactory.getMetadataSchemasManager(); this.contentManager = modelLayerFactory.getContentManager(); this.extensions = modelLayerFactory.getExtensions(); this.modelLayerFactory = modelLayerFactory; } public boolean isRestorable(Record record, User user) { ensureSameCollection(user, record); String parentId = record.getParentId(); boolean parentActiveOrNull; if (parentId != null) { Record parent = recordServices.getDocumentById(parentId); parentActiveOrNull = TRUE != parent.get(Schemas.LOGICALLY_DELETED_STATUS); } else { parentActiveOrNull = true; } String typeCode = new SchemaUtils().getSchemaTypeCode(record.getSchemaCode()); MetadataSchemaType schemaType = metadataSchemasManager.getSchemaTypes(record.getCollection()).getSchemaType(typeCode); return parentActiveOrNull && (!schemaType.hasSecurity() || user == User.GOD || authorizationsServices.hasRestaurationPermissionOnHierarchy(user, record)); } public void restore(Record record, User user) { if (!isRestorable(record, user)) { throw new RecordServicesRuntimeException_CannotRestoreRecord(record.getId()); } Transaction transaction = new Transaction(); transaction.getRecordUpdateOptions().setValidationsEnabled(false); transaction.getRecordUpdateOptions().setSkipMaskedMetadataValidations(true); transaction.getRecordUpdateOptions().setSkippingRequiredValuesValidation(true); transaction.getRecordUpdateOptions().setSkipUSRMetadatasRequirementValidations(true); transaction.getRecordUpdateOptions().setSkippingReferenceToLogicallyDeletedValidation(true); for (Record hierarchyRecord : getAllRecordsInHierarchy(record)) { hierarchyRecord.set(Schemas.LOGICALLY_DELETED_STATUS, false); hierarchyRecord.set(Schemas.LOGICALLY_DELETED_ON, null); transaction.add(hierarchyRecord); } if (!transaction.getRecords().contains(record)) { record.set(Schemas.LOGICALLY_DELETED_STATUS, false); record.set(Schemas.LOGICALLY_DELETED_ON, null); transaction.add(record); } try { recordServices.execute(transaction); } catch (RecordServicesException e) { throw new RecordDeleteServicesRuntimeException_RecordServicesErrorDuringOperation("restore", e); } } public boolean isLogicallyThenPhysicallyDeletable(Record record, User user) { return isLogicallyThenPhysicallyDeletable(record, user, new RecordPhysicalDeleteOptions()); } public boolean isLogicallyThenPhysicallyDeletable(Record record, User user, RecordPhysicalDeleteOptions options) { return isPhysicallyDeletableNoMatterTheStatus(record, user, options); } public boolean isPhysicallyDeletable(Record record, User user) { return isPhysicallyDeletable(record, user, new RecordPhysicalDeleteOptions()); } public boolean isPhysicallyDeletable(Record record, User user, RecordPhysicalDeleteOptions options) { ensureSameCollection(user, record); String typeCode = new SchemaUtils().getSchemaTypeCode(record.getSchemaCode()); MetadataSchemaType schemaType = metadataSchemasManager.getSchemaTypes(record.getCollection()).getSchemaType(typeCode); boolean correctStatus = TRUE == record.get(Schemas.LOGICALLY_DELETED_STATUS); boolean noActiveRecords = containsNoActiveRecords(record); boolean hasPermissions = !schemaType.hasSecurity() || authorizationsServices.hasRestaurationPermissionOnHierarchy(user, record); if (!correctStatus) { LOGGER.info("Not physically deletable : Record is not logically deleted"); return false; } else if (!noActiveRecords) { LOGGER.info("Not physically deletable : There is active records in the hierarchy"); return false; } else if (!hasPermissions) { LOGGER.info("Not physically deletable : No sufficient permissions on hierarchy"); return false; } else { return isPhysicallyDeletableNoMatterTheStatus(record, user, options); } } private boolean isPhysicallyDeletableNoMatterTheStatus(final Record record, User user, RecordPhysicalDeleteOptions options) { ensureSameCollection(user, record); String typeCode = new SchemaUtils().getSchemaTypeCode(record.getSchemaCode()); MetadataSchemaType schemaType = metadataSchemasManager.getSchemaTypes(record.getCollection()).getSchemaType(typeCode); boolean hasPermissions = !schemaType.hasSecurity() || authorizationsServices.hasDeletePermissionOnHierarchyNoMatterTheStatus(user, record); boolean referencesInConfigs = isReferencedByConfigs(record); boolean referencesUnhandled = isReferencedByOtherRecords(record) && !options.isSetMostReferencesToNull(); if (referencesInConfigs) { LOGGER.info("Not physically deletable : Record is used in configs"); } if (!hasPermissions) { LOGGER.info("Not physically deletable : No sufficient permissions on hierarchy"); } if (referencesUnhandled) { LOGGER.info("Not physically deletable : A record in the hierarchy is referenced outside of the hierarchy"); } boolean physicallyDeletable = hasPermissions && !referencesUnhandled && !referencesInConfigs; Factory<Boolean> referenced = new Factory<Boolean>() { @Override public Boolean get() { return !recordDao.getReferencedRecordsInHierarchy(record.getId()).isEmpty(); } }; if (physicallyDeletable) { RecordLogicalDeletionValidationEvent event = new RecordLogicalDeletionValidationEvent(record, user, referenced); physicallyDeletable = extensions.forCollectionOf(record).isLogicallyDeletable(event); } if (physicallyDeletable) { RecordPhysicalDeletionValidationEvent event = new RecordPhysicalDeletionValidationEvent(record, user); physicallyDeletable = extensions.forCollectionOf(record).isPhysicallyDeletable(event); } return physicallyDeletable; } public void physicallyDeleteNoMatterTheStatus(Record record, User user, RecordPhysicalDeleteOptions options) { if (TRUE.equals(record.get(Schemas.LOGICALLY_DELETED_STATUS))) { physicallyDelete(record, user, options); } else { logicallyDelete(record, user); recordServices.refresh(record); try { physicallyDelete(record, user, options); } catch (RecordServicesRuntimeException e) { recordServices.refresh(record); restore(record, user); throw e; } } } public void physicallyDelete(Record record, User user) { physicallyDelete(record, user, new RecordPhysicalDeleteOptions()); } public void physicallyDelete(final Record record, User user, RecordPhysicalDeleteOptions options) { final Set<String> recordsWithUnremovableReferences = new HashSet<>(); final Set<String> recordsIdsTitlesWithUnremovableReferences = new HashSet<>(); if (!isPhysicallyDeletable(record, user, options)) { throw new RecordServicesRuntimeException_CannotPhysicallyDeleteRecord(record.getId()); } List<Record> records = getAllRecordsInHierarchyForPhysicalDeletion(record, options); SchemasRecordsServices schemas = new SchemasRecordsServices(record.getCollection(), modelLayerFactory); if (schemas.getTypes().hasType(SolrAuthorizationDetails.SCHEMA_TYPE)) { for (Record recordInHierarchy : records) { for (SolrAuthorizationDetails details : schemas.searchSolrAuthorizationDetailss( where(schemas.authorizationDetails.target()).isEqualTo(recordInHierarchy.getId()))) { authorizationsServices.execute(authorizationDeleteRequest(details)); } } for (SolrAuthorizationDetails details : schemas.searchSolrAuthorizationDetailss( where(schemas.authorizationDetails.target()).isEqualTo(record.getId()))) { authorizationsServices.execute(authorizationDeleteRequest(details)); } } MetadataSchemaTypes types = metadataSchemasManager.getSchemaTypes(record.getCollection()); if (options.isSetMostReferencesToNull()) { //Collections.sort(records, sortByLevelFromLeafToRoot()); for (final Record recordInHierarchy : records) { String type = new SchemaUtils().getSchemaTypeCode(recordInHierarchy.getSchemaCode()); final List<Metadata> metadatas = types.getAllMetadatas().onlyReferencesToType(type).onlyNonParentReferences() .onlyManuals(); if (!metadatas.isEmpty()) { try { new ActionExecutorInBatch(searchServices, "Remove references to '" + recordInHierarchy.getId() + "'", 1000) { @Override public void doActionOnBatch(List<Record> recordsWithRef) throws Exception { Transaction transaction = new Transaction(); for (Record recordWithRef : recordsWithRef) { String recordWithRefType = new SchemaUtils().getSchemaTypeCode(recordWithRef.getSchemaCode()); for (Metadata metadata : metadatas) { String metadataType = new SchemaUtils().getSchemaTypeCode(metadata); if (recordWithRefType.equals(metadataType)) { if (metadata.isMultivalue()) { List<String> values = new ArrayList<>(recordWithRef.<String>getList(metadata)); int sizeBefore = values.size(); values.removeAll(Collections.singletonList(recordInHierarchy.getId())); if (sizeBefore != values.size()) { recordWithRef.set(metadata, values); } } else { String value = recordWithRef.get(metadata); if (recordInHierarchy.getId().equals(value)) { recordWithRef.set(metadata, null); } } } } try { recordServices.validateRecordInTransaction(recordWithRef, transaction); transaction.add(recordWithRef); } catch (ValidationException e) { e.printStackTrace(); recordsWithUnremovableReferences.add(recordWithRef.getId()); recordsIdsTitlesWithUnremovableReferences.add(recordWithRef.getTitle()); } } recordServices.execute(transaction); } }.execute(fromAllSchemasIn(record.getCollection()).whereAny(metadatas).isEqualTo(recordInHierarchy)); } catch (Exception e) { throw new RuntimeException(e); } } } } if (recordsWithUnremovableReferences.isEmpty()) { Set<String> ids = new HashSet<>(); for (Record aRecord : records) { ids.addAll(new RecordsToReindexResolver(types).findRecordsToReindexFromRecord(aRecord, true)); } deleteContents(records); List<RecordDTO> recordsDTO = newRecordUtils().toRecordDTOList(records); try { recordDao.execute( new TransactionDTO(RecordsFlushing.NOW).withDeletedRecords(recordsDTO)); } catch (OptimisticLocking optimisticLocking) { throw new RecordServicesRuntimeException_CannotPhysicallyDeleteRecord(record.getId(), optimisticLocking); } Transaction transaction = new Transaction(); transaction.add(recordServices.getDocumentById(record.getCollection())); transaction.addAllRecordsToReindex(ids); try { recordServices.execute(transaction); } catch (RecordServicesException e) { throw new RuntimeException(e); } for (Record hierarchyRecord : records) { RecordPhysicalDeletionEvent event = new RecordPhysicalDeletionEvent(hierarchyRecord); extensions.forCollectionOf(record).callRecordPhysicallyDeleted(event); } } else { throw new RecordServicesRuntimeException_CannotPhysicallyDeleteRecord_CannotSetNullOnRecords(record.getId(), recordsWithUnremovableReferences, recordsIdsTitlesWithUnremovableReferences); } } private List<Record> getAllRecordsInHierarchyForLogicalDeletion(Record record, RecordLogicalDeleteOptions options) { Taxonomy taxonomy = taxonomiesManager.getTaxonomyOf(record); Taxonomy principalTaxonomy = taxonomiesManager.getPrincipalTaxonomy(record.getCollection()); boolean includeRecords = false; if (taxonomy != null) { if (options.behaviorForRecordsAttachedToTaxonomy == LOGICALLY_DELETE_THEM) { includeRecords = true; } else if (taxonomy.hasSameCode(principalTaxonomy) && options.behaviorForRecordsAttachedToTaxonomy == LOGICALLY_DELETE_THEM_ONLY_IF_PRINCIPAL_TAXONOMY) { includeRecords = true; } } if (taxonomy != null && !includeRecords) { return getAllTaxonomyRecordsInHierarchy(record, taxonomy); } else { return getAllRecordsInHierarchy(record); } } private List<Record> getAllRecordsInHierarchyForPhysicalDeletion(Record record, RecordPhysicalDeleteOptions options) { Taxonomy taxonomy = taxonomiesManager.getTaxonomyOf(record); Taxonomy principalTaxonomy = taxonomiesManager.getPrincipalTaxonomy(record.getCollection()); boolean includeRecords = false; if (taxonomy != null) { if (options.behaviorForRecordsAttachedToTaxonomy == PHYSICALLY_DELETE_THEM) { includeRecords = true; } else if (taxonomy.hasSameCode(principalTaxonomy) && options.behaviorForRecordsAttachedToTaxonomy == PHYSICALLY_DELETE_THEM_ONLY_IF_PRINCIPAL_TAXONOMY) { includeRecords = true; } } if (taxonomy != null && !includeRecords) { return getAllTaxonomyRecordsInHierarchy(record, taxonomy); } else { return getAllRecordsInHierarchy(record); } } void deleteContents(List<Record> records) { String collection = records.get(0).getCollection(); for (String potentiallyDeletableHash : newContentModificationsBuilder(collection).buildForDeletedRecords(records)) { contentManager.silentlyMarkForDeletionIfNotReferenced(potentiallyDeletableHash); } } ContentModificationsBuilder newContentModificationsBuilder(String collection) { MetadataSchemaTypes types = metadataSchemasManager.getSchemaTypes(collection); return new ContentModificationsBuilder(types); } public boolean isLogicallyDeletable(final Record record, User user) { ensureSameCollection(user, record); String typeCode = new SchemaUtils().getSchemaTypeCode(record.getSchemaCode()); MetadataSchemaType schemaType = metadataSchemasManager.getSchemaTypes(record.getCollection()).getSchemaType(typeCode); boolean logicallyDeletable = !schemaType.hasSecurity() || authorizationsServices.hasDeletePermissionOnHierarchy(user, record); if (isReferencedByConfigs(record)) { logicallyDeletable = false; } if (logicallyDeletable) { Factory<Boolean> referenced = new Factory<Boolean>() { @Override public Boolean get() { return !recordDao.getReferencedRecordsInHierarchy(record.getId()).isEmpty(); } }; RecordLogicalDeletionValidationEvent event = new RecordLogicalDeletionValidationEvent(record, user, referenced); logicallyDeletable = extensions.forCollectionOf(record).isLogicallyDeletable(event); } return logicallyDeletable; } private void removedDefaultValues(String collection, List<Record> records) { List<String> defaultValuesIds = metadataSchemasManager.getSchemaTypes(collection).getReferenceDefaultValues(); final List<String> defaultValuesIdsToRemove = new ArrayList<>(); for (Record record : records) { if (defaultValuesIds.contains(record.getId())) { defaultValuesIdsToRemove.add(record.getId()); } } if (!defaultValuesIdsToRemove.isEmpty()) { metadataSchemasManager.modify(collection, new MetadataSchemaTypesAlteration() { @Override public void alter(MetadataSchemaTypesBuilder types) { for (MetadataSchemaTypeBuilder typeBuilder : types.getTypes()) { for (MetadataSchemaBuilder schemaBuilder : typeBuilder.getAllSchemas()) { for (MetadataBuilder metadataBuilder : schemaBuilder.getMetadatas()) { Object defaultValue = metadataBuilder.getDefaultValue(); if (metadataBuilder.getType() == MetadataValueType.REFERENCE && defaultValue != null) { if (defaultValue instanceof String && defaultValuesIdsToRemove.contains(defaultValue)) { metadataBuilder.setDefaultValue(null); } else if (defaultValue instanceof List) { List<String> withoutRemovedDefaultValues = null; for (Object item : (List) defaultValue) { if (defaultValuesIdsToRemove.contains(item)) { if (withoutRemovedDefaultValues == null) { withoutRemovedDefaultValues = new ArrayList<String>((List) defaultValue); } withoutRemovedDefaultValues.remove(item); } } if (withoutRemovedDefaultValues != null) { if (withoutRemovedDefaultValues.isEmpty()) { withoutRemovedDefaultValues = null; } metadataBuilder.setDefaultValue(withoutRemovedDefaultValues); } } } } } } } }); } } public void logicallyDelete(Record record, User user) { logicallyDelete(record, user, new RecordLogicalDeleteOptions()); } public void logicallyDelete(Record record, User user, RecordLogicalDeleteOptions options) { if (!isLogicallyDeletable(record, user)) { throw new RecordServicesRuntimeException_CannotLogicallyDeleteRecord(record.getId()); } Transaction transaction = new Transaction().setSkippingRequiredValuesValidation(true); List<Record> hierarchyRecords = new ArrayList<>(getAllRecordsInHierarchyForLogicalDeletion(record, options)); if (!new RecordUtils().toIdList(hierarchyRecords).contains(record.getId())) { hierarchyRecords.add(record); } removedDefaultValues(record.getCollection(), hierarchyRecords); LocalDateTime now = TimeProvider.getLocalDateTime(); for (Record hierarchyRecord : hierarchyRecords) { hierarchyRecord.set(Schemas.LOGICALLY_DELETED_STATUS, true); hierarchyRecord.set(Schemas.LOGICALLY_DELETED_ON, now); transaction.add(hierarchyRecord); } // if (!transaction.getRecords().contains(record)) { // record.set(Schemas.LOGICALLY_DELETED_STATUS, true); // transaction.add(record); // } transaction.setUser(user); try { recordServices.execute(transaction); } catch (RecordServicesException e) { throw new RecordDeleteServicesRuntimeException_RecordServicesErrorDuringOperation("logicallyDelete", e); } } public boolean isPrincipalConceptLogicallyDeletableIncludingContent(Record principalConcept, User user) { return authorizationsServices .hasDeletePermissionOnPrincipalConceptHierarchy(user, principalConcept, true, metadataSchemasManager); } public boolean isPrincipalConceptLogicallyDeletableExcludingContent(Record principalConcept, User user) { return authorizationsServices .hasDeletePermissionOnPrincipalConceptHierarchy(user, principalConcept, false, metadataSchemasManager); } List<Record> getAllRecordsInHierarchy(Record record) { if (record.getList(Schemas.PATH).isEmpty()) { return Arrays.asList(record); } else { LogicalSearchQuery query = new LogicalSearchQuery(); List<String> paths = record.getList(Schemas.PATH); query.setCondition(fromAllSchemasIn(record.getCollection()).where(Schemas.PATH).isStartingWithText(paths.get(0))); return searchServices.search(query); } } List<Record> getAllTaxonomyRecordsInHierarchy(Record record, Taxonomy taxonomy) { if (record.getList(Schemas.PATH).isEmpty()) { return Arrays.asList(record); } else { LogicalSearchQuery query = new LogicalSearchQuery(); List<String> paths = record.getList(Schemas.PATH); List<MetadataSchemaType> taxonomySchemaTypes = metadataSchemasManager.getSchemaTypes(record.getCollection()) .getSchemaTypesWithCode(taxonomy.getSchemaTypes()); query.setCondition(from(taxonomySchemaTypes).where(Schemas.PATH).isStartingWithText(paths.get(0))); return searchServices.search(query); } } List<Record> getAllPrincipalConceptsRecordsInHierarchy(Record principalConcept, Taxonomy principalTaxonomy) { List<Record> records = new ArrayList<>(); for (String schemaTypeCode : principalTaxonomy.getSchemaTypes()) { MetadataSchemaType schemaType = metadataSchemasManager.getSchemaTypes(principalConcept.getCollection()) .getSchemaType(schemaTypeCode); List<String> paths = principalConcept.getList(Schemas.PATH); LogicalSearchQuery query = new LogicalSearchQuery(); query.setCondition(from(schemaType).where(Schemas.PATH).isStartingWithText(paths.get(0))); records.addAll(searchServices.search(query)); } return records; } boolean containsNoActiveRecords(Record record) { Taxonomy taxonomy = taxonomiesManager.getTaxonomyOf(record); Taxonomy principalTaxonomy = taxonomiesManager.getPrincipalTaxonomy(record.getCollection()); LogicalSearchQuery query = new LogicalSearchQuery().filteredByStatus(StatusFilter.ACTIVES); if (taxonomy != null && !taxonomy.hasSameCode(principalTaxonomy)) { List<MetadataSchemaType> taxonomySchemaTypes = metadataSchemasManager.getSchemaTypes(record.getCollection()) .getSchemaTypesWithCode(taxonomy.getSchemaTypes()); query.setCondition(from(taxonomySchemaTypes).where(Schemas.PATH).isContainingText(record.getId())); } else { query.setCondition(fromAllSchemasIn(record.getCollection()).where(Schemas.PATH).isContainingText(record.getId())); } return !searchServices.hasResults(query); } public RecordUtils newRecordUtils() { return new RecordUtils(); } public List<Record> getVisibleRecordsWithReferenceToRecordInHierarchy(Record record, User user) { //1 - Find all hierarchy records (including the given record) that are referenced (using the counter index) List<Record> returnedRecords = new ArrayList<>(); List<String> recordsWithReferences = getRecordsInHierarchyWithDependency(record); if (!recordsWithReferences.isEmpty()) { for (MetadataSchemaType type : metadataSchemasManager.getSchemaTypes(record.getCollection()).getSchemaTypes()) { List<Metadata> references = type.getAllNonParentReferences(); if (!references.isEmpty()) { List<Record> recordsInType = getRecordsInTypeWithReferenceTo(user, recordsWithReferences, type, references); returnedRecords.addAll(recordsInType); } } } return Collections.unmodifiableList(returnedRecords); } List<Record> getRecordsInTypeWithReferenceTo(User user, List<String> recordsWithReferences, MetadataSchemaType type, List<Metadata> references) { LogicalSearchQuery query = new LogicalSearchQuery().filteredWithUser(user); query.setCondition(from(type).whereAny(references).isIn(recordsWithReferences)); return searchServices.search(query); } public List<String> getRecordsInHierarchyWithDependency(Record record) { return recordDao.getReferencedRecordsInHierarchy(record.getId()); } public boolean isReferencedByConfigs(Record record) { for (MetadataSchemaType schemaType : metadataSchemasManager.getSchemaTypes(record.getCollection()).getSchemaTypes()) { for (MetadataSchema schema : schemaType.getAllSchemas()) { for (Metadata metadata : schema.getMetadatas()) { if (metadata.getType() == MetadataValueType.REFERENCE && metadata.getDefaultValue() != null) { if (metadata.getDefaultValue() instanceof List) { if (((List) metadata.getDefaultValue()).contains(record.getId())) { return true; } } else if (metadata.getDefaultValue().equals(record.getId())) { return true; } } } } } return false; } public boolean isReferencedByOtherRecords(Record record) { List<String> references = recordDao.getReferencedRecordsInHierarchy(record.getId()); // List<Record> hierarchyRecords = recordServices.getRecordsById(record.getCollection(), references); // if (references.isEmpty()) { // return false; // } boolean hasReferences = false; List<String> paths = record.getList(Schemas.PARENT_PATH); // hasReferences = searchServices.hasResults(fromAllSchemasIn(record.getCollection()) // .where(Schemas.ALL_REFERENCES).isEqualTo(record.getId()) // .andWhere(Schemas.PATH_PARTS).isNotEqual(record.getId())); Taxonomy taxonomy = taxonomiesManager.getPrincipalTaxonomy(record.getCollection()); boolean isPrincipalTaxonomy = taxonomy != null && taxonomy.getSchemaTypes().contains(record.getTypeCode()); boolean isTaxonomy = taxonomiesManager.getTaxonomyOf(record) != null; if (isPrincipalTaxonomy || !isTaxonomy) { for (MetadataSchemaType type : metadataSchemasManager.getSchemaTypes(record.getCollection()).getSchemaTypes()) { List<Metadata> typeReferencesMetadata = type.getAllNonParentReferences(); if (!typeReferencesMetadata.isEmpty()) { LogicalSearchCondition condition = from(type).whereAny(typeReferencesMetadata).isIn(references); //if (!paths.isEmpty()) { condition = condition.andWhere(Schemas.PATH_PARTS).isNotEqual(record.getId()); //} boolean referencedByMetadatasOfType = searchServices.hasResults(new LogicalSearchQuery(condition)); hasReferences |= referencedByMetadatasOfType; } } } else { LogicalSearchCondition condition = fromAllSchemasIn(record.getCollection()) .where(Schemas.PATH_PARTS).isEqualTo(record.getId()) .andWhere(Schemas.SCHEMA).isNot(LogicalSearchQueryOperators.startingWithText(record.getTypeCode() + "_")); hasReferences = searchServices.hasResults(new LogicalSearchQuery(condition)); } return hasReferences; } private void ensureSameCollection(User user, Record record) { if (user != User.GOD && !user.getCollection().equals(record.getCollection())) { throw new RecordDeleteServicesRuntimeException_CannotDeleteRecordWithUserFromOtherCollection(record.getCollection(), user.getCollection()); } } public void totallyDeleteSchemaTypeRecords(MetadataSchemaType type) { if (type.isInTransactionLog()) { throw new RecordDeleteServicesRuntimeException_CannotTotallyDeleteSchemaType(type.getCode()); } totallyDeleteSchemaTypeRecordsSkippingValidation_WARNING_CANNOT_BE_REVERTED(type); } public void totallyDeleteSchemaTypeRecordsSkippingValidation_WARNING_CANNOT_BE_REVERTED(MetadataSchemaType type) { ModifiableSolrParams params = new ModifiableSolrParams(); params.set("q", "schema_s:" + type.getCode() + "_*"); try { recordDao.execute(new TransactionDTO(RecordsFlushing.NOW).withDeletedByQueries(params)); } catch (OptimisticLocking optimisticLocking) { throw new RuntimeException(optimisticLocking); } } }