#109 adds RSS feed button to project header and changes feed output slightly

Sat, 15 May 2021 16:19:29 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 15 May 2021 16:19:29 +0200
changeset 199
59393c8cc557
parent 198
94f174d591ab
child 200
a5ddfaf6b469

#109 adds RSS feed button to project header and changes feed output slightly

src/main/kotlin/de/uapcore/lightpit/RequestMapping.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/servlet/FeedServlet.kt file | annotate | diff | comparison | revisions
src/main/kotlin/de/uapcore/lightpit/viewmodel/Feeds.kt file | annotate | diff | comparison | revisions
src/main/resources/localization/strings.properties file | annotate | diff | comparison | revisions
src/main/resources/localization/strings_de.properties file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/issues-feed.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/project-feed.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jsp/site.jsp file | annotate | diff | comparison | revisions
src/main/webapp/WEB-INF/jspf/project-header.jspf file | annotate | diff | comparison | revisions
src/main/webapp/rss.svg file | annotate | diff | comparison | revisions
     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>

mercurial