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

changeset 86
0a658e53177c
parent 83
24a3596b8f98
child 88
1438e5a22c55
--- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Sat May 30 18:12:38 2020 +0200
+++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Mon Jun 01 14:46:58 2020 +0200
@@ -32,6 +32,7 @@
 import de.uapcore.lightpit.*;
 import de.uapcore.lightpit.dao.DataAccessObjects;
 import de.uapcore.lightpit.entities.*;
+import de.uapcore.lightpit.viewmodel.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,7 +43,10 @@
 import java.io.IOException;
 import java.sql.Date;
 import java.sql.SQLException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -59,41 +63,81 @@
     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");
-    public static final String SESSION_ATTR_HIDE_ZEROS = fqn(ProjectsModule.class, "stats_hide_zeros");
 
     private class SessionSelection {
         final HttpSession session;
+        final HttpServletRequest req;
+        final DataAccessObjects dao;
         Project project;
         Version version;
         Issue issue;
 
-        SessionSelection(HttpServletRequest req, Project project) {
-            this.session = req.getSession();
-            this.project = project;
+        SessionSelection(HttpServletRequest req, DataAccessObjects dao) {
+            this.req = req;
+            this.dao = dao;
+            session = req.getSession();
+        }
+
+        void newProject() {
+            project = null;
             version = null;
             issue = null;
             updateAttributes();
+            project = new Project(-1);
+            updateAttributes();
+        }
+
+        void newVersion() throws SQLException {
+            project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
+            syncProject();
+            version = null;
+            issue = null;
+            updateAttributes();
+            version = new Version(-1);
+            version.setProject(project);
+            updateAttributes();
         }
 
-        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());
+        void newIssue() throws SQLException {
+            project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
+            syncProject();
+            version = null;
+            issue = null;
+            updateAttributes();
+            issue = new Issue(-1);
+            issue.setProject(project);
+            updateAttributes();
+        }
+
+        void selectVersion(Version selectedVersion) throws SQLException {
+            issue = null;
+            version = selectedVersion;
+            if (!version.getProject().equals(project)) {
+                project = dao.getProjectDao().find(version.getProject().getId());
             }
-            if (issue != null) {
-                version = null; // show the issue globally
-                project = projectDao.find(issue.getProject().getId());
+            // our object contains more details
+            version.setProject(project);
+            updateAttributes();
+        }
+
+        void selectIssue(Issue selectedIssue) throws SQLException {
+            issue = selectedIssue;
+            if (!issue.getProject().equals(project)) {
+                project = dao.getProjectDao().find(issue.getProject().getId());
             }
+            // our object contains more details
+            issue.setProject(project);
+            if (!issue.getResolvedVersions().contains(version) && !issue.getScheduledVersions().contains(version)
+                    && !issue.getAffectedVersions().contains(version)) {
+                version = null;
+            }
+            updateAttributes();
+        }
 
+        void syncProject() throws SQLException {
             final var projectSelection = getParameter(req, Integer.class, "pid");
             if (projectSelection.isPresent()) {
-                final var selectedProject = projectDao.find(projectSelection.get());
+                final var selectedProject = dao.getProjectDao().find(projectSelection.get());
                 if (!Objects.equals(selectedProject, project)) {
                     // reset version and issue if project changed
                     version = null;
@@ -101,51 +145,57 @@
                 }
                 project = selectedProject;
             } else {
-                final var sessionProject = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
-                project = sessionProject == null ? null : projectDao.find(sessionProject.getId());
+                project = project == null ? null : dao.getProjectDao().find(project.getId());
+            }
+        }
+
+        void syncVersion() throws SQLException {
+            final var versionSelection = getParameter(req, Integer.class, "vid");
+            if (versionSelection.isPresent()) {
+                if (versionSelection.get() < 0) {
+                    version = null;
+                } else {
+                    final var selectedVersion = dao.getVersionDao().find(versionSelection.get());
+                    if (!Objects.equals(selectedVersion, version)) {
+                        issue = null;
+                    }
+                    selectVersion(selectedVersion);
+                }
+            } else {
+                version = version == null ? null : dao.getVersionDao().find(version.getId());
             }
+        }
+
+        void syncIssue() throws SQLException {
+            final var issueSelection = getParameter(req, Integer.class, "issue");
+            if (issueSelection.isPresent()) {
+                final var selectedIssue = dao.getIssueDao().find(issueSelection.get());
+                dao.getIssueDao().joinVersionInformation(selectedIssue);
+                selectIssue(selectedIssue);
+            } else {
+                issue = issue == null ? null : dao.getIssueDao().find(issue.getId());
+            }
+        }
+
+        void sync() throws SQLException {
+            project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT);
+            version = (Version) session.getAttribute(SESSION_ATTR_SELECTED_VERSION);
+            issue = (Issue) session.getAttribute(SESSION_ATTR_SELECTED_ISSUE);
+
+            syncProject();
+            syncVersion();
+            syncIssue();
+
             updateAttributes();
         }
 
-        void selectVersion(Version version) {
-            this.project = version.getProject();
-            this.version = version;
-            this.issue = null;
-            updateAttributes();
-        }
-
-        void selectIssue(Issue issue) {
-            this.project = issue.getProject();
-            this.issue = issue;
-            this.version = null;
-            updateAttributes();
-        }
-
-        void updateAttributes() {
+        private void updateAttributes() {
             session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, project);
             session.setAttribute(SESSION_ATTR_SELECTED_VERSION, version);
             session.setAttribute(SESSION_ATTR_SELECTED_ISSUE, issue);
         }
     }
 
