adds version selection in issue editor

Sat, 30 May 2020 18:05:06 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 30 May 2020 18:05:06 +0200
changeset 83
24a3596b8f98
parent 82
4ec7f2600c83
child 84
5407d4ea0611

adds version selection in issue editor

src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java file | annotate | diff | comparison | revisions
src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java file | annotate | diff | comparison | revisions
src/main/resources/localization/projects.properties file | annotate | diff | comparison | revisions
src/main/resources/localization/projects_de.properties file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/issue-form.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/version-form.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jspf/version-list.jsp file | annotate | diff | comparison | revisions
     1.1 --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sat May 30 15:28:27 2020 +0200
     1.2 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sat May 30 18:05:06 2020 +0200
     1.3 @@ -39,10 +39,7 @@
     1.4  import javax.servlet.http.HttpServletResponse;
     1.5  import javax.servlet.http.HttpSession;
     1.6  import java.io.IOException;
     1.7 -import java.lang.reflect.Constructor;
     1.8 -import java.lang.reflect.InvocationTargetException;
     1.9 -import java.lang.reflect.Method;
    1.10 -import java.lang.reflect.Modifier;
    1.11 +import java.lang.reflect.*;
    1.12  import java.sql.Connection;
    1.13  import java.sql.SQLException;
    1.14  import java.util.*;
    1.15 @@ -280,30 +277,44 @@
    1.16       * @return the parameter value or an empty optional, if no parameter with the specified name was found
    1.17       */
    1.18      protected <T> Optional<T> getParameter(HttpServletRequest req, Class<T> clazz, String name) {
    1.19 -        final String paramValue = req.getParameter(name);
    1.20 -        if (paramValue == null) return Optional.empty();
    1.21 -        if (clazz.equals(Boolean.class)) {
    1.22 -            if (paramValue.toLowerCase().equals("false") || paramValue.equals("0")) {
    1.23 -                return Optional.of((T)Boolean.FALSE);
    1.24 -            } else {
    1.25 -                return Optional.of((T)Boolean.TRUE);
    1.26 +        if (clazz.isArray()) {
    1.27 +            final String[] paramValues = req.getParameterValues(name);
    1.28 +            int len = paramValues == null ? 0 : paramValues.length;
    1.29 +            final var array = (T) Array.newInstance(clazz.getComponentType(), len);
    1.30 +            for (int i = 0 ; i < len ; i++) {
    1.31 +                try {
    1.32 +                    final Constructor<?> ctor = clazz.getComponentType().getConstructor(String.class);
    1.33 +                    Array.set(array, i, ctor.newInstance(paramValues[i]));
    1.34 +                } catch (ReflectiveOperationException e) {
    1.35 +                    throw new RuntimeException(e);
    1.36 +                }
    1.37 +            }
    1.38 +            return Optional.of(array);
    1.39 +        } else {
    1.40 +            final String paramValue = req.getParameter(name);
    1.41 +            if (paramValue == null) return Optional.empty();
    1.42 +            if (clazz.equals(Boolean.class)) {
    1.43 +                if (paramValue.toLowerCase().equals("false") || paramValue.equals("0")) {
    1.44 +                    return Optional.of((T) Boolean.FALSE);
    1.45 +                } else {
    1.46 +                    return Optional.of((T) Boolean.TRUE);
    1.47 +                }
    1.48 +            }
    1.49 +            if (clazz.equals(String.class)) return Optional.of((T) paramValue);
    1.50 +            if (java.sql.Date.class.isAssignableFrom(clazz)) {
    1.51 +                try {
    1.52 +                    return Optional.of((T) java.sql.Date.valueOf(paramValue));
    1.53 +                } catch (IllegalArgumentException ex) {
    1.54 +                    return Optional.empty();
    1.55 +                }
    1.56 +            }
    1.57 +            try {
    1.58 +                final Constructor<T> ctor = clazz.getConstructor(String.class);
    1.59 +                return Optional.of(ctor.newInstance(paramValue));
    1.60 +            } catch (ReflectiveOperationException e) {
    1.61 +                throw new RuntimeException(e);
    1.62              }
    1.63          }
    1.64 -        if (clazz.equals(String.class)) return Optional.of((T) paramValue);
    1.65 -        if (java.sql.Date.class.isAssignableFrom(clazz)) {
    1.66 -            try {
    1.67 -                return Optional.of((T)java.sql.Date.valueOf(paramValue));
    1.68 -            } catch (IllegalArgumentException ex) {
    1.69 -                return Optional.empty();
    1.70 -            }
    1.71 -        }
    1.72 -        try {
    1.73 -            final Constructor<T> ctor = clazz.getConstructor(String.class);
    1.74 -            return Optional.of(ctor.newInstance(paramValue));
    1.75 -        } catch (ReflectiveOperationException e) {
    1.76 -            throw new RuntimeException(e);
    1.77 -        }
    1.78 -
    1.79      }
    1.80  
    1.81      /**
     2.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java	Sat May 30 15:28:27 2020 +0200
     2.2 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGIssueDao.java	Sat May 30 18:05:06 2020 +0200
     2.3 @@ -43,22 +43,27 @@
     2.4  
     2.5  public final class PGIssueDao implements IssueDao {
     2.6  
     2.7 -    private final PreparedStatement insert, update, list, find, affectedVersions, scheduledVersions, resolvedVersions;
     2.8 +    private final PreparedStatement insert, update, list, find;
     2.9 +    private final PreparedStatement affectedVersions, scheduledVersions, resolvedVersions;
    2.10 +    private final PreparedStatement clearAffected, clearScheduled, clearResolved;
    2.11 +    private final PreparedStatement insertAffected, insertScheduled, insertResolved;
    2.12  
    2.13      public PGIssueDao(Connection connection) throws SQLException {
    2.14          list = connection.prepareStatement(
    2.15 -                "select issueid, project, status, category, subject, description, " +
    2.16 +                "select issueid, project, p.name as projectname, status, category, subject, i.description, " +
    2.17                          "userid, username, givenname, lastname, mail, " +
    2.18                          "created, updated, eta " +
    2.19 -                        "from lpit_issue " +
    2.20 +                        "from lpit_issue i " +
    2.21 +                        "left join lpit_project p on project = projectid " +
    2.22                          "left join lpit_user on userid = assignee " +
    2.23                          "where project = ? ");
    2.24  
    2.25          find = connection.prepareStatement(
    2.26 -                "select issueid, project, status, category, subject, description, " +
    2.27 +                "select issueid, project, p.name as projectname, status, category, subject, i.description, " +
    2.28                          "userid, username, givenname, lastname, mail, " +
    2.29                          "created, updated, eta " +
    2.30 -                        "from lpit_issue " +
    2.31 +                        "from lpit_issue i " +
    2.32 +                        "left join lpit_project p on project = projectid " +
    2.33                          "left join lpit_user on userid = assignee " +
    2.34                          "where issueid = ? ");
    2.35  
    2.36 @@ -72,25 +77,31 @@
    2.37          );
    2.38  
    2.39          affectedVersions = connection.prepareStatement(
    2.40 -                "select v.versionid, v.name, v.status, v.ordinal " +
    2.41 -                        "from lpit_version v join lpit_issue_affected_version using (versionid) " +
    2.42 +                "select versionid, name, status, ordinal " +
    2.43 +                        "from lpit_version join lpit_issue_affected_version using (versionid) " +
    2.44                          "where issueid = ? " +
    2.45 -                        "order by v.ordinal, v.name"
    2.46 +                        "order by ordinal, name"
    2.47          );
    2.48 +        clearAffected = connection.prepareStatement("delete from lpit_issue_affected_version where issueid = ?");
    2.49 +        insertAffected = connection.prepareStatement("insert into lpit_issue_affected_version (issueid, versionid) values (?,?)");
    2.50  
    2.51          scheduledVersions = connection.prepareStatement(
    2.52 -                "select v.versionid, v.name, v.status, v.ordinal " +
    2.53 -                        "from lpit_version v join lpit_issue_scheduled_version using (versionid) " +
    2.54 +                "select versionid, name, status, ordinal " +
    2.55 +                        "from lpit_version join lpit_issue_scheduled_version using (versionid) " +
    2.56                          "where issueid = ? " +
    2.57 -                        "order by v.ordinal, v.name"
    2.58 +                        "order by ordinal, name"
    2.59          );
    2.60 +        clearScheduled = connection.prepareStatement("delete from lpit_issue_scheduled_version where issueid = ?");
    2.61 +        insertScheduled = connection.prepareStatement("insert into lpit_issue_scheduled_version (issueid, versionid) values (?,?)");
    2.62  
    2.63          resolvedVersions = connection.prepareStatement(
    2.64 -                "select v.versionid, v.name, v.status, v.ordinal " +
    2.65 +                "select versionid, name, status, ordinal " +
    2.66                          "from lpit_version v join lpit_issue_resolved_version using (versionid) " +
    2.67                          "where issueid = ? " +
    2.68 -                        "order by v.ordinal, v.name"
    2.69 +                        "order by ordinal, name"
    2.70          );
    2.71 +        clearResolved = connection.prepareStatement("delete from lpit_issue_resolved_version where issueid = ?");
    2.72 +        insertResolved = connection.prepareStatement("insert into lpit_issue_resolved_version (issueid, versionid) values (?,?)");
    2.73      }
    2.74  
    2.75      private User obtainAssignee(ResultSet result) throws SQLException {
    2.76 @@ -109,6 +120,7 @@
    2.77  
    2.78      private Issue mapColumns(ResultSet result) throws SQLException {
    2.79          final var project = new Project(result.getInt("project"));
    2.80 +        project.setName(result.getString("projectname"));
    2.81          final var issue = new Issue(result.getInt("issueid"), project);
    2.82          issue.setStatus(IssueStatus.valueOf(result.getString("status")));
    2.83          issue.setCategory(IssueCategory.valueOf(result.getString("category")));
    2.84 @@ -122,13 +134,37 @@
    2.85      }
    2.86  
    2.87      private Version mapVersion(ResultSet result, Project project) throws SQLException {
    2.88 -        final var version = new Version(result.getInt("v.versionid"), project);
    2.89 -        version.setName(result.getString("v.name"));
    2.90 -        version.setOrdinal(result.getInt("v.ordinal"));
    2.91 -        version.setStatus(VersionStatus.valueOf(result.getString("v.status")));
    2.92 +        final var version = new Version(result.getInt("versionid"), project);
    2.93 +        version.setName(result.getString("name"));
    2.94 +        version.setOrdinal(result.getInt("ordinal"));
    2.95 +        version.setStatus(VersionStatus.valueOf(result.getString("status")));
    2.96          return version;
    2.97      }
    2.98  
    2.99 +    private void updateVersionLists(Issue instance) throws SQLException {
   2.100 +        clearAffected.setInt(1, instance.getId());
   2.101 +        clearScheduled.setInt(1, instance.getId());
   2.102 +        clearResolved.setInt(1, instance.getId());
   2.103 +        insertAffected.setInt(1, instance.getId());
   2.104 +        insertScheduled.setInt(1, instance.getId());
   2.105 +        insertResolved.setInt(1, instance.getId());
   2.106 +        clearAffected.executeUpdate();
   2.107 +        clearScheduled.executeUpdate();
   2.108 +        clearResolved.executeUpdate();
   2.109 +        for (Version v : instance.getAffectedVersions()) {
   2.110 +            insertAffected.setInt(2, v.getId());
   2.111 +            insertAffected.executeUpdate();
   2.112 +        }
   2.113 +        for (Version v : instance.getScheduledVersions()) {
   2.114 +            insertScheduled.setInt(2, v.getId());
   2.115 +            insertScheduled.executeUpdate();
   2.116 +        }
   2.117 +        for (Version v : instance.getResolvedVersions()) {
   2.118 +            insertResolved.setInt(2, v.getId());
   2.119 +            insertResolved.executeUpdate();
   2.120 +        }
   2.121 +    }
   2.122 +
   2.123      @Override
   2.124      public void save(Issue instance) throws SQLException {
   2.125          Objects.requireNonNull(instance.getSubject());
   2.126 @@ -144,6 +180,7 @@
   2.127          final var rs = insert.executeQuery();
   2.128          rs.next();
   2.129          instance.setId(rs.getInt(1));
   2.130 +        updateVersionLists(instance);
   2.131      }
   2.132  
   2.133      @Override
   2.134 @@ -157,7 +194,13 @@
   2.135          setForeignKeyOrNull(update, 5, instance.getAssignee(), User::getId);
   2.136          setDateOrNull(update, 6, instance.getEta());
   2.137          update.setInt(7, instance.getId());
   2.138 -        return update.executeUpdate() > 0;
   2.139 +        boolean success = update.executeUpdate() > 0;
   2.140 +        if (success) {
   2.141 +            updateVersionLists(instance);
   2.142 +            return true;
   2.143 +        } else {
   2.144 +            return false;
   2.145 +        }
   2.146      }
   2.147  
   2.148      @Override
     3.1 --- a/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java	Sat May 30 15:28:27 2020 +0200
     3.2 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGVersionDao.java	Sat May 30 18:05:06 2020 +0200
     3.3 @@ -46,14 +46,16 @@
     3.4  
     3.5      public PGVersionDao(Connection connection) throws SQLException {
     3.6          list = connection.prepareStatement(
     3.7 -                "select versionid, project, name, ordinal, status " +
     3.8 -                        "from lpit_version " +
     3.9 +                "select versionid, project, p.name as projectname, v.name, ordinal, status " +
    3.10 +                        "from lpit_version v " +
    3.11 +                        "join lpit_project p on v.project = p.projectid " +
    3.12                          "where project = ? " +
    3.13 -                        "order by ordinal desc, lower(name) desc");
    3.14 +                        "order by ordinal desc, lower(v.name) desc");
    3.15  
    3.16          find = connection.prepareStatement(
    3.17 -                "select versionid, project, name, ordinal, status " +
    3.18 -                        "from lpit_version " +
    3.19 +                "select versionid, project, p.name as projectname, v.name, ordinal, status " +
    3.20 +                        "from lpit_version  v " +
    3.21 +                        "join lpit_project p on v.project = p.projectid " +
    3.22                          "where versionid = ?");
    3.23  
    3.24          insert = connection.prepareStatement(
    3.25 @@ -88,6 +90,7 @@
    3.26  
    3.27      private Version mapColumns(ResultSet result) throws SQLException {
    3.28          final var project = new Project(result.getInt("project"));
    3.29 +        project.setName(result.getString("projectname"));
    3.30          final var version = new Version(result.getInt("versionid"), project);
    3.31          version.setName(result.getString("name"));
    3.32          version.setOrdinal(result.getInt("ordinal"));
     4.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Sat May 30 15:28:27 2020 +0200
     4.2 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java	Sat May 30 18:05:06 2020 +0200
     4.3 @@ -42,10 +42,9 @@
     4.4  import java.io.IOException;
     4.5  import java.sql.Date;
     4.6  import java.sql.SQLException;
     4.7 -import java.util.ArrayList;
     4.8 -import java.util.List;
     4.9 -import java.util.NoSuchElementException;
    4.10 -import java.util.Objects;
    4.11 +import java.util.*;
    4.12 +import java.util.stream.Collectors;
    4.13 +import java.util.stream.Stream;
    4.14  
    4.15  import static de.uapcore.lightpit.Functions.fqn;
    4.16  
    4.17 @@ -109,14 +108,14 @@
    4.18          }
    4.19  
    4.20          void selectVersion(Version version) {
    4.21 -            if (!Objects.equals(project, version.getProject())) throw new AssertionError("Nice, you implemented a bug!");
    4.22 +            this.project = version.getProject();
    4.23              this.version = version;
    4.24              this.issue = null;
    4.25              updateAttributes();
    4.26          }
    4.27  
    4.28          void selectIssue(Issue issue) {
    4.29 -            if (!Objects.equals(issue.getProject(), project)) throw new AssertionError("Nice, you implemented a bug!");
    4.30 +            this.project = issue.getProject();
    4.31              this.issue = issue;
    4.32              this.version = null;
    4.33              updateAttributes();
    4.34 @@ -375,7 +374,16 @@
    4.35      }
    4.36  
    4.37      private void configureEditIssueForm(HttpServletRequest req, DataAccessObjects dao, SessionSelection selection) throws SQLException {
    4.38 -        req.setAttribute("projects", dao.getProjectDao().list());
    4.39 +
    4.40 +        if (selection.issue.getProject() == null || selection.issue.getProject().getId() < 0) {
    4.41 +            req.setAttribute("projects", dao.getProjectDao().list());
    4.42 +            req.setAttribute("versions", Collections.<Version>emptyList());
    4.43 +        } else {
    4.44 +            req.setAttribute("projects", Collections.<Project>emptyList());
    4.45 +            req.setAttribute("versions", dao.getVersionDao().list(selection.issue.getProject()));
    4.46 +        }
    4.47 +
    4.48 +        dao.getIssueDao().joinVersionInformation(selection.issue);
    4.49          req.setAttribute("issue", selection.issue);
    4.50          req.setAttribute("issueStatusEnum", IssueStatus.values());
    4.51          req.setAttribute("issueCategoryEnum", IssueCategory.values());
    4.52 @@ -428,6 +436,23 @@
    4.53              ).ifPresent(issue::setAssignee);
    4.54              getParameter(req, String.class, "description").ifPresent(issue::setDescription);
    4.55              getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
    4.56 +
    4.57 +            getParameter(req, Integer[].class, "affected")
    4.58 +                    .map(Stream::of)
    4.59 +                    .map(stream ->
    4.60 +                        stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList())
    4.61 +                    ).ifPresent(issue::setAffectedVersions);
    4.62 +            getParameter(req, Integer[].class, "scheduled")
    4.63 +                    .map(Stream::of)
    4.64 +                    .map(stream ->
    4.65 +                            stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList())
    4.66 +                    ).ifPresent(issue::setScheduledVersions);
    4.67 +            getParameter(req, Integer[].class, "resolved")
    4.68 +                    .map(Stream::of)
    4.69 +                    .map(stream ->
    4.70 +                            stream.map(id -> new Version(id, sessionSelection.project)).collect(Collectors.toList())
    4.71 +                    ).ifPresent(issue::setResolvedVersions);
    4.72 +
    4.73              dao.getIssueDao().saveOrUpdate(issue);
    4.74  
    4.75              // specifying the issue parameter keeps the edited issue as breadcrumb
     5.1 --- a/src/main/resources/localization/projects.properties	Sat May 30 15:28:27 2020 +0200
     5.2 +++ b/src/main/resources/localization/projects.properties	Sat May 30 18:05:06 2020 +0200
     5.3 @@ -46,7 +46,7 @@
     5.4  version.project=Project
     5.5  version.name=Version
     5.6  version.status=Status
     5.7 -version.ordinal=Custom Ordering
     5.8 +version.ordinal=Ordering
     5.9  tooltip.ordinal=Use to override lexicographic ordering.
    5.10  
    5.11  placeholder.null-owner=Unassigned
    5.12 @@ -68,11 +68,8 @@
    5.13  issue.subject=Subject
    5.14  issue.description=Description
    5.15  issue.assignee=Assignee
    5.16 -issue.affected-version=Affected Version
    5.17  issue.affected-versions=Affected Versions
    5.18 -issue.scheduled-version=Scheduled for Version
    5.19  issue.scheduled-versions=Scheduled for Versions
    5.20 -issue.resolved-version=Resolved in Version
    5.21  issue.resolved-versions=Resolved in Versions
    5.22  issue.category=Category
    5.23  issue.status=Status
     6.1 --- a/src/main/resources/localization/projects_de.properties	Sat May 30 15:28:27 2020 +0200
     6.2 +++ b/src/main/resources/localization/projects_de.properties	Sat May 30 18:05:06 2020 +0200
     6.3 @@ -68,11 +68,8 @@
     6.4  issue.subject=Thema
     6.5  issue.description=Beschreibung
     6.6  issue.assignee=Zugewiesen
     6.7 -issue.affected-version=Betroffene Version
     6.8  issue.affected-versions=Betroffene Versionen
     6.9 -issue.scheduled-version=Zielversion
    6.10  issue.scheduled-versions=Zielversionen
    6.11 -issue.resolved-version=Gel\u00f6st in Version
    6.12  issue.resolved-versions=Gel\u00f6st in Versionen
    6.13  issue.category=Kategorie
    6.14  issue.status=Status
     7.1 --- a/src/main/webapp/WEB-INF/jsp/issue-form.jsp	Sat May 30 15:28:27 2020 +0200
     7.2 +++ b/src/main/webapp/WEB-INF/jsp/issue-form.jsp	Sat May 30 18:05:06 2020 +0200
     7.3 @@ -29,6 +29,7 @@
     7.4  <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
     7.5  
     7.6  <jsp:useBean id="projects" type="java.util.List<de.uapcore.lightpit.entities.Project>" scope="request" />
     7.7 +<jsp:useBean id="versions" type="java.util.List<de.uapcore.lightpit.entities.Version>" scope="request" />
     7.8  <jsp:useBean id="issue" type="de.uapcore.lightpit.entities.Issue" scope="request"/>
     7.9  <jsp:useBean id="issueStatusEnum" type="de.uapcore.lightpit.entities.IssueStatus[]" scope="request"/>
    7.10  <jsp:useBean id="issueCategoryEnum" type="de.uapcore.lightpit.entities.IssueCategory[]" scope="request"/>
    7.11 @@ -44,13 +45,19 @@
    7.12          <tr>
    7.13              <th><fmt:message key="issue.project"/></th>
    7.14              <td>
    7.15 +                <c:if test="${issue.project.id ge 0}">
    7.16 +                    <c:out value="${issue.project.name}" />
    7.17 +                    <input type="hidden" name="pid" value="${issue.project.id}" />
    7.18 +                </c:if>
    7.19 +                <c:if test="${empty issue.project or issue.project.id lt 0}">
    7.20                  <select name="pid" required>
    7.21                      <c:forEach var="project" items="${projects}">
    7.22 -                        <option value="${project.id}" <c:if test="${project eq issue.project}">selected</c:if> >
    7.23 +                        <option value="${project.id}">
    7.24                              <c:out value="${project.name}" />
    7.25                          </option>
    7.26                      </c:forEach>
    7.27                  </select>
    7.28 +                </c:if>
    7.29              </td>
    7.30          </tr>
    7.31          <tr>
    7.32 @@ -104,45 +111,32 @@
    7.33                  </select>
    7.34              </td>
    7.35          </tr>
    7.36 +        <c:if test="${issue.project.id ge 0}">
    7.37          <tr>
    7.38 -            <th>
    7.39 -                <c:choose>
    7.40 -                    <c:when test="${issue.affectedVersions.size() gt 1}">
    7.41 -                        <fmt:message key="issue.affected-versions"/>
    7.42 -                    </c:when>
    7.43 -                    <c:otherwise>
    7.44 -                        <fmt:message key="issue.affected-version"/>
    7.45 -                    </c:otherwise>
    7.46 -                </c:choose>
    7.47 -            </th>
    7.48 -            <td>TODO</td>
    7.49 +            <th class="vtop"><fmt:message key="issue.affected-versions"/></th>
    7.50 +            <td>
    7.51 +                <c:set var="fieldname" value="affected"/>
    7.52 +                <c:set var="data" value="${issue.affectedVersions}" />
    7.53 +                <%@include file="../jspf/version-list.jsp"%>
    7.54 +            </td>
    7.55          </tr>
    7.56          <tr>
    7.57 -            <th>
    7.58 -                <c:choose>
    7.59 -                    <c:when test="${issue.scheduledVersions.size() gt 1}">
    7.60 -                        <fmt:message key="issue.scheduled-versions"/>
    7.61 -                    </c:when>
    7.62 -                    <c:otherwise>
    7.63 -                        <fmt:message key="issue.scheduled-version"/>
    7.64 -                    </c:otherwise>
    7.65 -                </c:choose>
    7.66 -            </th>
    7.67 -            <td>TODO</td>
    7.68 +            <th class="vtop"><fmt:message key="issue.scheduled-versions"/></th>
    7.69 +            <td>
    7.70 +                <c:set var="fieldname" value="scheduled"/>
    7.71 +                <c:set var="data" value="${issue.scheduledVersions}" />
    7.72 +                <%@include file="../jspf/version-list.jsp"%>
    7.73 +            </td>
    7.74          </tr>
    7.75          <tr>
    7.76 -            <th>
    7.77 -                <c:choose>
    7.78 -                    <c:when test="${issue.resolvedVersions.size() gt 1}">
    7.79 -                        <fmt:message key="issue.resolved-versions"/>
    7.80 -                    </c:when>
    7.81 -                    <c:otherwise>
    7.82 -                        <fmt:message key="issue.resolved-version"/>
    7.83 -                    </c:otherwise>
    7.84 -                </c:choose>
    7.85 -            </th>
    7.86 -            <td>TODO</td>
    7.87 +            <th class="vtop"><fmt:message key="issue.resolved-versions"/></th>
    7.88 +            <td>
    7.89 +                <c:set var="fieldname" value="resolved"/>
    7.90 +                <c:set var="data" value="${issue.resolvedVersions}" />
    7.91 +                <%@include file="../jspf/version-list.jsp"%>
    7.92 +            </td>
    7.93          </tr>
    7.94 +        </c:if>
    7.95          <tr>
    7.96              <th><fmt:message key="issue.eta"/></th>
    7.97              <td><input name="eta" type="date" value="<fmt:formatDate value="${issue.eta}" pattern="YYYY-MM-dd" />" /> </td>
     8.1 --- a/src/main/webapp/WEB-INF/jsp/version-form.jsp	Sat May 30 15:28:27 2020 +0200
     8.2 +++ b/src/main/webapp/WEB-INF/jsp/version-form.jsp	Sat May 30 18:05:06 2020 +0200
     8.3 @@ -47,13 +47,19 @@
     8.4          <tr>
     8.5              <th><fmt:message key="version.project"/></th>
     8.6              <td>
     8.7 -                <select name="pid" required>
     8.8 -                    <c:forEach var="project" items="${projects}">
     8.9 -                        <option value="${project.id}" <c:if test="${project eq version.project}">selected</c:if> >
    8.10 -                            <c:out value="${project.name}" />
    8.11 -                        </option>
    8.12 -                    </c:forEach>
    8.13 -                </select>
    8.14 +                <c:if test="${version.project.id ge 0}">
    8.15 +                    <c:out value="${version.project.name}" />
    8.16 +                    <input type="hidden" name="pid" value="${version.project.id}" />
    8.17 +                </c:if>
    8.18 +                <c:if test="${empty version.project or version.project.id lt 0}">
    8.19 +                    <select name="pid" required>
    8.20 +                        <c:forEach var="project" items="${projects}">
    8.21 +                            <option value="${project.id}">
    8.22 +                                <c:out value="${project.name}" />
    8.23 +                            </option>
    8.24 +                        </c:forEach>
    8.25 +                    </select>
    8.26 +                </c:if>
    8.27              </td>
    8.28          </tr>
    8.29          <tr>
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/main/webapp/WEB-INF/jspf/version-list.jsp	Sat May 30 18:05:06 2020 +0200
     9.3 @@ -0,0 +1,13 @@
     9.4 +<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
     9.5 +
     9.6 +<select name="${fieldname}" multiple>
     9.7 +    <c:forEach var="version" items="${versions}">
     9.8 +        <option value="${version.id}"
     9.9 +                <c:forEach var="v" items="${data}">
    9.10 +                    <c:if test="${v eq version}">selected</c:if>
    9.11 +                </c:forEach>
    9.12 +        >
    9.13 +            <c:out value="${version.name}" />
    9.14 +        </option>
    9.15 +    </c:forEach>
    9.16 +</select>

mercurial