1.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat Jan 23 14:47:59 2021 +0100 1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 1.3 @@ -1,660 +0,0 @@ 1.4 -/* 1.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 1.6 - * 1.7 - * Copyright 2021 Mike Becker. All rights reserved. 1.8 - * 1.9 - * Redistribution and use in source and binary forms, with or without 1.10 - * modification, are permitted provided that the following conditions are met: 1.11 - * 1.12 - * 1. Redistributions of source code must retain the above copyright 1.13 - * notice, this list of conditions and the following disclaimer. 1.14 - * 1.15 - * 2. Redistributions in binary form must reproduce the above copyright 1.16 - * notice, this list of conditions and the following disclaimer in the 1.17 - * documentation and/or other materials provided with the distribution. 1.18 - * 1.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 1.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 1.29 - * POSSIBILITY OF SUCH DAMAGE. 1.30 - * 1.31 - */ 1.32 -package de.uapcore.lightpit.modules; 1.33 - 1.34 - 1.35 -import de.uapcore.lightpit.*; 1.36 -import de.uapcore.lightpit.dao.DataAccessObject; 1.37 -import de.uapcore.lightpit.entities.*; 1.38 -import de.uapcore.lightpit.filter.AllFilter; 1.39 -import de.uapcore.lightpit.filter.IssueFilter; 1.40 -import de.uapcore.lightpit.filter.NoneFilter; 1.41 -import de.uapcore.lightpit.filter.SpecificFilter; 1.42 -import de.uapcore.lightpit.types.IssueCategory; 1.43 -import de.uapcore.lightpit.types.IssueStatus; 1.44 -import de.uapcore.lightpit.types.VersionStatus; 1.45 -import de.uapcore.lightpit.types.WebColor; 1.46 -import de.uapcore.lightpit.viewmodel.*; 1.47 -import de.uapcore.lightpit.viewmodel.util.IssueSorter; 1.48 -import org.slf4j.Logger; 1.49 -import org.slf4j.LoggerFactory; 1.50 - 1.51 -import javax.servlet.ServletException; 1.52 -import javax.servlet.annotation.WebServlet; 1.53 -import javax.servlet.http.HttpServletRequest; 1.54 -import javax.servlet.http.HttpServletResponse; 1.55 -import java.io.IOException; 1.56 -import java.sql.Date; 1.57 -import java.sql.SQLException; 1.58 -import java.util.NoSuchElementException; 1.59 -import java.util.Optional; 1.60 -import java.util.stream.Collectors; 1.61 -import java.util.stream.Stream; 1.62 - 1.63 -@WebServlet( 1.64 - name = "ProjectsModule", 1.65 - urlPatterns = "/projects/*" 1.66 -) 1.67 -public final class ProjectsModule extends AbstractServlet { 1.68 - 1.69 - private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class); 1.70 - 1.71 - private static int parseIntOrZero(String str) { 1.72 - try { 1.73 - return Integer.parseInt(str); 1.74 - } catch (NumberFormatException ex) { 1.75 - return 0; 1.76 - } 1.77 - } 1.78 - 1.79 - private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObject dao) { 1.80 - dao.listProjects().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add); 1.81 - 1.82 - if (pathParameters == null) 1.83 - return; 1.84 - 1.85 - // Select Project 1.86 - final var project = dao.findProjectByNode(pathParameters.get("project")); 1.87 - if (project == null) 1.88 - return; 1.89 - 1.90 - final var info = new ProjectInfo(project); 1.91 - info.setVersions(dao.listVersions(project)); 1.92 - info.setComponents(dao.listComponents(project)); 1.93 - info.setIssueSummary(dao.collectIssueSummary(project)); 1.94 - viewModel.setProjectInfo(info); 1.95 - 1.96 - // Select Version 1.97 - final var versionNode = pathParameters.get("version"); 1.98 - if (versionNode != null) { 1.99 - if ("no-version".equals(versionNode)) { 1.100 - viewModel.setVersionFilter(ProjectView.NO_VERSION); 1.101 - } else if ("all-versions".equals(versionNode)) { 1.102 - viewModel.setVersionFilter(ProjectView.ALL_VERSIONS); 1.103 - } else { 1.104 - viewModel.setVersionFilter(dao.findVersionByNode(project, versionNode)); 1.105 - } 1.106 - } 1.107 - 1.108 - // Select Component 1.109 - final var componentNode = pathParameters.get("component"); 1.110 - if (componentNode != null) { 1.111 - if ("no-component".equals(componentNode)) { 1.112 - viewModel.setComponentFilter(ProjectView.NO_COMPONENT); 1.113 - } else if ("all-components".equals(componentNode)) { 1.114 - viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS); 1.115 - } else { 1.116 - viewModel.setComponentFilter(dao.findComponentByNode(project, componentNode)); 1.117 - } 1.118 - } 1.119 - } 1.120 - 1.121 - private static String sanitizeNode(String node, String defaultValue) { 1.122 - String result = node == null || node.isBlank() ? defaultValue : node; 1.123 - result = result.replace('/', '-'); 1.124 - if (result.equals(".") || result.equals("..")) { 1.125 - return "_"+result; 1.126 - } else { 1.127 - return result; 1.128 - } 1.129 - } 1.130 - 1.131 - private void forwardView(HttpServletRequest req, HttpServletResponse resp, ProjectView viewModel, String name) throws ServletException, IOException { 1.132 - setViewModel(req, viewModel); 1.133 - setContentPage(req, name); 1.134 - setStylesheet(req, "projects"); 1.135 - setNavigationMenu(req, "project-navmenu"); 1.136 - renderSite(req, resp); 1.137 - } 1.138 - 1.139 - @RequestMapping(method = HttpMethod.GET) 1.140 - public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws ServletException, IOException { 1.141 - final var viewModel = new ProjectView(); 1.142 - populate(viewModel, null, dao); 1.143 - 1.144 - for (var info : viewModel.getProjectList()) { 1.145 - info.setVersions(dao.listVersions(info.getProject())); 1.146 - info.setIssueSummary(dao.collectIssueSummary(info.getProject())); 1.147 - } 1.148 - 1.149 - forwardView(req, resp, viewModel, "projects"); 1.150 - } 1.151 - 1.152 - private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObject dao) { 1.153 - viewModel.setProject(project); 1.154 - viewModel.setUsers(dao.listUsers()); 1.155 - } 1.156 - 1.157 - @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET) 1.158 - public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws IOException, SQLException, ServletException { 1.159 - final var viewModel = new ProjectEditView(); 1.160 - populate(viewModel, pathParams, dao); 1.161 - 1.162 - if (!viewModel.isProjectInfoPresent()) { 1.163 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.164 - return; 1.165 - } 1.166 - 1.167 - configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao); 1.168 - forwardView(req, resp, viewModel, "project-form"); 1.169 - } 1.170 - 1.171 - @RequestMapping(requestPath = "create", method = HttpMethod.GET) 1.172 - public void create(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws SQLException, ServletException, IOException { 1.173 - final var viewModel = new ProjectEditView(); 1.174 - populate(viewModel, null, dao); 1.175 - configureProjectEditor(viewModel, new Project(-1), dao); 1.176 - forwardView(req, resp, viewModel, "project-form"); 1.177 - } 1.178 - 1.179 - @RequestMapping(requestPath = "commit", method = HttpMethod.POST) 1.180 - public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException { 1.181 - 1.182 - try { 1.183 - final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); 1.184 - project.setName(getParameter(req, String.class, "name").orElseThrow()); 1.185 - 1.186 - final var node = getParameter(req, String.class, "node").orElse(null); 1.187 - project.setNode(sanitizeNode(node, project.getName())); 1.188 - getParameter(req, Integer.class, "ordinal").ifPresent(project::setOrdinal); 1.189 - 1.190 - getParameter(req, String.class, "description").ifPresent(project::setDescription); 1.191 - getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl); 1.192 - getParameter(req, Integer.class, "owner").map( 1.193 - ownerId -> ownerId >= 0 ? new User(ownerId) : null 1.194 - ).ifPresent(project::setOwner); 1.195 - 1.196 - if (project.getId() > 0) { 1.197 - dao.updateProject(project); 1.198 - } else { 1.199 - dao.insertProject(project); 1.200 - } 1.201 - 1.202 - setRedirectLocation(req, "./projects/"); 1.203 - setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 1.204 - LOG.debug("Successfully updated project {}", project.getName()); 1.205 - 1.206 - renderSite(req, resp); 1.207 - } catch (NoSuchElementException | IllegalArgumentException ex) { 1.208 - resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 1.209 - // TODO: implement - fix issue #21 1.210 - } 1.211 - } 1.212 - 1.213 - @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET) 1.214 - public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws SQLException, IOException, ServletException { 1.215 - final var viewModel = new ProjectDetailsView(); 1.216 - populate(viewModel, pathParams, dao); 1.217 - 1.218 - if (!viewModel.isEveryFilterValid()) { 1.219 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.220 - return; 1.221 - } 1.222 - 1.223 - final var project = viewModel.getProjectInfo().getProject(); 1.224 - final var version = viewModel.getVersionFilter(); 1.225 - final var component = viewModel.getComponentFilter(); 1.226 - 1.227 - // TODO: use new IssueFilter class for the ViewModel 1.228 - 1.229 - final var projectFilter = new SpecificFilter<>(project); 1.230 - final IssueFilter filter; 1.231 - if (version.equals(ProjectView.NO_VERSION)) { 1.232 - if (component.equals(ProjectView.ALL_COMPONENTS)) { 1.233 - filter = new IssueFilter(projectFilter, 1.234 - new NoneFilter<>(), 1.235 - new AllFilter<>() 1.236 - ); 1.237 - } else if (component.equals(ProjectView.NO_COMPONENT)) { 1.238 - filter = new IssueFilter(projectFilter, 1.239 - new NoneFilter<>(), 1.240 - new NoneFilter<>() 1.241 - ); 1.242 - } else { 1.243 - filter = new IssueFilter(projectFilter, 1.244 - new NoneFilter<>(), 1.245 - new SpecificFilter<>(component) 1.246 - ); 1.247 - } 1.248 - } else if (version.equals(ProjectView.ALL_VERSIONS)) { 1.249 - if (component.equals(ProjectView.ALL_COMPONENTS)) { 1.250 - filter = new IssueFilter(projectFilter, 1.251 - new AllFilter<>(), 1.252 - new AllFilter<>() 1.253 - ); 1.254 - } else if (component.equals(ProjectView.NO_COMPONENT)) { 1.255 - filter = new IssueFilter(projectFilter, 1.256 - new AllFilter<>(), 1.257 - new NoneFilter<>() 1.258 - ); 1.259 - } else { 1.260 - filter = new IssueFilter(projectFilter, 1.261 - new AllFilter<>(), 1.262 - new SpecificFilter<>(component) 1.263 - ); 1.264 - } 1.265 - } else { 1.266 - if (component.equals(ProjectView.ALL_COMPONENTS)) { 1.267 - filter = new IssueFilter(projectFilter, 1.268 - new SpecificFilter<>(version), 1.269 - new AllFilter<>() 1.270 - ); 1.271 - } else if (component.equals(ProjectView.NO_COMPONENT)) { 1.272 - filter = new IssueFilter(projectFilter, 1.273 - new SpecificFilter<>(version), 1.274 - new NoneFilter<>() 1.275 - ); 1.276 - } else { 1.277 - filter = new IssueFilter(projectFilter, 1.278 - new SpecificFilter<>(version), 1.279 - new SpecificFilter<>(component) 1.280 - ); 1.281 - } 1.282 - } 1.283 - 1.284 - final var issues = dao.listIssues(filter); 1.285 - issues.sort(new IssueSorter( 1.286 - new IssueSorter.Criteria(IssueSorter.Field.DONE, true), 1.287 - new IssueSorter.Criteria(IssueSorter.Field.ETA, true), 1.288 - new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false) 1.289 - )); 1.290 - 1.291 - 1.292 - viewModel.getProjectDetails().updateDetails(issues); 1.293 - if (version.getId() > 0) 1.294 - viewModel.getProjectDetails().updateVersionInfo(version); 1.295 - 1.296 - forwardView(req, resp, viewModel, "project-details"); 1.297 - } 1.298 - 1.299 - @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET) 1.300 - public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException { 1.301 - final var viewModel = new VersionsView(); 1.302 - populate(viewModel, pathParameters, dao); 1.303 - 1.304 - final var projectInfo = viewModel.getProjectInfo(); 1.305 - if (projectInfo == null) { 1.306 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.307 - return; 1.308 - } 1.309 - 1.310 - final var issues = dao.listIssues( 1.311 - new IssueFilter( 1.312 - new SpecificFilter<>(projectInfo.getProject()), 1.313 - new AllFilter<>(), 1.314 - new AllFilter<>() 1.315 - ) 1.316 - ); 1.317 - viewModel.update(projectInfo.getVersions(), issues); 1.318 - 1.319 - forwardView(req, resp, viewModel, "versions"); 1.320 - } 1.321 - 1.322 - @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET) 1.323 - public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException { 1.324 - final var viewModel = new VersionEditView(); 1.325 - populate(viewModel, pathParameters, dao); 1.326 - 1.327 - if (viewModel.getProjectInfo() == null || viewModel.getVersionFilter() == null) { 1.328 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.329 - return; 1.330 - } 1.331 - 1.332 - viewModel.setVersion(viewModel.getVersionFilter()); 1.333 - 1.334 - forwardView(req, resp, viewModel, "version-form"); 1.335 - } 1.336 - 1.337 - @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET) 1.338 - public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException { 1.339 - final var viewModel = new VersionEditView(); 1.340 - populate(viewModel, pathParameters, dao); 1.341 - 1.342 - if (viewModel.getProjectInfo() == null) { 1.343 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.344 - return; 1.345 - } 1.346 - 1.347 - viewModel.setVersion(new Version(-1, viewModel.getProjectInfo().getProject().getId())); 1.348 - 1.349 - forwardView(req, resp, viewModel, "version-form"); 1.350 - } 1.351 - 1.352 - @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST) 1.353 - public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException { 1.354 - 1.355 - try { 1.356 - final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow()); 1.357 - if (project == null) { 1.358 - // TODO: improve error handling, because not found is not correct for this POST request 1.359 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.360 - return; 1.361 - } 1.362 - final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), project.getId()); 1.363 - version.setName(getParameter(req, String.class, "name").orElseThrow()); 1.364 - 1.365 - final var node = getParameter(req, String.class, "node").orElse(null); 1.366 - version.setNode(sanitizeNode(node, version.getName())); 1.367 - 1.368 - getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal); 1.369 - version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); 1.370 - 1.371 - if (version.getId() > 0) { 1.372 - dao.updateVersion(version); 1.373 - } else { 1.374 - dao.insertVersion(version); 1.375 - } 1.376 - 1.377 - setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/"); 1.378 - setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 1.379 - 1.380 - renderSite(req, resp); 1.381 - } catch (NoSuchElementException | IllegalArgumentException ex) { 1.382 - resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 1.383 - // TODO: implement - fix issue #21 1.384 - } 1.385 - } 1.386 - 1.387 - @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET) 1.388 - public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException { 1.389 - final var viewModel = new ComponentsView(); 1.390 - populate(viewModel, pathParameters, dao); 1.391 - 1.392 - final var projectInfo = viewModel.getProjectInfo(); 1.393 - if (projectInfo == null) { 1.394 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.395 - return; 1.396 - } 1.397 - 1.398 - final var issues = dao.listIssues( 1.399 - new IssueFilter( 1.400 - new SpecificFilter<>(projectInfo.getProject()), 1.401 - new AllFilter<>(), 1.402 - new AllFilter<>() 1.403 - ) 1.404 - ); 1.405 - viewModel.update(projectInfo.getComponents(), issues); 1.406 - 1.407 - forwardView(req, resp, viewModel, "components"); 1.408 - } 1.409 - 1.410 - @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET) 1.411 - public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException { 1.412 - final var viewModel = new ComponentEditView(); 1.413 - populate(viewModel, pathParameters, dao); 1.414 - 1.415 - if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) { 1.416 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.417 - return; 1.418 - } 1.419 - 1.420 - viewModel.setComponent(viewModel.getComponentFilter()); 1.421 - viewModel.setUsers(dao.listUsers()); 1.422 - 1.423 - forwardView(req, resp, viewModel, "component-form"); 1.424 - } 1.425 - 1.426 - @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET) 1.427 - public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException { 1.428 - final var viewModel = new ComponentEditView(); 1.429 - populate(viewModel, pathParameters, dao); 1.430 - 1.431 - if (viewModel.getProjectInfo() == null) { 1.432 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.433 - return; 1.434 - } 1.435 - 1.436 - viewModel.setComponent(new Component(-1, viewModel.getProjectInfo().getProject().getId())); 1.437 - viewModel.setUsers(dao.listUsers()); 1.438 - 1.439 - forwardView(req, resp, viewModel, "component-form"); 1.440 - } 1.441 - 1.442 - @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST) 1.443 - public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException { 1.444 - 1.445 - try { 1.446 - final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow()); 1.447 - if (project == null) { 1.448 - // TODO: improve error handling, because not found is not correct for this POST request 1.449 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.450 - return; 1.451 - } 1.452 - final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow(), project.getId()); 1.453 - component.setName(getParameter(req, String.class, "name").orElseThrow()); 1.454 - 1.455 - final var node = getParameter(req, String.class, "node").orElse(null); 1.456 - component.setNode(sanitizeNode(node, component.getName())); 1.457 - 1.458 - component.setColor(getParameter(req, WebColor.class, "color").orElseThrow()); 1.459 - getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal); 1.460 - getParameter(req, Integer.class, "lead").map( 1.461 - userid -> userid >= 0 ? new User(userid) : null 1.462 - ).ifPresent(component::setLead); 1.463 - getParameter(req, String.class, "description").ifPresent(component::setDescription); 1.464 - 1.465 - if (component.getId() > 0) { 1.466 - dao.updateComponent(component); 1.467 - } else { 1.468 - dao.insertComponent(component); 1.469 - } 1.470 - 1.471 - setRedirectLocation(req, "./projects/" + project.getNode() + "/components/"); 1.472 - setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 1.473 - 1.474 - renderSite(req, resp); 1.475 - } catch (NoSuchElementException | IllegalArgumentException ex) { 1.476 - resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 1.477 - // TODO: implement - fix issue #21 1.478 - } 1.479 - } 1.480 - 1.481 - private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObject dao) { 1.482 - final var project = viewModel.getProjectInfo().getProject(); 1.483 - issue.setProject(project); // automatically set current project for new issues 1.484 - viewModel.setIssue(issue); 1.485 - viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions()); 1.486 - viewModel.setUsers(dao.listUsers()); 1.487 - viewModel.setComponents(dao.listComponents(project)); 1.488 - } 1.489 - 1.490 - @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET) 1.491 - public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException { 1.492 - final var viewModel = new IssueDetailView(); 1.493 - populate(viewModel, pathParameters, dao); 1.494 - 1.495 - final var projectInfo = viewModel.getProjectInfo(); 1.496 - if (projectInfo == null) { 1.497 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.498 - return; 1.499 - } 1.500 - 1.501 - final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue"))); 1.502 - if (issue == null) { 1.503 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.504 - return; 1.505 - } 1.506 - 1.507 - viewModel.setIssue(issue); 1.508 - viewModel.setComments(dao.listComments(issue)); 1.509 - 1.510 - viewModel.processMarkdown(); 1.511 - 1.512 - forwardView(req, resp, viewModel, "issue-view"); 1.513 - } 1.514 - 1.515 - // TODO: why should the issue editor be child of $project? 1.516 - @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET) 1.517 - public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException { 1.518 - final var viewModel = new IssueEditView(); 1.519 - populate(viewModel, pathParameters, dao); 1.520 - 1.521 - final var projectInfo = viewModel.getProjectInfo(); 1.522 - if (projectInfo == null) { 1.523 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.524 - return; 1.525 - } 1.526 - 1.527 - final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue"))); 1.528 - if (issue == null) { 1.529 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.530 - return; 1.531 - } 1.532 - 1.533 - configureIssueEditor(viewModel, issue, dao); 1.534 - 1.535 - forwardView(req, resp, viewModel, "issue-form"); 1.536 - } 1.537 - 1.538 - @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET) 1.539 - public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException { 1.540 - final var viewModel = new IssueEditView(); 1.541 - populate(viewModel, pathParameters, dao); 1.542 - 1.543 - final var projectInfo = viewModel.getProjectInfo(); 1.544 - if (projectInfo == null) { 1.545 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.546 - return; 1.547 - } 1.548 - 1.549 - setAttributeFromParameter(req, "more"); 1.550 - setAttributeFromParameter(req, "cid"); 1.551 - setAttributeFromParameter(req, "vid"); 1.552 - 1.553 - final var issue = new Issue(-1, projectInfo.getProject(), null); 1.554 - issue.setProject(projectInfo.getProject()); 1.555 - configureIssueEditor(viewModel, issue, dao); 1.556 - 1.557 - forwardView(req, resp, viewModel, "issue-form"); 1.558 - } 1.559 - 1.560 - @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST) 1.561 - public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException { 1.562 - try { 1.563 - final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow()); 1.564 - if (project == null) { 1.565 - // TODO: improve error handling, because not found is not correct for this POST request 1.566 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.567 - return; 1.568 - } 1.569 - final var componentId = getParameter(req, Integer.class, "component"); 1.570 - final Component component; 1.571 - if (componentId.isPresent()) { 1.572 - component = dao.findComponent(componentId.get()); 1.573 - } else { 1.574 - component = null; 1.575 - } 1.576 - final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), project, component); 1.577 - getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory); 1.578 - getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus); 1.579 - issue.setSubject(getParameter(req, String.class, "subject").orElseThrow()); 1.580 - getParameter(req, Integer.class, "assignee").map(userid -> { 1.581 - if (userid >= 0) { 1.582 - return new User(userid); 1.583 - } else if (userid == -2) { 1.584 - return Optional.ofNullable(component).map(Component::getLead).orElse(null); 1.585 - } else { 1.586 - return null; 1.587 - } 1.588 - } 1.589 - ).ifPresent(issue::setAssignee); 1.590 - getParameter(req, String.class, "description").ifPresent(issue::setDescription); 1.591 - getParameter(req, Date.class, "eta").ifPresent(issue::setEta); 1.592 - 1.593 - getParameter(req, Integer[].class, "affected") 1.594 - .map(Stream::of) 1.595 - .map(stream -> 1.596 - stream.map(id -> new Version(id, project.getId())) 1.597 - .collect(Collectors.toList()) 1.598 - ).ifPresent(issue::setAffectedVersions); 1.599 - getParameter(req, Integer[].class, "resolved") 1.600 - .map(Stream::of) 1.601 - .map(stream -> 1.602 - stream.map(id -> new Version(id, project.getId())) 1.603 - .collect(Collectors.toList()) 1.604 - ).ifPresent(issue::setResolvedVersions); 1.605 - 1.606 - if (issue.getId() > 0) { 1.607 - dao.updateIssue(issue); 1.608 - } else { 1.609 - dao.insertIssue(issue); 1.610 - } 1.611 - 1.612 - if (getParameter(req, Boolean.class, "create-another").orElse(false)) { 1.613 - // TODO: fix #38 - automatically select component (and version) 1.614 - setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/create-issue?more=true"); 1.615 - } else{ 1.616 - setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/issues/" + issue.getId() + "/view"); 1.617 - } 1.618 - setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 1.619 - 1.620 - renderSite(req, resp); 1.621 - } catch (NoSuchElementException | IllegalArgumentException ex) { 1.622 - resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 1.623 - // TODO: implement - fix issue #21 1.624 - } 1.625 - } 1.626 - 1.627 - @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST) 1.628 - public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException { 1.629 - final var issueIdParam = getParameter(req, Integer.class, "issueid"); 1.630 - if (issueIdParam.isEmpty()) { 1.631 - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form."); 1.632 - return; 1.633 - } 1.634 - final var issue = dao.findIssue(issueIdParam.get()); 1.635 - if (issue == null) { 1.636 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.637 - return; 1.638 - } 1.639 - try { 1.640 - final var issueComment = new IssueComment(getParameter(req, Integer.class, "commentid").orElse(-1), issue.getId()); 1.641 - issueComment.setComment(getParameter(req, String.class, "comment").orElse("")); 1.642 - 1.643 - if (issueComment.getComment().isBlank()) { 1.644 - throw new IllegalArgumentException("comment.null"); 1.645 - } 1.646 - 1.647 - LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId()); 1.648 - if (req.getRemoteUser() != null) { 1.649 - Optional.ofNullable(dao.findUserByName(req.getRemoteUser())).ifPresent(issueComment::setAuthor); 1.650 - } 1.651 - 1.652 - dao.insertComment(issueComment); 1.653 - 1.654 - setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view"); 1.655 - setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 1.656 - 1.657 - renderSite(req, resp); 1.658 - } catch (NoSuchElementException | IllegalArgumentException ex) { 1.659 - resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 1.660 - // TODO: implement - fix issue #21 1.661 - } 1.662 - } 1.663 -}