# HG changeset patch # User Mike Becker # Date 1602957410 -7200 # Node ID f47e82cd60777fc04cccb17f1304d2877ddbd414 # Parent ef075cd7ce55a98314aa5f4e8c973474bddcf9ef completes feature: project components diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/dao/IssueDao.java --- a/src/main/java/de/uapcore/lightpit/dao/IssueDao.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/IssueDao.java Sat Oct 17 19:56:50 2020 +0200 @@ -28,10 +28,7 @@ */ package de.uapcore.lightpit.dao; -import de.uapcore.lightpit.entities.Issue; -import de.uapcore.lightpit.entities.IssueComment; -import de.uapcore.lightpit.entities.Project; -import de.uapcore.lightpit.entities.Version; +import de.uapcore.lightpit.entities.*; import java.sql.SQLException; import java.util.List; @@ -39,14 +36,39 @@ public interface IssueDao extends ChildEntityDao { /** - * Lists all issues that are somehow related to the specified version. - * If version is null, search for issues that are not related to any version. + * Lists all issues that are related to the specified component and version. + * If component or version is null, search for issues that are not assigned to any + * component or version, respectively. * + * @param project the project + * @param component the component or null * @param version the version or null * @return a list of issues * @throws SQLException on any kind of SQL error */ - List list(Version version) throws SQLException; + List list(Project project, Component component, Version version) throws SQLException; + + /** + * Lists all issues that are related to the specified version. + * If the version is null, lists issues that are not assigned to any version. + * + * @param project the project (mandatory) + * @param version the version or null + * @return a list of issues + * @throws SQLException on any kind of SQL error + */ + List list(Project project, Version version) throws SQLException; + + /** + * Lists all issues that are related to the specified component. + * If the component is null, lists issues that are not assigned to a component. + * + * @param project the project (mandatory) + * @param component the component or null + * @return a list of issues + * @throws SQLException on any kind of SQL error + */ + List list(Project project, Component component) throws SQLException; /** * Lists all comments for a specific issue in chronological order. diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Sat Oct 17 19:56:50 2020 +0200 @@ -31,13 +31,11 @@ import de.uapcore.lightpit.dao.IssueDao; import de.uapcore.lightpit.entities.*; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; +import java.sql.*; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import static de.uapcore.lightpit.dao.Functions.*; @@ -51,43 +49,50 @@ public PGIssueDao(Connection connection) throws SQLException { list = connection.prepareStatement( - "select issueid, project, p.name as projectname, status, category, subject, i.description, " + + "select issueid, i.project, p.name as projectname, component, c.name as componentname, " + + "status, category, subject, i.description, " + "userid, username, givenname, lastname, mail, " + "created, updated, eta " + "from lpit_issue i " + - "join lpit_project p on project = projectid " + + "join lpit_project p on i.project = projectid " + + "left join lpit_component c on component = c.id " + "left join lpit_user on userid = assignee " + - "where project = ? "); + "where i.project = ? and coalesce(component, -1) = coalesce(?, component, -1)"); listForVersion = connection.prepareStatement( "with issue_version as ( "+ "select issueid, versionid from lpit_issue_affected_version union "+ "select issueid, versionid from lpit_issue_resolved_version) "+ - "select issueid, project, p.name as projectname, status, category, subject, i.description, " + + "select issueid, i.project, p.name as projectname, component, c.name as componentname, " + + "status, category, subject, i.description, " + "userid, username, givenname, lastname, mail, " + "created, updated, eta " + "from lpit_issue i " + - "join lpit_project p on project = projectid " + + "join lpit_project p on i.project = projectid " + + "left join lpit_component c on component = c.id " + "left join issue_version using (issueid) "+ "left join lpit_user on userid = assignee " + - "where coalesce(versionid,-1) = ? " + "where coalesce(versionid,-1) = ? and coalesce(component, -1) = coalesce(?, component, -1)" ); find = connection.prepareStatement( - "select issueid, project, p.name as projectname, status, category, subject, i.description, " + + "select issueid, i.project, p.name as projectname, component, c.name as componentname, " + + "status, category, subject, i.description, " + "userid, username, givenname, lastname, mail, " + "created, updated, eta " + "from lpit_issue i " + - "left join lpit_project p on project = projectid " + + "join lpit_project p on i.project = projectid " + + "left join lpit_component c on component = c.id " + "left join lpit_user on userid = assignee " + "where issueid = ? "); insert = connection.prepareStatement( - "insert into lpit_issue (project, status, category, subject, description, assignee, eta) " + + "insert into lpit_issue (project, component, status, category, subject, description, assignee, eta) " + "values (?, ?::issue_status, ?::issue_category, ?, ?, ?, ?) returning issueid" ); update = connection.prepareStatement( - "update lpit_issue set updated = now(), status = ?::issue_status, category = ?::issue_category, " + + "update lpit_issue set " + + "updated = now(), component = ?, status = ?::issue_status, category = ?::issue_category, " + "subject = ?, description = ?, assignee = ?, eta = ? where issueid = ?" ); @@ -123,8 +128,15 @@ private Issue mapColumns(ResultSet result) throws SQLException { final var project = new Project(result.getInt("project")); project.setName(result.getString("projectname")); + var component = new Component(result.getInt("component")); + if (result.wasNull()) { + component = null; + } else { + component.setName(result.getString("componentname")); + } final var issue = new Issue(result.getInt("issueid")); issue.setProject(project); + issue.setComponent(component); issue.setStatus(IssueStatus.valueOf(result.getString("status"))); issue.setCategory(IssueCategory.valueOf(result.getString("category"))); issue.setSubject(result.getString("subject")); @@ -161,17 +173,24 @@ } } + private int setData(PreparedStatement stmt, int column, Issue instance) throws SQLException { + setForeignKeyOrNull(stmt, ++column, instance.getComponent(), Component::getId); + stmt.setString(++column, instance.getStatus().name()); + stmt.setString(++column, instance.getCategory().name()); + stmt.setString(++column, instance.getSubject()); + setStringOrNull(stmt, ++column, instance.getDescription()); + setForeignKeyOrNull(stmt, ++column, instance.getAssignee(), User::getId); + setDateOrNull(stmt, ++column, instance.getEta()); + return column; + } + @Override public void save(Issue instance, Project project) throws SQLException { Objects.requireNonNull(instance.getSubject()); instance.setProject(project); - insert.setInt(1, instance.getProject().getId()); - insert.setString(2, instance.getStatus().name()); - insert.setString(3, instance.getCategory().name()); - insert.setString(4, instance.getSubject()); - setStringOrNull(insert, 5, instance.getDescription()); - setForeignKeyOrNull(insert, 6, instance.getAssignee(), User::getId); - setDateOrNull(insert, 7, instance.getEta()); + int column = 0; + insert.setInt(++column, instance.getProject().getId()); + setData(insert, column, instance); // insert and retrieve the ID final var rs = insert.executeQuery(); rs.next(); @@ -183,13 +202,8 @@ public boolean update(Issue instance) throws SQLException { if (instance.getId() < 0) return false; Objects.requireNonNull(instance.getSubject()); - update.setString(1, instance.getStatus().name()); - update.setString(2, instance.getCategory().name()); - update.setString(3, instance.getSubject()); - setStringOrNull(update, 4, instance.getDescription()); - setForeignKeyOrNull(update, 5, instance.getAssignee(), User::getId); - setDateOrNull(update, 6, instance.getEta()); - update.setInt(7, instance.getId()); + int column = setData(update, 0, instance); + update.setInt(++column, instance.getId()); boolean success = update.executeUpdate() > 0; if (success) { updateVersionLists(instance); @@ -199,8 +213,7 @@ } } - private List list(PreparedStatement query, int arg) throws SQLException { - query.setInt(1, arg); + private List executeQuery(PreparedStatement query) throws SQLException { List issues = new ArrayList<>(); try (var result = query.executeQuery()) { while (result.next()) { @@ -212,12 +225,30 @@ @Override public List list(Project project) throws SQLException { - return list(list, project.getId()); + list.setInt(1, project.getId()); + list.setNull(2, Types.INTEGER); + return executeQuery(list); } @Override - public List list(Version version) throws SQLException { - return list(listForVersion, version == null ? -1 : version.getId()); + public List list(Project project, Component component, Version version) throws SQLException { + listForVersion.setInt(1, Optional.ofNullable(version).map(Version::getId).orElse(-1)); + listForVersion.setInt(2, Optional.ofNullable(component).map(Component::getId).orElse(-1)); + return executeQuery(listForVersion); + } + + @Override + public List list(Project project, Version version) throws SQLException { + listForVersion.setInt(1, Optional.ofNullable(version).map(Version::getId).orElse(-1)); + listForVersion.setNull(2, Types.INTEGER); + return executeQuery(listForVersion); + } + + @Override + public List list(Project project, Component component) throws SQLException { + list.setInt(1, project.getId()); + list.setInt(2, Optional.ofNullable(component).map(Component::getId).orElse(-1)); + return executeQuery(list); } @Override diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/entities/Component.java --- a/src/main/java/de/uapcore/lightpit/entities/Component.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Component.java Sat Oct 17 19:56:50 2020 +0200 @@ -38,7 +38,9 @@ private String name; - private WebColor color; + private String node; + + private WebColor color = new WebColor("000000"); private int ordinal = 0; @@ -66,6 +68,14 @@ this.name = name; } + public String getNode() { + return node == null ? String.valueOf(id) : node; + } + + public void setNode(String node) { + this.node = node; + } + public WebColor getColor() { return color; } diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/entities/Issue.java --- a/src/main/java/de/uapcore/lightpit/entities/Issue.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Issue.java Sat Oct 17 19:56:50 2020 +0200 @@ -39,6 +39,7 @@ private int id; private Project project; + private Component component; private IssueStatus status; private IssueCategory category; @@ -78,6 +79,14 @@ return project; } + public Component getComponent() { + return component; + } + + public void setComponent(Component component) { + this.component = component; + } + public IssueStatus getStatus() { return status; } diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/entities/Project.java --- a/src/main/java/de/uapcore/lightpit/entities/Project.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Project.java Sat Oct 17 19:56:50 2020 +0200 @@ -34,6 +34,7 @@ private final int id; private String name; + private String node; private String description; private String repoUrl; private User owner; @@ -54,6 +55,14 @@ this.name = name; } + public String getNode() { + return node == null ? String.valueOf(id) : node; + } + + public void setNode(String node) { + this.node = node; + } + public String getDescription() { return description; } diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/entities/Version.java --- a/src/main/java/de/uapcore/lightpit/entities/Version.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Version.java Sat Oct 17 19:56:50 2020 +0200 @@ -34,6 +34,7 @@ private final int id; private String name; + private String node; /** * If we do not want versions to be ordered lexicographically we may specify an order. */ @@ -56,6 +57,14 @@ this.name = name; } + public String getNode() { + return node == null ? String.valueOf(id) : node; + } + + public void setNode(String node) { + this.node = node; + } + public int getOrdinal() { return ordinal; } diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat Oct 17 19:56:50 2020 +0200 @@ -32,6 +32,7 @@ import de.uapcore.lightpit.*; import de.uapcore.lightpit.dao.DataAccessObjects; import de.uapcore.lightpit.entities.*; +import de.uapcore.lightpit.types.WebColor; import de.uapcore.lightpit.viewmodel.*; import de.uapcore.lightpit.viewmodel.util.IssueSorter; import org.slf4j.Logger; @@ -43,6 +44,7 @@ import java.io.IOException; import java.sql.Date; import java.sql.SQLException; +import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -84,19 +86,30 @@ } // Select Version - final int vid = Functions.parseIntOrZero(pathParameters.get("version")); - if (vid > 0) { - viewModel.setVersionFilter(versionDao.find(vid)); + final var pathParamVersion = pathParameters.get("version"); + if ("no-version".equals(pathParamVersion)) { + viewModel.setVersionFilter(ProjectView.NO_VERSION); + } else if ("all-versions".equals(pathParamVersion)) { + viewModel.setVersionFilter(ProjectView.ALL_VERSIONS); + } else { + final int vid = Functions.parseIntOrZero(pathParamVersion); + if (vid > 0) { + viewModel.setVersionFilter(versionDao.find(vid)); + } } - // TODO: don't treat unknown == unassigned - send 404 for unknown and introduce special word for unassigned // Select Component - final int cid = Functions.parseIntOrZero(pathParameters.get("component")); - if (cid > 0) { - viewModel.setComponentFilter(componentDao.find(cid)); + final var pathParamComponent = pathParameters.get("component"); + if ("no-component".equals(pathParamComponent)) { + viewModel.setComponentFilter(ProjectView.NO_COMPONENT); + } else if ("all-components".equals(pathParamComponent)) { + viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS); + } else { + final int cid = Functions.parseIntOrZero(pathParamComponent); + if (cid > 0) { + viewModel.setComponentFilter(componentDao.find(cid)); + } } - - // TODO: distinguish all/unassigned for components } private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) { @@ -133,7 +146,7 @@ final var viewModel = new ProjectEditView(); populate(viewModel, pathParams, dao); - if (viewModel.getProjectInfo() == null) { + if (!viewModel.isProjectInfoPresent()) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); return ResponseType.NONE; } @@ -176,28 +189,60 @@ } } - @RequestMapping(requestPath = "$project/versions/$version", method = HttpMethod.GET) - public ResponseType view(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException { + @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET) + public ResponseType issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException { final var viewModel = new ProjectDetailsView(); populate(viewModel, pathParams, dao); - final var version = viewModel.getVersionFilter(); - if (viewModel.getProjectInfo() == null || version == null) { + if (!viewModel.isEveryFilterValid()) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); return ResponseType.NONE; } + final var project = viewModel.getProjectInfo().getProject(); + final var version = viewModel.getVersionFilter(); + final var component = viewModel.getComponentFilter(); + final var issueDao = dao.getIssueDao(); - final var detailView = viewModel.getProjectDetails(); - final var issues = issueDao.list(version); + final List issues; + if (version.equals(ProjectView.NO_VERSION)) { + if (component.equals(ProjectView.ALL_COMPONENTS)) { + issues = issueDao.list(project, (Version) null); + } else if (component.equals(ProjectView.NO_COMPONENT)) { + issues = issueDao.list(project, null, null); + } else { + issues = issueDao.list(project, component, null); + } + } else if (version.equals(ProjectView.ALL_VERSIONS)) { + if (component.equals(ProjectView.ALL_COMPONENTS)) { + issues = issueDao.list(project); + } else if (component.equals(ProjectView.NO_COMPONENT)) { + issues = issueDao.list(project, (Component)null); + } else { + issues = issueDao.list(project, component); + } + } else { + if (component.equals(ProjectView.ALL_COMPONENTS)) { + issues = issueDao.list(project, version); + } else if (component.equals(ProjectView.NO_COMPONENT)) { + issues = issueDao.list(project, null, version); + } else { + issues = issueDao.list(project, component, version); + } + } + for (var issue : issues) issueDao.joinVersionInformation(issue); issues.sort(new IssueSorter( new IssueSorter.Criteria(IssueSorter.Field.PHASE, true), new IssueSorter.Criteria(IssueSorter.Field.ETA, true), new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false) )); - detailView.updateDetails(issues, version); + + + viewModel.getProjectDetails().updateDetails(issues); + if (version.getId() > 0) + viewModel.getProjectDetails().updateVersionInfo(version); return forwardView(req, viewModel, "project-details"); } @@ -262,7 +307,6 @@ version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); dao.getVersionDao().saveOrUpdate(version, project); - // TODO: improve building the redirect location setRedirectLocation(req, "./projects/" + project.getId() + "/versions/"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { @@ -274,11 +318,90 @@ return ResponseType.HTML; } + @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET) + public ResponseType components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { + final var viewModel = new ComponentsView(); + populate(viewModel, pathParameters, dao); + + final var projectInfo = viewModel.getProjectInfo(); + if (projectInfo == null) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + return ResponseType.NONE; + } + + final var issueDao = dao.getIssueDao(); + final var issues = issueDao.list(projectInfo.getProject()); + viewModel.update(projectInfo.getComponents(), issues); + + return forwardView(req, viewModel, "components"); + } + + @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET) + public ResponseType editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { + final var viewModel = new ComponentEditView(); + populate(viewModel, pathParameters, dao); + + if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + return ResponseType.NONE; + } + + viewModel.setComponent(viewModel.getComponentFilter()); + viewModel.setUsers(dao.getUserDao().list()); + + return forwardView(req, viewModel, "component-form"); + } + + @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET) + public ResponseType createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { + final var viewModel = new ComponentEditView(); + populate(viewModel, pathParameters, dao); + + if (viewModel.getProjectInfo() == null) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + return ResponseType.NONE; + } + + viewModel.setComponent(new Component(-1)); + viewModel.setUsers(dao.getUserDao().list()); + + return forwardView(req, viewModel, "component-form"); + } + + @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST) + public ResponseType commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { + + try { + final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); + final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow()); + component.setName(getParameter(req, String.class, "name").orElseThrow()); + component.setColor(getParameter(req, WebColor.class, "color").orElseThrow()); + getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal); + getParameter(req, Integer.class, "lead").map( + userid -> userid >= 0 ? new User(userid) : null + ).ifPresent(component::setLead); + getParameter(req, String.class, "description").ifPresent(component::setDescription); + + dao.getComponentDao().saveOrUpdate(component, project); + + setRedirectLocation(req, "./projects/" + project.getId() + "/components/"); + setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); + } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { + resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); + // TODO: implement - fix issue #21 + return ResponseType.NONE; + } + + return ResponseType.HTML; + } + private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException { - issue.setProject(viewModel.getProjectInfo().getProject()); + final var project = viewModel.getProjectInfo().getProject(); + issue.setProject(project); viewModel.setIssue(issue); viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions()); viewModel.setUsers(dao.getUserDao().list()); + viewModel.setComponents(dao.getComponentDao().list(project)); if (issue.getId() >= 0) { viewModel.setComments(dao.getIssueDao().listComments(issue)); } @@ -337,6 +460,9 @@ getParameter(req, Integer.class, "assignee").map( userid -> userid >= 0 ? new User(userid) : null ).ifPresent(issue::setAssignee); + getParameter(req, Integer.class, "component").map( + cid -> cid >= 0 ? new Component(cid) : null + ).ifPresent(issue::setComponent); getParameter(req, String.class, "description").ifPresent(issue::setDescription); getParameter(req, Date.class, "eta").ifPresent(issue::setEta); @@ -354,7 +480,7 @@ dao.getIssueDao().saveOrUpdate(issue, issue.getProject()); // TODO: fix issue #14 - setRedirectLocation(req, "./projects/" + issue.getProject().getId() + "/versions/"); + setRedirectLocation(req, "./projects/" + issue.getProject().getId() + "/all-components/all-versions/issues/"); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); return ResponseType.HTML; diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/viewmodel/ComponentEditView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ComponentEditView.java Sat Oct 17 19:56:50 2020 +0200 @@ -0,0 +1,40 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.Component; +import de.uapcore.lightpit.entities.User; + +import java.util.List; + +public class ComponentEditView extends ProjectView { + private Component component; + private List users; + private String errorText; + + public ComponentEditView() { + setSelectedPage(SELECTED_PAGE_COMPONENTS); + } + + public void setComponent(Component component) { + this.component = component; + } + + public Component getComponent() { + return component; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public String getErrorText() { + return errorText; + } + + public void setErrorText(String errorText) { + this.errorText = errorText; + } +} diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/viewmodel/ComponentInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ComponentInfo.java Sat Oct 17 19:56:50 2020 +0200 @@ -0,0 +1,42 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.Component; +import de.uapcore.lightpit.entities.Issue; +import de.uapcore.lightpit.entities.IssueSummary; + +import java.util.ArrayList; +import java.util.List; + +public class ComponentInfo { + + private final Component component; + + private final IssueSummary issueSummary = new IssueSummary(); + + private final List issues = new ArrayList<>(); + + public ComponentInfo(Component component) { + this.component = component; + } + + public Component getComponent() { + return component; + } + + public IssueSummary getIssueSummary() { + return issueSummary; + } + + public List getIssues() { + return issues; + } + + public void collectIssues(List issues) { + for (Issue issue : issues) { + if (component.equals(issue.getComponent())) { + this.issues.add(issue); + this.issueSummary.add(issue); + } + } + } +} diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/viewmodel/ComponentsView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ComponentsView.java Sat Oct 17 19:56:50 2020 +0200 @@ -0,0 +1,29 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.Component; +import de.uapcore.lightpit.entities.Issue; + +import java.util.ArrayList; +import java.util.List; + +public class ComponentsView extends ProjectView { + + private List componentInfos = new ArrayList<>(); + + public ComponentsView() { + setSelectedPage(SELECTED_PAGE_COMPONENTS); + } + + public void update(List components, List issues) { + componentInfos.clear(); + for (var component : components) { + final var info = new ComponentInfo(component); + info.collectIssues(issues); + componentInfos.add(info); + } + } + + public List getComponentInfos() { + return componentInfos; + } +} diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java --- a/src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java Sat Oct 17 19:56:50 2020 +0200 @@ -11,6 +11,7 @@ private Set versionsUpcoming = new HashSet<>(); private Set versionsRecent = new HashSet<>(); private List users; + private List components; private List comments; public void setIssue(Issue issue) { @@ -45,7 +46,8 @@ versionsUpcoming.addAll(issue.getResolvedVersions()); for (var v : versions) { if (v.getStatus().isReleased()) { - versionsRecent.add(v); + if (!v.getStatus().equals(VersionStatus.Deprecated)) + versionsRecent.add(v); } else { versionsUpcoming.add(v); } @@ -60,6 +62,14 @@ this.users = users; } + public List getComponents() { + return components; + } + + public void setComponents(List components) { + this.components = components; + } + public IssueStatus[] getIssueStatus() { return IssueStatus.values(); } diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/viewmodel/ProjectDetails.java --- a/src/main/java/de/uapcore/lightpit/viewmodel/ProjectDetails.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectDetails.java Sat Oct 17 19:56:50 2020 +0200 @@ -13,14 +13,15 @@ private List issues; private IssueSummary issueSummary; - public void updateDetails(List issues, Version version) { + public void updateDetails(List issues) { this.issues = issues; issueSummary = new IssueSummary(); issues.forEach(issueSummary::add); - if (version != null) { - versionInfo = new VersionInfo(version); - versionInfo.collectIssues(issues); - } + } + + public void updateVersionInfo(Version version) { + versionInfo = new VersionInfo(version); + versionInfo.collectIssues(issues); } public List getIssues() { diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/viewmodel/ProjectView.java --- a/src/main/java/de/uapcore/lightpit/viewmodel/ProjectView.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectView.java Sat Oct 17 19:56:50 2020 +0200 @@ -8,11 +8,29 @@ public class ProjectView { + public static final int SELECTED_PAGE_ISSUES = 0; + public static final int SELECTED_PAGE_VERSIONS = 1; + public static final int SELECTED_PAGE_COMPONENTS = 2; + + public static final Version ALL_VERSIONS = new Version(0); + public static final Version NO_VERSION = new Version(-1); + public static final Component ALL_COMPONENTS = new Component(0); + public static final Component NO_COMPONENT = new Component(-1); + + static { + ALL_VERSIONS.setNode("all-versions"); + NO_VERSION.setNode("no-version"); + ALL_COMPONENTS.setNode("all-components"); + NO_COMPONENT.setNode("no-component"); + } + private final List projectList = new ArrayList<>(); private ProjectInfo projectInfo; private Version versionFilter; private Component componentFilter; + private int selectedPage = SELECTED_PAGE_ISSUES; + public List getProjectList() { return projectList; } @@ -25,6 +43,14 @@ this.projectInfo = projectInfo; } + public int getSelectedPage() { + return selectedPage; + } + + public void setSelectedPage(int selectedPage) { + this.selectedPage = selectedPage; + } + public Version getVersionFilter() { return versionFilter; } @@ -40,4 +66,20 @@ public void setComponentFilter(Component componentFilter) { this.componentFilter = componentFilter; } + + public boolean isProjectInfoPresent() { + return projectInfo != null; + } + + public boolean isVersionFilterValid() { + return projectInfo != null && versionFilter != null; + } + + public boolean isComponentFilterValid() { + return projectInfo != null && componentFilter != null; + } + + public boolean isEveryFilterValid() { + return projectInfo != null && versionFilter != null && componentFilter != null; + } } diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/viewmodel/VersionEditView.java --- a/src/main/java/de/uapcore/lightpit/viewmodel/VersionEditView.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionEditView.java Sat Oct 17 19:56:50 2020 +0200 @@ -7,6 +7,10 @@ private Version version; private String errorText; + public VersionEditView() { + setSelectedPage(SELECTED_PAGE_VERSIONS); + } + public void setVersion(Version version) { this.version = version; } diff -r ef075cd7ce55 -r f47e82cd6077 src/main/java/de/uapcore/lightpit/viewmodel/VersionsView.java --- a/src/main/java/de/uapcore/lightpit/viewmodel/VersionsView.java Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionsView.java Sat Oct 17 19:56:50 2020 +0200 @@ -8,18 +8,22 @@ public class VersionsView extends ProjectView { - private List versionInfo = new ArrayList<>(); + private List versionInfos = new ArrayList<>(); + + public VersionsView() { + setSelectedPage(SELECTED_PAGE_VERSIONS); + } public void update(List versions, List issues) { - versionInfo.clear(); + versionInfos.clear(); for (var version : versions) { final var info = new VersionInfo(version); info.collectIssues(issues); - versionInfo.add(info); + versionInfos.add(info); } } - public List getVersionInfo() { - return versionInfo; + public List getVersionInfos() { + return versionInfos; } } diff -r ef075cd7ce55 -r f47e82cd6077 src/main/resources/localization/projects.properties --- a/src/main/resources/localization/projects.properties Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/resources/localization/projects.properties Sat Oct 17 19:56:50 2020 +0200 @@ -24,6 +24,8 @@ pageTitle=Project Tracking button.create=New Project +button.component.create=New Component +button.component.edit=Edit Component button.version.create=New Version button.version.edit=Edit Version button.issue.create=New Issue @@ -43,6 +45,8 @@ owner=Project Lead version.latest=Latest Version version.next=Next Version +issues=Issues +component=Component progress=Overall Progress @@ -56,10 +60,20 @@ version.name=Version version.status=Status version.ordinal=Ordering + +component.project=Project +component.name=Component +component.color=Color +component.lead=Lead +component.ordinal=Ordering +component.description=Description + tooltip.ordinal=Use to override lexicographic ordering. placeholder.null-owner=Unassigned +placeholder.null-lead=Unassigned placeholder.null-assignee=Unassigned +placeholder.null-component=Unassigned version.status.Future=Future version.status.Unreleased=Unreleased @@ -67,10 +81,10 @@ version.status.LTS=LTS version.status.Deprecated=Deprecated - issue.without-version=No Assigned Version issue.id=Issue ID issue.project=Project +issue.component=Component issue.subject=Subject issue.description=Description issue.assignee=Assignee diff -r ef075cd7ce55 -r f47e82cd6077 src/main/resources/localization/projects_de.properties --- a/src/main/resources/localization/projects_de.properties Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/resources/localization/projects_de.properties Sat Oct 17 19:56:50 2020 +0200 @@ -24,6 +24,8 @@ pageTitle=Projektverwaltung button.create=Neues Projekt +button.component.create=Neue Komponente +button.component.edit=Komponente Bearbeiten button.version.create=Neue Version button.version.edit=Version Bearbeiten button.issue.create=Neuer Vorgang @@ -43,6 +45,8 @@ owner=Projektleitung version.latest=Neuste Version version.next=N\u00e4chste Version +component=Komponente +issues=Vorg\u00e4nge progress=Gesamtfortschritt @@ -56,10 +60,20 @@ version.name=Version version.status=Status version.ordinal=Sequenznummer + +component.project=Projekt +component.name=Komponente +component.color=Farbe +component.lead=Leitung +component.ordinal=Sequenznummer +component.description=Beschreibung + tooltip.ordinal=\u00dcbersteuert die lexikographische Sortierung. placeholder.null-owner=Nicht Zugewiesen +placeholder.null-lead=Niemand placeholder.null-assignee=Niemandem +placeholder.null-component=Keine version.status.Future=Geplant version.status.Unreleased=Unver\u00f6ffentlicht @@ -70,6 +84,7 @@ issue.without-version=Keine Version zugeordnet issue.id=Vorgangs-ID issue.project=Projekt +issue.component=Komponente issue.subject=Thema issue.description=Beschreibung issue.assignee=Zugewiesen diff -r ef075cd7ce55 -r f47e82cd6077 src/main/webapp/WEB-INF/jsp/component-form.jsp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jsp/component-form.jsp Sat Oct 17 19:56:50 2020 +0200 @@ -0,0 +1,95 @@ +<%-- +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2020 Mike Becker. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--%> +<%@page pageEncoding="UTF-8" %> +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + "> + + + + + + + + + + + + + +
+ + +
" />
+ +
+ +
+ +
+ + + + + +
+
diff -r ef075cd7ce55 -r f47e82cd6077 src/main/webapp/WEB-INF/jsp/components.jsp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jsp/components.jsp Sat Oct 17 19:56:50 2020 +0200 @@ -0,0 +1,95 @@ +<%-- +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2020 Mike Becker. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--%> +<%@page pageEncoding="UTF-8" %> +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + + + + +<%@include file="../jspf/project-header.jspf"%> + +
+ + +
+ +

