# HG changeset patch # User Mike Becker # Date 1629291465 -7200 # Node ID 87328572e36fad736bbce90c20976fb8fc7536bc # Parent da975b1f188d25212360089cb824ef368fcdd098 #159 adds release and eol dates diff -r da975b1f188d -r 87328572e36f setup/postgres/psql_create_tables.sql --- a/setup/postgres/psql_create_tables.sql Wed Aug 18 12:47:32 2021 +0200 +++ b/setup/postgres/psql_create_tables.sql Wed Aug 18 14:57:45 2021 +0200 @@ -36,7 +36,9 @@ name text not null, node text not null, ordinal integer not null default 0, - status version_status not null default 'Future' + status version_status not null default 'Future', + release date, + eol date ); create unique index lpit_version_node_unique on lpit_version (project, node); diff -r da975b1f188d -r 87328572e36f src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt --- a/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Wed Aug 18 14:57:45 2021 +0200 @@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletResponse import javax.servlet.http.HttpSession import kotlin.math.min +import java.sql.Date as SqlDate typealias MappingMethod = (HttpRequest, DataAccessObject) -> Unit typealias PathParameters = Map @@ -289,3 +290,18 @@ } } +// + +fun dateOptValidator(input: String?): ValidationResult { + return if (input.isNullOrBlank()) { + ValidatedValue(null) + } else { + try { + ValidatedValue(SqlDate.valueOf(input)) + } catch (ignored: IllegalArgumentException) { + ValidationError("validation.date.format") + } + } +} + +// diff -r da975b1f188d -r 87328572e36f src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt --- a/src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/dao/PostgresDataAccessObject.kt Wed Aug 18 14:57:45 2021 +0200 @@ -129,17 +129,19 @@ executeUpdate() } } -// + // // //language=SQL - private val versionQuery = "select versionid, project, name, node, ordinal, status from lpit_version" + private val versionQuery = "select versionid, project, name, node, ordinal, status, release, eol from lpit_version" private fun ResultSet.extractVersion() = Version(getInt("versionid"), getInt("project")).apply { name = getString("name") node = getString("node") ordinal = getInt("ordinal") + release = getDate("release") + eol = getDate("eol") status = getEnum("status") } @@ -171,7 +173,7 @@ from lpit_version v left join issues using (versionid) ) - select v.versionid, project, name, node, ordinal, status, + select v.versionid, project, name, node, ordinal, status, release, eol, ro.total as resolved_open, ra.total as resolved_active, rd.total as resolved_done, ao.total as affected_open, aa.total as affected_active, ad.total as affected_done from lpit_version v @@ -212,13 +214,15 @@ } override fun insertVersion(version: Version) { - withStatement("insert into lpit_version (name, node, ordinal, status, project) values (?, ?, ?, ?::version_status, ?)") { + withStatement("insert into lpit_version (name, node, ordinal, status, project, release, eol) values (?, ?, ?, ?::version_status, ?, ?, ?)") { with(version) { setStringSafe(1, name) setStringSafe(2, node) setInt(3, ordinal) setEnum(4, status) setInt(5, version.projectid) + setDateOrNull(6, version.release) + setDateOrNull(7, version.eol) } executeUpdate() } @@ -226,18 +230,20 @@ } override fun updateVersion(version: Version) { - withStatement("update lpit_version set name = ?, node = ?, ordinal = ?, status = ?::version_status where versionid = ?") { + withStatement("update lpit_version set name = ?, node = ?, ordinal = ?, status = ?::version_status, release=?,eol=? where versionid = ?") { with(version) { setStringSafe(1, name) setStringSafe(2, node) setInt(3, ordinal) setEnum(4, status) - setInt(5, id) + setDateOrNull(5, version.release) + setDateOrNull(6, version.eol) + setInt(7, id) } executeUpdate() } } -// + // // //language=SQL @@ -347,9 +353,9 @@ } } -// + // -// + // //language=SQL private val projectQuery = @@ -441,9 +447,9 @@ } } -// + // -// + // //language=SQL private val issueQuery = @@ -485,19 +491,24 @@ } //language=SQL - fun versionQuery(table: String) = + val queryAffected = """ - select versionid, project, name, status, ordinal, node - from lpit_version join $table using (versionid) - where issueid = ? - order by ordinal, name + $versionQuery join lpit_issue_affected_version using (versionid) + where issueid = ? order by ordinal, name """.trimIndent() - issue.affectedVersions = withStatement(versionQuery("lpit_issue_affected_version")) { + //language=SQL + val queryResolved = + """ + $versionQuery join lpit_issue_resolved_version using (versionid) + where issueid = ? order by ordinal, name + """.trimIndent() + + issue.affectedVersions = withStatement(queryAffected) { setInt(1, issue.id) queryAll { it.extractVersion() } } - issue.resolvedVersions = withStatement(versionQuery("lpit_issue_resolved_version")) { + issue.resolvedVersions = withStatement(queryResolved) { setInt(1, issue.id) queryAll { it.extractVersion() } } @@ -640,9 +651,9 @@ insertVersionInfo(issue.id, issue) } -// + // -// + // private fun ResultSet.extractIssueComment() = IssueComment(getInt("commentid"), getInt("issueid")).apply { @@ -693,5 +704,5 @@ } } } -// + // } \ No newline at end of file diff -r da975b1f188d -r 87328572e36f src/main/kotlin/de/uapcore/lightpit/entities/Version.kt --- a/src/main/kotlin/de/uapcore/lightpit/entities/Version.kt Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/entities/Version.kt Wed Aug 18 14:57:45 2021 +0200 @@ -26,12 +26,22 @@ package de.uapcore.lightpit.entities import de.uapcore.lightpit.types.VersionStatus +import java.sql.Date data class Version(override val id: Int, val projectid: Int) : Entity, HasNode, Comparable { var name: String = "" override var node = name var ordinal = 0 var status = VersionStatus.Future + var release: Date? = null + var eol: Date? = null + + /** + * If this version is deprecated, this gives the [eol] date, otherwise this gives the [release] date. + * Note that a [release] date may be specified for the actual release in which case in should be + * understood as the planned release date. + */ + val releaseOrEolDate: Date? get() = if (status.isEndOfLife) eol else release override fun compareTo(other: Version): Int { val ord = ordinal.compareTo(other.ordinal) diff -r da975b1f188d -r 87328572e36f src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt --- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Wed Aug 18 14:57:45 2021 +0200 @@ -28,6 +28,7 @@ import de.uapcore.lightpit.AbstractServlet import de.uapcore.lightpit.HttpRequest import de.uapcore.lightpit.dao.DataAccessObject +import de.uapcore.lightpit.dateOptValidator import de.uapcore.lightpit.entities.* import de.uapcore.lightpit.types.IssueCategory import de.uapcore.lightpit.types.IssueStatus @@ -329,12 +330,22 @@ node = http.param("node") ?: "" ordinal = http.param("ordinal")?.toIntOrNull() ?: 0 status = http.param("status")?.let(VersionStatus::valueOf) ?: VersionStatus.Future + // TODO: process error messages + eol = http.param("eol", ::dateOptValidator, null, mutableListOf()) + release = http.param("release", ::dateOptValidator, null, mutableListOf()) // intentional defaults if (node.isBlank()) node = name // sanitizing node = sanitizeNode(node) } + // sanitize eol and release date + if (version.status.isEndOfLife) { + if (version.eol == null) version.eol = Date(System.currentTimeMillis()) + } else if (version.status.isReleased) { + if (version.release == null) version.release = Date(System.currentTimeMillis()) + } + if (id < 0) { dao.insertVersion(version) } else { @@ -548,7 +559,8 @@ else -> dao.findUser(it) } } - eta = http.param("eta")?.let { if (it.isBlank()) null else Date.valueOf(it) } + // TODO: process error messages + eta = http.param("eta", ::dateOptValidator, null, mutableListOf()) affectedVersions = http.paramArray("affected") .mapNotNull { param -> param.toIntOrNull()?.let { Version(it, project.id) } } diff -r da975b1f188d -r 87328572e36f src/main/kotlin/de/uapcore/lightpit/types/VersionStatus.kt --- a/src/main/kotlin/de/uapcore/lightpit/types/VersionStatus.kt Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/types/VersionStatus.kt Wed Aug 18 14:57:45 2021 +0200 @@ -29,4 +29,5 @@ Future, Unreleased, Released, LTS, Deprecated; val isReleased get() = this.ordinal >= Released.ordinal + val isEndOfLife get() = this.ordinal >= Deprecated.ordinal } \ No newline at end of file diff -r da975b1f188d -r 87328572e36f src/main/resources/localization/strings.properties --- a/src/main/resources/localization/strings.properties Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/resources/localization/strings.properties Wed Aug 18 14:57:45 2021 +0200 @@ -130,10 +130,13 @@ user.lastname=Last Name user.mail=E-Mail username=User Name +validation.date.format=Illegal date format. validation.username.null=Username is mandatory. validation.username.unique=Username is already taken. +version.eol=End of Life version.latest=Latest Version version.next=Next Version +version.release=Release version.status.Deprecated=Deprecated version.status.Future=Future version.status.LTS=LTS diff -r da975b1f188d -r 87328572e36f src/main/resources/localization/strings_de.properties --- a/src/main/resources/localization/strings_de.properties Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/resources/localization/strings_de.properties Wed Aug 18 14:57:45 2021 +0200 @@ -129,10 +129,13 @@ user.lastname=Nachname user.mail=E-Mail username=Benutzername +validation.date.format=Datumsformat wird nicht unterst\u00fctzt. validation.username.null=Benutzername ist ein Pflichtfeld. validation.username.unique=Der Benutzername wird bereits verwendet. +version.eol=Supportende version.latest=Neuste Version version.next=N\u00e4chste Version +version.release=Release version.status.Deprecated=Veraltet version.status.Future=Geplant version.status.LTS=Langzeitsupport diff -r da975b1f188d -r 87328572e36f src/main/webapp/WEB-INF/changelogs/changelog-de.jspf --- a/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/webapp/WEB-INF/changelogs/changelog-de.jspf Wed Aug 18 14:57:45 2021 +0200 @@ -27,6 +27,7 @@

