src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java

changeset 75
33b6843fdf8a
parent 74
91d1fc2a3a14
child 76
82f71fb1758a
--- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Fri May 22 17:26:27 2020 +0200
+++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Fri May 22 21:23:57 2020 +0200
@@ -38,12 +38,14 @@
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
 import java.io.IOException;
+import java.sql.Date;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
-import java.util.Optional;
+import java.util.Objects;
 
 import static de.uapcore.lightpit.Functions.fqn;
 
@@ -61,32 +63,85 @@
     private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
 
     public static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected-project");
+    public static final String SESSION_ATTR_SELECTED_ISSUE = fqn(ProjectsModule.class, "selected-issue");
+    public static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected-version");
 
-    private Project getSelectedProject(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
-        final var projectDao = dao.getProjectDao();
-        final var session = req.getSession();
-        final var projectSelection = getParameter(req, Integer.class, "pid");
-        final Project selectedProject;
-        if (projectSelection.isPresent()) {
-            selectedProject = projectDao.find(projectSelection.get());
-        } else {
-            final var sessionProject = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
-            selectedProject = sessionProject == null ? null : projectDao.find(sessionProject.getId());
+    private class SessionSelection {
+        final HttpSession session;
+        Project project;
+        Version version;
+        Issue issue;
+
+        SessionSelection(HttpServletRequest req, Project project) {
+            this.session = req.getSession();
+            this.project = project;
+            version = null;
+            issue = null;
+            updateAttributes();
         }
-        session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, selectedProject);
-        return selectedProject;
+
+        SessionSelection(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
+            this.session = req.getSession();
+            final var issueDao = dao.getIssueDao();
+            final var projectDao = dao.getProjectDao();
+            final var issueSelection = getParameter(req, Integer.class, "issue");
+            if (issueSelection.isPresent()) {
+                issue = issueDao.find(issueSelection.get());
+            } else {
+                final var issue = (Issue) session.getAttribute(SESSION_ATTR_SELECTED_ISSUE);
+                this.issue = issue == null ? null : issueDao.find(issue.getId());
+            }
+            if (issue != null) {
+                version = null; // show the issue globally
+                project = projectDao.find(issue.getProject().getId());
+            }
+
+            final var projectSelection = getParameter(req, Integer.class, "pid");
+            if (projectSelection.isPresent()) {
+                final var selectedProject = projectDao.find(projectSelection.get());
+                if (!Objects.equals(selectedProject, project)) {
+                    // reset version and issue if project changed
+                    version = null;
+                    issue = null;
+                }
+                project = selectedProject;
+            } else {
+                final var sessionProject = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
+                project = sessionProject == null ? null : projectDao.find(sessionProject.getId());
+            }
+            updateAttributes();
+        }
+
+        void selectVersion(Version version) {
+            if (!version.getProject().equals(project)) throw new AssertionError("Nice, you implemented a bug!");
+            this.version = version;
+            this.issue = null;
+            updateAttributes();
+        }
+
+        void selectIssue(Issue issue) {
+            if (!issue.getProject().equals(project)) throw new AssertionError("Nice, you implemented a bug!");
+            this.issue = issue;
+            this.version = null;
+            updateAttributes();
+        }
+
+        void updateAttributes() {
+            session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, project);
+            session.setAttribute(SESSION_ATTR_SELECTED_VERSION, version);
+            session.setAttribute(SESSION_ATTR_SELECTED_ISSUE, issue);
+        }
     }
 
 
     /**
      * Creates the breadcrumb menu.
      *
-     * @param level           the current active level
-     * @param selectedProject the selected project, if any, or null
+     * @param level           the current active level (0: root, 1: project, 2: version, 3: issue)
+     * @param sessionSelection the currently selected objects
      * @return a dynamic breadcrumb menu trying to display as many levels as possible
      */
