br.com.bluesoft.pronto.controller.TicketController.java Source code

Java tutorial

Introduction

Here is the source code for br.com.bluesoft.pronto.controller.TicketController.java

Source

/*
 * Copyright 2009 Pronto Agile Project Management.
 *
 * This file is part of Pronto.
 *
 * Pronto 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.
 *
 * Pronto 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 Pronto. If not, see <http://www.gnu.org/licenses/>.
 *
 */

package br.com.bluesoft.pronto.controller;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

import br.com.bluesoft.pronto.ProntoException;
import br.com.bluesoft.pronto.SegurancaException;
import br.com.bluesoft.pronto.core.Backlog;
import br.com.bluesoft.pronto.core.Papel;
import br.com.bluesoft.pronto.core.TipoDeTicket;
import br.com.bluesoft.pronto.dao.BacklogDao;
import br.com.bluesoft.pronto.dao.ClienteDao;
import br.com.bluesoft.pronto.dao.KanbanStatusDao;
import br.com.bluesoft.pronto.dao.SprintDao;
import br.com.bluesoft.pronto.dao.TicketDao;
import br.com.bluesoft.pronto.dao.TipoDeTicketDao;
import br.com.bluesoft.pronto.dao.UsuarioDao;
import br.com.bluesoft.pronto.model.Anexo;
import br.com.bluesoft.pronto.model.Classificacao;
import br.com.bluesoft.pronto.model.Sprint;
import br.com.bluesoft.pronto.model.Ticket;
import br.com.bluesoft.pronto.model.TicketLog;
import br.com.bluesoft.pronto.model.TicketOrdem;
import br.com.bluesoft.pronto.model.Usuario;
import br.com.bluesoft.pronto.service.Config;
import br.com.bluesoft.pronto.service.Seguranca;
import br.com.bluesoft.pronto.util.ControllerUtil;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

@Controller
public class TicketController {

    private static final String VIEW_LISTAR = "ticket.listar.jsp";
    private static final String VIEW_BRANCHES = "ticket.branches.jsp";
    private static final String VIEW_ESTIMAR = "ticket.estimar.jsp";
    private static final String VIEW_EDITAR = "ticket.editar.jsp";
    private static final String VIEW_LISTAR_AGRUPADO = "ticket.listarAgrupado.jsp";

    @Autowired
    private ClienteDao clienteDao;

    @Autowired
    private SessionFactory sessionFactory;

    @Autowired
    private TicketDao ticketDao;

    @Autowired
    private SprintDao sprintDao;

    @Autowired
    private UsuarioDao usuarioDao;

    @Autowired
    private KanbanStatusDao kanbanStatusDao;

    @Autowired
    private TipoDeTicketDao tipoDeTicketDao;

    @Autowired
    private BacklogDao backlogDao;

    private Logger log = Logger.getLogger(TicketController.class.getName());

    @ModelAttribute("usuarios")
    public List<Usuario> getUsuarios() {
        return usuarioDao.listar();
    }

    @ModelAttribute("tiposDeTicket")
    public List<TipoDeTicket> getTiposDeTicket() {
        return tipoDeTicketDao.listar();
    }