+ + +<%@include file="../jspf/issue-summary.jspf"%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + ${componentInfo.issueSummary.open}${componentInfo.issueSummary.active}${componentInfo.issueSummary.done}
+ + <%@include file="../jspf/issue-progress.jspf" %> +
\ No newline at end of file diff -r ef075cd7ce55 -r f47e82cd6077 src/main/webapp/WEB-INF/jsp/issue-form.jsp --- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat Oct 17 19:56:50 2020 +0200 @@ -74,6 +74,19 @@ + + + + + + <%-- TODO: fix #14 --%> - + diff -r ef075cd7ce55 -r f47e82cd6077 src/main/webapp/WEB-INF/jsp/issues.jsp --- a/src/main/webapp/WEB-INF/jsp/issues.jsp Sat Oct 17 15:21:56 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -<%-- -DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - -Copyright 2018 Mike Becker. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---%> -<%@page pageEncoding="UTF-8" %> -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - -

TODO: REWRITE THIS PAGE

-<%-- -TODO: rewrite - - - -<%@include file="../jspf/project-header.jspf"%> - - -

- - - -

-
- -
-
- - - - -
-
- - -<%@include file="../jspf/issue-list.jspf"%> ---%> \ No newline at end of file diff -r ef075cd7ce55 -r f47e82cd6077 src/main/webapp/WEB-INF/jsp/project-details.jsp --- a/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat Oct 17 19:56:50 2020 +0200 @@ -24,21 +24,26 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --%> -<%@page pageEncoding="UTF-8" %> +<%@page pageEncoding="UTF-8" import="de.uapcore.lightpit.viewmodel.ProjectView" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + <%@include file="../jspf/project-header.jspf"%>
- - + + + - - + + + + +

