/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
*
* Copyright (C) 2011 WirelessGalicia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.libreplan.web.planner.tabs;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.daos.IConfigurationDAO;
import org.libreplan.business.orders.daos.IOrderDAO;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.planner.entities.TaskElement;
import org.libreplan.business.resources.daos.IResourcesSearcher;
import org.libreplan.business.templates.entities.OrderTemplate;
import org.libreplan.business.users.entities.UserRole;
import org.libreplan.web.common.ConfirmCloseUtil;
import org.libreplan.web.common.GatheredUsageStats;
import org.libreplan.web.common.entrypoints.EntryPointsHandler;
import org.libreplan.web.common.entrypoints.URLHandlerRegistry;
import org.libreplan.web.dashboard.DashboardController;
import org.libreplan.web.dashboard.DashboardControllerGlobal;
import org.libreplan.web.limitingresources.LimitingResourcesController;
import org.libreplan.web.logs.LogsController;
import org.libreplan.web.montecarlo.MonteCarloController;
import org.libreplan.web.orders.OrderCRUDController;
import org.libreplan.web.planner.allocation.AdvancedAllocationController.IBack;
import org.libreplan.web.planner.company.CompanyPlanningController;
import org.libreplan.web.planner.order.IOrderPlanningGate;
import org.libreplan.web.planner.order.OrderPlanningController;
import org.libreplan.web.planner.order.PlanningStateCreator;
import org.libreplan.web.resourceload.ResourceLoadController;
import org.libreplan.web.security.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.zkoss.ganttz.TabSwitcher;
import org.zkoss.ganttz.TabsRegistry;
import org.zkoss.ganttz.TabsRegistry.IBeforeShowAction;
import org.zkoss.ganttz.adapters.State;
import org.zkoss.ganttz.adapters.TabsConfiguration;
import org.zkoss.ganttz.adapters.TabsConfiguration.ChangeableTab;
import org.zkoss.ganttz.extensions.ITab;
import org.zkoss.ganttz.extensions.TabProxy;
import org.zkoss.ganttz.util.LongOperationFeedback;
import org.zkoss.ganttz.util.LongOperationFeedback.ILongOperation;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zkplus.spring.SpringUtil;
import java.util.Map;
import static org.libreplan.web.I18nHelper._;
import static org.zkoss.ganttz.adapters.TabsConfiguration.configure;
/**
* Creates and handles several tabs.
*
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
* @author Vova Perebykivskyi <vova@libreplan-enterprise.com>
*/
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class MultipleTabsPlannerController implements Composer, IGlobalViewEntryPoints {
/**
* URL used for Email Template functionality
*/
public static String WELCOME_URL = "-- no URL provided --";
public static final String BREADCRUMBS_SEPARATOR = "/common/img/migas_separacion.gif";
private Mode mode = Mode.initial();
private CompanyPlanningController companyPlanningController;
private OrderCRUDController orderCRUDController;
private PlanningStateCreator planningStateCreator;
/**
* Projects Planning.
*/
private TabWithLoadingFeedback planningTab;
/**
* Resources Load.
*/
private ITab resourceLoadTab;
/**
* Queue-based Resources Planning.
*/
private ITab limitingResourcesTab;
/**
* Monte Carlo Method.
*/
private ITab monteCarloTab;
/**
* Projects List.
*/
private ITab ordersTab;
/**
* Advanced Allocation.
*/
private ITab advancedAllocationTab;
/**
* Dashboard.
*/
private ITab dashboardTab;
/**
* Logs.
*/
private ITab logsTab;
private TabSwitcher tabsSwitcher;
private org.zkoss.zk.ui.Component breadcrumbs;
@Autowired
private OrderPlanningController orderPlanningController;
@Autowired
private ResourceLoadController resourceLoadController;
@Autowired
private ResourceLoadController resourceLoadControllerGlobal;
@Autowired
private MonteCarloController monteCarloController;
@Autowired
private LimitingResourcesController limitingResourcesControllerGlobal;
@Autowired
private DashboardController dashboardController;
@Autowired
private DashboardControllerGlobal dashboardControllerGlobal;
@Autowired
private LogsController logsController;
@Autowired
private LogsController logsControllerGlobal;
@Autowired
private IAdHocTransactionService transactionService;
@Autowired
private IOrderDAO orderDAO;
@Autowired
private IResourcesSearcher resourcesSearcher;
@Autowired
private IConfigurationDAO configurationDAO;
@Autowired
private URLHandlerRegistry registry;
private final class TabWithLoadingFeedback extends TabProxy {
private boolean feedback = true;
private TabWithLoadingFeedback(ITab tab) {
super(tab);
}
@Override
public void show() {
if ( feedback ) {
showWithFeedback();
} else {
showWithoutFeedback();
}
}
private void showWithFeedback() {
LongOperationFeedback.execute(tabsSwitcher, new ILongOperation() {
@Override
public String getName() {
return _("changing perspective");
}
@Override
public void doAction() {
proxiedTab.show();
}
});
}
private void showWithoutFeedback() {
proxiedTab.show();
}
public void toggleToNoFeedback() {
feedback = false;
}
public void toggleToFeedback() {
feedback = true;
}
}
public static String getSchedulingLabel() {
return _("Planning");
}
public MultipleTabsPlannerController() {
orderCRUDController = (OrderCRUDController) SpringUtil.getBean("orderCRUDController");
companyPlanningController = (CompanyPlanningController) SpringUtil.getBean("companyPlanningController");
planningStateCreator = (PlanningStateCreator) SpringUtil.getBean("planningStateCreator");
}
private TabsConfiguration buildTabsConfiguration(final Desktop desktop) {
Map<String, String[]> parameters = getURLQueryParametersMap();
mode.addListener((oldType, newType) -> {
switch (newType) {
case GLOBAL:
ConfirmCloseUtil.resetConfirmClose();
break;
case ORDER:
if ( SecurityUtils.loggedUserCanWrite(mode.getOrder()) ) {
ConfirmCloseUtil.setConfirmClose(
desktop,
_("You are about to leave the planning editing. Unsaved changes will be lost!"));
}
break;
default:
break;
}
});
planningTab = doFeedbackOn(PlanningTabCreator.create(
mode, companyPlanningController, orderPlanningController, orderDAO, breadcrumbs, parameters, this));
resourceLoadTab = ResourcesLoadTabCreator.create(
mode,
resourceLoadController,
resourceLoadControllerGlobal,
new IOrderPlanningGate() {
@Override
public void goToScheduleOf(Order order) {
getTabsRegistry().show(planningTab, changeModeTo(order));
}
@Override
public void goToOrderDetails(Order order) {}
@Override
public void goToTaskResourceAllocation(Order order, TaskElement task) {
orderPlanningController.setShowedTask(task);
orderPlanningController
.setCurrentControllerToShow(orderPlanningController.getEditTaskController());
getTabsRegistry().show(planningTab, changeModeTo(order));
}
@Override
public void goToDashboard(Order order) {}
},
breadcrumbs);
limitingResourcesTab = LimitingResourcesTabCreator.create(mode, limitingResourcesControllerGlobal, breadcrumbs);
ordersTab = OrdersTabCreator.create(
mode,
orderCRUDController,
breadcrumbs,
new IOrderPlanningGate() {
@Override
public void goToScheduleOf(Order order) {
getTabsRegistry().show(planningTab, changeModeTo(order));
}
@Override
public void goToOrderDetails(Order order) {
getTabsRegistry().show(ordersTab, changeModeTo(order));
}
@Override
public void goToTaskResourceAllocation(Order order, TaskElement task) {}
@Override
public void goToDashboard(Order order) {}
},
parameters);
dashboardTab = DashboardTabCreator.create(
mode,
planningStateCreator,
dashboardController,
dashboardControllerGlobal,
orderPlanningController,
breadcrumbs,
resourcesSearcher);
logsTab = LogsTabCreator.create(
mode,
logsController,
logsControllerGlobal,
breadcrumbs,
new IOrderPlanningGate() {
@Override
public void goToScheduleOf(Order order) {
getTabsRegistry().show(planningTab, changeModeTo(order));
}
@Override
public void goToOrderDetails(Order order) {
getTabsRegistry().show(ordersTab, changeModeTo(order));
}
@Override
public void goToTaskResourceAllocation(Order order, TaskElement task) {}
@Override
public void goToDashboard(Order order) {}
},
parameters);
final boolean isMontecarloVisible = isMonteCarloVisible();
if (isMontecarloVisible) {
monteCarloTab = MonteCarloTabCreator.create(
mode,
planningStateCreator,
monteCarloController,
orderPlanningController,
breadcrumbs,
resourcesSearcher);
}
final State<Void> typeChanged = typeChangedState();
advancedAllocationTab = doFeedbackOn(AdvancedAllocationTabCreator.create(
mode, transactionService, planningStateCreator, returnToPlanningTab(), breadcrumbs));
TabsConfiguration tabsConfiguration = TabsConfiguration
.create()
.add(tabWithNameReloading(planningTab, typeChanged))
.add(tabWithNameReloading(ordersTab, typeChanged));
if (SecurityUtils.isSuperuserOrUserInRoles(UserRole.ROLE_PLANNING)) {
tabsConfiguration
.add(tabWithNameReloading(resourceLoadTab, typeChanged))
.add(tabWithNameReloading(limitingResourcesTab, typeChanged));
} else {
tabsConfiguration.add(visibleOnlyAtOrderModeWithNameReloading(resourceLoadTab, typeChanged));
}
tabsConfiguration
.add(visibleOnlyAtOrderMode(advancedAllocationTab))
.add(tabWithNameReloading(dashboardTab, typeChanged));
if (isMontecarloVisible) {
tabsConfiguration.add(visibleOnlyAtOrderMode(monteCarloTab));
}
tabsConfiguration.add(tabWithNameReloading(logsTab, typeChanged));
return tabsConfiguration;
}
private boolean isMonteCarloVisible() {
Boolean result = configurationDAO.getConfiguration().isMonteCarloMethodTabVisible();
return result != null && result;
}
@SuppressWarnings("unchecked")
private Map<String, String[]> getURLQueryParametersMap() {
return Executions.getCurrent().getParameterMap();
}
private TabWithLoadingFeedback doFeedbackOn(ITab tab) {
return new TabWithLoadingFeedback(tab);
}
private IBack returnToPlanningTab() {
return new IBack() {
private String eventName = "onShowPlanningTab";
{
tabsSwitcher.addEventListener(eventName, showPlanningTab());
}
private EventListener showPlanningTab() {
return event -> getTabsRegistry().show(planningTab);
}
@Override
public void goBack() {
notGoBackImmediately();
}
private void notGoBackImmediately() {
Events.postEvent(new Event(eventName, tabsSwitcher));
}
@Override
public boolean isAdvanceAssignmentOfSingleTask() {
return false;
}
};
}
private ChangeableTab tabWithNameReloading(ITab tab, final State<Void> typeChanged) {
return configure(tab).reloadNameOn(typeChanged);
}
private State<Void> typeChangedState() {
final State<Void> typeChanged = State.create();
mode.addListener((oldType, newType) -> typeChanged.changeValueTo(null));
return typeChanged;
}
private ChangeableTab visibleOnlyAtOrderMode(ITab tab) {
return visibleOnlyAtOrderModeWithNameReloading(tab, null);
}
private ChangeableTab visibleOnlyAtOrderModeWithNameReloading(ITab tab, final State<Void> typeChanged) {
final State<Boolean> state = State.create(mode.isOf(ModeType.ORDER));
ChangeableTab result;
if (typeChanged == null) {
result = configure(tab).visibleOn(state);
} else {
result = configure(tab).visibleOn(state).reloadNameOn(typeChanged);
}
mode.addListener((oldType, newType) -> state.changeValueTo(ModeType.ORDER == newType));
return result;
}
@Override
@Transactional(readOnly=true)
public void doAfterCompose(org.zkoss.zk.ui.Component comp) {
Execution execution = Executions.getCurrent();
WELCOME_URL = "http://" + execution.getServerName() + ":" +
execution.getServerPort() + Executions.encodeURL("/planner/index.zul");
tabsSwitcher = (TabSwitcher) comp;
breadcrumbs = comp.getPage().getFellow("breadcrumbs");
tabsSwitcher.setConfiguration(buildTabsConfiguration(comp.getDesktop()));
final EntryPointsHandler<IGlobalViewEntryPoints> handler =
registry.getRedirectorFor(IGlobalViewEntryPoints.class);
if (!handler.applyIfMatches(this)) {
planningTab.toggleToNoFeedback();
goToCompanyScheduling();
planningTab.toggleToFeedback();
}
handler.registerBookmarkListener(this, comp.getPage());
if (SecurityUtils.isSuperuserOrUserInRoles(UserRole.ROLE_CREATE_PROJECTS)) {
org.zkoss.zk.ui.Component createOrderButton = comp.getPage().getFellowIfAny("createOrderButton");
if (createOrderButton != null) {
createOrderButton.addEventListener(Events.ON_CLICK, event -> goToCreateForm());
}
}
// Send data to server
if ( !SecurityUtils.isGatheredStatsAlreadySent &&
configurationDAO.getConfiguration().isAllowedToGatherUsageStatsEnabled() ) {
sendDataToServer();
}
}
private void sendDataToServer() {
GatheredUsageStats gatheredUsageStats = new GatheredUsageStats();
gatheredUsageStats.sendGatheredUsageStatsToServer();
SecurityUtils.isGatheredStatsAlreadySent = true;
}
private TabsRegistry getTabsRegistry() {
return tabsSwitcher.getTabsRegistry();
}
@Override
public void goToCompanyScheduling() {
LogsController.goToGlobalMode();
getTabsRegistry().show(planningTab);
}
@Override
public void goToCompanyLoad() {
getTabsRegistry().show(resourceLoadTab);
}
@Override
public void goToCompanyLimitingResources() {
getTabsRegistry().show(limitingResourcesTab);
}
@Override
public void goToOrdersList() {
getTabsRegistry().show(ordersTab);
}
public void goToCreateForm() {
orderCRUDController.prepareForCreate(tabsSwitcher.getDesktop());
orderCRUDController.getCreationPopup().showWindow(orderCRUDController, this);
}
@Override
public void goToOrder(Order order) {
planningTab.toggleToNoFeedback();
getTabsRegistry().show(planningTab, changeModeTo(order));
planningTab.toggleToFeedback();
}
@Override
public void goToOrderElementDetails(Order order, OrderElement orderElement) {
getTabsRegistry().show(ordersTab, changeModeTo(order));
orderCRUDController.highLight(orderElement);
}
@Override
public void goToLimitingResources() {
getTabsRegistry().show(limitingResourcesTab);
}
@Override
public void goToOrderDetails(Order order) {
getTabsRegistry().show(ordersTab, changeModeTo(order));
}
@Override
public void goToResourcesLoad(Order order) {
getTabsRegistry().show(resourceLoadTab, changeModeTo(order));
}
@Override
public void goToAdvancedAllocation(Order order) {
getTabsRegistry().show(advancedAllocationTab, changeModeTo(order));
}
@Override
public void goToCreateOtherOrderFromTemplate(OrderTemplate template) {
getTabsRegistry().show(ordersTab);
orderCRUDController.showCreateFormFromTemplate(template);
}
@Override
public void goToAdvanceTask(Order order,TaskElement task) {
orderPlanningController.setShowedTask(task);
orderPlanningController.setCurrentControllerToShow(
orderPlanningController.getAdvanceAssignmentPlanningController());
getTabsRegistry().show(planningTab, changeModeTo(order));
}
private IBeforeShowAction changeModeTo(final Order order) {
/* Makes possible to show Risk/Issue Logs only for specific Order */
LogsController.goToOrderMode(order);
return () -> mode.goToOrderMode(order);
}
}