# HG changeset patch # User Mike Becker # Date 1591015618 -7200 # Node ID 0a658e53177cb335fb6cff2a56c9a6970bbf8ae5 # Parent 3d16ad54b3dc7b41fece43c3611574e792aabc61 improves issue overview and adds progress information diff -r 3d16ad54b3dc -r 0a658e53177c pom.xml --- a/pom.xml Sat May 30 18:12:38 2020 +0200 +++ b/pom.xml Mon Jun 01 14:46:58 2020 +0200 @@ -4,7 +4,7 @@ 4.0.0 de.uapcore lightpit - 0.1-SNAPSHOT + 0.2 war UTF-8 diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Mon Jun 01 14:46:58 2020 +0200 @@ -88,6 +88,7 @@ /** * Returns the name of the resource bundle associated with this servlet. + * * @return the resource bundle base name */ protected abstract String getResourceBundleName(); @@ -266,6 +267,17 @@ } /** + * Sets the view model object. + * The type must match the expected type in the JSP file. + * + * @param req the servlet request object + * @param viewModel the view model object + */ + public void setViewModel(HttpServletRequest req, Object viewModel) { + req.setAttribute(Constants.REQ_ATTR_VIEWMODEL, viewModel); + } + + /** * Obtains a request parameter of the specified type. * The specified type must have a single-argument constructor accepting a string to perform conversion. * The constructor of the specified type may throw an exception on conversion failures. @@ -281,7 +293,7 @@ final String[] paramValues = req.getParameterValues(name); int len = paramValues == null ? 0 : paramValues.length; final var array = (T) Array.newInstance(clazz.getComponentType(), len); - for (int i = 0 ; i < len ; i++) { + for (int i = 0; i < len; i++) { try { final Constructor ctor = clazz.getComponentType().getConstructor(String.class); Array.set(array, i, ctor.newInstance(paramValues[i])); diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/Constants.java --- a/src/main/java/de/uapcore/lightpit/Constants.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/Constants.java Mon Jun 01 14:46:58 2020 +0200 @@ -92,6 +92,11 @@ public static final String REQ_ATTR_CONTENT_PAGE = fqn(AbstractLightPITServlet.class, "content-page"); /** + * Key for the view model object (the type depends on the rendered site). + */ + public static final String REQ_ATTR_VIEWMODEL = "viewmodel"; + + /** * Key for the name of the additional stylesheet used by a module. */ public static final String REQ_ATTR_STYLESHEET = fqn(AbstractLightPITServlet.class, "extraCss"); diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/dao/IssueDao.java --- a/src/main/java/de/uapcore/lightpit/dao/IssueDao.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/IssueDao.java Mon Jun 01 14:46:58 2020 +0200 @@ -30,6 +30,7 @@ import de.uapcore.lightpit.entities.Issue; import de.uapcore.lightpit.entities.Project; +import de.uapcore.lightpit.entities.Version; import java.sql.SQLException; import java.util.List; @@ -48,6 +49,15 @@ List list(Project project) throws SQLException; /** + * Lists all issues that are somehow related to the specified version. + * + * @param version the version + * @return a list of issues + * @throws SQLException on any kind of SQL error + */ + List list(Version version) throws SQLException; + + /** * Saves an instances to the database. * Implementations of this DAO must guarantee that the generated ID is stored in the instance. * diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/dao/ProjectDao.java --- a/src/main/java/de/uapcore/lightpit/dao/ProjectDao.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/ProjectDao.java Mon Jun 01 14:46:58 2020 +0200 @@ -28,6 +28,7 @@ */ package de.uapcore.lightpit.dao; +import de.uapcore.lightpit.entities.IssueSummary; import de.uapcore.lightpit.entities.Project; import java.sql.SQLException; @@ -35,4 +36,6 @@ public interface ProjectDao extends GenericDao { List list() throws SQLException; + + IssueSummary getIssueSummary(Project project) throws SQLException; } diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/dao/VersionDao.java --- a/src/main/java/de/uapcore/lightpit/dao/VersionDao.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/VersionDao.java Mon Jun 01 14:46:58 2020 +0200 @@ -30,7 +30,6 @@ import de.uapcore.lightpit.entities.Project; import de.uapcore.lightpit.entities.Version; -import de.uapcore.lightpit.entities.VersionStatistics; import java.sql.SQLException; import java.util.List; @@ -45,31 +44,4 @@ * @throws SQLException on any kind of SQL error */ List list(Project project) throws SQLException; - - /** - * Retrieves statistics about issues that arose in a version. - * - * @param version the version - * @return version statistics - * @throws SQLException on any kind of SQL error - */ - VersionStatistics statsOpenedIssues(Version version) throws SQLException; - - /** - * Retrieves statistics about issues that are scheduled for a version. - * - * @param version the version - * @return version statistics - * @throws SQLException on any kind of SQL error - */ - VersionStatistics statsScheduledIssues(Version version) throws SQLException; - - /** - * Retrieves statistics about issues that are resolved in a version. - * - * @param version the version - * @return version statistics - * @throws SQLException on any kind of SQL error - */ - VersionStatistics statsResolvedIssues(Version version) throws SQLException; } diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java Mon Jun 01 14:46:58 2020 +0200 @@ -43,7 +43,7 @@ public final class PGIssueDao implements IssueDao { - private final PreparedStatement insert, update, list, find; + private final PreparedStatement insert, update, list, listForVersion, find; private final PreparedStatement affectedVersions, scheduledVersions, resolvedVersions; private final PreparedStatement clearAffected, clearScheduled, clearResolved; private final PreparedStatement insertAffected, insertScheduled, insertResolved; @@ -56,7 +56,24 @@ "from lpit_issue i " + "left join lpit_project p on project = projectid " + "left join lpit_user on userid = assignee " + - "where project = ? "); + "where project = ? "+ + "order by eta asc, updated desc"); + + listForVersion = connection.prepareStatement( + "with issue_version as ( "+ + "select issueid, versionid from lpit_issue_affected_version union "+ + "select issueid, versionid from lpit_issue_scheduled_version union "+ + "select issueid, versionid from lpit_issue_resolved_version) "+ + "select issueid, project, p.name as projectname, status, category, subject, i.description, " + + "userid, username, givenname, lastname, mail, " + + "created, updated, eta " + + "from lpit_issue i " + + "join issue_version using (issueid) "+ + "left join lpit_project p on project = projectid " + + "left join lpit_user on userid = assignee " + + "where versionid = ? "+ + "order by eta asc, updated desc" + ); find = connection.prepareStatement( "select issueid, project, p.name as projectname, status, category, subject, i.description, " + @@ -121,7 +138,8 @@ private Issue mapColumns(ResultSet result) throws SQLException { final var project = new Project(result.getInt("project")); project.setName(result.getString("projectname")); - final var issue = new Issue(result.getInt("issueid"), project); + final var issue = new Issue(result.getInt("issueid")); + issue.setProject(project); issue.setStatus(IssueStatus.valueOf(result.getString("status"))); issue.setCategory(IssueCategory.valueOf(result.getString("category"))); issue.setSubject(result.getString("subject")); @@ -133,8 +151,8 @@ return issue; } - private Version mapVersion(ResultSet result, Project project) throws SQLException { - final var version = new Version(result.getInt("versionid"), project); + private Version mapVersion(ResultSet result) throws SQLException { + final var version = new Version(result.getInt("versionid")); version.setName(result.getString("name")); version.setOrdinal(result.getInt("ordinal")); version.setStatus(VersionStatus.valueOf(result.getString("status"))); @@ -203,11 +221,10 @@ } } - @Override - public List list(Project project) throws SQLException { - list.setInt(1, project.getId()); + private List list(PreparedStatement query, int arg) throws SQLException { + query.setInt(1, arg); List issues = new ArrayList<>(); - try (var result = list.executeQuery()) { + try (var result = query.executeQuery()) { while (result.next()) { issues.add(mapColumns(result)); } @@ -216,6 +233,16 @@ } @Override + public List list(Project project) throws SQLException { + return list(list, project.getId()); + } + + @Override + public List list(Version version) throws SQLException { + return list(listForVersion, version.getId()); + } + + @Override public Issue find(int id) throws SQLException { find.setInt(1, id); try (var result = find.executeQuery()) { @@ -232,7 +259,7 @@ List versions = new ArrayList<>(); try (var result = stmt.executeQuery()) { while (result.next()) { - versions.add(mapVersion(result, issue.getProject())); + versions.add(mapVersion(result)); } } return versions; diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGProjectDao.java Mon Jun 01 14:46:58 2020 +0200 @@ -29,6 +29,7 @@ package de.uapcore.lightpit.dao.postgres; import de.uapcore.lightpit.dao.ProjectDao; +import de.uapcore.lightpit.entities.IssueSummary; import de.uapcore.lightpit.entities.Project; import de.uapcore.lightpit.entities.User; @@ -98,24 +99,26 @@ return proj; } - private void mapIssueSummary(Project proj) throws SQLException { - issue_summary.setInt(1, proj.getId()); + public IssueSummary getIssueSummary(Project project) throws SQLException { + issue_summary.setInt(1, project.getId()); final var result = issue_summary.executeQuery(); + final var summary = new IssueSummary(); while (result.next()) { final var phase = result.getInt("phase"); final var total = result.getInt("total"); switch(phase) { case 0: - proj.setOpenIssues(total); + summary.setOpen(total); break; case 1: - proj.setActiveIssues(total); + summary.setActive(total); break; case 2: - proj.setDoneIssues(total); + summary.setDone(total); break; } } + return summary; } @Override @@ -146,7 +149,6 @@ try (var result = list.executeQuery()) { while (result.next()) { final var project = mapColumns(result); - mapIssueSummary(project); projects.add(project); } } @@ -159,7 +161,6 @@ try (var result = find.executeQuery()) { if (result.next()) { final var project = mapColumns(result); - mapIssueSummary(project); return project; } else { return null; diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java Mon Jun 01 14:46:58 2020 +0200 @@ -29,7 +29,9 @@ package de.uapcore.lightpit.dao.postgres; import de.uapcore.lightpit.dao.VersionDao; -import de.uapcore.lightpit.entities.*; +import de.uapcore.lightpit.entities.Project; +import de.uapcore.lightpit.entities.Version; +import de.uapcore.lightpit.entities.VersionStatus; import java.sql.Connection; import java.sql.PreparedStatement; @@ -42,7 +44,6 @@ public final class PGVersionDao implements VersionDao { private final PreparedStatement insert, update, list, find; - private final PreparedStatement issuesAffected, issuesScheduled, issuesResolved; public PGVersionDao(Connection connection) throws SQLException { list = connection.prepareStatement( @@ -64,54 +65,19 @@ update = connection.prepareStatement( "update lpit_version set name = ?, ordinal = ?, status = ?::version_status where versionid = ?" ); - - issuesAffected = connection.prepareStatement( - "select category, status, count(*) as issuecount " + - "from lpit_issue_affected_version " + - "join lpit_issue using (issueid) " + - "where versionid = ? " + - "group by category, status" - ); - issuesScheduled = connection.prepareStatement( - "select category, status, count(*) as issuecount " + - "from lpit_issue_scheduled_version " + - "join lpit_issue using (issueid) " + - "where versionid = ? " + - "group by category, status" - ); - issuesResolved = connection.prepareStatement( - "select category, status, count(*) as issuecount " + - "from lpit_issue_resolved_version " + - "join lpit_issue using (issueid) " + - "where versionid = ? " + - "group by category, status" - ); } private Version mapColumns(ResultSet result) throws SQLException { final var project = new Project(result.getInt("project")); project.setName(result.getString("projectname")); - final var version = new Version(result.getInt("versionid"), project); + final var version = new Version(result.getInt("versionid")); + version.setProject(project); version.setName(result.getString("name")); version.setOrdinal(result.getInt("ordinal")); version.setStatus(VersionStatus.valueOf(result.getString("status"))); return version; } - private VersionStatistics versionStatistics(Version version, PreparedStatement stmt) throws SQLException { - stmt.setInt(1, version.getId()); - final var result = stmt.executeQuery(); - final var stats = new VersionStatistics(version); - while (result.next()) { - stats.setIssueCount( - IssueCategory.valueOf(result.getString("category")), - IssueStatus.valueOf(result.getString("status")), - result.getInt("issuecount") - ); - } - return stats; - } - @Override public void save(Version instance) throws SQLException { Objects.requireNonNull(instance.getName()); @@ -159,19 +125,4 @@ } } } - - @Override - public VersionStatistics statsOpenedIssues(Version version) throws SQLException { - return versionStatistics(version, issuesAffected); - } - - @Override - public VersionStatistics statsScheduledIssues(Version version) throws SQLException { - return versionStatistics(version, issuesScheduled); - } - - @Override - public VersionStatistics statsResolvedIssues(Version version) throws SQLException { - return versionStatistics(version, issuesResolved); - } } diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/entities/Issue.java --- a/src/main/java/de/uapcore/lightpit/entities/Issue.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Issue.java Mon Jun 01 14:46:58 2020 +0200 @@ -38,7 +38,7 @@ public final class Issue { private int id; - private final Project project; + private Project project; private IssueStatus status; private IssueCategory category; @@ -55,9 +55,8 @@ private Timestamp updated = Timestamp.from(Instant.now()); private Date eta; - public Issue(int id, Project project) { + public Issue(int id) { this.id = id; - this.project = project; } public int getId() { @@ -72,6 +71,10 @@ this.id = id; } + public void setProject(Project project) { + this.project = project; + } + public Project getProject() { return project; } diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/entities/IssueSummary.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/entities/IssueSummary.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,67 @@ +package de.uapcore.lightpit.entities; + +public class IssueSummary { + private int open = 0; + private int active = 0; + private int done = 0; + + public int getOpen() { + return open; + } + + public void setOpen(int open) { + this.open = open; + } + + public int getActive() { + return active; + } + + public void setActive(int active) { + this.active = active; + } + + public int getDone() { + return done; + } + + public void setDone(int done) { + this.done = done; + } + + public int getTotal() { + return open+active+done; + } + + public int getOpenPercent() { + return 100-getActivePercent()-getDonePercent(); + } + + public int getActivePercent() { + int total = getTotal(); + return total > 0 ? 100*active/total : 0; + } + + public int getDonePercent() { + int total = getTotal(); + return total > 0 ? 100*done/total : 0; + } + + /** + * Adds the specified issue to the summary by increming the respective counter. + * @param issue the issue + */ + public void add(Issue issue) { + switch (issue.getStatus().getPhase()) { + case 0: + open++; + break; + case 1: + active++; + break; + case 2: + done++; + break; + } + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/entities/Project.java --- a/src/main/java/de/uapcore/lightpit/entities/Project.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Project.java Mon Jun 01 14:46:58 2020 +0200 @@ -38,10 +38,6 @@ private String repoUrl; private User owner; - private int openIssues; - private int activeIssues; - private int doneIssues; - public Project(int id) { this.id = id; } @@ -82,30 +78,6 @@ this.owner = owner; } - public int getOpenIssues() { - return openIssues; - } - - public void setOpenIssues(int openIssues) { - this.openIssues = openIssues; - } - - public int getActiveIssues() { - return activeIssues; - } - - public void setActiveIssues(int activeIssues) { - this.activeIssues = activeIssues; - } - - public int getDoneIssues() { - return doneIssues; - } - - public void setDoneIssues(int doneIssues) { - this.doneIssues = doneIssues; - } - @Override public boolean equals(Object o) { if (this == o) return true; diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/entities/Version.java --- a/src/main/java/de/uapcore/lightpit/entities/Version.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Version.java Mon Jun 01 14:46:58 2020 +0200 @@ -41,9 +41,8 @@ private int ordinal = 0; private VersionStatus status = VersionStatus.Future; - public Version(int id, Project project) { + public Version(int id) { this.id = id; - this.project = project; } public int getId() { diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/modules/LanguageModule.java --- a/src/main/java/de/uapcore/lightpit/modules/LanguageModule.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/LanguageModule.java Mon Jun 01 14:46:58 2020 +0200 @@ -29,6 +29,7 @@ package de.uapcore.lightpit.modules; import de.uapcore.lightpit.*; +import de.uapcore.lightpit.viewmodel.LanguageView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -86,9 +87,12 @@ @RequestMapping(method = HttpMethod.GET) public ResponseType handle(HttpServletRequest req) { - req.setAttribute("languages", languages); - req.setAttribute("browserLanguage", req.getLocale()); + final var viewModel = new LanguageView(); + viewModel.setLanguages(languages); + viewModel.setBrowserLanguage(req.getLocale()); + viewModel.setCurrentLanguage((Locale)req.getSession().getAttribute(Constants.SESSION_ATTR_LANGUAGE)); + setViewModel(req, viewModel); setStylesheet(req, "language"); setContentPage(req, "language"); return ResponseType.HTML; diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Mon Jun 01 14:46:58 2020 +0200 @@ -32,6 +32,7 @@ import de.uapcore.lightpit.*; import de.uapcore.lightpit.dao.DataAccessObjects; import de.uapcore.lightpit.entities.*; +import de.uapcore.lightpit.viewmodel.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +43,10 @@ import java.io.IOException; import java.sql.Date; import java.sql.SQLException; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -59,41 +63,81 @@ public static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project"); public static final String SESSION_ATTR_SELECTED_ISSUE = fqn(ProjectsModule.class, "selected_issue"); public static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version"); - public static final String SESSION_ATTR_HIDE_ZEROS = fqn(ProjectsModule.class, "stats_hide_zeros"); private class SessionSelection { final HttpSession session; + final HttpServletRequest req; + final DataAccessObjects dao; Project project; Version version; Issue issue; - SessionSelection(HttpServletRequest req, Project project) { - this.session = req.getSession(); - this.project = project; + SessionSelection(HttpServletRequest req, DataAccessObjects dao) { + this.req = req; + this.dao = dao; + session = req.getSession(); + } + + void newProject() { + project = null; version = null; issue = null; updateAttributes(); + project = new Project(-1); + updateAttributes(); } - SessionSelection(HttpServletRequest req, DataAccessObjects dao) throws SQLException { - this.session = req.getSession(); - final var issueDao = dao.getIssueDao(); - final var projectDao = dao.getProjectDao(); - final var issueSelection = getParameter(req, Integer.class, "issue"); - if (issueSelection.isPresent()) { - issue = issueDao.find(issueSelection.get()); - } else { - final var issue = (Issue) session.getAttribute(SESSION_ATTR_SELECTED_ISSUE); - this.issue = issue == null ? null : issueDao.find(issue.getId()); + void newVersion() throws SQLException { + project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); + syncProject(); + version = null; + issue = null; + updateAttributes(); + version = new Version(-1); + version.setProject(project); + updateAttributes(); + } + + void newIssue() throws SQLException { + project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); + syncProject(); + version = null; + issue = null; + updateAttributes(); + issue = new Issue(-1); + issue.setProject(project); + updateAttributes(); + } + + void selectVersion(Version selectedVersion) throws SQLException { + issue = null; + version = selectedVersion; + if (!version.getProject().equals(project)) { + project = dao.getProjectDao().find(version.getProject().getId()); } - if (issue != null) { - version = null; // show the issue globally - project = projectDao.find(issue.getProject().getId()); + // our object contains more details + version.setProject(project); + updateAttributes(); + } + + void selectIssue(Issue selectedIssue) throws SQLException { + issue = selectedIssue; + if (!issue.getProject().equals(project)) { + project = dao.getProjectDao().find(issue.getProject().getId()); } + // our object contains more details + issue.setProject(project); + if (!issue.getResolvedVersions().contains(version) && !issue.getScheduledVersions().contains(version) + && !issue.getAffectedVersions().contains(version)) { + version = null; + } + updateAttributes(); + } + void syncProject() throws SQLException { final var projectSelection = getParameter(req, Integer.class, "pid"); if (projectSelection.isPresent()) { - final var selectedProject = projectDao.find(projectSelection.get()); + final var selectedProject = dao.getProjectDao().find(projectSelection.get()); if (!Objects.equals(selectedProject, project)) { // reset version and issue if project changed version = null; @@ -101,51 +145,57 @@ } project = selectedProject; } else { - final var sessionProject = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); - project = sessionProject == null ? null : projectDao.find(sessionProject.getId()); + project = project == null ? null : dao.getProjectDao().find(project.getId()); } + } + + void syncVersion() throws SQLException { + final var versionSelection = getParameter(req, Integer.class, "vid"); + if (versionSelection.isPresent()) { + if (versionSelection.get() < 0) { + version = null; + } else { + final var selectedVersion = dao.getVersionDao().find(versionSelection.get()); + if (!Objects.equals(selectedVersion, version)) { + issue = null; + } + selectVersion(selectedVersion); + } + } else { + version = version == null ? null : dao.getVersionDao().find(version.getId()); + } + } + + void syncIssue() throws SQLException { + final var issueSelection = getParameter(req, Integer.class, "issue"); + if (issueSelection.isPresent()) { + final var selectedIssue = dao.getIssueDao().find(issueSelection.get()); + dao.getIssueDao().joinVersionInformation(selectedIssue); + selectIssue(selectedIssue); + } else { + issue = issue == null ? null : dao.getIssueDao().find(issue.getId()); + } + } + + void sync() throws SQLException { + project = (Project) session.getAttribute(SESSION_ATTR_SELECTED_PROJECT); + version = (Version) session.getAttribute(SESSION_ATTR_SELECTED_VERSION); + issue = (Issue) session.getAttribute(SESSION_ATTR_SELECTED_ISSUE); + + syncProject(); + syncVersion(); + syncIssue(); + updateAttributes(); } - void selectVersion(Version version) { - this.project = version.getProject(); - this.version = version; - this.issue = null; - updateAttributes(); - } - - void selectIssue(Issue issue) { - this.project = issue.getProject(); - this.issue = issue; - this.version = null; - updateAttributes(); - } - - void updateAttributes() { + private void updateAttributes() { session.setAttribute(SESSION_ATTR_SELECTED_PROJECT, project); session.setAttribute(SESSION_ATTR_SELECTED_VERSION, version); session.setAttribute(SESSION_ATTR_SELECTED_ISSUE, issue); } } - private void setAttributeHideZeros(HttpServletRequest req) { - final Boolean value; - final var param = getParameter(req, Boolean.class, "reduced"); - if (param.isPresent()) { - value = param.get(); - req.getSession().setAttribute(SESSION_ATTR_HIDE_ZEROS, value); - } else { - final var sessionValue = req.getSession().getAttribute(SESSION_ATTR_HIDE_ZEROS); - if (sessionValue != null) { - value = (Boolean) sessionValue; - } else { - value = false; - req.getSession().setAttribute(SESSION_ATTR_HIDE_ZEROS, value); - } - } - req.setAttribute("statsHideZeros", value); - } - @Override protected String getResourceBundleName() { return "localization.projects"; @@ -162,10 +212,10 @@ * Creates the breadcrumb menu. * * @param level the current active level (0: root, 1: project, 2: version, 3: issue list, 4: issue) - * @param sessionSelection the currently selected objects + * @param selection the currently selected objects * @return a dynamic breadcrumb menu trying to display as many levels as possible */ - private List getBreadcrumbs(int level, SessionSelection sessionSelection) { + private List getBreadcrumbs(int level, SessionSelection selection) { MenuEntry entry; final var breadcrumbs = new ArrayList(); @@ -174,47 +224,49 @@ breadcrumbs.add(entry); if (level == BREADCRUMB_LEVEL_ROOT) entry.setActive(true); - if (sessionSelection.project != null) { - if (sessionSelection.project.getId() < 0) { + if (selection.project != null) { + if (selection.project.getId() < 0) { entry = new MenuEntry(new ResourceKey("localization.projects", "button.create"), "projects/edit"); } else { - entry = new MenuEntry(sessionSelection.project.getName(), - "projects/view?pid=" + sessionSelection.project.getId()); + entry = new MenuEntry(selection.project.getName(), + "projects/view?pid=" + selection.project.getId()); } if (level == BREADCRUMB_LEVEL_PROJECT) entry.setActive(true); breadcrumbs.add(entry); } - if (sessionSelection.version != null) { - if (sessionSelection.version.getId() < 0) { + if (selection.version != null) { + if (selection.version.getId() < 0) { entry = new MenuEntry(new ResourceKey("localization.projects", "button.version.create"), "projects/versions/edit"); } else { - entry = new MenuEntry(sessionSelection.version.getName(), - // TODO: change link to issue overview for that version - "projects/versions/edit?id=" + sessionSelection.version.getId()); + entry = new MenuEntry(selection.version.getName(), + "projects/versions/view?vid=" + selection.version.getId()); } if (level == BREADCRUMB_LEVEL_VERSION) entry.setActive(true); breadcrumbs.add(entry); } - if (sessionSelection.project != null) { + if (selection.project != null) { + String path = "projects/issues/?pid=" + selection.project.getId(); + if (selection.version != null) { + path += "&vid="+selection.version.getId(); + } entry = new MenuEntry(new ResourceKey("localization.projects", "menu.issues"), - // TODO: maybe also add selected version - "projects/issues/?pid=" + sessionSelection.project.getId()); + path); if (level == BREADCRUMB_LEVEL_ISSUE_LIST) entry.setActive(true); breadcrumbs.add(entry); } - if (sessionSelection.issue != null) { - if (sessionSelection.issue.getId() < 0) { + if (selection.issue != null) { + if (selection.issue.getId() < 0) { entry = new MenuEntry(new ResourceKey("localization.projects", "button.issue.create"), "projects/issues/edit"); } else { - entry = new MenuEntry("#" + sessionSelection.issue.getId(), + entry = new MenuEntry("#" + selection.issue.getId(), // TODO: maybe change link to a view rather than directly opening the editor - "projects/issues/edit?id=" + sessionSelection.issue.getId()); + "projects/issues/edit?issue=" + selection.issue.getId()); } if (level == BREADCRUMB_LEVEL_ISSUE) entry.setActive(true); breadcrumbs.add(entry); @@ -226,8 +278,22 @@ @RequestMapping(method = HttpMethod.GET) public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException { final var sessionSelection = new SessionSelection(req, dao); - final var projectList = dao.getProjectDao().list(); - req.setAttribute("projects", projectList); + sessionSelection.sync(); + + final var projectDao = dao.getProjectDao(); + final var versionDao = dao.getVersionDao(); + + final var projectList = projectDao.list(); + + final var viewModel = new ProjectIndexView(); + for (var project : projectList) { + final var info = new ProjectInfo(project); + info.setVersions(versionDao.list(project)); + info.setIssueSummary(projectDao.getIssueSummary(project)); + viewModel.getProjects().add(info); + } + + setViewModel(req, viewModel); setContentPage(req, "projects"); setStylesheet(req, "projects"); @@ -236,17 +302,24 @@ return ResponseType.HTML; } - private void configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { - req.setAttribute("project", selection.project); - req.setAttribute("users", dao.getUserDao().list()); + private ProjectEditView configureEditForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { + final var viewModel = new ProjectEditView(); + viewModel.setProject(selection.project); + viewModel.setUsers(dao.getUserDao().list()); + setViewModel(req, viewModel); setContentPage(req, "project-form"); setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, selection)); + return viewModel; } @RequestMapping(requestPath = "edit", method = HttpMethod.GET) public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { - final var selection = new SessionSelection(req, findByParameter(req, Integer.class, "id", - dao.getProjectDao()::find).orElse(new Project(-1))); + final var selection = new SessionSelection(req, dao); + if (getParameter(req, Integer.class, "pid").isEmpty()) { + selection.newProject(); + } else { + selection.sync(); + } configureEditForm(req, dao, selection); @@ -272,10 +345,12 @@ setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); LOG.debug("Successfully updated project {}", project.getName()); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { - // TODO: set request attribute with error text LOG.warn("Form validation failure: {}", ex.getMessage()); LOG.debug("Details:", ex); - configureEditForm(req, dao, new SessionSelection(req, project)); + final var selection = new SessionSelection(req, dao); + selection.project = project; + final var vm = configureEditForm(req, dao, selection); + vm.setErrorText(ex.getMessage()); // TODO: error text } return ResponseType.HTML; @@ -283,127 +358,143 @@ @RequestMapping(requestPath = "view", method = HttpMethod.GET) public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { - final var sessionSelection = new SessionSelection(req, dao); - if (sessionSelection.project == null) { + final var selection = new SessionSelection(req, dao); + selection.sync(); + + if (selection.project == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); return ResponseType.NONE; } final var versionDao = dao.getVersionDao(); - final var versions = versionDao.list(sessionSelection.project); - final var statsAffected = new ArrayList(); - final var statsScheduled = new ArrayList(); - final var statsResolved = new ArrayList(); - for (Version version : versions) { - statsAffected.add(versionDao.statsOpenedIssues(version)); - statsScheduled.add(versionDao.statsScheduledIssues(version)); - statsResolved.add(versionDao.statsResolvedIssues(version)); - } + final var issueDao = dao.getIssueDao(); - setAttributeHideZeros(req); + final var viewModel = new ProjectView(selection.project); + final var issues = issueDao.list(selection.project); + for (var issue : issues) issueDao.joinVersionInformation(issue); + viewModel.setIssues(issues); + viewModel.setVersions(versionDao.list(selection.project)); + viewModel.updateVersionInfo(); + setViewModel(req, viewModel); - req.setAttribute("project", sessionSelection.project); - req.setAttribute("versions", versions); - req.setAttribute("statsAffected", statsAffected); - req.setAttribute("statsScheduled", statsScheduled); - req.setAttribute("statsResolved", statsResolved); - - req.setAttribute("issueStatusEnum", IssueStatus.values()); - req.setAttribute("issueCategoryEnum", IssueCategory.values()); - - setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, sessionSelection)); + setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_PROJECT, selection)); setContentPage(req, "project-details"); setStylesheet(req, "projects"); return ResponseType.HTML; } - private void configureEditVersionForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { - final var versionDao = dao.getVersionDao(); - req.setAttribute("projects", dao.getProjectDao().list()); - req.setAttribute("version", selection.version); - req.setAttribute("versionStatusEnum", VersionStatus.values()); + @RequestMapping(requestPath = "versions/view", method = HttpMethod.GET) + public ResponseType viewVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { + final var selection = new SessionSelection(req, dao); + selection.sync(); + if (selection.version == null) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + return ResponseType.NONE; + } - req.setAttribute("issueStatusEnum", IssueStatus.values()); - req.setAttribute("issueCategoryEnum", IssueCategory.values()); - req.setAttribute("statsAffected", versionDao.statsOpenedIssues(selection.version)); - req.setAttribute("statsScheduled", versionDao.statsScheduledIssues(selection.version)); - req.setAttribute("statsResolved", versionDao.statsResolvedIssues(selection.version)); - setAttributeHideZeros(req); + final var issueDao = dao.getIssueDao(); + final var viewModel = new VersionView(selection.version); + final var issues = issueDao.list(selection.version); + for (var issue : issues) issueDao.joinVersionInformation(issue); + viewModel.setIssues(issues); + setViewModel(req, viewModel); + setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_VERSION, selection)); + setContentPage(req, "version"); + setStylesheet(req, "projects"); + + return ResponseType.HTML; + } + + private VersionEditView configureEditVersionForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { + final var viewModel = new VersionEditView(selection.version); + if (selection.version.getProject() == null) { + viewModel.setProjects(dao.getProjectDao().list()); + } + setViewModel(req, viewModel); setContentPage(req, "version-form"); setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_VERSION, selection)); + return viewModel; } @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET) - public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { - final var sessionSelection = new SessionSelection(req, dao); + public ResponseType editVersion(HttpServletRequest req, DataAccessObjects dao) throws SQLException { + final var selection = new SessionSelection(req, dao); + if (getParameter(req, Integer.class, "vid").isEmpty()) { + selection.newVersion(); + } else { + selection.sync(); + } - sessionSelection.selectVersion(findByParameter(req, Integer.class, "id", dao.getVersionDao()::find) - .orElse(new Version(-1, sessionSelection.project))); - configureEditVersionForm(req, dao, sessionSelection); + configureEditVersionForm(req, dao, selection); return ResponseType.HTML; } @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST) public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { - final var sessionSelection = new SessionSelection(req, dao); - var version = new Version(-1, sessionSelection.project); + var version = new Version(-1); try { - version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project); + version = new Version(getParameter(req, Integer.class, "id").orElseThrow()); + version.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow())); version.setName(getParameter(req, String.class, "name").orElseThrow()); getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal); version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); dao.getVersionDao().saveOrUpdate(version); // specifying the pid parameter will purposely reset the session selected version! - setRedirectLocation(req, "./projects/view?pid="+sessionSelection.project.getId()); + setRedirectLocation(req, "./projects/view?pid="+version.getProject().getId()); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); - LOG.debug("Successfully updated version {} for project {}", version.getName(), sessionSelection.project.getName()); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { - // TODO: set request attribute with error text LOG.warn("Form validation failure: {}", ex.getMessage()); LOG.debug("Details:", ex); - sessionSelection.selectVersion(version); - configureEditVersionForm(req, dao, sessionSelection); + final var selection = new SessionSelection(req, dao); + selection.selectVersion(version); + final var viewModel = configureEditVersionForm(req, dao, selection); + // TODO: set Error Text } return ResponseType.HTML; } - private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { + private IssueEditView configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException { + final var viewModel = new IssueEditView(selection.issue); - if (selection.issue.getProject() == null || selection.issue.getProject().getId() < 0) { - req.setAttribute("projects", dao.getProjectDao().list()); - req.setAttribute("versions", Collections.emptyList()); + if (selection.issue.getProject() == null) { + viewModel.setProjects(dao.getProjectDao().list()); } else { - req.setAttribute("projects", Collections.emptyList()); - req.setAttribute("versions", dao.getVersionDao().list(selection.issue.getProject())); + viewModel.setVersions(dao.getVersionDao().list(selection.issue.getProject())); } - - dao.getIssueDao().joinVersionInformation(selection.issue); - req.setAttribute("issue", selection.issue); - req.setAttribute("issueStatusEnum", IssueStatus.values()); - req.setAttribute("issueCategoryEnum", IssueCategory.values()); - req.setAttribute("users", dao.getUserDao().list()); + viewModel.setUsers(dao.getUserDao().list()); + setViewModel(req, viewModel); setContentPage(req, "issue-form"); setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE, selection)); + return viewModel; } @RequestMapping(requestPath = "issues/", method = HttpMethod.GET) public ResponseType issues(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { - final var sessionSelection = new SessionSelection(req, dao); - if (sessionSelection.project == null) { + final var selection = new SessionSelection(req, dao); + selection.sync(); + if (selection.project == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); return ResponseType.NONE; } - req.setAttribute("issues", dao.getIssueDao().list(sessionSelection.project)); + final var viewModel = new IssuesView(); + viewModel.setProject(selection.project); + if (selection.version == null) { + viewModel.setIssues(dao.getIssueDao().list(selection.project)); + } else { + viewModel.setVersion(selection.version); + viewModel.setIssues(dao.getIssueDao().list(selection.version)); + } + setViewModel(req, viewModel); - setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE_LIST, sessionSelection)); + setBreadcrumbs(req, getBreadcrumbs(BREADCRUMB_LEVEL_ISSUE_LIST, selection)); setContentPage(req, "issues"); setStylesheet(req, "projects"); @@ -412,22 +503,25 @@ @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET) public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { - final var sessionSelection = new SessionSelection(req, dao); + final var selection = new SessionSelection(req, dao); + if (getParameter(req, Integer.class, "issue").isEmpty()) { + selection.newIssue(); + } else { + selection.sync(); + } - sessionSelection.selectIssue(findByParameter(req, Integer.class, "id", - dao.getIssueDao()::find).orElse(new Issue(-1, sessionSelection.project))); - configureEditIssueForm(req, dao, sessionSelection); + configureEditIssueForm(req, dao, selection); return ResponseType.HTML; } @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST) public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { - final var sessionSelection = new SessionSelection(req, dao); - Issue issue = new Issue(-1, sessionSelection.project); + Issue issue = new Issue(-1); try { - issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), sessionSelection.project); + issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow()); + issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow())); getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory); getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus); issue.setSubject(getParameter(req, String.class, "subject").orElseThrow()); @@ -440,17 +534,17 @@ getParameter(req, Integer[].class, "affected") .map(Stream::of) .map(stream -> - stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) + stream.map(Version::new).collect(Collectors.toList()) ).ifPresent(issue::setAffectedVersions); getParameter(req, Integer[].class, "scheduled") .map(Stream::of) .map(stream -> - stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) + stream.map(Version::new).collect(Collectors.toList()) ).ifPresent(issue::setScheduledVersions); getParameter(req, Integer[].class, "resolved") .map(Stream::of) .map(stream -> - stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList()) + stream.map(Version::new).collect(Collectors.toList()) ).ifPresent(issue::setResolvedVersions); dao.getIssueDao().saveOrUpdate(issue); @@ -458,13 +552,14 @@ // specifying the issue parameter keeps the edited issue as breadcrumb setRedirectLocation(req, "./projects/issues/?issue="+issue.getId()); setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); - LOG.debug("Successfully updated issue {} for project {}", issue.getId(), sessionSelection.project.getName()); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { // TODO: set request attribute with error text LOG.warn("Form validation failure: {}", ex.getMessage()); LOG.debug("Details:", ex); - sessionSelection.selectIssue(issue); - configureEditIssueForm(req, dao, sessionSelection); + final var selection = new SessionSelection(req, dao); + selection.selectIssue(issue); + final var viewModel = configureEditIssueForm(req, dao, selection); + // TODO: set Error Text } return ResponseType.HTML; diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/modules/UsersModule.java --- a/src/main/java/de/uapcore/lightpit/modules/UsersModule.java Sat May 30 18:12:38 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/UsersModule.java Mon Jun 01 14:46:58 2020 +0200 @@ -32,6 +32,8 @@ import de.uapcore.lightpit.*; import de.uapcore.lightpit.dao.DataAccessObjects; import de.uapcore.lightpit.entities.User; +import de.uapcore.lightpit.viewmodel.UsersEditView; +import de.uapcore.lightpit.viewmodel.UsersView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +59,9 @@ public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException { final var userDao = dao.getUserDao(); - req.setAttribute("users", userDao.list()); + final var viewModel = new UsersView(); + viewModel.setUsers(userDao.list()); + setViewModel(req, viewModel); setContentPage(req, "users"); return ResponseType.HTML; @@ -66,9 +70,11 @@ @RequestMapping(requestPath = "edit", method = HttpMethod.GET) public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { - req.setAttribute("user", findByParameter(req, Integer.class, "id", + final var viewModel = new UsersEditView(); + viewModel.setUser(findByParameter(req, Integer.class, "id", dao.getUserDao()::find).orElse(new User(-1))); + setViewModel(req, viewModel); setContentPage(req, "user-form"); return ResponseType.HTML; @@ -92,8 +98,10 @@ LOG.debug("Successfully updated user {}", user.getUsername()); } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { - // TODO: set request attribute with error text - req.setAttribute("user", user); + final var viewModel = new UsersEditView(); + viewModel.setUser(user); + // TODO: viewModel.setErrorText() + setViewModel(req, viewModel); setContentPage(req, "user-form"); LOG.warn("Form validation failure: {}", ex.getMessage()); LOG.debug("Details:", ex); diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/IssueEditView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,54 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.*; + +import java.util.Collections; +import java.util.List; + +public class IssueEditView { + private final Issue issue; + + private List projects = Collections.emptyList(); + private List versions = Collections.emptyList(); + private List users; + + public IssueEditView(Issue issue) { + this.issue = issue; + } + + public Issue getIssue() { + return issue; + } + + public List getProjects() { + return projects; + } + + public void setProjects(List projects) { + this.projects = projects; + } + + public List getVersions() { + return versions; + } + + public void setVersions(List versions) { + this.versions = versions; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public IssueStatus[] getIssueStatus() { + return IssueStatus.values(); + } + + public IssueCategory[] getIssueCategory() { + return IssueCategory.values(); + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/IssuesView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/IssuesView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,37 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.Issue; +import de.uapcore.lightpit.entities.Project; +import de.uapcore.lightpit.entities.Version; + +import java.util.List; + +public class IssuesView { + private List issues; + private Project project; + private Version version; + + public List getIssues() { + return issues; + } + + public void setIssues(List issues) { + this.issues = issues; + } + + public Version getVersion() { + return version; + } + + public void setVersion(Version version) { + this.version = version; + } + + public Project getProject() { + return project; + } + + public void setProject(Project project) { + this.project = project; + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/LanguageView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/LanguageView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,36 @@ +package de.uapcore.lightpit.viewmodel; + +import java.util.List; +import java.util.Locale; + +public class LanguageView { + + private List languages; + private Locale browserLanguage; + private Locale currentLanguage; + + + public List getLanguages() { + return languages; + } + + public void setLanguages(List languages) { + this.languages = languages; + } + + public Locale getBrowserLanguage() { + return browserLanguage; + } + + public void setBrowserLanguage(Locale browserLanguage) { + this.browserLanguage = browserLanguage; + } + + public Locale getCurrentLanguage() { + return currentLanguage; + } + + public void setCurrentLanguage(Locale currentLanguage) { + this.currentLanguage = currentLanguage; + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/ProjectEditView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectEditView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,37 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.Project; +import de.uapcore.lightpit.entities.User; + +import java.util.List; + +public class ProjectEditView { + + private Project project; + private List users; + private String errorText; + + public Project getProject() { + return project; + } + + public void setProject(Project project) { + this.project = project; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public String getErrorText() { + return errorText; + } + + public void setErrorText(String errorText) { + this.errorText = errorText; + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/ProjectIndexView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectIndexView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,17 @@ +package de.uapcore.lightpit.viewmodel; + +import java.util.ArrayList; +import java.util.List; + +public class ProjectIndexView { + + private List projects = new ArrayList<>(); + + public List getProjects() { + return projects; + } + + public void setProjects(List projects) { + this.projects = projects; + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/ProjectInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectInfo.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,58 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.IssueSummary; +import de.uapcore.lightpit.entities.Project; +import de.uapcore.lightpit.entities.Version; +import de.uapcore.lightpit.entities.VersionStatus; + +import java.util.Collections; +import java.util.List; + +public class ProjectInfo { + + private final Project project; + private List versions = Collections.emptyList(); + private IssueSummary issueSummary = new IssueSummary(); + + public ProjectInfo(Project project) { + this.project = project; + } + + public Project getProject() { + return project; + } + + public List getVersions() { + return versions; + } + + public void setVersions(List versions) { + this.versions = versions; + } + + public Version getLatestVersion() { + for (var v : versions) { + if (v.getStatus().ordinal() >= VersionStatus.Released.ordinal()) + return v; + } + return null; + } + + public Version getNextVersion() { + Version next = null; + for (var v : versions) { + if (v.getStatus().ordinal() >= VersionStatus.Released.ordinal()) + break; + next = v; + } + return next; + } + + public IssueSummary getIssueSummary() { + return issueSummary; + } + + public void setIssueSummary(IssueSummary issueSummary) { + this.issueSummary = issueSummary; + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/ProjectView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/ProjectView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,81 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.Issue; +import de.uapcore.lightpit.entities.IssueSummary; +import de.uapcore.lightpit.entities.Project; +import de.uapcore.lightpit.entities.Version; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ProjectView { + + private final Project project; + private List versions = Collections.emptyList(); + private List issues = Collections.emptyList(); + + private IssueSummary issuesTotal; + private List issuesWithoutVersion; + private IssueSummary issuesWithoutVersionTotal; + private List versionInfos = Collections.emptyList(); + + public ProjectView(Project project) { + this.project = project; + } + + public Project getProject() { + return project; + } + + public List getIssues() { + return issues; + } + + public void setIssues(List issues) { + this.issues = issues; + issuesTotal = new IssueSummary(); + issuesWithoutVersion = new ArrayList<>(); + issuesWithoutVersionTotal = new IssueSummary(); + for (Issue issue : issues) { + issuesTotal.add(issue); + if (issue.getResolvedVersions().isEmpty() && issue.getScheduledVersions().isEmpty() && issue.getResolvedVersions().isEmpty()) { + issuesWithoutVersion.add(issue); + issuesWithoutVersionTotal.add(issue); + } + } + } + + public List getVersions() { + return versions; + } + + public void setVersions(List versions) { + this.versions = versions; + } + + public void updateVersionInfo() { + versionInfos = new ArrayList<>(); + for (Version version : versions) { + final var info = new VersionInfo(version); + info.collectIssues(issues); + versionInfos.add(info); + } + } + + public IssueSummary getIssuesTotal() { + return issuesTotal; + } + + public List getIssuesWithoutVersion() { + return issuesWithoutVersion; + } + + public IssueSummary getIssuesWithoutVersionTotal() { + return issuesWithoutVersionTotal; + } + + public List getVersionInfos() { + return versionInfos; + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/UsersEditView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/UsersEditView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,24 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.User; + +public class UsersEditView { + private User user; + private String errorText; + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public String getErrorText() { + return errorText; + } + + public void setErrorText(String errorText) { + this.errorText = errorText; + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/UsersView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/UsersView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,17 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.User; + +import java.util.List; + +public class UsersView { + private List users; + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/VersionEditView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionEditView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,42 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.Project; +import de.uapcore.lightpit.entities.Version; +import de.uapcore.lightpit.entities.VersionStatus; + +import java.util.Collections; +import java.util.List; + +public class VersionEditView { + private final Version version; + private List projects = Collections.emptyList(); + private String errorText; + + public VersionEditView(Version version) { + this.version = version; + } + + public Version getVersion() { + return version; + } + + public List getProjects() { + return projects; + } + + public void setProjects(List projects) { + this.projects = projects; + } + + public VersionStatus[] getVersionStatus() { + return VersionStatus.values(); + } + + public String getErrorText() { + return errorText; + } + + public void setErrorText(String errorText) { + this.errorText = errorText; + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/VersionInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionInfo.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,82 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.Issue; +import de.uapcore.lightpit.entities.IssueSummary; +import de.uapcore.lightpit.entities.Version; + +import java.util.ArrayList; +import java.util.List; + +public class VersionInfo { + + private final Version version; + + private final IssueSummary reportedTotal = new IssueSummary(); + private final IssueSummary scheduledTotal = new IssueSummary(); + private final IssueSummary resolvedTotal = new IssueSummary(); + + private final List reported = new ArrayList<>(); + private final List scheduled = new ArrayList<>(); + private final List resolved = new ArrayList<>(); + + public VersionInfo(Version version) { + this.version = version; + } + + public Version getVersion() { + return version; + } + + public void addReported(Issue issue) { + reportedTotal.add(issue); + reported.add(issue); + } + + public void addScheduled(Issue issue) { + scheduledTotal.add(issue); + scheduled.add(issue); + } + + public void addResolved(Issue issue) { + resolvedTotal.add(issue); + resolved.add(issue); + } + + public IssueSummary getReportedTotal() { + return reportedTotal; + } + + public IssueSummary getScheduledTotal() { + return scheduledTotal; + } + + public IssueSummary getResolvedTotal() { + return resolvedTotal; + } + + public List getReported() { + return reported; + } + + public List getScheduled() { + return scheduled; + } + + public List getResolved() { + return resolved; + } + + public void collectIssues(List issues) { + for (Issue issue : issues) { + if (issue.getAffectedVersions().contains(version)) { + addReported(issue); + } + if (issue.getScheduledVersions().contains(version)) { + addScheduled(issue); + } + if (issue.getResolvedVersions().contains(version)) { + addResolved(issue); + } + } + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/java/de/uapcore/lightpit/viewmodel/VersionView.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/viewmodel/VersionView.java Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,23 @@ +package de.uapcore.lightpit.viewmodel; + +import de.uapcore.lightpit.entities.Issue; +import de.uapcore.lightpit.entities.Version; + +import java.util.List; + +public class VersionView { + + private final VersionInfo versionInfo; + + public VersionView(Version version) { + this.versionInfo = new VersionInfo(version); + } + + public VersionInfo getVersionInfo() { + return versionInfo; + } + + public void setIssues(List issues) { + versionInfo.collectIssues(issues); + } +} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/resources/localization/projects.properties --- a/src/main/resources/localization/projects.properties Sat May 30 18:12:38 2020 +0200 +++ b/src/main/resources/localization/projects.properties Mon Jun 01 14:46:58 2020 +0200 @@ -26,7 +26,7 @@ button.create=New Project button.version.create=New Version button.issue.create=New Issue -button.issue.list=Show Issues +button.issue.all=All Issues button.stats.hidezeros=Reduced View button.stats.showzeros=Full View @@ -39,9 +39,18 @@ description=Description repoUrl=Repository owner=Project Lead +version.latest=Latest Version +version.next=Next Version + +progress=Overall Progress + issues.open=Open issues.active=In Progress issues.done=Done +issues.total=Total +issues.reported=Reported Issues +issues.scheduled=Scheduled Issues +issues.resolved=Resolved Issues version.project=Project version.name=Version @@ -59,11 +68,8 @@ version.status.LTS=LTS version.status.Deprecated=Deprecated -version.statistics.affected=Affected by Issues -version.statistics.scheduled=Scheduled Issues -version.statistics.resolved=Resolved Issues -version.statistics.total=Total +issue.without-version=Issues w/o Assigned Version issue.project=Project issue.subject=Subject issue.description=Description diff -r 3d16ad54b3dc -r 0a658e53177c src/main/resources/localization/projects_de.properties --- a/src/main/resources/localization/projects_de.properties Sat May 30 18:12:38 2020 +0200 +++ b/src/main/resources/localization/projects_de.properties Mon Jun 01 14:46:58 2020 +0200 @@ -26,7 +26,7 @@ button.create=Neues Projekt button.version.create=Neue Version button.issue.create=Neuer Vorgang -button.issue.list=Vorg\u00e4nge +button.issue.all=Alle Vorg\u00e4nge button.stats.hidezeros=Reduzierte Ansicht button.stats.showzeros=Komplettansicht @@ -39,9 +39,18 @@ description=Beschreibung repoUrl=Repository owner=Projektleitung +version.latest=Neuste Version +version.next=N\u00e4chste Version + +progress=Gesamtfortschritt + issues.open=Offen issues.active=In Arbeit issues.done=Erledigt +issues.reported=Er\u00f6ffnete Vorg\u00e4nge +issues.scheduled=Geplante Vorg\u00e4nge +issues.resolved=Gel\u00f6ste Vorg\u00e4nge +issues.total=Summe version.project=Projekt version.name=Version @@ -59,11 +68,7 @@ version.status.LTS=Langzeitsupport version.status.Deprecated=Veraltet -version.statistics.affected=Betroffen von Vorg\u00e4ngen -version.statistics.scheduled=Geplante Vorg\u00e4nge -version.statistics.resolved=Gel\u00f6ste Vorg\u00e4nge -version.statistics.total=Summe - +issue.without-version=Vorg\u00e4nge ohne Version issue.project=Projekt issue.subject=Thema issue.description=Beschreibung diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/issue-form.jsp --- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -28,12 +28,9 @@ <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - - - - - - + + +
@@ -45,26 +42,28 @@ @@ -103,7 +102,7 @@
- - - - - - - + + + + + + + + +
- +
- + ./projects/issues/?pid=${issue.project.id} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/issues.jsp --- a/src/main/webapp/WEB-INF/jsp/issues.jsp Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/issues.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -28,60 +28,26 @@ <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - + + + +<%@include file="../jspf/project-header.jsp"%> + + +

