package com.constellio.app.services.schemasDisplay; import static java.util.Arrays.asList; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.jdom2.Document; import org.jdom2.Element; import com.constellio.app.entities.schemasDisplay.MetadataDisplayConfig; import com.constellio.app.entities.schemasDisplay.SchemaDisplayConfig; import com.constellio.app.entities.schemasDisplay.SchemaTypeDisplayConfig; import com.constellio.app.entities.schemasDisplay.SchemaTypesDisplayConfig; import com.constellio.data.dao.managers.StatefulService; import com.constellio.data.dao.managers.config.ConfigManager; import com.constellio.data.dao.managers.config.ConfigManagerException.OptimisticLockingConfiguration; import com.constellio.data.dao.managers.config.DocumentAlteration; import com.constellio.data.dao.managers.config.values.XMLConfiguration; import com.constellio.data.utils.ImpossibleRuntimeException; import com.constellio.model.entities.Language; 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.frameworks.validation.ValidationErrors; import com.constellio.model.frameworks.validation.ValidationRuntimeException; import com.constellio.model.services.collections.CollectionsListManager; import com.constellio.model.services.schemas.MetadataSchemasManager; import com.constellio.model.services.schemas.MetadataSchemasManagerListener; import com.constellio.model.utils.OneXMLConfigPerCollectionManager; import com.constellio.model.utils.OneXMLConfigPerCollectionManagerListener; import com.constellio.model.utils.XMLConfigReader; public class SchemasDisplayManager implements OneXMLConfigPerCollectionManagerListener<SchemasDisplayManagerCache>, StatefulService { public static final String REQUIRED_METADATA_IN_FORM_LIST = "requiredMetadataInFormList"; private static final String SCHEMAS_DISPLAY_CONFIG = "/schemasDisplay.xml"; private ConfigManager configManager; private CollectionsListManager collectionsListManager; private OneXMLConfigPerCollectionManager<SchemasDisplayManagerCache> oneXMLConfigPerCollectionManager; private MetadataSchemasManager metadataSchemasManager; public SchemasDisplayManager(ConfigManager configManager, CollectionsListManager collectionsListManager, MetadataSchemasManager metadataSchemasManager) { this.configManager = configManager; this.collectionsListManager = collectionsListManager; this.metadataSchemasManager = metadataSchemasManager; } public Set<String> getReturnedFieldsForSearch(String collection) { return getCacheForCollection(collection).getReturnedFieldsForSearch(metadataSchemasManager); } public Set<String> getReturnedFieldsForTable(String collection) { return getCacheForCollection(collection).getReturnedFieldsForTable(metadataSchemasManager); } public void execute(final SchemaDisplayManagerTransaction transaction) { validate(transaction); String collection = transaction.getCollection(); if (collection != null) { oneXMLConfigPerCollectionManager.updateXML(collection, new DocumentAlteration() { @Override public void alter(Document document) { SchemasDisplayWriter writer = newSchemasDisplayWriter(document); if (transaction.getModifiedCollectionTypes() != null) { writer.saveTypes(transaction.getModifiedCollectionTypes()); } for (SchemaTypeDisplayConfig typeDisplayConfig : transaction.getModifiedTypes()) { writer.saveType(typeDisplayConfig); } for (SchemaDisplayConfig schemaDisplayConfig : transaction.getModifiedSchemas()) { writer.saveSchema(schemaDisplayConfig); } for (MetadataDisplayConfig metadataDisplayConfig : transaction.getModifiedMetadatas()) { writer.saveMetadata(metadataDisplayConfig); } } }); } } private void validate(SchemaDisplayManagerTransaction transaction) { ValidationErrors errors = new ValidationErrors(); for (SchemaDisplayConfig config : transaction.getModifiedSchemas()) { validate(errors, config); } if (!errors.getValidationErrors().isEmpty()) { throw new ValidationRuntimeException(errors); } } private void validate(ValidationErrors errors, SchemaDisplayConfig config) { MetadataSchema schema = metadataSchemasManager.getSchemaTypes(config.getCollection()).getSchema(config.getSchemaCode()); for (Metadata metadata : SchemaDisplayUtils.getRequiredMetadatasInSchemaForm(schema)) { if (!config.getFormMetadataCodes().contains(metadata.getCode())) { Map<String, Object> params = new HashMap<>(); params.put("code", metadata.getCode()); params.put("label", metadata.getLabelsByLanguageCodes()); errors.add(SchemasDisplayManager.class, REQUIRED_METADATA_IN_FORM_LIST, params); } } } public void saveTypes(final SchemaTypesDisplayConfig config) { SchemaDisplayManagerTransaction transaction = new SchemaDisplayManagerTransaction(); transaction.setModifiedCollectionTypes(config); execute(transaction); } public void saveType(final SchemaTypeDisplayConfig config) { SchemaDisplayManagerTransaction transaction = new SchemaDisplayManagerTransaction(); transaction.add(config); execute(transaction); } public void saveSchema(final SchemaDisplayConfig config) { SchemaDisplayManagerTransaction transaction = new SchemaDisplayManagerTransaction(); transaction.add(config); execute(transaction); } public void saveMetadata(final MetadataDisplayConfig config) { SchemaDisplayManagerTransaction transaction = new SchemaDisplayManagerTransaction(); transaction.add(config); execute(transaction); } public SchemasDisplayManagerCache getCacheForCollection(String collection) { return oneXMLConfigPerCollectionManager.get(collection); } public SchemaTypesDisplayConfig getTypes(String collection) { return getCacheForCollection(collection).getTypes(); } public SchemaTypeDisplayConfig getType(String collection, String typeCode) { if (typeCode.split("_").length != 1) { throw new RuntimeException("Invalid code : " + typeCode); } return getCacheForCollection(collection).getType(typeCode); } public SchemaDisplayConfig getSchema(String collection, String schemaCode) { if (schemaCode.split("_").length != 2) { throw new RuntimeException("Invalid code : " + schemaCode); } return getCacheForCollection(collection).getSchema(schemaCode, metadataSchemasManager); } public MetadataDisplayConfig getMetadata(String collection, String schemaCode, String metadataLocalCode) { return getMetadata(collection, schemaCode + "_" + metadataLocalCode); } public SchemaDisplayConfig getDisplayConfig(MetadataSchema schema) { return getSchema(schema.getCollection(), schema.getCode()); } public MetadataDisplayConfig getDisplayConfig(Metadata metadata) { return getMetadata(metadata.getCollection(), metadata.getCode()); } public MetadataDisplayConfig getMetadata(String collection, String metadataCode) { if (metadataCode.split("_").length != 3) { throw new RuntimeException("Invalid code : " + metadataCode); } return getCacheForCollection(collection).getMetadata(metadataCode, metadataSchemasManager); } public List<SchemaTypeDisplayConfig> getSimpleSearchSchemaTypeConfigs(String collection) { List<SchemaTypeDisplayConfig> simpleSearchSchemaTypes = new ArrayList<>(); for (MetadataSchemaType type : metadataSchemasManager.getSchemaTypes(collection).getSchemaTypes()) { SchemaTypeDisplayConfig typeDisplayConfig = getType(collection, type.getCode()); if (typeDisplayConfig.isSimpleSearch()) { simpleSearchSchemaTypes.add(typeDisplayConfig); } } return simpleSearchSchemaTypes; } public List<SchemaTypeDisplayConfig> getAdvancedSearchSchemaTypeConfigs(String collection) { List<SchemaTypeDisplayConfig> simpleSearchSchemaTypes = new ArrayList<>(); for (MetadataSchemaType type : metadataSchemasManager.getSchemaTypes(collection).getSchemaTypes()) { SchemaTypeDisplayConfig typeDisplayConfig = getType(collection, type.getCode()); if (typeDisplayConfig.isAdvancedSearch()) { simpleSearchSchemaTypes.add(typeDisplayConfig); } } return simpleSearchSchemaTypes; } public List<MetadataDisplayConfig> getAdvancedSearchMetadatas(String collection, String type) { List<MetadataDisplayConfig> metadataDisplayConfigs = new ArrayList<>(); for (Metadata metadata : metadataSchemasManager.getSchemaTypes(collection).getSchemaType(type).getAllMetadatas()) { MetadataDisplayConfig metadataDisplayConfig = getMetadata(collection, metadata.getCode()); if (metadataDisplayConfig.isVisibleInAdvancedSearch()) { metadataDisplayConfigs.add(metadataDisplayConfig); } } return metadataDisplayConfigs; } @Override public void initialize() { this.oneXMLConfigPerCollectionManager = new OneXMLConfigPerCollectionManager<>(configManager, collectionsListManager, SCHEMAS_DISPLAY_CONFIG, xmlConfigReader(), this, new DocumentAlteration() { @Override public void alter(Document document) { SchemasDisplayWriter writer = newSchemasDisplayWriter(document); writer.writeEmptyDocument(); } }); metadataSchemasManager.registerListener(new MetadataSchemasManagerListener() { @Override public void onCollectionSchemasModified(String collection) { oneXMLConfigPerCollectionManager.reload(collection); } }); } private XMLConfigReader<SchemasDisplayManagerCache> xmlConfigReader() { return new XMLConfigReader<SchemasDisplayManagerCache>() { @Override public SchemasDisplayManagerCache read(String collection, Document document) { Element rootElement = document.getRootElement(); String formatVersion = rootElement == null ? null : rootElement.getAttributeValue(SchemasDisplayWriter.FORMAT_ATTRIBUTE); MetadataSchemaTypes types = metadataSchemasManager.getSchemaTypes(collection); List<Language> languages = Language .withCodes(collectionsListManager.getCollectionLanguages(types.getCollection())); if (formatVersion == null) { SchemasDisplayReader1 reader = new SchemasDisplayReader1(document, types, languages); return reader.readSchemaTypesDisplay(collection); } else if (SchemasDisplayReader2.FORMAT_VERSION.equals(formatVersion)) { SchemasDisplayReader2 reader = new SchemasDisplayReader2(document, types, languages); return reader.readSchemaTypesDisplay(collection); } else { throw new ImpossibleRuntimeException("Invalid format version '" + formatVersion + "'"); } } }; } @Override public void close() { //Nothing to do } @Override public void onValueModified(String collection, SchemasDisplayManagerCache newValue) { } private SchemasDisplayWriter newSchemasDisplayWriter(Document document) { return new SchemasDisplayWriter(document); } public void enableAllMetadatasInAdvancedSearch(String collection, String schemaType) { SchemaDisplayManagerTransaction transaction = new SchemaDisplayManagerTransaction(); SchemaTypeDisplayConfig schemaTypeDisplayConfig = getType(collection, schemaType); if (!schemaTypeDisplayConfig.isAdvancedSearch()) { transaction.getModifiedTypes().add(schemaTypeDisplayConfig.withAdvancedSearchStatus(true)); } transaction.getModifiedTypes().add(getType(collection, schemaType).withAdvancedSearchStatus(true)); List<MetadataValueType> restrictedTypes = asList(MetadataValueType.CONTENT, MetadataValueType.STRUCTURE); for (Metadata metadata : metadataSchemasManager.getSchemaTypes(collection).getSchemaType(schemaType).getAllMetadatas()) { if ("id".equals(metadata.getLocalCode()) || (!metadata.getCode().toLowerCase().contains("entered") && !restrictedTypes .contains(metadata.getType()) && !metadata .isSystemReserved())) { transaction.getModifiedMetadatas().add( getMetadata(collection, metadata.getCode()).withVisibleInAdvancedSearchStatus(true)); } } execute(transaction); } public void enableMetadatasInAdvancedSearch(String collection, String schemaType, String... metadatas) { SchemaDisplayManagerTransaction transaction = new SchemaDisplayManagerTransaction(); SchemaTypeDisplayConfig schemaTypeDisplayConfig = getType(collection, schemaType); if (!schemaTypeDisplayConfig.isAdvancedSearch()) { transaction.getModifiedTypes().add(schemaTypeDisplayConfig.withAdvancedSearchStatus(true)); } List<String> metadatasToEnable = asList(metadatas); transaction.getModifiedTypes().add(getType(collection, schemaType).withAdvancedSearchStatus(true)); for (Metadata metadata : metadataSchemasManager.getSchemaTypes(collection).getSchemaType(schemaType).getAllMetadatas()) { if (metadatasToEnable.contains(metadata.getLocalCode()) || metadatasToEnable.contains(metadata.getCode())) { transaction.getModifiedMetadatas().add( getMetadata(collection, metadata.getCode()).withVisibleInAdvancedSearchStatus(true)); } } execute(transaction); } public SchemaTypesDisplayTransactionBuilder newTransactionBuilderFor(String collection) { MetadataSchemaTypes types = metadataSchemasManager.getSchemaTypes(collection); return new SchemaTypesDisplayTransactionBuilder(types, this); } public void resetSchema(String collection, final String code) { oneXMLConfigPerCollectionManager.updateXML(collection, new DocumentAlteration() { @Override public void alter(Document document) { SchemasDisplayWriter writer = newSchemasDisplayWriter(document); writer.resetSchema(code); } }); } public List<MetadataSchemaType> getAllowedSchemaTypesForSimpleSearch(String collection) { List<MetadataSchemaType> result = new ArrayList<>(); for (MetadataSchemaType type : metadataSchemasManager.getSchemaTypes(collection).getSchemaTypes()) { SchemaTypeDisplayConfig config = getType(collection, type.getCode()); if (config.isSimpleSearch()) { result.add(type); } } return result; } public List<String> getDefinedMetadatasIn(String collection) { List<String> definedMetadatas = new ArrayList<>(); XMLConfiguration initialConfig = configManager.getXML(oneXMLConfigPerCollectionManager.getConfigPath(collection)); Document document = initialConfig.getDocument(); Element element = document.getRootElement(); Element metadataDisplayConfigs = element.getChild("MetadataDisplayConfigs"); if (metadataDisplayConfigs != null) { for (Element metadataElement : metadataDisplayConfigs.getChildren()) { definedMetadatas.add(metadataElement.getName()); } } return definedMetadatas; } public List<String> rewriteInOrderAndGetCodes(final String collection) { XMLConfiguration initialConfig = configManager.getXML(oneXMLConfigPerCollectionManager.getConfigPath(collection)); Document document = initialConfig.getDocument(); Element rootElement = document.detachRootElement(); sort(rootElement); Document newDocument = new Document(rootElement); try { oneXMLConfigPerCollectionManager.update(collection, initialConfig.getHash(), newDocument); } catch (OptimisticLockingConfiguration optimisticLockingConfiguration) { throw new RuntimeException(optimisticLockingConfiguration); } return getCodesOfElements(newDocument); } public List<String> getCodesOfElements(Document newDocument) { List<String> codes = new ArrayList<>(); getCodesOfElements(newDocument.getRootElement(), codes); return codes; } private void getCodesOfElements(Element element, List<String> codes) { String value = element.getAttributeValue("code"); if (value == null) { value = element.getAttributeValue("SchemaCode"); } if (value == null) { value = element.getName(); } codes.add(value); if (!asList("SchemaTypesDisplayConfig", "DisplayMetadataCodes", "FormMetadataCodes", "SearchResultsMetadataCodes", "TableMetadataCodes") .contains(element.getName())) { for (Element child : element.getChildren()) { getCodesOfElements(child, codes); } } } private void sort(Element element) { if (!asList("SchemaTypesDisplayConfig", "DisplayMetadataCodes", "FormMetadataCodes", "SearchResultsMetadataCodes", "TableMetadataCodes").contains(element.getName())) { List<Element> children = new ArrayList<Element>(element.getChildren()); element.removeContent(); Comparator<Element> comparator = new Comparator<Element>() { public int compare(Element o1, Element o2) { String n1 = o1.getAttributeValue("code"); String n2 = o2.getAttributeValue("code"); if (n1 == null) { n1 = o1.getAttributeValue("SchemaCode"); } if (n2 == null) { n2 = o2.getAttributeValue("SchemaCode"); } if (n1 == null) { n1 = o1.getName(); } if (n2 == null) { n2 = o2.getName(); } return n1.compareTo(n2); } }; Collections.sort(children, comparator); for (Element child : children) { sort(child); } element.addContent(children); } } }