/* * 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. * * 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.common; import org.apache.commons.lang3.Validate; import org.joda.time.LocalDate; import org.libreplan.business.common.IAdHocTransactionService; import org.libreplan.business.common.IOnTransaction; import org.libreplan.business.common.Registry; import org.libreplan.business.common.daos.IConfigurationDAO; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.TaskSource; import org.libreplan.business.planner.daos.ITaskSourceDAO; import org.libreplan.business.planner.entities.Dependency; import org.libreplan.business.planner.entities.Dependency.Type; import org.libreplan.business.planner.entities.Task; import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.resources.daos.IResourcesSearcher; import org.libreplan.business.scenarios.daos.IOrderVersionDAO; import org.libreplan.business.scenarios.daos.IScenarioDAO; import org.libreplan.business.scenarios.entities.OrderVersion; import org.libreplan.business.scenarios.entities.Scenario; import org.libreplan.business.users.daos.IUserDAO; import org.libreplan.business.users.entities.User; import org.libreplan.web.UserUtil; import org.libreplan.web.planner.tabs.GanttDiagramBuilder; import org.libreplan.web.security.SecurityUtils; import org.libreplan.web.users.bootstrap.PredefinedUsers; import org.libreplan.web.users.services.CustomUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.zkoss.ganttz.data.ConstraintCalculator; import org.zkoss.ganttz.data.DependencyType; import org.zkoss.ganttz.data.DependencyType.Point; import org.zkoss.ganttz.data.GanttDate; import org.zkoss.ganttz.data.IDependency; import org.zkoss.ganttz.data.constraint.Constraint; import org.zkoss.ganttz.util.LongOperationFeedback; import org.zkoss.ganttz.util.LongOperationFeedback.IBackGroundOperation; import org.zkoss.ganttz.util.LongOperationFeedback.IDesktopUpdate; import org.zkoss.ganttz.util.LongOperationFeedback.IDesktopUpdatesEmitter; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Execution; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.util.Clients; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map.Entry; import java.util.Set; import static org.libreplan.web.I18nHelper._; /** * Model to manage UI operations from main template. * * @author Manuel Rego Casasnovas <mrego@igalia.com> */ @Service @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class TemplateModel implements ITemplateModel { @Autowired private IScenarioDAO scenarioDAO; @Autowired private IOrderVersionDAO orderVersionDAO; @Autowired private ITaskSourceDAO taskSourceDAO; @Autowired private IUserDAO userDAO; @Autowired private IResourcesSearcher resourcesSearcher; @Autowired private IAdHocTransactionService transactionService; @Autowired private IConfigurationDAO configurationDAO; public static class DependencyWithVisibility implements IDependency<TaskElement> { private final TaskElement source; private final TaskElement destination; private final DependencyType type; private final boolean visible; private DependencyWithVisibility(TaskElement source, TaskElement destination, DependencyType type, boolean visible) { Validate.notNull(source); Validate.notNull(destination); Validate.notNull(type); this.source = source; this.destination = destination; this.type = type; this.visible = visible; } static DependencyWithVisibility createInvisible(TaskElement source, TaskElement destination, DependencyType type) { return new DependencyWithVisibility(source, destination, type, false); } public static DependencyWithVisibility existent(Dependency each) { return new DependencyWithVisibility( each.getOrigin(), each.getDestination(), toGraphicalType(each.getType()), true); } static List<Constraint<GanttDate>> getConstraints(ConstraintCalculator<TaskElement> calculator, Set<DependencyWithVisibility> withDependencies, Point point) { List<Constraint<GanttDate>> result = new ArrayList<>(); for (DependencyWithVisibility each : withDependencies) { result.addAll(calculator.getConstraints(each, point)); } return result; } public boolean isVisible() { return visible; } @Override public TaskElement getSource() { return source; } @Override public TaskElement getDestination() { return destination; } @Override public DependencyType getType() { return type; } private static DependencyType toGraphicalType(Type domainDependencyType) { switch (domainDependencyType) { case END_START: return DependencyType.END_START; case START_START: return DependencyType.START_START; case END_END: return DependencyType.END_END; case START_END: return DependencyType.START_END; default: throw new RuntimeException("can't handle " + domainDependencyType); } } } @Override @Transactional(readOnly = true) public List<Scenario> getScenarios() { return scenarioDAO.getAll(); } @Override @Transactional(readOnly = true) public String getCompanyLogoURL() { return configurationDAO.getConfiguration().getCompanyLogoURL(); } @Override @Transactional public void setScenario(String loginName, Scenario scenario, IOnFinished onFinish) { Scenario scenarioReloaded = reloadScenario(scenario); associateToUser(scenarioReloaded, findUserByLoginName(loginName)); doReassignations(scenarioReloaded, onFinish); } private Scenario reloadScenario(Scenario scenario) { return scenarioDAO.findExistingEntity(scenario.getId()); } private User findUserByLoginName(String loginName) { try { return userDAO.findByLoginName(loginName); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } } private void associateToUser(Scenario scenario, User user) { user.setLastConnectedScenario(scenario); userDAO.save(user); CustomUser customUser = SecurityUtils.getLoggedUser(); assert customUser != null : "user must be logged for this method to be called"; customUser.setScenario(scenario); } private void doReassignations(final Scenario scenario, IOnFinished onFinish) { if (isOnZKExecution()) { doReassignationsWithFeedback(getDesktop(), scenario, onFinish); } else { doReassignations(scenario, LongOperationFeedback.doNothingEmitter()); onFinish.onWithoutErrorFinish(); } } private boolean isOnZKExecution() { Execution current = Executions.getCurrent(); return current != null && current.getDesktop() != null; } private Desktop getDesktop() { return Executions.getCurrent().getDesktop(); } private void doReassignationsWithFeedback(Desktop desktop, final Scenario scenario, final IOnFinished onFinish) { IBackGroundOperation<IDesktopUpdate> reassignations = new IBackGroundOperation<IDesktopUpdate>() { @Override public void doOperation(final IDesktopUpdatesEmitter<IDesktopUpdate> desktopUpdateEmitter) { Exception exceptionHappened = null; try { transactionService.runOnTransaction(new IOnTransaction<Void>() { @Override public Void execute() { doReassignations(reloadScenario(scenario), desktopUpdateEmitter); return null; } }); } catch (Exception e) { exceptionHappened = e; } finally { desktopUpdateEmitter.doUpdate(showEnd()); } if (exceptionHappened == null) { desktopUpdateEmitter.doUpdate(notifySuccess(onFinish)); } else { desktopUpdateEmitter.doUpdate(notifyException(onFinish, exceptionHappened)); } } }; LongOperationFeedback.progressive(desktop, reassignations); } private IDesktopUpdate notifySuccess(final IOnFinished onFinish) { return new IDesktopUpdate() { @Override public void doUpdate() { onFinish.onWithoutErrorFinish(); } }; } private IDesktopUpdate notifyException(final IOnFinished onFinish, final Exception exceptionHappened) { return new IDesktopUpdate() { @Override public void doUpdate() { onFinish.errorHappened(exceptionHappened); } }; } private void doReassignations(Scenario scenario, IDesktopUpdatesEmitter<IDesktopUpdate> emitter) { List<Entry<Order, OrderVersion>> needingReassignation = scenario.getOrderVersionsNeedingReassignation(); final int total = needingReassignation.size(); if (!needingReassignation.isEmpty()) { emitter.doUpdate(showStart(total)); } int i = 1; for (Entry<Order, OrderVersion> each : needingReassignation) { OrderVersion orderVersion = each.getValue(); Order order = each.getKey(); order.useSchedulingDataFor(scenario); if (order.isScheduled()) { doReassignationsOn(order, orderVersion.getOwnerScenario(), scenario); orderVersion.savingThroughOwner(); orderVersionDAO.save(orderVersion); } emitter.doUpdate(showProgress(total - i)); } } private IDesktopUpdate showStart(final int ordersNumber) { return sendMessage(_("Reassigning {0} projects", ordersNumber)); } private IDesktopUpdate showProgress(int remaining) { return sendMessage(_("{0} projects remaining to reassign", remaining)); } private IDesktopUpdate sendMessage(final String message) { return () -> Clients.showBusy((Component) new Object(), message); } private IDesktopUpdate showEnd() { return () -> Clients.showBusy(null, ""); } private void doReassignationsOn(Order order, Scenario from, Scenario to) { copyAssignments(order, from, to); GanttDiagramBuilder.createForcingDependencies( order, TemplateModelAdapter.create( to, asLocalDate(order.getInitDate()), asLocalDate(order.getDeadline()), resourcesSearcher)); doReassignations(order, to); doTheSaving(order); } private LocalDate asLocalDate(Date date) { return date != null ? LocalDate.fromDateFields(date) : null; } private void copyAssignments(Order order, Scenario from, Scenario to) { for (Task each : getTasksFrom(order)) { each.copyAssignmentsFromOneScenarioToAnother(from, to); } } private void doReassignations(Order order, Scenario scenario) { for (Task each : getTasksFrom(order)) { each.reassignAllocationsWithNewResources(scenario, resourcesSearcher); } } private void doTheSaving(Order order) { for (TaskSource each : order.getTaskSourcesFromBottomToTop()) { taskSourceDAO.save(each); } } private List<Task> getTasksFrom(Order order) { List<Task> result = new ArrayList<>(); for (TaskElement each : getTaskElementsFrom(order)) { if (each instanceof Task) { result.add((Task) each); } } return result; } private List<TaskElement> getTaskElementsFrom(Order order) { List<TaskElement> result = new ArrayList<>(); for (TaskSource each : order.getTaskSourcesFromBottomToTop()) { result.add(each.getTask()); } return result; } @Override @Transactional(readOnly = true) public boolean isScenariosVisible() { return configurationDAO.getConfiguration().isScenariosVisible(); } @Override @Transactional(readOnly = true) public boolean hasChangedDefaultPassword(PredefinedUsers user) { return user.hasChangedDefaultPasswordOrDisabled(); } @Override @Transactional(readOnly = true) public boolean adminPasswordChangedAndSomeOtherNotChanged() { return PredefinedUsers.adminChangedAndSomeOtherNotChanged(); } @Override @Transactional(readOnly = true) public String getIdUser(String login) { try { return Registry.getUserDAO().findByLoginName(login).getId().toString(); } catch (InstanceNotFoundException e) { return null; } } @Override @Transactional(readOnly = true) public boolean isUserAdmin() { User user = UserUtil.getUserFromSession(); return user != null && user.isSuperuser(); } @Override @Transactional(readOnly = true) public boolean isCheckNewVersionEnabled() { return configurationDAO.getConfiguration().isCheckNewVersionEnabled(); } }