+ - + +

+
+ + +
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - -
+ +<%@include file="../jspf/issue-list.jsp"%> \ No newline at end of file diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/language.jsp --- a/src/main/webapp/WEB-INF/jsp/language.jsp Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/language.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -25,23 +25,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --%> <%@page pageEncoding="UTF-8" %> -<%@page import="de.uapcore.lightpit.Constants" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - - - - + - + diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/project-details.jsp --- a/src/main/webapp/WEB-INF/jsp/project-details.jsp Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/project-details.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -28,66 +28,46 @@ <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - - - - - - - - + -
-
-
:
-
-
:
-
-
-
-
:
-
- -
-
:
-
- - "> - -
-
-
+ +<%@include file="../jspf/project-header.jsp"%>
- - - - - - - - +
-
- +

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

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

- - - + - + (open)

-

- - <%@include file="../jspf/version-stats.jsp" %> +

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

- - <%@include file="../jspf/version-stats.jsp" %> +

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

- - <%@include file="../jspf/version-stats.jsp" %> -
-
+

+ + + <%@include file="../jspf/issue-summary.jsp"%> +
\ No newline at end of file diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/projects.jsp --- a/src/main/webapp/WEB-INF/jsp/projects.jsp Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/projects.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -25,15 +25,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --%> <%@page pageEncoding="UTF-8" %> -<%@page import="de.uapcore.lightpit.modules.ProjectsModule" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - + - - - +
@@ -43,30 +40,35 @@ - + - - - + + + + + - - - + + + + + - + + - + - - - + + + + + diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/site.jsp --- a/src/main/webapp/WEB-INF/jsp/site.jsp Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -26,7 +26,6 @@ --%> <%@page contentType="text/html" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %> <%@page import="de.uapcore.lightpit.Constants" %> -<%@page import="de.uapcore.lightpit.modules.ProjectsModule" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> @@ -60,9 +59,6 @@ -<%-- Selected project, if any --%> - - <%-- Load resource bundles --%> diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/user-form.jsp --- a/src/main/webapp/WEB-INF/jsp/user-form.jsp Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/user-form.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -28,7 +28,8 @@ <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - + +
@@ -75,9 +77,19 @@ value="${project.repoUrl}"/> ${project.openIssues}${project.activeIssues}${project.doneIssues} + + + + + + + + ${projectInfo.issueSummary.open}${projectInfo.issueSummary.active}${projectInfo.issueSummary.done}
diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/users.jsp --- a/src/main/webapp/WEB-INF/jsp/users.jsp Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/users.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -28,9 +28,9 @@ <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - + - +
@@ -40,7 +40,7 @@ - +
@@ -49,7 +49,7 @@ - + diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/version-form.jsp --- a/src/main/webapp/WEB-INF/jsp/version-form.jsp Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/WEB-INF/jsp/version-form.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -28,14 +28,8 @@ <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> - - - - - - - - + +
@@ -47,19 +41,21 @@ @@ -70,7 +66,7 @@
- - - - - - - + + + + + + + + +
- + ./projects/view?pid=${version.project.id} diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jsp/version.jsp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jsp/version.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,65 @@ +<%-- +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + +Copyright 2018 Mike Becker. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--%> +<%@page pageEncoding="UTF-8" %> +<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> + + + + + +<%@include file="../jspf/project-header.jsp"%> + + +
+ +
+ +

