FormInstance.java

/*
 * Copyright (C) 2000 - 2024 Silverpeas
 *
 * 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.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception. You should have recieved a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * 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 <https://www.gnu.org/licenses/>.
 */
package org.silverpeas.components.formsonline.model;

import org.silverpeas.core.admin.user.model.User;
import org.silverpeas.core.contribution.ContributionStatus;
import org.silverpeas.core.contribution.content.form.Form;
import org.silverpeas.core.contribution.model.ContributionValidation;
import org.silverpeas.core.contribution.model.SilverpeasContent;

import javax.persistence.Transient;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.silverpeas.core.util.StringUtil.EMPTY;

public class FormInstance implements SilverpeasContent {
  private static final long serialVersionUID = -3341454138112938275L;

  public static final int STATE_DRAFT = 0;
  public static final int STATE_UNREAD = 1;
  public static final int STATE_READ = 2;
  public static final int STATE_VALIDATED = 3;
  public static final int STATE_REFUSED = 4;
  public static final int STATE_ARCHIVED = 5;
  public static final int STATE_CANCELED = 6;

  private final FormInstanceValidations validations = new FormInstanceValidations();

  @Transient
  protected transient FormDetail form;
  private String id;
  private int formId = -1;
  private int state = -1;
  private String creatorId = null;
  private Date creationDate = null;
  private String instanceId = null;

  @Transient
  private boolean validationEnabled = false;

  @Transient
  private transient Form formWithData;

  @Override
  public String getId() {
    return id;
  }

  /**
   * @param id the id to set
   */
  public void setId(int id) {
    this.id = Integer.toString(id);
  }

  public int getIdAsInt() {
    return Integer.parseInt(getId());
  }

  @Override
  public String getComponentInstanceId() {
    return instanceId;
  }

  /**
   * @return the formId
   */
  public int getFormId() {
    return formId;
  }

  /**
   * @param formId the formId to set
   */
  public void setFormId(int formId) {
    this.formId = formId;
  }

  /**
   * @return the state
   */
  public int getState() {
    return state;
  }

  /**
   * @param state the state to set
   */
  public void setState(int state) {
    this.state = state;
  }

  /**
   * @return the creatorId
   */
  public String getCreatorId() {
    return creatorId;
  }

  /**
   * @param creatorId the creatorId to set
   */
  public void setCreatorId(String creatorId) {
    this.creatorId = creatorId;
  }

  /**
   * @return the creationDate
   */
  @Override
  public Date getCreationDate() {
    return creationDate != null ? new Date(creationDate.getTime()) : null;
  }

  /**
   * @param creationDate the creationDate to set
   */
  public void setCreationDate(Date creationDate) {
    this.creationDate = creationDate != null ? new Date(creationDate.getTime()) : null;
  }

  @Override
  public Date getLastUpdateDate() {
    return getValidationDate() != null ? getValidationDate() : getCreationDate();
  }

  @Override
  public String getTitle() {
    return null;
  }

  @Override
  public String getDescription() {
    return null;
  }

  @Override
  public String getContributionType() {
    return null;
  }

  @Override
  public boolean canBeAccessedBy(final User user) {
    String userId = user.getId();
    return getCreatorId().equals(userId) || form.isValidator(userId) ||
        isHierarchicalValidator(userId);
  }

  @Override
  public boolean canBeDeletedBy(final User user) {
    return ((isDraft() || getForm().isDeleteAfterRequestExchange()) && user.getId().equals(getCreatorId())) ||
        ((isCanceled() || isValidated() || isDenied() || isArchived()) && isOneOfLastValidators(user));
  }

  public String getHierarchicalValidator() {
    return form.getHvManager().getHierarchicalValidatorOf(getCreatorId());
  }

  public boolean isHierarchicalValidator(String userId) {
    return form.isHierarchicalValidation() && userId.equals(getHierarchicalValidator());
  }

  /**
   * Gets all the validations performed on the form instance.
   * @return a {@link FormInstanceValidations} instance.
   */
  public FormInstanceValidations getValidations() {
    return validations;
  }

  public List<FormInstanceValidation> getPreviousValidations() {
    return getValidationsSchema().stream()
        .filter(v -> !v.isPendingValidation())
        .collect(Collectors.toList());
  }

  public List<FormInstanceValidation> getValidationsSchema() {
    return Stream.of(FormInstanceValidationType.values())
        .map(v -> getValidations().getValidationOfType(v).orElseGet(() -> {
          final Function<FormInstance, Supplier<List<User>>> validatorSupplier = form
              .getPossibleRequestValidations().get(v);
          final FormInstanceValidation validation;
          if (validatorSupplier != null) {
            validation = new FormInstanceValidation(this);
            validation.setValidationType(v);
            validation.setStatus(ContributionStatus.PENDING_VALIDATION);
            // adding user who have to validate if it is unique
            final List<User> validators = validatorSupplier.apply(this).get();
            if (validators.size() == 1) {
              validation.setValidator(validators.get(0));
            }
          } else {
            validation = null;
          }
          return validation;
        }))
        .filter(Objects::nonNull)
        .collect(Collectors.toList());
  }

