package org.sigmah.client.ui.presenter.orgunit; /* * #%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.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.extjs.gxt.ui.client.widget.Layout; import org.sigmah.client.dispatch.CommandResultHandler; import org.sigmah.client.dispatch.DispatchQueue; import org.sigmah.client.dispatch.monitor.LoadingMask; import org.sigmah.client.i18n.I18N; import org.sigmah.client.inject.Injector; import org.sigmah.client.page.Page; import org.sigmah.client.page.PageRequest; import org.sigmah.client.page.RequestParameter; import org.sigmah.client.ui.notif.N10N; import org.sigmah.client.ui.view.orgunit.OrgUnitDetailsView; import org.sigmah.client.ui.widget.button.Button; import org.sigmah.client.ui.widget.form.Forms; import org.sigmah.client.ui.widget.form.IterableGroupPanel; import org.sigmah.client.ui.widget.form.IterableGroupPanel.IterableGroupItem; import org.sigmah.client.ui.widget.layout.Layouts; import org.sigmah.client.util.ClientUtils; import org.sigmah.shared.command.GetLayoutGroupIterations; import org.sigmah.shared.command.GetValue; import org.sigmah.shared.command.UpdateLayoutGroupIterations; import org.sigmah.shared.command.UpdateLayoutGroupIterations.IterationChange; import org.sigmah.shared.command.UpdateProject; import org.sigmah.shared.command.result.ListResult; import org.sigmah.shared.command.result.ValueResult; import org.sigmah.shared.command.result.VoidResult; import org.sigmah.shared.dto.OrgUnitDetailsDTO; import org.sigmah.shared.dto.element.BudgetElementDTO; import org.sigmah.shared.dto.element.BudgetSubFieldDTO; import org.sigmah.shared.dto.element.DefaultFlexibleElementDTO; import org.sigmah.shared.dto.element.FlexibleElementContainer; import org.sigmah.shared.dto.element.FlexibleElementDTO; import org.sigmah.shared.dto.element.event.RequiredValueEvent; import org.sigmah.shared.dto.element.event.RequiredValueHandler; import org.sigmah.shared.dto.element.event.ValueEvent; import org.sigmah.shared.dto.element.event.ValueHandler; import org.sigmah.shared.dto.layout.LayoutConstraintDTO; import org.sigmah.shared.dto.layout.LayoutDTO; import org.sigmah.shared.dto.layout.LayoutGroupDTO; import org.sigmah.shared.dto.layout.LayoutGroupIterationDTO; import org.sigmah.shared.dto.orgunit.OrgUnitDTO; import org.sigmah.shared.dto.referential.GlobalPermissionEnum; import org.sigmah.shared.servlet.ServletConstants.Servlet; import org.sigmah.shared.servlet.ServletConstants.ServletMethod; import org.sigmah.shared.servlet.ServletUrlBuilder; import org.sigmah.shared.util.ProfileUtils; import org.sigmah.shared.util.ValueResultUtils; import com.allen_sauer.gwt.log.client.Log; 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.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Label; import com.extjs.gxt.ui.client.widget.form.FieldSet; import com.extjs.gxt.ui.client.widget.layout.FormData; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Widget; import com.google.inject.ImplementedBy; import com.google.inject.Inject; import com.google.inject.Singleton; import org.sigmah.client.computation.ComputationTriggerManager; import org.sigmah.client.event.UpdateEvent; /** * OrgUnit Details Presenter. */ @Singleton public class OrgUnitDetailsPresenter extends AbstractOrgUnitPresenter<OrgUnitDetailsPresenter.View> implements IterableGroupPanel.Delegate { /** * Presenter's view interface. */ @ImplementedBy(OrgUnitDetailsView.class) public static interface View extends AbstractOrgUnitPresenter.View { ContentPanel getContentOrgUnitDetailsPanel(); Button getSaveButton(); Button getExcelExportButton(); void setMainPanelWidget(final Widget widget); } /** * List of values changes. */ private List<ValueEvent> valueChanges; private final Map<Integer, IterationChange> iterationChanges = new HashMap<Integer, IterationChange>(); private final Map<Integer, IterableGroupItem> newIterationsTabItems = new HashMap<Integer, IterableGroupItem>(); /** * Listen to the values of flexible elements to update computated values. */ @Inject private ComputationTriggerManager computationTriggerManager; @Inject protected OrgUnitDetailsPresenter(View view, Injector injector) { super(view, injector); } /** * {@inheritDoc} */ @Override public Page getPage() { return Page.ORGUNIT_DETAILS; } /** * {@inheritDoc} */ @Override public void onBind() { // Initialization. valueChanges = new ArrayList<ValueEvent>(); // Save action. view.getSaveButton().addListener(Events.OnClick, new Listener<ButtonEvent>() { @Override public void handleEvent(ButtonEvent be) { view.getSaveButton().disable(); onSaveAction(); } }); // Excel action. view.getExcelExportButton().addListener(Events.OnClick, new Listener<ButtonEvent>() { @Override public void handleEvent(final ButtonEvent be) { final ServletUrlBuilder urlBuilder = new ServletUrlBuilder(injector.getAuthenticationProvider(), injector.getPageManager(), Servlet.EXPORT, ServletMethod.EXPORT_ORG_UNIT); urlBuilder.addParameter(RequestParameter.ID, getOrgUnit().getId()); ClientUtils.launchDownload(urlBuilder.toString()); } }); } /** * {@inheritDoc} */ @Override public void onPageRequest(final PageRequest request) { // clear changes valueChanges.clear(); view.getSaveButton().disable(); // load view details load(getOrgUnit().getOrgUnitModel().getDetails()); } /** * {@inheritDoc} */ @Override protected boolean hasValueChanged() { return !valueChanges.isEmpty() || !iterationChanges.isEmpty(); } /** * Loads the presenter with the org unit details. * * @param details * The details. */ private void load(OrgUnitDetailsDTO details) { // Prepare the manager of computation elements computationTriggerManager.prepareForOrgUnit(getOrgUnit()); // Clear panel. view.getContentOrgUnitDetailsPanel().removeAll(); // Layout. final LayoutDTO layout = details.getLayout(); // Counts elements. int count = 0; for (final LayoutGroupDTO groupDTO : layout.getGroups()) { count += groupDTO.getConstraints().size(); } if (count == 0) { // Default details page. view.setMainPanelWidget(new Label(I18N.CONSTANTS.projectDetailsNoDetails())); return; } final Grid gridLayout = new Grid(layout.getRowsCount(), layout.getColumnsCount()); gridLayout.setCellPadding(0); gridLayout.setCellSpacing(0); gridLayout.setWidth("100%"); final DispatchQueue queue = new DispatchQueue(dispatch, true); for (final LayoutGroupDTO groupLayout : layout.getGroups()) { // simple group if(!groupLayout.getHasIterations()) { FieldSet fieldSet = createGroupLayoutFieldSet(getOrgUnit(), groupLayout, queue, null, null, null); fieldSet.setHeadingHtml(groupLayout.getTitle()); fieldSet.setCollapsible(true); fieldSet.setBorders(true); gridLayout.setWidget(groupLayout.getRow(), groupLayout.getColumn(), fieldSet); continue; } final FieldSet fieldSet = (FieldSet) groupLayout.getWidget(); gridLayout.setWidget(groupLayout.getRow(), groupLayout.getColumn(), fieldSet); // iterative group final IterableGroupPanel tabPanel = Forms.iterableGroupPanel(dispatch, groupLayout, getOrgUnit(), ProfileUtils.isGranted(auth(), GlobalPermissionEnum.CREATE_ITERATIONS)); tabPanel.setDelegate(this); fieldSet.add(tabPanel); tabPanel.setAutoHeight(true); tabPanel.setAutoWidth(true); tabPanel.setTabScroll(true); tabPanel.addStyleName("white-tab-body"); tabPanel.setBorders(true); tabPanel.setBodyBorder(false); GetLayoutGroupIterations getIterations = new GetLayoutGroupIterations(groupLayout.getId(), getOrgUnit().getId(), -1); queue.add(getIterations, new CommandResultHandler<ListResult<LayoutGroupIterationDTO>>() { @Override public void onCommandFailure(final Throwable throwable) { if (Log.isErrorEnabled()) { Log.error("Error, layout group iterations not loaded.", throwable); } throw new RuntimeException(throwable); } @Override protected void onCommandSuccess(ListResult<LayoutGroupIterationDTO> result) { DispatchQueue iterationsQueue = new DispatchQueue(dispatch, true); for(final LayoutGroupIterationDTO iteration : result.getList()) { final IterableGroupItem tab = new IterableGroupItem(tabPanel, iteration.getId(), iteration.getName()); tabPanel.addIterationTab(tab); Layout tabLayout = Layouts.fitLayout(); tab.setLayout(tabLayout); FieldSet tabSet = createGroupLayoutFieldSet(getOrgUnit(), groupLayout, iterationsQueue, iteration == null ? null : iteration.getId(), tabPanel, tab); tab.add(tabSet); } iterationsQueue.start(); if(tabPanel.getItemCount() > 0) { tabPanel.setSelection(tabPanel.getItem(0)); } } }, new LoadingMask(view.getContentOrgUnitDetailsPanel())); fieldSet.layout(); } queue.start(); view.setMainPanelWidget(gridLayout); } @Override public IterationChange getIterationChange(int iterationId) { return iterationChanges.get(iterationId); } @Override public void setIterationChange(IterationChange iterationChange) { iterationChanges.put(iterationChange.getIterationId(), iterationChange); view.getSaveButton().enable(); } @Override public void addIterationTabItem(int iterationId, IterableGroupItem tab) { newIterationsTabItems.put(iterationId, tab); } @Override public FieldSet createGroupLayoutFieldSet(FlexibleElementContainer container, LayoutGroupDTO groupLayout, DispatchQueue queue, final Integer iterationId, final IterableGroupPanel tabPanel, final IterableGroupItem tabItem) { final OrgUnitDTO orgUnit = (OrgUnitDTO)container; // Creates the fieldset and positions it. final FieldSet fieldSet = (FieldSet) groupLayout.getWidget(); // For each constraint in the current layout group. if (ClientUtils.isEmpty(groupLayout.getConstraints())) { return fieldSet; } for (final LayoutConstraintDTO constraintDTO : groupLayout.getConstraints()) { // Gets the element managed by this constraint. final FlexibleElementDTO elementDTO = constraintDTO.getFlexibleElementDTO(); // -- // -- DISABLED ELEMENTS // -- if(elementDTO.isDisabled()) { continue; } // -- // -- ELEMENT VALUE // -- // Remote call to ask for this element value. GetValue getValue; getValue = new GetValue(orgUnit.getId(), elementDTO.getId(), elementDTO.getEntityName(), null, iterationId); queue.add(getValue, new CommandResultHandler<ValueResult>() { @Override public void onCommandFailure(final Throwable throwable) { if (Log.isErrorEnabled()) { Log.error("Error, element value not loaded.", throwable); } throw new RuntimeException(throwable); } @Override public void onCommandSuccess(final ValueResult valueResult) { if (Log.isDebugEnabled()) { Log.debug("Element value(s) object : " + valueResult); } // -- // -- ELEMENT COMPONENT // -- // Configures the flexible element for the current application state before generating its component. elementDTO.setService(dispatch); elementDTO.setAuthenticationProvider(injector.getAuthenticationProvider()); elementDTO.setEventBus(eventBus); elementDTO.setCache(injector.getClientCache()); elementDTO.setCurrentContainerDTO(orgUnit); elementDTO.setTransfertManager(injector.getTransfertManager()); elementDTO.assignValue(valueResult); elementDTO.setTabPanel(tabPanel); // Generates element component (with the value). elementDTO.init(); final Component elementComponent = elementDTO.getElementComponent(valueResult); // Component width. final FormData formData; if (elementDTO.getPreferredWidth() == 0) { formData = new FormData("100%"); } else { formData = new FormData(elementDTO.getPreferredWidth(), -1); } if (elementComponent != null) { fieldSet.add(elementComponent, formData); } fieldSet.layout(); // -- // -- ELEMENT HANDLERS // -- // Adds a value change handler if this element is a dependency of a ComputationElementDTO. computationTriggerManager.listenToValueChangesOfElement(elementDTO, elementComponent, valueChanges); // Adds a value change handler to this element. elementDTO.addValueHandler(new ValueHandler() { @Override public void onValueChange(final ValueEvent event) { if(tabPanel != null) { event.setIterationId(tabPanel.getCurrentIterationId()); } // TODO: Find linked computation fields if any and recompute the value. // Stores the change to be saved later. valueChanges.add(event); // Enables the save action. view.getSaveButton().enable(); } }); if(elementDTO.getValidates() && tabItem != null) { tabItem.setElementValidity(elementDTO, elementDTO.isCorrectRequiredValue(valueResult)); tabItem.refreshTitle(); elementDTO.addRequiredValueHandler(new RequiredValueHandlerImpl(elementDTO)); } } }, new LoadingMask(view.getContentOrgUnitDetailsPanel())); } fieldSet.setCollapsible(false); fieldSet.setAutoHeight(true); fieldSet.setBorders(false); fieldSet.setHeadingHtml(""); return fieldSet; } /** * Internal class handling the value changes of the required elements. */ private class RequiredValueHandlerImpl implements RequiredValueHandler { private final FlexibleElementDTO elementDTO; public RequiredValueHandlerImpl(FlexibleElementDTO elementDTO) { this.elementDTO = elementDTO; } @Override public void onRequiredValueChange(RequiredValueEvent event) { // Refresh the panel's header elementDTO.getTabPanel().setElementValidity(elementDTO, event.isValueOn()); elementDTO.getTabPanel().validateElements(); } } /** * Method executed on save button action. */ private void onSaveAction() { // Checks if there are any changes regarding layout group iterations dispatch.execute(new UpdateLayoutGroupIterations(new ArrayList<IterationChange>(iterationChanges.values()), getOrgUnit().getId()), new CommandResultHandler<ListResult<IterationChange>>() { @Override public void onCommandFailure(final Throwable caught) { N10N.error(I18N.CONSTANTS.save(), I18N.CONSTANTS.saveError()); } @Override protected void onCommandSuccess(ListResult<IterationChange> result) { for(IterationChange iterationChange : result.getList()) { if(iterationChange.isDeleted()) { // remove corresponding valueEvents Iterator<ValueEvent> valuesIterator = valueChanges.iterator(); while(valuesIterator.hasNext()) { ValueEvent valueEvent = valuesIterator.next(); if(valueEvent.getIterationId() == iterationChange.getIterationId()) { valuesIterator.remove(); } } } else if(iterationChange.isCreated()) { // change ids in valueEvents int oldId = iterationChange.getIterationId(); int newId = iterationChange.getNewIterationId(); // updating tabitem id newIterationsTabItems.get(oldId).setIterationId(newId); for(ValueEvent valueEvent : valueChanges) { if(valueEvent.getIterationId() == oldId) { valueEvent.setIterationId(newId); } } } } iterationChanges.clear(); newIterationsTabItems.clear(); dispatch.execute(new UpdateProject(getOrgUnit().getId(), valueChanges), new CommandResultHandler<VoidResult>() { @Override public void onCommandFailure(final Throwable caught) { N10N.error(I18N.CONSTANTS.save(), I18N.CONSTANTS.saveError()); } @Override public void onCommandSuccess(final VoidResult result) { N10N.infoNotif(I18N.CONSTANTS.infoConfirmation(), I18N.CONSTANTS.saveConfirm()); // Checks if there is any update needed to the local project instance. for (final ValueEvent event : valueChanges) { if (event.getSource() instanceof DefaultFlexibleElementDTO) { updateCurrentProject(((DefaultFlexibleElementDTO) event.getSource()), event.getSingleValue()); } } valueChanges.clear(); eventBus.fireEvent(new UpdateEvent(UpdateEvent.VALUE_UPDATE, getOrgUnit())); } }, view.getSaveButton(), view.getExcelExportButton(), new LoadingMask(view.getContentOrgUnitDetailsPanel())); } }, view.getSaveButton(), view.getExcelExportButton(), new LoadingMask(view.getContentOrgUnitDetailsPanel())); } /** * Updates locally the DTO to avoid a remote server call. * * @param element * The default flexible element. * @param value * The new value. */ private void updateCurrentProject(DefaultFlexibleElementDTO element, String value) { final OrgUnitDTO currentOrgUnitDTO = getOrgUnit(); switch (element.getType()) { case CODE: currentOrgUnitDTO.setName(value); break; case TITLE: currentOrgUnitDTO.setFullName(value); break; case BUDGET: try { final BudgetElementDTO budgetElement = (BudgetElementDTO) element; final Map<Integer, String> values = ValueResultUtils.splitMapElements(value); double plannedBudget = 0.0; double spendBudget = 0.0; double receivedBudget = 0.0; for (final BudgetSubFieldDTO bf : budgetElement.getBudgetSubFields()) { if (bf.getType() != null) { switch (bf.getType()) { case PLANNED: plannedBudget = Double.parseDouble(values.get(bf.getId())); break; case RECEIVED: receivedBudget = Double.parseDouble(values.get(bf.getId())); break; case SPENT: spendBudget = Double.parseDouble(values.get(bf.getId())); break; default: break; } } } currentOrgUnitDTO.setPlannedBudget(plannedBudget); currentOrgUnitDTO.setSpendBudget(spendBudget); currentOrgUnitDTO.setReceivedBudget(receivedBudget); } catch (final Exception e) { // nothing, invalid budget. } break; default: // Nothing, non managed type. break; } } }