    @SuppressWarnings("unchecked")
    @RequestMapping("/ticket/listarPorBacklog.action")
    public String listarPorBacklog(final Model model, final int backlogKey) throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE, Papel.SCRUM_MASTER);

        final List<Ticket> tickets = ticketDao.listarEstoriasEDefeitosPorBacklog(backlogKey);
        final List<Ticket> tarefasSoltas = ticketDao
                .listarTarefasEmBacklogsDiferentesDasEstoriasPorBacklog(backlogKey);

        model.addAttribute("tickets", tickets);
        model.addAttribute("tarefasSoltas", tarefasSoltas);
        model.addAttribute("backlog", backlogDao.obter(backlogKey));

        final Map<Integer, Integer> totaisPorTipoDeTicket = totaisPorTipoDeTicket(tickets, tarefasSoltas);
        model.addAttribute("descricaoTotal", montaDescricaoTotal(totaisPorTipoDeTicket));

        return VIEW_LISTAR;
    }

    @SuppressWarnings("unchecked")
    @RequestMapping("/ticket/listarPorSprint.action")
    public String listarPorSprint(final Model model, final int sprintKey) throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE, Papel.SCRUM_MASTER);

        final List<Ticket> tickets = ticketDao.listarEstoriasEDefeitosPorSprint(sprintKey);
        model.addAttribute("tickets", tickets);
        model.addAttribute("sprint", sprintDao.obter(sprintKey));
        model.addAttribute("sprints", sprintDao.listarSprintsEmAberto());

        final Map<Integer, Integer> totaisPorTipoDeTicket = totaisPorTipoDeTicket(tickets);
        model.addAttribute("descricaoTotal", montaDescricaoTotal(totaisPorTipoDeTicket));

        return VIEW_LISTAR;
    }

    @RequestMapping("/ticket/listarPendentesPorCliente.action")
    public String listarTicketsPendentesPorCliente(final Model model, final Integer kanbanStatusKey,
            final Integer clienteKey, final String ordem, final String classificacao) throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE, Papel.SCRUM_MASTER);

        TicketOrdem ticketOrdem = TicketOrdem.TITULO;

        if (ordem != null && ordem.length() > 0) {
            ticketOrdem = TicketOrdem.valueOf(ordem);
        }

        Classificacao ticketClassificacao = Classificacao.ASCENDENTE;
        if (classificacao != null && classificacao.length() > 0) {
            ticketClassificacao = Classificacao.valueOf(classificacao);
        }

        List<Ticket> tickets = null;
        tickets = ticketDao.buscar(null, kanbanStatusKey, clienteKey, ticketOrdem, ticketClassificacao);

        final Multimap<String, Ticket> ticketsAgrupados = ArrayListMultimap.create();
        for (final Ticket ticket : tickets) {
            // No exibe nem as tarefas nem os itens na lixeira
            if (ticket.getPai() == null && ticket.getBacklog().getBacklogKey() != Backlog.LIXEIRA) {
                ticketsAgrupados.put(ticket.getCliente() != null ? ticket.getCliente().getNome() : "Indefinido",
                        ticket);
            }
        }

        model.addAttribute("ticketsAgrupados", ticketsAgrupados.asMap());

        final ArrayList<String> grupo = new ArrayList<String>(ticketsAgrupados.keySet());
        Collections.sort(grupo);

        model.addAttribute("grupos", grupo);
        model.addAttribute("clienteKey", clienteKey);
        model.addAttribute("kanbanStatusKey", kanbanStatusKey);
        model.addAttribute("kanbanStatus", kanbanStatusDao.listar());
        model.addAttribute("clientes", clienteDao.listar());
        model.addAttribute("ordens", TicketOrdem.values());
        model.addAttribute("ordem", ticketOrdem);
        model.addAttribute("classificacoes", Classificacao.values());
        model.addAttribute("classificacao", ticketClassificacao);

        return VIEW_LISTAR_AGRUPADO;
    }

    private String montaDescricaoTotal(final Map<Integer, Integer> totaisPorTipoDeTicket)
            throws SegurancaException {

        final Integer totalDeDefeitos = totaisPorTipoDeTicket.get(TipoDeTicket.DEFEITO);
        final Integer totalDeEstorias = totaisPorTipoDeTicket.get(TipoDeTicket.ESTORIA);
        final Integer totalDeTarefas = totaisPorTipoDeTicket.get(TipoDeTicket.TAREFA);
        final Integer totalDeIdeias = totaisPorTipoDeTicket.get(TipoDeTicket.IDEIA);

        final StringBuilder descricaoTotal = new StringBuilder();
        descricaoTotal.append(totalDeDefeitos > 0 ? totalDeDefeitos + " defeito(s), " : "nenhum defeito, ");
        descricaoTotal.append(totalDeEstorias > 0 ? totalDeEstorias + " projeto(s), " : "nenhum projeto, ");
        descricaoTotal.append(totalDeTarefas > 0 ? totalDeTarefas + " tarefa(s), " : "nenhuma tarefa, ");
        descricaoTotal.append(totalDeIdeias > 0 ? totalDeIdeias + " ideia(s)" : "nenhuma ideia");

        return descricaoTotal.toString();
    }

    private Map<Integer, Integer> totaisPorTipoDeTicket(final List<Ticket>... listas) {
        final Map<Integer, Integer> totais = new HashMap<Integer, Integer>();
        totais.put(TipoDeTicket.DEFEITO, 0);
        totais.put(TipoDeTicket.ESTORIA, 0);
        totais.put(TipoDeTicket.TAREFA, 0);
        totais.put(TipoDeTicket.IDEIA, 0);

        for (final List<Ticket> tickets : listas) {
            for (final Ticket ticket : tickets) {
                totais.put(ticket.getTipoDeTicket().getTipoDeTicketKey(),
                        totais.get(ticket.getTipoDeTicket().getTipoDeTicketKey()) + 1);
                for (final Ticket tarefa : ticket.getFilhos()) {
                    if (tarefa.getBacklog().getBacklogKey() == ticket.getBacklog().getBacklogKey()) {
                        totais.put(tarefa.getTipoDeTicket().getTipoDeTicketKey(),
                                totais.get(tarefa.getTipoDeTicket().getTipoDeTicketKey()) + 1);
                    }
                }
            }
        }

        return totais;
    }

    @RequestMapping("/ticket/branches.action")
    public String branches(final Model model) {
        final List<Ticket> tickets = ticketDao.listarTicketsQueNaoEstaoNoBranchMaster();
        model.addAttribute("tickets", tickets);
        return VIEW_BRANCHES;
    }

    @RequestMapping("/ticket/sprintAtual.action")
    public String sprintAtual(final Model model) {
        final Sprint sprint = sprintDao.getSprintAtual();
        final int quantidadeDeSprints = sprintDao.listar().size();

        if (quantidadeDeSprints == 0) {
            model.addAttribute("mensagem", "Por favor, crie um sprint.");
            return "forward:/sprint/listar.action";
        } else if (sprint == null) {
            model.addAttribute("mensagem", "Por favor, informe qual &eacute; o Sprint atual.");
            return "forward:/sprint/listar.action";
        } else {
            return "forward:/ticket/listarPorSprint.action?sprintKey=" + sprint.getSprintKey();
        }
    }

    @RequestMapping("/ticket/salvar.action")
    public String salvar(final Model model, final Ticket ticket, final String comentario,
            final String[] desenvolvedor, final String[] testador, final Integer paiKey, final Integer clienteKey)
            throws SegurancaException {
        log.info("Entrou TicketController.salvar");
        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE, Papel.SCRUM_MASTER);
        Transaction tx = null;
        try {
            tx = sessionFactory.getCurrentSession().beginTransaction();
            log.info("Iniciou transacao para gravar ticket");
            if (ticket.getTitulo() == null || ticket.getTitulo().trim().length() <= 0) {
                throw new ProntoException(
                        "N&atilde;o &eacute; poss&iacute;vel salvar um projeto, defeito ou tarefa sem t&iacute;tulo!");
            }

            if (paiKey == null) {
                log.info("Ticket e um projeto(nao tem pai)");
                ticket.setBacklog(backlogDao.obter(ticket.getBacklog().getBacklogKey()));
                ticket.setTipoDeTicket(tipoDeTicketDao.obter(ticket.getTipoDeTicket().getTipoDeTicketKey()));

                addSprintToTicket(ticket);

            } else {
                log.info("Ticket nao e projeto(tem pai)");
                final Ticket pai = ticketDao.obter(paiKey);
                copiarDadosDoPai(pai, ticket);
            }

            ticket.setKanbanStatus(kanbanStatusDao.obter(ticket.getKanbanStatus().getKanbanStatusKey()));
            log.info("setou status no kanban ==>> " + ticket.getKanbanStatus());
            if (clienteKey != null) {
                ticket.setCliente(clienteDao.obter(clienteKey));
                log.info("Setou cliente no ticket");
            }

            if (ticket.getTicketKey() == 0) {
                ticket.setReporter(usuarioDao.obter(ticket.getReporter().getUsername()));
                log.info("Setou reporter do ticket(quem editou)");
            }

            if (comentario != null && comentario.trim().length() > 0) {
                ticket.addComentario(comentario, Seguranca.getUsuario());
                log.info("Setou comentario do ticket");
            }

            definirDesenvolvedores(ticket, desenvolvedor);
            log.info("Setou desenvolvedores");
            definirTestadores(ticket, testador);
            log.info("Setou testadores");

            ticketDao.salvar(ticket);

            tx.commit();
            log.info("Executou TicketController.salvar");
            return "redirect:editar.action?ticketKey=" + ticket.getTicketKey();
        } catch (final Exception e) {
            e.printStackTrace();
            model.addAttribute("erro", e.getMessage());
            log.info("ERRO - TicketController.salvar!");
            log.error("Ooooopssss, ocorreu um erro vou dar rollback na transacao! ==>> " + e.getMessage());
            tx.rollback();
            return "forward:editar.action";
        }

    }

    private void addSprintToTicket(final Ticket ticket) {
        log.info("Entrou TicketController.addSprintToTicket");
        if (!ticket.isTarefa() && ticket.getSprint() != null && ticket.getSprint().getSprintKey() <= 0) {
            ticket.setSprint(null);
            log.info("Sprint foi setado para null no ticket..");
        } else if (ticket.isTarefa() && ticket.getSprint() != null && ticket.getSprint().getSprintKey() <= 0) {
            log.info("Ticket.isTarefa recuperando sprint do pai para cadastro da tarefa.");
            ticket.setSprint(ticket.getPai().getSprint());
        } else {
            log.info("Setando sprint pegando valor da combo na pagina");
            ticket.setSprint((Sprint) sessionFactory.getCurrentSession().get(Sprint.class,
                    ticket.getSprint().getSprintKey()));
        }
        log.info("Executou TicketController.addSprintToTicket");
    }

    private void definirDesenvolvedores(final Ticket ticket, final String[] desenvolvedor)
            throws SegurancaException {

        final Set<Usuario> desenvolvedoresAntigos = new TreeSet<Usuario>(
                ticketDao.listarDesenvolvedoresDoTicket(ticket.getTicketKey()));

        if (desenvolvedor != null && desenvolvedor.length > 0) {
            ticket.setDesenvolvedores(new TreeSet<Usuario>());
            for (final String username : desenvolvedor) {
                ticket.addDesenvolvedor((Usuario) sessionFactory.getCurrentSession().get(Usuario.class, username));
            }
        }

        final String desenvolvedoresAntigosStr = desenvolvedoresAntigos == null
                || desenvolvedoresAntigos.size() == 0 ? "nenhum" : desenvolvedoresAntigos.toString();
        final String desenvolvedoresNovosStr = ticket.getDesenvolvedores() == null
                || ticket.getDesenvolvedores().size() == 0 ? "nenhum" : ticket.getDesenvolvedores().toString();
        if (!desenvolvedoresAntigosStr.equals(desenvolvedoresNovosStr)) {
            ticket.addLogDeAlteracao("desenvolvedores", desenvolvedoresAntigosStr, desenvolvedoresNovosStr);
        }
    }

    private void definirTestadores(final Ticket ticket, final String[] testador) throws SegurancaException {

        final Set<Usuario> testadoresAntigos = new TreeSet<Usuario>(
                ticketDao.listarTestadoresDoTicket(ticket.getTicketKey()));

        if (testador != null && testador.length > 0) {
            ticket.setTestadores(new TreeSet<Usuario>());
            for (final String username : testador) {
                ticket.addTestador(usuarioDao.obter(username));
            }
        }

        final String testadoresAntigosStr = testadoresAntigos == null || testadoresAntigos.size() == 0 ? "nenhum"
                : testadoresAntigos.toString();
        final String testadoresNovosStr = ticket.getTestadores() == null || ticket.getTestadores().size() == 0
                ? "nenhum"
                : ticket.getTestadores().toString();
        if (!testadoresAntigosStr.equals(testadoresNovosStr)) {
            ticket.addLogDeAlteracao("testadores", testadoresAntigosStr, testadoresNovosStr);
        }
    }

    @RequestMapping("/ticket/jogarNoLixo.action")
    public String jogarNoLixo(final Model model, final int ticketKey, final HttpServletResponse response)
            throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE);

        final Ticket ticket = (Ticket) sessionFactory.getCurrentSession().get(Ticket.class, ticketKey);
        ticket.setBacklog((Backlog) sessionFactory.getCurrentSession().get(Backlog.class, Backlog.LIXEIRA));
        ticketDao.salvar(ticket);
        return "redirect:/ticket/editar.action?ticketKey=" + ticketKey;
    }

    @RequestMapping("/ticket/moverParaImpedimentos.action")
    public String moverParaImpedimentos(final Model model, final int ticketKey, final HttpServletResponse response)
            throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE);

        final Ticket ticket = (Ticket) sessionFactory.getCurrentSession().get(Ticket.class, ticketKey);
        ticket.setBacklog((Backlog) sessionFactory.getCurrentSession().get(Backlog.class, Backlog.IMPEDIMENTOS));
        ticketDao.salvar(ticket);
        return "redirect:/ticket/editar.action?ticketKey=" + ticketKey;
    }

    @RequestMapping("/ticket/moverParaBranchMaster.action")
    public String moverParaBranchMaster(final Model model, final int ticketKey, final HttpServletResponse response)
            throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE);

        final Ticket ticket = (Ticket) sessionFactory.getCurrentSession().get(Ticket.class, ticketKey);
        ticket.setBranch(Ticket.BRANCH_MASTER);
        ticketDao.salvar(ticket);
        return null;
    }

    @RequestMapping("/ticket/moverParaProductBacklog.action")
    public String moverParaProductBacklog(final Model model, final int ticketKey,
            final HttpServletResponse response) throws SegurancaException {

        final Ticket ticket = (Ticket) sessionFactory.getCurrentSession().get(Ticket.class, ticketKey);

        final int backlogDeOrigem = ticket.getBacklog().getBacklogKey();
        if (backlogDeOrigem == Backlog.IDEIAS) {
            Seguranca.validarPermissao(Papel.PRODUCT_OWNER);
        }

        ticket.setBacklog((Backlog) sessionFactory.getCurrentSession().get(Backlog.class, Backlog.PRODUCT_BACKLOG));

        // caso no seja uma ideia mantm o tipo original
        if (ticket.getTipoDeTicket().getTipoDeTicketKey() == TipoDeTicket.IDEIA) {
            ticket.setTipoDeTicket((TipoDeTicket) sessionFactory.getCurrentSession().get(TipoDeTicket.class,
                    TipoDeTicket.ESTORIA));
        }

        ticket.setSprint(null);
        ticketDao.salvar(ticket);
        return "redirect:/ticket/editar.action?ticketKey=" + ticketKey;
    }

    @RequestMapping("/ticket/moverParaOSprintAtual.action")
    public String moverParaOSprintAtual(final Model model, final int ticketKey, final HttpServletResponse response)
            throws ProntoException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER);

        final Ticket ticket = (Ticket) sessionFactory.getCurrentSession().get(Ticket.class, ticketKey);

        final int backlogDeOrigem = ticket.getBacklog().getBacklogKey();
        if (backlogDeOrigem != Backlog.PRODUCT_BACKLOG) {
            throw new ProntoException(
                    "Para que um projeto ou Bug seja movida para o Sprint Atual &ecute; preciso que ela esteja no Product Backlog.");
        }

        ticket.setSprint(sprintDao.getSprintAtual());
        ticket.setBacklog(backlogDao.obter(Backlog.SPRINT_BACKLOG));
        ticketDao.salvar(ticket);
        return "redirect:/ticket/editar.action?ticketKey=" + ticketKey;

    }

    @RequestMapping("/ticket/moverParaIdeias.action")
    public String moverParaIdeias(final Model model, final int ticketKey, final HttpServletResponse response)
            throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER);

        final Ticket ticket = (Ticket) sessionFactory.getCurrentSession().get(Ticket.class, ticketKey);
        ticket.setBacklog((Backlog) sessionFactory.getCurrentSession().get(Backlog.class, Backlog.IDEIAS));
        ticket.setTipoDeTicket(
                (TipoDeTicket) sessionFactory.getCurrentSession().get(TipoDeTicket.class, TipoDeTicket.IDEIA));
        ticket.setSprint(null);
        ticketDao.salvar(ticket);
        return "redirect:/ticket/editar.action?ticketKey=" + ticketKey;
    }

    @RequestMapping("/ticket/restaurar.action")
    public String restaurar(final Model model, final int ticketKey, final HttpServletResponse response)
            throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE);

        final Ticket ticket = (Ticket) sessionFactory.getCurrentSession().get(Ticket.class, ticketKey);

        Backlog backlog = null;
        switch (ticket.getTipoDeTicket().getTipoDeTicketKey()) {
        case TipoDeTicket.ESTORIA:
        case TipoDeTicket.DEFEITO:
            backlog = (Backlog) sessionFactory.getCurrentSession().get(Backlog.class, Backlog.PRODUCT_BACKLOG);
            break;
        case TipoDeTicket.IDEIA:
            backlog = (Backlog) sessionFactory.getCurrentSession().get(Backlog.class, Backlog.IDEIAS);
            break;
        case TipoDeTicket.TAREFA:
            backlog = ticket.getPai().getBacklog();
            break;
        }

        ticket.setBacklog(backlog);

        if (!ticket.isTarefa() && ticket.getSprint() != null && ticket.getSprint().isFechado()) {
            ticket.setSprint(null);
        }

        ticketDao.salvar(ticket);

        if (ticket.getFilhos() != null) {
            for (final Ticket filho : ticket.getFilhos()) {
                filho.setBacklog(backlog);
                ticketDao.salvar(ticket);
            }
        }

        return "redirect:/ticket/editar.action?ticketKey=" + ticketKey;
    }

    @SuppressWarnings("unchecked")
    @RequestMapping("/ticket/upload.action")
    public String upload(final HttpServletRequest request, final int ticketKey) throws Exception {

        final FileItemFactory factory = new DiskFileItemFactory();
        final ServletFileUpload upload = new ServletFileUpload(factory);

        final List<FileItem> items = upload.parseRequest(request);
        final String ticketDir = Config.getImagesFolder() + ticketKey + "/";
        final File dir = new File(ticketDir);
        dir.mkdirs();

        for (final FileItem fileItem : items) {
            final String nomeDoArquivo = fileItem.getName().toLowerCase().replace(' ', '_')
                    .replaceAll("[^A-Za-z0-9._\\-]", "");
            fileItem.write(new File(ticketDir + nomeDoArquivo));
        }

        return "redirect:editar.action?ticketKey=" + ticketKey;
    }

    private List<Anexo> listarAnexos(final int ticketKey) {
        final File folder = new File(Config.getImagesFolder() + ticketKey);
        if (folder.exists()) {
            final List<Anexo> anexos = new ArrayList<Anexo>();
            final String[] files = folder.list();
            Arrays.sort(files);
            if (files.length > 0) {
                for (final String file : files) {
                    anexos.add(new Anexo(file));

                }
            }
            return anexos;
        } else {
            return null;
        }

    }

    @RequestMapping("/ticket/excluirAnexo.action")
    public String excluirAnexo(final String file, final int ticketKey) throws Exception {
        final File arquivo = new File(Config.getImagesFolder() + ticketKey + "/" + file);
        if (arquivo.exists()) {
            arquivo.delete();
        }
        return "redirect:editar.action?ticketKey=" + ticketKey;
    }

    @RequestMapping("/ticket/download.action")
    public String download(final HttpServletResponse response, final String file, final int ticketKey)
            throws Exception {

        final File arquivo = new File(Config.getImagesFolder() + ticketKey + "/" + file);
        final FileInputStream fis = new FileInputStream(arquivo);
        final int numberBytes = fis.available();
        final byte bytes[] = new byte[numberBytes];
        fis.read(bytes);
        fis.close();

        String extensao = null;
        if (file.lastIndexOf('.') > 0) {
            extensao = file.substring(file.lastIndexOf('.') + 1, file.length());
        }

        String mime = null;
        if (extensao == null) {
            mime = "text/plain";
        } else if (extensao.equalsIgnoreCase("png")) {
            mime = "images/png";
        } else if (extensao.equalsIgnoreCase("jpg") || extensao.equalsIgnoreCase("jpeg")) {
            mime = "images/jpeg";
        } else if (extensao.equalsIgnoreCase("gif")) {
            mime = "images/gif";
        } else if (extensao.equalsIgnoreCase("pdf")) {
            mime = "application/pdf";
        } else if (extensao.equalsIgnoreCase("xls") || extensao.equalsIgnoreCase("xlsx")) {
            mime = "application/vnd.ms-excel";
        } else if (extensao.equalsIgnoreCase("csv")) {
            mime = "text/csv";
        } else if (extensao.equalsIgnoreCase("txt")) {
            mime = "text/plain";
        } else if (extensao.equalsIgnoreCase("doc") || extensao.equalsIgnoreCase("docx")) {
            mime = "application/ms-word";
        }

        response.addHeader("content-disposition", "attachment; filename=" + file);
        response.setContentType(mime);
        response.setContentLength(bytes.length);
        FileCopyUtils.copy(bytes, response.getOutputStream());

        return null;
    }

    @RequestMapping("/ticket/estimarSprint.action")
    public String estimarSprint(final Model model, final int sprintKey) throws SegurancaException {
        Seguranca.validarPermissao(Papel.EQUIPE, Papel.PRODUCT_OWNER);
        final List<Ticket> tickets = ticketDao.listarEstoriasEDefeitosPorSprint(sprintKey);
        model.addAttribute("tickets", tickets);
        model.addAttribute("sprint", sessionFactory.getCurrentSession().get(Sprint.class, sprintKey));
        return VIEW_ESTIMAR;
    }

    @RequestMapping("/ticket/estimarBacklog.action")
    public String estimarBacklog(final Model model, final int backlogKey) throws SegurancaException {
        Seguranca.validarPermissao(Papel.EQUIPE, Papel.PRODUCT_OWNER);
        final List<Ticket> tickets = ticketDao.listarEstoriasEDefeitosPorBacklog(backlogKey);
        model.addAttribute("tickets", tickets);
        model.addAttribute("backlog", sessionFactory.getCurrentSession().get(Backlog.class, backlogKey));
        return VIEW_ESTIMAR;
    }

    @RequestMapping("/ticket/salvarValorDeNegocio.action")
    public void salvarValorDeNegocio(final HttpServletResponse response, final int ticketKey,
            final int valorDeNegocio) throws SegurancaException {
        try {
            Seguranca.validarPermissao(Papel.PRODUCT_OWNER);
            final Ticket ticket = ticketDao.obter(ticketKey);
            ticket.setValorDeNegocio(valorDeNegocio);
            ticketDao.salvar(ticket);
            ControllerUtil.writeText(response, true);
        } catch (final Exception e) {
            ControllerUtil.writeText(response, false);
        }
    }

    @RequestMapping("/ticket/salvarBranch.action")
    public void salvarBranch(final HttpServletResponse response, final int ticketKey, final String branch)
            throws SegurancaException {
        try {
            Seguranca.validarPermissao(Papel.EQUIPE);
            final Ticket ticket = ticketDao.obter(ticketKey);
            ticket.setBranch(branch);
            ticketDao.salvar(ticket);
            ControllerUtil.writeText(response, true);
        } catch (final Exception e) {
            ControllerUtil.writeText(response, false);
        }
    }

    @RequestMapping("/ticket/salvarEsforco.action")
    public void salvarEsforco(final HttpServletResponse response, final int ticketKey, final double esforco)
            throws SegurancaException {
        try {
            Seguranca.validarPermissao(Papel.EQUIPE);
            final Ticket ticket = ticketDao.obter(ticketKey);
            ticket.setEsforco(esforco);
            ticketDao.salvar(ticket);
            ControllerUtil.writeText(response, true);
        } catch (final Exception e) {
            ControllerUtil.writeText(response, false);
        }
    }

    @RequestMapping("/ticket/salvarPar.action")
    public void salvarPar(final HttpServletResponse response, final int ticketKey, final boolean par)
            throws SegurancaException {
        try {
            Seguranca.validarPermissao(Papel.EQUIPE);
            final Ticket ticket = ticketDao.obter(ticketKey);
            ticket.setPar(par);
            ticketDao.salvar(ticket);
            ControllerUtil.writeText(response, true);
        } catch (final Exception e) {
            ControllerUtil.writeText(response, false);
        }
    }

    @RequestMapping("/ticket/listarTarefasParaAdicionarAoSprint.action")
    public String listarTarefasParaAdicionarAoSprint(final Model model, final int sprintKey)
            throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE);

        model.addAttribute("sprint", sessionFactory.getCurrentSession().get(Sprint.class, sprintKey));
        model.addAttribute("tickets", ticketDao.listarEstoriasEDefeitosDoProductBacklog());
        return "/ticket/ticket.adicionarAoSprint.jsp";
    }

    @RequestMapping("/ticket/adicionarAoSprint.action")
    public String adicionarAoSprint(final Model model, final int sprintKey, final int[] ticketKey) {

        final Sprint sprint = (Sprint) sessionFactory.getCurrentSession().get(Sprint.class, sprintKey);
        for (final int key : ticketKey) {
            final Ticket ticket = (Ticket) sessionFactory.getCurrentSession().get(Ticket.class, key);
            ticket.setBacklog(
                    (Backlog) sessionFactory.getCurrentSession().get(Backlog.class, Backlog.SPRINT_BACKLOG));
            ticket.setSprint(sprint);
            ticketDao.salvar(ticket);
        }
        return "redirect:/ticket/listarPorSprint.action?sprintKey=" + sprintKey;
    }

    @RequestMapping("/ticket/logDescricao.action")
    public String logDescricao(final Model model, final int ticketHistoryKey) {

        final TicketLog log = (TicketLog) sessionFactory.getCurrentSession().get(TicketLog.class, ticketHistoryKey);
        model.addAttribute("log", log);

        return "/ticket/ticket.logDescricao.jsp";

    }

    @RequestMapping("/ticket/transformarEmEstoria.action")
    public String transformarEmEstoria(final Model model, final int ticketKey) throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE);

        final Ticket ticket = ticketDao.obter(ticketKey);
        ticket.setTipoDeTicket((TipoDeTicket) sessionFactory.getCurrentSession().createCriteria(TipoDeTicket.class)
                .add(Restrictions.eq("tipoDeTicketKey", TipoDeTicket.ESTORIA)).uniqueResult());
        ticketDao.salvar(ticket);
        return "forward:/ticket/editar.action";
    }

    @RequestMapping("/ticket/transformarEmDefeito.action")
    public String transformarEmDefeito(final Model model, final int ticketKey) throws SegurancaException {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE);

        final Ticket ticket = ticketDao.obter(ticketKey);

        if (ticket.getFilhos() != null && ticket.getFilhos().size() > 0) {
            model.addAttribute("erro",
                    "Esse projeto possui tarefas e por isso n&atilde;o pode ser transformada em um defeito.");
        } else {
            ticket.setTipoDeTicket(
                    (TipoDeTicket) sessionFactory.getCurrentSession().createCriteria(TipoDeTicket.class)
                            .add(Restrictions.eq("tipoDeTicketKey", TipoDeTicket.DEFEITO)).uniqueResult());
            ticketDao.salvar(ticket);
        }
        return "forward:/ticket/editar.action";
    }

    @RequestMapping("/ticket/verDescricao.action")
    public String verDescricao(final Model model, final int ticketKey) throws Exception {
        final Ticket ticket = ticketDao.obter(ticketKey);
        model.addAttribute("ticket", ticket);
        model.addAttribute("anexos", listarAnexos(ticketKey));
        return "/ticket/ticket.preview.jsp";
    }

    @RequestMapping("/ticket/editar.action")
    public String editar(final Model model, final Integer ticketKey, final Integer tipoDeTicketKey,
            final Integer backlogKey) throws SegurancaException {
        log.info("Entrou TicketController.editar");
        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE);

        if (ticketKey != null) {
            final Ticket ticket = ticketDao.obterComDependecias(ticketKey);

            if (ticket == null) {
                model.addAttribute("mensagem", "O Ticket #" + ticketKey + " n&atilde;o existe.");
                return "/branca.jsp";
            }
            model.addAttribute("sprints", sprintDao.listarSprintsEmAberto());
            model.addAttribute("ticket", ticket);
            model.addAttribute("anexos", listarAnexos(ticketKey));
        } else {
            final Ticket novoTicket = new Ticket();
            novoTicket.setReporter(Seguranca.getUsuario());
            novoTicket.setTipoDeTicket(
                    (TipoDeTicket) sessionFactory.getCurrentSession().get(TipoDeTicket.class, tipoDeTicketKey));
            novoTicket.setBacklog((Backlog) sessionFactory.getCurrentSession().get(Backlog.class, backlogKey));
            model.addAttribute("ticket", novoTicket);
            model.addAttribute("tipoDeTicketKey", tipoDeTicketKey);
        }

        model.addAttribute("clientes", clienteDao.listar());
        model.addAttribute("testadores", usuarioDao.listarEquipe());
        model.addAttribute("desenvolvedores", usuarioDao.listarEquipe());
        model.addAttribute("kanbanStatus", kanbanStatusDao.listar());
        log.info("Executou TicketController.editar");
        return VIEW_EDITAR;
    }

    @RequestMapping("/ticket/incluirTarefa.action")
    public String incluirTarefa(final Model model, final int paiKey) throws Exception {

        Seguranca.validarPermissao(Papel.PRODUCT_OWNER, Papel.EQUIPE);

        final Ticket pai = ticketDao.obter(paiKey);

        final Ticket tarefa = new Ticket();
        copiarDadosDoPai(pai, tarefa);
        tarefa.setPrioridade(9999);
        tarefa.setSprint(pai.getSprint());
        model.addAttribute("ticket", tarefa);
        model.addAttribute("tipoDeTicketKey", TipoDeTicket.TAREFA);
        model.addAttribute("kanbanStatus", kanbanStatusDao.listar());
        model.addAttribute("testadores", usuarioDao.listarEquipe());
        model.addAttribute("desenvolvedores", usuarioDao.listarEquipe());

        return VIEW_EDITAR;
    }

    private void copiarDadosDoPai(final Ticket pai, final Ticket filho) {
        log.info("Entrou TicketController.copiarDadosDoPai");
        filho.setPai(pai);
        if (filho.getReporter() == null || filho.getReporter().getUsername() == null) {
            filho.setReporter(Seguranca.getUsuario());
        }
        filho.setTipoDeTicket(tipoDeTicketDao.obter(TipoDeTicket.TAREFA));
        filho.setBacklog(pai.getBacklog());
        addSprintToTicket(filho);
        // filho.setSprint(pai.getSprint());
        filho.setCliente(pai.getCliente());
        filho.setSolicitador(pai.getSolicitador());
        log.info("Executou TicketController.copiarDadosDoPai");
    }

    @RequestMapping("/ticket/alterarOrdemDasTarefas.action")
    public void alterarOrdem(final int pai, final int ticketKey[]) {
        final Ticket ticketPai = ticketDao.obterComDependecias(pai);
        int prioridade = 0;
        for (final int filhoKey : ticketKey) {
            final Ticket ticketFilho = ticketPai.getFilhoPorKey(filhoKey);
            if (ticketFilho != null) {
                ticketFilho.setPrioridade(++prioridade);
                ticketDao.salvar(ticketFilho);
            }
        }
    }
}