package org.sigmah.server.handler; /* * #%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.List; import javax.persistence.Query; import javax.persistence.TypedQuery; import org.sigmah.server.dispatch.impl.UserDispatch.UserExecutionContext; import org.sigmah.server.domain.ContactModel; import org.sigmah.server.domain.OrgUnitModel; import org.sigmah.server.domain.Phase; import org.sigmah.server.domain.PhaseModel; import org.sigmah.server.domain.Project; import org.sigmah.server.domain.ProjectFunding; import org.sigmah.server.domain.ProjectModel; import org.sigmah.server.domain.UserDatabase; import org.sigmah.server.domain.report.ProjectReport; import org.sigmah.server.domain.report.ProjectReportVersion; import org.sigmah.server.domain.report.RichTextElement; import org.sigmah.server.domain.util.Deleteable; import org.sigmah.server.domain.value.Value; import org.sigmah.server.handler.base.AbstractCommandHandler; import org.sigmah.shared.command.Delete; import org.sigmah.shared.command.result.VoidResult; import org.sigmah.shared.dto.ContactModelDTO; import org.sigmah.shared.dto.OrgUnitModelDTO; import org.sigmah.shared.dto.PhaseModelDTO; import org.sigmah.shared.dto.ProjectModelDTO; import org.sigmah.shared.dto.referential.ProjectModelStatus; import com.google.inject.Inject; import com.google.inject.persist.Transactional; import java.util.Objects; import org.sigmah.server.domain.User; import org.sigmah.server.domain.value.File; import org.sigmah.server.domain.value.FileVersion; import org.sigmah.server.handler.util.Conflicts; import org.sigmah.server.i18n.I18nServer; import org.sigmah.shared.Language; import org.sigmah.shared.dispatch.CommandException; import org.sigmah.shared.dispatch.UpdateConflictException; /** * Handler for {@link Delete} command. * * @author Alex Bertram (v1.3) * @author Maxime Lombard (mlombard@ideia.fr) (v2.0) * @author Raphaƫl Calabro (rcalabro@ideia.fr) * * @see org.sigmah.shared.command.Delete * @see org.sigmah.server.domain.util.Deleteable */ @SuppressWarnings("unchecked") public class DeleteHandler extends AbstractCommandHandler<Delete, VoidResult> { @Inject private Conflicts conflicts; @Inject private I18nServer i18nServer; /** * {@inheritDoc} */ @Override public VoidResult execute(final Delete cmd, final UserExecutionContext context) throws CommandException { // TODO check permissions for delete! // These handler should redirect to one of the Entity policy classes. final Class<? extends Deleteable> entityClass = entityClassForEntityName(cmd.getEntityName()); performDelete(cmd, entityClass, context); return null; } /** * Delete the given objets. * * @param cmd Command defining what to delete. * @param entityClass Type of the entity to delete. * @param context Execution context. * * @throws org.sigmah.shared.dispatch.CommandException If the object can't be deleted. */ @Transactional(rollbackOn = CommandException.class) protected void performDelete(final Delete cmd, final Class<? extends Deleteable> entityClass, final UserExecutionContext context) throws CommandException { if (ProjectModelStatus.DRAFT.equals(cmd.getProjectModelStatus()) && ProjectModelDTO.ENTITY_NAME.equals(cmd.getEntityName())) { // Delete draft project model final ProjectModel projectModel = (ProjectModel) em().find(entityClass, cmd.getId()); deleteProjectModelWithDate(projectModel); deleteDraftProjectModel(projectModel); } else if (PhaseModelDTO.ENTITY_NAME.equals(cmd.getEntityName())) { final PhaseModel phaseModel = em().find(PhaseModel.class, cmd.getId()); deletePhaseModel(phaseModel); } else if (cmd.getProjectModelStatus() == ProjectModelStatus.DRAFT && OrgUnitModelDTO.ENTITY_NAME.equals(cmd.getEntityName())) { // Delete draft OrgUnit model OrgUnitModel orgUnitModel = em().find(OrgUnitModel.class, cmd.getId()); deleteOrgUnitModelWithDate(orgUnitModel); } else if (cmd.getProjectModelStatus() == ProjectModelStatus.DRAFT && ContactModelDTO.ENTITY_NAME.equals(cmd.getEntityName())) { ContactModel contactModel = em().find(ContactModel.class, cmd.getId()); deleteContactModelWithDate(contactModel); } else { final Deleteable entity = (Deleteable) em().find(entityClass, cmd.getId()); searchForConflicts(entity, context.getLanguage(), context.getUser()); entity.delete(); em().persist(entity); } } private void deletePhaseModel(PhaseModel phaseModel) { // ----STEP1: delete the successor relation--------------------------- // If this model is the successor of other phase model, // this relation should be removed first Query query = em().createQuery("FROM PhaseModel "); List<PhaseModel> models = query.getResultList(); for (PhaseModel p : models) { if (p.getSuccessors() != null) { if (p.getSuccessors().contains(phaseModel)) { p.getSuccessors().remove(phaseModel); em().merge(p); } } } // ----STEP2: delete all child phases using this phase model----------- final TypedQuery<Phase> queryPhases = em().createQuery("SELECT p FROM Phase p WHERE p.phaseModel.id = :phaseModelId", Phase.class); queryPhases.setParameter("phaseModelId", phaseModel.getId()); for (final Phase phase : queryPhases.getResultList()) { if (phase.getParentProject() != null && Objects.equals(phase.getParentProject().getCurrentPhase(), phase)) { final Project parentProject = em().find(Project.class, phase.getParentProject().getId()); parentProject.setCurrentPhase(null); em().merge(parentProject); } em().remove(phase); } // -----STEP3: delete the phase // model------------------------------------------- em().remove(phaseModel); em().flush(); em().clear(); } private Class<? extends Deleteable> entityClassForEntityName(String entityName) { try { return (Class<? extends Deleteable>) Class.forName(UserDatabase.class.getPackage().getName() + "." + entityName); } catch (ClassNotFoundException e) { throw new RuntimeException("Invalid entity name '" + entityName + "'", e); } catch (ClassCastException e) { throw new RuntimeException("Entity type '" + entityName + "' is not Deletable", e); } } /** * Delete the test project object. * * @param project * the object to delete. * @deprecated Use {@link #deleteProjectWithDate(Project)} */ @SuppressWarnings("unused") @Deprecated private void deleteTestProject(Project project) { // delete the project flexible elem()ents deleteProjectFlexibleElement(project); // delete the test project em().remove(project); } /** * Delete the project object. * * @param project * the object to delete. * @deprecated Use {@link #deleteProjectWithDate(Project)} */ @SuppressWarnings("unused") @Deprecated private void deleteProject(Project project) { // delete the project flexible elem()ents deleteProjectFlexibleElement(project); // delete the test project em().remove(project); } /** * Sets the deleted date of the given project. * * @param project * the project to delete. */ private void deleteProjectWithDate(Project project) { project.delete(); // Deletes all the links of the project (avoids orphan links) final List<ProjectFunding> listfundingsToDelete = new ArrayList<ProjectFunding>(); listfundingsToDelete.addAll(project.getFunded()); listfundingsToDelete.addAll(project.getFunding()); project.getFunded().clear(); project.getFunding().clear(); for (ProjectFunding pf : listfundingsToDelete) { em().remove(pf); } } /** * Sets the deleted date of the given project model. * * @param projectModel * the project model to delete. */ private void deleteProjectModelWithDate(ProjectModel projectModel) { projectModel.delete(); } /** * Sets the deleted date of the given org unit model. * * @param orgUnitModel * the org unit model to delete. */ private void deleteOrgUnitModelWithDate(OrgUnitModel orgUnitModel) { orgUnitModel.delete(); } /** * Sets the deleted date of the given contact model. * * @param contactModel * the org unit model to delete. */ private void deleteContactModelWithDate(ContactModel contactModel) { contactModel.delete(); } /** * Delete the values of the test project. * * @param project * @deprecated Use {@link #deleteProjectWithDate(Project)} */ @Deprecated private void deleteProjectFlexibleElement(Project project) { // delete values Query query = em().createQuery("Select v FROM Value v WHERE v.containerId =:containerId"); query.setParameter("containerId", project.getId()); final List<Value> listResultsValues = query.getResultList(); if (listResultsValues != null) { for (Value value : listResultsValues) { em().remove(value); } } // delete project reports query = em().createQuery("Select pr FROM ProjectReport pr WHERE pr.project.id =:databaseid"); query.setParameter("databaseid", project.getId()); final List<ProjectReport> listResultReports = query.getResultList(); if (listResultReports != null) { for (ProjectReport report : listResultReports) { // Delete the project report's version ProjectReportVersion version = report.getCurrentVersion(); if (version != null) { // delete vercion's richText elem()ents List<RichTextElement> richTextElements = version.getTexts(); if (richTextElements != null) { for (RichTextElement richTextElement : richTextElements) { em().remove(richTextElement); } } em().remove(version); } em().remove(report); } } } /** * Method to delete a project model. Only draft project model is allowed to delete. * * @param projectModel * @author HUZHE(zhe.hu32@gmail.com) */ private void deleteDraftProjectModel(ProjectModel projectModel) { // ------STEP 1: Get all projects using this project model and delete // them()------------ final Query query = em().createQuery("SELECT p FROM Project p WHERE p.projectModel=:model"); query.setParameter("model", projectModel); List<Project> projects = query.getResultList(); for (Project p : projects) { deleteProjectWithDate(p); } em().flush(); } /** * Check if the deletion will happen in a conflicting state. * * @param deleteable Delete command. * @param language Language of the current user. * @param user User trying to delete a field. * @throws UpdateConflictException If a conflict has been detected. */ private void searchForConflicts(Deleteable deleteable, Language language, User user) throws UpdateConflictException { // For now, this method only verify conflicts with files since the // offline mode handle only file deletion. final File file; if(deleteable instanceof FileVersion) { final FileVersion version = (FileVersion)deleteable; file = version.getParentFile(); } else if(deleteable instanceof File) { file = (File)deleteable; } else { file = null; } if(file != null) { conflicts.searchForFileDeleteConflicts(file, language, user); } } }