universe@41: /* universe@41: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@41: * universe@41: * Copyright 2018 Mike Becker. All rights reserved. universe@41: * universe@41: * Redistribution and use in source and binary forms, with or without universe@41: * modification, are permitted provided that the following conditions are met: universe@41: * universe@41: * 1. Redistributions of source code must retain the above copyright universe@41: * notice, this list of conditions and the following disclaimer. universe@41: * universe@41: * 2. Redistributions in binary form must reproduce the above copyright universe@41: * notice, this list of conditions and the following disclaimer in the universe@41: * documentation and/or other materials provided with the distribution. universe@41: * universe@41: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@41: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@41: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE universe@41: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE universe@41: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR universe@41: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF universe@41: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS universe@41: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN universe@41: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) universe@41: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE universe@41: * POSSIBILITY OF SUCH DAMAGE. universe@41: * universe@41: */ universe@41: package de.uapcore.lightpit.modules; universe@41: universe@41: universe@41: import de.uapcore.lightpit.*; universe@41: import de.uapcore.lightpit.dao.DataAccessObjects; universe@64: import de.uapcore.lightpit.entities.*; universe@59: import org.slf4j.Logger; universe@59: import org.slf4j.LoggerFactory; universe@41: universe@41: import javax.servlet.annotation.WebServlet; universe@41: import javax.servlet.http.HttpServletRequest; universe@59: import javax.servlet.http.HttpServletResponse; universe@59: import java.io.IOException; universe@47: import java.sql.SQLException; universe@71: import java.util.ArrayList; universe@71: import java.util.List; universe@59: import java.util.NoSuchElementException; universe@71: import java.util.Optional; universe@41: universe@52: import static de.uapcore.lightpit.Functions.fqn; universe@52: universe@41: @LightPITModule( universe@41: bundleBaseName = "localization.projects", universe@41: modulePath = "projects", universe@41: defaultPriority = 20 universe@41: ) universe@41: @WebServlet( universe@41: name = "ProjectsModule", universe@41: urlPatterns = "/projects/*" universe@41: ) universe@41: public final class ProjectsModule extends AbstractLightPITServlet { universe@41: universe@59: private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class); universe@59: universe@52: public static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected-project"); universe@52: universe@64: private Project getSelectedProject(HttpServletRequest req, DataAccessObjects dao) throws SQLException { universe@64: final var projectDao = dao.getProjectDao(); universe@64: final var session = req.getSession(); universe@64: final var projectSelection = getParameter(req, Integer.class, "pid"); universe@71: final Project selectedProject; universe@64: if (projectSelection.isPresent()) { universe@71: selectedProject = projectDao.find(projectSelection.get()); universe@64: } else { universe@71: final var sessionProject = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); universe@71: selectedProject = sessionProject == null ? null : projectDao.find(sessionProject.getId()); universe@64: } universe@71: session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, selectedProject); universe@71: return selectedProject; universe@71: } universe@71: universe@71: universe@71: /** universe@71: * Creates the breadcrumb menu. universe@71: * universe@71: * @param level the current active level universe@71: * @param selectedProject the selected project, if any, or null universe@71: * @return a dynamic breadcrumb menu trying to display as many levels as possible universe@71: */ universe@71: private List getBreadcrumbs(int level, universe@71: Project selectedProject) { universe@71: MenuEntry entry; universe@71: universe@71: final var breadcrumbs = new ArrayList(); universe@71: entry = new MenuEntry(new ResourceKey("localization.projects", "menuLabel"), universe@71: "projects/", 0); universe@71: breadcrumbs.add(entry); universe@71: if (level == 0) entry.setActive(true); universe@71: universe@71: if (selectedProject == null) universe@71: return breadcrumbs; universe@71: universe@71: entry = new MenuEntry(selectedProject.getName(), universe@71: "projects/view?pid=" + selectedProject.getId(), 1); universe@71: if (level == 1) entry.setActive(true); universe@71: universe@71: breadcrumbs.add(entry); universe@71: return breadcrumbs; universe@64: } universe@64: universe@61: @RequestMapping(method = HttpMethod.GET) universe@47: public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException { universe@64: universe@52: final var projectList = dao.getProjectDao().list(); universe@52: req.setAttribute("projects", projectList); universe@47: setDynamicFragment(req, "projects"); universe@52: setStylesheet(req, "projects"); universe@52: universe@71: final var selectedProject = getSelectedProject(req, dao); universe@71: setBreadcrumbs(req, getBreadcrumbs(0, selectedProject)); universe@45: universe@45: return ResponseType.HTML; universe@45: } universe@45: universe@71: private void configureEditForm(HttpServletRequest req, DataAccessObjects dao, Optional project) throws SQLException { universe@71: if (project.isPresent()) { universe@71: req.setAttribute("project", project.get()); universe@71: setBreadcrumbs(req, getBreadcrumbs(1, project.get())); universe@71: } else { universe@71: req.setAttribute("project", new Project(-1)); universe@71: setBreadcrumbs(req, getBreadcrumbs(0, null)); universe@71: } universe@71: universe@71: req.setAttribute("users", dao.getUserDao().list()); universe@71: setDynamicFragment(req, "project-form"); universe@71: } universe@71: universe@47: @RequestMapping(requestPath = "edit", method = HttpMethod.GET) universe@51: public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { universe@47: universe@71: Optional project = findByParameter(req, Integer.class, "id", dao.getProjectDao()::find); universe@71: configureEditForm(req, dao, project); universe@71: if (project.isPresent()) { universe@71: req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, project.get()); universe@71: } universe@47: universe@47: return ResponseType.HTML; universe@47: } universe@47: universe@47: @RequestMapping(requestPath = "commit", method = HttpMethod.POST) universe@68: public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { universe@47: universe@71: Project project = null; universe@47: try { universe@47: project = new Project(getParameter(req, Integer.class, "id").orElseThrow()); universe@47: project.setName(getParameter(req, String.class, "name").orElseThrow()); universe@47: getParameter(req, String.class, "description").ifPresent(project::setDescription); universe@47: getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl); universe@47: getParameter(req, Integer.class, "owner").map( universe@47: ownerId -> ownerId >= 0 ? new User(ownerId) : null universe@47: ).ifPresent(project::setOwner); universe@47: universe@47: dao.getProjectDao().saveOrUpdate(project); universe@47: universe@70: setRedirectLocation(req, "./projects/"); universe@47: setDynamicFragment(req, Constants.DYN_FRAGMENT_COMMIT_SUCCESSFUL); universe@59: LOG.debug("Successfully updated project {}", project.getName()); universe@59: } catch (NoSuchElementException | NumberFormatException | SQLException ex) { universe@47: // TODO: set request attribute with error text universe@59: LOG.warn("Form validation failure: {}", ex.getMessage()); universe@59: LOG.debug("Details:", ex); universe@71: configureEditForm(req, dao, Optional.ofNullable(project)); universe@47: } universe@47: universe@47: return ResponseType.HTML; universe@47: } universe@47: universe@70: @RequestMapping(requestPath = "view", method = HttpMethod.GET) universe@70: public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { universe@64: final var selectedProject = getSelectedProject(req, dao); universe@59: if (selectedProject == null) { universe@59: resp.sendError(HttpServletResponse.SC_FORBIDDEN); universe@59: return ResponseType.NONE; universe@59: } universe@47: universe@59: req.setAttribute("versions", dao.getVersionDao().list(selectedProject)); universe@70: req.setAttribute("issues", dao.getIssueDao().list(selectedProject)); universe@70: universe@71: // TODO: add more levels depending on last visited location universe@71: setBreadcrumbs(req, getBreadcrumbs(1, selectedProject)); universe@71: universe@70: setDynamicFragment(req, "project-details"); universe@59: universe@59: return ResponseType.HTML; universe@59: } universe@59: universe@71: private void configureEditVersionForm(HttpServletRequest req, Optional version, Project selectedProject) { universe@71: req.setAttribute("version", version.orElse(new Version(-1, selectedProject))); universe@71: req.setAttribute("versionStatusEnum", VersionStatus.values()); universe@71: universe@71: setDynamicFragment(req, "version-form"); universe@71: } universe@71: universe@59: @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET) universe@59: public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { universe@64: final var selectedProject = getSelectedProject(req, dao); universe@59: if (selectedProject == null) { universe@59: resp.sendError(HttpServletResponse.SC_FORBIDDEN); universe@59: return ResponseType.NONE; universe@59: } universe@59: universe@71: configureEditVersionForm(req, universe@71: findByParameter(req, Integer.class, "id", dao.getVersionDao()::find), universe@71: selectedProject); universe@59: universe@59: return ResponseType.HTML; universe@59: } universe@59: universe@59: @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST) universe@64: public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { universe@64: final var selectedProject = getSelectedProject(req, dao); universe@59: if (selectedProject == null) { universe@59: resp.sendError(HttpServletResponse.SC_FORBIDDEN); universe@59: return ResponseType.NONE; universe@59: } universe@59: universe@71: Version version = null; universe@59: try { universe@59: version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), selectedProject); universe@59: version.setName(getParameter(req, String.class, "name").orElseThrow()); universe@59: getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal); universe@59: version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); universe@59: dao.getVersionDao().saveOrUpdate(version); universe@59: universe@59: setRedirectLocation(req, "./projects/versions/"); universe@59: setDynamicFragment(req, Constants.DYN_FRAGMENT_COMMIT_SUCCESSFUL); universe@59: LOG.debug("Successfully updated version {} for project {}", version.getName(), selectedProject.getName()); universe@59: } catch (NoSuchElementException | NumberFormatException | SQLException ex) { universe@59: // TODO: set request attribute with error text universe@59: LOG.warn("Form validation failure: {}", ex.getMessage()); universe@59: LOG.debug("Details:", ex); universe@71: configureEditVersionForm(req, Optional.ofNullable(version), selectedProject); universe@59: } universe@41: universe@43: return ResponseType.HTML; universe@41: } universe@64: universe@71: private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, Optional issue, Project selectedProject) throws SQLException { universe@71: universe@71: req.setAttribute("issue", issue.orElse(new Issue(-1, selectedProject))); universe@71: req.setAttribute("issueStatusEnum", IssueStatus.values()); universe@71: req.setAttribute("issueCategoryEnum", IssueCategory.values()); universe@71: req.setAttribute("versions", dao.getVersionDao().list(selectedProject)); universe@71: universe@71: setDynamicFragment(req, "issue-form"); universe@71: } universe@71: universe@64: @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET) universe@64: public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { universe@64: final var selectedProject = getSelectedProject(req, dao); universe@64: if (selectedProject == null) { universe@64: resp.sendError(HttpServletResponse.SC_FORBIDDEN); universe@64: return ResponseType.NONE; universe@64: } universe@64: universe@71: configureEditIssueForm(req, dao, universe@71: findByParameter(req, Integer.class, "id", dao.getIssueDao()::find), universe@71: selectedProject); universe@64: universe@64: return ResponseType.HTML; universe@64: } universe@64: universe@64: @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST) universe@64: public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { universe@64: final var selectedProject = getSelectedProject(req, dao); universe@64: if (selectedProject == null) { universe@64: resp.sendError(HttpServletResponse.SC_FORBIDDEN); universe@64: return ResponseType.NONE; universe@64: } universe@64: universe@71: Issue issue = null; universe@64: try { universe@64: issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), selectedProject); universe@64: universe@64: // TODO: implement universe@64: universe@64: dao.getIssueDao().saveOrUpdate(issue); universe@64: universe@64: setRedirectLocation(req, "./projects/issues/"); universe@64: setDynamicFragment(req, Constants.DYN_FRAGMENT_COMMIT_SUCCESSFUL); universe@64: LOG.debug("Successfully updated issue {} for project {}", issue.getId(), selectedProject.getName()); universe@64: } catch (NoSuchElementException | NumberFormatException | SQLException ex) { universe@64: // TODO: set request attribute with error text universe@64: LOG.warn("Form validation failure: {}", ex.getMessage()); universe@64: LOG.debug("Details:", ex); universe@71: configureEditIssueForm(req, dao, Optional.ofNullable(issue), selectedProject); universe@64: } universe@64: universe@64: return ResponseType.HTML; universe@64: } universe@41: }