@@ -46,36 +51,43 @@ <%@include file="../jspf/issue-summary.jspf"%> - - -

- - -

- -

- - <%@include file="../jspf/issue-summary.jspf"%> - - - <%@include file="../jspf/issue-list.jspf"%> - + + +

+ + + + + + +

+ + + <%@include file="../jspf/issue-summary.jspf"%> + + <%@include file="../jspf/issue-list.jspf"%> + +
+ + +

+ - +

- - -

- +

+ <%@include file="../jspf/issue-summary.jspf"%> - <%@include file="../jspf/issue-list.jspf"%> -
-
- -

- -

- - - <%@include file="../jspf/issue-summary.jspf"%> - - <%@include file="../jspf/issue-list.jspf"%> - -
+ + + <%@include file="../jspf/issue-list.jspf"%> + + + + +

+ + <%@include file="../jspf/issue-summary.jspf"%> + <%@include file="../jspf/issue-list.jspf"%> +
+ + \ No newline at end of file diff -r ef075cd7ce55 -r f47e82cd6077 src/main/webapp/WEB-INF/jsp/project-navmenu.jsp --- a/src/main/webapp/WEB-INF/jsp/project-navmenu.jsp Sat Oct 17 15:21:56 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/project-navmenu.jsp Sat Oct 17 19:56:50 2020 +0200 @@ -24,7 +24,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --%> -<%@page pageEncoding="UTF-8" %> +<%@page pageEncoding="UTF-8" import="de.uapcore.lightpit.viewmodel.ProjectView" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> @@ -33,20 +33,27 @@ -