-    private List<MenuEntry> getBreadcrumbs(int level,
-                                           Project selectedProject) {
+    private List<MenuEntry> getBreadcrumbs(int level, SessionSelection sessionSelection) {
         MenuEntry entry;
 
         final var breadcrumbs = new ArrayList<MenuEntry>();
@@ -95,52 +150,77 @@
         breadcrumbs.add(entry);
         if (level == 0) entry.setActive(true);
 
-        if (selectedProject == null)
-            return breadcrumbs;
+        if (sessionSelection.project != null) {
+            if (sessionSelection.project.getId() < 0) {
+                entry = new MenuEntry(new ResourceKey("localization.projects", "button.create"),
+                        "projects/edit", 1);
+            } else {
+                entry = new MenuEntry(sessionSelection.project.getName(),
+                        "projects/view?pid=" + sessionSelection.project.getId(), 1);
+            }
+            if (level == 1) entry.setActive(true);
+            breadcrumbs.add(entry);
+        }
 
-        entry = new MenuEntry(selectedProject.getName(),
-                "projects/view?pid=" + selectedProject.getId(), 1);
-        if (level == 1) entry.setActive(true);
+        if (sessionSelection.version != null) {
+            if (sessionSelection.version.getId() < 0) {
+                entry = new MenuEntry(new ResourceKey("localization.projects", "button.version.create"),
+                        "projects/versions/edit", 2);
+            } else {
+                entry = new MenuEntry(sessionSelection.version.getName(),
+                        // TODO: change link to issue overview for that version
+                        "projects/versions/edit?id=" + sessionSelection.version.getId(), 2);
+            }
+            if (level == 2) entry.setActive(true);
+            breadcrumbs.add(entry);
+        }
 
-        breadcrumbs.add(entry);
+        if (sessionSelection.issue != null) {
+            entry = new MenuEntry(new ResourceKey("localization.projects", "menu.issues"),
+                    // TODO: change link to a separate issue view (maybe depending on the selected version)
+                    "projects/view?pid=" + sessionSelection.issue.getProject().getId(), 3);
+            breadcrumbs.add(entry);
+            if (sessionSelection.issue.getId() < 0) {
+                entry = new MenuEntry(new ResourceKey("localization.projects", "button.issue.create"),
+                        "projects/issues/edit", 2);
+            } else {
+                entry = new MenuEntry("#" + sessionSelection.issue.getId(),
+                        // TODO: maybe change link to a view rather than directly opening the editor
+                        "projects/issues/edit?id=" + sessionSelection.issue.getId(), 4);
+            }
+            if (level == 3) entry.setActive(true);
+            breadcrumbs.add(entry);
+        }
+
         return breadcrumbs;
     }
 
     @RequestMapping(method = HttpMethod.GET)
     public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
-
+        final var sessionSelection = new SessionSelection(req, dao);
         final var projectList = dao.getProjectDao().list();
         req.setAttribute("projects", projectList);
         setContentPage(req, "projects");
         setStylesheet(req, "projects");
 
-        final var selectedProject = getSelectedProject(req, dao);
-        setBreadcrumbs(req, getBreadcrumbs(0, selectedProject));
+        setBreadcrumbs(req, getBreadcrumbs(0, sessionSelection));
 
         return ResponseType.HTML;
     }
 
-    private void configureEditForm(HttpServletRequest req, DataAccessObjects dao, Optional<Project> project) throws SQLException {
-        if (project.isPresent()) {
-            req.setAttribute("project", project.get());
-            setBreadcrumbs(req, getBreadcrumbs(1, project.get()));
-        } else {
-            req.setAttribute("project", new Project(-1));
-            setBreadcrumbs(req, getBreadcrumbs(0, null));
-        }
-
+    private void configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
+        req.setAttribute("project", selection.project);
         req.setAttribute("users", dao.getUserDao().list());
         setContentPage(req, "project-form");
+        setBreadcrumbs(req, getBreadcrumbs(1, selection));
     }
 
     @RequestMapping(requestPath = "edit", method = HttpMethod.GET)
     public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
+        final var selection = new SessionSelection(req, findByParameter(req, Integer.class, "id",
+                dao.getProjectDao()::find).orElse(new Project(-1)));
 
-        Optional<Project> project = findByParameter(req, Integer.class, "id", dao.getProjectDao()::find);
-        configureEditForm(req, dao, project);
-        if (project.isPresent()) {
-            req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, project.get());
-        }
+        configureEditForm(req, dao, selection);
 
         return ResponseType.HTML;
     }
@@ -148,7 +228,7 @@
     @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
     public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
 
