# HG changeset patch # User Mike Becker # Date 1620927069 -7200 # Node ID 94f174d591ab94abe227dd835456193709bd3ff1 # Parent 0a2ad22ac656102100a107041c83e0112314cde5 fixes wrong handling of feeds - only one channel per feed is allowed diff -r 0a2ad22ac656 -r 94f174d591ab src/main/kotlin/de/uapcore/lightpit/Constants.kt --- a/src/main/kotlin/de/uapcore/lightpit/Constants.kt Thu May 13 18:01:56 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/Constants.kt Thu May 13 19:31:09 2021 +0200 @@ -62,6 +62,11 @@ const val REQ_ATTR_BASE_HREF = "base_href" /** + * Key for the request attribute containing the RSS feed href. + */ + const val REQ_ATTR_FEED_HREF = "feed_href" + + /** * Key for the request attribute containing the full path information (servlet path + path info). */ const val REQ_ATTR_PATH = "requestPath" diff -r 0a2ad22ac656 -r 94f174d591ab src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt --- a/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Thu May 13 18:01:56 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Thu May 13 19:31:09 2021 +0200 @@ -86,6 +86,12 @@ request.setAttribute(Constants.REQ_ATTR_REDIRECT_LOCATION, baseHref + value) } + var feedPath = "" + set(value) { + field = value + request.setAttribute(Constants.REQ_ATTR_FEED_HREF, baseHref + value) + } + /** * The view object. * @@ -98,9 +104,21 @@ } /** + * Additional port info, if necessary. + */ + private val portInfo = + if ((request.scheme == "http" && request.serverPort == 80) + || (request.scheme == "https" && request.serverPort == 443) + ) "" else ":${request.serverPort}" + + /** * The base path of this application. */ - val baseHref get() = "${request.scheme}://${request.serverName}:${request.serverPort}${request.contextPath}/" + val baseHref get() = "${request.scheme}://${request.serverName}$portInfo${request.contextPath}/" + + init { + feedPath = "feed/projects.rss" + } private fun String.withExt(ext: String) = if (endsWith(ext)) this else plus(ext) private fun jspPath(name: String) = Constants.JSP_PATH_PREFIX.plus(name).withExt(".jsp") @@ -108,10 +126,15 @@ fun param(name: String): String? = request.getParameter(name) fun paramArray(name: String): Array = request.getParameterValues(name) ?: emptyArray() - fun forward(jsp: String) { + private fun forward(jsp: String) { request.getRequestDispatcher(jspPath(jsp)).forward(request, response) } + fun renderFeed(page: String? = null) { + page?.let { contentPage = it } + forward("feed") + } + fun render(page: String? = null) { page?.let { contentPage = it } forward("site") diff -r 0a2ad22ac656 -r 94f174d591ab src/main/kotlin/de/uapcore/lightpit/servlet/FeedServlet.kt --- a/src/main/kotlin/de/uapcore/lightpit/servlet/FeedServlet.kt Thu May 13 18:01:56 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/FeedServlet.kt Thu May 13 19:31:09 2021 +0200 @@ -28,24 +28,39 @@ import de.uapcore.lightpit.AbstractServlet import de.uapcore.lightpit.HttpRequest import de.uapcore.lightpit.dao.DataAccessObject -import de.uapcore.lightpit.entities.Issue import de.uapcore.lightpit.util.IssueFilter import de.uapcore.lightpit.util.IssueSorter +import de.uapcore.lightpit.util.SpecificFilter import de.uapcore.lightpit.viewmodel.IssueFeed +import de.uapcore.lightpit.viewmodel.ProjectFeed import javax.servlet.annotation.WebServlet @WebServlet(urlPatterns = ["/feed/*"]) class FeedServlet : AbstractServlet() { init { - get("/issues.rss", this::issues) + get("/projects.rss", this::projects) + get("/%project/issues.rss", this::issues) + } + + private fun projects(http: HttpRequest, dao: DataAccessObject) { + + val projects = dao.listProjects() + + http.view = ProjectFeed(projects) + http.renderFeed("project-feed") } private fun issues(http: HttpRequest, dao: DataAccessObject) { + val project = http.pathParams["project"]?.let { dao.findProjectByNode(it) } + if (project == null) { + http.response.sendError(404) + return + } - val issues = dao.listIssues(IssueFilter()).sortedWith(IssueSorter.DEFAULT_ISSUE_SORTER) + val issues = dao.listIssues(IssueFilter(SpecificFilter(project))).sortedWith(IssueSorter.DEFAULT_ISSUE_SORTER) - http.view = IssueFeed(issues.groupBy(Issue::project)) - http.forward("issues-feed") + http.view = IssueFeed(project, issues) + http.renderFeed("issues-feed") } } \ No newline at end of file diff -r 0a2ad22ac656 -r 94f174d591ab src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt --- a/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Thu May 13 18:01:56 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/ProjectServlet.kt Thu May 13 19:31:09 2021 +0200 @@ -145,12 +145,15 @@ } } + private fun feedPath(project: Project) = "feed/${project.node}/issues.rss" + data class PathInfos( val projectInfo: ProjectInfo, val version: Version?, val component: Component? ) { - val issuesHref by lazyOf("projects/${projectInfo.project.node}/issues/${version?.node ?: "-"}/${component?.node ?: "-"}/") + val project = projectInfo.project + val issuesHref by lazyOf("projects/${project.node}/issues/${version?.node ?: "-"}/${component?.node ?: "-"}/") } private fun withPathInfo(http: HttpRequest, dao: DataAccessObject): PathInfos? { @@ -186,13 +189,14 @@ withPathInfo(http, dao)?.run { val issues = dao.listIssues(IssueFilter( - project = SpecificFilter(projectInfo.project), + project = SpecificFilter(project), version = version?.let { SpecificFilter(it) } ?: AllFilter(), component = component?.let { SpecificFilter(it) } ?: AllFilter() )).sortedWith(DEFAULT_ISSUE_SORTER) with(http) { view = ProjectDetails(projectInfo, issues, version, component) + feedPath = feedPath(project) navigationMenu = activeProjectNavMenu( dao.listProjects(), projectInfo, @@ -261,6 +265,7 @@ projectInfo, dao.listVersionSummaries(projectInfo.project) ) + feedPath = feedPath(projectInfo.project) navigationMenu = activeProjectNavMenu( dao.listProjects(), projectInfo @@ -290,6 +295,7 @@ with(http) { view = VersionEditView(projectInfo, version) + feedPath = feedPath(projectInfo.project) navigationMenu = activeProjectNavMenu( dao.listProjects(), projectInfo, @@ -342,6 +348,7 @@ projectInfo, dao.listComponentSummaries(projectInfo.project) ) + feedPath = feedPath(projectInfo.project) navigationMenu = activeProjectNavMenu( dao.listProjects(), projectInfo @@ -371,6 +378,7 @@ with(http) { view = ComponentEditView(projectInfo, component, dao.listUsers()) + feedPath = feedPath(projectInfo.project) navigationMenu = activeProjectNavMenu( dao.listProjects(), projectInfo, @@ -426,7 +434,9 @@ val comments = dao.listComments(issue) with(http) { - view = IssueDetailView(issue, comments, projectInfo.project, version, component) + view = IssueDetailView(issue, comments, project, version, component) + // TODO: feed path for this particular issue + feedPath = feedPath(projectInfo.project) navigationMenu = activeProjectNavMenu( dao.listProjects(), projectInfo, @@ -443,7 +453,7 @@ withPathInfo(http, dao)?.run { val issue = dao.findIssue(http.pathParams["issue"]?.toIntOrNull() ?: -1) ?: Issue( -1, - projectInfo.project, + project, ) // pre-select component, if available in the path info @@ -464,10 +474,11 @@ projectInfo.versions, projectInfo.components, dao.listUsers(), - projectInfo.project, + project, version, component ) + feedPath = feedPath(projectInfo.project) navigationMenu = activeProjectNavMenu( dao.listProjects(), projectInfo, @@ -505,7 +516,7 @@ // TODO: throw validator exception instead of using defaults val issue = Issue( http.param("id")?.toIntOrNull() ?: -1, - projectInfo.project + project ).apply { component = dao.findComponent(http.param("component")?.toIntOrNull() ?: -1) category = IssueCategory.valueOf(http.param("category") ?: "") @@ -522,9 +533,9 @@ eta = http.param("eta")?.let { if (it.isBlank()) null else Date.valueOf(it) } affectedVersions = http.paramArray("affected") - .mapNotNull { param -> param.toIntOrNull()?.let { Version(it, projectInfo.project.id) } } + .mapNotNull { param -> param.toIntOrNull()?.let { Version(it, project.id) } } resolvedVersions = http.paramArray("resolved") - .mapNotNull { param -> param.toIntOrNull()?.let { Version(it, projectInfo.project.id) } } + .mapNotNull { param -> param.toIntOrNull()?.let { Version(it, project.id) } } } val openId = if (issue.id < 0) { diff -r 0a2ad22ac656 -r 94f174d591ab src/main/kotlin/de/uapcore/lightpit/viewmodel/Feeds.kt --- a/src/main/kotlin/de/uapcore/lightpit/viewmodel/Feeds.kt Thu May 13 18:01:56 2021 +0200 +++ b/src/main/kotlin/de/uapcore/lightpit/viewmodel/Feeds.kt Thu May 13 19:31:09 2021 +0200 @@ -28,6 +28,11 @@ import de.uapcore.lightpit.entities.Issue import de.uapcore.lightpit.entities.Project +class ProjectFeed( + val projects: List +) : View() + class IssueFeed( - val issues: Map> + val project: Project, + val issues: List ) : View() \ No newline at end of file diff -r 0a2ad22ac656 -r 94f174d591ab src/main/resources/localization/strings.properties --- a/src/main/resources/localization/strings.properties Thu May 13 18:01:56 2021 +0200 +++ b/src/main/resources/localization/strings.properties Thu May 13 19:31:09 2021 +0200 @@ -54,6 +54,11 @@ error.message = Server Message error.returnLink = Return to error.timestamp = Timestamp +feed.issues.description=Feed about recently updated issues. +feed.issues.title=LightPIT Issues +feed.projects.description=Feed about tracked projects. +feed.projects.source=Issues +feed.projects.title=LightPIT Projects issue.affected-versions=Affected Versions issue.assignee=Assignee issue.category.Bug=Bug @@ -126,6 +131,4 @@ version.status.Released=Released version.status.Unreleased=Unreleased version.status=Status -version=Version -feed.issues.title=LightPIT - Issues -feed.issues.description=Feed about recently updated issues. \ No newline at end of file +version=Version \ No newline at end of file diff -r 0a2ad22ac656 -r 94f174d591ab src/main/resources/localization/strings_de.properties --- a/src/main/resources/localization/strings_de.properties Thu May 13 18:01:56 2021 +0200 +++ b/src/main/resources/localization/strings_de.properties Thu May 13 19:31:09 2021 +0200 @@ -54,6 +54,11 @@ error.message = Server Nachricht error.returnLink = Kehre zurück zu error.timestamp = Zeitstempel +feed.issues.description=Feed \u00fcber k\u00fcrzlich aktualisierte Vorg\u00e4nge. +feed.issues.title=LightPIT Vorg\u00e4nge +feed.projects.description=Feed \u00fcber verwaltete Projekte. +feed.projects.source=Vorg\u00e4nge +feed.projects.title=LightPIT Projekte issue.affected-versions=Betroffene Versionen issue.assignee=Zugewiesen issue.category.Bug=Fehler @@ -127,5 +132,3 @@ version.status.Unreleased=Unver\u00f6ffentlicht version.status=Status version=Version -feed.issues.title=LightPIT - Vorg\u00e4nge -feed.issues.description=Feed \u00fcber k\u00fcrzlich aktualisierte Vorg\u00e4nge. diff -r 0a2ad22ac656 -r 94f174d591ab src/main/webapp/WEB-INF/jsp/feed.jsp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jsp/feed.jsp Thu May 13 19:31:09 2021 +0200 @@ -0,0 +1,36 @@ +<%-- + ~ Copyright 2021 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 contentType="application/rss+xml;charset=UTF-8" trimDirectiveWhitespaces="true" %> +<%@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 0a2ad22ac656 -r 94f174d591ab src/main/webapp/WEB-INF/jsp/issues-feed.jsp --- a/src/main/webapp/WEB-INF/jsp/issues-feed.jsp Thu May 13 18:01:56 2021 +0200 +++ b/src/main/webapp/WEB-INF/jsp/issues-feed.jsp Thu May 13 19:31:09 2021 +0200 @@ -22,37 +22,24 @@ ~ 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 contentType="application/rss+xml;charset=UTF-8" trimDirectiveWhitespaces="true" %> -<%@page import="de.uapcore.lightpit.Constants" %> +<%@page contentType="application/rss+xml;charset=UTF-8" 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" %> - - - - - - - - - - - <fmt:message key="feed.issues.title"/> - <c:out value="${project.name}"/> - - ${baseHref}projects/${project.node}/ - - ${pageContext.response.locale.language} + + <c:out value="${viewmodel.project.name}"/> | <fmt:message key="feed.issues.title"/> + + ${baseHref}projects/${viewmodel.project.node} + ${pageContext.response.locale.language} - - - <c:if test="${not empty issue.component}"><c:out value="${issue.component.name}"/> - </c:if><c:out value="${issue.subject}"/> - - - ${baseHref}projects/${issue.project.node}/issues/-/${empty issue.component ? '-' : issue.component.node}/${issue.id} - ${baseHref}projects/${issue.project.node}/issues/-/-/${issue.id} - - - - + + + <c:if test="${not empty issue.component}"><c:out value="${issue.component.name}"/> - </c:if><c:out value="${issue.subject}"/> + + + ${baseHref}projects/${issue.project.node}/issues/-/${empty issue.component ? '-' : issue.component.node}/${issue.id} + ${baseHref}projects/${issue.project.node}/issues/-/-/${issue.id} + + - \ No newline at end of file + diff -r 0a2ad22ac656 -r 94f174d591ab src/main/webapp/WEB-INF/jsp/project-feed.jsp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/jsp/project-feed.jsp Thu May 13 19:31:09 2021 +0200 @@ -0,0 +1,43 @@ +<%-- + ~ Copyright 2021 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 contentType="application/rss+xml;charset=UTF-8" 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" %> + + + <fmt:message key="feed.projects.title"/> + + ${baseHref}projects/ + ${pageContext.response.locale.language} + + + + <c:out value="${project.name}"/> + + ${baseHref}projects/${project.node} + + + + diff -r 0a2ad22ac656 -r 94f174d591ab src/main/webapp/WEB-INF/jsp/site.jsp --- a/src/main/webapp/WEB-INF/jsp/site.jsp Thu May 13 18:01:56 2021 +0200 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Thu May 13 19:31:09 2021 +0200 @@ -33,6 +33,9 @@ <%-- Make the base href easily available at request scope --%> +<%-- The feed URL for this page. --%> + + <%-- Define an alias for the request path --%> @@ -62,8 +65,7 @@ - +