package com.constellio.model.services.taxonomies;
import com.constellio.data.dao.managers.StatefulService;
import com.constellio.data.dao.managers.config.ConfigManager;
import com.constellio.data.dao.managers.config.DocumentAlteration;
import com.constellio.data.dao.managers.config.FileSystemConfigManager;
import com.constellio.model.entities.Taxonomy;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.wrappers.User;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchemaType;
import com.constellio.model.entities.schemas.MetadataSchemaTypes;
import com.constellio.model.entities.schemas.Schemas;
import com.constellio.model.services.batch.manager.BatchProcessesManager;
import com.constellio.model.services.collections.CollectionsListManager;
import com.constellio.model.services.records.cache.CacheConfig;
import com.constellio.model.services.records.cache.RecordsCaches;
import com.constellio.model.services.schemas.MetadataSchemasManager;
import com.constellio.model.services.schemas.SchemaUtils;
import com.constellio.model.services.search.SearchServices;
import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition;
import com.constellio.model.services.taxonomies.TaxonomiesManager.TaxonomiesManagerCache;
import com.constellio.model.services.taxonomies.TaxonomiesManagerRuntimeException.*;
import com.constellio.model.utils.OneXMLConfigPerCollectionManager;
import com.constellio.model.utils.OneXMLConfigPerCollectionManagerListener;
import com.constellio.model.utils.XMLConfigReader;
import org.jdom2.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from;
import static java.util.Arrays.asList;
public class TaxonomiesManager implements StatefulService, OneXMLConfigPerCollectionManagerListener<TaxonomiesManagerCache> {
public static final String TAXONOMIES_CONFIG = "/taxonomies.xml";
private static final Logger LOGGER = LoggerFactory.getLogger(FileSystemConfigManager.class);
private final RecordsCaches recordsCaches;
private final SearchServices searchServices;
private final ConfigManager configManager;
private final CollectionsListManager collectionsListManager;
private final BatchProcessesManager batchProcessesManager;
private OneXMLConfigPerCollectionManager<TaxonomiesManagerCache> oneXMLConfigPerCollectionManager;
public TaxonomiesManager(ConfigManager configManager, SearchServices searchServices,
BatchProcessesManager batchProcessesManager, CollectionsListManager collectionsListManager,
RecordsCaches recordsCaches) {
this.searchServices = searchServices;
this.configManager = configManager;
this.collectionsListManager = collectionsListManager;
this.batchProcessesManager = batchProcessesManager;
this.recordsCaches = recordsCaches;
}
@Override
public void initialize() {
this.oneXMLConfigPerCollectionManager = newOneXMLConfigPerCollectionManager();
}
public OneXMLConfigPerCollectionManager<TaxonomiesManagerCache> newOneXMLConfigPerCollectionManager() {
return new OneXMLConfigPerCollectionManager<>(configManager, collectionsListManager,
TAXONOMIES_CONFIG, xmlConfigReader(), this);
}
private XMLConfigReader<TaxonomiesManagerCache> xmlConfigReader() {
return new XMLConfigReader<TaxonomiesManagerCache>() {
@Override
public TaxonomiesManagerCache read(String collection, Document document) {
TaxonomiesReader reader = newTaxonomyReader(document);
List<Taxonomy> enableTaxonomies = Collections.unmodifiableList(reader.readEnables());
List<Taxonomy> disableTaxonomies = Collections.unmodifiableList(reader.readDisables());
String principalTaxonomyCode = reader.readPrincipalCode();
Taxonomy principalTaxonomy = null;
for (Taxonomy taxonomy : enableTaxonomies) {
if (taxonomy.getCode().equals(principalTaxonomyCode)) {
principalTaxonomy = taxonomy;
break;
}
}
return new TaxonomiesManagerCache(principalTaxonomy, enableTaxonomies, disableTaxonomies);
}
};
}
public void addTaxonomy(Taxonomy taxonomy, MetadataSchemasManager schemasManager) {
canCreateTaxonomy(taxonomy, schemasManager);
String collection = taxonomy.getCollection();
oneXMLConfigPerCollectionManager.updateXML(collection, newAddTaxonomyDocumentAlteration(taxonomy));
createCacheForTaxonomyTypes(taxonomy, schemasManager, collection);
}
void createCacheForTaxonomyTypes(Taxonomy taxonomy, MetadataSchemasManager schemasManager, String collection) {
for (String schemaType : taxonomy.getSchemaTypes()) {
MetadataSchemaType type = schemasManager.getSchemaTypes(collection).getSchemaType(schemaType);
recordsCaches.getCache(collection).configureCache(CacheConfig.permanentCache(type));
}
}
public void editTaxonomy(Taxonomy taxonomy) {
String collection = taxonomy.getCollection();
oneXMLConfigPerCollectionManager.updateXML(collection, newEditTaxonomyDocumentAlteration(taxonomy));
}
public void enable(Taxonomy taxonomy, MetadataSchemasManager schemasManager) {
verifyRecordsWithTaxonomiesSchemaTypes(taxonomy, schemasManager);
String collection = taxonomy.getCollection();
oneXMLConfigPerCollectionManager.updateXML(collection, newEnableTaxonomyDocumentAlteration(taxonomy.getCode()));
}
public void disable(Taxonomy taxonomy, MetadataSchemasManager schemasManager) {
verifyRecordsWithTaxonomiesSchemaTypes(taxonomy, schemasManager);
String collection = taxonomy.getCollection();
TaxonomiesManagerCache cache = oneXMLConfigPerCollectionManager.get(collection);
if (cache.principalTaxonomy != null && taxonomy.getCode().equals(cache.principalTaxonomy.getCode())) {
throw new PrincipalTaxonomyCannotBeDisabled();
}
oneXMLConfigPerCollectionManager.updateXML(collection, newDisableTaxonomyDocumentAlteration(taxonomy.getCode()));
}
public void setPrincipalTaxonomy(Taxonomy taxonomy, MetadataSchemasManager schemasManager) {
List<Metadata> metadatas = asList(Schemas.PRINCIPAL_PATH);
List<MetadataSchemaType> types = schemasManager.getSchemaTypes(taxonomy.getCollection())
.getSchemaTypesWithCode(taxonomy.getSchemaTypes());
validateCanBePrincipalTaxonomy(taxonomy, schemasManager);
String collection = taxonomy.getCollection();
oneXMLConfigPerCollectionManager.updateXML(collection, newSetPrincipalTaxonomy(taxonomy));
}
private void validateCanBePrincipalTaxonomy(Taxonomy taxonomy, MetadataSchemasManager schemasManager) {
String collection = taxonomy.getCollection();
TaxonomiesManagerCache cache = oneXMLConfigPerCollectionManager.get(collection);
if (cache.principalTaxonomy != null) {
throw new PrincipalTaxonomyIsAlreadyDefined();
}
if (getDisabledTaxonomies(taxonomy.getCollection()).contains(taxonomy)) {
throw new PrincipalTaxonomyCannotBeDisabled();
}
if (!getEnabledTaxonomies(taxonomy.getCollection()).contains(taxonomy)) {
throw new TaxonomyMustBeAddedBeforeSettingItHasPrincipal();
}
MetadataSchemaTypes types = schemasManager.getSchemaTypes(taxonomy.getCollection());
for (MetadataSchemaType type : types.getSchemaTypes()) {
for (Metadata metadata : type.getAllReferencesToTaxonomySchemas(asList(taxonomy))) {
if (metadata.isMultivalue()) {
throw new TaxonomySchemaIsReferencedInMultivalueReference();
}
}
}
}
public List<String> getSecondaryTaxonomySchemaTypes(String collection) {
List<String> secondaryTaxonomySchemaTypes = new ArrayList<>();
Taxonomy principalTaxonomy = getPrincipalTaxonomy(collection);
for (Taxonomy taxonomy : getEnabledTaxonomies(collection)) {
if (principalTaxonomy == null || !principalTaxonomy.getCode().equals(taxonomy.getCode())) {
secondaryTaxonomySchemaTypes.addAll(taxonomy.getSchemaTypes());
}
}
return secondaryTaxonomySchemaTypes;
}
public Taxonomy getPrincipalTaxonomy(String collection) {
return oneXMLConfigPerCollectionManager.get(collection).principalTaxonomy;
}
public List<Taxonomy> getEnabledTaxonomies(String collection) {
return oneXMLConfigPerCollectionManager.get(collection).enableTaxonomies;
}
public List<Taxonomy> getDisabledTaxonomies(String collection) {
return oneXMLConfigPerCollectionManager.get(collection).disableTaxonomies;
}
public Taxonomy getEnabledTaxonomyWithCode(String collection, String code) {
for (Taxonomy taxonomy : getEnabledTaxonomies(collection)) {
if (taxonomy.getCode().equals(code)) {
return taxonomy;
}
}
throw new TaxonomiesManagerRuntimeException_EnableTaxonomyNotFound(code, collection);
}
public Taxonomy getTaxonomyOf(Record record) {
return getTaxonomyFor(record.getCollection(), new SchemaUtils().getSchemaTypeCode(record.getSchemaCode()));
}
public Taxonomy getTaxonomyFor(String collection, String schemaTypeCode) {
List<Taxonomy> enableTaxonomies = oneXMLConfigPerCollectionManager.get(collection).enableTaxonomies;
if (enableTaxonomies != null) {
for (Taxonomy taxonomy : enableTaxonomies) {
if (taxonomy.getSchemaTypes().contains(schemaTypeCode)) {
return taxonomy;
}
}
}
return null;
}
TaxonomiesWriter newTaxonomyWriter(Document document) {
return new TaxonomiesWriter(document);
}
TaxonomiesReader newTaxonomyReader(Document document) {
return new TaxonomiesReader(document);
}
DocumentAlteration newAddTaxonomyDocumentAlteration(final Taxonomy taxonomy) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newTaxonomyWriter(document).addTaxonmy(taxonomy);
}
};
}
DocumentAlteration newEditTaxonomyDocumentAlteration(final Taxonomy taxonomy) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newTaxonomyWriter(document).editTaxonmy(taxonomy);
}
};
}
DocumentAlteration newDisableTaxonomyDocumentAlteration(final String taxonomyCode) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newTaxonomyWriter(document).disable(taxonomyCode);
}
};
}
DocumentAlteration newEnableTaxonomyDocumentAlteration(final String taxonomyCode) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newTaxonomyWriter(document).enable(taxonomyCode);
}
};
}
DocumentAlteration newSetPrincipalTaxonomy(final Taxonomy taxonomy) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newTaxonomyWriter(document).setPrincipalCode(taxonomy.getCode());
}
};
}
void canCreateTaxonomy(Taxonomy taxonomy, MetadataSchemasManager schemasManager) {
verifyIfExists(taxonomy);
verifyRecordsWithTaxonomiesSchemaTypes(taxonomy, schemasManager);
}
void verifyRecordsWithTaxonomiesSchemaTypes(Taxonomy taxonomy, MetadataSchemasManager schemasManager) {
List<String> taxonomiesTypes = new ArrayList<>();
taxonomiesTypes.addAll(taxonomy.getSchemaTypes());
MetadataSchemaTypes schemaTypes = schemasManager.getSchemaTypes(taxonomy.getCollection());
for (String taxonomieType : taxonomiesTypes) {
MetadataSchemaType schemaType = schemaTypes.getSchemaType(taxonomieType);
LogicalSearchCondition condition = from(schemaType).returnAll();
if (searchServices.hasResults(condition)) {
throw new TaxonomySchemaTypesHaveRecords(schemaType.getCode());
}
}
}
void verifyIfExists(Taxonomy taxonomy) {
List<Taxonomy> enableTaxonomies = oneXMLConfigPerCollectionManager.get(taxonomy.getCollection()).enableTaxonomies;
if (enableTaxonomies != null) {
for (Taxonomy enableTaxonomy : enableTaxonomies) {
if (enableTaxonomy.getCode().equals(taxonomy.getCode())) {
throw new TaxonomiesManagerRuntimeException.TaxonomyAlreadyExists(taxonomy.getCode());
}
verifyTaxonomiesSchemaTypes(taxonomy);
}
}
}
void verifyTaxonomiesSchemaTypes(Taxonomy taxonomy) {
for (String schemaType : taxonomy.getSchemaTypes()) {
if (getTaxonomyFor(taxonomy.getCollection(), schemaType) != null) {
throw new TaxonomiesManagerRuntimeException.TaxonomyAlreadyExists(taxonomy.getCode());
}
}
}
public void createCollectionTaxonomies(String collection) {
DocumentAlteration createConfigAlteration = new DocumentAlteration() {
@Override
public void alter(Document document) {
TaxonomiesWriter writer = newTaxonomyWriter(document);
writer.createEmptyTaxonomy();
}
};
oneXMLConfigPerCollectionManager.createCollectionFile(collection, createConfigAlteration);
}
@Override
public void onValueModified(String collection, TaxonomiesManagerCache newValue) {
}
public List<Taxonomy> getAvailableTaxonomiesInHomePage(User user) {
List<Taxonomy> taxonomies = new ArrayList<>();
for (Taxonomy taxonomy : getEnabledTaxonomies(user.getCollection())) {
if (taxonomy.isVisibleInHomePage()) {
boolean visibleByUser;
if (taxonomy.getUserIds().isEmpty() && taxonomy.getGroupIds().isEmpty()) {
visibleByUser = true;
} else {
boolean userInList = taxonomy.getUserIds().contains(user.getId());
boolean groupInList = false;
for (String aUserGroupId : user.getUserGroups()) {
groupInList |= taxonomy.getGroupIds().contains(aUserGroupId);
}
visibleByUser = userInList || groupInList;
}
if (visibleByUser) {
taxonomies.add(taxonomy);
}
}
}
return taxonomies;
}
public List<Taxonomy> getAvailableTaxonomiesForSchema(String schemaCode, User user,
MetadataSchemasManager metadataSchemasManager) {
Set<Taxonomy> taxonomies = new HashSet<>();
SchemaUtils schemaUtils = new SchemaUtils();
String schemaType = schemaUtils.getSchemaTypeCode(schemaCode);
List<Taxonomy> schemaTaxonomies = getAvailableTaxonomiesForSelectionOfType(schemaType, user, metadataSchemasManager);
taxonomies.addAll(schemaTaxonomies);
return new ArrayList<>(taxonomies);
}
public List<Taxonomy> getAvailableTaxonomiesForSelectionOfType(String schemaType, User user,
MetadataSchemasManager metadataSchemasManager) {
MetadataSchemaTypes types = metadataSchemasManager.getSchemaTypes(user.getCollection());
Taxonomy taxonomyWithType = getTaxonomyFor(user.getCollection(), schemaType);
if (taxonomyWithType != null) {
return asList(taxonomyWithType);
} else {
MetadataSchemaType type = types.getSchemaType(schemaType);
Set<Taxonomy> taxonomies = new HashSet<>();
for (Metadata metadatas : type.getAllMetadatas().onlyTaxonomyReferences()) {
String referenceTypeCode = metadatas.getAllowedReferences().getTypeWithAllowedSchemas();
Taxonomy taxonomy = getTaxonomyFor(user.getCollection(), referenceTypeCode);
if(hasCurrentUserRightsOnTaxonomy(taxonomy, user)) {
taxonomies.add(taxonomy);
}
}
for (Metadata metadatas : type.getAllMetadatas().onlyParentReferences()) {
String referenceTypeCode = metadatas.getAllowedReferences().getTypeWithAllowedSchemas();
if (!referenceTypeCode.equals(type.getCode())) {
taxonomies.addAll(getAvailableTaxonomiesForSelectionOfType(referenceTypeCode, user, metadataSchemasManager));
}
}
return new ArrayList<>(taxonomies);
}
}
private boolean hasCurrentUserRightsOnTaxonomy(Taxonomy taxonomy, User currentUser) {
String userid = currentUser.getId();
if(taxonomy != null) {
List<String> taxonomyGroupIds = taxonomy.getGroupIds();
List<String> taxonomyUserIds = taxonomy.getUserIds();
List<String> userGroups = currentUser.getUserGroups();
for(String group: taxonomyGroupIds) {
for(String userGroup: userGroups) {
if(userGroup.equals(group)) {
return true;
}
}
}
return (taxonomyGroupIds.isEmpty() && taxonomyUserIds.isEmpty()) || taxonomyUserIds.contains(userid);
} else {
return true;
}
}
public boolean isTypeInPrincipalTaxonomy(MetadataSchemaType type) {
return isTypeInPrincipalTaxonomy(type.getCollection(), type.getCode());
}
public boolean isTypeInPrincipalTaxonomy(String collection, String typeCode) {
Taxonomy typeTaxonomy = getTaxonomyFor(collection, typeCode);
if (typeTaxonomy == null) {
return false;
} else {
Taxonomy principalTaxonomy = getPrincipalTaxonomy(collection);
return principalTaxonomy != null && principalTaxonomy.getCode().equals(typeTaxonomy.getCode());
}
}
public static class TaxonomiesManagerCache {
final Taxonomy principalTaxonomy;
final List<Taxonomy> enableTaxonomies;
final List<Taxonomy> disableTaxonomies;
TaxonomiesManagerCache(Taxonomy principalTaxonomy, List<Taxonomy> enableTaxonomies, List<Taxonomy> disableTaxonomies) {
this.principalTaxonomy = principalTaxonomy;
this.enableTaxonomies = enableTaxonomies;
this.disableTaxonomies = disableTaxonomies;
}
}
@Override
public void close() {
}
}