-    private void setAttributeHideZeros(HttpServletRequest req) {
-        final Boolean value;
-        final var param = getParameter(req, Boolean.class, "reduced");
-        if (param.isPresent()) {
-            value = param.get();
-            req.getSession().setAttribute(SESSION_ATTR_HIDE_ZEROS, value);
-        } else {
-            final var sessionValue = req.getSession().getAttribute(SESSION_ATTR_HIDE_ZEROS);
-            if (sessionValue != null) {
-                value = (Boolean) sessionValue;
-            } else {
-                value = false;
-                req.getSession().setAttribute(SESSION_ATTR_HIDE_ZEROS, value);
-            }
-        }
-        req.setAttribute("statsHideZeros", value);
-    }
-
     @Override
     protected String getResourceBundleName() {
         return "localization.projects";
@@ -162,10 +212,10 @@
      * Creates the breadcrumb menu.
      *
      * @param level           the current active level (0: root, 1: project, 2: version, 3: issue list, 4: issue)
-     * @param sessionSelection the currently selected objects
+     * @param selection the currently selected objects
      * @return a dynamic breadcrumb menu trying to display as many levels as possible
      */
-    private List<MenuEntry> getBreadcrumbs(int level, SessionSelection sessionSelection) {
+    private List<MenuEntry> getBreadcrumbs(int level, SessionSelection selection) {
         MenuEntry entry;
 
         final var breadcrumbs = new ArrayList<MenuEntry>();
@@ -174,47 +224,49 @@
         breadcrumbs.add(entry);
         if (level == BREADCRUMB_LEVEL_ROOT) entry.setActive(true);
 
-        if (sessionSelection.project != null) {
-            if (sessionSelection.project.getId() < 0) {
+        if (selection.project != null) {
+            if (selection.project.getId() < 0) {
                 entry = new MenuEntry(new ResourceKey("localization.projects", "button.create"),
                         "projects/edit");
             } else {
-                entry = new MenuEntry(sessionSelection.project.getName(),
-                        "projects/view?pid=" + sessionSelection.project.getId());
+                entry = new MenuEntry(selection.project.getName(),
+                        "projects/view?pid=" + selection.project.getId());
             }
             if (level == BREADCRUMB_LEVEL_PROJECT) entry.setActive(true);
             breadcrumbs.add(entry);
         }
 
-        if (sessionSelection.version != null) {
-            if (sessionSelection.version.getId() < 0) {
+        if (selection.version != null) {
+            if (selection.version.getId() < 0) {
                 entry = new MenuEntry(new ResourceKey("localization.projects", "button.version.create"),
                         "projects/versions/edit");
             } else {
-                entry = new MenuEntry(sessionSelection.version.getName(),
-                        // TODO: change link to issue overview for that version
-                        "projects/versions/edit?id=" + sessionSelection.version.getId());
+                entry = new MenuEntry(selection.version.getName(),
+                        "projects/versions/view?vid=" + selection.version.getId());
             }
             if (level == BREADCRUMB_LEVEL_VERSION) entry.setActive(true);
             breadcrumbs.add(entry);
         }
 
-        if (sessionSelection.project != null) {
+        if (selection.project != null) {
+            String path = "projects/issues/?pid=" + selection.project.getId();
+            if (selection.version != null) {
+                path += "&vid="+selection.version.getId();
+            }
             entry = new MenuEntry(new ResourceKey("localization.projects", "menu.issues"),
-                    // TODO: maybe also add selected version
-                    "projects/issues/?pid=" + sessionSelection.project.getId());
+                    path);
             if (level == BREADCRUMB_LEVEL_ISSUE_LIST) entry.setActive(true);
             breadcrumbs.add(entry);
         }
 
-        if (sessionSelection.issue != null) {
-            if (sessionSelection.issue.getId() < 0) {
+        if (selection.issue != null) {
+            if (selection.issue.getId() < 0) {
                 entry = new MenuEntry(new ResourceKey("localization.projects", "button.issue.create"),
                         "projects/issues/edit");
             } else {
-                entry = new MenuEntry("#" + sessionSelection.issue.getId(),
+                entry = new MenuEntry("#" + selection.issue.getId(),
                         // TODO: maybe change link to a view rather than directly opening the editor
-                        "projects/issues/edit?id=" + sessionSelection.issue.getId());
+                        "projects/issues/edit?issue=" + selection.issue.getId());
             }
             if (level == BREADCRUMB_LEVEL_ISSUE) entry.setActive(true);
             breadcrumbs.add(entry);
@@ -226,8 +278,22 @@
     @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);
+        sessionSelection.sync();
+
+        final var projectDao = dao.getProjectDao();
+        final var versionDao = dao.getVersionDao();
+
+        final var projectList = projectDao.list();
+
+        final var viewModel = new ProjectIndexView();
+        for (var project : projectList) {
+            final var info = new ProjectInfo(project);
+            info.setVersions(versionDao.list(project));
+            info.setIssueSummary(projectDao.getIssueSummary(project));
+            viewModel.getProjects().add(info);
+        }
+
+        setViewModel(req, viewModel);
         setContentPage(req, "projects");
         setStylesheet(req, "projects");
 
@@ -236,17 +302,24 @@
         return ResponseType.HTML;
     }
 
-    private void configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
-        req.setAttribute("project", selection.project);
-        req.setAttribute("users", dao.getUserDao().list());
+    private ProjectEditView configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
+        final var viewModel = new ProjectEditView();
+        viewModel.setProject(selection.project);
+        viewModel.setUsers(dao.getUserDao().list());
+        setViewModel(req, viewModel);
         setContentPage(req, "project-form");
         setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, selection));
+        return viewModel;
     }
 
     @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)));
+        final var selection = new SessionSelection(req, dao);
+        if (getParameter(req, Integer.class, "pid").isEmpty()) {
+            selection.newProject();
+        } else {
+            selection.sync();
+        }
 
         configureEditForm(req, dao, selection);
 
@@ -272,10 +345,12 @@
             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
             LOG.debug("Successfully updated project {}", 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);
-            configureEditForm(req, dao, new SessionSelection(req, project));
+            final var selection = new SessionSelection(req, dao);
+            selection.project = project;
+            final var vm = configureEditForm(req, dao, selection);
+            vm.setErrorText(ex.getMessage()); // TODO: error text
         }
 
         return ResponseType.HTML;
@@ -283,127 +358,143 @@
 
     @RequestMapping(requestPath = "view", method = HttpMethod.GET)
     public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
-        final var sessionSelection = new SessionSelection(req, dao);
-        if (sessionSelection.project == null) {
+        final var selection = new SessionSelection(req, dao);
+        selection.sync();
+
+        if (selection.project == null) {
             resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
             return ResponseType.NONE;
         }
 
         final var versionDao = dao.getVersionDao();
-        final var versions = versionDao.list(sessionSelection.project);
-        final var statsAffected = new ArrayList<VersionStatistics>();
-        final var statsScheduled = new ArrayList<VersionStatistics>();
-        final var statsResolved = new ArrayList<VersionStatistics>();
-        for (Version version : versions) {
-            statsAffected.add(versionDao.statsOpenedIssues(version));
-            statsScheduled.add(versionDao.statsScheduledIssues(version));
-            statsResolved.add(versionDao.statsResolvedIssues(version));
-        }
+        final var issueDao = dao.getIssueDao();
 
-        setAttributeHideZeros(req);
+        final var viewModel = new ProjectView(selection.project);
+        final var issues = issueDao.list(selection.project);
+        for (var issue : issues) issueDao.joinVersionInformation(issue);
+        viewModel.setIssues(issues);
+        viewModel.setVersions(versionDao.list(selection.project));
+        viewModel.updateVersionInfo();
+        setViewModel(req, viewModel);
 
-        req.setAttribute("project", sessionSelection.project);
-        req.setAttribute("versions", versions);
-        req.setAttribute("statsAffected", statsAffected);
-        req.setAttribute("statsScheduled", statsScheduled);
-        req.setAttribute("statsResolved", statsResolved);
-
-        req.setAttribute("issueStatusEnum", IssueStatus.values());
-        req.setAttribute("issueCategoryEnum", IssueCategory.values());
-
-        setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, sessionSelection));
+        setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, selection));
         setContentPage(req, "project-details");
         setStylesheet(req, "projects");
 
         return ResponseType.HTML;
     }
 
