Sat, 17 Oct 2020 19:56:50 +0200
completes feature: project components
1.1 --- a/src/main/java/de/uapcore/lightpit/dao/IssueDao.java Sat Oct 17 15:21:56 2020 +0200 1.2 +++ b/src/main/java/de/uapcore/lightpit/dao/IssueDao.java Sat Oct 17 19:56:50 2020 +0200 1.3 @@ -28,10 +28,7 @@ 1.4 */ 1.5 package de.uapcore.lightpit.dao; 1.6 1.7 -import de.uapcore.lightpit.entities.Issue; 1.8 -import de.uapcore.lightpit.entities.IssueComment; 1.9 -import de.uapcore.lightpit.entities.Project; 1.10 -import de.uapcore.lightpit.entities.Version; 1.11 +import de.uapcore.lightpit.entities.*; 1.12 1.13 import java.sql.SQLException; 1.14 import java.util.List; 1.15 @@ -39,14 +36,39 @@ 1.16 public interface IssueDao extends ChildEntityDao<Issue, Project> { 1.17 1.18 /** 1.19 - * Lists all issues that are somehow related to the specified version. 1.20 - * If version is null, search for issues that are not related to any version. 1.21 + * Lists all issues that are related to the specified component and version. 1.22 + * If component or version is null, search for issues that are not assigned to any 1.23 + * component or version, respectively. 1.24 * 1.25 + * @param project the project 1.26 + * @param component the component or null 1.27 * @param version the version or null 1.28 * @return a list of issues 1.29 * @throws SQLException on any kind of SQL error 1.30 */ 1.31 - List<Issue> list(Version version) throws SQLException; 1.32 + List<Issue> list(Project project, Component component, Version version) throws SQLException; 1.33 + 1.34 + /** 1.35 + * Lists all issues that are related to the specified version. 1.36 + * If the version is null, lists issues that are not assigned to any version. 1.37 + * 1.38 + * @param project the project (mandatory) 1.39 + * @param version the version or null 1.40 + * @return a list of issues 1.41 + * @throws SQLException on any kind of SQL error 1.42 + */ 1.43 + List<Issue> list(Project project, Version version) throws SQLException; 1.44 + 1.45 + /** 1.46 + * Lists all issues that are related to the specified component. 1.47 + * If the component is null, lists issues that are not assigned to a component. 1.48 + * 1.49 + * @param project the project (mandatory) 1.50 + * @param component the component or null 1.51 + * @return a list of issues 1.52 + * @throws SQLException on any kind of SQL error 1.53 + */ 1.54 + List<Issue> list(Project project, Component component) throws SQLException; 1.55 1.56 /** 1.57 * Lists all comments for a specific issue in chronological order.
2.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Sat Oct 17 15:21:56 2020 +0200 2.2 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Sat Oct 17 19:56:50 2020 +0200 2.3 @@ -31,13 +31,11 @@ 2.4 import de.uapcore.lightpit.dao.IssueDao; 2.5 import de.uapcore.lightpit.entities.*; 2.6 2.7 -import java.sql.Connection; 2.8 -import java.sql.PreparedStatement; 2.9 -import java.sql.ResultSet; 2.10 -import java.sql.SQLException; 2.11 +import java.sql.*; 2.12 import java.util.ArrayList; 2.13 import java.util.List; 2.14 import java.util.Objects; 2.15 +import java.util.Optional; 2.16 2.17 import static de.uapcore.lightpit.dao.Functions.*; 2.18 2.19 @@ -51,43 +49,50 @@ 2.20 2.21 public PGIssueDao(Connection connection) throws SQLException { 2.22 list = connection.prepareStatement( 2.23 - "select issueid, project, p.name as projectname, status, category, subject, i.description, " + 2.24 + "select issueid, i.project, p.name as projectname, component, c.name as componentname, " + 2.25 + "status, category, subject, i.description, " + 2.26 "userid, username, givenname, lastname, mail, " + 2.27 "created, updated, eta " + 2.28 "from lpit_issue i " + 2.29 - "join lpit_project p on project = projectid " + 2.30 + "join lpit_project p on i.project = projectid " + 2.31 + "left join lpit_component c on component = c.id " + 2.32 "left join lpit_user on userid = assignee " + 2.33 - "where project = ? "); 2.34 + "where i.project = ? and coalesce(component, -1) = coalesce(?, component, -1)"); 2.35 2.36 listForVersion = connection.prepareStatement( 2.37 "with issue_version as ( "+ 2.38 "select issueid, versionid from lpit_issue_affected_version union "+ 2.39 "select issueid, versionid from lpit_issue_resolved_version) "+ 2.40 - "select issueid, project, p.name as projectname, status, category, subject, i.description, " + 2.41 + "select issueid, i.project, p.name as projectname, component, c.name as componentname, " + 2.42 + "status, category, subject, i.description, " + 2.43 "userid, username, givenname, lastname, mail, " + 2.44 "created, updated, eta " + 2.45 "from lpit_issue i " + 2.46 - "join lpit_project p on project = projectid " + 2.47 + "join lpit_project p on i.project = projectid " + 2.48 + "left join lpit_component c on component = c.id " + 2.49 "left join issue_version using (issueid) "+ 2.50 "left join lpit_user on userid = assignee " + 2.51 - "where coalesce(versionid,-1) = ? " 2.52 + "where coalesce(versionid,-1) = ? and coalesce(component, -1) = coalesce(?, component, -1)" 2.53 ); 2.54 2.55 find = connection.prepareStatement( 2.56 - "select issueid, project, p.name as projectname, status, category, subject, i.description, " + 2.57 + "select issueid, i.project, p.name as projectname, component, c.name as componentname, " + 2.58 + "status, category, subject, i.description, " + 2.59 "userid, username, givenname, lastname, mail, " + 2.60 "created, updated, eta " + 2.61 "from lpit_issue i " + 2.62 - "left join lpit_project p on project = projectid " + 2.63 + "join lpit_project p on i.project = projectid " + 2.64 + "left join lpit_component c on component = c.id " + 2.65 "left join lpit_user on userid = assignee " + 2.66 "where issueid = ? "); 2.67 2.68 insert = connection.prepareStatement( 2.69 - "insert into lpit_issue (project, status, category, subject, description, assignee, eta) " + 2.70 + "insert into lpit_issue (project, component, status, category, subject, description, assignee, eta) " + 2.71 "values (?, ?::issue_status, ?::issue_category, ?, ?, ?, ?) returning issueid" 2.72 ); 2.73 update = connection.prepareStatement( 2.74 - "update lpit_issue set updated = now(), status = ?::issue_status, category = ?::issue_category, " + 2.75 + "update lpit_issue set " + 2.76 + "updated = now(), component = ?, status = ?::issue_status, category = ?::issue_category, " + 2.77 "subject = ?, description = ?, assignee = ?, eta = ? where issueid = ?" 2.78 ); 2.79 2.80 @@ -123,8 +128,15 @@ 2.81 private Issue mapColumns(ResultSet result) throws SQLException { 2.82 final var project = new Project(result.getInt("project")); 2.83 project.setName(result.getString("projectname")); 2.84 + var component = new Component(result.getInt("component")); 2.85 + if (result.wasNull()) { 2.86 + component = null; 2.87 + } else { 2.88 + component.setName(result.getString("componentname")); 2.89 + } 2.90 final var issue = new Issue(result.getInt("issueid")); 2.91 issue.setProject(project); 2.92 + issue.setComponent(component); 2.93 issue.setStatus(IssueStatus.valueOf(result.getString("status"))); 2.94 issue.setCategory(IssueCategory.valueOf(result.getString("category"))); 2.95 issue.setSubject(result.getString("subject")); 2.96 @@ -161,17 +173,24 @@ 2.97 } 2.98 } 2.99 2.100 + private int setData(PreparedStatement stmt, int column, Issue instance) throws SQLException { 2.101 + setForeignKeyOrNull(stmt, ++column, instance.getComponent(), Component::getId); 2.102 + stmt.setString(++column, instance.getStatus().name()); 2.103 + stmt.setString(++column, instance.getCategory().name()); 2.104 + stmt.setString(++column, instance.getSubject()); 2.105 + setStringOrNull(stmt, ++column, instance.getDescription()); 2.106 + setForeignKeyOrNull(stmt, ++column, instance.getAssignee(), User::getId); 2.107 + setDateOrNull(stmt, ++column, instance.getEta()); 2.108 + return column; 2.109 + } 2.110 + 2.111 @Override 2.112 public void save(Issue instance, Project project) throws SQLException { 2.113 Objects.requireNonNull(instance.getSubject()); 2.114 instance.setProject(project); 2.115 - insert.setInt(1, instance.getProject().getId()); 2.116 - insert.setString(2, instance.getStatus().name()); 2.117 - insert.setString(3, instance.getCategory().name()); 2.118 - insert.setString(4, instance.getSubject()); 2.119 - setStringOrNull(insert, 5, instance.getDescription()); 2.120 - setForeignKeyOrNull(insert, 6, instance.getAssignee(), User::getId); 2.121 - setDateOrNull(insert, 7, instance.getEta()); 2.122 + int column = 0; 2.123 + insert.setInt(++column, instance.getProject().getId()); 2.124 + setData(insert, column, instance); 2.125 // insert and retrieve the ID 2.126 final var rs = insert.executeQuery(); 2.127 rs.next(); 2.128 @@ -183,13 +202,8 @@ 2.129 public boolean update(Issue instance) throws SQLException { 2.130 if (instance.getId() < 0) return false; 2.131 Objects.requireNonNull(instance.getSubject()); 2.132 - update.setString(1, instance.getStatus().name()); 2.133 - update.setString(2, instance.getCategory().name()); 2.134 - update.setString(3, instance.getSubject()); 2.135 - setStringOrNull(update, 4, instance.getDescription()); 2.136 - setForeignKeyOrNull(update, 5, instance.getAssignee(), User::getId); 2.137 - setDateOrNull(update, 6, instance.getEta()); 2.138 - update.setInt(7, instance.getId()); 2.139 + int column = setData(update, 0, instance); 2.140 + update.setInt(++column, instance.getId()); 2.141 boolean success = update.executeUpdate() > 0; 2.142 if (success) { 2.143 updateVersionLists(instance); 2.144 @@ -199,8 +213,7 @@ 2.145 } 2.146 } 2.147 2.148 - private List<Issue> list(PreparedStatement query, int arg) throws SQLException { 2.149 - query.setInt(1, arg); 2.150 + private List<Issue> executeQuery(PreparedStatement query) throws SQLException { 2.151 List<Issue> issues = new ArrayList<>(); 2.152 try (var result = query.executeQuery()) { 2.153 while (result.next()) { 2.154 @@ -212,12 +225,30 @@ 2.155 2.156 @Override 2.157 public List<Issue> list(Project project) throws SQLException { 2.158 - return list(list, project.getId()); 2.159 + list.setInt(1, project.getId()); 2.160 + list.setNull(2, Types.INTEGER); 2.161 + return executeQuery(list); 2.162 } 2.163 2.164 @Override 2.165 - public List<Issue> list(Version version) throws SQLException { 2.166 - return list(listForVersion, version == null ? -1 : version.getId()); 2.167 + public List<Issue> list(Project project, Component component, Version version) throws SQLException { 2.168 + listForVersion.setInt(1, Optional.ofNullable(version).map(Version::getId).orElse(-1)); 2.169 + listForVersion.setInt(2, Optional.ofNullable(component).map(Component::getId).orElse(-1)); 2.170 + return executeQuery(listForVersion); 2.171 + } 2.172 + 2.173 + @Override 2.174 + public List<Issue> list(Project project, Version version) throws SQLException { 2.175 + listForVersion.setInt(1, Optional.ofNullable(version).map(Version::getId).orElse(-1)); 2.176 + listForVersion.setNull(2, Types.INTEGER); 2.177 + return executeQuery(listForVersion); 2.178 + } 2.179 + 2.180 + @Override 2.181 + public List<Issue> list(Project project, Component component) throws SQLException { 2.182 + list.setInt(1, project.getId()); 2.183 + list.setInt(2, Optional.ofNullable(component).map(Component::getId).orElse(-1)); 2.184 + return executeQuery(list); 2.185 } 2.186 2.187 @Override
3.1 --- a/src/main/java/de/uapcore/lightpit/entities/Component.java Sat Oct 17 15:21:56 2020 +0200 3.2 +++ b/src/main/java/de/uapcore/lightpit/entities/Component.java Sat Oct 17 19:56:50 2020 +0200 3.3 @@ -38,7 +38,9 @@ 3.4 3.5 private String name; 3.6 3.7 - private WebColor color; 3.8 + private String node; 3.9 + 3.10 + private WebColor color = new WebColor("000000"); 3.11 3.12 private int ordinal = 0; 3.13 3.14 @@ -66,6 +68,14 @@ 3.15 this.name = name; 3.16 } 3.17 3.18 + public String getNode() { 3.19 + return node == null ? String.valueOf(id) : node; 3.20 + } 3.21 + 3.22 + public void setNode(String node) { 3.23 + this.node = node; 3.24 + } 3.25 + 3.26 public WebColor getColor() { 3.27 return color; 3.28 }
4.1 --- a/src/main/java/de/uapcore/lightpit/entities/Issue.java Sat Oct 17 15:21:56 2020 +0200 4.2 +++ b/src/main/java/de/uapcore/lightpit/entities/Issue.java Sat Oct 17 19:56:50 2020 +0200 4.3 @@ -39,6 +39,7 @@ 4.4 4.5 private int id; 4.6 private Project project; 4.7 + private Component component; 4.8 4.9 private IssueStatus status; 4.10 private IssueCategory category; 4.11 @@ -78,6 +79,14 @@ 4.12 return project; 4.13 } 4.14 4.15 + public Component getComponent() { 4.16 + return component; 4.17 + } 4.18 + 4.19 + public void setComponent(Component component) { 4.20 + this.component = component; 4.21 + } 4.22 + 4.23 public IssueStatus getStatus() { 4.24 return status; 4.25 }
5.1 --- a/src/main/java/de/uapcore/lightpit/entities/Project.java Sat Oct 17 15:21:56 2020 +0200 5.2 +++ b/src/main/java/de/uapcore/lightpit/entities/Project.java Sat Oct 17 19:56:50 2020 +0200 5.3 @@ -34,6 +34,7 @@ 5.4 5.5 private final int id; 5.6 private String name; 5.7 + private String node; 5.8 private String description; 5.9 private String repoUrl; 5.10 private User owner; 5.11 @@ -54,6 +55,14 @@ 5.12 this.name = name; 5.13 } 5.14 5.15 + public String getNode() { 5.16 + return node == null ? String.valueOf(id) : node; 5.17 + } 5.18 + 5.19 + public void setNode(String node) { 5.20 + this.node = node; 5.21 + } 5.22 + 5.23 public String getDescription() { 5.24 return description; 5.25 }
6.1 --- a/src/main/java/de/uapcore/lightpit/entities/Version.java Sat Oct 17 15:21:56 2020 +0200 6.2 +++ b/src/main/java/de/uapcore/lightpit/entities/Version.java Sat Oct 17 19:56:50 2020 +0200 6.3 @@ -34,6 +34,7 @@ 6.4 6.5 private final int id; 6.6 private String name; 6.7 + private String node; 6.8 /** 6.9 * If we do not want versions to be ordered lexicographically we may specify an order. 6.10 */ 6.11 @@ -56,6 +57,14 @@ 6.12 this.name = name; 6.13 } 6.14 6.15 + public String getNode() { 6.16 + return node == null ? String.valueOf(id) : node; 6.17 + } 6.18 + 6.19 + public void setNode(String node) { 6.20 + this.node = node; 6.21 + } 6.22 + 6.23 public int getOrdinal() { 6.24 return ordinal; 6.25 }
7.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat Oct 17 15:21:56 2020 +0200 7.2 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat Oct 17 19:56:50 2020 +0200 7.3 @@ -32,6 +32,7 @@ 7.4 import de.uapcore.lightpit.*; 7.5 import de.uapcore.lightpit.dao.DataAccessObjects; 7.6 import de.uapcore.lightpit.entities.*; 7.7 +import de.uapcore.lightpit.types.WebColor; 7.8 import de.uapcore.lightpit.viewmodel.*; 7.9 import de.uapcore.lightpit.viewmodel.util.IssueSorter; 7.10 import org.slf4j.Logger; 7.11 @@ -43,6 +44,7 @@ 7.12 import java.io.IOException; 7.13 import java.sql.Date; 7.14 import java.sql.SQLException; 7.15 +import java.util.List; 7.16 import java.util.NoSuchElementException; 7.17 import java.util.stream.Collectors; 7.18 import java.util.stream.Stream; 7.19 @@ -84,19 +86,30 @@ 7.20 } 7.21 7.22 // Select Version 7.23 - final int vid = Functions.parseIntOrZero(pathParameters.get("version")); 7.24 - if (vid > 0) { 7.25 - viewModel.setVersionFilter(versionDao.find(vid)); 7.26 + final var pathParamVersion = pathParameters.get("version"); 7.27 + if ("no-version".equals(pathParamVersion)) { 7.28 + viewModel.setVersionFilter(ProjectView.NO_VERSION); 7.29 + } else if ("all-versions".equals(pathParamVersion)) { 7.30 + viewModel.setVersionFilter(ProjectView.ALL_VERSIONS); 7.31 + } else { 7.32 + final int vid = Functions.parseIntOrZero(pathParamVersion); 7.33 + if (vid > 0) { 7.34 + viewModel.setVersionFilter(versionDao.find(vid)); 7.35 + } 7.36 } 7.37 - // TODO: don't treat unknown == unassigned - send 404 for unknown and introduce special word for unassigned 7.38 7.39 // Select Component 7.40 - final int cid = Functions.parseIntOrZero(pathParameters.get("component")); 7.41 - if (cid > 0) { 7.42 - viewModel.setComponentFilter(componentDao.find(cid)); 7.43 + final var pathParamComponent = pathParameters.get("component"); 7.44 + if ("no-component".equals(pathParamComponent)) { 7.45 + viewModel.setComponentFilter(ProjectView.NO_COMPONENT); 7.46 + } else if ("all-components".equals(pathParamComponent)) { 7.47 + viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS); 7.48 + } else { 7.49 + final int cid = Functions.parseIntOrZero(pathParamComponent); 7.50 + if (cid > 0) { 7.51 + viewModel.setComponentFilter(componentDao.find(cid)); 7.52 + } 7.53 } 7.54 - 7.55 - // TODO: distinguish all/unassigned for components 7.56 } 7.57 7.58 private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) { 7.59 @@ -133,7 +146,7 @@ 7.60 final var viewModel = new ProjectEditView(); 7.61 populate(viewModel, pathParams, dao); 7.62 7.63 - if (viewModel.getProjectInfo() == null) { 7.64 + if (!viewModel.isProjectInfoPresent()) { 7.65 resp.sendError(HttpServletResponse.SC_NOT_FOUND); 7.66 return ResponseType.NONE; 7.67 } 7.68 @@ -176,28 +189,60 @@ 7.69 } 7.70 } 7.71 7.72 - @RequestMapping(requestPath = "$project/versions/$version", method = HttpMethod.GET) 7.73 - public ResponseType view(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException { 7.74 + @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET) 7.75 + public ResponseType issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException { 7.76 final var viewModel = new ProjectDetailsView(); 7.77 populate(viewModel, pathParams, dao); 7.78 - final var version = viewModel.getVersionFilter(); 7.79 7.80 - if (viewModel.getProjectInfo() == null || version == null) { 7.81 + if (!viewModel.isEveryFilterValid()) { 7.82 resp.sendError(HttpServletResponse.SC_NOT_FOUND); 7.83 return ResponseType.NONE; 7.84 } 7.85 7.86 + final var project = viewModel.getProjectInfo().getProject(); 7.87 + final var version = viewModel.getVersionFilter(); 7.88 + final var component = viewModel.getComponentFilter(); 7.89 + 7.90 final var issueDao = dao.getIssueDao(); 7.91 7.92 - final var detailView = viewModel.getProjectDetails(); 7.93 - final var issues = issueDao.list(version); 7.94 + final List<Issue> issues; 7.95 + if (version.equals(ProjectView.NO_VERSION)) { 7.96 + if (component.equals(ProjectView.ALL_COMPONENTS)) { 7.97 + issues = issueDao.list(project, (Version) null); 7.98 + } else if (component.equals(ProjectView.NO_COMPONENT)) { 7.99 + issues = issueDao.list(project, null, null); 7.100 + } else { 7.101 + issues = issueDao.list(project, component, null); 7.102 + } 7.103 + } else if (version.equals(ProjectView.ALL_VERSIONS)) { 7.104 + if (component.equals(ProjectView.ALL_COMPONENTS)) { 7.105 + issues = issueDao.list(project); 7.106 + } else if (component.equals(ProjectView.NO_COMPONENT)) { 7.107 + issues = issueDao.list(project, (Component)null); 7.108 + } else { 7.109 + issues = issueDao.list(project, component); 7.110 + } 7.111 + } else { 7.112 + if (component.equals(ProjectView.ALL_COMPONENTS)) { 7.113 + issues = issueDao.list(project, version); 7.114 + } else if (component.equals(ProjectView.NO_COMPONENT)) { 7.115 + issues = issueDao.list(project, null, version); 7.116 + } else { 7.117 + issues = issueDao.list(project, component, version); 7.118 + } 7.119 + } 7.120 + 7.121 for (var issue : issues) issueDao.joinVersionInformation(issue); 7.122 issues.sort(new IssueSorter( 7.123 new IssueSorter.Criteria(IssueSorter.Field.PHASE, true), 7.124 new IssueSorter.Criteria(IssueSorter.Field.ETA, true), 7.125 new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false) 7.126 )); 7.127 - detailView.updateDetails(issues, version); 7.128 + 7.129 + 7.130 + viewModel.getProjectDetails().updateDetails(issues); 7.131 + if (version.getId() > 0) 7.132 + viewModel.getProjectDetails().updateVersionInfo(version); 7.133 7.134 return forwardView(req, viewModel, "project-details"); 7.135 } 7.136 @@ -262,7 +307,6 @@ 7.137 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); 7.138 dao.getVersionDao().saveOrUpdate(version, project); 7.139 7.140 - // TODO: improve building the redirect location 7.141 setRedirectLocation(req, "./projects/" + project.getId() + "/versions/"); 7.142 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 7.143 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 7.144 @@ -274,11 +318,90 @@ 7.145 return ResponseType.HTML; 7.146 } 7.147 7.148 + @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET) 7.149 + public ResponseType components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { 7.150 + final var viewModel = new ComponentsView(); 7.151 + populate(viewModel, pathParameters, dao); 7.152 + 7.153 + final var projectInfo = viewModel.getProjectInfo(); 7.154 + if (projectInfo == null) { 7.155 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 7.156 + return ResponseType.NONE; 7.157 + } 7.158 + 7.159 + final var issueDao = dao.getIssueDao(); 7.160 + final var issues = issueDao.list(projectInfo.getProject()); 7.161 + viewModel.update(projectInfo.getComponents(), issues); 7.162 + 7.163 + return forwardView(req, viewModel, "components"); 7.164 + } 7.165 + 7.166 + @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET) 7.167 + public ResponseType editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { 7.168 + final var viewModel = new ComponentEditView(); 7.169 + populate(viewModel, pathParameters, dao); 7.170 + 7.171 + if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) { 7.172 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 7.173 + return ResponseType.NONE; 7.174 + } 7.175 + 7.176 + viewModel.setComponent(viewModel.getComponentFilter()); 7.177 + viewModel.setUsers(dao.getUserDao().list()); 7.178 + 7.179 + return forwardView(req, viewModel, "component-form"); 7.180 + } 7.181 + 7.182 + @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET) 7.183 + public ResponseType createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { 7.184 + final var viewModel = new ComponentEditView(); 7.185 + populate(viewModel, pathParameters, dao); 7.186 + 7.187 + if (viewModel.getProjectInfo() == null) { 7.188 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 7.189 + return ResponseType.NONE; 7.190 + } 7.191 + 7.192 + viewModel.setComponent(new Component(-1)); 7.193 + viewModel.setUsers(dao.getUserDao().list()); 7.194 + 7.195 + return forwardView(req, viewModel, "component-form"); 7.196 + } 7.197 + 7.198 + @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST) 7.199 + public ResponseType commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { 7.200 + 7.201 + try { 7.202 + final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); 7.203 + final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow()); 7.204 + component.setName(getParameter(req, String.class, "name").orElseThrow()); 7.205 + component.setColor(getParameter(req, WebColor.class, "color").orElseThrow()); 7.206 + getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal); 7.207 + getParameter(req, Integer.class, "lead").map( 7.208 + userid -> userid >= 0 ? new User(userid) : null 7.209 + ).ifPresent(component::setLead); 7.210 + getParameter(req, String.class, "description").ifPresent(component::setDescription); 7.211 + 7.212 + dao.getComponentDao().saveOrUpdate(component, project); 7.213 + 7.214 + setRedirectLocation(req, "./projects/" + project.getId() + "/components/"); 7.215 + setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 7.216 + } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 7.217 + resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 7.218 + // TODO: implement - fix issue #21 7.219 + return ResponseType.NONE; 7.220 + } 7.221 + 7.222 + return ResponseType.HTML; 7.223 + } 7.224 + 7.225 private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException { 7.226 - issue.setProject(viewModel.getProjectInfo().getProject()); 7.227 + final var project = viewModel.getProjectInfo().getProject(); 7.228 + issue.setProject(project); 7.229 viewModel.setIssue(issue); 7.230 viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions()); 7.231 viewModel.setUsers(dao.getUserDao().list()); 7.232 + viewModel.setComponents(dao.getComponentDao().list(project)); 7.233 if (issue.getId() >= 0) { 7.234 viewModel.setComments(dao.getIssueDao().listComments(issue)); 7.235 } 7.236 @@ -337,6 +460,9 @@ 7.237 getParameter(req, Integer.class, "assignee").map( 7.238 userid -> userid >= 0 ? new User(userid) : null 7.239 ).ifPresent(issue::setAssignee); 7.240 + getParameter(req, Integer.class, "component").map( 7.241 + cid -> cid >= 0 ? new Component(cid) : null 7.242 + ).ifPresent(issue::setComponent); 7.243 getParameter(req, String.class, "description").ifPresent(issue::setDescription); 7.244 getParameter(req, Date.class, "eta").ifPresent(issue::setEta); 7.245 7.246 @@ -354,7 +480,7 @@ 7.247 dao.getIssueDao().saveOrUpdate(issue, issue.getProject()); 7.248 7.249 // TODO: fix issue #14 7.250 - setRedirectLocation(req, "./projects/" + issue.getProject().getId() + "/versions/"); 7.251 + setRedirectLocation(req, "./projects/" + issue.getProject().getId() + "/all-components/all-versions/issues/"); 7.252 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 7.253 7.254 return ResponseType.HTML;
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ComponentEditView.java Sat Oct 17 19:56:50 2020 +0200 8.3 @@ -0,0 +1,40 @@ 8.4 +package de.uapcore.lightpit.viewmodel; 8.5 + 8.6 +import de.uapcore.lightpit.entities.Component; 8.7 +import de.uapcore.lightpit.entities.User; 8.8 + 8.9 +import java.util.List; 8.10 + 8.11 +public class ComponentEditView extends ProjectView { 8.12 + private Component component; 8.13 + private List<User> users; 8.14 + private String errorText; 8.15 + 8.16 + public ComponentEditView() { 8.17 + setSelectedPage(SELECTED_PAGE_COMPONENTS); 8.18 + } 8.19 + 8.20 + public void setComponent(Component component) { 8.21 + this.component = component; 8.22 + } 8.23 + 8.24 + public Component getComponent() { 8.25 + return component; 8.26 + } 8.27 + 8.28 + public List<User> getUsers() { 8.29 + return users; 8.30 + } 8.31 + 8.32 + public void setUsers(List<User> users) { 8.33 + this.users = users; 8.34 + } 8.35 + 8.36 + public String getErrorText() { 8.37 + return errorText; 8.38 + } 8.39 + 8.40 + public void setErrorText(String errorText) { 8.41 + this.errorText = errorText; 8.42 + } 8.43 +}
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ComponentInfo.java Sat Oct 17 19:56:50 2020 +0200 9.3 @@ -0,0 +1,42 @@ 9.4 +package de.uapcore.lightpit.viewmodel; 9.5 + 9.6 +import de.uapcore.lightpit.entities.Component; 9.7 +import de.uapcore.lightpit.entities.Issue; 9.8 +import de.uapcore.lightpit.entities.IssueSummary; 9.9 + 9.10 +import java.util.ArrayList; 9.11 +import java.util.List; 9.12 + 9.13 +public class ComponentInfo { 9.14 + 9.15 + private final Component component; 9.16 + 9.17 + private final IssueSummary issueSummary = new IssueSummary(); 9.18 + 9.19 + private final List<Issue> issues = new ArrayList<>(); 9.20 + 9.21 + public ComponentInfo(Component component) { 9.22 + this.component = component; 9.23 + } 9.24 + 9.25 + public Component getComponent() { 9.26 + return component; 9.27 + } 9.28 + 9.29 + public IssueSummary getIssueSummary() { 9.30 + return issueSummary; 9.31 + } 9.32 + 9.33 + public List<Issue> getIssues() { 9.34 + return issues; 9.35 + } 9.36 + 9.37 + public void collectIssues(List<Issue> issues) { 9.38 + for (Issue issue : issues) { 9.39 + if (component.equals(issue.getComponent())) { 9.40 + this.issues.add(issue); 9.41 + this.issueSummary.add(issue); 9.42 + } 9.43 + } 9.44 + } 9.45 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ComponentsView.java Sat Oct 17 19:56:50 2020 +0200 10.3 @@ -0,0 +1,29 @@ 10.4 +package de.uapcore.lightpit.viewmodel; 10.5 + 10.6 +import de.uapcore.lightpit.entities.Component; 10.7 +import de.uapcore.lightpit.entities.Issue; 10.8 + 10.9 +import java.util.ArrayList; 10.10 +import java.util.List; 10.11 + 10.12 +public class ComponentsView extends ProjectView { 10.13 + 10.14 + private List<ComponentInfo> componentInfos = new ArrayList<>(); 10.15 + 10.16 + public ComponentsView() { 10.17 + setSelectedPage(SELECTED_PAGE_COMPONENTS); 10.18 + } 10.19 + 10.20 + public void update(List<Component> components, List<Issue> issues) { 10.21 + componentInfos.clear(); 10.22 + for (var component : components) { 10.23 + final var info = new ComponentInfo(component); 10.24 + info.collectIssues(issues); 10.25 + componentInfos.add(info); 10.26 + } 10.27 + } 10.28 + 10.29 + public List<ComponentInfo> getComponentInfos() { 10.30 + return componentInfos; 10.31 + } 10.32 +}
11.1 --- a/src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java Sat Oct 17 15:21:56 2020 +0200 11.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java Sat Oct 17 19:56:50 2020 +0200 11.3 @@ -11,6 +11,7 @@ 11.4 private Set<Version> versionsUpcoming = new HashSet<>(); 11.5 private Set<Version> versionsRecent = new HashSet<>(); 11.6 private List<User> users; 11.7 + private List<Component> components; 11.8 private List<IssueComment> comments; 11.9 11.10 public void setIssue(Issue issue) { 11.11 @@ -45,7 +46,8 @@ 11.12 versionsUpcoming.addAll(issue.getResolvedVersions()); 11.13 for (var v : versions) { 11.14 if (v.getStatus().isReleased()) { 11.15 - versionsRecent.add(v); 11.16 + if (!v.getStatus().equals(VersionStatus.Deprecated)) 11.17 + versionsRecent.add(v); 11.18 } else { 11.19 versionsUpcoming.add(v); 11.20 } 11.21 @@ -60,6 +62,14 @@ 11.22 this.users = users; 11.23 } 11.24 11.25 + public List<Component> getComponents() { 11.26 + return components; 11.27 + } 11.28 + 11.29 + public void setComponents(List<Component> components) { 11.30 + this.components = components; 11.31 + } 11.32 + 11.33 public IssueStatus[] getIssueStatus() { 11.34 return IssueStatus.values(); 11.35 }
12.1 --- a/src/main/java/de/uapcore/lightpit/viewmodel/ProjectDetails.java Sat Oct 17 15:21:56 2020 +0200 12.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectDetails.java Sat Oct 17 19:56:50 2020 +0200 12.3 @@ -13,14 +13,15 @@ 12.4 private List<Issue> issues; 12.5 private IssueSummary issueSummary; 12.6 12.7 - public void updateDetails(List<Issue> issues, Version version) { 12.8 + public void updateDetails(List<Issue> issues) { 12.9 this.issues = issues; 12.10 issueSummary = new IssueSummary(); 12.11 issues.forEach(issueSummary::add); 12.12 - if (version != null) { 12.13 - versionInfo = new VersionInfo(version); 12.14 - versionInfo.collectIssues(issues); 12.15 - } 12.16 + } 12.17 + 12.18 + public void updateVersionInfo(Version version) { 12.19 + versionInfo = new VersionInfo(version); 12.20 + versionInfo.collectIssues(issues); 12.21 } 12.22 12.23 public List<Issue> getIssues() {
13.1 --- a/src/main/java/de/uapcore/lightpit/viewmodel/ProjectView.java Sat Oct 17 15:21:56 2020 +0200 13.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectView.java Sat Oct 17 19:56:50 2020 +0200 13.3 @@ -8,11 +8,29 @@ 13.4 13.5 public class ProjectView { 13.6 13.7 + public static final int SELECTED_PAGE_ISSUES = 0; 13.8 + public static final int SELECTED_PAGE_VERSIONS = 1; 13.9 + public static final int SELECTED_PAGE_COMPONENTS = 2; 13.10 + 13.11 + public static final Version ALL_VERSIONS = new Version(0); 13.12 + public static final Version NO_VERSION = new Version(-1); 13.13 + public static final Component ALL_COMPONENTS = new Component(0); 13.14 + public static final Component NO_COMPONENT = new Component(-1); 13.15 + 13.16 + static { 13.17 + ALL_VERSIONS.setNode("all-versions"); 13.18 + NO_VERSION.setNode("no-version"); 13.19 + ALL_COMPONENTS.setNode("all-components"); 13.20 + NO_COMPONENT.setNode("no-component"); 13.21 + } 13.22 + 13.23 private final List<ProjectInfo> projectList = new ArrayList<>(); 13.24 private ProjectInfo projectInfo; 13.25 private Version versionFilter; 13.26 private Component componentFilter; 13.27 13.28 + private int selectedPage = SELECTED_PAGE_ISSUES; 13.29 + 13.30 public List<ProjectInfo> getProjectList() { 13.31 return projectList; 13.32 } 13.33 @@ -25,6 +43,14 @@ 13.34 this.projectInfo = projectInfo; 13.35 } 13.36 13.37 + public int getSelectedPage() { 13.38 + return selectedPage; 13.39 + } 13.40 + 13.41 + public void setSelectedPage(int selectedPage) { 13.42 + this.selectedPage = selectedPage; 13.43 + } 13.44 + 13.45 public Version getVersionFilter() { 13.46 return versionFilter; 13.47 } 13.48 @@ -40,4 +66,20 @@ 13.49 public void setComponentFilter(Component componentFilter) { 13.50 this.componentFilter = componentFilter; 13.51 } 13.52 + 13.53 + public boolean isProjectInfoPresent() { 13.54 + return projectInfo != null; 13.55 + } 13.56 + 13.57 + public boolean isVersionFilterValid() { 13.58 + return projectInfo != null && versionFilter != null; 13.59 + } 13.60 + 13.61 + public boolean isComponentFilterValid() { 13.62 + return projectInfo != null && componentFilter != null; 13.63 + } 13.64 + 13.65 + public boolean isEveryFilterValid() { 13.66 + return projectInfo != null && versionFilter != null && componentFilter != null; 13.67 + } 13.68 }
14.1 --- a/src/main/java/de/uapcore/lightpit/viewmodel/VersionEditView.java Sat Oct 17 15:21:56 2020 +0200 14.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionEditView.java Sat Oct 17 19:56:50 2020 +0200 14.3 @@ -7,6 +7,10 @@ 14.4 private Version version; 14.5 private String errorText; 14.6 14.7 + public VersionEditView() { 14.8 + setSelectedPage(SELECTED_PAGE_VERSIONS); 14.9 + } 14.10 + 14.11 public void setVersion(Version version) { 14.12 this.version = version; 14.13 }
15.1 --- a/src/main/java/de/uapcore/lightpit/viewmodel/VersionsView.java Sat Oct 17 15:21:56 2020 +0200 15.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionsView.java Sat Oct 17 19:56:50 2020 +0200 15.3 @@ -8,18 +8,22 @@ 15.4 15.5 public class VersionsView extends ProjectView { 15.6 15.7 - private List<VersionInfo> versionInfo = new ArrayList<>(); 15.8 + private List<VersionInfo> versionInfos = new ArrayList<>(); 15.9 + 15.10 + public VersionsView() { 15.11 + setSelectedPage(SELECTED_PAGE_VERSIONS); 15.12 + } 15.13 15.14 public void update(List<Version> versions, List<Issue> issues) { 15.15 - versionInfo.clear(); 15.16 + versionInfos.clear(); 15.17 for (var version : versions) { 15.18 final var info = new VersionInfo(version); 15.19 info.collectIssues(issues); 15.20 - versionInfo.add(info); 15.21 + versionInfos.add(info); 15.22 } 15.23 } 15.24 15.25 - public List<VersionInfo> getVersionInfo() { 15.26 - return versionInfo; 15.27 + public List<VersionInfo> getVersionInfos() { 15.28 + return versionInfos; 15.29 } 15.30 }
16.1 --- a/src/main/resources/localization/projects.properties Sat Oct 17 15:21:56 2020 +0200 16.2 +++ b/src/main/resources/localization/projects.properties Sat Oct 17 19:56:50 2020 +0200 16.3 @@ -24,6 +24,8 @@ 16.4 pageTitle=Project Tracking 16.5 16.6 button.create=New Project 16.7 +button.component.create=New Component 16.8 +button.component.edit=Edit Component 16.9 button.version.create=New Version 16.10 button.version.edit=Edit Version 16.11 button.issue.create=New Issue 16.12 @@ -43,6 +45,8 @@ 16.13 owner=Project Lead 16.14 version.latest=Latest Version 16.15 version.next=Next Version 16.16 +issues=Issues 16.17 +component=Component 16.18 16.19 progress=Overall Progress 16.20 16.21 @@ -56,10 +60,20 @@ 16.22 version.name=Version 16.23 version.status=Status 16.24 version.ordinal=Ordering 16.25 + 16.26 +component.project=Project 16.27 +component.name=Component 16.28 +component.color=Color 16.29 +component.lead=Lead 16.30 +component.ordinal=Ordering 16.31 +component.description=Description 16.32 + 16.33 tooltip.ordinal=Use to override lexicographic ordering. 16.34 16.35 placeholder.null-owner=Unassigned 16.36 +placeholder.null-lead=Unassigned 16.37 placeholder.null-assignee=Unassigned 16.38 +placeholder.null-component=Unassigned 16.39 16.40 version.status.Future=Future 16.41 version.status.Unreleased=Unreleased 16.42 @@ -67,10 +81,10 @@ 16.43 version.status.LTS=LTS 16.44 version.status.Deprecated=Deprecated 16.45 16.46 - 16.47 issue.without-version=No Assigned Version 16.48 issue.id=Issue ID 16.49 issue.project=Project 16.50 +issue.component=Component 16.51 issue.subject=Subject 16.52 issue.description=Description 16.53 issue.assignee=Assignee
17.1 --- a/src/main/resources/localization/projects_de.properties Sat Oct 17 15:21:56 2020 +0200 17.2 +++ b/src/main/resources/localization/projects_de.properties Sat Oct 17 19:56:50 2020 +0200 17.3 @@ -24,6 +24,8 @@ 17.4 pageTitle=Projektverwaltung 17.5 17.6 button.create=Neues Projekt 17.7 +button.component.create=Neue Komponente 17.8 +button.component.edit=Komponente Bearbeiten 17.9 button.version.create=Neue Version 17.10 button.version.edit=Version Bearbeiten 17.11 button.issue.create=Neuer Vorgang 17.12 @@ -43,6 +45,8 @@ 17.13 owner=Projektleitung 17.14 version.latest=Neuste Version 17.15 version.next=N\u00e4chste Version 17.16 +component=Komponente 17.17 +issues=Vorg\u00e4nge 17.18 17.19 progress=Gesamtfortschritt 17.20 17.21 @@ -56,10 +60,20 @@ 17.22 version.name=Version 17.23 version.status=Status 17.24 version.ordinal=Sequenznummer 17.25 + 17.26 +component.project=Projekt 17.27 +component.name=Komponente 17.28 +component.color=Farbe 17.29 +component.lead=Leitung 17.30 +component.ordinal=Sequenznummer 17.31 +component.description=Beschreibung 17.32 + 17.33 tooltip.ordinal=\u00dcbersteuert die lexikographische Sortierung. 17.34 17.35 placeholder.null-owner=Nicht Zugewiesen 17.36 +placeholder.null-lead=Niemand 17.37 placeholder.null-assignee=Niemandem 17.38 +placeholder.null-component=Keine 17.39 17.40 version.status.Future=Geplant 17.41 version.status.Unreleased=Unver\u00f6ffentlicht 17.42 @@ -70,6 +84,7 @@ 17.43 issue.without-version=Keine Version zugeordnet 17.44 issue.id=Vorgangs-ID 17.45 issue.project=Projekt 17.46 +issue.component=Komponente 17.47 issue.subject=Thema 17.48 issue.description=Beschreibung 17.49 issue.assignee=Zugewiesen
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 18.2 +++ b/src/main/webapp/WEB-INF/jsp/component-form.jsp Sat Oct 17 19:56:50 2020 +0200 18.3 @@ -0,0 +1,95 @@ 18.4 +<%-- 18.5 +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 18.6 + 18.7 +Copyright 2020 Mike Becker. All rights reserved. 18.8 + 18.9 +Redistribution and use in source and binary forms, with or without 18.10 +modification, are permitted provided that the following conditions are met: 18.11 + 18.12 +1. Redistributions of source code must retain the above copyright 18.13 +notice, this list of conditions and the following disclaimer. 18.14 + 18.15 +2. Redistributions in binary form must reproduce the above copyright 18.16 +notice, this list of conditions and the following disclaimer in the 18.17 +documentation and/or other materials provided with the distribution. 18.18 + 18.19 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18.20 +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18.21 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18.22 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18.23 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18.24 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18.25 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 18.26 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 18.27 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 18.28 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18.29 +--%> 18.30 +<%@page pageEncoding="UTF-8" %> 18.31 +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 18.32 +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 18.33 + 18.34 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.ComponentEditView" scope="request" /> 18.35 +<c:set var="component" scope="page" value="${viewmodel.component}"/> 18.36 +<c:set var="project" scope="page" value="${viewmodel.projectInfo.project}"/> 18.37 + 18.38 +<form action="./projects/commit-component" method="post"> 18.39 + <table class="formtable" style="width: 70ch"> 18.40 + <colgroup> 18.41 + <col> 18.42 + <col style="width: 100%"> 18.43 + </colgroup> 18.44 + <tbody> 18.45 + <tr> 18.46 + <th><fmt:message key="component.project"/></th> 18.47 + <td> 18.48 + <c:out value="${project.name}" /> 18.49 + <input type="hidden" name="pid" value="${project.id}" /> 18.50 + </td> 18.51 + </tr> 18.52 + <tr> 18.53 + <th><fmt:message key="component.name"/></th> 18.54 + <td><input name="name" type="text" maxlength="20" required value="<c:out value="${component.name}"/>" /></td> 18.55 + </tr> 18.56 + <tr> 18.57 + <th><fmt:message key="component.color"/></th> 18.58 + <td><input name="color" type="color" required value="${component.color}" /></td> 18.59 + </tr> 18.60 + <tr> 18.61 + <th><fmt:message key="component.lead"/></th> 18.62 + <td> 18.63 + <select name="lead"> 18.64 + <option value="-1"><fmt:message key="placeholder.null-lead"/></option> 18.65 + <c:forEach var="user" items="${viewmodel.users}"> 18.66 + <option 18.67 + <c:if test="${not empty component.lead and user eq component.lead}">selected</c:if> 18.68 + value="${user.id}"><c:out value="${user.displayname}"/></option> 18.69 + </c:forEach> 18.70 + </select> 18.71 + </td> 18.72 + </tr> 18.73 + <tr title="<fmt:message key="tooltip.ordinal" />"> 18.74 + <th><fmt:message key="component.ordinal"/></th> 18.75 + <td> 18.76 + <input name="ordinal" type="number" min="0" value="${component.ordinal}"/> 18.77 + </td> 18.78 + </tr> 18.79 + <tr> 18.80 + <th class="vtop"><fmt:message key="component.description"/></th> 18.81 + <td> 18.82 + <textarea name="description" rows="5"><c:out value="${component.description}"/></textarea> 18.83 + </td> 18.84 + </tr> 18.85 + </tbody> 18.86 + <tfoot> 18.87 + <tr> 18.88 + <td colspan="2"> 18.89 + <input type="hidden" name="id" value="${component.id}"/> 18.90 + <a href="./projects/${project.node}/components/" class="button"> 18.91 + <fmt:message bundle="${lightpit_bundle}" key="button.cancel"/> 18.92 + </a> 18.93 + <button type="submit"><fmt:message bundle="${lightpit_bundle}" key="button.okay"/></button> 18.94 + </td> 18.95 + </tr> 18.96 + </tfoot> 18.97 + </table> 18.98 +</form>
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 19.2 +++ b/src/main/webapp/WEB-INF/jsp/components.jsp Sat Oct 17 19:56:50 2020 +0200 19.3 @@ -0,0 +1,95 @@ 19.4 +<%-- 19.5 +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 19.6 + 19.7 +Copyright 2020 Mike Becker. All rights reserved. 19.8 + 19.9 +Redistribution and use in source and binary forms, with or without 19.10 +modification, are permitted provided that the following conditions are met: 19.11 + 19.12 +1. Redistributions of source code must retain the above copyright 19.13 +notice, this list of conditions and the following disclaimer. 19.14 + 19.15 +2. Redistributions in binary form must reproduce the above copyright 19.16 +notice, this list of conditions and the following disclaimer in the 19.17 +documentation and/or other materials provided with the distribution. 19.18 + 19.19 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19.20 +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19.21 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19.22 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19.23 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19.24 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19.25 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 19.26 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 19.27 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19.28 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19.29 +--%> 19.30 +<%@page pageEncoding="UTF-8" %> 19.31 +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 19.32 +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 19.33 + 19.34 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.ComponentsView" scope="request" /> 19.35 + 19.36 +<c:set var="project" scope="page" value="${viewmodel.projectInfo.project}"/> 19.37 + 19.38 +<%@include file="../jspf/project-header.jspf"%> 19.39 + 19.40 +<div id="tool-area"> 19.41 + <a href="./projects/${project.id}/create-component" class="button"><fmt:message key="button.component.create"/></a> 19.42 + <a href="./projects/${project.id}/create-issue" class="button"><fmt:message key="button.issue.create"/></a> 19.43 +</div> 19.44 + 19.45 +<h2><fmt:message key="progress" /></h2> 19.46 + 19.47 +<c:set var="summary" value="${viewmodel.projectInfo.issueSummary}" /> 19.48 +<%@include file="../jspf/issue-summary.jspf"%> 19.49 + 19.50 +<table id="version-list" class="datatable medskip fullwidth"> 19.51 + <colgroup> 19.52 + <col> 19.53 + <col width="20%"> 19.54 + <col width="44%"> 19.55 + <col width="12%"> 19.56 + <col width="12%"> 19.57 + <col width="12%"> 19.58 + </colgroup> 19.59 + <thead> 19.60 + <tr> 19.61 + <th colspan="3"></th> 19.62 + <th colspan="3" class="hcenter"> 19.63 + <fmt:message key="issues"/> 19.64 + </th> 19.65 + </tr> 19.66 + <tr> 19.67 + <th></th> 19.68 + <th><fmt:message key="component.name"/></th> 19.69 + <th><fmt:message key="component.description"/></th> 19.70 + <th class="hcenter"><fmt:message key="issues.open" /></th> 19.71 + <th class="hcenter"><fmt:message key="issues.active" /></th> 19.72 + <th class="hcenter"><fmt:message key="issues.done" /></th> 19.73 + </tr> 19.74 + </thead> 19.75 + <tbody> 19.76 + <c:forEach var="componentInfo" items="${viewmodel.componentInfos}" > 19.77 + <tr> 19.78 + <td rowspan="2" style="width: 2em;"><a href="./projects/${project.node}/components/${componentInfo.component.node}/edit">✎</a></td> 19.79 + <td rowspan="2"> 19.80 + <div class="navmenu-icon" style="background-color: ${componentInfo.component.color}"></div> 19.81 + <a href="./projects/${project.node}/${componentInfo.component.node}/all-versions/issues/"> 19.82 + <c:out value="${componentInfo.component.name}"/> 19.83 + </a> 19.84 + </td> 19.85 + <td rowspan="2"><c:out value="${componentInfo.component.description}"/> </td> 19.86 + <td class="hright">${componentInfo.issueSummary.open}</td> 19.87 + <td class="hright">${componentInfo.issueSummary.active}</td> 19.88 + <td class="hright">${componentInfo.issueSummary.done}</td> 19.89 + </tr> 19.90 + <tr> 19.91 + <td colspan="3"> 19.92 + <c:set var="summary" value="${componentInfo.issueSummary}"/> 19.93 + <%@include file="../jspf/issue-progress.jspf" %> 19.94 + </td> 19.95 + </tr> 19.96 + </c:forEach> 19.97 + </tbody> 19.98 +</table> 19.99 \ No newline at end of file
20.1 --- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat Oct 17 15:21:56 2020 +0200 20.2 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat Oct 17 19:56:50 2020 +0200 20.3 @@ -74,6 +74,19 @@ 20.4 <td><fmt:formatDate value="${issue.updated}" /></td> 20.5 </tr> 20.6 <tr> 20.7 + <th><fmt:message key="issue.component"/></th> 20.8 + <td> 20.9 + <select name="component"> 20.10 + <option value="-1"><fmt:message key="placeholder.null-component"/></option> 20.11 + <c:forEach var="component" items="${viewmodel.components}"> 20.12 + <option 20.13 + <c:if test="${not empty issue.component and component eq issue.component}">selected</c:if> 20.14 + value="${component.id}"><c:out value="${component.name}"/></option> 20.15 + </c:forEach> 20.16 + </select> 20.17 + </td> 20.18 + </tr> 20.19 + <tr> 20.20 <th><fmt:message key="issue.category"/></th> 20.21 <td> 20.22 <select name="category"> 20.23 @@ -154,7 +167,7 @@ 20.24 <td colspan="2"> 20.25 <input type="hidden" name="id" value="${issue.id}"/> 20.26 <%-- TODO: fix #14 --%> 20.27 - <a href="./projects/${issue.project.id}/versions/" class="button"> 20.28 + <a href="./projects/${issue.project.node}/versions/" class="button"> 20.29 <fmt:message bundle="${lightpit_bundle}" key="button.cancel"/> 20.30 </a> 20.31 <button type="submit"><fmt:message bundle="${lightpit_bundle}" key="button.okay"/></button>
21.1 --- a/src/main/webapp/WEB-INF/jsp/issues.jsp Sat Oct 17 15:21:56 2020 +0200 21.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 21.3 @@ -1,57 +0,0 @@ 21.4 -<%-- 21.5 -DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 21.6 - 21.7 -Copyright 2018 Mike Becker. All rights reserved. 21.8 - 21.9 -Redistribution and use in source and binary forms, with or without 21.10 -modification, are permitted provided that the following conditions are met: 21.11 - 21.12 -1. Redistributions of source code must retain the above copyright 21.13 -notice, this list of conditions and the following disclaimer. 21.14 - 21.15 -2. Redistributions in binary form must reproduce the above copyright 21.16 -notice, this list of conditions and the following disclaimer in the 21.17 -documentation and/or other materials provided with the distribution. 21.18 - 21.19 -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21.20 -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21.21 -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21.22 -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21.23 -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21.24 -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21.25 -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21.26 -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21.27 -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 21.28 -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21.29 ---%> 21.30 -<%@page pageEncoding="UTF-8" %> 21.31 -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 21.32 -<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 21.33 - 21.34 -<h1>TODO: REWRITE THIS PAGE</h1> 21.35 -<%-- 21.36 -TODO: rewrite 21.37 -<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.IssuesView" scope="request"/> 21.38 -<c:set var="project" scope="page" value="${viewmodel.project}"/> 21.39 -<c:set var="version" scope="page" value="${viewmodel.version}"/> 21.40 -<%@include file="../jspf/project-header.jspf"%> 21.41 - 21.42 -<c:if test="${not empty version}"> 21.43 - <h2> 21.44 - <fmt:message key="version.name" /> <c:out value="${version.name}" /> - <fmt:message key="version.status.${version.status}"/> 21.45 - <a href="./projects/versions/edit?vid=${version.id}">✎</a> 21.46 - </h2> 21.47 -</c:if> 21.48 - 21.49 -<div id="tool-area"> 21.50 - <div> 21.51 - <a href="./projects/issues/edit" class="button"><fmt:message key="button.issue.create"/></a> 21.52 - <c:if test="${not empty version}"> 21.53 - <a href="./projects/issues/?pid=${project.id}&vid=-1" class="button"><fmt:message key="button.issue.all"/></a> 21.54 - </c:if> 21.55 - </div> 21.56 -</div> 21.57 - 21.58 -<c:set var="issues" value="${viewmodel.issues}"/> 21.59 -<%@include file="../jspf/issue-list.jspf"%> 21.60 ---%> 21.61 \ No newline at end of file
22.1 --- a/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat Oct 17 15:21:56 2020 +0200 22.2 +++ b/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat Oct 17 19:56:50 2020 +0200 22.3 @@ -24,21 +24,26 @@ 22.4 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22.5 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22.6 --%> 22.7 -<%@page pageEncoding="UTF-8" %> 22.8 +<%@page pageEncoding="UTF-8" import="de.uapcore.lightpit.viewmodel.ProjectView" %> 22.9 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 22.10 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 22.11 22.12 <jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.ProjectDetailsView" scope="request" /> 22.13 22.14 <c:set var="project" scope="page" value="${viewmodel.projectInfo.project}"/> 22.15 +<c:set var="component" scope="page" value="${viewmodel.componentFilter}"/> 22.16 <%@include file="../jspf/project-header.jspf"%> 22.17 22.18 <div id="tool-area"> 22.19 - <c:if test="${not empty viewmodel.versionFilter}"> 22.20 - <a href="./projects/${project.id}/versions/${viewmodel.versionFilter.id}/edit" class="button"><fmt:message key="button.version.edit"/></a> 22.21 + <a href="./projects/${project.node}/create-issue" class="button"><fmt:message key="button.issue.create"/></a> 22.22 + <c:if test="${viewmodel.versionFilter.id gt 0}"> 22.23 + <a href="./projects/${project.node}/versions/${viewmodel.versionFilter.node}/edit" class="button"><fmt:message key="button.version.edit"/></a> 22.24 </c:if> 22.25 - <a href="./projects/${project.id}/create-version" class="button"><fmt:message key="button.version.create"/></a> 22.26 - <a href="./projects/${project.id}/create-issue" class="button"><fmt:message key="button.issue.create"/></a> 22.27 + <a href="./projects/${project.node}/create-version" class="button"><fmt:message key="button.version.create"/></a> 22.28 + <c:if test="${viewmodel.componentFilter.id gt 0}"> 22.29 + <a href="./projects/${project.node}/components/${viewmodel.componentFilter.node}/edit" class="button"><fmt:message key="button.component.edit"/></a> 22.30 + </c:if> 22.31 + <a href="./projects/${project.node}/create-component" class="button"><fmt:message key="button.component.create"/></a> 22.32 </div> 22.33 22.34 <h2><fmt:message key="progress" /></h2> 22.35 @@ -46,36 +51,43 @@ 22.36 <c:set var="summary" value="${viewmodel.projectInfo.issueSummary}" /> 22.37 <%@include file="../jspf/issue-summary.jspf"%> 22.38 22.39 -<c:if test="${not empty viewmodel.versionFilter}"> 22.40 - <c:set var="versionInfo" value="${viewmodel.projectDetails.versionInfo}"/> 22.41 - <h2> 22.42 - <fmt:message key="version.name" /> <c:out value="${versionInfo.version.name}" /> - <fmt:message key="version.status.${versionInfo.version.status}"/> 22.43 - </h2> 22.44 +<c:choose> 22.45 + <c:when test="${viewmodel.versionFilter eq ProjectView.NO_VERSION or viewmodel.versionFilter eq ProjectView.ALL_VERSIONS}"> 22.46 + <h2> 22.47 + <c:if test="${viewmodel.versionFilter eq ProjectView.NO_VERSION}"> 22.48 + <fmt:message key="issue.without-version" /> 22.49 + </c:if> 22.50 + <c:if test="${viewmodel.versionFilter ne ProjectView.NO_VERSION}"> 22.51 + <fmt:message key="issues" /> 22.52 + </c:if> 22.53 + </h2> 22.54 + <c:set var="summary" value="${viewmodel.projectDetails.issueSummary}"/> 22.55 + <c:set var="issues" value="${viewmodel.projectDetails.issues}"/> 22.56 + <%@include file="../jspf/issue-summary.jspf"%> 22.57 + <c:if test="${not empty issues}"> 22.58 + <%@include file="../jspf/issue-list.jspf"%> 22.59 + </c:if> 22.60 + </c:when> 22.61 + <c:otherwise> 22.62 + <c:set var="versionInfo" value="${viewmodel.projectDetails.versionInfo}"/> 22.63 + <h2> 22.64 + <fmt:message key="version.name" /> <c:out value="${versionInfo.version.name}" /> - <fmt:message key="version.status.${versionInfo.version.status}"/> 22.65 + </h2> 22.66 22.67 - <h3><fmt:message key="issues.resolved"/> </h3> 22.68 - <c:set var="summary" value="${versionInfo.resolvedTotal}"/> 22.69 - <%@include file="../jspf/issue-summary.jspf"%> 22.70 - <c:set var="issues" value="${versionInfo.resolved}"/> 22.71 - <c:if test="${not empty issues}"> 22.72 - <%@include file="../jspf/issue-list.jspf"%> 22.73 - </c:if> 22.74 + <h3><fmt:message key="issues.resolved"/> </h3> 22.75 + <c:set var="summary" value="${versionInfo.resolvedTotal}"/> 22.76 + <%@include file="../jspf/issue-summary.jspf"%> 22.77 + <c:set var="issues" value="${versionInfo.resolved}"/> 22.78 + <c:if test="${not empty issues}"> 22.79 + <%@include file="../jspf/issue-list.jspf"%> 22.80 + </c:if> 22.81 22.82 - <c:set var="issues" value="${versionInfo.reported}"/> 22.83 - <c:if test="${not empty issues}"> 22.84 - <h3><fmt:message key="issues.reported"/> </h3> 22.85 - <c:set var="summary" value="${versionInfo.reportedTotal}"/> 22.86 - <%@include file="../jspf/issue-summary.jspf"%> 22.87 - <%@include file="../jspf/issue-list.jspf"%> 22.88 - </c:if> 22.89 -</c:if> 22.90 -<c:if test="${empty viewmodel.versionFilter}"> 22.91 - <h2> 22.92 - <fmt:message key="issue.without-version" /> 22.93 - </h2> 22.94 - <c:set var="summary" value="${viewmodel.projectDetails.issueSummary}"/> 22.95 - <c:set var="issues" value="${viewmodel.projectDetails.issues}"/> 22.96 - <%@include file="../jspf/issue-summary.jspf"%> 22.97 - <c:if test="${not empty issues}"> 22.98 - <%@include file="../jspf/issue-list.jspf"%> 22.99 - </c:if> 22.100 -</c:if> 22.101 + <c:set var="issues" value="${versionInfo.reported}"/> 22.102 + <c:if test="${not empty issues}"> 22.103 + <h3><fmt:message key="issues.reported"/> </h3> 22.104 + <c:set var="summary" value="${versionInfo.reportedTotal}"/> 22.105 + <%@include file="../jspf/issue-summary.jspf"%> 22.106 + <%@include file="../jspf/issue-list.jspf"%> 22.107 + </c:if> 22.108 + </c:otherwise> 22.109 +</c:choose> 22.110 \ No newline at end of file
23.1 --- a/src/main/webapp/WEB-INF/jsp/project-navmenu.jsp Sat Oct 17 15:21:56 2020 +0200 23.2 +++ b/src/main/webapp/WEB-INF/jsp/project-navmenu.jsp Sat Oct 17 19:56:50 2020 +0200 23.3 @@ -24,7 +24,7 @@ 23.4 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23.5 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23.6 --%> 23.7 -<%@page pageEncoding="UTF-8" %> 23.8 +<%@page pageEncoding="UTF-8" import="de.uapcore.lightpit.viewmodel.ProjectView" %> 23.9 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 23.10 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 23.11 23.12 @@ -33,20 +33,27 @@ 23.13 <c:forEach var="projectInfo" items="${viewmodel.projectList}"> 23.14 <c:set var="isActive" value="${viewmodel.projectInfo.project eq projectInfo.project}" /> 23.15 <div class="menuEntry level-0" <c:if test="${isActive}">data-active</c:if> > 23.16 - <a href="projects/${projectInfo.project.id}/versions/"> 23.17 + <a href="projects/${projectInfo.project.node}/versions/"> 23.18 <c:out value="${projectInfo.project.name}"/> 23.19 </a> 23.20 </div> 23.21 <c:if test="${isActive}"> 23.22 <!-- VERSIONS --> 23.23 - <div class="menuEntry level-1"> 23.24 - <a href="projects/${projectInfo.project.id}/versions/"> 23.25 + <c:set var="componentNode" value="${not empty viewmodel.componentFilter ? viewmodel.componentFilter.node : 'all-components'}"/> 23.26 + <div class="menuEntry level-1" <c:if test="${viewmodel.selectedPage eq ProjectView.SELECTED_PAGE_VERSIONS}">data-active</c:if> > 23.27 + <a href="projects/${projectInfo.project.node}/versions/"> 23.28 <fmt:message key="navmenu.versions"/> 23.29 </a> 23.30 </div> 23.31 - <div class="menuEntry level-2"> 23.32 + <div class="menuEntry level-2" <c:if test="${viewmodel.versionFilter eq ProjectView.ALL_VERSIONS}">data-active</c:if>> 23.33 <div class="navmenu-icon" style="background: black"></div> 23.34 - <a href="projects/${projectInfo.project.id}/versions/unassigned"> 23.35 + <a href="projects/${projectInfo.project.node}/${componentNode}/all-versions/issues/"> 23.36 + <fmt:message key="navmenu.all" /> 23.37 + </a> 23.38 + </div> 23.39 + <div class="menuEntry level-2" <c:if test="${viewmodel.versionFilter eq ProjectView.NO_VERSION}">data-active</c:if>> 23.40 + <div class="navmenu-icon" style="background: black"></div> 23.41 + <a href="projects/${projectInfo.project.node}/${componentNode}/no-version/issues/"> 23.42 <fmt:message key="navmenu.unassigned" /> 23.43 </a> 23.44 </div> 23.45 @@ -55,39 +62,38 @@ 23.46 <div class="menuEntry level-2" <c:if test="${isVersionActive}">data-active</c:if> 23.47 title="<fmt:message key="version.status.${version.status}" />"> 23.48 <div class="navmenu-icon version-${version.status}"></div> 23.49 - <a href="projects/${projectInfo.project.id}/versions/${version.id}"> 23.50 + <a href="projects/${projectInfo.project.node}/${componentNode}/${version.node}/issues/"> 23.51 <c:out value="${version.name}"/> 23.52 </a> 23.53 </div> 23.54 </c:forEach> 23.55 - <%-- COMPONENTS 23.56 - TODO: find a way to combine version and component into one URL 23.57 - <div class="menuEntry level-1"> 23.58 - <a href="projects/${projectInfo.project.id}/components/"> 23.59 + <!-- COMPONENTS --> 23.60 + <c:set var="versionNode" value="${not empty viewmodel.versionFilter ? viewmodel.versionFilter.node : 'all-versions'}"/> 23.61 + <div class="menuEntry level-1" <c:if test="${viewmodel.selectedPage eq ProjectView.SELECTED_PAGE_COMPONENTS}">data-active</c:if>> 23.62 + <a href="projects/${projectInfo.project.node}/components/"> 23.63 <fmt:message key="navmenu.components"/> 23.64 </a> 23.65 </div> 23.66 - <div class="menuEntry level-2"> 23.67 + <div class="menuEntry level-2" <c:if test="${viewmodel.componentFilter eq ProjectView.ALL_COMPONENTS}">data-active</c:if>> 23.68 <div class="navmenu-icon" style="background: black"></div> 23.69 - <a href="projects/${projectInfo.project.id}/components/"> 23.70 + <a href="projects/${projectInfo.project.node}/all-components/${versionNode}/issues/"> 23.71 <fmt:message key="navmenu.all" /> 23.72 </a> 23.73 </div> 23.74 - <div class="menuEntry level-2"> 23.75 + <div class="menuEntry level-2" <c:if test="${viewmodel.componentFilter eq ProjectView.NO_COMPONENT}">data-active</c:if>> 23.76 <div class="navmenu-icon" style="background: black"></div> 23.77 - <a href="projects/${projectInfo.project.id}/components/unassigned"> 23.78 + <a href="projects/${projectInfo.project.node}/no-component/${versionNode}/issues/"> 23.79 <fmt:message key="navmenu.unassigned" /> 23.80 </a> 23.81 </div> 23.82 <c:forEach var="component" items="${viewmodel.projectInfo.components}"> 23.83 <c:set var="isComponentActive" value="${viewmodel.componentFilter eq component}" /> 23.84 - <div class="menuEntry level-2" <c:if test="${isVersionActive}">data-active</c:if> > 23.85 + <div class="menuEntry level-2" <c:if test="${isComponentActive}">data-active</c:if> > 23.86 <div class="navmenu-icon" style="background-color: ${component.color}"></div> 23.87 - <a href="projects/view?pid=${projectInfo.project.id}&cid=${component.id}"> 23.88 + <a href="projects/${projectInfo.project.node}/${component.node}/${versionNode}/issues/"> 23.89 <c:out value="${component.name}"/> 23.90 </a> 23.91 </div> 23.92 </c:forEach> 23.93 - --%> 23.94 </c:if> 23.95 </c:forEach>
24.1 --- a/src/main/webapp/WEB-INF/jsp/projects.jsp Sat Oct 17 15:21:56 2020 +0200 24.2 +++ b/src/main/webapp/WEB-INF/jsp/projects.jsp Sat Oct 17 19:56:50 2020 +0200 24.3 @@ -68,8 +68,8 @@ 24.4 <c:forEach var="projectInfo" items="${viewmodel.projectList}"> 24.5 <c:set var="project" scope="page" value="${projectInfo.project}"/> 24.6 <tr class="nowrap"> 24.7 - <td style="width: 2em;"><a href="./projects/${project.id}/edit">✎</a></td> 24.8 - <td><a href="./projects/${project.id}/versions/"><c:out value="${project.name}"/></a> 24.9 + <td style="width: 2em;"><a href="./projects/${project.node}/edit">✎</a></td> 24.10 + <td><a href="./projects/${project.node}/versions/"><c:out value="${project.name}"/></a> 24.11 </td> 24.12 <td> 24.13 <c:if test="${not empty project.repoUrl}"> 24.14 @@ -79,12 +79,12 @@ 24.15 </td> 24.16 <td class="hright"> 24.17 <c:if test="${not empty projectInfo.latestVersion}"> 24.18 - <a href="./projects/${project.id}/versions/${projectInfo.latestVersion.id}"><c:out value="${projectInfo.latestVersion.name}"/></a> 24.19 + <a href="./projects/${project.node}/all-components/${projectInfo.latestVersion.node}/issues/"><c:out value="${projectInfo.latestVersion.name}"/></a> 24.20 </c:if> 24.21 </td> 24.22 <td class="hright"> 24.23 <c:if test="${not empty projectInfo.nextVersion}"> 24.24 - <a href="./projects/${project.id}/versions/${projectInfo.nextVersion.id}"><c:out value="${projectInfo.nextVersion.name}"/></a> 24.25 + <a href="./projects/${project.node}/all-components/${projectInfo.nextVersion.node}/issues/"><c:out value="${projectInfo.nextVersion.name}"/></a> 24.26 </c:if> 24.27 </td> 24.28 <td class="hright">${projectInfo.issueSummary.open}</td>
25.1 --- a/src/main/webapp/WEB-INF/jsp/version-form.jsp Sat Oct 17 15:21:56 2020 +0200 25.2 +++ b/src/main/webapp/WEB-INF/jsp/version-form.jsp Sat Oct 17 19:56:50 2020 +0200 25.3 @@ -73,7 +73,7 @@ 25.4 <tr> 25.5 <td colspan="2"> 25.6 <input type="hidden" name="id" value="${version.id}"/> 25.7 - <a href="./projects/${project.id}/versions/" class="button"> 25.8 + <a href="./projects/${project.node}/versions/" class="button"> 25.9 <fmt:message bundle="${lightpit_bundle}" key="button.cancel"/> 25.10 </a> 25.11 <button type="submit"><fmt:message bundle="${lightpit_bundle}" key="button.okay"/></button>
26.1 --- a/src/main/webapp/WEB-INF/jsp/versions.jsp Sat Oct 17 15:21:56 2020 +0200 26.2 +++ b/src/main/webapp/WEB-INF/jsp/versions.jsp Sat Oct 17 19:56:50 2020 +0200 26.3 @@ -76,11 +76,11 @@ 26.4 </tr> 26.5 </thead> 26.6 <tbody> 26.7 - <c:forEach var="versionInfo" items="${viewmodel.versionInfo}" > 26.8 + <c:forEach var="versionInfo" items="${viewmodel.versionInfos}" > 26.9 <tr> 26.10 - <td rowspan="2" style="width: 2em;"><a href="./projects/${project.id}/versions/${versionInfo.version.id}/edit">✎</a></td> 26.11 + <td rowspan="2" style="width: 2em;"><a href="./projects/${project.node}/versions/${versionInfo.version.node}/edit">✎</a></td> 26.12 <td rowspan="2"> 26.13 - <a href="./projects/${project.id}/versions/${versionInfo.version.id}"> 26.14 + <a href="./projects/${project.node}/all-components/${versionInfo.version.node}/issues/"> 26.15 <c:out value="${versionInfo.version.name}"/> 26.16 </a> 26.17 <div class="version-tag version-${versionInfo.version.status}">
27.1 --- a/src/main/webapp/WEB-INF/jspf/project-header.jspf Sat Oct 17 15:21:56 2020 +0200 27.2 +++ b/src/main/webapp/WEB-INF/jspf/project-header.jspf Sat Oct 17 19:56:50 2020 +0200 27.3 @@ -1,5 +1,6 @@ 27.4 <%-- 27.5 project: Project 27.6 +component: Component (optional) 27.7 --%> 27.8 <div class="table project-attributes"> 27.9 <div class="row"> 27.10 @@ -21,4 +22,19 @@ 27.11 </c:if> 27.12 </div> 27.13 </div> 27.14 + <c:if test="${not empty component and component.id gt 0}"> 27.15 + <div class="row"> 27.16 + <div class="caption"><fmt:message key="component"/>:</div> 27.17 + <div><c:out value="${component.name}"/></div> 27.18 + <div class="caption"><fmt:message key="component.lead"/>:</div> 27.19 + <div> 27.20 + <c:if test="${not empty component.lead}"> 27.21 + <c:out value="${component.lead.displayname}"/> 27.22 + </c:if> 27.23 + <c:if test="${empty component.lead}"> 27.24 + <fmt:message key="placeholder.null-lead"/> 27.25 + </c:if> 27.26 + </div> 27.27 + </div> 27.28 + </c:if> 27.29 </div>