@Override
 public void onCloseProject(GwtConversation conversation) {
   Project project = conversation.getProject();
   if (project != null && conversation.getSession().getGwtConversations().size() < 2) {
     ProjectUserConfig config = project.getUserConfig(conversation.getSession().getUser());
     config.reset();
     sendToClients(conversation, config);
   }
   conversation.clearRemoteEntities();
   conversation.setProject(null);
 }
 public void sendProjectEventEmails(
     String message, AEntity subject, Project project, String exceptionEmail) {
   if (exceptionEmail != null) exceptionEmail = exceptionEmail.toLowerCase();
   String subjectText = EmailHelper.createSubject(project, message);
   String emailText = createProjectEventEmailText(project, message, subject);
   for (ProjectUserConfig config : project.getUserConfigs()) {
     if (!config.isReceiveEmailsOnProjectEvents()) continue;
     String email = config.getUser().getEmail();
     if (!Str.isEmail(email)) continue;
     if (email.toLowerCase().equals(exceptionEmail)) continue;
     emailSender.sendEmail(project, email, subjectText, emailText);
   }
 }
  @Override
  public void onSelectProject(GwtConversation conversation, String projectId) {
    Project project = projectDao.getById(projectId);
    User user = conversation.getSession().getUser();
    if (!project.isVisibleFor(user))
      throw new PermissionDeniedException(
          "Project '" + project + "' is not visible for user '" + user + "'");

    project.setLastOpenedDateAndTime(DateAndTime.now());
    conversation.setProject(project);
    user.setCurrentProject(project);
    ProjectUserConfig config = project.getUserConfig(user);
    config.touch();

    conversation.sendToClient(project);
    conversation.sendToClient(project.getSprints());
    conversation.sendToClient(project.getSprintReports());
    conversation.sendToClient(project.getParticipants());
    for (Requirement requirement : project.getProductBacklogRequirements()) {
      conversation.sendToClient(requirement);
      conversation.sendToClient(requirement.getEstimationVotes());
    }
    for (Requirement requirement : project.getCurrentSprint().getRequirements()) {
      conversation.sendToClient(requirement);
      conversation.sendToClient(requirement.getTasksInSprint());
    }
    conversation.sendToClient(project.getQualitys());
    conversation.sendToClient(project.getUserConfigs());
    conversation.sendToClient(project.getWikipages());
    conversation.sendToClient(project.getImpediments());
    conversation.sendToClient(project.getRisks());
    conversation.sendToClient(project.getLatestProjectEvents(5));
    conversation.sendToClient(project.getCalendarEvents());
    conversation.sendToClient(project.getFiles());
    conversation.sendToClient(project.getOpenIssues());
    conversation.sendToClient(project.getReleases());
    conversation.sendToClient(project.getBlogEntrys());

    sendToClients(conversation, config);
  }
  @Override
  public void onDeleteEntity(GwtConversation conversation, String entityId) {
    AEntity entity = getDaoService().getEntityById(entityId);
    User user = conversation.getSession().getUser();
    if (!Auth.isDeletable(entity, user)) throw new PermissionDeniedException();

    if (entity instanceof File) {
      File file = (File) entity;
      file.deleteFile();
    }

    if (entity instanceof Task) {
      // update sprint day snapshot before delete
      conversation
          .getProject()
          .getCurrentSprint()
          .getDaySnapshot(Date.today())
          .updateWithCurrentSprint();
    }

    ADao dao = getDaoService().getDao(entity);
    dao.deleteEntity(entity);

    if (entity instanceof Task) onTaskDeleted(conversation, (Task) entity);

    Project project = conversation.getProject();
    if (project != null) {
      for (GwtConversation c : webApplication.getConversationsByProject(project, conversation)) {
        c.getNextData().addDeletedEntity(entityId);
      }
    }

    if (user != null && project != null) {
      ProjectUserConfig config = project.getUserConfig(user);
      config.touch();
      sendToClients(conversation, config);
    }
  }
  @Override
  public void onChangeProperties(GwtConversation conversation, String entityId, Map properties) {
    AEntity entity = getDaoService().getEntityById(entityId);
    User currentUser = conversation.getSession().getUser();
    if (!Auth.isEditable(entity, currentUser)) throw new PermissionDeniedException();

    Sprint previousRequirementSprint =
        entity instanceof Requirement ? ((Requirement) entity).getSprint() : null;

    if (entity instanceof Requirement) {
      postChangeIfChanged(conversation, entity, properties, currentUser, "label");
      postChangeIfChanged(conversation, entity, properties, currentUser, "description");
      postChangeIfChanged(conversation, entity, properties, currentUser, "testDescription");
      postChangeIfChanged(conversation, entity, properties, currentUser, "sprintId");
      postChangeIfChanged(conversation, entity, properties, currentUser, "closed");
      postChangeIfChanged(conversation, entity, properties, currentUser, "issueId");
    }
    Project project = conversation.getProject();
    if (entity instanceof Task) {
      // update sprint day snapshot before change
      if (project.isCurrentSprintSet())
        project.getCurrentSprint().getDaySnapshot(Date.today()).updateWithCurrentSprint();
      postChangeIfChanged(conversation, entity, properties, currentUser, "label");
      postChangeIfChanged(conversation, entity, properties, currentUser, "description");
    }
    if (entity instanceof Wikipage) {
      postChangeIfChanged(conversation, entity, properties, currentUser, "text");
    }
    if (entity instanceof Risk) {
      postChangeIfChanged(conversation, entity, properties, currentUser, "description");
      postChangeIfChanged(conversation, entity, properties, currentUser, "probability");
      postChangeIfChanged(conversation, entity, properties, currentUser, "impact");
      postChangeIfChanged(conversation, entity, properties, currentUser, "probabilityMitigation");
      postChangeIfChanged(conversation, entity, properties, currentUser, "impactMitigation");
    }
    if (entity instanceof Impediment) {
      postChangeIfChanged(conversation, entity, properties, currentUser, "description");
      postChangeIfChanged(conversation, entity, properties, currentUser, "solution");
      postChangeIfChanged(conversation, entity, properties, currentUser, "closed");
    }
    if (entity instanceof Issue) {
      postChangeIfChanged(conversation, entity, properties, currentUser, "description");
      postChangeIfChanged(conversation, entity, properties, currentUser, "statement");
      postChangeIfChanged(conversation, entity, properties, currentUser, "closeDate");
      postChangeIfChanged(conversation, entity, properties, currentUser, "storyId");
    }
    if (entity instanceof BlogEntry) {
      postChangeIfChanged(conversation, entity, properties, currentUser, "text");
    }

    entity.updateProperties(properties);

    if (entity instanceof Task) onTaskChanged(conversation, (Task) entity, properties);
    if (entity instanceof Requirement)
      onRequirementChanged(
          conversation, (Requirement) entity, properties, previousRequirementSprint);
    if (entity instanceof Impediment)
      onImpedimentChanged(conversation, (Impediment) entity, properties);
    if (entity instanceof Issue) onIssueChanged(conversation, (Issue) entity, properties);
    if (entity instanceof BlogEntry)
      onBlogEntryChanged(conversation, (BlogEntry) entity, properties);
    if (entity instanceof Comment) onCommentChanged(conversation, (Comment) entity, properties);
    if (entity instanceof SystemConfig)
      onSystemConfigChanged(conversation, (SystemConfig) entity, properties);
    if (entity instanceof Wikipage) onWikipageChanged(conversation, (Wikipage) entity, properties);

    Project currentProject = project;
    if (currentUser != null && currentProject != null) {
      ProjectUserConfig config = currentProject.getUserConfig(currentUser);
      config.touch();
      sendToClients(conversation, config);
    }

    conversation.clearRemoteEntitiesByType(Change.class);
    sendToClients(conversation, entity);
  }
  @Override
  public void onCreateEntity(GwtConversation conversation, String type, Map properties) {
    String id = (String) properties.get("id");
    if (id == null) throw new NullPointerException("id == null");

    ADao dao = getDaoService().getDaoByName(type);
    AEntity entity = dao.newEntityInstance(id);
    entity.updateProperties(properties);
    User currentUser = conversation.getSession().getUser();
    Project currentProject = conversation.getProject();

    if (entity instanceof Numbered) {
      ((Numbered) entity).updateNumber();
    }

    if (entity instanceof Project) {
      Project project = (Project) entity;
      project.addParticipant(currentUser);
      project.addAdmin(currentUser);
      project.addProductOwner(currentUser);
      project.addScrumMaster(currentUser);
      project.addTeamMember(currentUser);
    }

    if (entity instanceof Comment) {
      Comment comment = (Comment) entity;
      comment.setDateAndTime(DateAndTime.now());
      postProjectEvent(
          conversation,
          comment.getAuthor().getName() + " commented on " + comment.getParent(),
          comment.getParent());
      currentProject.updateHomepage(comment.getParent(), true);
    }

    if (entity instanceof ChatMessage) {
      ChatMessage chatMessage = (ChatMessage) entity;
      chatMessage.setDateAndTime(DateAndTime.now());
    }

    if (entity instanceof Impediment) {
      Impediment impediment = (Impediment) entity;
      impediment.setDate(Date.today());
    }

    if (entity instanceof Issue) {
      Issue issue = (Issue) entity;
      issue.setDate(DateAndTime.now());
      issue.setCreator(currentUser);
    }

    if (entity instanceof Task) {
      Task task = (Task) entity;
      Requirement requirement = task.getRequirement();
      requirement.setRejectDate(null);
      requirement.setClosed(false);
      sendToClients(conversation, requirement);
    }

    if (entity instanceof BlogEntry) {
      BlogEntry blogEntry = (BlogEntry) entity;
      blogEntry.setDateAndTime(DateAndTime.now());
      blogEntry.addAuthor(currentUser);
    }

    if (entity instanceof Change) {
      Change change = (Change) entity;
      change.setDateAndTime(DateAndTime.now());
      change.setUser(currentUser);
    }

    if (!(entity instanceof Transient)) dao.saveEntity(entity);

    sendToClients(conversation, entity);

    if (entity instanceof Requirement) {
      Requirement requirement = (Requirement) entity;
      Requirement epic = requirement.getEpic();
      String value = null;
      if (epic != null) {
        value = epic.getReferenceAndLabel();
        Change change =
            changeDao.postChange(epic, currentUser, "@split", null, requirement.getReference());
        conversation.sendToClient(change);
      }
      Change change = changeDao.postChange(requirement, currentUser, "@created", null, value);
      conversation.sendToClient(change);
    }

    if (entity instanceof Task
        || entity instanceof Wikipage
        || entity instanceof Risk
        || entity instanceof Impediment
        || entity instanceof Issue
        || entity instanceof BlogEntry) {
      Change change = changeDao.postChange(entity, currentUser, "@created", null, null);
      conversation.sendToClient(change);
    }

    if (currentUser != null && currentProject != null) {
      ProjectUserConfig config = currentProject.getUserConfig(currentUser);
      config.touch();
      sendToClients(conversation, config);
    }
  }