-    private void configureEditVersionForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
-        final var versionDao = dao.getVersionDao();
-        req.setAttribute("projects", dao.getProjectDao().list());
-        req.setAttribute("version", selection.version);
-        req.setAttribute("versionStatusEnum", VersionStatus.values());
+    @RequestMapping(requestPath = "versions/view", method = HttpMethod.GET)
+    public ResponseType viewVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
+        final var selection = new SessionSelection(req, dao);
+        selection.sync();
+        if (selection.version == null) {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return ResponseType.NONE;
+        }
 
-        req.setAttribute("issueStatusEnum", IssueStatus.values());
-        req.setAttribute("issueCategoryEnum", IssueCategory.values());
-        req.setAttribute("statsAffected", versionDao.statsOpenedIssues(selection.version));
-        req.setAttribute("statsScheduled", versionDao.statsScheduledIssues(selection.version));
-        req.setAttribute("statsResolved", versionDao.statsResolvedIssues(selection.version));
-        setAttributeHideZeros(req);
+        final var issueDao = dao.getIssueDao();
+        final var viewModel = new VersionView(selection.version);
+        final var issues = issueDao.list(selection.version);
+        for (var issue : issues) issueDao.joinVersionInformation(issue);
+        viewModel.setIssues(issues);
+        setViewModel(req, viewModel);
 