+ - + +

+ +

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

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

+ + +<%@include file="../jspf/issue-summary.jsp"%> +<%@include file="../jspf/issue-list.jsp"%> + diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jspf/issue-list.jsp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jspf/issue-list.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,53 @@ +<%-- +issues: List +--%> + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jspf/issue-summary.jsp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jspf/issue-summary.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,20 @@ +<%-- +summary: IssueSummary +--%> + +
+
+
:
+
+
:
+
+
:
+
+
+
+ +
+
+
+
+
\ No newline at end of file diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jspf/project-header.jsp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jspf/project-header.jsp Mon Jun 01 14:46:58 2020 +0200 @@ -0,0 +1,24 @@ +<%-- +project: Project +--%> +
+
+
:
+
+
:
+
+
+
+
:
+
+ +
+
:
+
+ + "> + +
+
+
diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/WEB-INF/jspf/version-stats.jsp --- a/src/main/webapp/WEB-INF/jspf/version-stats.jsp Sat May 30 18:12:38 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -<%@ taglib uri = "http://java.sun.com/jsp/jstl/functions" prefix = "fn" %> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
${stats.issueCount[categoryIter.index][statusIter.index]}${stats.rowTotals[categoryIter.index]}
${stats.columnTotals[statusIter.index]}${stats.total}
\ No newline at end of file diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/lightpit.css --- a/src/main/webapp/lightpit.css Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/lightpit.css Mon Jun 01 14:46:58 2020 +0200 @@ -117,7 +117,6 @@ } table.datatable { - width: auto; border-style: solid; border-width: 1pt; border-color: silver; diff -r 3d16ad54b3dc -r 0a658e53177c src/main/webapp/projects.css --- a/src/main/webapp/projects.css Sat May 30 18:12:38 2020 +0200 +++ b/src/main/webapp/projects.css Mon Jun 01 14:46:58 2020 +0200 @@ -27,21 +27,12 @@ * */ -#issue-list td { - white-space: nowrap; +.project-attributes, .issue-summary { + display: table; } -#version-stats h2 { - white-space: nowrap; -} - -#version-stats td { - text-align: right; -} - -#project-attributes { +.project-attributes { margin-bottom: 2em; - display: table; } .row { @@ -63,3 +54,28 @@ span.phase-2 { text-decoration: line-through; } + +.issue-progress-bar { + display: flex; + position: relative; + width: 100ex; + height: 2em; + border-style: inset; + border-width: 2pt; + border-color: #6060cc; +} + +.issue-progress-bar .open { + height: 100%; + background: steelblue; +} + +.issue-progress-bar .active { + height: 100%; + background: gold; +} + +.issue-progress-bar .done { + height: 100%; + background: green; +}