Mon, 01 Jun 2020 14:46:58 +0200
improves issue overview and adds progress information
1.1 --- a/pom.xml Sat May 30 18:12:38 2020 +0200 1.2 +++ b/pom.xml Mon Jun 01 14:46:58 2020 +0200 1.3 @@ -4,7 +4,7 @@ 1.4 <modelVersion>4.0.0</modelVersion> 1.5 <groupId>de.uapcore</groupId> 1.6 <artifactId>lightpit</artifactId> 1.7 - <version>0.1-SNAPSHOT</version> 1.8 + <version>0.2</version> 1.9 <packaging>war</packaging> 1.10 <properties> 1.11 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
2.1 --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat May 30 18:12:38 2020 +0200 2.2 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Mon Jun 01 14:46:58 2020 +0200 2.3 @@ -88,6 +88,7 @@ 2.4 2.5 /** 2.6 * Returns the name of the resource bundle associated with this servlet. 2.7 + * 2.8 * @return the resource bundle base name 2.9 */ 2.10 protected abstract String getResourceBundleName(); 2.11 @@ -266,6 +267,17 @@ 2.12 } 2.13 2.14 /** 2.15 + * Sets the view model object. 2.16 + * The type must match the expected type in the JSP file. 2.17 + * 2.18 + * @param req the servlet request object 2.19 + * @param viewModel the view model object 2.20 + */ 2.21 + public void setViewModel(HttpServletRequest req, Object viewModel) { 2.22 + req.setAttribute(Constants.REQ_ATTR_VIEWMODEL, viewModel); 2.23 + } 2.24 + 2.25 + /** 2.26 * Obtains a request parameter of the specified type. 2.27 * The specified type must have a single-argument constructor accepting a string to perform conversion. 2.28 * The constructor of the specified type may throw an exception on conversion failures. 2.29 @@ -281,7 +293,7 @@ 2.30 final String[] paramValues = req.getParameterValues(name); 2.31 int len = paramValues == null ? 0 : paramValues.length; 2.32 final var array = (T) Array.newInstance(clazz.getComponentType(), len); 2.33 - for (int i = 0 ; i < len ; i++) { 2.34 + for (int i = 0; i < len; i++) { 2.35 try { 2.36 final Constructor<?> ctor = clazz.getComponentType().getConstructor(String.class); 2.37 Array.set(array, i, ctor.newInstance(paramValues[i]));
3.1 --- a/src/main/java/de/uapcore/lightpit/Constants.java Sat May 30 18:12:38 2020 +0200 3.2 +++ b/src/main/java/de/uapcore/lightpit/Constants.java Mon Jun 01 14:46:58 2020 +0200 3.3 @@ -92,6 +92,11 @@ 3.4 public static final String REQ_ATTR_CONTENT_PAGE = fqn(AbstractLightPITServlet.class, "content-page"); 3.5 3.6 /** 3.7 + * Key for the view model object (the type depends on the rendered site). 3.8 + */ 3.9 + public static final String REQ_ATTR_VIEWMODEL = "viewmodel"; 3.10 + 3.11 + /** 3.12 * Key for the name of the additional stylesheet used by a module. 3.13 */ 3.14 public static final String REQ_ATTR_STYLESHEET = fqn(AbstractLightPITServlet.class, "extraCss");
4.1 --- a/src/main/java/de/uapcore/lightpit/dao/IssueDao.java Sat May 30 18:12:38 2020 +0200 4.2 +++ b/src/main/java/de/uapcore/lightpit/dao/IssueDao.java Mon Jun 01 14:46:58 2020 +0200 4.3 @@ -30,6 +30,7 @@ 4.4 4.5 import de.uapcore.lightpit.entities.Issue; 4.6 import de.uapcore.lightpit.entities.Project; 4.7 +import de.uapcore.lightpit.entities.Version; 4.8 4.9 import java.sql.SQLException; 4.10 import java.util.List; 4.11 @@ -48,6 +49,15 @@ 4.12 List<Issue> list(Project project) throws SQLException; 4.13 4.14 /** 4.15 + * Lists all issues that are somehow related to the specified version. 4.16 + * 4.17 + * @param version the version 4.18 + * @return a list of issues 4.19 + * @throws SQLException on any kind of SQL error 4.20 + */ 4.21 + List<Issue> list(Version version) throws SQLException; 4.22 + 4.23 + /** 4.24 * Saves an instances to the database. 4.25 * Implementations of this DAO must guarantee that the generated ID is stored in the instance. 4.26 *
5.1 --- a/src/main/java/de/uapcore/lightpit/dao/ProjectDao.java Sat May 30 18:12:38 2020 +0200 5.2 +++ b/src/main/java/de/uapcore/lightpit/dao/ProjectDao.java Mon Jun 01 14:46:58 2020 +0200 5.3 @@ -28,6 +28,7 @@ 5.4 */ 5.5 package de.uapcore.lightpit.dao; 5.6 5.7 +import de.uapcore.lightpit.entities.IssueSummary; 5.8 import de.uapcore.lightpit.entities.Project; 5.9 5.10 import java.sql.SQLException; 5.11 @@ -35,4 +36,6 @@ 5.12 5.13 public interface ProjectDao extends GenericDao<Project> { 5.14 List<Project> list() throws SQLException; 5.15 + 5.16 + IssueSummary getIssueSummary(Project project) throws SQLException; 5.17 }
6.1 --- a/src/main/java/de/uapcore/lightpit/dao/VersionDao.java Sat May 30 18:12:38 2020 +0200 6.2 +++ b/src/main/java/de/uapcore/lightpit/dao/VersionDao.java Mon Jun 01 14:46:58 2020 +0200 6.3 @@ -30,7 +30,6 @@ 6.4 6.5 import de.uapcore.lightpit.entities.Project; 6.6 import de.uapcore.lightpit.entities.Version; 6.7 -import de.uapcore.lightpit.entities.VersionStatistics; 6.8 6.9 import java.sql.SQLException; 6.10 import java.util.List; 6.11 @@ -45,31 +44,4 @@ 6.12 * @throws SQLException on any kind of SQL error 6.13 */ 6.14 List<Version> list(Project project) throws SQLException; 6.15 - 6.16 - /** 6.17 - * Retrieves statistics about issues that arose in a version. 6.18 - * 6.19 - * @param version the version 6.20 - * @return version statistics 6.21 - * @throws SQLException on any kind of SQL error 6.22 - */ 6.23 - VersionStatistics statsOpenedIssues(Version version) throws SQLException; 6.24 - 6.25 - /** 6.26 - * Retrieves statistics about issues that are scheduled for a version. 6.27 - * 6.28 - * @param version the version 6.29 - * @return version statistics 6.30 - * @throws SQLException on any kind of SQL error 6.31 - */ 6.32 - VersionStatistics statsScheduledIssues(Version version) throws SQLException; 6.33 - 6.34 - /** 6.35 - * Retrieves statistics about issues that are resolved in a version. 6.36 - * 6.37 - * @param version the version 6.38 - * @return version statistics 6.39 - * @throws SQLException on any kind of SQL error 6.40 - */ 6.41 - VersionStatistics statsResolvedIssues(Version version) throws SQLException; 6.42 }
7.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Sat May 30 18:12:38 2020 +0200 7.2 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Mon Jun 01 14:46:58 2020 +0200 7.3 @@ -43,7 +43,7 @@ 7.4 7.5 public final class PGIssueDao implements IssueDao { 7.6 7.7 - private final PreparedStatement insert, update, list, find; 7.8 + private final PreparedStatement insert, update, list, listForVersion, find; 7.9 private final PreparedStatement affectedVersions, scheduledVersions, resolvedVersions; 7.10 private final PreparedStatement clearAffected, clearScheduled, clearResolved; 7.11 private final PreparedStatement insertAffected, insertScheduled, insertResolved; 7.12 @@ -56,7 +56,24 @@ 7.13 "from lpit_issue i " + 7.14 "left join lpit_project p on project = projectid " + 7.15 "left join lpit_user on userid = assignee " + 7.16 - "where project = ? "); 7.17 + "where project = ? "+ 7.18 + "order by eta asc, updated desc"); 7.19 + 7.20 + listForVersion = connection.prepareStatement( 7.21 + "with issue_version as ( "+ 7.22 + "select issueid, versionid from lpit_issue_affected_version union "+ 7.23 + "select issueid, versionid from lpit_issue_scheduled_version union "+ 7.24 + "select issueid, versionid from lpit_issue_resolved_version) "+ 7.25 + "select issueid, project, p.name as projectname, status, category, subject, i.description, " + 7.26 + "userid, username, givenname, lastname, mail, " + 7.27 + "created, updated, eta " + 7.28 + "from lpit_issue i " + 7.29 + "join issue_version using (issueid) "+ 7.30 + "left join lpit_project p on project = projectid " + 7.31 + "left join lpit_user on userid = assignee " + 7.32 + "where versionid = ? "+ 7.33 + "order by eta asc, updated desc" 7.34 + ); 7.35 7.36 find = connection.prepareStatement( 7.37 "select issueid, project, p.name as projectname, status, category, subject, i.description, " + 7.38 @@ -121,7 +138,8 @@ 7.39 private Issue mapColumns(ResultSet result) throws SQLException { 7.40 final var project = new Project(result.getInt("project")); 7.41 project.setName(result.getString("projectname")); 7.42 - final var issue = new Issue(result.getInt("issueid"), project); 7.43 + final var issue = new Issue(result.getInt("issueid")); 7.44 + issue.setProject(project); 7.45 issue.setStatus(IssueStatus.valueOf(result.getString("status"))); 7.46 issue.setCategory(IssueCategory.valueOf(result.getString("category"))); 7.47 issue.setSubject(result.getString("subject")); 7.48 @@ -133,8 +151,8 @@ 7.49 return issue; 7.50 } 7.51 7.52 - private Version mapVersion(ResultSet result, Project project) throws SQLException { 7.53 - final var version = new Version(result.getInt("versionid"), project); 7.54 + private Version mapVersion(ResultSet result) throws SQLException { 7.55 + final var version = new Version(result.getInt("versionid")); 7.56 version.setName(result.getString("name")); 7.57 version.setOrdinal(result.getInt("ordinal")); 7.58 version.setStatus(VersionStatus.valueOf(result.getString("status"))); 7.59 @@ -203,11 +221,10 @@ 7.60 } 7.61 } 7.62 7.63 - @Override 7.64 - public List<Issue> list(Project project) throws SQLException { 7.65 - list.setInt(1, project.getId()); 7.66 + private List<Issue> list(PreparedStatement query, int arg) throws SQLException { 7.67 + query.setInt(1, arg); 7.68 List<Issue> issues = new ArrayList<>(); 7.69 - try (var result = list.executeQuery()) { 7.70 + try (var result = query.executeQuery()) { 7.71 while (result.next()) { 7.72 issues.add(mapColumns(result)); 7.73 } 7.74 @@ -216,6 +233,16 @@ 7.75 } 7.76 7.77 @Override 7.78 + public List<Issue> list(Project project) throws SQLException { 7.79 + return list(list, project.getId()); 7.80 + } 7.81 + 7.82 + @Override 7.83 + public List<Issue> list(Version version) throws SQLException { 7.84 + return list(listForVersion, version.getId()); 7.85 + } 7.86 + 7.87 + @Override 7.88 public Issue find(int id) throws SQLException { 7.89 find.setInt(1, id); 7.90 try (var result = find.executeQuery()) { 7.91 @@ -232,7 +259,7 @@ 7.92 List<Version> versions = new ArrayList<>(); 7.93 try (var result = stmt.executeQuery()) { 7.94 while (result.next()) { 7.95 - versions.add(mapVersion(result, issue.getProject())); 7.96 + versions.add(mapVersion(result)); 7.97 } 7.98 } 7.99 return versions;
8.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Sat May 30 18:12:38 2020 +0200 8.2 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Mon Jun 01 14:46:58 2020 +0200 8.3 @@ -29,6 +29,7 @@ 8.4 package de.uapcore.lightpit.dao.postgres; 8.5 8.6 import de.uapcore.lightpit.dao.ProjectDao; 8.7 +import de.uapcore.lightpit.entities.IssueSummary; 8.8 import de.uapcore.lightpit.entities.Project; 8.9 import de.uapcore.lightpit.entities.User; 8.10 8.11 @@ -98,24 +99,26 @@ 8.12 return proj; 8.13 } 8.14 8.15 - private void mapIssueSummary(Project proj) throws SQLException { 8.16 - issue_summary.setInt(1, proj.getId()); 8.17 + public IssueSummary getIssueSummary(Project project) throws SQLException { 8.18 + issue_summary.setInt(1, project.getId()); 8.19 final var result = issue_summary.executeQuery(); 8.20 + final var summary = new IssueSummary(); 8.21 while (result.next()) { 8.22 final var phase = result.getInt("phase"); 8.23 final var total = result.getInt("total"); 8.24 switch(phase) { 8.25 case 0: 8.26 - proj.setOpenIssues(total); 8.27 + summary.setOpen(total); 8.28 break; 8.29 case 1: 8.30 - proj.setActiveIssues(total); 8.31 + summary.setActive(total); 8.32 break; 8.33 case 2: 8.34 - proj.setDoneIssues(total); 8.35 + summary.setDone(total); 8.36 break; 8.37 } 8.38 } 8.39 + return summary; 8.40 } 8.41 8.42 @Override 8.43 @@ -146,7 +149,6 @@ 8.44 try (var result = list.executeQuery()) { 8.45 while (result.next()) { 8.46 final var project = mapColumns(result); 8.47 - mapIssueSummary(project); 8.48 projects.add(project); 8.49 } 8.50 } 8.51 @@ -159,7 +161,6 @@ 8.52 try (var result = find.executeQuery()) { 8.53 if (result.next()) { 8.54 final var project = mapColumns(result); 8.55 - mapIssueSummary(project); 8.56 return project; 8.57 } else { 8.58 return null;
9.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java Sat May 30 18:12:38 2020 +0200 9.2 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java Mon Jun 01 14:46:58 2020 +0200 9.3 @@ -29,7 +29,9 @@ 9.4 package de.uapcore.lightpit.dao.postgres; 9.5 9.6 import de.uapcore.lightpit.dao.VersionDao; 9.7 -import de.uapcore.lightpit.entities.*; 9.8 +import de.uapcore.lightpit.entities.Project; 9.9 +import de.uapcore.lightpit.entities.Version; 9.10 +import de.uapcore.lightpit.entities.VersionStatus; 9.11 9.12 import java.sql.Connection; 9.13 import java.sql.PreparedStatement; 9.14 @@ -42,7 +44,6 @@ 9.15 public final class PGVersionDao implements VersionDao { 9.16 9.17 private final PreparedStatement insert, update, list, find; 9.18 - private final PreparedStatement issuesAffected, issuesScheduled, issuesResolved; 9.19 9.20 public PGVersionDao(Connection connection) throws SQLException { 9.21 list = connection.prepareStatement( 9.22 @@ -64,54 +65,19 @@ 9.23 update = connection.prepareStatement( 9.24 "update lpit_version set name = ?, ordinal = ?, status = ?::version_status where versionid = ?" 9.25 ); 9.26 - 9.27 - issuesAffected = connection.prepareStatement( 9.28 - "select category, status, count(*) as issuecount " + 9.29 - "from lpit_issue_affected_version " + 9.30 - "join lpit_issue using (issueid) " + 9.31 - "where versionid = ? " + 9.32 - "group by category, status" 9.33 - ); 9.34 - issuesScheduled = connection.prepareStatement( 9.35 - "select category, status, count(*) as issuecount " + 9.36 - "from lpit_issue_scheduled_version " + 9.37 - "join lpit_issue using (issueid) " + 9.38 - "where versionid = ? " + 9.39 - "group by category, status" 9.40 - ); 9.41 - issuesResolved = connection.prepareStatement( 9.42 - "select category, status, count(*) as issuecount " + 9.43 - "from lpit_issue_resolved_version " + 9.44 - "join lpit_issue using (issueid) " + 9.45 - "where versionid = ? " + 9.46 - "group by category, status" 9.47 - ); 9.48 } 9.49 9.50 private Version mapColumns(ResultSet result) throws SQLException { 9.51 final var project = new Project(result.getInt("project")); 9.52 project.setName(result.getString("projectname")); 9.53 - final var version = new Version(result.getInt("versionid"), project); 9.54 + final var version = new Version(result.getInt("versionid")); 9.55 + version.setProject(project); 9.56 version.setName(result.getString("name")); 9.57 version.setOrdinal(result.getInt("ordinal")); 9.58 version.setStatus(VersionStatus.valueOf(result.getString("status"))); 9.59 return version; 9.60 } 9.61 9.62 - private VersionStatistics versionStatistics(Version version, PreparedStatement stmt) throws SQLException { 9.63 - stmt.setInt(1, version.getId()); 9.64 - final var result = stmt.executeQuery(); 9.65 - final var stats = new VersionStatistics(version); 9.66 - while (result.next()) { 9.67 - stats.setIssueCount( 9.68 - IssueCategory.valueOf(result.getString("category")), 9.69 - IssueStatus.valueOf(result.getString("status")), 9.70 - result.getInt("issuecount") 9.71 - ); 9.72 - } 9.73 - return stats; 9.74 - } 9.75 - 9.76 @Override 9.77 public void save(Version instance) throws SQLException { 9.78 Objects.requireNonNull(instance.getName()); 9.79 @@ -159,19 +125,4 @@ 9.80 } 9.81 } 9.82 } 9.83 - 9.84 - @Override 9.85 - public VersionStatistics statsOpenedIssues(Version version) throws SQLException { 9.86 - return versionStatistics(version, issuesAffected); 9.87 - } 9.88 - 9.89 - @Override 9.90 - public VersionStatistics statsScheduledIssues(Version version) throws SQLException { 9.91 - return versionStatistics(version, issuesScheduled); 9.92 - } 9.93 - 9.94 - @Override 9.95 - public VersionStatistics statsResolvedIssues(Version version) throws SQLException { 9.96 - return versionStatistics(version, issuesResolved); 9.97 - } 9.98 }
10.1 --- a/src/main/java/de/uapcore/lightpit/entities/Issue.java Sat May 30 18:12:38 2020 +0200 10.2 +++ b/src/main/java/de/uapcore/lightpit/entities/Issue.java Mon Jun 01 14:46:58 2020 +0200 10.3 @@ -38,7 +38,7 @@ 10.4 public final class Issue { 10.5 10.6 private int id; 10.7 - private final Project project; 10.8 + private Project project; 10.9 10.10 private IssueStatus status; 10.11 private IssueCategory category; 10.12 @@ -55,9 +55,8 @@ 10.13 private Timestamp updated = Timestamp.from(Instant.now()); 10.14 private Date eta; 10.15 10.16 - public Issue(int id, Project project) { 10.17 + public Issue(int id) { 10.18 this.id = id; 10.19 - this.project = project; 10.20 } 10.21 10.22 public int getId() { 10.23 @@ -72,6 +71,10 @@ 10.24 this.id = id; 10.25 } 10.26 10.27 + public void setProject(Project project) { 10.28 + this.project = project; 10.29 + } 10.30 + 10.31 public Project getProject() { 10.32 return project; 10.33 }
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/src/main/java/de/uapcore/lightpit/entities/IssueSummary.java Mon Jun 01 14:46:58 2020 +0200 11.3 @@ -0,0 +1,67 @@ 11.4 +package de.uapcore.lightpit.entities; 11.5 + 11.6 +public class IssueSummary { 11.7 + private int open = 0; 11.8 + private int active = 0; 11.9 + private int done = 0; 11.10 + 11.11 + public int getOpen() { 11.12 + return open; 11.13 + } 11.14 + 11.15 + public void setOpen(int open) { 11.16 + this.open = open; 11.17 + } 11.18 + 11.19 + public int getActive() { 11.20 + return active; 11.21 + } 11.22 + 11.23 + public void setActive(int active) { 11.24 + this.active = active; 11.25 + } 11.26 + 11.27 + public int getDone() { 11.28 + return done; 11.29 + } 11.30 + 11.31 + public void setDone(int done) { 11.32 + this.done = done; 11.33 + } 11.34 + 11.35 + public int getTotal() { 11.36 + return open+active+done; 11.37 + } 11.38 + 11.39 + public int getOpenPercent() { 11.40 + return 100-getActivePercent()-getDonePercent(); 11.41 + } 11.42 + 11.43 + public int getActivePercent() { 11.44 + int total = getTotal(); 11.45 + return total > 0 ? 100*active/total : 0; 11.46 + } 11.47 + 11.48 + public int getDonePercent() { 11.49 + int total = getTotal(); 11.50 + return total > 0 ? 100*done/total : 0; 11.51 + } 11.52 + 11.53 + /** 11.54 + * Adds the specified issue to the summary by increming the respective counter. 11.55 + * @param issue the issue 11.56 + */ 11.57 + public void add(Issue issue) { 11.58 + switch (issue.getStatus().getPhase()) { 11.59 + case 0: 11.60 + open++; 11.61 + break; 11.62 + case 1: 11.63 + active++; 11.64 + break; 11.65 + case 2: 11.66 + done++; 11.67 + break; 11.68 + } 11.69 + } 11.70 +}
12.1 --- a/src/main/java/de/uapcore/lightpit/entities/Project.java Sat May 30 18:12:38 2020 +0200 12.2 +++ b/src/main/java/de/uapcore/lightpit/entities/Project.java Mon Jun 01 14:46:58 2020 +0200 12.3 @@ -38,10 +38,6 @@ 12.4 private String repoUrl; 12.5 private User owner; 12.6 12.7 - private int openIssues; 12.8 - private int activeIssues; 12.9 - private int doneIssues; 12.10 - 12.11 public Project(int id) { 12.12 this.id = id; 12.13 } 12.14 @@ -82,30 +78,6 @@ 12.15 this.owner = owner; 12.16 } 12.17 12.18 - public int getOpenIssues() { 12.19 - return openIssues; 12.20 - } 12.21 - 12.22 - public void setOpenIssues(int openIssues) { 12.23 - this.openIssues = openIssues; 12.24 - } 12.25 - 12.26 - public int getActiveIssues() { 12.27 - return activeIssues; 12.28 - } 12.29 - 12.30 - public void setActiveIssues(int activeIssues) { 12.31 - this.activeIssues = activeIssues; 12.32 - } 12.33 - 12.34 - public int getDoneIssues() { 12.35 - return doneIssues; 12.36 - } 12.37 - 12.38 - public void setDoneIssues(int doneIssues) { 12.39 - this.doneIssues = doneIssues; 12.40 - } 12.41 - 12.42 @Override 12.43 public boolean equals(Object o) { 12.44 if (this == o) return true;
13.1 --- a/src/main/java/de/uapcore/lightpit/entities/Version.java Sat May 30 18:12:38 2020 +0200 13.2 +++ b/src/main/java/de/uapcore/lightpit/entities/Version.java Mon Jun 01 14:46:58 2020 +0200 13.3 @@ -41,9 +41,8 @@ 13.4 private int ordinal = 0; 13.5 private VersionStatus status = VersionStatus.Future; 13.6 13.7 - public Version(int id, Project project) { 13.8 + public Version(int id) { 13.9 this.id = id; 13.10 - this.project = project; 13.11 } 13.12 13.13 public int getId() {
14.1 --- a/src/main/java/de/uapcore/lightpit/modules/LanguageModule.java Sat May 30 18:12:38 2020 +0200 14.2 +++ b/src/main/java/de/uapcore/lightpit/modules/LanguageModule.java Mon Jun 01 14:46:58 2020 +0200 14.3 @@ -29,6 +29,7 @@ 14.4 package de.uapcore.lightpit.modules; 14.5 14.6 import de.uapcore.lightpit.*; 14.7 +import de.uapcore.lightpit.viewmodel.LanguageView; 14.8 import org.slf4j.Logger; 14.9 import org.slf4j.LoggerFactory; 14.10 14.11 @@ -86,9 +87,12 @@ 14.12 @RequestMapping(method = HttpMethod.GET) 14.13 public ResponseType handle(HttpServletRequest req) { 14.14 14.15 - req.setAttribute("languages", languages); 14.16 - req.setAttribute("browserLanguage", req.getLocale()); 14.17 + final var viewModel = new LanguageView(); 14.18 + viewModel.setLanguages(languages); 14.19 + viewModel.setBrowserLanguage(req.getLocale()); 14.20 + viewModel.setCurrentLanguage((Locale)req.getSession().getAttribute(Constants.SESSION_ATTR_LANGUAGE)); 14.21 14.22 + setViewModel(req, viewModel); 14.23 setStylesheet(req, "language"); 14.24 setContentPage(req, "language"); 14.25 return ResponseType.HTML;
15.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat May 30 18:12:38 2020 +0200 15.2 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Mon Jun 01 14:46:58 2020 +0200 15.3 @@ -32,6 +32,7 @@ 15.4 import de.uapcore.lightpit.*; 15.5 import de.uapcore.lightpit.dao.DataAccessObjects; 15.6 import de.uapcore.lightpit.entities.*; 15.7 +import de.uapcore.lightpit.viewmodel.*; 15.8 import org.slf4j.Logger; 15.9 import org.slf4j.LoggerFactory; 15.10 15.11 @@ -42,7 +43,10 @@ 15.12 import java.io.IOException; 15.13 import java.sql.Date; 15.14 import java.sql.SQLException; 15.15 -import java.util.*; 15.16 +import java.util.ArrayList; 15.17 +import java.util.List; 15.18 +import java.util.NoSuchElementException; 15.19 +import java.util.Objects; 15.20 import java.util.stream.Collectors; 15.21 import java.util.stream.Stream; 15.22 15.23 @@ -59,41 +63,81 @@ 15.24 public static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project"); 15.25 public static final String SESSION_ATTR_SELECTED_ISSUE = fqn(ProjectsModule.class, "selected_issue"); 15.26 public static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version"); 15.27 - public static final String SESSION_ATTR_HIDE_ZEROS = fqn(ProjectsModule.class, "stats_hide_zeros"); 15.28 15.29 private class SessionSelection { 15.30 final HttpSession session; 15.31 + final HttpServletRequest req; 15.32 + final DataAccessObjects dao; 15.33 Project project; 15.34 Version version; 15.35 Issue issue; 15.36 15.37 - SessionSelection(HttpServletRequest req, Project project) { 15.38 - this.session = req.getSession(); 15.39 - this.project = project; 15.40 + SessionSelection(HttpServletRequest req, DataAccessObjects dao) { 15.41 + this.req = req; 15.42 + this.dao = dao; 15.43 + session = req.getSession(); 15.44 + } 15.45 + 15.46 + void newProject() { 15.47 + project = null; 15.48 version = null; 15.49 issue = null; 15.50 updateAttributes(); 15.51 + project = new Project(-1); 15.52 + updateAttributes(); 15.53 } 15.54 15.55 - SessionSelection(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 15.56 - this.session = req.getSession(); 15.57 - final var issueDao = dao.getIssueDao(); 15.58 - final var projectDao = dao.getProjectDao(); 15.59 - final var issueSelection = getParameter(req, Integer.class, "issue"); 15.60 - if (issueSelection.isPresent()) { 15.61 - issue = issueDao.find(issueSelection.get()); 15.62 - } else { 15.63 - final var issue = (Issue) session.getAttribute(SESSION_ATTR_SELECTED_ISSUE); 15.64 - this.issue = issue == null ? null : issueDao.find(issue.getId()); 15.65 + void newVersion() throws SQLException { 15.66 + project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); 15.67 + syncProject(); 15.68 + version = null; 15.69 + issue = null; 15.70 + updateAttributes(); 15.71 + version = new Version(-1); 15.72 + version.setProject(project); 15.73 + updateAttributes(); 15.74 + } 15.75 + 15.76 + void newIssue() throws SQLException { 15.77 + project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); 15.78 + syncProject(); 15.79 + version = null; 15.80 + issue = null; 15.81 + updateAttributes(); 15.82 + issue = new Issue(-1); 15.83 + issue.setProject(project); 15.84 + updateAttributes(); 15.85 + } 15.86 + 15.87 + void selectVersion(Version selectedVersion) throws SQLException { 15.88 + issue = null; 15.89 + version = selectedVersion; 15.90 + if (!version.getProject().equals(project)) { 15.91 + project = dao.getProjectDao().find(version.getProject().getId()); 15.92 } 15.93 - if (issue != null) { 15.94 - version = null; // show the issue globally 15.95 - project = projectDao.find(issue.getProject().getId()); 15.96 + // our object contains more details 15.97 + version.setProject(project); 15.98 + updateAttributes(); 15.99 + } 15.100 + 15.101 + void selectIssue(Issue selectedIssue) throws SQLException { 15.102 + issue = selectedIssue; 15.103 + if (!issue.getProject().equals(project)) { 15.104 + project = dao.getProjectDao().find(issue.getProject().getId()); 15.105 } 15.106 + // our object contains more details 15.107 + issue.setProject(project); 15.108 + if (!issue.getResolvedVersions().contains(version) && !issue.getScheduledVersions().contains(version) 15.109 + && !issue.getAffectedVersions().contains(version)) { 15.110 + version = null; 15.111 + } 15.112 + updateAttributes(); 15.113 + } 15.114 15.115 + void syncProject() throws SQLException { 15.116 final var projectSelection = getParameter(req, Integer.class, "pid"); 15.117 if (projectSelection.isPresent()) { 15.118 - final var selectedProject = projectDao.find(projectSelection.get()); 15.119 + final var selectedProject = dao.getProjectDao().find(projectSelection.get()); 15.120 if (!Objects.equals(selectedProject, project)) { 15.121 // reset version and issue if project changed 15.122 version = null; 15.123 @@ -101,51 +145,57 @@ 15.124 } 15.125 project = selectedProject; 15.126 } else { 15.127 - final var sessionProject = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); 15.128 - project = sessionProject == null ? null : projectDao.find(sessionProject.getId()); 15.129 + project = project == null ? null : dao.getProjectDao().find(project.getId()); 15.130 } 15.131 + } 15.132 + 15.133 + void syncVersion() throws SQLException { 15.134 + final var versionSelection = getParameter(req, Integer.class, "vid"); 15.135 + if (versionSelection.isPresent()) { 15.136 + if (versionSelection.get() < 0) { 15.137 + version = null; 15.138 + } else { 15.139 + final var selectedVersion = dao.getVersionDao().find(versionSelection.get()); 15.140 + if (!Objects.equals(selectedVersion, version)) { 15.141 + issue = null; 15.142 + } 15.143 + selectVersion(selectedVersion); 15.144 + } 15.145 + } else { 15.146 + version = version == null ? null : dao.getVersionDao().find(version.getId()); 15.147 + } 15.148 + } 15.149 + 15.150 + void syncIssue() throws SQLException { 15.151 + final var issueSelection = getParameter(req, Integer.class, "issue"); 15.152 + if (issueSelection.isPresent()) { 15.153 + final var selectedIssue = dao.getIssueDao().find(issueSelection.get()); 15.154 + dao.getIssueDao().joinVersionInformation(selectedIssue); 15.155 + selectIssue(selectedIssue); 15.156 + } else { 15.157 + issue = issue == null ? null : dao.getIssueDao().find(issue.getId()); 15.158 + } 15.159 + } 15.160 + 15.161 + void sync() throws SQLException { 15.162 + project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); 15.163 + version = (Version) session.getAttribute(SESSION_ATTR_SELECTED_VERSION); 15.164 + issue = (Issue) session.getAttribute(SESSION_ATTR_SELECTED_ISSUE); 15.165 + 15.166 + syncProject(); 15.167 + syncVersion(); 15.168 + syncIssue(); 15.169 + 15.170 updateAttributes(); 15.171 } 15.172 15.173 - void selectVersion(Version version) { 15.174 - this.project = version.getProject(); 15.175 - this.version = version; 15.176 - this.issue = null; 15.177 - updateAttributes(); 15.178 - } 15.179 - 15.180 - void selectIssue(Issue issue) { 15.181 - this.project = issue.getProject(); 15.182 - this.issue = issue; 15.183 - this.version = null; 15.184 - updateAttributes(); 15.185 - } 15.186 - 15.187 - void updateAttributes() { 15.188 + private void updateAttributes() { 15.189 session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, project); 15.190 session.setAttribute(SESSION_ATTR_SELECTED_VERSION, version); 15.191 session.setAttribute(SESSION_ATTR_SELECTED_ISSUE, issue); 15.192 } 15.193 } 15.194 15.195 - private void setAttributeHideZeros(HttpServletRequest req) { 15.196 - final Boolean value; 15.197 - final var param = getParameter(req, Boolean.class, "reduced"); 15.198 - if (param.isPresent()) { 15.199 - value = param.get(); 15.200 - req.getSession().setAttribute(SESSION_ATTR_HIDE_ZEROS, value); 15.201 - } else { 15.202 - final var sessionValue = req.getSession().getAttribute(SESSION_ATTR_HIDE_ZEROS); 15.203 - if (sessionValue != null) { 15.204 - value = (Boolean) sessionValue; 15.205 - } else { 15.206 - value = false; 15.207 - req.getSession().setAttribute(SESSION_ATTR_HIDE_ZEROS, value); 15.208 - } 15.209 - } 15.210 - req.setAttribute("statsHideZeros", value); 15.211 - } 15.212 - 15.213 @Override 15.214 protected String getResourceBundleName() { 15.215 return "localization.projects"; 15.216 @@ -162,10 +212,10 @@ 15.217 * Creates the breadcrumb menu. 15.218 * 15.219 * @param level the current active level (0: root, 1: project, 2: version, 3: issue list, 4: issue) 15.220 - * @param sessionSelection the currently selected objects 15.221 + * @param selection the currently selected objects 15.222 * @return a dynamic breadcrumb menu trying to display as many levels as possible 15.223 */ 15.224 - private List<MenuEntry> getBreadcrumbs(int level, SessionSelection sessionSelection) { 15.225 + private List<MenuEntry> getBreadcrumbs(int level, SessionSelection selection) { 15.226 MenuEntry entry; 15.227 15.228 final var breadcrumbs = new ArrayList<MenuEntry>(); 15.229 @@ -174,47 +224,49 @@ 15.230 breadcrumbs.add(entry); 15.231 if (level == BREADCRUMB_LEVEL_ROOT) entry.setActive(true); 15.232 15.233 - if (sessionSelection.project != null) { 15.234 - if (sessionSelection.project.getId() < 0) { 15.235 + if (selection.project != null) { 15.236 + if (selection.project.getId() < 0) { 15.237 entry = new MenuEntry(new ResourceKey("localization.projects", "button.create"), 15.238 "projects/edit"); 15.239 } else { 15.240 - entry = new MenuEntry(sessionSelection.project.getName(), 15.241 - "projects/view?pid=" + sessionSelection.project.getId()); 15.242 + entry = new MenuEntry(selection.project.getName(), 15.243 + "projects/view?pid=" + selection.project.getId()); 15.244 } 15.245 if (level == BREADCRUMB_LEVEL_PROJECT) entry.setActive(true); 15.246 breadcrumbs.add(entry); 15.247 } 15.248 15.249 - if (sessionSelection.version != null) { 15.250 - if (sessionSelection.version.getId() < 0) { 15.251 + if (selection.version != null) { 15.252 + if (selection.version.getId() < 0) { 15.253 entry = new MenuEntry(new ResourceKey("localization.projects", "button.version.create"), 15.254 "projects/versions/edit"); 15.255 } else { 15.256 - entry = new MenuEntry(sessionSelection.version.getName(), 15.257 - // TODO: change link to issue overview for that version 15.258 - "projects/versions/edit?id=" + sessionSelection.version.getId()); 15.259 + entry = new MenuEntry(selection.version.getName(), 15.260 + "projects/versions/view?vid=" + selection.version.getId()); 15.261 } 15.262 if (level == BREADCRUMB_LEVEL_VERSION) entry.setActive(true); 15.263 breadcrumbs.add(entry); 15.264 } 15.265 15.266 - if (sessionSelection.project != null) { 15.267 + if (selection.project != null) { 15.268 + String path = "projects/issues/?pid=" + selection.project.getId(); 15.269 + if (selection.version != null) { 15.270 + path += "&vid="+selection.version.getId(); 15.271 + } 15.272 entry = new MenuEntry(new ResourceKey("localization.projects", "menu.issues"), 15.273 - // TODO: maybe also add selected version 15.274 - "projects/issues/?pid=" + sessionSelection.project.getId()); 15.275 + path); 15.276 if (level == BREADCRUMB_LEVEL_ISSUE_LIST) entry.setActive(true); 15.277 breadcrumbs.add(entry); 15.278 } 15.279 15.280 - if (sessionSelection.issue != null) { 15.281 - if (sessionSelection.issue.getId() < 0) { 15.282 + if (selection.issue != null) { 15.283 + if (selection.issue.getId() < 0) { 15.284 entry = new MenuEntry(new ResourceKey("localization.projects", "button.issue.create"), 15.285 "projects/issues/edit"); 15.286 } else { 15.287 - entry = new MenuEntry("#" + sessionSelection.issue.getId(), 15.288 + entry = new MenuEntry("#" + selection.issue.getId(), 15.289 // TODO: maybe change link to a view rather than directly opening the editor 15.290 - "projects/issues/edit?id=" + sessionSelection.issue.getId()); 15.291 + "projects/issues/edit?issue=" + selection.issue.getId()); 15.292 } 15.293 if (level == BREADCRUMB_LEVEL_ISSUE) entry.setActive(true); 15.294 breadcrumbs.add(entry); 15.295 @@ -226,8 +278,22 @@ 15.296 @RequestMapping(method = HttpMethod.GET) 15.297 public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 15.298 final var sessionSelection = new SessionSelection(req, dao); 15.299 - final var projectList = dao.getProjectDao().list(); 15.300 - req.setAttribute("projects", projectList); 15.301 + sessionSelection.sync(); 15.302 + 15.303 + final var projectDao = dao.getProjectDao(); 15.304 + final var versionDao = dao.getVersionDao(); 15.305 + 15.306 + final var projectList = projectDao.list(); 15.307 + 15.308 + final var viewModel = new ProjectIndexView(); 15.309 + for (var project : projectList) { 15.310 + final var info = new ProjectInfo(project); 15.311 + info.setVersions(versionDao.list(project)); 15.312 + info.setIssueSummary(projectDao.getIssueSummary(project)); 15.313 + viewModel.getProjects().add(info); 15.314 + } 15.315 + 15.316 + setViewModel(req, viewModel); 15.317 setContentPage(req, "projects"); 15.318 setStylesheet(req, "projects"); 15.319 15.320 @@ -236,17 +302,24 @@ 15.321 return ResponseType.HTML; 15.322 } 15.323 15.324 - private void configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 15.325 - req.setAttribute("project", selection.project); 15.326 - req.setAttribute("users", dao.getUserDao().list()); 15.327 + private ProjectEditView configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 15.328 + final var viewModel = new ProjectEditView(); 15.329 + viewModel.setProject(selection.project); 15.330 + viewModel.setUsers(dao.getUserDao().list()); 15.331 + setViewModel(req, viewModel); 15.332 setContentPage(req, "project-form"); 15.333 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, selection)); 15.334 + return viewModel; 15.335 } 15.336 15.337 @RequestMapping(requestPath = "edit", method = HttpMethod.GET) 15.338 public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 15.339 - final var selection = new SessionSelection(req, findByParameter(req, Integer.class, "id", 15.340 - dao.getProjectDao()::find).orElse(new Project(-1))); 15.341 + final var selection = new SessionSelection(req, dao); 15.342 + if (getParameter(req, Integer.class, "pid").isEmpty()) { 15.343 + selection.newProject(); 15.344 + } else { 15.345 + selection.sync(); 15.346 + } 15.347 15.348 configureEditForm(req, dao, selection); 15.349 15.350 @@ -272,10 +345,12 @@ 15.351 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 15.352 LOG.debug("Successfully updated project {}", project.getName()); 15.353 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 15.354 - // TODO: set request attribute with error text 15.355 LOG.warn("Form validation failure: {}", ex.getMessage()); 15.356 LOG.debug("Details:", ex); 15.357 - configureEditForm(req, dao, new SessionSelection(req, project)); 15.358 + final var selection = new SessionSelection(req, dao); 15.359 + selection.project = project; 15.360 + final var vm = configureEditForm(req, dao, selection); 15.361 + vm.setErrorText(ex.getMessage()); // TODO: error text 15.362 } 15.363 15.364 return ResponseType.HTML; 15.365 @@ -283,127 +358,143 @@ 15.366 15.367 @RequestMapping(requestPath = "view", method = HttpMethod.GET) 15.368 public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { 15.369 - final var sessionSelection = new SessionSelection(req, dao); 15.370 - if (sessionSelection.project == null) { 15.371 + final var selection = new SessionSelection(req, dao); 15.372 + selection.sync(); 15.373 + 15.374 + if (selection.project == null) { 15.375 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); 15.376 return ResponseType.NONE; 15.377 } 15.378 15.379 final var versionDao = dao.getVersionDao(); 15.380 - final var versions = versionDao.list(sessionSelection.project); 15.381 - final var statsAffected = new ArrayList<VersionStatistics>(); 15.382 - final var statsScheduled = new ArrayList<VersionStatistics>(); 15.383 - final var statsResolved = new ArrayList<VersionStatistics>(); 15.384 - for (Version version : versions) { 15.385 - statsAffected.add(versionDao.statsOpenedIssues(version)); 15.386 - statsScheduled.add(versionDao.statsScheduledIssues(version)); 15.387 - statsResolved.add(versionDao.statsResolvedIssues(version)); 15.388 - } 15.389 + final var issueDao = dao.getIssueDao(); 15.390 15.391 - setAttributeHideZeros(req); 15.392 + final var viewModel = new ProjectView(selection.project); 15.393 + final var issues = issueDao.list(selection.project); 15.394 + for (var issue : issues) issueDao.joinVersionInformation(issue); 15.395 + viewModel.setIssues(issues); 15.396 + viewModel.setVersions(versionDao.list(selection.project)); 15.397 + viewModel.updateVersionInfo(); 15.398 + setViewModel(req, viewModel); 15.399 15.400 - req.setAttribute("project", sessionSelection.project); 15.401 - req.setAttribute("versions", versions); 15.402 - req.setAttribute("statsAffected", statsAffected); 15.403 - req.setAttribute("statsScheduled", statsScheduled); 15.404 - req.setAttribute("statsResolved", statsResolved); 15.405 - 15.406 - req.setAttribute("issueStatusEnum", IssueStatus.values()); 15.407 - req.setAttribute("issueCategoryEnum", IssueCategory.values()); 15.408 - 15.409 - setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, sessionSelection)); 15.410 + setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, selection)); 15.411 setContentPage(req, "project-details"); 15.412 setStylesheet(req, "projects"); 15.413 15.414 return ResponseType.HTML; 15.415 } 15.416 15.417 - private void configureEditVersionForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 15.418 - final var versionDao = dao.getVersionDao(); 15.419 - req.setAttribute("projects", dao.getProjectDao().list()); 15.420 - req.setAttribute("version", selection.version); 15.421 - req.setAttribute("versionStatusEnum", VersionStatus.values()); 15.422 + @RequestMapping(requestPath = "versions/view", method = HttpMethod.GET) 15.423 + public ResponseType viewVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { 15.424 + final var selection = new SessionSelection(req, dao); 15.425 + selection.sync(); 15.426 + if (selection.version == null) { 15.427 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 15.428 + return ResponseType.NONE; 15.429 + } 15.430 15.431 - req.setAttribute("issueStatusEnum", IssueStatus.values()); 15.432 - req.setAttribute("issueCategoryEnum", IssueCategory.values()); 15.433 - req.setAttribute("statsAffected", versionDao.statsOpenedIssues(selection.version)); 15.434 - req.setAttribute("statsScheduled", versionDao.statsScheduledIssues(selection.version)); 15.435 - req.setAttribute("statsResolved", versionDao.statsResolvedIssues(selection.version)); 15.436 - setAttributeHideZeros(req); 15.437 + final var issueDao = dao.getIssueDao(); 15.438 + final var viewModel = new VersionView(selection.version); 15.439 + final var issues = issueDao.list(selection.version); 15.440 + for (var issue : issues) issueDao.joinVersionInformation(issue); 15.441 + viewModel.setIssues(issues); 15.442 + setViewModel(req, viewModel); 15.443 15.444 + setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_VERSION, selection)); 15.445 + setContentPage(req, "version"); 15.446 + setStylesheet(req, "projects"); 15.447 + 15.448 + return ResponseType.HTML; 15.449 + } 15.450 + 15.451 + private VersionEditView configureEditVersionForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 15.452 + final var viewModel = new VersionEditView(selection.version); 15.453 + if (selection.version.getProject() == null) { 15.454 + viewModel.setProjects(dao.getProjectDao().list()); 15.455 + } 15.456 + setViewModel(req, viewModel); 15.457 setContentPage(req, "version-form"); 15.458 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_VERSION, selection)); 15.459 + return viewModel; 15.460 } 15.461 15.462 @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET) 15.463 - public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 15.464 - final var sessionSelection = new SessionSelection(req, dao); 15.465 + public ResponseType editVersion(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 15.466 + final var selection = new SessionSelection(req, dao); 15.467 + if (getParameter(req, Integer.class, "vid").isEmpty()) { 15.468 + selection.newVersion(); 15.469 + } else { 15.470 + selection.sync(); 15.471 + } 15.472 15.473 - sessionSelection.selectVersion(findByParameter(req, Integer.class, "id", dao.getVersionDao()::find) 15.474 - .orElse(new Version(-1, sessionSelection.project))); 15.475 - configureEditVersionForm(req, dao, sessionSelection); 15.476 + configureEditVersionForm(req, dao, selection); 15.477 15.478 return ResponseType.HTML; 15.479 } 15.480 15.481 @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST) 15.482 public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 15.483 - final var sessionSelection = new SessionSelection(req, dao); 15.484 15.485 - var version = new Version(-1, sessionSelection.project); 15.486 + var version = new Version(-1); 15.487 try { 15.488 - version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project); 15.489 + version = new Version(getParameter(req, Integer.class, "id").orElseThrow()); 15.490 + version.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow())); 15.491 version.setName(getParameter(req, String.class, "name").orElseThrow()); 15.492 getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal); 15.493 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); 15.494 dao.getVersionDao().saveOrUpdate(version); 15.495 15.496 // specifying the pid parameter will purposely reset the session selected version! 15.497 - setRedirectLocation(req, "./projects/view?pid="+sessionSelection.project.getId()); 15.498 + setRedirectLocation(req, "./projects/view?pid="+version.getProject().getId()); 15.499 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 15.500 - LOG.debug("Successfully updated version {} for project {}", version.getName(), sessionSelection.project.getName()); 15.501 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 15.502 - // TODO: set request attribute with error text 15.503 LOG.warn("Form validation failure: {}", ex.getMessage()); 15.504 LOG.debug("Details:", ex); 15.505 - sessionSelection.selectVersion(version); 15.506 - configureEditVersionForm(req, dao, sessionSelection); 15.507 + final var selection = new SessionSelection(req, dao); 15.508 + selection.selectVersion(version); 15.509 + final var viewModel = configureEditVersionForm(req, dao, selection); 15.510 + // TODO: set Error Text 15.511 } 15.512 15.513 return ResponseType.HTML; 15.514 } 15.515 15.516 - private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 15.517 + private IssueEditView configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { 15.518 + final var viewModel = new IssueEditView(selection.issue); 15.519 15.520 - if (selection.issue.getProject() == null || selection.issue.getProject().getId() < 0) { 15.521 - req.setAttribute("projects", dao.getProjectDao().list()); 15.522 - req.setAttribute("versions", Collections.<Version>emptyList()); 15.523 + if (selection.issue.getProject() == null) { 15.524 + viewModel.setProjects(dao.getProjectDao().list()); 15.525 } else { 15.526 - req.setAttribute("projects", Collections.<Project>emptyList()); 15.527 - req.setAttribute("versions", dao.getVersionDao().list(selection.issue.getProject())); 15.528 + viewModel.setVersions(dao.getVersionDao().list(selection.issue.getProject())); 15.529 } 15.530 - 15.531 - dao.getIssueDao().joinVersionInformation(selection.issue); 15.532 - req.setAttribute("issue", selection.issue); 15.533 - req.setAttribute("issueStatusEnum", IssueStatus.values()); 15.534 - req.setAttribute("issueCategoryEnum", IssueCategory.values()); 15.535 - req.setAttribute("users", dao.getUserDao().list()); 15.536 + viewModel.setUsers(dao.getUserDao().list()); 15.537 + setViewModel(req, viewModel); 15.538 15.539 setContentPage(req, "issue-form"); 15.540 setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE, selection)); 15.541 + return viewModel; 15.542 } 15.543 15.544 @RequestMapping(requestPath = "issues/", method = HttpMethod.GET) 15.545 public ResponseType issues(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { 15.546 - final var sessionSelection = new SessionSelection(req, dao); 15.547 - if (sessionSelection.project == null) { 15.548 + final var selection = new SessionSelection(req, dao); 15.549 + selection.sync(); 15.550 + if (selection.project == null) { 15.551 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); 15.552 return ResponseType.NONE; 15.553 } 15.554 15.555 - req.setAttribute("issues", dao.getIssueDao().list(sessionSelection.project)); 15.556 + final var viewModel = new IssuesView(); 15.557 + viewModel.setProject(selection.project); 15.558 + if (selection.version == null) { 15.559 + viewModel.setIssues(dao.getIssueDao().list(selection.project)); 15.560 + } else { 15.561 + viewModel.setVersion(selection.version); 15.562 + viewModel.setIssues(dao.getIssueDao().list(selection.version)); 15.563 + } 15.564 + setViewModel(req, viewModel); 15.565 15.566 - setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE_LIST, sessionSelection)); 15.567 + setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE_LIST, selection)); 15.568 setContentPage(req, "issues"); 15.569 setStylesheet(req, "projects"); 15.570 15.571 @@ -412,22 +503,25 @@ 15.572 15.573 @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET) 15.574 public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 15.575 - final var sessionSelection = new SessionSelection(req, dao); 15.576 + final var selection = new SessionSelection(req, dao); 15.577 + if (getParameter(req, Integer.class, "issue").isEmpty()) { 15.578 + selection.newIssue(); 15.579 + } else { 15.580 + selection.sync(); 15.581 + } 15.582 15.583 - sessionSelection.selectIssue(findByParameter(req, Integer.class, "id", 15.584 - dao.getIssueDao()::find).orElse(new Issue(-1, sessionSelection.project))); 15.585 - configureEditIssueForm(req, dao, sessionSelection); 15.586 + configureEditIssueForm(req, dao, selection); 15.587 15.588 return ResponseType.HTML; 15.589 } 15.590 15.591 @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST) 15.592 public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 15.593 - final var sessionSelection = new SessionSelection(req, dao); 15.594 15.595 - Issue issue = new Issue(-1, sessionSelection.project); 15.596 + Issue issue = new Issue(-1); 15.597 try { 15.598 - issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project); 15.599 + issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow()); 15.600 + issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow())); 15.601 getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory); 15.602 getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus); 15.603 issue.setSubject(getParameter(req, String.class, "subject").orElseThrow()); 15.604 @@ -440,17 +534,17 @@ 15.605 getParameter(req, Integer[].class, "affected") 15.606 .map(Stream::of) 15.607 .map(stream -> 15.608 - stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) 15.609 + stream.map(Version::new).collect(Collectors.toList()) 15.610 ).ifPresent(issue::setAffectedVersions); 15.611 getParameter(req, Integer[].class, "scheduled") 15.612 .map(Stream::of) 15.613 .map(stream -> 15.614 - stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) 15.615 + stream.map(Version::new).collect(Collectors.toList()) 15.616 ).ifPresent(issue::setScheduledVersions); 15.617 getParameter(req, Integer[].class, "resolved") 15.618 .map(Stream::of) 15.619 .map(stream -> 15.620 - stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) 15.621 + stream.map(Version::new).collect(Collectors.toList()) 15.622 ).ifPresent(issue::setResolvedVersions); 15.623 15.624 dao.getIssueDao().saveOrUpdate(issue); 15.625 @@ -458,13 +552,14 @@ 15.626 // specifying the issue parameter keeps the edited issue as breadcrumb 15.627 setRedirectLocation(req, "./projects/issues/?issue="+issue.getId()); 15.628 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 15.629 - LOG.debug("Successfully updated issue {} for project {}", issue.getId(), sessionSelection.project.getName()); 15.630 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 15.631 // TODO: set request attribute with error text 15.632 LOG.warn("Form validation failure: {}", ex.getMessage()); 15.633 LOG.debug("Details:", ex); 15.634 - sessionSelection.selectIssue(issue); 15.635 - configureEditIssueForm(req, dao, sessionSelection); 15.636 + final var selection = new SessionSelection(req, dao); 15.637 + selection.selectIssue(issue); 15.638 + final var viewModel = configureEditIssueForm(req, dao, selection); 15.639 + // TODO: set Error Text 15.640 } 15.641 15.642 return ResponseType.HTML;
16.1 --- a/src/main/java/de/uapcore/lightpit/modules/UsersModule.java Sat May 30 18:12:38 2020 +0200 16.2 +++ b/src/main/java/de/uapcore/lightpit/modules/UsersModule.java Mon Jun 01 14:46:58 2020 +0200 16.3 @@ -32,6 +32,8 @@ 16.4 import de.uapcore.lightpit.*; 16.5 import de.uapcore.lightpit.dao.DataAccessObjects; 16.6 import de.uapcore.lightpit.entities.User; 16.7 +import de.uapcore.lightpit.viewmodel.UsersEditView; 16.8 +import de.uapcore.lightpit.viewmodel.UsersView; 16.9 import org.slf4j.Logger; 16.10 import org.slf4j.LoggerFactory; 16.11 16.12 @@ -57,7 +59,9 @@ 16.13 public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 16.14 final var userDao = dao.getUserDao(); 16.15 16.16 - req.setAttribute("users", userDao.list()); 16.17 + final var viewModel = new UsersView(); 16.18 + viewModel.setUsers(userDao.list()); 16.19 + setViewModel(req, viewModel); 16.20 setContentPage(req, "users"); 16.21 16.22 return ResponseType.HTML; 16.23 @@ -66,9 +70,11 @@ 16.24 @RequestMapping(requestPath = "edit", method = HttpMethod.GET) 16.25 public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 16.26 16.27 - req.setAttribute("user", findByParameter(req, Integer.class, "id", 16.28 + final var viewModel = new UsersEditView(); 16.29 + viewModel.setUser(findByParameter(req, Integer.class, "id", 16.30 dao.getUserDao()::find).orElse(new User(-1))); 16.31 16.32 + setViewModel(req, viewModel); 16.33 setContentPage(req, "user-form"); 16.34 16.35 return ResponseType.HTML; 16.36 @@ -92,8 +98,10 @@ 16.37 16.38 LOG.debug("Successfully updated user {}", user.getUsername()); 16.39 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 16.40 - // TODO: set request attribute with error text 16.41 - req.setAttribute("user", user); 16.42 + final var viewModel = new UsersEditView(); 16.43 + viewModel.setUser(user); 16.44 + // TODO: viewModel.setErrorText() 16.45 + setViewModel(req, viewModel); 16.46 setContentPage(req, "user-form"); 16.47 LOG.warn("Form validation failure: {}", ex.getMessage()); 16.48 LOG.debug("Details:", ex);
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 17.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java Mon Jun 01 14:46:58 2020 +0200 17.3 @@ -0,0 +1,54 @@ 17.4 +package de.uapcore.lightpit.viewmodel; 17.5 + 17.6 +import de.uapcore.lightpit.entities.*; 17.7 + 17.8 +import java.util.Collections; 17.9 +import java.util.List; 17.10 + 17.11 +public class IssueEditView { 17.12 + private final Issue issue; 17.13 + 17.14 + private List<Project> projects = Collections.emptyList(); 17.15 + private List<Version> versions = Collections.emptyList(); 17.16 + private List<User> users; 17.17 + 17.18 + public IssueEditView(Issue issue) { 17.19 + this.issue = issue; 17.20 + } 17.21 + 17.22 + public Issue getIssue() { 17.23 + return issue; 17.24 + } 17.25 + 17.26 + public List<Project> getProjects() { 17.27 + return projects; 17.28 + } 17.29 + 17.30 + public void setProjects(List<Project> projects) { 17.31 + this.projects = projects; 17.32 + } 17.33 + 17.34 + public List<Version> getVersions() { 17.35 + return versions; 17.36 + } 17.37 + 17.38 + public void setVersions(List<Version> versions) { 17.39 + this.versions = versions; 17.40 + } 17.41 + 17.42 + public List<User> getUsers() { 17.43 + return users; 17.44 + } 17.45 + 17.46 + public void setUsers(List<User> users) { 17.47 + this.users = users; 17.48 + } 17.49 + 17.50 + public IssueStatus[] getIssueStatus() { 17.51 + return IssueStatus.values(); 17.52 + } 17.53 + 17.54 + public IssueCategory[] getIssueCategory() { 17.55 + return IssueCategory.values(); 17.56 + } 17.57 +}
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 18.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/IssuesView.java Mon Jun 01 14:46:58 2020 +0200 18.3 @@ -0,0 +1,37 @@ 18.4 +package de.uapcore.lightpit.viewmodel; 18.5 + 18.6 +import de.uapcore.lightpit.entities.Issue; 18.7 +import de.uapcore.lightpit.entities.Project; 18.8 +import de.uapcore.lightpit.entities.Version; 18.9 + 18.10 +import java.util.List; 18.11 + 18.12 +public class IssuesView { 18.13 + private List<Issue> issues; 18.14 + private Project project; 18.15 + private Version version; 18.16 + 18.17 + public List<Issue> getIssues() { 18.18 + return issues; 18.19 + } 18.20 + 18.21 + public void setIssues(List<Issue> issues) { 18.22 + this.issues = issues; 18.23 + } 18.24 + 18.25 + public Version getVersion() { 18.26 + return version; 18.27 + } 18.28 + 18.29 + public void setVersion(Version version) { 18.30 + this.version = version; 18.31 + } 18.32 + 18.33 + public Project getProject() { 18.34 + return project; 18.35 + } 18.36 + 18.37 + public void setProject(Project project) { 18.38 + this.project = project; 18.39 + } 18.40 +}
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 19.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/LanguageView.java Mon Jun 01 14:46:58 2020 +0200 19.3 @@ -0,0 +1,36 @@ 19.4 +package de.uapcore.lightpit.viewmodel; 19.5 + 19.6 +import java.util.List; 19.7 +import java.util.Locale; 19.8 + 19.9 +public class LanguageView { 19.10 + 19.11 + private List<Locale> languages; 19.12 + private Locale browserLanguage; 19.13 + private Locale currentLanguage; 19.14 + 19.15 + 19.16 + public List<Locale> getLanguages() { 19.17 + return languages; 19.18 + } 19.19 + 19.20 + public void setLanguages(List<Locale> languages) { 19.21 + this.languages = languages; 19.22 + } 19.23 + 19.24 + public Locale getBrowserLanguage() { 19.25 + return browserLanguage; 19.26 + } 19.27 + 19.28 + public void setBrowserLanguage(Locale browserLanguage) { 19.29 + this.browserLanguage = browserLanguage; 19.30 + } 19.31 + 19.32 + public Locale getCurrentLanguage() { 19.33 + return currentLanguage; 19.34 + } 19.35 + 19.36 + public void setCurrentLanguage(Locale currentLanguage) { 19.37 + this.currentLanguage = currentLanguage; 19.38 + } 19.39 +}
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 20.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectEditView.java Mon Jun 01 14:46:58 2020 +0200 20.3 @@ -0,0 +1,37 @@ 20.4 +package de.uapcore.lightpit.viewmodel; 20.5 + 20.6 +import de.uapcore.lightpit.entities.Project; 20.7 +import de.uapcore.lightpit.entities.User; 20.8 + 20.9 +import java.util.List; 20.10 + 20.11 +public class ProjectEditView { 20.12 + 20.13 + private Project project; 20.14 + private List<User> users; 20.15 + private String errorText; 20.16 + 20.17 + public Project getProject() { 20.18 + return project; 20.19 + } 20.20 + 20.21 + public void setProject(Project project) { 20.22 + this.project = project; 20.23 + } 20.24 + 20.25 + public List<User> getUsers() { 20.26 + return users; 20.27 + } 20.28 + 20.29 + public void setUsers(List<User> users) { 20.30 + this.users = users; 20.31 + } 20.32 + 20.33 + public String getErrorText() { 20.34 + return errorText; 20.35 + } 20.36 + 20.37 + public void setErrorText(String errorText) { 20.38 + this.errorText = errorText; 20.39 + } 20.40 +}
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 21.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectIndexView.java Mon Jun 01 14:46:58 2020 +0200 21.3 @@ -0,0 +1,17 @@ 21.4 +package de.uapcore.lightpit.viewmodel; 21.5 + 21.6 +import java.util.ArrayList; 21.7 +import java.util.List; 21.8 + 21.9 +public class ProjectIndexView { 21.10 + 21.11 + private List<ProjectInfo> projects = new ArrayList<>(); 21.12 + 21.13 + public List<ProjectInfo> getProjects() { 21.14 + return projects; 21.15 + } 21.16 + 21.17 + public void setProjects(List<ProjectInfo> projects) { 21.18 + this.projects = projects; 21.19 + } 21.20 +}
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 22.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectInfo.java Mon Jun 01 14:46:58 2020 +0200 22.3 @@ -0,0 +1,58 @@ 22.4 +package de.uapcore.lightpit.viewmodel; 22.5 + 22.6 +import de.uapcore.lightpit.entities.IssueSummary; 22.7 +import de.uapcore.lightpit.entities.Project; 22.8 +import de.uapcore.lightpit.entities.Version; 22.9 +import de.uapcore.lightpit.entities.VersionStatus; 22.10 + 22.11 +import java.util.Collections; 22.12 +import java.util.List; 22.13 + 22.14 +public class ProjectInfo { 22.15 + 22.16 + private final Project project; 22.17 + private List<Version> versions = Collections.emptyList(); 22.18 + private IssueSummary issueSummary = new IssueSummary(); 22.19 + 22.20 + public ProjectInfo(Project project) { 22.21 + this.project = project; 22.22 + } 22.23 + 22.24 + public Project getProject() { 22.25 + return project; 22.26 + } 22.27 + 22.28 + public List<Version> getVersions() { 22.29 + return versions; 22.30 + } 22.31 + 22.32 + public void setVersions(List<Version> versions) { 22.33 + this.versions = versions; 22.34 + } 22.35 + 22.36 + public Version getLatestVersion() { 22.37 + for (var v : versions) { 22.38 + if (v.getStatus().ordinal() >= VersionStatus.Released.ordinal()) 22.39 + return v; 22.40 + } 22.41 + return null; 22.42 + } 22.43 + 22.44 + public Version getNextVersion() { 22.45 + Version next = null; 22.46 + for (var v : versions) { 22.47 + if (v.getStatus().ordinal() >= VersionStatus.Released.ordinal()) 22.48 + break; 22.49 + next = v; 22.50 + } 22.51 + return next; 22.52 + } 22.53 + 22.54 + public IssueSummary getIssueSummary() { 22.55 + return issueSummary; 22.56 + } 22.57 + 22.58 + public void setIssueSummary(IssueSummary issueSummary) { 22.59 + this.issueSummary = issueSummary; 22.60 + } 22.61 +}
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 23.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectView.java Mon Jun 01 14:46:58 2020 +0200 23.3 @@ -0,0 +1,81 @@ 23.4 +package de.uapcore.lightpit.viewmodel; 23.5 + 23.6 +import de.uapcore.lightpit.entities.Issue; 23.7 +import de.uapcore.lightpit.entities.IssueSummary; 23.8 +import de.uapcore.lightpit.entities.Project; 23.9 +import de.uapcore.lightpit.entities.Version; 23.10 + 23.11 +import java.util.ArrayList; 23.12 +import java.util.Collections; 23.13 +import java.util.List; 23.14 + 23.15 +public class ProjectView { 23.16 + 23.17 + private final Project project; 23.18 + private List<Version> versions = Collections.emptyList(); 23.19 + private List<Issue> issues = Collections.emptyList(); 23.20 + 23.21 + private IssueSummary issuesTotal; 23.22 + private List<Issue> issuesWithoutVersion; 23.23 + private IssueSummary issuesWithoutVersionTotal; 23.24 + private List<VersionInfo> versionInfos = Collections.emptyList(); 23.25 + 23.26 + public ProjectView(Project project) { 23.27 + this.project = project; 23.28 + } 23.29 + 23.30 + public Project getProject() { 23.31 + return project; 23.32 + } 23.33 + 23.34 + public List<Issue> getIssues() { 23.35 + return issues; 23.36 + } 23.37 + 23.38 + public void setIssues(List<Issue> issues) { 23.39 + this.issues = issues; 23.40 + issuesTotal = new IssueSummary(); 23.41 + issuesWithoutVersion = new ArrayList<>(); 23.42 + issuesWithoutVersionTotal = new IssueSummary(); 23.43 + for (Issue issue : issues) { 23.44 + issuesTotal.add(issue); 23.45 + if (issue.getResolvedVersions().isEmpty() && issue.getScheduledVersions().isEmpty() && issue.getResolvedVersions().isEmpty()) { 23.46 + issuesWithoutVersion.add(issue); 23.47 + issuesWithoutVersionTotal.add(issue); 23.48 + } 23.49 + } 23.50 + } 23.51 + 23.52 + public List<Version> getVersions() { 23.53 + return versions; 23.54 + } 23.55 + 23.56 + public void setVersions(List<Version> versions) { 23.57 + this.versions = versions; 23.58 + } 23.59 + 23.60 + public void updateVersionInfo() { 23.61 + versionInfos = new ArrayList<>(); 23.62 + for (Version version : versions) { 23.63 + final var info = new VersionInfo(version); 23.64 + info.collectIssues(issues); 23.65 + versionInfos.add(info); 23.66 + } 23.67 + } 23.68 + 23.69 + public IssueSummary getIssuesTotal() { 23.70 + return issuesTotal; 23.71 + } 23.72 + 23.73 + public List<Issue> getIssuesWithoutVersion() { 23.74 + return issuesWithoutVersion; 23.75 + } 23.76 + 23.77 + public IssueSummary getIssuesWithoutVersionTotal() { 23.78 + return issuesWithoutVersionTotal; 23.79 + } 23.80 + 23.81 + public List<VersionInfo> getVersionInfos() { 23.82 + return versionInfos; 23.83 + } 23.84 +}
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 24.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/UsersEditView.java Mon Jun 01 14:46:58 2020 +0200 24.3 @@ -0,0 +1,24 @@ 24.4 +package de.uapcore.lightpit.viewmodel; 24.5 + 24.6 +import de.uapcore.lightpit.entities.User; 24.7 + 24.8 +public class UsersEditView { 24.9 + private User user; 24.10 + private String errorText; 24.11 + 24.12 + public User getUser() { 24.13 + return user; 24.14 + } 24.15 + 24.16 + public void setUser(User user) { 24.17 + this.user = user; 24.18 + } 24.19 + 24.20 + public String getErrorText() { 24.21 + return errorText; 24.22 + } 24.23 + 24.24 + public void setErrorText(String errorText) { 24.25 + this.errorText = errorText; 24.26 + } 24.27 +}
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 25.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/UsersView.java Mon Jun 01 14:46:58 2020 +0200 25.3 @@ -0,0 +1,17 @@ 25.4 +package de.uapcore.lightpit.viewmodel; 25.5 + 25.6 +import de.uapcore.lightpit.entities.User; 25.7 + 25.8 +import java.util.List; 25.9 + 25.10 +public class UsersView { 25.11 + private List<User> users; 25.12 + 25.13 + public List<User> getUsers() { 25.14 + return users; 25.15 + } 25.16 + 25.17 + public void setUsers(List<User> users) { 25.18 + this.users = users; 25.19 + } 25.20 +}
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 26.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionEditView.java Mon Jun 01 14:46:58 2020 +0200 26.3 @@ -0,0 +1,42 @@ 26.4 +package de.uapcore.lightpit.viewmodel; 26.5 + 26.6 +import de.uapcore.lightpit.entities.Project; 26.7 +import de.uapcore.lightpit.entities.Version; 26.8 +import de.uapcore.lightpit.entities.VersionStatus; 26.9 + 26.10 +import java.util.Collections; 26.11 +import java.util.List; 26.12 + 26.13 +public class VersionEditView { 26.14 + private final Version version; 26.15 + private List<Project> projects = Collections.emptyList(); 26.16 + private String errorText; 26.17 + 26.18 + public VersionEditView(Version version) { 26.19 + this.version = version; 26.20 + } 26.21 + 26.22 + public Version getVersion() { 26.23 + return version; 26.24 + } 26.25 + 26.26 + public List<Project> getProjects() { 26.27 + return projects; 26.28 + } 26.29 + 26.30 + public void setProjects(List<Project> projects) { 26.31 + this.projects = projects; 26.32 + } 26.33 + 26.34 + public VersionStatus[] getVersionStatus() { 26.35 + return VersionStatus.values(); 26.36 + } 26.37 + 26.38 + public String getErrorText() { 26.39 + return errorText; 26.40 + } 26.41 + 26.42 + public void setErrorText(String errorText) { 26.43 + this.errorText = errorText; 26.44 + } 26.45 +}
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 27.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionInfo.java Mon Jun 01 14:46:58 2020 +0200 27.3 @@ -0,0 +1,82 @@ 27.4 +package de.uapcore.lightpit.viewmodel; 27.5 + 27.6 +import de.uapcore.lightpit.entities.Issue; 27.7 +import de.uapcore.lightpit.entities.IssueSummary; 27.8 +import de.uapcore.lightpit.entities.Version; 27.9 + 27.10 +import java.util.ArrayList; 27.11 +import java.util.List; 27.12 + 27.13 +public class VersionInfo { 27.14 + 27.15 + private final Version version; 27.16 + 27.17 + private final IssueSummary reportedTotal = new IssueSummary(); 27.18 + private final IssueSummary scheduledTotal = new IssueSummary(); 27.19 + private final IssueSummary resolvedTotal = new IssueSummary(); 27.20 + 27.21 + private final List<Issue> reported = new ArrayList<>(); 27.22 + private final List<Issue> scheduled = new ArrayList<>(); 27.23 + private final List<Issue> resolved = new ArrayList<>(); 27.24 + 27.25 + public VersionInfo(Version version) { 27.26 + this.version = version; 27.27 + } 27.28 + 27.29 + public Version getVersion() { 27.30 + return version; 27.31 + } 27.32 + 27.33 + public void addReported(Issue issue) { 27.34 + reportedTotal.add(issue); 27.35 + reported.add(issue); 27.36 + } 27.37 + 27.38 + public void addScheduled(Issue issue) { 27.39 + scheduledTotal.add(issue); 27.40 + scheduled.add(issue); 27.41 + } 27.42 + 27.43 + public void addResolved(Issue issue) { 27.44 + resolvedTotal.add(issue); 27.45 + resolved.add(issue); 27.46 + } 27.47 + 27.48 + public IssueSummary getReportedTotal() { 27.49 + return reportedTotal; 27.50 + } 27.51 + 27.52 + public IssueSummary getScheduledTotal() { 27.53 + return scheduledTotal; 27.54 + } 27.55 + 27.56 + public IssueSummary getResolvedTotal() { 27.57 + return resolvedTotal; 27.58 + } 27.59 + 27.60 + public List<Issue> getReported() { 27.61 + return reported; 27.62 + } 27.63 + 27.64 + public List<Issue> getScheduled() { 27.65 + return scheduled; 27.66 + } 27.67 + 27.68 + public List<Issue> getResolved() { 27.69 + return resolved; 27.70 + } 27.71 + 27.72 + public void collectIssues(List<Issue> issues) { 27.73 + for (Issue issue : issues) { 27.74 + if (issue.getAffectedVersions().contains(version)) { 27.75 + addReported(issue); 27.76 + } 27.77 + if (issue.getScheduledVersions().contains(version)) { 27.78 + addScheduled(issue); 27.79 + } 27.80 + if (issue.getResolvedVersions().contains(version)) { 27.81 + addResolved(issue); 27.82 + } 27.83 + } 27.84 + } 27.85 +}
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 28.2 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionView.java Mon Jun 01 14:46:58 2020 +0200 28.3 @@ -0,0 +1,23 @@ 28.4 +package de.uapcore.lightpit.viewmodel; 28.5 + 28.6 +import de.uapcore.lightpit.entities.Issue; 28.7 +import de.uapcore.lightpit.entities.Version; 28.8 + 28.9 +import java.util.List; 28.10 + 28.11 +public class VersionView { 28.12 + 28.13 + private final VersionInfo versionInfo; 28.14 + 28.15 + public VersionView(Version version) { 28.16 + this.versionInfo = new VersionInfo(version); 28.17 + } 28.18 + 28.19 + public VersionInfo getVersionInfo() { 28.20 + return versionInfo; 28.21 + } 28.22 + 28.23 + public void setIssues(List<Issue> issues) { 28.24 + versionInfo.collectIssues(issues); 28.25 + } 28.26 +}
29.1 --- a/src/main/resources/localization/projects.properties Sat May 30 18:12:38 2020 +0200 29.2 +++ b/src/main/resources/localization/projects.properties Mon Jun 01 14:46:58 2020 +0200 29.3 @@ -26,7 +26,7 @@ 29.4 button.create=New Project 29.5 button.version.create=New Version 29.6 button.issue.create=New Issue 29.7 -button.issue.list=Show Issues 29.8 +button.issue.all=All Issues 29.9 29.10 button.stats.hidezeros=Reduced View 29.11 button.stats.showzeros=Full View 29.12 @@ -39,9 +39,18 @@ 29.13 description=Description 29.14 repoUrl=Repository 29.15 owner=Project Lead 29.16 +version.latest=Latest Version 29.17 +version.next=Next Version 29.18 + 29.19 +progress=Overall Progress 29.20 + 29.21 issues.open=Open 29.22 issues.active=In Progress 29.23 issues.done=Done 29.24 +issues.total=Total 29.25 +issues.reported=Reported Issues 29.26 +issues.scheduled=Scheduled Issues 29.27 +issues.resolved=Resolved Issues 29.28 29.29 version.project=Project 29.30 version.name=Version 29.31 @@ -59,11 +68,8 @@ 29.32 version.status.LTS=LTS 29.33 version.status.Deprecated=Deprecated 29.34 29.35 -version.statistics.affected=Affected by Issues 29.36 -version.statistics.scheduled=Scheduled Issues 29.37 -version.statistics.resolved=Resolved Issues 29.38 -version.statistics.total=Total 29.39 29.40 +issue.without-version=Issues w/o Assigned Version 29.41 issue.project=Project 29.42 issue.subject=Subject 29.43 issue.description=Description
30.1 --- a/src/main/resources/localization/projects_de.properties Sat May 30 18:12:38 2020 +0200 30.2 +++ b/src/main/resources/localization/projects_de.properties Mon Jun 01 14:46:58 2020 +0200 30.3 @@ -26,7 +26,7 @@ 30.4 button.create=Neues Projekt 30.5 button.version.create=Neue Version 30.6 button.issue.create=Neuer Vorgang 30.7 -button.issue.list=Vorg\u00e4nge 30.8 +button.issue.all=Alle Vorg\u00e4nge 30.9 30.10 button.stats.hidezeros=Reduzierte Ansicht 30.11 button.stats.showzeros=Komplettansicht 30.12 @@ -39,9 +39,18 @@ 30.13 description=Beschreibung 30.14 repoUrl=Repository 30.15 owner=Projektleitung 30.16 +version.latest=Neuste Version 30.17 +version.next=N\u00e4chste Version 30.18 + 30.19 +progress=Gesamtfortschritt 30.20 + 30.21 issues.open=Offen 30.22 issues.active=In Arbeit 30.23 issues.done=Erledigt 30.24 +issues.reported=Er\u00f6ffnete Vorg\u00e4nge 30.25 +issues.scheduled=Geplante Vorg\u00e4nge 30.26 +issues.resolved=Gel\u00f6ste Vorg\u00e4nge 30.27 +issues.total=Summe 30.28 30.29 version.project=Projekt 30.30 version.name=Version 30.31 @@ -59,11 +68,7 @@ 30.32 version.status.LTS=Langzeitsupport 30.33 version.status.Deprecated=Veraltet 30.34 30.35 -version.statistics.affected=Betroffen von Vorg\u00e4ngen 30.36 -version.statistics.scheduled=Geplante Vorg\u00e4nge 30.37 -version.statistics.resolved=Gel\u00f6ste Vorg\u00e4nge 30.38 -version.statistics.total=Summe 30.39 - 30.40 +issue.without-version=Vorg\u00e4nge ohne Version 30.41 issue.project=Projekt 30.42 issue.subject=Thema 30.43 issue.description=Beschreibung
31.1 --- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat May 30 18:12:38 2020 +0200 31.2 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Mon Jun 01 14:46:58 2020 +0200 31.3 @@ -28,12 +28,9 @@ 31.4 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 31.5 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 31.6 31.7 -<jsp:useBean id="projects" type="java.util.List<de.uapcore.lightpit.entities.Project>" scope="request" /> 31.8 -<jsp:useBean id="versions" type="java.util.List<de.uapcore.lightpit.entities.Version>" scope="request" /> 31.9 -<jsp:useBean id="issue" type="de.uapcore.lightpit.entities.Issue" scope="request"/> 31.10 -<jsp:useBean id="issueStatusEnum" type="de.uapcore.lightpit.entities.IssueStatus[]" scope="request"/> 31.11 -<jsp:useBean id="issueCategoryEnum" type="de.uapcore.lightpit.entities.IssueCategory[]" scope="request"/> 31.12 -<jsp:useBean id="users" type="java.util.List<de.uapcore.lightpit.entities.User>" scope="request"/> 31.13 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.IssueEditView" scope="request"/> 31.14 +<c:set var="issue" scope="page" value="${viewmodel.issue}" /> 31.15 +<c:set var="versions" value="${viewmodel.versions}" /> 31.16 31.17 <form action="./projects/issues/commit" method="post"> 31.18 <table class="formtable"> 31.19 @@ -45,26 +42,28 @@ 31.20 <tr> 31.21 <th><fmt:message key="issue.project"/></th> 31.22 <td> 31.23 - <c:if test="${issue.project.id ge 0}"> 31.24 - <c:out value="${issue.project.name}" /> 31.25 - <input type="hidden" name="pid" value="${issue.project.id}" /> 31.26 - </c:if> 31.27 - <c:if test="${empty issue.project or issue.project.id lt 0}"> 31.28 - <select name="pid" required> 31.29 - <c:forEach var="project" items="${projects}"> 31.30 - <option value="${project.id}"> 31.31 - <c:out value="${project.name}" /> 31.32 - </option> 31.33 - </c:forEach> 31.34 - </select> 31.35 - </c:if> 31.36 + <c:choose> 31.37 + <c:when test="${not empty issue.project}"> 31.38 + <c:out value="${issue.project.name}" /> 31.39 + <input type="hidden" name="pid" value="${issue.project.id}" /> 31.40 + </c:when> 31.41 + <c:otherwise> 31.42 + <select name="pid" required> 31.43 + <c:forEach var="project" items="${viewmodel.projects}"> 31.44 + <option value="${project.id}"> 31.45 + <c:out value="${project.name}" /> 31.46 + </option> 31.47 + </c:forEach> 31.48 + </select> 31.49 + </c:otherwise> 31.50 + </c:choose> 31.51 </td> 31.52 </tr> 31.53 <tr> 31.54 <th><fmt:message key="issue.category"/></th> 31.55 <td> 31.56 <select name="category"> 31.57 - <c:forEach var="category" items="${issueCategoryEnum}"> 31.58 + <c:forEach var="category" items="${viewmodel.issueCategory}"> 31.59 <option 31.60 <c:if test="${category eq issue.category}">selected</c:if> 31.61 value="${category}"> 31.62 @@ -78,7 +77,7 @@ 31.63 <th><fmt:message key="issue.status"/></th> 31.64 <td> 31.65 <select name="status"> 31.66 - <c:forEach var="status" items="${issueStatusEnum}"> 31.67 + <c:forEach var="status" items="${viewmodel.issueStatus}"> 31.68 <option 31.69 <c:if test="${status eq issue.status}">selected</c:if> 31.70 value="${status}"> 31.71 @@ -95,7 +94,7 @@ 31.72 <tr> 31.73 <th class="vtop"><fmt:message key="issue.description"/></th> 31.74 <td> 31.75 - <textarea name="description"><c:out value="${issue.description}"/></textarea> 31.76 + <textarea name="description" rows="10"><c:out value="${issue.description}"/></textarea> 31.77 </td> 31.78 </tr> 31.79 <tr> 31.80 @@ -103,7 +102,7 @@ 31.81 <td> 31.82 <select name="assignee"> 31.83 <option value="-1"><fmt:message key="placeholder.null-assignee"/></option> 31.84 - <c:forEach var="user" items="${users}"> 31.85 + <c:forEach var="user" items="${viewmodel.users}"> 31.86 <option 31.87 <c:if test="${not empty issue.assignee and user eq issue.assignee}">selected</c:if> 31.88 value="${user.id}"><c:out value="${user.displayname}"/></option> 31.89 @@ -155,7 +154,7 @@ 31.90 <td colspan="2"> 31.91 <input type="hidden" name="id" value="${issue.id}"/> 31.92 <c:choose> 31.93 - <c:when test="${not empty issue.project and issue.project.id ge 0}"> 31.94 + <c:when test="${not empty issue.project}"> 31.95 <c:set var="cancelUrl">./projects/issues/?pid=${issue.project.id}</c:set> 31.96 </c:when> 31.97 <c:otherwise>
32.1 --- a/src/main/webapp/WEB-INF/jsp/issues.jsp Sat May 30 18:12:38 2020 +0200 32.2 +++ b/src/main/webapp/WEB-INF/jsp/issues.jsp Mon Jun 01 14:46:58 2020 +0200 32.3 @@ -28,60 +28,26 @@ 32.4 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 32.5 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 32.6 32.7 -<jsp:useBean id="issues" type="java.util.List<de.uapcore.lightpit.entities.Issue>" scope="request"/> 32.8 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.IssuesView" scope="request"/> 32.9 +<c:set var="project" scope="page" value="${viewmodel.project}"/> 32.10 +<c:set var="version" scope="page" value="${viewmodel.version}"/> 32.11 +<%@include file="../jspf/project-header.jsp"%> 32.12 + 32.13 +<c:if test="${not empty version}"> 32.14 + <h2> 32.15 + <fmt:message key="version.label" /> <c:out value="${version.name}" /> - <fmt:message key="version.status.${version.status}"/> 32.16 + <a href="./projects/versions/edit?vid=${version.id}">✎</a> 32.17 + </h2> 32.18 +</c:if> 32.19 32.20 <div id="tool-area"> 32.21 <div> 32.22 <a href="./projects/issues/edit" class="button"><fmt:message key="button.issue.create"/></a> 32.23 + <c:if test="${not empty version}"> 32.24 + <a href="./projects/issues/?pid=${project.id}&vid=-1" class="button"><fmt:message key="button.issue.all"/></a> 32.25 + </c:if> 32.26 </div> 32.27 </div> 32.28 32.29 -<table id="issue-list" class="datatable medskip"> 32.30 - <thead> 32.31 - <tr> 32.32 - <th><fmt:message key="issue.subject"/></th> 32.33 - <th><fmt:message key="issue.assignee"/></th> 32.34 - <th><fmt:message key="issue.category"/></th> 32.35 - <th><fmt:message key="issue.status"/></th> 32.36 - <th><fmt:message key="issue.created"/></th> 32.37 - <th><fmt:message key="issue.updated"/></th> 32.38 - <th><fmt:message key="issue.eta"/></th> 32.39 - </tr> 32.40 - </thead> 32.41 - <tbody> 32.42 - <c:forEach var="issue" items="${issues}"> 32.43 - <tr> 32.44 - <td> 32.45 - <span class="phase-${issue.status.phase}"> 32.46 - <a href="./projects/issues/edit?id=${issue.id}"> 32.47 - <c:out value="${issue.subject}" /> 32.48 - </a> 32.49 - </span> 32.50 - </td> 32.51 - <td> 32.52 - <c:if test="${not empty issue.assignee}"> 32.53 - <c:out value="${issue.assignee.shortDisplayname}" /> 32.54 - </c:if> 32.55 - <c:if test="${empty issue.assignee}"> 32.56 - <fmt:message key="placeholder.null-assignee" /> 32.57 - </c:if> 32.58 - </td> 32.59 - <td> 32.60 - <fmt:message key="issue.category.${issue.category}" /> 32.61 - </td> 32.62 - <td> 32.63 - <fmt:message key="issue.status.${issue.status}" /> 32.64 - </td> 32.65 - <td> 32.66 - <fmt:formatDate value="${issue.created}" type="BOTH"/> 32.67 - </td> 32.68 - <td> 32.69 - <fmt:formatDate value="${issue.updated}" type="BOTH"/> 32.70 - </td> 32.71 - <td> 32.72 - <fmt:formatDate value="${issue.eta}" /> 32.73 - </td> 32.74 - </tr> 32.75 - </c:forEach> 32.76 - </tbody> 32.77 -</table> 32.78 +<c:set var="issues" value="${viewmodel.issues}"/> 32.79 +<%@include file="../jspf/issue-list.jsp"%> 32.80 \ No newline at end of file
33.1 --- a/src/main/webapp/WEB-INF/jsp/language.jsp Sat May 30 18:12:38 2020 +0200 33.2 +++ b/src/main/webapp/WEB-INF/jsp/language.jsp Mon Jun 01 14:46:58 2020 +0200 33.3 @@ -25,23 +25,19 @@ 33.4 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33.5 --%> 33.6 <%@page pageEncoding="UTF-8" %> 33.7 -<%@page import="de.uapcore.lightpit.Constants" %> 33.8 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 33.9 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 33.10 33.11 -<jsp:useBean id="languages" type="java.util.List<java.util.Locale>" scope="request"/> 33.12 -<jsp:useBean id="browserLanguage" type="java.util.Locale" scope="request"/> 33.13 - 33.14 -<c:set scope="page" var="currentLanguage" value="${sessionScope[Constants.SESSION_ATTR_LANGUAGE]}"/> 33.15 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.LanguageView" scope="request"/> 33.16 33.17 <form method="POST" id="lang-selector"> 33.18 - <c:forEach items="${languages}" var="l"> 33.19 + <c:forEach items="${viewmodel.languages}" var="l"> 33.20 <label> 33.21 <input type="radio" name="language" value="${l.language}" 33.22 - <c:if test="${l.language eq currentLanguage.language}">checked</c:if>/> 33.23 + <c:if test="${l.language eq viewmodel.currentLanguage.language}">checked</c:if>/> 33.24 ${l.displayLanguage} 33.25 - (${l.getDisplayLanguage(currentLanguage)} 33.26 - <c:if test="${not empty browserLanguage and l.language eq browserLanguage.language}"><c:set 33.27 + (${l.getDisplayLanguage(viewmodel.currentLanguage)} 33.28 + <c:if test="${not empty viewmodel.browserLanguage and l.language eq viewmodel.browserLanguage.language}"><c:set 33.29 var="browserLanguagePresent" value="true"/> - <fmt:message key="browserLanguage"/></c:if>) 33.30 </label> 33.31 </c:forEach>
34.1 --- a/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat May 30 18:12:38 2020 +0200 34.2 +++ b/src/main/webapp/WEB-INF/jsp/project-details.jsp Mon Jun 01 14:46:58 2020 +0200 34.3 @@ -28,66 +28,46 @@ 34.4 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 34.5 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 34.6 34.7 -<jsp:useBean id="project" type="de.uapcore.lightpit.entities.Project" scope="request" /> 34.8 -<jsp:useBean id="versions" type="java.util.List<de.uapcore.lightpit.entities.Version>" scope="request"/> 34.9 -<jsp:useBean id="statsAffected" type="java.util.List<de.uapcore.lightpit.entities.VersionStatistics>" scope="request"/> 34.10 -<jsp:useBean id="statsScheduled" type="java.util.List<de.uapcore.lightpit.entities.VersionStatistics>" scope="request"/> 34.11 -<jsp:useBean id="statsResolved" type="java.util.List<de.uapcore.lightpit.entities.VersionStatistics>" scope="request"/> 34.12 -<jsp:useBean id="issueStatusEnum" type="de.uapcore.lightpit.entities.IssueStatus[]" scope="request"/> 34.13 -<jsp:useBean id="issueCategoryEnum" type="de.uapcore.lightpit.entities.IssueCategory[]" scope="request"/> 34.14 -<jsp:useBean id="statsHideZeros" type="java.lang.Boolean" scope="request"/> 34.15 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.ProjectView" scope="request" /> 34.16 34.17 -<div id="project-attributes"> 34.18 - <div class="row"> 34.19 - <div class="caption"><fmt:message key="name"/>:</div> 34.20 - <div><c:out value="${project.name}"/></div> 34.21 - <div class="caption"><fmt:message key="description"/>:</div> 34.22 - <div><c:out value="${project.description}"/></div> 34.23 - </div> 34.24 - <div class="row"> 34.25 - <div class="caption"><fmt:message key="owner"/>:</div> 34.26 - <div> 34.27 - <c:if test="${not empty project.owner}"><c:out value="${project.owner.displayname}"/></c:if> 34.28 - </div> 34.29 - <div class="caption"><fmt:message key="repoUrl"/>:</div> 34.30 - <div> 34.31 - <c:if test="${not empty project.repoUrl}"> 34.32 - <a target="_blank" href="<c:out value="${project.repoUrl}"/>"><c:out 34.33 - value="${project.repoUrl}"/></a> 34.34 - </c:if> 34.35 - </div> 34.36 - </div> 34.37 -</div> 34.38 +<c:set var="project" scope="page" value="${viewmodel.project}"/> 34.39 +<%@include file="../jspf/project-header.jsp"%> 34.40 34.41 <div id="tool-area"> 34.42 <a href="./projects/versions/edit" class="button"><fmt:message key="button.version.create"/></a> 34.43 - <a href="./projects/issues/edit" class="button"><fmt:message key="button.issue.create"/></a> 34.44 - <a href="./projects/issues/" class="button"><fmt:message key="button.issue.list"/></a> 34.45 - <c:if test="${not statsHideZeros}"> 34.46 - <a href="./projects/view?reduced=1" class="button"><fmt:message key="button.stats.hidezeros"/></a> 34.47 - </c:if> 34.48 - <c:if test="${statsHideZeros}"> 34.49 - <a href="./projects/view?reduced=0" class="button"><fmt:message key="button.stats.showzeros"/></a> 34.50 - </c:if> 34.51 + <a href="./projects/issues/edit?pid=${project.id}" class="button"><fmt:message key="button.issue.create"/></a> 34.52 </div> 34.53 34.54 -<div id="version-stats"> 34.55 -<c:forEach var="version" items="${versions}" varStatus="iter"> 34.56 +<h2><fmt:message key="progress" /></h2> 34.57 + 34.58 +<c:set var="summary" value="${viewmodel.issuesTotal}" /> 34.59 +<%@include file="../jspf/issue-summary.jsp"%> 34.60 + 34.61 +<h2><fmt:message key="issue.without-version" /></h2> 34.62 + 34.63 +<c:set var="issues" value="${viewmodel.issuesWithoutVersion}"/> 34.64 +<c:set var="summary" value="${viewmodel.issuesWithoutVersionTotal}" /> 34.65 +<%@include file="../jspf/issue-summary.jsp"%> 34.66 +<%@include file="../jspf/issue-list.jsp"%> 34.67 + 34.68 +<c:forEach var="versionInfo" items="${viewmodel.versionInfos}"> 34.69 <h2> 34.70 - <fmt:message key="version.label" /> <c:out value="${version.name}" /> - <fmt:message key="version.status.${version.status}"/> 34.71 - <a href="./projects/versions/edit?id=${version.id}">✎</a> 34.72 + <fmt:message key="version.label" /> <c:out value="${versionInfo.version.name}" /> - <fmt:message key="version.status.${versionInfo.version.status}"/> 34.73 + (<a href="./projects/versions/view?vid=${versionInfo.version.id}">open</a>) 34.74 </h2> 34.75 34.76 - <h3><fmt:message key="version.statistics.affected" /></h3> 34.77 - <c:set var="stats" value="${statsAffected[iter.index]}" /> 34.78 - <%@include file="../jspf/version-stats.jsp" %> 34.79 + <h3><fmt:message key="issues.reported"/> </h3> 34.80 + <c:set var="summary" value="${versionInfo.reportedTotal}"/> 34.81 + <c:set var="issues" value="${versionInfo.reported}"/> 34.82 + <%@include file="../jspf/issue-summary.jsp"%> 34.83 34.84 - <h3><fmt:message key="version.statistics.scheduled" /></h3> 34.85 - <c:set var="stats" value="${statsScheduled[iter.index]}" /> 34.86 - <%@include file="../jspf/version-stats.jsp" %> 34.87 + <h3><fmt:message key="issues.scheduled"/> </h3> 34.88 + <c:set var="summary" value="${versionInfo.scheduledTotal}"/> 34.89 + <c:set var="issues" value="${versionInfo.scheduled}"/> 34.90 + <%@include file="../jspf/issue-summary.jsp"%> 34.91 34.92 - <h3><fmt:message key="version.statistics.resolved" /></h3> 34.93 - <c:set var="stats" value="${statsResolved[iter.index]}" /> 34.94 - <%@include file="../jspf/version-stats.jsp" %> 34.95 -</c:forEach> 34.96 -</div> 34.97 + <h3><fmt:message key="issues.resolved"/> </h3> 34.98 + <c:set var="summary" value="${versionInfo.resolvedTotal}"/> 34.99 + <c:set var="issues" value="${versionInfo.resolved}"/> 34.100 + <%@include file="../jspf/issue-summary.jsp"%> 34.101 +</c:forEach> 34.102 \ No newline at end of file
35.1 --- a/src/main/webapp/WEB-INF/jsp/projects.jsp Sat May 30 18:12:38 2020 +0200 35.2 +++ b/src/main/webapp/WEB-INF/jsp/projects.jsp Mon Jun 01 14:46:58 2020 +0200 35.3 @@ -25,15 +25,12 @@ 35.4 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35.5 --%> 35.6 <%@page pageEncoding="UTF-8" %> 35.7 -<%@page import="de.uapcore.lightpit.modules.ProjectsModule" %> 35.8 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 35.9 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 35.10 35.11 -<c:set scope="page" var="selectedProject" value="${sessionScope[ProjectsModule.SESSION_ATTR_SELECTED_PROJECT]}"/> 35.12 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.ProjectIndexView" scope="request"/> 35.13 35.14 -<jsp:useBean id="projects" type="java.util.List<de.uapcore.lightpit.entities.Project>" scope="request"/> 35.15 - 35.16 -<c:if test="${empty projects}"> 35.17 +<c:if test="${empty viewmodel.projects}"> 35.18 <div class="info-box"> 35.19 <fmt:message key="no-projects"/> 35.20 </div> 35.21 @@ -43,30 +40,35 @@ 35.22 <a href="./projects/edit" class="button"><fmt:message key="button.create"/></a> 35.23 </div> 35.24 35.25 -<c:if test="${not empty projects}"> 35.26 +<c:if test="${not empty viewmodel.projects}"> 35.27 <table id="project-list" class="datatable medskip"> 35.28 <colgroup> 35.29 <col> 35.30 <col width="20%"> 35.31 <col width="50%"> 35.32 - <col width="10%"> 35.33 - <col width="10%"> 35.34 - <col width="10%"> 35.35 + <col width="6%"> 35.36 + <col width="6%"> 35.37 + <col width="6%"> 35.38 + <col width="6%"> 35.39 + <col width="6%"> 35.40 </colgroup> 35.41 <thead> 35.42 <tr> 35.43 <th></th> 35.44 <th><fmt:message key="name"/></th> 35.45 <th><fmt:message key="repoUrl"/></th> 35.46 - <th><fmt:message key="issues.open"/></th> 35.47 - <th><fmt:message key="issues.active"/></th> 35.48 - <th><fmt:message key="issues.done"/></th> 35.49 + <th class="hcenter"><fmt:message key="version.latest"/></th> 35.50 + <th class="hcenter"><fmt:message key="version.next"/></th> 35.51 + <th class="hcenter"><fmt:message key="issues.open"/></th> 35.52 + <th class="hcenter"><fmt:message key="issues.active"/></th> 35.53 + <th class="hcenter"><fmt:message key="issues.done"/></th> 35.54 </tr> 35.55 </thead> 35.56 <tbody> 35.57 - <c:forEach var="project" items="${projects}"> 35.58 + <c:forEach var="projectInfo" items="${viewmodel.projects}"> 35.59 + <c:set var="project" scope="page" value="${projectInfo.project}"/> 35.60 <tr class="nowrap"> 35.61 - <td style="width: 2em;"><a href="./projects/edit?id=${project.id}">✎</a></td> 35.62 + <td style="width: 2em;"><a href="./projects/edit?pid=${project.id}">✎</a></td> 35.63 <td><a href="./projects/view?pid=${project.id}"><c:out value="${project.name}"/></a> 35.64 </td> 35.65 <td> 35.66 @@ -75,9 +77,19 @@ 35.67 value="${project.repoUrl}"/></a> 35.68 </c:if> 35.69 </td> 35.70 - <td>${project.openIssues}</td> 35.71 - <td>${project.activeIssues}</td> 35.72 - <td>${project.doneIssues}</td> 35.73 + <td class="hright"> 35.74 + <c:if test="${not empty projectInfo.latestVersion}"> 35.75 + <a href="./projects/versions/view?vid=${projectInfo.latestVersion.id}"><c:out value="${projectInfo.latestVersion.name}"/></a> 35.76 + </c:if> 35.77 + </td> 35.78 + <td class="hright"> 35.79 + <c:if test="${not empty projectInfo.nextVersion}"> 35.80 + <a href="./projects/versions/view?vid=${projectInfo.nextVersion.id}"><c:out value="${projectInfo.nextVersion.name}"/></a> 35.81 + </c:if> 35.82 + </td> 35.83 + <td class="hright">${projectInfo.issueSummary.open}</td> 35.84 + <td class="hright">${projectInfo.issueSummary.active}</td> 35.85 + <td class="hright">${projectInfo.issueSummary.done}</td> 35.86 </tr> 35.87 </c:forEach> 35.88 </tbody>
36.1 --- a/src/main/webapp/WEB-INF/jsp/site.jsp Sat May 30 18:12:38 2020 +0200 36.2 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Mon Jun 01 14:46:58 2020 +0200 36.3 @@ -26,7 +26,6 @@ 36.4 --%> 36.5 <%@page contentType="text/html" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %> 36.6 <%@page import="de.uapcore.lightpit.Constants" %> 36.7 -<%@page import="de.uapcore.lightpit.modules.ProjectsModule" %> 36.8 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 36.9 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 36.10 <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> 36.11 @@ -60,9 +59,6 @@ 36.12 <fmt:setLocale scope="request" value="${sessionScope[Constants.SESSION_ATTR_LANGUAGE]}"/> 36.13 </c:if> 36.14 36.15 -<%-- Selected project, if any --%> 36.16 -<c:set scope="page" var="selectedProject" value="${sessionScope[ProjectsModule.SESSION_ATTR_SELECTED_PROJECT]}"/> 36.17 - 36.18 <%-- Load resource bundles --%> 36.19 <fmt:setBundle scope="request" basename="${bundleName}"/> 36.20 <fmt:setBundle scope="request" var="lightpit_bundle" basename="localization.lightpit"/>
37.1 --- a/src/main/webapp/WEB-INF/jsp/user-form.jsp Sat May 30 18:12:38 2020 +0200 37.2 +++ b/src/main/webapp/WEB-INF/jsp/user-form.jsp Mon Jun 01 14:46:58 2020 +0200 37.3 @@ -28,7 +28,8 @@ 37.4 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 37.5 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 37.6 37.7 -<jsp:useBean id="user" type="de.uapcore.lightpit.entities.User" scope="request"/> 37.8 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.UsersEditView" scope="request"/> 37.9 +<c:set var="user" scope="page" value="${viewmodel.user}" /> 37.10 37.11 <form action="./teams/commit" method="post"> 37.12 <table class="formtable">
38.1 --- a/src/main/webapp/WEB-INF/jsp/users.jsp Sat May 30 18:12:38 2020 +0200 38.2 +++ b/src/main/webapp/WEB-INF/jsp/users.jsp Mon Jun 01 14:46:58 2020 +0200 38.3 @@ -28,9 +28,9 @@ 38.4 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 38.5 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 38.6 38.7 -<jsp:useBean id="users" type="java.util.List<de.uapcore.lightpit.entities.User>" scope="request"/> 38.8 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.UsersView" scope="request"/> 38.9 38.10 -<c:if test="${empty users}"> 38.11 +<c:if test="${empty viewmodel.users}"> 38.12 <div class="info-box"> 38.13 <fmt:message key="no-users"/> 38.14 </div> 38.15 @@ -40,7 +40,7 @@ 38.16 <a href="./teams/edit" class="button"><fmt:message key="button.create"/></a> 38.17 </div> 38.18 38.19 -<c:if test="${not empty users}"> 38.20 +<c:if test="${not empty viewmodel.users}"> 38.21 <table class="datatable medskip"> 38.22 <thead> 38.23 <tr> 38.24 @@ -49,7 +49,7 @@ 38.25 </tr> 38.26 </thead> 38.27 <tbody> 38.28 - <c:forEach var="user" items="${users}"> 38.29 + <c:forEach var="user" items="${viewmodel.users}"> 38.30 <tr> 38.31 <td><a href="./teams/edit?id=${user.id}">✎</a></td> 38.32 <td><c:out value="${user.displayname}"/></td>
39.1 --- a/src/main/webapp/WEB-INF/jsp/version-form.jsp Sat May 30 18:12:38 2020 +0200 39.2 +++ b/src/main/webapp/WEB-INF/jsp/version-form.jsp Mon Jun 01 14:46:58 2020 +0200 39.3 @@ -28,14 +28,8 @@ 39.4 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 39.5 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 39.6 39.7 -<jsp:useBean id="projects" type="java.util.List<de.uapcore.lightpit.entities.Project>" scope="request" /> 39.8 -<jsp:useBean id="version" type="de.uapcore.lightpit.entities.Version" scope="request"/> 39.9 -<jsp:useBean id="versionStatusEnum" type="de.uapcore.lightpit.entities.VersionStatus[]" scope="request"/> 39.10 - 39.11 -<jsp:useBean id="statsAffected" type="de.uapcore.lightpit.entities.VersionStatistics" scope="request"/> 39.12 -<jsp:useBean id="statsScheduled" type="de.uapcore.lightpit.entities.VersionStatistics" scope="request"/> 39.13 -<jsp:useBean id="statsResolved" type="de.uapcore.lightpit.entities.VersionStatistics" scope="request"/> 39.14 -<jsp:useBean id="statsHideZeros" type="java.lang.Boolean" scope="request"/> 39.15 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.VersionEditView" scope="request" /> 39.16 +<c:set var="version" scope="page" value="${viewmodel.version}"/> 39.17 39.18 <form action="./projects/versions/commit" method="post"> 39.19 <table class="formtable" style="width: 35ch"> 39.20 @@ -47,19 +41,21 @@ 39.21 <tr> 39.22 <th><fmt:message key="version.project"/></th> 39.23 <td> 39.24 - <c:if test="${version.project.id ge 0}"> 39.25 - <c:out value="${version.project.name}" /> 39.26 - <input type="hidden" name="pid" value="${version.project.id}" /> 39.27 - </c:if> 39.28 - <c:if test="${empty version.project or version.project.id lt 0}"> 39.29 - <select name="pid" required> 39.30 - <c:forEach var="project" items="${projects}"> 39.31 - <option value="${project.id}"> 39.32 - <c:out value="${project.name}" /> 39.33 - </option> 39.34 - </c:forEach> 39.35 - </select> 39.36 - </c:if> 39.37 + <c:choose> 39.38 + <c:when test="${not empty version.project}"> 39.39 + <c:out value="${version.project.name}" /> 39.40 + <input type="hidden" name="pid" value="${version.project.id}" /> 39.41 + </c:when> 39.42 + <c:otherwise> 39.43 + <select name="pid" required> 39.44 + <c:forEach var="project" items="${viewmodel.projects}"> 39.45 + <option value="${project.id}"> 39.46 + <c:out value="${project.name}" /> 39.47 + </option> 39.48 + </c:forEach> 39.49 + </select> 39.50 + </c:otherwise> 39.51 + </c:choose> 39.52 </td> 39.53 </tr> 39.54 <tr> 39.55 @@ -70,7 +66,7 @@ 39.56 <th><fmt:message key="version.status"/></th> 39.57 <td> 39.58 <select name="status" required> 39.59 - <c:forEach var="elem" items="${versionStatusEnum}"> 39.60 + <c:forEach var="elem" items="${viewmodel.versionStatus}"> 39.61 <option 39.62 <c:if test="${elem eq version.status}">selected</c:if> value="${elem}"><fmt:message 39.63 key="version.status.${elem}"/></option> 39.64 @@ -90,7 +86,7 @@ 39.65 <td colspan="2"> 39.66 <input type="hidden" name="id" value="${version.id}"/> 39.67 <c:choose> 39.68 - <c:when test="${not empty version.project and version.project.id ge 0}"> 39.69 + <c:when test="${not empty version.project}"> 39.70 <c:set var="cancelUrl">./projects/view?pid=${version.project.id}</c:set> 39.71 </c:when> 39.72 <c:otherwise>
40.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 40.2 +++ b/src/main/webapp/WEB-INF/jsp/version.jsp Mon Jun 01 14:46:58 2020 +0200 40.3 @@ -0,0 +1,65 @@ 40.4 +<%-- 40.5 +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 40.6 + 40.7 +Copyright 2018 Mike Becker. All rights reserved. 40.8 + 40.9 +Redistribution and use in source and binary forms, with or without 40.10 +modification, are permitted provided that the following conditions are met: 40.11 + 40.12 +1. Redistributions of source code must retain the above copyright 40.13 +notice, this list of conditions and the following disclaimer. 40.14 + 40.15 +2. Redistributions in binary form must reproduce the above copyright 40.16 +notice, this list of conditions and the following disclaimer in the 40.17 +documentation and/or other materials provided with the distribution. 40.18 + 40.19 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 40.20 +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 40.21 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40.22 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 40.23 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 40.24 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 40.25 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 40.26 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 40.27 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 40.28 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40.29 +--%> 40.30 +<%@page pageEncoding="UTF-8" %> 40.31 +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 40.32 +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 40.33 + 40.34 +<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.VersionView" scope="request"/> 40.35 +<c:set var="version" scope="page" value="${viewmodel.versionInfo.version}"/> 40.36 + 40.37 +<c:set var="project" scope="page" value="${version.project}"/> 40.38 +<%@include file="../jspf/project-header.jsp"%> 40.39 + 40.40 + 40.41 +<div id="tool-area"> 40.42 + <a href="./projects/issues/edit?pid=${project.id}" class="button"><fmt:message key="button.issue.create"/></a> 40.43 +</div> 40.44 + 40.45 +<h2> 40.46 +<fmt:message key="version.label" /> <c:out value="${version.name}" /> - <fmt:message key="version.status.${version.status}"/> 40.47 +<a href="./projects/versions/edit?vid=${version.id}">✎</a> 40.48 +</h2> 40.49 + 40.50 +<h3><fmt:message key="issues.reported"/> </h3> 40.51 + 40.52 +<c:set var="summary" value="${viewmodel.versionInfo.reportedTotal}"/> 40.53 +<c:set var="issues" value="${viewmodel.versionInfo.reported}"/> 40.54 +<%@include file="../jspf/issue-summary.jsp"%> 40.55 +<%@include file="../jspf/issue-list.jsp"%> 40.56 + 40.57 +<h3><fmt:message key="issues.scheduled"/> </h3> 40.58 +<c:set var="summary" value="${viewmodel.versionInfo.scheduledTotal}"/> 40.59 +<c:set var="issues" value="${viewmodel.versionInfo.scheduled}"/> 40.60 +<%@include file="../jspf/issue-summary.jsp"%> 40.61 +<%@include file="../jspf/issue-list.jsp"%> 40.62 + 40.63 +<h3><fmt:message key="issues.resolved"/> </h3> 40.64 +<c:set var="summary" value="${viewmodel.versionInfo.resolvedTotal}"/> 40.65 +<c:set var="issues" value="${viewmodel.versionInfo.resolved}"/> 40.66 +<%@include file="../jspf/issue-summary.jsp"%> 40.67 +<%@include file="../jspf/issue-list.jsp"%> 40.68 +
41.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 41.2 +++ b/src/main/webapp/WEB-INF/jspf/issue-list.jsp Mon Jun 01 14:46:58 2020 +0200 41.3 @@ -0,0 +1,53 @@ 41.4 +<%-- 41.5 +issues: List<Issue> 41.6 +--%> 41.7 + 41.8 +<table class="fullwidth datatable medskip"> 41.9 + <thead> 41.10 + <tr> 41.11 + <th><fmt:message key="issue.subject"/></th> 41.12 + <th><fmt:message key="issue.assignee"/></th> 41.13 + <th><fmt:message key="issue.category"/></th> 41.14 + <th><fmt:message key="issue.status"/></th> 41.15 + <th><fmt:message key="issue.created"/></th> 41.16 + <th><fmt:message key="issue.updated"/></th> 41.17 + <th><fmt:message key="issue.eta"/></th> 41.18 + </tr> 41.19 + </thead> 41.20 + <tbody> 41.21 + <c:forEach var="issue" items="${issues}"> 41.22 + <tr> 41.23 + <td> 41.24 + <span class="phase-${issue.status.phase}"> 41.25 + <a href="./projects/issues/edit?issue=${issue.id}"> 41.26 + <c:out value="${issue.subject}" /> 41.27 + </a> 41.28 + </span> 41.29 + </td> 41.30 + <td> 41.31 + <c:if test="${not empty issue.assignee}"> 41.32 + <c:out value="${issue.assignee.shortDisplayname}" /> 41.33 + </c:if> 41.34 + <c:if test="${empty issue.assignee}"> 41.35 + <fmt:message key="placeholder.null-assignee" /> 41.36 + </c:if> 41.37 + </td> 41.38 + <td> 41.39 + <fmt:message key="issue.category.${issue.category}" /> 41.40 + </td> 41.41 + <td> 41.42 + <fmt:message key="issue.status.${issue.status}" /> 41.43 + </td> 41.44 + <td> 41.45 + <fmt:formatDate value="${issue.created}" type="BOTH"/> 41.46 + </td> 41.47 + <td> 41.48 + <fmt:formatDate value="${issue.updated}" type="BOTH"/> 41.49 + </td> 41.50 + <td> 41.51 + <fmt:formatDate value="${issue.eta}" /> 41.52 + </td> 41.53 + </tr> 41.54 + </c:forEach> 41.55 + </tbody> 41.56 +</table>
42.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 42.2 +++ b/src/main/webapp/WEB-INF/jspf/issue-summary.jsp Mon Jun 01 14:46:58 2020 +0200 42.3 @@ -0,0 +1,20 @@ 42.4 +<%-- 42.5 +summary: IssueSummary 42.6 +--%> 42.7 + 42.8 +<div class="issue-summary"> 42.9 + <div class="row"> 42.10 + <div class="caption"><fmt:message key="issues.open"/>:</div> 42.11 + <div><c:out value="${summary.open}"/></div> 42.12 + <div class="caption"><fmt:message key="issues.active"/>:</div> 42.13 + <div><c:out value="${summary.active}"/></div> 42.14 + <div class="caption"><fmt:message key="issues.done"/>:</div> 42.15 + <div><c:out value="${summary.done}"/></div> 42.16 + </div> 42.17 +</div> 42.18 + 42.19 +<div class="issue-progress-bar"> 42.20 + <div class="open" style="width: ${summary.openPercent}%"></div> 42.21 + <div class="active" style="width: ${summary.activePercent}%"></div> 42.22 + <div class="done" style="width: ${summary.donePercent}%"></div> 42.23 +</div> 42.24 \ No newline at end of file
43.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 43.2 +++ b/src/main/webapp/WEB-INF/jspf/project-header.jsp Mon Jun 01 14:46:58 2020 +0200 43.3 @@ -0,0 +1,24 @@ 43.4 +<%-- 43.5 +project: Project 43.6 +--%> 43.7 +<div class="project-attributes"> 43.8 + <div class="row"> 43.9 + <div class="caption"><fmt:message key="name"/>:</div> 43.10 + <div><c:out value="${project.name}"/></div> 43.11 + <div class="caption"><fmt:message key="description"/>:</div> 43.12 + <div><c:out value="${project.description}"/></div> 43.13 + </div> 43.14 + <div class="row"> 43.15 + <div class="caption"><fmt:message key="owner"/>:</div> 43.16 + <div> 43.17 + <c:if test="${not empty project.owner}"><c:out value="${project.owner.displayname}"/></c:if> 43.18 + </div> 43.19 + <div class="caption"><fmt:message key="repoUrl"/>:</div> 43.20 + <div> 43.21 + <c:if test="${not empty project.repoUrl}"> 43.22 + <a target="_blank" href="<c:out value="${project.repoUrl}"/>"><c:out 43.23 + value="${project.repoUrl}"/></a> 43.24 + </c:if> 43.25 + </div> 43.26 + </div> 43.27 +</div>
44.1 --- a/src/main/webapp/WEB-INF/jspf/version-stats.jsp Sat May 30 18:12:38 2020 +0200 44.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 44.3 @@ -1,54 +0,0 @@ 44.4 -<%@ taglib uri = "http://java.sun.com/jsp/jstl/functions" prefix = "fn" %> 44.5 - 44.6 -<table class="datatable"> 44.7 - <c:if test="${statsHideZeros}"> 44.8 - <c:set var="visibleColumns" value="0"/> 44.9 - <c:forEach var="idx" begin="0" end="${fn:length(issueStatusEnum)-1}"> 44.10 - <c:set var="visibleColumns" value="${visibleColumns + (stats.columnTotals[idx] eq 0 ? 0 :1)}"/> 44.11 - </c:forEach> 44.12 - </c:if> 44.13 - <c:if test="${not statsHideZeros}"> 44.14 - <c:set var="visibleColumns" value="${fn:length(issueStatusEnum)}" /> 44.15 - </c:if> 44.16 - <c:set var="colwidth"><fmt:formatNumber value="${100/(visibleColumns+2)}" maxFractionDigits="0" /></c:set> 44.17 - <colgroup> 44.18 - <c:forEach var="idx" begin="1" end="${visibleColumns+2}"> 44.19 - <col width="${colwidth}%"> 44.20 - </c:forEach> 44.21 - </colgroup> 44.22 - <thead> 44.23 - <tr> 44.24 - <th></th> 44.25 - <c:forEach var="issueStatus" items="${issueStatusEnum}" varStatus="statusIter"> 44.26 - <c:if test="${not statsHideZeros or stats.columnTotals[statusIter.index] gt 0}"> 44.27 - <th class="hcenter"><fmt:message key="issue.status.${issueStatus}"/></th> 44.28 - </c:if> 44.29 - </c:forEach> 44.30 - <th class="hcenter"><fmt:message key="version.statistics.total"/> </th> 44.31 - </tr> 44.32 - </thead> 44.33 - <tbody> 44.34 - <c:forEach var="issueCategory" items="${issueCategoryEnum}" varStatus="categoryIter"> 44.35 - <c:if test="${not statsHideZeros or stats.rowTotals[categoryIter.index] gt 0}"> 44.36 - <tr> 44.37 - <th><fmt:message key="issue.category.${issueCategory}" /></th> 44.38 - <c:forEach var="issueStatus" items="${issueStatusEnum}" varStatus="statusIter"> 44.39 - <c:if test="${not statsHideZeros or stats.columnTotals[statusIter.index] gt 0}"> 44.40 - <td>${stats.issueCount[categoryIter.index][statusIter.index]}</td> 44.41 - </c:if> 44.42 - </c:forEach> 44.43 - <td>${stats.rowTotals[categoryIter.index]}</td> 44.44 - </tr> 44.45 - </c:if> 44.46 - </c:forEach> 44.47 - <tr> 44.48 - <th><fmt:message key="version.statistics.total"/> </th> 44.49 - <c:forEach var="issueStatus" items="${issueStatusEnum}" varStatus="statusIter"> 44.50 - <c:if test="${not statsHideZeros or stats.columnTotals[statusIter.index] gt 0}"> 44.51 - <td>${stats.columnTotals[statusIter.index]}</td> 44.52 - </c:if> 44.53 - </c:forEach> 44.54 - <td>${stats.total}</td> 44.55 - </tr> 44.56 - </tbody> 44.57 -</table> 44.58 \ No newline at end of file
45.1 --- a/src/main/webapp/lightpit.css Sat May 30 18:12:38 2020 +0200 45.2 +++ b/src/main/webapp/lightpit.css Mon Jun 01 14:46:58 2020 +0200 45.3 @@ -117,7 +117,6 @@ 45.4 } 45.5 45.6 table.datatable { 45.7 - width: auto; 45.8 border-style: solid; 45.9 border-width: 1pt; 45.10 border-color: silver;
46.1 --- a/src/main/webapp/projects.css Sat May 30 18:12:38 2020 +0200 46.2 +++ b/src/main/webapp/projects.css Mon Jun 01 14:46:58 2020 +0200 46.3 @@ -27,21 +27,12 @@ 46.4 * 46.5 */ 46.6 46.7 -#issue-list td { 46.8 - white-space: nowrap; 46.9 +.project-attributes, .issue-summary { 46.10 + display: table; 46.11 } 46.12 46.13 -#version-stats h2 { 46.14 - white-space: nowrap; 46.15 -} 46.16 - 46.17 -#version-stats td { 46.18 - text-align: right; 46.19 -} 46.20 - 46.21 -#project-attributes { 46.22 +.project-attributes { 46.23 margin-bottom: 2em; 46.24 - display: table; 46.25 } 46.26 46.27 .row { 46.28 @@ -63,3 +54,28 @@ 46.29 span.phase-2 { 46.30 text-decoration: line-through; 46.31 } 46.32 + 46.33 +.issue-progress-bar { 46.34 + display: flex; 46.35 + position: relative; 46.36 + width: 100ex; 46.37 + height: 2em; 46.38 + border-style: inset; 46.39 + border-width: 2pt; 46.40 + border-color: #6060cc; 46.41 +} 46.42 + 46.43 +.issue-progress-bar .open { 46.44 + height: 100%; 46.45 + background: steelblue; 46.46 +} 46.47 + 46.48 +.issue-progress-bar .active { 46.49 + height: 100%; 46.50 + background: gold; 46.51 +} 46.52 + 46.53 +.issue-progress-bar .done { 46.54 + height: 100%; 46.55 + background: green; 46.56 +}