+        setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_VERSION, selection));
+        setContentPage(req, "version");
+        setStylesheet(req, "projects");
+
+        return ResponseType.HTML;
+    }
+
+    private VersionEditView configureEditVersionForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
+        final var viewModel = new VersionEditView(selection.version);
+        if (selection.version.getProject() == null) {
+            viewModel.setProjects(dao.getProjectDao().list());
+        }
+        setViewModel(req, viewModel);
         setContentPage(req, "version-form");
         setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_VERSION, selection));
+        return viewModel;
     }
 
     @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET)
-    public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
-        final var sessionSelection = new SessionSelection(req, dao);
+    public ResponseType editVersion(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
+        final var selection = new SessionSelection(req, dao);
+        if (getParameter(req, Integer.class, "vid").isEmpty()) {
+            selection.newVersion();
+        } else {
+            selection.sync();
+        }
 
-        sessionSelection.selectVersion(findByParameter(req, Integer.class, "id", dao.getVersionDao()::find)
-                .orElse(new Version(-1, sessionSelection.project)));
-        configureEditVersionForm(req, dao, sessionSelection);
+        configureEditVersionForm(req, dao, selection);
 
         return ResponseType.HTML;
     }
 
     @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST)
     public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
-        final var sessionSelection = new SessionSelection(req, dao);
 
-        var version = new Version(-1, sessionSelection.project);
+        var version = new Version(-1);
         try {
-            version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project);
+            version = new Version(getParameter(req, Integer.class, "id").orElseThrow());
+            version.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
             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);
 
             // specifying the pid parameter will purposely reset the session selected version!
-            setRedirectLocation(req, "./projects/view?pid="+sessionSelection.project.getId());
+            setRedirectLocation(req, "./projects/view?pid="+version.getProject().getId());
             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
-            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);
-            sessionSelection.selectVersion(version);
-            configureEditVersionForm(req, dao, sessionSelection);
+            final var selection = new SessionSelection(req, dao);
+            selection.selectVersion(version);
+            final var viewModel = configureEditVersionForm(req, dao, selection);
+            // TODO: set Error Text
         }
 
         return ResponseType.HTML;
     }
 
-    private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
+    private IssueEditView configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
+        final var viewModel = new IssueEditView(selection.issue);
 