-        Project project = null;
+        Project project = new Project(-1);
         try {
             project = new Project(getParameter(req, Integer.class, "id").orElseThrow());
             project.setName(getParameter(req, String.class, "name").orElseThrow());
@@ -163,11 +243,11 @@
             setRedirectLocation(req, "./projects/");
             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
             LOG.debug("Successfully updated project {}", project.getName());
-        } catch (NoSuchElementException | NumberFormatException | SQLException ex) {
+        } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
             // TODO: set request attribute with error text
             LOG.warn("Form validation failure: {}", ex.getMessage());
             LOG.debug("Details:", ex);
-            configureEditForm(req, dao, Optional.ofNullable(project));
+            configureEditForm(req, dao, new SessionSelection(req, project));
         }
 
         return ResponseType.HTML;
@@ -175,123 +255,133 @@
 
     @RequestMapping(requestPath = "view", method = HttpMethod.GET)
     public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
-        final var selectedProject = getSelectedProject(req, dao);
-        if (selectedProject == null) {
-            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
-            return ResponseType.NONE;
-        }
+        final var sessionSelection = new SessionSelection(req, dao);
 
-        req.setAttribute("versions", dao.getVersionDao().list(selectedProject));
-        req.setAttribute("issues", dao.getIssueDao().list(selectedProject));
+        req.setAttribute("versions", dao.getVersionDao().list(sessionSelection.project));
+        req.setAttribute("issues", dao.getIssueDao().list(sessionSelection.project));
 
-        // TODO: add more levels depending on last visited location
-        setBreadcrumbs(req, getBreadcrumbs(1, selectedProject));
-
+        setBreadcrumbs(req, getBreadcrumbs(1, sessionSelection));
         setContentPage(req, "project-details");
 
         return ResponseType.HTML;
     }
 
-    private void configureEditVersionForm(HttpServletRequest req, Optional<Version> version, Project selectedProject) {
-        req.setAttribute("version", version.orElse(new Version(-1, selectedProject)));
+    private void configureEditVersionForm(HttpServletRequest req, SessionSelection selection) {
+        req.setAttribute("version", selection.version);
         req.setAttribute("versionStatusEnum", VersionStatus.values());
 
         setContentPage(req, "version-form");
+        setBreadcrumbs(req, getBreadcrumbs(2, selection));
     }
 
     @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET)
     public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
-        final var selectedProject = getSelectedProject(req, dao);
-        if (selectedProject == null) {
+        final var sessionSelection = new SessionSelection(req, dao);
+        if (sessionSelection.project == null) {
+            // TODO: remove this bullshit and only retrieve the object from session if we are creating a fresh version
             resp.sendError(HttpServletResponse.SC_FORBIDDEN);
             return ResponseType.NONE;
         }
 
-        configureEditVersionForm(req,
-                findByParameter(req, Integer.class, "id", dao.getVersionDao()::find),
-                selectedProject);
+        sessionSelection.selectVersion(findByParameter(req, Integer.class, "id", dao.getVersionDao()::find)
+                .orElse(new Version(-1, sessionSelection.project)));
+        configureEditVersionForm(req, sessionSelection);
 
         return ResponseType.HTML;
     }
 
     @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST)
     public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
-        final var selectedProject = getSelectedProject(req, dao);
-        if (selectedProject == null) {
+        final var sessionSelection = new SessionSelection(req, dao);
+        if (sessionSelection.project == null) {
+            // TODO: remove this bullshit and retrieve project id from hidden field
             resp.sendError(HttpServletResponse.SC_FORBIDDEN);
             return ResponseType.NONE;
         }
 
-        Version version = null;
+        var version = new Version(-1, sessionSelection.project);
         try {
-            version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), selectedProject);
+            version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project);
             version.setName(getParameter(req, String.class, "name").orElseThrow());
             getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
             version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
             dao.getVersionDao().saveOrUpdate(version);
 
-            setRedirectLocation(req, "./projects/versions/");
+            // specifying the pid parameter will purposely reset the session selected version!
+            setRedirectLocation(req, "./projects/view?pid="+sessionSelection.project.getId());
             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
