package com.constellio.app.ui.pages.base;
import com.constellio.app.api.extensions.params.AvailableActionsParam;
import com.constellio.app.entities.navigation.NavigationConfig;
import com.constellio.app.entities.navigation.NavigationItem;
import com.constellio.app.entities.schemasDisplay.MetadataDisplayConfig;
import com.constellio.app.entities.schemasDisplay.SchemaTypeDisplayConfig;
import com.constellio.app.modules.rm.constants.RMPermissionsTo;
import com.constellio.app.modules.rm.services.RMSchemasRecordsServices;
import com.constellio.app.modules.rm.ui.builders.UserToVOBuilder;
import com.constellio.app.modules.rm.wrappers.*;
import com.constellio.app.services.extensions.ConstellioModulesManagerImpl;
import com.constellio.app.services.factories.AppLayerFactory;
import com.constellio.app.services.factories.ConstellioFactories;
import com.constellio.app.services.schemasDisplay.SchemasDisplayManager;
import com.constellio.app.ui.entities.*;
import com.constellio.app.ui.entities.RecordVO.VIEW_MODE;
import com.constellio.app.ui.framework.builders.MetadataSchemaToVOBuilder;
import com.constellio.app.ui.framework.builders.MetadataSchemaTypeToVOBuilder;
import com.constellio.app.ui.framework.builders.MetadataToVOBuilder;
import com.constellio.app.ui.framework.builders.RecordToVOBuilder;
import com.constellio.app.ui.framework.components.ComponentState;
import com.constellio.app.ui.framework.data.RecordVODataProvider;
import com.constellio.app.ui.i18n.i18n;
import com.constellio.app.ui.pages.search.AdvancedSearchCriteriaComponent.SearchCriteriaPresenter;
import com.constellio.data.dao.dto.records.FacetValue;
import com.constellio.data.utils.ImpossibleRuntimeException;
import com.constellio.data.utils.TimeProvider;
import com.constellio.model.entities.Language;
import com.constellio.model.entities.Taxonomy;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.Transaction;
import com.constellio.model.entities.records.wrappers.User;
import com.constellio.model.entities.schemas.*;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.model.services.records.RecordServices;
import com.constellio.model.services.records.RecordServicesException;
import com.constellio.model.services.records.RecordServicesRuntimeException.NoSuchRecordWithId;
import com.constellio.model.services.schemas.MetadataSchemasManager;
import com.constellio.model.services.schemas.SchemaUtils;
import com.constellio.model.services.schemas.builders.CommonMetadataBuilder;
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.users.UserServices;
import com.vaadin.server.Page;
import com.vaadin.server.Resource;
import com.vaadin.ui.Component;
import com.vaadin.ui.Notification;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.*;
import static com.constellio.app.ui.i18n.i18n.$;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from;
import static java.util.Arrays.asList;
public class ConstellioHeaderPresenter implements SearchCriteriaPresenter {
Boolean allItemsSelected = true;
Boolean allItemsDeselected = false;
private final ConstellioHeader header;
private String schemaTypeCode;
private transient AppLayerFactory appLayerFactory;
private transient ModelLayerFactory modelLayerFactory;
private transient SchemasDisplayManager schemasDisplayManager;
private boolean advancedSearchFormVisible;
private BasePresenterUtils presenterUtils;
private UserToVOBuilder voBuilder = new UserToVOBuilder();
private boolean refreshSelectionPanel;
private Map<String, String> deselectedRecordsWithSchema;
public ConstellioHeaderPresenter(ConstellioHeader header) {
this.header = header;
init();
}
public void searchRequested(String expression, String schemaTypeCode) {
if (StringUtils.isNotBlank(schemaTypeCode)) {
header.hideAdvancedSearchPopup().navigateTo().advancedSearch();
} else if (StringUtils.isNotBlank(expression)) {
header.hideAdvancedSearchPopup().navigateTo().simpleSearch(expression);
}
}
public void addCriterionRequested() {
header.addEmptyCriterion();
}
public void savedSearchPageRequested() {
header.hideAdvancedSearchPopup().navigateTo().listSavedSearches();
}
public void schemaTypeSelected(String schemaTypeCode) {
this.schemaTypeCode = schemaTypeCode;
header.setAdvancedSearchSchemaType(schemaTypeCode);
}
public List<MetadataSchemaTypeVO> getSchemaTypes() {
MetadataSchemaTypeToVOBuilder builder = new MetadataSchemaTypeToVOBuilder();
List<MetadataSchemaTypeVO> result = new ArrayList<>();
MetadataSchemaTypes types = types();
if (types != null) {
for (MetadataSchemaType type : types.getSchemaTypes()) {
SchemaTypeDisplayConfig config = schemasDisplayManager().getType(header.getCollection(), type.getCode());
if (config.isAdvancedSearch() && isVisibleForUser(type, getCurrentUser())) {
result.add(builder.build(type));
}
}
}
return result;
}
private boolean isVisibleForUser(MetadataSchemaType type, User currentUser) {
if (ContainerRecord.SCHEMA_TYPE.equals(type.getCode()) && !currentUser.hasAny(RMPermissionsTo.DISPLAY_CONTAINERS, RMPermissionsTo.MANAGE_CONTAINERS)
.globally()) {
return false;
} else if (StorageSpace.SCHEMA_TYPE.equals(type.getCode()) && !currentUser.has(RMPermissionsTo.MANAGE_STORAGE_SPACES)
.globally()) {
return false;
}
return true;
}
public String getSchemaType() {
return schemaTypeCode;
}
public boolean isValidAdvancedSearchCriterionPresent() {
return schemaTypeCode != null;
}
@Override
public List<MetadataVO> getMetadataAllowedInCriteria() {
MetadataSchemaType schemaType = types().getSchemaType(schemaTypeCode);
List<FacetValue> schema_s = modelLayerFactory.newSearchServices().query(new LogicalSearchQuery()
.setNumberOfRows(0)
.setCondition(from(schemaType).returnAll()).addFieldFacet("schema_s").filteredWithUser(getCurrentUser()))
.getFieldFacetValues("schema_s");
Set<String> metadataLocalCodes = new HashSet<>();
if (schema_s != null) {
for (FacetValue facetValue : schema_s) {
if (facetValue.getQuantity() > 0) {
String schema = facetValue.getValue();
metadataLocalCodes.addAll(types().getSchema(schema).getMetadatas().toLocalCodesList());
}
}
}
MetadataToVOBuilder builder = new MetadataToVOBuilder();
List<MetadataVO> result = new ArrayList<>();
result.add(builder.build(schemaType.getMetadataWithAtomicCode(CommonMetadataBuilder.PATH), header.getSessionContext()));
for (Metadata metadata : schemaType.getAllMetadatas()) {
if (!schemaType.hasSecurity() || metadataLocalCodes.contains(metadata.getLocalCode())) {
boolean isTextOrString = metadata.getType() == MetadataValueType.STRING || metadata.getType() == MetadataValueType.TEXT;
MetadataDisplayConfig config = schemasDisplayManager().getMetadata(header.getCollection(), metadata.getCode());
if (config.isVisibleInAdvancedSearch() && metadata.isEnabled() && isMetadataVisibleForUser(metadata, getCurrentUser()) && (!isTextOrString || isTextOrString && metadata.isSearchable())) {
result.add(builder.build(metadata, header.getSessionContext()));
}
}
}
sort(result);
return result;
}
private boolean isMetadataVisibleForUser(Metadata metadata, User currentUser) {
if(MetadataValueType.REFERENCE.equals(metadata.getType())) {
String referencedSchemaType = metadata.getAllowedReferences().getTypeWithAllowedSchemas();
Taxonomy taxonomy = appLayerFactory.getModelLayerFactory().getTaxonomiesManager().getTaxonomyFor(header.getCollection(), referencedSchemaType);
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(currentUser.getId());
} else {
return true;
}
}
return true;
}
protected void sort(List<MetadataVO> metadataVOs) {
Collections.sort(metadataVOs, new Comparator<MetadataVO>() {
@Override
public int compare(MetadataVO o1, MetadataVO o2) {
return o1.getLabel().toLowerCase().compareTo(o2.getLabel().toLowerCase());
}
});
}
@Override
public MetadataVO getMetadataVO(String metadataCode) {
if (metadataCode != null) {
MetadataToVOBuilder builder = new MetadataToVOBuilder();
MetadataSchemaTypes types = types();
Metadata metadata = types.getMetadata(metadataCode);
return builder.build(metadata);
} else {
return null;
}
}
private MetadataSchemaTypes types() {
MetadataSchemasManager metadataSchemasManager = modelLayerFactory.getMetadataSchemasManager();
return metadataSchemasManager.getSchemaTypes(header.getCollection());
}
private SchemasDisplayManager schemasDisplayManager() {
if (schemasDisplayManager == null) {
schemasDisplayManager = appLayerFactory.getMetadataSchemasDisplayManager();
}
return schemasDisplayManager;
}
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
init();
}
private void init() {
ConstellioFactories constellioFactories = header.getConstellioFactories();
SessionContext sessionContext = header.getSessionContext();
appLayerFactory = constellioFactories.getAppLayerFactory();
modelLayerFactory = constellioFactories.getModelLayerFactory();
this.presenterUtils = new BasePresenterUtils(constellioFactories, sessionContext);
this.deselectedRecordsWithSchema = new HashMap<>();
UserServices userServices = modelLayerFactory.newUserServices();
List<String> collections = userServices.getUser(getCurrentUser().getUsername()).getCollections();
header.setCollections(collections);
if (sessionContext.getSelectedRecordIds().isEmpty()) {
header.setSelectionButtonEnabled(false);
} else {
header.setSelectionButtonEnabled(true);
}
updateSelectionCount();
}
/**
* FIXME Remove from presenter
*/
public Resource getUserLogoResource() {
return LogoUtils.getUserLogoResource(modelLayerFactory);
}
public List<NavigationItem> getActionMenuItems() {
return navigationConfig().getNavigation(ConstellioHeader.ACTION_MENU);
}
private NavigationConfig navigationConfig() {
ConstellioModulesManagerImpl manager = (ConstellioModulesManagerImpl) appLayerFactory.getModulesManager();
return manager.getNavigationConfig(header.getCollection());
}
public ComponentState getStateFor(NavigationItem item) {
return item.getStateFor(getCurrentUser(), appLayerFactory);
}
protected User getCurrentUser() {
return presenterUtils.getCurrentUser();
}
public void collectionClicked(String newCollection) {
SessionContext sessionContext = header.getSessionContext();
String currentCollection = sessionContext.getCurrentCollection();
if (!currentCollection.equals(newCollection)) {
String username = getCurrentUser().getUsername();
UserServices userServices = modelLayerFactory.newUserServices();
User newUser = userServices.getUserInCollection(username, newCollection);
try {
modelLayerFactory.newRecordServices().update(newUser
.setLastLogin(TimeProvider.getLocalDateTime())
.setLastIPAddress(sessionContext.getCurrentUserIPAddress()));
Locale userLanguage = getSessionLanguage(newUser);
sessionContext.setCurrentLocale(userLanguage);
i18n.setLocale(userLanguage);
} catch (RecordServicesException e) {
throw new RuntimeException(e);
}
UserVO newUserVO = voBuilder.build(newUser.getWrappedRecord(), VIEW_MODE.DISPLAY, sessionContext);
sessionContext.setCurrentCollection(newCollection);
sessionContext.setCurrentUser(newUserVO);
header.navigateTo().home();
header.updateUIContent();
}
}
//FIXME use service and remove redundant code in LoginPresenter
Locale getSessionLanguage(User userInLastCollection) {
String userPreferredLanguage = userInLastCollection.getLoginLanguageCode();
String systemLanguage = modelLayerFactory.getConfiguration().getMainDataLanguage();
if (StringUtils.isBlank(userPreferredLanguage)) {
return getLocale(systemLanguage);
} else {
List<String> collectionLanguages = modelLayerFactory.getCollectionsListManager()
.getCollectionLanguages(userInLastCollection.getCollection());
if (collectionLanguages == null || collectionLanguages.isEmpty() || !collectionLanguages
.contains(userPreferredLanguage)) {
return getLocale(systemLanguage);
} else {
return getLocale(userPreferredLanguage);
}
}
}
private Locale getLocale(String languageCode) {
i18n.getSupportedLanguages();
for (Language language : Language.values()) {
if (language.getCode().equals(languageCode)) {
return new Locale(languageCode);
}
}
throw new ImpossibleRuntimeException("Invalid language " + languageCode);
}
public void logoClicked() {
header.navigateTo().home();
}
private long popupClosedTS = -1;
void popupClosed() {
popupClosedTS = System.currentTimeMillis();
advancedSearchFormVisible = false;
}
private boolean isPopupClosedIgnored() {
return System.currentTimeMillis() - popupClosedTS < 500;
}
void advancedSearchFormButtonClicked() {
if (advancedSearchFormVisible) {
advancedSearchFormVisible = false;
} else {
advancedSearchFormVisible = !isPopupClosedIgnored();
}
header.setAdvancedSearchFormVisible(advancedSearchFormVisible);
}
void selectionButtonClicked() {
header.setSelectionPanelVisible(true, refreshSelectionPanel);
refreshSelectionPanel = false;
}
private void updateSelectionButton() {
SessionContext sessionContext = header.getSessionContext();
if (sessionContext.getSelectedRecordIds().isEmpty()) {
header.setSelectionButtonEnabled(false);
} else {
header.setSelectionButtonEnabled(true);
}
header.refreshSelectionPanel();
updateSelectionCount();
}
public void selectedRecordsCleared() {
allItemsSelected = false;
allItemsDeselected = false;
refreshSelectionPanel = true;
updateSelectionButton();
}
public void selectedRecordIdRemoved(String recordId) {
refreshSelectionPanel = true;
updateSelectionButton();
}
public void selectedRecordIdAdded(String recordId) {
refreshSelectionPanel = true;
updateSelectionButton();
}
public void clearSelectionButtonClicked() {
SessionContext sessionContext = header.getSessionContext();
List<String> selectedRecordIds = new ArrayList<>(sessionContext.getSelectedRecordIds());
for (String id : deselectedRecordsWithSchema.keySet()) {
selectedRecordIds.remove(id);
}
SearchServices searchServices = modelLayerFactory.newSearchServices();
String currentCollection = sessionContext.getCurrentCollection();
for (String id : selectedRecordIds) {
Record record = searchServices.searchSingleResult(LogicalSearchQueryOperators.fromAllSchemasIn(currentCollection)
.where(Schemas.IDENTIFIER).isEqualTo(id));
String schemaTypeCode = record == null ? null : record.getTypeCode();
sessionContext.removeSelectedRecordId(id, schemaTypeCode);
}
header.removeRecordsFromPanel(selectedRecordIds);
updateSelectionCount();
}
public boolean isSelected(String recordId) {
return !allItemsDeselected && (allItemsSelected || !deselectedRecordsWithSchema.containsKey(recordId));
}
public void selectionChanged(String recordId, Boolean selected) {
allItemsSelected = false;
allItemsDeselected = false;
// refreshSelectionPanel = true;
SessionContext sessionContext = header.getSessionContext();
RecordServices recordServices = modelLayerFactory.newRecordServices();
Record record;
try {
record = recordServices.getDocumentById(recordId);
} catch (NoSuchRecordWithId e) {
record = null;
}
String schemaTypeCode = record == null ? null : record.getTypeCode();
if (selected) {
sessionContext.addSelectedRecordId(recordId, schemaTypeCode);
deselectedRecordsWithSchema.remove(recordId);
} else {
deselectedRecordsWithSchema.put(recordId, schemaTypeCode);
}
}
private void updateSelectionCount() {
SessionContext sessionContext = header.getSessionContext();
int selectionCount = sessionContext.getSelectedRecordIds().size();
header.setSelectionCount(selectionCount);
}
/**
* FIXME Remove Vaadin references from presenter
*/
public void buildSelectionPanelActionButtons(Component actionMenuLayout) {
appLayerFactory.getExtensions().forCollection(header.getCollection())
.addAvailableActions(buildAvailableActionsParam(actionMenuLayout));
}
public AvailableActionsParam buildAvailableActionsParam(Component actionMenuLayout) {
List<String> selectedRecordIds = new ArrayList<>();
Map<String, Long> selectedRecordSchemaTypeCodes = new HashMap<>();
if (!allItemsDeselected) {
selectedRecordIds.addAll(header.getSessionContext().getSelectedRecordIds());
selectedRecordSchemaTypeCodes.putAll(header.getSessionContext().getSelectedRecordSchemaTypeCodes());
if (!allItemsSelected) {
Set<Map.Entry<String, String>> entries = deselectedRecordsWithSchema.entrySet();
for (Iterator<Map.Entry<String, String>> it = entries.iterator(); it.hasNext(); ) {
Map.Entry<String, String> entry = it.next();
String deselectedRecordId = entry.getKey();
String schemaCode = entry.getValue();
if (selectedRecordIds.contains(deselectedRecordId)) {
selectedRecordIds.remove(deselectedRecordId);
if (selectedRecordSchemaTypeCodes.containsKey(schemaCode)) {
if (selectedRecordSchemaTypeCodes.get(schemaCode) == 1L) {
selectedRecordSchemaTypeCodes.remove(schemaCode);
} else {
selectedRecordSchemaTypeCodes
.put(entry.getValue(), selectedRecordSchemaTypeCodes.get(schemaCode) - 1);
}
}
} else {
it.remove();
}
}
}
}
return new AvailableActionsParam(selectedRecordIds, new ArrayList<>(selectedRecordSchemaTypeCodes.keySet()),
getCurrentUser(), actionMenuLayout);
}
public void createNewCartAndAddToItRequested(String title) {
RMSchemasRecordsServices rm = new RMSchemasRecordsServices(header.getCollection(), appLayerFactory);
Cart cart = rm.newCart();
cart.setTitle(title);
cart.setOwner(getCurrentUser());
List<String> selectedRecords = header.getSessionContext().getSelectedRecordIds();
RecordServices recordServices = modelLayerFactory.newRecordServices();
for (String record : selectedRecords) {
switch (recordServices.getDocumentById(record).getTypeCode()) {
case Folder.SCHEMA_TYPE:
cart.addFolders(asList(record));
break;
case Document.SCHEMA_TYPE:
cart.addDocuments(asList(record));
break;
case ContainerRecord.SCHEMA_TYPE:
cart.addContainers(asList(record));
break;
}
}
try {
modelLayerFactory.newRecordServices().execute(new Transaction(cart.getWrappedRecord()).setUser(getCurrentUser()));
showMessage($("ConstellioHeader.selection.actions.actionCompleted", selectedRecords.size()));
// view.showMessage($("SearchView.addedToCart"));
} catch (RecordServicesException e) {
e.printStackTrace();
}
}
public RecordVODataProvider getOwnedCartsDataProvider() {
MetadataSchemaToVOBuilder schemaToVOBuilder = new MetadataSchemaToVOBuilder();
final RMSchemasRecordsServices rm = new RMSchemasRecordsServices(header.getCollection(), appLayerFactory);
final MetadataSchemaVO cartSchemaVO = schemaToVOBuilder
.build(rm.cartSchema(), RecordVO.VIEW_MODE.TABLE, header.getSessionContext());
return new RecordVODataProvider(cartSchemaVO, new RecordToVOBuilder(), modelLayerFactory, header.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(header.getCollection(), appLayerFactory);
final MetadataSchemaVO cartSchemaVO = schemaToVOBuilder
.build(rm.cartSchema(), RecordVO.VIEW_MODE.TABLE, header.getSessionContext());
return new RecordVODataProvider(cartSchemaVO, new RecordToVOBuilder(), modelLayerFactory, header.getSessionContext()) {
@Override
protected LogicalSearchQuery getQuery() {
return new LogicalSearchQuery(from(rm.cartSchema()).where(rm.cartSharedWithUsers())
.isContaining(asList(getCurrentUser().getId()))).sortAsc(Schemas.TITLE);
}
};
}
public void addToCartRequested(List<String> recordIds, RecordVO cartVO) {
// TODO: Create an extension for this
RMSchemasRecordsServices rm = new RMSchemasRecordsServices(header.getCollection(), appLayerFactory);
Cart cart = rm.getOrCreateCart(getCurrentUser(), cartVO.getId());
RecordServices recordServices = modelLayerFactory.newRecordServices();
for (String record : recordIds) {
switch (recordServices.getDocumentById(record).getTypeCode()) {
case Folder.SCHEMA_TYPE:
cart.addFolders(asList(record));
break;
case Document.SCHEMA_TYPE:
cart.addDocuments(asList(record));
break;
case ContainerRecord.SCHEMA_TYPE:
cart.addContainers(asList(record));
break;
}
}
try {
modelLayerFactory.newRecordServices().add(cart);
showMessage($("ConstellioHeader.selection.actions.actionCompleted", recordIds.size()));
} catch (RecordServicesException e) {
showMessage($(e));
}
}
public void showMessage(String errorMessage) {
Notification notification = new Notification(errorMessage + "<br/><br/>" + $("clickToClose"),
Notification.Type.WARNING_MESSAGE);
notification.setHtmlContentAllowed(true);
notification.show(Page.getCurrent());
}
void computeAllItemsSelected() {
SessionContext sessionContext = header.getSessionContext();
List<String> selectedRecordIds = sessionContext.getSelectedRecordIds();
for (String selectedRecordId : selectedRecordIds) {
if (deselectedRecordsWithSchema.containsKey(selectedRecordId)) {
allItemsSelected = false;
return;
}
}
allItemsSelected = true;
allItemsDeselected = false;
}
boolean isAllItemsSelected() {
return allItemsSelected;
}
boolean isAllItemsDeselected() {
return allItemsDeselected;
}
void selectAllClicked() {
deselectedRecordsWithSchema.clear();
allItemsSelected = true;
allItemsDeselected = false;
updateSelectionButton();
header.refreshButtons();
}
void deselectAllClicked() {
deselectedRecordsWithSchema.clear();
SessionContext sessionContext = header.getSessionContext();
String collection = sessionContext.getCurrentCollection();
List<String> selectedRecordIds = sessionContext.getSelectedRecordIds();
RecordServices recordServices = modelLayerFactory.newRecordServices();
List<Record> selectedRecords = recordServices.getRecordsById(collection, selectedRecordIds);
for (Record selectedRecord : selectedRecords) {
String selectedRecordId = selectedRecord.getId();
String selectedRecordSchemaType = SchemaUtils.getSchemaTypeCode(selectedRecord.getSchemaCode());
deselectedRecordsWithSchema.put(selectedRecordId, selectedRecordSchemaType);
}
allItemsSelected = false;
allItemsDeselected = true;
updateSelectionButton();
header.refreshButtons();
}
}