-        if (selection.issue.getProject() == null || selection.issue.getProject().getId() < 0) {
-            req.setAttribute("projects", dao.getProjectDao().list());
-            req.setAttribute("versions", Collections.<Version>emptyList());
+        if (selection.issue.getProject() == null) {
+            viewModel.setProjects(dao.getProjectDao().list());
         } else {
-            req.setAttribute("projects", Collections.<Project>emptyList());
-            req.setAttribute("versions", dao.getVersionDao().list(selection.issue.getProject()));
+            viewModel.setVersions(dao.getVersionDao().list(selection.issue.getProject()));
         }
-
-        dao.getIssueDao().joinVersionInformation(selection.issue);
-        req.setAttribute("issue", selection.issue);
-        req.setAttribute("issueStatusEnum", IssueStatus.values());
-        req.setAttribute("issueCategoryEnum", IssueCategory.values());
-        req.setAttribute("users", dao.getUserDao().list());
+        viewModel.setUsers(dao.getUserDao().list());
+        setViewModel(req, viewModel);
 
         setContentPage(req, "issue-form");
         setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE, selection));
+        return viewModel;
     }
 
     @RequestMapping(requestPath = "issues/", method = HttpMethod.GET)
     public ResponseType issues(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
-        final var sessionSelection = new SessionSelection(req, dao);
-        if (sessionSelection.project == null) {
+        final var selection = new SessionSelection(req, dao);
+        selection.sync();
+        if (selection.project == null) {
             resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected.");
             return ResponseType.NONE;
         }
 
-        req.setAttribute("issues", dao.getIssueDao().list(sessionSelection.project));
+        final var viewModel = new IssuesView();
+        viewModel.setProject(selection.project);
+        if (selection.version == null) {
+            viewModel.setIssues(dao.getIssueDao().list(selection.project));
+        } else {
+            viewModel.setVersion(selection.version);
+            viewModel.setIssues(dao.getIssueDao().list(selection.version));
+        }
+        setViewModel(req, viewModel);
 
-        setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE_LIST, sessionSelection));
+        setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE_LIST, selection));
         setContentPage(req, "issues");
         setStylesheet(req, "projects");
 
@@ -412,22 +503,25 @@
 
     @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET)
     public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
-        final var sessionSelection = new SessionSelection(req, dao);
+        final var selection = new SessionSelection(req, dao);
+        if (getParameter(req, Integer.class, "issue").isEmpty()) {
+            selection.newIssue();
+        } else {
+            selection.sync();
+        }
 
-        sessionSelection.selectIssue(findByParameter(req, Integer.class, "id",
-                dao.getIssueDao()::find).orElse(new Issue(-1, sessionSelection.project)));
-        configureEditIssueForm(req, dao, sessionSelection);
+        configureEditIssueForm(req, dao, selection);
 
         return ResponseType.HTML;
     }
 
     @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST)
     public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException {
-        final var sessionSelection = new SessionSelection(req, dao);
 
-        Issue issue = new Issue(-1, sessionSelection.project);
+        Issue issue = new Issue(-1);
         try {
-            issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project);
+            issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
+            issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow()));
             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());
@@ -440,17 +534,17 @@
             getParameter(req, Integer[].class, "affected")
                     .map(Stream::of)
                     .map(stream ->
-                        stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList())
+                        stream.map(Version::new).collect(Collectors.toList())
                     ).ifPresent(issue::setAffectedVersions);
             getParameter(req, Integer[].class, "scheduled")
                     .map(Stream::of)
                     .map(stream ->
-                            stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList())
+                            stream.map(Version::new).collect(Collectors.toList())
                     ).ifPresent(issue::setScheduledVersions);
             getParameter(req, Integer[].class, "resolved")
                     .map(Stream::of)
                     .map(stream ->
-                            stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList())
+                            stream.map(Version::new).collect(Collectors.toList())
                     ).ifPresent(issue::setResolvedVersions);
 
             dao.getIssueDao().saveOrUpdate(issue);
@@ -458,13 +552,14 @@
             // specifying the issue parameter keeps the edited issue as breadcrumb
             setRedirectLocation(req, "./projects/issues/?issue="+issue.getId());
             setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
-            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);
-            sessionSelection.selectIssue(issue);
-            configureEditIssueForm(req, dao, sessionSelection);
+            final var selection = new SessionSelection(req, dao);
+            selection.selectIssue(issue);
+            final var viewModel = configureEditIssueForm(req, dao, selection);
+            // TODO: set Error Text
         }
 
         return ResponseType.HTML;

mercurial