Sat, 15 May 2021 16:19:29 +0200
#109 adds RSS feed button to project header and changes feed output slightly
1.1 --- a/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Thu May 13 19:31:09 2021 +0200 1.2 +++ b/src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt Sat May 15 16:19:29 2021 +0200 1.3 @@ -80,16 +80,24 @@ 1.4 request.setAttribute(Constants.REQ_ATTR_NAVIGATION, navigationMenu) 1.5 } 1.6 1.7 - var redirectLocation = "" 1.8 + var redirectLocation: String? = null 1.9 set(value) { 1.10 field = value 1.11 - request.setAttribute(Constants.REQ_ATTR_REDIRECT_LOCATION, baseHref + value) 1.12 + if (value == null) { 1.13 + request.removeAttribute(Constants.REQ_ATTR_REDIRECT_LOCATION) 1.14 + } else { 1.15 + request.setAttribute(Constants.REQ_ATTR_REDIRECT_LOCATION, baseHref + value) 1.16 + } 1.17 } 1.18 1.19 - var feedPath = "" 1.20 + var feedPath: String? = null 1.21 set(value) { 1.22 field = value 1.23 - request.setAttribute(Constants.REQ_ATTR_FEED_HREF, baseHref + value) 1.24 + if (value == null) { 1.25 + request.removeAttribute(Constants.REQ_ATTR_FEED_HREF) 1.26 + } else { 1.27 + request.setAttribute(Constants.REQ_ATTR_FEED_HREF, baseHref + value) 1.28 + } 1.29 } 1.30 1.31 /** 1.32 @@ -116,10 +124,6 @@ 1.33 */ 1.34 val baseHref get() = "${request.scheme}://${request.serverName}$portInfo${request.contextPath}/" 1.35 1.36 - init { 1.37 - feedPath = "feed/projects.rss" 1.38 - } 1.39 - 1.40 private fun String.withExt(ext: String) = if (endsWith(ext)) this else plus(ext) 1.41 private fun jspPath(name: String) = Constants.JSP_PATH_PREFIX.plus(name).withExt(".jsp") 1.42
2.1 --- a/src/main/kotlin/de/uapcore/lightpit/servlet/FeedServlet.kt Thu May 13 19:31:09 2021 +0200 2.2 +++ b/src/main/kotlin/de/uapcore/lightpit/servlet/FeedServlet.kt Sat May 15 16:19:29 2021 +0200 2.3 @@ -32,25 +32,15 @@ 2.4 import de.uapcore.lightpit.util.IssueSorter 2.5 import de.uapcore.lightpit.util.SpecificFilter 2.6 import de.uapcore.lightpit.viewmodel.IssueFeed 2.7 -import de.uapcore.lightpit.viewmodel.ProjectFeed 2.8 import javax.servlet.annotation.WebServlet 2.9 2.10 @WebServlet(urlPatterns = ["/feed/*"]) 2.11 class FeedServlet : AbstractServlet() { 2.12 2.13 init { 2.14 - get("/projects.rss", this::projects) 2.15 get("/%project/issues.rss", this::issues) 2.16 } 2.17 2.18 - private fun projects(http: HttpRequest, dao: DataAccessObject) { 2.19 - 2.20 - val projects = dao.listProjects() 2.21 - 2.22 - http.view = ProjectFeed(projects) 2.23 - http.renderFeed("project-feed") 2.24 - } 2.25 - 2.26 private fun issues(http: HttpRequest, dao: DataAccessObject) { 2.27 val project = http.pathParams["project"]?.let { dao.findProjectByNode(it) } 2.28 if (project == null) { 2.29 @@ -58,6 +48,7 @@ 2.30 return 2.31 } 2.32 2.33 + // TODO: add a timestamp filter (e.g. last 30 days) 2.34 val issues = dao.listIssues(IssueFilter(SpecificFilter(project))).sortedWith(IssueSorter.DEFAULT_ISSUE_SORTER) 2.35 2.36 http.view = IssueFeed(project, issues)
3.1 --- a/src/main/kotlin/de/uapcore/lightpit/viewmodel/Feeds.kt Thu May 13 19:31:09 2021 +0200 3.2 +++ b/src/main/kotlin/de/uapcore/lightpit/viewmodel/Feeds.kt Sat May 15 16:19:29 2021 +0200 3.3 @@ -27,12 +27,12 @@ 3.4 3.5 import de.uapcore.lightpit.entities.Issue 3.6 import de.uapcore.lightpit.entities.Project 3.7 - 3.8 -class ProjectFeed( 3.9 - val projects: List<Project> 3.10 -) : View() 3.11 +import java.sql.Timestamp 3.12 +import java.time.Instant 3.13 3.14 class IssueFeed( 3.15 val project: Project, 3.16 val issues: List<Issue> 3.17 -) : View() 3.18 \ No newline at end of file 3.19 +) : View() { 3.20 + val lastModified = issues.map(Issue::updated).maxOrNull() ?: Timestamp.from(Instant.now()) 3.21 +} 3.22 \ No newline at end of file
4.1 --- a/src/main/resources/localization/strings.properties Thu May 13 19:31:09 2021 +0200 4.2 +++ b/src/main/resources/localization/strings.properties Sat May 15 16:19:29 2021 +0200 4.3 @@ -54,11 +54,11 @@ 4.4 error.message = Server Message 4.5 error.returnLink = Return to 4.6 error.timestamp = Timestamp 4.7 +feed.issues.created=Issue has been created. 4.8 feed.issues.description=Feed about recently updated issues. 4.9 feed.issues.title=LightPIT Issues 4.10 -feed.projects.description=Feed about tracked projects. 4.11 -feed.projects.source=Issues 4.12 -feed.projects.title=LightPIT Projects 4.13 +feed.issues.updated=Issue has been updated. 4.14 +feed=Feed 4.15 issue.affected-versions=Affected Versions 4.16 issue.assignee=Assignee 4.17 issue.category.Bug=Bug
5.1 --- a/src/main/resources/localization/strings_de.properties Thu May 13 19:31:09 2021 +0200 5.2 +++ b/src/main/resources/localization/strings_de.properties Sat May 15 16:19:29 2021 +0200 5.3 @@ -54,11 +54,11 @@ 5.4 error.message = Server Nachricht 5.5 error.returnLink = Kehre zurück zu 5.6 error.timestamp = Zeitstempel 5.7 +feed=Feed 5.8 +feed.issues.created=Vorgang wurde erstellt. 5.9 feed.issues.description=Feed \u00fcber k\u00fcrzlich aktualisierte Vorg\u00e4nge. 5.10 feed.issues.title=LightPIT Vorg\u00e4nge 5.11 -feed.projects.description=Feed \u00fcber verwaltete Projekte. 5.12 -feed.projects.source=Vorg\u00e4nge 5.13 -feed.projects.title=LightPIT Projekte 5.14 +feed.issues.updated=Vorgang wurde aktualisiert. 5.15 issue.affected-versions=Betroffene Versionen 5.16 issue.assignee=Zugewiesen 5.17 issue.category.Bug=Fehler 5.18 @@ -130,5 +130,5 @@ 5.19 version.status.LTS=Langzeitsupport 5.20 version.status.Released=Ver\u00f6ffentlicht 5.21 version.status.Unreleased=Unver\u00f6ffentlicht 5.22 -version.status=Status 5.23 +version.status=Status 5.24 version=Version
6.1 --- a/src/main/webapp/WEB-INF/jsp/issues-feed.jsp Thu May 13 19:31:09 2021 +0200 6.2 +++ b/src/main/webapp/WEB-INF/jsp/issues-feed.jsp Sat May 15 16:19:29 2021 +0200 6.3 @@ -22,7 +22,7 @@ 6.4 ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 6.5 ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 6.6 --%> 6.7 -<%@page contentType="application/rss+xml;charset=UTF-8" pageEncoding="UTF-8" %> 6.8 +<%@page contentType="application/rss+xml;charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %> 6.9 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 6.10 <%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 6.11 <jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.IssueFeed" scope="request"/> 6.12 @@ -31,11 +31,20 @@ 6.13 <description><fmt:message key="feed.issues.description"/></description> 6.14 <link>${baseHref}projects/${viewmodel.project.node}</link> 6.15 <language>${pageContext.response.locale.language}</language> 6.16 + <pubDate><fmt:formatDate value="${viewmodel.lastModified}" pattern="EEE, dd MMM yyyy HH:mm:ss zzz" /></pubDate> 6.17 + <lastBuildDate><fmt:formatDate value="${viewmodel.lastModified}" pattern="EEE, dd MMM yyyy HH:mm:ss zzz" /></lastBuildDate> 6.18 6.19 <c:forEach items="${viewmodel.issues}" var="issue"> 6.20 <item> 6.21 <title><c:if test="${not empty issue.component}"><c:out value="${issue.component.name}"/> - </c:if><c:out value="${issue.subject}"/></title> 6.22 - <description><c:out value="${issue.description}"/></description> 6.23 + <description><c:choose> 6.24 + <c:when test="${issue.created eq issue.updated}"> 6.25 + <fmt:message key="feed.issues.created"/> 6.26 + </c:when> 6.27 + <c:otherwise> 6.28 + <fmt:message key="feed.issues.updated"/> 6.29 + </c:otherwise> 6.30 + </c:choose></description> 6.31 <category><fmt:message key="issue.category.${issue.category}"/></category> 6.32 <link>${baseHref}projects/${issue.project.node}/issues/-/${empty issue.component ? '-' : issue.component.node}/${issue.id}</link> 6.33 <guid isPermaLink="true">${baseHref}projects/${issue.project.node}/issues/-/-/${issue.id}</guid>
7.1 --- a/src/main/webapp/WEB-INF/jsp/project-feed.jsp Thu May 13 19:31:09 2021 +0200 7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 7.3 @@ -1,43 +0,0 @@ 7.4 -<%-- 7.5 - ~ Copyright 2021 Mike Becker. All rights reserved. 7.6 - ~ 7.7 - ~ Redistribution and use in source and binary forms, with or without 7.8 - ~ modification, are permitted provided that the following conditions are met: 7.9 - ~ 7.10 - ~ 1. Redistributions of source code must retain the above copyright 7.11 - ~ notice, this list of conditions and the following disclaimer. 7.12 - ~ 7.13 - ~ 2. Redistributions in binary form must reproduce the above copyright 7.14 - ~ notice, this list of conditions and the following disclaimer in the 7.15 - ~ documentation and/or other materials provided with the distribution. 7.16 - ~ 7.17 - ~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 7.18 - ~ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 7.19 - ~ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 7.20 - ~ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 7.21 - ~ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 7.22 - ~ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 7.23 - ~ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 7.24 - ~ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 7.25 - ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 7.26 - ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 7.27 - --%> 7.28 -<%@page contentType="application/rss+xml;charset=UTF-8" pageEncoding="UTF-8" %> 7.29 -<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 7.30 -<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> 7.31 -<jsp:useBean id="viewmodel" type="de.uapcore.lightpit.viewmodel.ProjectFeed" scope="request"/> 7.32 -<channel> 7.33 - <title><fmt:message key="feed.projects.title"/></title> 7.34 - <description><fmt:message key="feed.projects.description"/></description> 7.35 - <link>${baseHref}projects/</link> 7.36 - <language>${pageContext.response.locale.language}</language> 7.37 - 7.38 - <c:forEach items="${viewmodel.projects}" var="project"> 7.39 - <item> 7.40 - <title><c:out value="${project.name}"/></title> 7.41 - <description><c:out value="${project.description}"/></description> 7.42 - <link>${baseHref}projects/${project.node}</link> 7.43 - <source url="${baseHref}feed/${project.node}/issues.rss"><fmt:message key="feed.projects.source"/></source> 7.44 - </item> 7.45 - </c:forEach> 7.46 -</channel>
8.1 --- a/src/main/webapp/WEB-INF/jsp/site.jsp Thu May 13 19:31:09 2021 +0200 8.2 +++ b/src/main/webapp/WEB-INF/jsp/site.jsp Sat May 15 16:19:29 2021 +0200 8.3 @@ -65,26 +65,31 @@ 8.4 <meta http-equiv="refresh" content="0; URL=${redirectLocation}"> 8.5 </c:if> 8.6 <link rel="stylesheet" href="lightpit.css" type="text/css"> 8.7 - <link rel="alternate" type="application/rss+xml" title="RSS" href="${feedHref}" /> 8.8 + <c:if test="${not empty feedHref}"> 8.9 + <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="${feedHref}"/> 8.10 + </c:if> 8.11 <c:if test="${not empty extraCss}"> 8.12 <c:forEach items="${extraCss}" var="cssFile"> 8.13 - <link rel="stylesheet" href="${cssFile}" type="text/css"> 8.14 + <link rel="stylesheet" href="${cssFile}" type="text/css"> 8.15 </c:forEach> 8.16 </c:if> 8.17 </head> 8.18 <body> 8.19 <div id="mainMenu"> 8.20 - <div class="menuEntry" <c:if test="${fn:startsWith(requestPath, '/projects/')}">data-active</c:if> > 8.21 + <div class="menuEntry" 8.22 + <c:if test="${fn:startsWith(requestPath, '/projects/')}">data-active</c:if> > 8.23 <a href="projects/"> 8.24 <fmt:message key="menu.projects"/> 8.25 </a> 8.26 </div> 8.27 - <div class="menuEntry" <c:if test="${fn:startsWith(requestPath, '/users/')}">data-active</c:if> > 8.28 + <div class="menuEntry" 8.29 + <c:if test="${fn:startsWith(requestPath, '/users/')}">data-active</c:if> > 8.30 <a href="users/"> 8.31 <fmt:message key="menu.users"/> 8.32 </a> 8.33 </div> 8.34 - <div class="menuEntry" <c:if test="${fn:startsWith(requestPath, '/language/')}">data-active</c:if> > 8.35 + <div class="menuEntry" 8.36 + <c:if test="${fn:startsWith(requestPath, '/language/')}">data-active</c:if> > 8.37 <a href="language/"> 8.38 <fmt:message key="menu.languages"/> 8.39 </a> 8.40 @@ -93,7 +98,7 @@ 8.41 <div> 8.42 <c:if test="${not empty navMenu}"> 8.43 <div id="sideMenu"> 8.44 - <%@include file="../jspf/navmenu.jspf"%> 8.45 + <%@include file="../jspf/navmenu.jspf" %> 8.46 </div> 8.47 </c:if> 8.48 <div id="content-area" <c:if test="${not empty navMenu}">class="sidebar-spacing"</c:if>>
9.1 --- a/src/main/webapp/WEB-INF/jspf/project-header.jspf Thu May 13 19:31:09 2021 +0200 9.2 +++ b/src/main/webapp/WEB-INF/jspf/project-header.jspf Sat May 15 16:19:29 2021 +0200 9.3 @@ -4,6 +4,15 @@ 9.4 --%> 9.5 <div class="table project-attributes"> 9.6 <div class="row"> 9.7 + <div class="caption"><fmt:message key="feed"/>:</div> 9.8 + <div class="caption"> 9.9 + <a class="rss-feed" href="./feed/${project.node}/issues.rss"> 9.10 + <img src="./rss.svg" alt="Feed" style="width: 1em; height: 1em;"> 9.11 + RSS 9.12 + </a> 9.13 + </div> 9.14 + </div> 9.15 + <div class="row"> 9.16 <div class="caption"><fmt:message key="project.name"/>:</div> 9.17 <div><c:out value="${project.name}"/></div> 9.18 <div class="caption"><fmt:message key="description"/>:</div>
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/src/main/webapp/rss.svg Sat May 15 16:19:29 2021 +0200 10.3 @@ -0,0 +1,18 @@ 10.4 +<?xml version="1.0"?> 10.5 +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 10.6 +<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="128px" height="128px" id="RSSicon" viewBox="0 0 256 256"> 10.7 +<defs> 10.8 +<linearGradient x1="0.085" y1="0.085" x2="0.915" y2="0.915" id="RSSg"> 10.9 +<stop offset="0.0" stop-color="#E3702D"/><stop offset="0.1071" stop-color="#EA7D31"/> 10.10 +<stop offset="0.3503" stop-color="#F69537"/><stop offset="0.5" stop-color="#FB9E3A"/> 10.11 +<stop offset="0.7016" stop-color="#EA7C31"/><stop offset="0.8866" stop-color="#DE642B"/> 10.12 +<stop offset="1.0" stop-color="#D95B29"/> 10.13 +</linearGradient> 10.14 +</defs> 10.15 +<rect width="256" height="256" rx="55" ry="55" x="0" y="0" fill="#CC5D15"/> 10.16 +<rect width="246" height="246" rx="50" ry="50" x="5" y="5" fill="#F49C52"/> 10.17 +<rect width="236" height="236" rx="47" ry="47" x="10" y="10" fill="url(#RSSg)"/> 10.18 +<circle cx="68" cy="189" r="24" fill="#FFF"/> 10.19 +<path d="M160 213h-34a82 82 0 0 0 -82 -82v-34a116 116 0 0 1 116 116z" fill="#FFF"/> 10.20 +<path d="M184 213A140 140 0 0 0 44 73 V 38a175 175 0 0 1 175 175z" fill="#FFF"/> 10.21 +</svg>