Bookmarks: Add Atom feed.
Change-Id: I902473b6bffa10afa0cb2295d365d50335de9021
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
index 00e2824..3219f57 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/bookmarks/BookmarkResource.java
@@ -1,10 +1,19 @@
package eu.mulk.mulkcms2.benki.bookmarks;
+import static javax.ws.rs.core.MediaType.APPLICATION_ATOM_XML;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.TEXT_HTML;
+import com.rometools.rome.feed.atom.Content;
+import com.rometools.rome.feed.atom.Entry;
+import com.rometools.rome.feed.atom.Feed;
+import com.rometools.rome.feed.atom.Link;
+import com.rometools.rome.feed.synd.SyndPersonImpl;
+import com.rometools.rome.io.FeedException;
+import com.rometools.rome.io.WireFeedOutput;
import eu.mulk.mulkcms2.benki.accesscontrol.Role;
import eu.mulk.mulkcms2.benki.users.User;
+import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.panache.common.Sort;
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateExtension;
@@ -15,14 +24,18 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.time.Instant;
import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor;
+import java.util.Comparator;
+import java.util.Date;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.json.JsonObject;
import javax.json.spi.JsonProvider;
@@ -37,7 +50,10 @@
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;
import org.jsoup.Jsoup;
@@ -63,30 +79,87 @@
@Inject SecurityIdentity identity;
+ @Context UriInfo uri;
+
+ @Inject
+ @ConfigProperty(name = "mulkcms.tag-base")
+ String tagBase;
+
@GET
@Produces(TEXT_HTML)
public TemplateInstance getIndex() {
- List<Bookmark> bookmarks;
- if (identity.isAnonymous()) {
- Role world = Role.find("from Role r join r.tags tag where tag = 'world'").singleResult();
- bookmarks =
- Bookmark.find(
- "select bm from Bookmark bm join bm.targets target left join fetch bm.owner where target = ?1",
- Sort.by("date").descending(),
- world)
- .list();
- } else {
- var userName = identity.getPrincipal().getName();
- User user =
- User.find("from BenkiUser u join u.nicknames n where ?1 = n", userName).singleResult();
- bookmarks =
- Bookmark.find(
- "select bm from BenkiUser u inner join u.visibleBookmarks bm left join fetch bm.owner where u.id = ?1",
- Sort.by("date").descending(),
- user.id)
- .list();
- }
- return bookmarkList.data("bookmarks", bookmarks).data("authenticated", !identity.isAnonymous());
+ var bookmarkQuery = bookmarkQuery();
+ return bookmarkList
+ .data("bookmarks", bookmarkQuery.list())
+ .data("authenticated", !identity.isAnonymous());
+ }
+
+ @GET
+ @Path("feed")
+ @Produces(APPLICATION_ATOM_XML)
+ public String getFeed() throws FeedException {
+ var bookmarks = bookmarkQuery().list();
+ var feed = new Feed("atom_1.0");
+
+ feed.setTitle("Book Marx");
+ feed.setId(
+ String.format(
+ "tag:%s,2019:marx:%s",
+ tagBase, identity.isAnonymous() ? "world" : identity.getPrincipal().getName()));
+ feed.setUpdated(
+ Date.from(
+ bookmarks.stream()
+ .map(x -> x.date)
+ .max(Comparator.comparing(x -> x))
+ .orElse(OffsetDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC))
+ .toInstant()));
+
+ var selfLink = new Link();
+ selfLink.setHref(uri.getRequestUri().toString());
+ selfLink.setRel("self");
+ feed.setOtherLinks(List.of(selfLink));
+
+ var htmlAltLink = new Link();
+ htmlAltLink.setHref(uri.resolve(URI.create("/bookmarks")).toString());
+ htmlAltLink.setRel("alternate");
+ htmlAltLink.setType("text/html");
+ feed.setAlternateLinks(List.of(htmlAltLink));
+
+ feed.setEntries(
+ bookmarks.stream()
+ .map(
+ bookmark -> {
+ var entry = new Entry();
+
+ entry.setId(String.format("tag:%s,2012:/marx/%d", tagBase, bookmark.id));
+ entry.setPublished(Date.from(bookmark.date.toInstant()));
+ entry.setUpdated(Date.from(bookmark.date.toInstant()));
+
+ var author = new SyndPersonImpl();
+ author.setName(bookmark.owner.getFirstAndLastName());
+ entry.setAuthors(List.of(author));
+
+ var title = new Content();
+ title.setType("text");
+ title.setValue(bookmark.title);
+ entry.setTitleEx(title);
+
+ var summary = new Content();
+ summary.setType("html");
+ summary.setValue(bookmark.getDescriptionHtml());
+ entry.setSummary(summary);
+
+ var link = new Link();
+ link.setHref(bookmark.uri);
+ link.setRel("alternate");
+ entry.setAlternateLinks(List.of(link));
+
+ return entry;
+ })
+ .collect(Collectors.toUnmodifiableList()));
+
+ var wireFeedOutput = new WireFeedOutput();
+ return wireFeedOutput.outputString(feed);
}
@GET
@@ -155,4 +228,22 @@
static String htmlDateTime(TemporalAccessor x) {
return htmlDateFormatter.format(x);
}
+
+ private PanacheQuery<Bookmark> bookmarkQuery() {
+ if (identity.isAnonymous()) {
+ Role world = Role.find("from Role r join r.tags tag where tag = 'world'").singleResult();
+ return Bookmark.find(
+ "select bm from Bookmark bm join bm.targets target left join fetch bm.owner where target = ?1",
+ Sort.by("date").descending(),
+ world);
+ } else {
+ var userName = identity.getPrincipal().getName();
+ User user =
+ User.find("from BenkiUser u join u.nicknames n where ?1 = n", userName).singleResult();
+ return Bookmark.find(
+ "select bm from BenkiUser u inner join u.visibleBookmarks bm left join fetch bm.owner where u.id = ?1",
+ Sort.by("date").descending(),
+ user.id);
+ }
+ }
}
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/users/User.java b/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
index 1a0dae6..71f6c43 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/users/User.java
@@ -23,6 +23,7 @@
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
+import javax.persistence.Transient;
@Entity(name = "BenkiUser")
@Table(name = "users", schema = "benki")
@@ -131,4 +132,9 @@
joinColumns = @JoinColumn(name = "user"),
inverseJoinColumns = @JoinColumn(name = "role"))
public Set<Role> effectiveRoles;
+
+ @Transient
+ public String getFirstAndLastName() {
+ return String.format("%s %s", firstName, lastName);
+ }
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 886db80..4237503 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -7,6 +7,8 @@
#quarkus.log.category."io.vertx.ext.auth.oauth2".level = FINEST
#quarkus.log.category."io.vertx.ext.jwt".level = FINEST
+mulkcms.tag-base = hub.benkard.de
+
quarkus.datasource.driver = org.postgresql.Driver
quarkus.datasource.max-size = 8
quarkus.datasource.min-size = 0
diff --git a/src/main/resources/templates/benki/bookmarks/bookmarkList.html b/src/main/resources/templates/benki/bookmarks/bookmarkList.html
index a53abdd..7d23d45 100644
--- a/src/main/resources/templates/benki/bookmarks/bookmarkList.html
+++ b/src/main/resources/templates/benki/bookmarks/bookmarkList.html
@@ -7,7 +7,9 @@
{#siteSection}Bookmarks{/siteSection}
{#bookmarksClass}this-page{/bookmarksClass}
-{#head}{/head}
+{#head}
+ <link href="/bookmarks/feed" rel="alternate" type="application/atom+xml" />
+{/head}
{#body}