package org.sigmah.client.ui.presenter.contact.dashboardlist; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Set; import com.allen_sauer.gwt.log.client.Log; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.ButtonEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.SelectionChangedEvent; import com.extjs.gxt.ui.client.event.SelectionChangedListener; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Window; import com.extjs.gxt.ui.client.widget.form.AdapterField; import com.extjs.gxt.ui.client.widget.form.ComboBox; import com.extjs.gxt.ui.client.widget.form.TextField; import com.extjs.gxt.ui.client.widget.grid.filters.GridFilters; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Image; import com.google.inject.ImplementedBy; import com.google.inject.Inject; import org.sigmah.client.dispatch.CommandResultHandler; import org.sigmah.client.dispatch.monitor.LoadingMask; import org.sigmah.client.event.UpdateEvent; import org.sigmah.client.event.handler.UpdateHandler; import org.sigmah.client.i18n.I18N; import org.sigmah.client.inject.Injector; import org.sigmah.client.page.Page; import org.sigmah.client.page.RequestParameter; import org.sigmah.client.ui.notif.N10N; import org.sigmah.client.ui.presenter.base.AbstractPresenter; import org.sigmah.client.ui.view.base.ViewInterface; import org.sigmah.client.ui.view.contact.dashboardlist.ContactsListView; import org.sigmah.client.ui.view.contact.dashboardlist.DashboardContact; import org.sigmah.client.ui.widget.HasGrid; import org.sigmah.client.ui.widget.button.Button; import org.sigmah.client.ui.widget.contact.DedupeContactDialog; import org.sigmah.client.ui.widget.form.FormPanel; import org.sigmah.client.ui.widget.form.Forms; import org.sigmah.client.ui.widget.form.ListComboBox; import org.sigmah.client.util.ImageProvider; import org.sigmah.client.util.profiler.Profiler; import org.sigmah.client.util.profiler.Scenario; import org.sigmah.offline.sync.SuccessCallback; import org.sigmah.shared.command.CheckContactDuplication; import org.sigmah.shared.command.CreateEntity; import org.sigmah.shared.command.DedupeContact; import org.sigmah.shared.command.GetContactDuplicatedProperties; import org.sigmah.shared.command.GetContactHistory; import org.sigmah.shared.command.GetContactModels; import org.sigmah.shared.command.GetContacts; import org.sigmah.shared.command.GetOrgUnits; import org.sigmah.shared.command.result.ContactDuplicatedProperty; import org.sigmah.shared.command.result.ContactHistory; import org.sigmah.shared.command.result.CreateResult; import org.sigmah.shared.command.result.ListResult; import org.sigmah.shared.dto.ContactDTO; import org.sigmah.shared.dto.ContactModelDTO; import org.sigmah.shared.dto.orgunit.OrgUnitDTO; import org.sigmah.shared.dto.referential.ContactModelType; import org.sigmah.shared.dto.referential.GlobalPermissionEnum; import org.sigmah.shared.util.ProfileUtils; public class ContactsListWidget extends AbstractPresenter<ContactsListWidget.View> { @ImplementedBy(ContactsListView.class) public static interface View extends ViewInterface, HasGrid<DashboardContact> { void updateAccessibilityState(boolean authorized); ContentPanel getContactsPanel(); GridFilters getGridFilters(); void addContact(DashboardContact contact); void clearContacts(); void updateToolbar(boolean addContact, boolean importContact, boolean exportContact); Button getAddButton(); Button getImportButton(); Button getExportButton(); void syncSize(); } public interface CreateContactHandler { void handleContactCreation(ContactModelDTO contactModelDTO, String email, String firstName, String familyName, String organizationName, OrgUnitDTO mainOrgUnit, List<OrgUnitDTO> secondaryOrgUnits); } // Current contacts grid parameters. private boolean currentlyLoading = false; private final Queue<GetContacts> commandQueue = new LinkedList<GetContacts>(); private CreateContactHandler createContactHandler; @Inject private ImageProvider imageProvider; /** * The GetContacts command which will be executed for the next refresh. */ private GetContacts command; /** * Builds a new contact list panel with default values. */ @Inject public ContactsListWidget(final View view, final Injector injector) { super(view, injector); createContactHandler = new CreateContactHandler() { @Override public void handleContactCreation(final ContactModelDTO contactModelDTO, final String email, final String firstName, final String familyName, final String organizationName, final OrgUnitDTO mainOrgUnit, final List<OrgUnitDTO> secondaryOrgUnits) { CheckContactDuplication checkContactDuplication; if (contactModelDTO.getType() == ContactModelType.INDIVIDUAL) { checkContactDuplication = new CheckContactDuplication(null, email, familyName, firstName, contactModelDTO); } else { checkContactDuplication = new CheckContactDuplication(null, email, familyName, null, contactModelDTO); } dispatch.execute(checkContactDuplication, new AsyncCallback<ListResult<ContactDTO>>() { @Override public void onFailure(Throwable caught) { Log.error("Error while checking contact duplicates."); } @Override public void onSuccess(ListResult<ContactDTO> result) { final HashMap<String, Object> properties = buildPropertyMap(contactModelDTO, email, firstName, familyName, organizationName, mainOrgUnit, secondaryOrgUnits); if (result == null || result.getSize() == 0) { createEntity(properties); return; } final DedupeContactDialog dedupeContactDialog = new DedupeContactDialog(true); dedupeContactDialog.getPossibleDuplicatesGrid().getStore().add(result.getList()); dedupeContactDialog.getFirstStepMainButton().addSelectionListener(new SelectionListener<ButtonEvent>() { @Override public void componentSelected(ButtonEvent ce) { createEntity(properties); dedupeContactDialog.hide(); } }); dedupeContactDialog.setSecondStepHandler(new DedupeContactDialog.SecondStepHandler() { @Override public void initialize(final Integer contactId, final ListStore<ContactDuplicatedProperty> propertiesStore) { dispatch.execute(new GetContactDuplicatedProperties(contactId, null, properties), new CommandResultHandler<ListResult<ContactDuplicatedProperty>>() { @Override protected void onCommandSuccess(ListResult<ContactDuplicatedProperty> result) { propertiesStore.add(result.getList()); } }, new LoadingMask(dedupeContactDialog)); } @Override public void downloadImage(String id, final Image image) { imageProvider.provideDataUrl(id, new SuccessCallback<String>() { @Override public void onSuccess(String dataUrl) { image.setUrl(dataUrl); } }); } @Override public void handleDedupeContact(final Integer targetedContactId, List<ContactDuplicatedProperty> selectedProperties) { dispatch.execute(new DedupeContact(selectedProperties, targetedContactId), new CommandResultHandler<ContactDTO>() { @Override protected void onCommandSuccess(final ContactDTO targetedContactDTO) { dedupeContactDialog.hide(); GetContactHistory historyCmd = new GetContactHistory(targetedContactDTO.getId(), true); dispatch.execute(historyCmd, new CommandResultHandler<ListResult<ContactHistory>>() { @Override protected void onCommandSuccess(ListResult<ContactHistory> result) { ContactHistory lastChange = result.isEmpty() ? null : result.getList().get(0); view.addContact(new DashboardContact(targetedContactDTO, lastChange)); } }); } }); } @Override public void handleCancel() { dedupeContactDialog.hide(); } }); dedupeContactDialog.show(); } }); } }; } private void createEntity(HashMap<String, Object> properties) { dispatch.execute(new CreateEntity(ContactDTO.ENTITY_NAME, properties), new AsyncCallback<CreateResult>() { @Override public void onFailure(Throwable caught) { Log.error("Error while creating a new Contact from contact creation dialog."); } @Override public void onSuccess(CreateResult result) { final ContactDTO contact = (ContactDTO)result.getEntity(); GetContactHistory historyCmd = new GetContactHistory(contact.getId(), true); dispatch.execute(historyCmd, new CommandResultHandler<ListResult<ContactHistory>>() { @Override protected void onCommandSuccess(ListResult<ContactHistory> result) { ContactHistory lastChange = result.isEmpty() ? null : result.getList().get(0); view.addContact(new DashboardContact(contact, lastChange)); } }); } }); } private HashMap<String, Object> buildPropertyMap(ContactModelDTO contactModelDTO, String email, String firstName, String familyName, String organizationName, OrgUnitDTO mainOrgUnit, List<OrgUnitDTO> secondaryOrgUnits) { HashMap<String, Object> properties = new HashMap<String, Object>(); properties.put(ContactDTO.CONTACT_MODEL, contactModelDTO.getId()); properties.put(ContactDTO.EMAIL, email); properties.put(ContactDTO.FIRSTNAME, contactModelDTO.getType() == ContactModelType.INDIVIDUAL ? firstName : null); properties.put(ContactDTO.NAME, contactModelDTO.getType() == ContactModelType.INDIVIDUAL ? familyName : organizationName); if (mainOrgUnit != null) { properties.put(ContactDTO.MAIN_ORG_UNIT, mainOrgUnit.getId()); } if (secondaryOrgUnits != null) { HashSet<Integer> secondaryOrgUnitIds = new HashSet<Integer>(); for (OrgUnitDTO secondaryOrgUnit : secondaryOrgUnits) { secondaryOrgUnitIds.add(secondaryOrgUnit.getId()); } properties.put(ContactDTO.SECONDARY_ORG_UNITS, secondaryOrgUnitIds); } return properties; } /** * {@inheritDoc} */ @Override public void onBind() { // -- // Update contact event. // -- registerHandler(eventBus.addHandler(UpdateEvent.getType(), new UpdateHandler() { @Override public void onUpdate(final UpdateEvent event) { if (event.concern(UpdateEvent.CONTACT_CREATE)) { // On contact creation event. final ContactDTO createdContact = event.getParam(1); GetContactHistory historyCmd = new GetContactHistory(createdContact.getId(), true); dispatch.execute(historyCmd, new CommandResultHandler<ListResult<ContactHistory>>() { @Override protected void onCommandSuccess(ListResult<ContactHistory> result) { ContactHistory lastChange = result.isEmpty() ? null : result.getList().get(0); view.addContact(new DashboardContact(createdContact, lastChange)); } }); } } })); // -- // Add action handler. // -- view.getAddButton().addListener(Events.Select, new Listener<BaseEvent>() { @Override public void handleEvent(final BaseEvent be) { showContactCreator(); } }); // -- // Import action handler. // -- view.getImportButton().addListener(Events.Select, new Listener<BaseEvent>() { @Override public void handleEvent(final BaseEvent be) { // TODO : eventBus.navigate(Page.CONTACT_EXPORTS); } }); // -- // Export action handler. // -- view.getExportButton().addListener(Events.Select, new Listener<BaseEvent>() { @Override public void handleEvent(final BaseEvent be) { eventBus.navigate(Page.CONTACT_EXPORTS); } }); // -- // Contact name click handler. // -- view.setGridEventHandler(new HasGrid.GridEventHandler<DashboardContact>() { @Override public void onRowClickEvent(final DashboardContact rowElement) { Profiler.INSTANCE.startScenario(Scenario.OPEN_CONTACT); eventBus.navigateRequest(Page.CONTACT_DASHBOARD.requestWith(RequestParameter.ID, rowElement.getContact().getId())); } }); } /** * Asks for a refresh of the contacts list. */ public void refresh(final boolean mainOrgUnitOnly) { // Updates toolbar. final boolean addEnabled = ProfileUtils.isGranted(auth(), GlobalPermissionEnum.EDIT_VISIBLE_CONTACTS); final boolean importEnabled = ProfileUtils.isGranted(auth(), GlobalPermissionEnum.IMPORT_CONTACTS); final boolean exportEnabled = ProfileUtils.isGranted(auth(), GlobalPermissionEnum.EXPORT_ALL_CONTACTS); view.updateToolbar(addEnabled, importEnabled, exportEnabled); // Updates accessibility. view.updateAccessibilityState(ProfileUtils.isGranted(auth(), GlobalPermissionEnum.VIEW_VISIBLE_CONTACTS)); // Builds the next refresh command. command = new GetContacts(); Set<Integer> orgUnitsIds = new HashSet<Integer>(); if(mainOrgUnitOnly) { orgUnitsIds.add(auth().getMainOrgUnitId()); } else { orgUnitsIds.addAll(auth().getOrgUnitIds()); } command.setOrgUnitsIds(orgUnitsIds); refreshContactGrid(command); } // --------------------------------------------------------------------------------------------------------- // // UTILITY METHODS. // // --------------------------------------------------------------------------------------------------------- /** * Applies the store filters. */ private void applyContactFilters() { view.getStore().applyFilters(null); } /** * Refreshes the contacts grid with the current parameters. * * @param cmd * The {@link GetContacts} command to execute. */ private void refreshContactGrid(final GetContacts cmd) { // Checks that the user can view contacts. if (!ProfileUtils.isGranted(auth(), GlobalPermissionEnum.VIEW_VISIBLE_CONTACTS)) { return; } if (cmd == null) { return; } if (currentlyLoading) { commandQueue.add(cmd); return; } currentlyLoading = true; view.clearContacts(); // -- // Builds a new chunks worker. // -- final GetContactsWorker worker = new GetContactsWorker(dispatch, cmd, view.getContactsPanel()); worker.addWorkerListener(new GetContactsWorker.WorkerListener() { private int chunk = 0; @Override public void serverError(final Throwable error) { if (Log.isErrorEnabled()) { Log.error("Error while getting contacts by chunks.", error); } applyContactFilters(); N10N.warn(I18N.CONSTANTS.error(), I18N.CONSTANTS.refreshContactListError()); } @Override public void chunkRetrieved(final DashboardContact contact) { view.addContact(contact); Profiler.INSTANCE.markCheckpoint(Scenario.LOGIN, "Chunk #" + (chunk++) + " loaded."); } @Override public void ended() { applyContactFilters(); view.getContactsPanel().layout(); currentlyLoading = false; // Try to execute the next loader refreshContactGrid(commandQueue.poll()); } }); // -- // Runs the worker. // -- view.getStore().removeAll(); view.getStore().clearFilters(); Profiler.INSTANCE.markCheckpoint(Scenario.LOGIN, "Before contact loading."); worker.run(); } private void showContactCreator() { final Window window = new Window(); window.setPlain(true); window.setModal(true); window.setBlinkModal(true); window.setLayout(new FitLayout()); window.setSize(700, 300); window.setHeadingHtml(I18N.CONSTANTS.createContactDialogTitle()); final ComboBox<ContactModelDTO> contactModelComboBox = Forms.combobox(I18N.CONSTANTS.contactModelLabel(), true, ContactModelDTO.ID, ContactModelDTO.NAME); final TextField<String> emailField = Forms.text(I18N.CONSTANTS.contactEmailAddress(), false); final TextField<String> firstNameField = Forms.text(I18N.CONSTANTS.contactFirstName(), false); final TextField<String> familyNameField = Forms.text(I18N.CONSTANTS.contactFamilyName(), false); final TextField<String> organizationNameField = Forms.text(I18N.CONSTANTS.contactOrganizationName(), false); firstNameField.setVisible(false); familyNameField.setVisible(false); organizationNameField.setVisible(false); final ComboBox<OrgUnitDTO> mainOrgUnitComboBox = Forms.combobox(I18N.CONSTANTS.contactMainOrgUnit(), true, OrgUnitDTO.ID, OrgUnitDTO.FULL_NAME); final ListComboBox<OrgUnitDTO> secondaryOrgUnitsComboBox = new ListComboBox<OrgUnitDTO>(OrgUnitDTO.ID, OrgUnitDTO.FULL_NAME); secondaryOrgUnitsComboBox.initComponent(); final AdapterField secondaryOrgUnitsFieldAdapter = Forms.adapter(I18N.CONSTANTS.contactSecondaryOrgUnits(), secondaryOrgUnitsComboBox); secondaryOrgUnitsFieldAdapter.setVisible(false); dispatch.execute(new GetContactModels(null, Collections.<Integer>emptySet(), true), new AsyncCallback<ListResult<ContactModelDTO>>() { @Override public void onFailure(Throwable caught) { Log.error("Error while retrieving contact models for contact creation dialog."); } @Override public void onSuccess(ListResult<ContactModelDTO> result) { contactModelComboBox.getStore().add(result.getList()); } }); dispatch.execute(new GetOrgUnits(OrgUnitDTO.Mode.WITH_TREE), new AsyncCallback<ListResult<OrgUnitDTO>>() { @Override public void onFailure(Throwable caught) { Log.error("Error while retrieving org units for contact creation dialog."); } @Override public void onSuccess(ListResult<OrgUnitDTO> result) { for (OrgUnitDTO orgUnitDTO : result.getData()) { fillOrgUnitsComboboxes(orgUnitDTO, mainOrgUnitComboBox, secondaryOrgUnitsComboBox); } } }); mainOrgUnitComboBox.addSelectionChangedListener(new SelectionChangedListener<OrgUnitDTO>() { @Override public void selectionChanged(SelectionChangedEvent<OrgUnitDTO> se) { if (se.getSelectedItem() == null) { secondaryOrgUnitsFieldAdapter.setVisible(false); return; } secondaryOrgUnitsFieldAdapter.setVisible(true); } }); contactModelComboBox.addSelectionChangedListener(new SelectionChangedListener<ContactModelDTO>() { @Override public void selectionChanged(SelectionChangedEvent<ContactModelDTO> event) { ContactModelType currentType = null; if (event.getSelectedItem() != null) { currentType = event.getSelectedItem().getType(); } firstNameField.setVisible(currentType == ContactModelType.INDIVIDUAL); familyNameField.setVisible(currentType == ContactModelType.INDIVIDUAL); organizationNameField.setVisible(currentType == ContactModelType.ORGANIZATION); firstNameField.setAllowBlank(currentType != ContactModelType.INDIVIDUAL); familyNameField.setAllowBlank(currentType != ContactModelType.INDIVIDUAL); organizationNameField.setAllowBlank(currentType != ContactModelType.ORGANIZATION); } }); org.sigmah.client.ui.widget.button.Button button = Forms.button(I18N.CONSTANTS.createContact()); final FormPanel formPanel = Forms.panel(200); formPanel.add(contactModelComboBox); formPanel.add(emailField); formPanel.add(firstNameField); formPanel.add(familyNameField); formPanel.add(organizationNameField); formPanel.add(mainOrgUnitComboBox); formPanel.add(secondaryOrgUnitsFieldAdapter); formPanel.getButtonBar().add(button); button.addSelectionListener(new SelectionListener<ButtonEvent>() { @Override public void componentSelected(ButtonEvent event) { if (!formPanel.isValid()) { return; } createContactHandler.handleContactCreation(contactModelComboBox.getValue(), emailField.getValue(), firstNameField.getValue(), familyNameField.getValue(), organizationNameField.getValue(), mainOrgUnitComboBox.getValue(), secondaryOrgUnitsComboBox.getListStore().getModels()); window.hide(); } }); window.add(formPanel); window.show(); } private void fillOrgUnitsComboboxes(OrgUnitDTO unit, final ComboBox<OrgUnitDTO> mainOrgUnitComboBox, final ListComboBox<OrgUnitDTO> secondaryOrgUnitsComboBox) { mainOrgUnitComboBox.getStore().add(unit); secondaryOrgUnitsComboBox.getAvailableValuesStore().add(unit); final Set<OrgUnitDTO> children = unit.getChildrenOrgUnits(); if (children != null && !children.isEmpty()) { for (final OrgUnitDTO child : children) { fillOrgUnitsComboboxes(child, mainOrgUnitComboBox, secondaryOrgUnitsComboBox); } } } }