  public FormInstanceValidation getPendingValidation() {
    return getValidationsSchema().stream()
        .filter(FormInstanceValidation::isPendingValidation)
        .findFirst()
        .orElse(null);
  }

  /**
   * @return the validatorId
   */
  public String getValidatorId() {
    return validations.getFinalValidation().map(v -> v.getValidator().getId()).orElse(EMPTY);
  }

  /**
   * @return the validationDate
   */
  public Date getValidationDate() {
    return validations.getFinalValidation().map(ContributionValidation::getDate).orElse(null);
  }

  /**
   * @return the comments
   */
  public String getComments() {
    return validations.getFinalValidation().map(ContributionValidation::getComment).orElse(EMPTY);
  }

  /**
   * @param instanceId the instanceId to set
   */
  public void setInstanceId(String instanceId) {
    this.instanceId = instanceId;
  }

  public FormDetail getForm() {
    return form;
  }

  public void setForm(final FormDetail form) {
    this.form = form;
  }

  public boolean isRead() {
    return getState() == STATE_READ;
  }

  public boolean isUnread() {
    return getState() == STATE_UNREAD;
  }

  public boolean isCanceled() {
    return getState() == STATE_CANCELED;
  }

  public boolean isValidated() {
    return getState() == STATE_VALIDATED;
  }

  public boolean isDenied() {
    return getState() == STATE_REFUSED;
  }

  public boolean isArchived() {
    return getState() == STATE_ARCHIVED;
  }

  public boolean canBeValidated() {
    return !isCanceled() && !isValidated() && !isDenied() && !isArchived();
  }

  public boolean canBeArchivedBy(final User user) {
    return (isCanceled() || isValidated() || isDenied()) && isOneOfLastValidators(user);
  }

  private boolean isOneOfLastValidators(final User user) {
    boolean isOneOfLastValidator;
    if (form.isFinalValidation()) {
      isOneOfLastValidator = form.isFinalValidator(user.getId());
      if (!isOneOfLastValidator && getValidations().getIntermediateValidation().filter(FormInstanceValidation::isRefused).isPresent()) {
        isOneOfLastValidator = form.isIntermediateValidator(user.getId());
      }
      if (!isOneOfLastValidator && getValidations().getHierarchicalValidation().filter(FormInstanceValidation::isRefused).isPresent()) {
        isOneOfLastValidator = getHierarchicalValidator().equals(user.getId());
      }
    } else if (form.isIntermediateValidation()) {
      isOneOfLastValidator = form.isIntermediateValidator(user.getId());
      if (!isOneOfLastValidator && getValidations().getHierarchicalValidation().filter(FormInstanceValidation::isRefused).isPresent()) {
        isOneOfLastValidator = getHierarchicalValidator().equals(user.getId());
      }
    } else if (form.isHierarchicalValidation()) {
      isOneOfLastValidator = getHierarchicalValidator().equals(user.getId());
    } else {
      isOneOfLastValidator = false;
    }
    return isOneOfLastValidator;
  }

  public boolean canBeCanceledBy(final User user) {
    return canBeValidated() && user.getId().equals(getCreatorId());
  }

  public boolean isDraft() {
    return getState() == STATE_DRAFT;
  }

  @Override
  public User getCreator() {
    return User.getById(getCreatorId());
  }

  @Override
  public User getLastUpdater() {
    return validations.getFinalValidation()
        .map(ContributionValidation::getValidator)
        .orElseGet(this::getCreator);
  }

  public User getValidator() {
    return User.getById(getValidatorId());
  }

  public FormPK getFormPK() {
    return new FormPK(Integer.toString(getFormId()), getComponentInstanceId());
  }

  public boolean isValidationEnabled() {
    return validationEnabled;
  }

  public void setValidationEnabled(final boolean validationEnabled) {
    this.validationEnabled = validationEnabled;
  }

  public Form getFormWithData() {
    return formWithData;
  }

  public void setFormWithData(final Form formWithData) {
    this.formWithData = formWithData;
  }

  public RequestPK getPK() {
    return new RequestPK(getId(), getComponentInstanceId());
  }

  public boolean isVoidable() {
    return isUnread() || isRead();
  }

  public List<String> getValidationsImages() {
    final List<FormInstanceValidation> validationForImages = canBeValidated()
        ? getValidationsSchema()
        : getValidations();
    return Stream.concat(Stream.of(isUnread() ? "vu.png" : "vu-ok.png"),
        validationForImages.stream().map(this::getValidationImage)).collect(Collectors.toList());
  }

  private String getValidationImage(FormInstanceValidation validation) {
    String prefix = "vh";
    if (validation.getValidationType().isIntermediate()) {
      prefix = "vi";
    } else if (validation.getValidationType().isFinal()) {
      prefix = "vf";
    }
    String stateSuffix = "";
    if (validation.isValidated()) {
      stateSuffix = "-ok";
    } else if (validation.isRefused()) {
      stateSuffix = "-nok";
    }
    return prefix + stateSuffix + ".png";
  }
}