package com.constellio.app.ui.pages.search; import com.constellio.app.entities.schemasDisplay.MetadataDisplayConfig; import com.constellio.app.entities.schemasDisplay.enums.MetadataInputType; import com.constellio.app.extensions.AppLayerCollectionExtensions; import com.constellio.app.modules.rm.constants.RMPermissionsTo; import com.constellio.app.modules.rm.model.labelTemplate.LabelTemplate; import com.constellio.app.modules.rm.model.labelTemplate.LabelTemplateManager; import com.constellio.app.modules.rm.reports.builders.search.SearchResultReportParameters; import com.constellio.app.modules.rm.reports.builders.search.SearchResultReportWriterFactory; import com.constellio.app.modules.rm.services.RMSchemasRecordsServices; import com.constellio.app.modules.rm.wrappers.*; import com.constellio.app.ui.entities.MetadataSchemaVO; import com.constellio.app.ui.entities.MetadataVO; import com.constellio.app.ui.entities.RecordVO; import com.constellio.app.ui.framework.builders.MetadataSchemaToVOBuilder; import com.constellio.app.ui.framework.builders.MetadataToVOBuilder; import com.constellio.app.ui.framework.builders.RecordToVOBuilder; import com.constellio.app.ui.framework.components.RecordFieldFactory; import com.constellio.app.ui.framework.data.RecordVODataProvider; import com.constellio.app.ui.framework.reports.NewReportWriterFactory; import com.constellio.app.ui.pages.base.SessionContext; import com.constellio.app.ui.pages.search.batchProcessing.BatchProcessingPresenter; import com.constellio.app.ui.pages.search.batchProcessing.BatchProcessingPresenterService; import com.constellio.app.ui.pages.search.batchProcessing.entities.BatchProcessResults; import com.constellio.app.ui.pages.search.criteria.ConditionBuilder; import com.constellio.app.ui.pages.search.criteria.ConditionException; import com.constellio.app.ui.pages.search.criteria.ConditionException.ConditionException_EmptyCondition; import com.constellio.app.ui.pages.search.criteria.ConditionException.ConditionException_TooManyClosedParentheses; import com.constellio.app.ui.pages.search.criteria.ConditionException.ConditionException_UnclosedParentheses; import com.constellio.model.entities.Language; import com.constellio.model.entities.batchprocess.BatchProcess; import com.constellio.model.entities.batchprocess.BatchProcessAction; import com.constellio.model.entities.enums.BatchProcessingMode; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.records.Transaction; import com.constellio.model.entities.records.wrappers.SavedSearch; 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.MetadataValueType; import com.constellio.model.entities.schemas.Schemas; import com.constellio.model.entities.schemas.entries.DataEntryType; import com.constellio.model.services.batch.actions.ChangeValueOfMetadataBatchProcessAction; import com.constellio.model.services.batch.manager.BatchProcessesManager; import com.constellio.model.services.factories.ModelLayerFactory; import com.constellio.model.services.migrations.ConstellioEIMConfigs; import com.constellio.model.services.records.RecordImpl; import com.constellio.model.services.records.RecordServicesException; import com.constellio.model.services.reports.ReportServices; import com.constellio.model.services.search.query.logical.LogicalSearchQuery; import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; import java.util.*; import static com.constellio.app.ui.i18n.i18n.$; import static com.constellio.model.entities.schemas.Schemas.IDENTIFIER; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.fromAllSchemasIn; public class AdvancedSearchPresenter extends SearchPresenter<AdvancedSearchView> implements BatchProcessingPresenter { private static final Logger LOGGER = LoggerFactory.getLogger(AdvancedSearchPresenter.class); String searchExpression; String schemaTypeCode; private int pageNumber; private String searchID; private boolean batchProcessOnAllSearchResults; private transient LogicalSearchCondition condition; private transient BatchProcessingPresenterService batchProcessingPresenterService; public AdvancedSearchPresenter(AdvancedSearchView view) { super(view); } @Override protected boolean hasPageAccess(String params, User user) { return true; } @Override public AdvancedSearchPresenter forRequestParameters(String params) { if (StringUtils.isNotBlank(params)) { String[] parts = params.split("/", 2); searchID = parts[1]; SavedSearch search = getSavedSearch(searchID); setSavedSearch(search); } else { searchExpression = StringUtils.stripToNull(view.getSearchExpression()); resetFacetSelection(); schemaTypeCode = view.getSchemaType(); pageNumber = 1; resultsViewMode = SearchResultsViewMode.DETAILED; saveTemporarySearch(true); } return this; } private void setSavedSearch(SavedSearch search) { searchExpression = search.getFreeTextSearch(); facetSelections.putAll(search.getSelectedFacets()); sortCriterion = search.getSortField(); sortOrder = SortOrder.valueOf(search.getSortOrder().name()); schemaTypeCode = search.getSchemaFilter(); pageNumber = search.getPageNumber(); resultsViewMode = search.getResultsViewMode() != null ? search.getResultsViewMode() : SearchResultsViewMode.DETAILED; setSelectedPageLength(search.getPageLength()); view.setSchemaType(schemaTypeCode); view.setSearchExpression(searchExpression); view.setSearchCriteria(search.getAdvancedSearch()); } @Override public int getPageNumber() { return pageNumber; } @Override public void setPageNumber(int pageNumber) { this.pageNumber = pageNumber; } @Override public boolean mustDisplayResults() { if (StringUtils.isBlank(schemaTypeCode)) { return false; } try { buildSearchCondition(); return true; } catch (ConditionException_EmptyCondition e) { view.showErrorMessage($("AdvancedSearchView.emptyCondition")); } catch (ConditionException_TooManyClosedParentheses e) { view.showErrorMessage($("AdvancedSearchView.tooManyClosedParentheses")); } catch (ConditionException_UnclosedParentheses e) { view.showErrorMessage($("AdvancedSearchView.unclosedParentheses")); } catch (ConditionException e) { throw new RuntimeException("BUG: Uncaught ConditionException", e); } return false; } @Override public void suggestionSelected(String suggestion) { searchExpression = suggestion; view.setSearchExpression(suggestion); view.refreshSearchResultsAndFacets(); } @Override public String getUserSearchExpression() { return searchExpression; } public void batchEditRequested(List<String> selectedRecordIds, String code, Object value) { Map<String, Object> changes = new HashMap<>(); changes.put(code, value); BatchProcessAction action = new ChangeValueOfMetadataBatchProcessAction(changes); BatchProcessesManager manager = modelLayerFactory.getBatchProcessesManager(); LogicalSearchCondition condition = fromAllSchemasIn(collection).where(IDENTIFIER).isIn(selectedRecordIds); BatchProcess process = manager.addBatchProcessInStandby(condition, action, "userBatchProcess"); manager.markAsPending(process); } @Override public List<MetadataVO> getMetadataAllowedInSort() { return getMetadataAllowedInSort(schemaTypeCode); } @Override public boolean isPreferAnalyzedFields() { return false; } public List<MetadataVO> getMetadataAllowedInBatchEdit() { MetadataToVOBuilder builder = new MetadataToVOBuilder(); List<MetadataVO> result = new ArrayList<>(); Language language = Language.withCode(view.getSessionContext().getCurrentLocale().getLanguage()); for (Metadata metadata : types().getSchemaType(schemaTypeCode).getAllMetadatas().sortAscTitle(language)) { if (isBatchEditable(metadata)) { result.add(builder.build(metadata, view.getSessionContext())); } } return result; } @Override protected LogicalSearchCondition getSearchCondition() { if (condition == null) { try { buildSearchCondition(); } catch (ConditionException e) { throw new RuntimeException("Unexpected exception (should be unreachable)", e); } } return condition; } BatchProcessingPresenterService batchProcessingPresenterService() { if (batchProcessingPresenterService == null) { Locale locale = view.getSessionContext().getCurrentLocale(); batchProcessingPresenterService = new BatchProcessingPresenterService(collection, appLayerFactory, locale); } return batchProcessingPresenterService; } public RecordVODataProvider getSearchResultsAsRecordVOs() { MetadataSchemaVO schemaVO = new MetadataSchemaToVOBuilder().build( schemaType(schemaTypeCode).getDefaultSchema(), RecordVO.VIEW_MODE.SEARCH, view.getSessionContext()); return new RecordVODataProvider(schemaVO, new RecordToVOBuilder(), modelLayerFactory, view.getSessionContext()) { @Override protected LogicalSearchQuery getQuery() { LogicalSearchQuery query = getSearchQuery().setHighlighting(highlighter).setOverridedQueryParams(extraSolrParams); if (sortCriterion == null) { if (StringUtils.isNotBlank(getUserSearchExpression())) { query.setFieldBoosts(searchBoostManager().getAllSearchBoostsByMetadataType(view.getCollection())); query.setQueryBoosts(searchBoostManager().getAllSearchBoostsByQueryType(view.getCollection())); } return query; } Metadata metadata = getMetadata(sortCriterion); return sortOrder == SortOrder.ASCENDING ? query.sortAsc(metadata) : query.sortDesc(metadata); } }; } void buildSearchCondition() throws ConditionException { String languageCode = searchServices().getLanguageCode(view.getCollection()); MetadataSchemaType type = schemaType(schemaTypeCode); condition = (view.getSearchCriteria().isEmpty()) ? from(type).returnAll() : new ConditionBuilder(type, languageCode).build(view.getSearchCriteria()); } private boolean isBatchEditable(Metadata metadata) { return !metadata.isSystemReserved() && !metadata.isUnmodifiable() && metadata.isEnabled() && !metadata.getType().isStructureOrContent() && metadata.getDataEntry().getType() == DataEntryType.MANUAL && isNotHidden(metadata) // XXX: Not supported in the backend && metadata.getType() != MetadataValueType.ENUM ; } private boolean isNotHidden(Metadata metadata) { MetadataDisplayConfig config = schemasDisplayManager().getMetadata(view.getCollection(), metadata.getCode()); return config.getInputType() != MetadataInputType.HIDDEN; } public List<LabelTemplate> getCustomTemplates() { LabelTemplateManager labelTemplateManager = appLayerFactory.getLabelTemplateManager(); return labelTemplateManager.listExtensionTemplates(schemaTypeCode); } public List<LabelTemplate> getDefaultTemplates() { LabelTemplateManager labelTemplateManager = appLayerFactory.getLabelTemplateManager(); return labelTemplateManager.listTemplates(schemaTypeCode); } public Boolean computeStatistics() { return schemaTypeCode != null && schemaTypeCode.equals(Folder.SCHEMA_TYPE); } @Override public List<String> getSupportedReports() { List<String> supportedReports = super.getSupportedReports(); ReportServices reportServices = new ReportServices(modelLayerFactory, collection); List<String> userReports = reportServices.getUserReportTitles(getCurrentUser(), view.getSchemaType()); supportedReports.addAll(userReports); return supportedReports; } @Override public NewReportWriterFactory<SearchResultReportParameters> getReport(String reportTitle) { try { return super.getReport(reportTitle); } catch (UnknownReportRuntimeException e) { /**/ return new SearchResultReportWriterFactory(appLayerFactory); } } public void addToCartRequested(List<String> recordIds, RecordVO cartVO) { // TODO: Create an extension for this RMSchemasRecordsServices rm = new RMSchemasRecordsServices(collection, appLayerFactory); Cart cart = rm.getOrCreateCart(getCurrentUser(), cartVO.getId()); switch (schemaTypeCode) { case Folder.SCHEMA_TYPE: cart.addFolders(recordIds); break; case Document.SCHEMA_TYPE: cart.addDocuments(recordIds); break; case ContainerRecord.SCHEMA_TYPE: cart.addContainers(recordIds); break; } try { recordServices().add(cart); view.showMessage($("SearchView.addedToCart")); } catch (RecordServicesException e) { view.showErrorMessage($(e)); } } public void createNewCartAndAddToItRequested(String title) { RMSchemasRecordsServices rm = new RMSchemasRecordsServices(collection, modelLayerFactory); Cart cart = rm.newCart(); cart.setTitle(title); cart.setOwner(getCurrentUser()); List<String> selectedRecords = view.getSelectedRecordIds(); switch (schemaTypeCode) { case Folder.SCHEMA_TYPE: cart.addFolders(selectedRecords); break; case Document.SCHEMA_TYPE: cart.addDocuments(selectedRecords); break; case ContainerRecord.SCHEMA_TYPE: cart.addContainers(selectedRecords); break; } try { recordServices().execute(new Transaction(cart.getWrappedRecord()).setUser(getCurrentUser())); view.showMessage($("SearchView.addedToCart")); } catch (RecordServicesException e) { e.printStackTrace(); } } public RecordVODataProvider getOwnedCartsDataProvider() { MetadataSchemaToVOBuilder schemaToVOBuilder = new MetadataSchemaToVOBuilder(); final RMSchemasRecordsServices rm = new RMSchemasRecordsServices(collection, appLayerFactory); final MetadataSchemaVO cartSchemaVO = schemaToVOBuilder .build(rm.cartSchema(), RecordVO.VIEW_MODE.TABLE, view.getSessionContext()); return new RecordVODataProvider(cartSchemaVO, new RecordToVOBuilder(), modelLayerFactory, view.getSessionContext()) { @Override protected LogicalSearchQuery getQuery() { return new LogicalSearchQuery(from(rm.cartSchema()).where(rm.cartOwner()) .isEqualTo(getCurrentUser().getId())).sortAsc(Schemas.TITLE); } }; } public RecordVODataProvider getSharedCartsDataProvider() { MetadataSchemaToVOBuilder schemaToVOBuilder = new MetadataSchemaToVOBuilder(); final RMSchemasRecordsServices rm = new RMSchemasRecordsServices(collection, appLayerFactory); final MetadataSchemaVO cartSchemaVO = schemaToVOBuilder .build(rm.cartSchema(), RecordVO.VIEW_MODE.TABLE, view.getSessionContext()); return new RecordVODataProvider(cartSchemaVO, new RecordToVOBuilder(), modelLayerFactory, view.getSessionContext()) { @Override protected LogicalSearchQuery getQuery() { return new LogicalSearchQuery(from(rm.cartSchema()).where(rm.cartSharedWithUsers()) .isContaining(Arrays.asList(getCurrentUser().getId()))).sortAsc(Schemas.TITLE); } }; } @Override protected SavedSearch prepareSavedSearch(SavedSearch search) { return search.setSearchType(AdvancedSearchView.SEARCH_TYPE) .setSchemaFilter(schemaTypeCode) .setFreeTextSearch(searchExpression) .setAdvancedSearch(view.getSearchCriteria()) .setPageNumber(pageNumber); } public Record getTemporarySearchRecord() { try { return modelLayerFactory.getRecordsCaches().getCache(collection).get(searchID); } catch (Exception e) { //TODO exception e.printStackTrace(); } return null; } protected SavedSearch saveTemporarySearch(boolean refreshPage) { Record tmpSearchRecord; if (searchID == null) { tmpSearchRecord = recordServices().newRecordWithSchema(schema(SavedSearch.DEFAULT_SCHEMA)); } else { tmpSearchRecord = getTemporarySearchRecord(); } SavedSearch search = new SavedSearch(tmpSearchRecord, types()) .setTitle("temporaryAdvance") .setUser(getCurrentUser().getId()) .setPublic(false) .setSortField(sortCriterion) .setSortOrder(SavedSearch.SortOrder.valueOf(sortOrder.name())) .setSelectedFacets(facetSelections.getNestedMap()) .setTemporary(true) .setSearchType(AdvancedSearchView.SEARCH_TYPE) .setSchemaFilter(schemaTypeCode) .setFreeTextSearch(searchExpression) .setAdvancedSearch(view.getSearchCriteria()) .setPageNumber(pageNumber) .setResultsViewMode(resultsViewMode) .setPageLength(selectedPageLength); try { ((RecordImpl) search.getWrappedRecord()).markAsSaved(1, search.getSchema()); modelLayerFactory.getRecordsCaches().getCache(collection).forceInsert(search.getWrappedRecord()); //recordServices().update(search); updateUIContext(search); if (refreshPage) { view.navigate().to().advancedSearchReplay(search.getId()); } } catch (Exception e) { e.printStackTrace(); } return search; } @Override public String getOriginType() { return batchProcessingPresenterService().getOriginType(buildBatchProcessLogicalSearchQuery()); } @Override public RecordVO newRecordVO(String schema, SessionContext sessionContext) { return batchProcessingPresenterService().newRecordVO(schema, sessionContext, buildBatchProcessLogicalSearchQuery()); } @Override public InputStream simulateButtonClicked(String selectedType, RecordVO viewObject) throws RecordServicesException { BatchProcessResults results = batchProcessingPresenterService() .simulate(selectedType, buildBatchProcessLogicalSearchQuery().setNumberOfRows(100), viewObject, getCurrentUser()); return batchProcessingPresenterService().formatBatchProcessingResults(results); } @Override public void processBatchButtonClicked(String selectedType, RecordVO viewObject) throws RecordServicesException { BatchProcessResults results = batchProcessingPresenterService() .execute(selectedType, buildBatchProcessLogicalSearchQuery(), viewObject, getCurrentUser()); if(searchID != null) { view.navigate().to().advancedSearchReplay(searchID); } else { view.navigate().to().advancedSearch(); } } @Override public BatchProcessingMode getBatchProcessingMode() { return batchProcessingPresenterService().getBatchProcessingMode(); } @Override public AppLayerCollectionExtensions getBatchProcessingExtension() { return batchProcessingPresenterService().getBatchProcessingExtension(); } @Override public String getSchema(String schemaType, String type) { return batchProcessingPresenterService().getSchema(schemaType, type); } @Override public String getTypeSchemaType(String schemaType) { return batchProcessingPresenterService().getTypeSchemaType(schemaType); } @Override public RecordFieldFactory newRecordFieldFactory(String schemaType, String selectedType) { return batchProcessingPresenterService() .newRecordFieldFactory(schemaType, selectedType, buildBatchProcessLogicalSearchQuery()); } @Override public boolean hasWriteAccessOnAllRecords() { LogicalSearchQuery query = buildBatchProcessLogicalSearchQuery(); return searchServices().getResultsCount(query.filteredWithUserWrite(getCurrentUser())) == searchServices() .getResultsCount(query); } @Override public long getNumberOfRecords() { return (int) searchServices().getResultsCount(buildBatchProcessLogicalSearchQuery()); } public void switchToTableView() { resultsViewMode = SearchResultsViewMode.TABLE; saveTemporarySearch(true); } public void switchToDetailedView() { resultsViewMode = SearchResultsViewMode.DETAILED; saveTemporarySearch(true); } public int getMaxSelectableResults() { return modelLayerFactory.getSystemConfigurationsManager().getValue(ConstellioEIMConfigs.MAX_SELECTABLE_SEARCH_RESULTS); } @Override public Object getReportParameters(String report) { switch (report) { case "Reports.fakeReport": case "Reports.FolderLinearMeasureStats": return super.getReportParameters(report); } return new SearchResultReportParameters(view.getSelectedRecordIds(), view.getSchemaType(), collection, report, getCurrentUser(), getSearchQuery()); } public boolean hasCurrentUserPermissionToUseCart() { return getCurrentUser().has(RMPermissionsTo.USE_CART).globally(); } public LogicalSearchQuery buildBatchProcessLogicalSearchQuery() { // if (((AdvancedSearchViewImpl) view).isSelectAllMode()) { if(ContainerRecord.SCHEMA_TYPE.equals(schemaTypeCode) || StorageSpace.SCHEMA_TYPE.equals(schemaTypeCode)) { if (!batchProcessOnAllSearchResults) { return buildUnsecuredLogicalSearchQueryWithSelectedIds(); } else { return buildUnsecuredLogicalSearchQueryWithUnselectedIds(); } } else { if (!batchProcessOnAllSearchResults) { return buildLogicalSearchQueryWithSelectedIds(); } else { return buildLogicalSearchQueryWithUnselectedIds(); } } } public LogicalSearchQuery buildLogicalSearchQueryWithSelectedIds() { LogicalSearchQuery query = new LogicalSearchQuery() .setCondition(condition.andWhere(Schemas.IDENTIFIER).isIn(view.getSelectedRecordIds())) .filteredWithUser(getCurrentUser()).filteredWithUserWrite(getCurrentUser()) .setPreferAnalyzedFields(isPreferAnalyzedFields()); if (searchExpression != null && !searchExpression.isEmpty()) { query.setFreeTextQuery(searchExpression); } return query; } public LogicalSearchQuery buildLogicalSearchQueryWithUnselectedIds() { LogicalSearchQuery query = new LogicalSearchQuery() .setCondition(condition.andWhere(Schemas.IDENTIFIER).isNotIn(view.getUnselectedRecordIds())) .filteredWithUser(getCurrentUser()).filteredWithUserWrite(getCurrentUser()) .setPreferAnalyzedFields(isPreferAnalyzedFields()); if (searchExpression != null && !searchExpression.isEmpty()) { query.setFreeTextQuery(searchExpression); } return query; } public LogicalSearchQuery buildUnsecuredLogicalSearchQueryWithSelectedIds() { LogicalSearchQuery query = new LogicalSearchQuery() .setCondition(condition.andWhere(Schemas.IDENTIFIER).isIn(view.getSelectedRecordIds())) .setPreferAnalyzedFields(isPreferAnalyzedFields()); if (searchExpression != null && !searchExpression.isEmpty()) { query.setFreeTextQuery(searchExpression); } return query; } public LogicalSearchQuery buildUnsecuredLogicalSearchQueryWithUnselectedIds() { LogicalSearchQuery query = new LogicalSearchQuery() .setCondition(condition.andWhere(Schemas.IDENTIFIER).isNotIn(view.getUnselectedRecordIds())) .setPreferAnalyzedFields(isPreferAnalyzedFields()); if (searchExpression != null && !searchExpression.isEmpty()) { query.setFreeTextQuery(searchExpression); } return query; } public LogicalSearchQuery buildLogicalSearchQueryWithAllSearchResults() { LogicalSearchQuery query = new LogicalSearchQuery() .filteredWithUser(getCurrentUser()).filteredWithUserWrite(getCurrentUser()) .setPreferAnalyzedFields(isPreferAnalyzedFields()); if (searchExpression != null && !searchExpression.isEmpty()) { query.setFreeTextQuery(searchExpression); } return query; } public void logRecordView(RecordVO recordVO) { Record record = presenterService().getRecord(recordVO.getId()); ModelLayerFactory modelLayerFactory = view.getConstellioFactories().getModelLayerFactory(); User user = getCurrentUser(); modelLayerFactory.newLoggingServices().logRecordView(record, user); } @Override public void allSearchResultsButtonClicked() { batchProcessOnAllSearchResults = true; } @Override public void selectedSearchResultsButtonClicked() { batchProcessOnAllSearchResults = false; } @Override public boolean isSearchResultsSelectionForm() { return true; } public User getUser() { return getCurrentUser(); } }