Version 1.0 (Vorschau)

    +
  • Datum der Veröffentlichung und des Supportendes zu Versionen hinzugefügt.
  • Gesamtanzahl der Kommentare wird nun angezeigt.
  • Spalte für zugewiesener Entwickler zu den Vorgangstabellen hinzugefügt.
  • Installationsanweisungen hinzugefügt.
  • diff -r da975b1f188d -r 87328572e36f src/main/webapp/WEB-INF/changelogs/changelog.jspf --- a/src/main/webapp/WEB-INF/changelogs/changelog.jspf Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/webapp/WEB-INF/changelogs/changelog.jspf Wed Aug 18 14:57:45 2021 +0200 @@ -27,6 +27,7 @@

    Version 1.0 (snapshot)

      +
    • Adds release and end of life dates to versions.
    • Adds the total number of comments to the caption.
    • Adds assignee to the issue overview tables.
    • Adds install instructions.
    • diff -r da975b1f188d -r 87328572e36f src/main/webapp/WEB-INF/jsp/project-details.jsp --- a/src/main/webapp/WEB-INF/jsp/project-details.jsp Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/webapp/WEB-INF/jsp/project-details.jsp Wed Aug 18 14:57:45 2021 +0200 @@ -69,6 +69,9 @@

      - + + () +

      diff -r da975b1f188d -r 87328572e36f src/main/webapp/WEB-INF/jsp/site.jsp --- a/src/main/webapp/WEB-INF/jsp/site.jsp Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Wed Aug 18 14:57:45 2021 +0200 @@ -31,7 +31,7 @@ <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <%-- Version suffix for forcing browsers to update the CSS / JS files --%> - + <%-- Make the base href easily available at request scope --%> diff -r da975b1f188d -r 87328572e36f src/main/webapp/WEB-INF/jsp/version-form.jsp --- a/src/main/webapp/WEB-INF/jsp/version-form.jsp Wed Aug 18 12:47:32 2021 +0200 +++ b/src/main/webapp/WEB-INF/jsp/version-form.jsp Wed Aug 18 14:57:45 2021 +0200 @@ -47,17 +47,17 @@ - - " /> + + " /> "> - - " /> + + " /> - + -