-            LOG.debug("Successfully updated version {} for project {}", version.getName(), selectedProject.getName());
-        } catch (NoSuchElementException | NumberFormatException | SQLException ex) {
+            LOG.debug("Successfully updated version {} for project {}", version.getName(), sessionSelection.project.getName());
+        } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
             // TODO: set request attribute with error text
             LOG.warn("Form validation failure: {}", ex.getMessage());
             LOG.debug("Details:", ex);
-            configureEditVersionForm(req, Optional.ofNullable(version), selectedProject);
+            sessionSelection.selectVersion(version);
+            configureEditVersionForm(req, sessionSelection);
         }
 
         return ResponseType.HTML;
     }
 
-    private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, Optional<Issue> issue, Project selectedProject) throws SQLException {
-
-        req.setAttribute("issue", issue.orElse(new Issue(-1, selectedProject)));
+    private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
+        req.setAttribute("issue", selection.issue);
         req.setAttribute("issueStatusEnum", IssueStatus.values());
         req.setAttribute("issueCategoryEnum", IssueCategory.values());
-        req.setAttribute("versions", dao.getVersionDao().list(selectedProject));
+        req.setAttribute("versions", dao.getVersionDao().list(selection.project));
+        req.setAttribute("users", dao.getUserDao().list());
 
         setContentPage(req, "issue-form");
+        setBreadcrumbs(req, getBreadcrumbs(3, selection));
     }
 
     @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET)
     public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
-        final var selectedProject = getSelectedProject(req, dao);
-        if (selectedProject == null) {
+        final var sessionSelection = new SessionSelection(req, dao);
+        if (sessionSelection.project == null) {
+            // TODO: remove this bullshit and only retrieve the object from session if we are creating a fresh issue
             resp.sendError(HttpServletResponse.SC_FORBIDDEN);
             return ResponseType.NONE;
         }
 
-        configureEditIssueForm(req, dao,
-                findByParameter(req, Integer.class, "id", dao.getIssueDao()::find),
-                selectedProject);
+        sessionSelection.selectIssue(findByParameter(req, Integer.class, "id",
+                dao.getIssueDao()::find).orElse(new Issue(-1, sessionSelection.project)));
+        configureEditIssueForm(req, dao, sessionSelection);
 
         return ResponseType.HTML;
     }
 
     @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST)
     public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException {
-        final var selectedProject = getSelectedProject(req, dao);
-        if (selectedProject == null) {
+        // TODO: remove this bullshit and store the project ID as hidden field
+        final var sessionSelection = new SessionSelection(req, dao);
+        if (sessionSelection.project == null) {
             resp.sendError(HttpServletResponse.SC_FORBIDDEN);
             return ResponseType.NONE;
         }
 
-        Issue issue = null;
+        Issue issue = new Issue(-1, sessionSelection.project);
         try {
-            issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), selectedProject);
-
-            // TODO: implement
-
+            issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project);
+            getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
+            getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
+            issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
+            getParameter(req, Integer.class, "assignee").map(
+                    userid -> userid >= 0 ? new User(userid) : null
+            ).ifPresent(issue::setAssignee);
+            getParameter(req, String.class, "description").ifPresent(issue::setDescription);
+            getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
             dao.getIssueDao().saveOrUpdate(issue);
 
-            setRedirectLocation(req, "./projects/issues/");
+            // TODO: redirect to issue overview
+            // specifying the issue parameter keeps the edited issue as breadcrumb
+            setRedirectLocation(req, "./projects/view?issue="+issue.getId());
             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
-            LOG.debug("Successfully updated issue {} for project {}", issue.getId(), selectedProject.getName());
-        } catch (NoSuchElementException | NumberFormatException | SQLException ex) {
+            LOG.debug("Successfully updated issue {} for project {}", issue.getId(), sessionSelection.project.getName());
+        } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
             // TODO: set request attribute with error text
             LOG.warn("Form validation failure: {}", ex.getMessage());
             LOG.debug("Details:", ex);
-            configureEditIssueForm(req, dao, Optional.ofNullable(issue), selectedProject);
+            sessionSelection.selectIssue(issue);
+            configureEditIssueForm(req, dao, sessionSelection);
         }
 
         return ResponseType.HTML;

mercurial