Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP:feat: implement comment notifications for link page #91

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions src/main/java/run/halo/links/CommentNotificationReasonPublisher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package run.halo.links;

import static run.halo.links.LinkRouter.LINKS_ROUTE_PATH;

import com.fasterxml.jackson.core.type.TypeReference;
import java.util.Map;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import run.halo.app.core.extension.Plugin;
import run.halo.app.core.extension.content.Comment;
import run.halo.app.core.extension.notification.Reason;
import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.MetadataUtil;
import run.halo.app.extension.Ref;
import run.halo.app.infra.ExternalLinkProcessor;
import run.halo.app.infra.utils.JsonUtils;
import run.halo.app.notification.NotificationReasonEmitter;
import run.halo.app.notification.UserIdentity;
import run.halo.app.plugin.SettingFetcher;

/**
* @author LIlGG
*/
@Component
@RequiredArgsConstructor
public class CommentNotificationReasonPublisher {

public static final String NEW_COMMENT_ON_LINK = "new-comment-on-link";
public static final String NOTIFIED_ANNO = "link.halo.run/notified";

private final ExtensionClient client;
private final NotificationReasonEmitter notificationReasonEmitter;
private final ExternalLinkProcessor externalLinkProcessor;
private final SettingFetcher settingFetcher;


/**
* On new comment.
*/
@Async
@EventListener(LinkHasNewCommentEvent.class)
public void onNewComment(LinkHasNewCommentEvent event) {
Comment comment = event.getComment();
var annotations = MetadataUtil.nullSafeAnnotations(comment);
if (annotations.containsKey(NOTIFIED_ANNO)) {
return;
}
publishReasonBy(comment);
markAsNotified(comment.getMetadata().getName());
}

private void markAsNotified(String commentName) {
client.fetch(Comment.class, commentName).ifPresent(latestComment -> {
MetadataUtil.nullSafeAnnotations(latestComment).put(NOTIFIED_ANNO, "true");
client.update(latestComment);
});
}

public void publishReasonBy(Comment comment) {
Ref subjectRef = comment.getSpec().getSubjectRef();
var plugin = client.fetch(Plugin.class, subjectRef.getName()).orElseThrow();

String linkUrl = externalLinkProcessor.processLink(LINKS_ROUTE_PATH);
String linkTitle = settingFetcher.get("title").asText();
if (StringUtils.isBlank(linkTitle)) {
linkTitle = "链接";
}
var reasonSubject = Reason.Subject.builder()
.apiVersion(plugin.getApiVersion())
.kind(plugin.getKind())
.title(linkTitle + ": " + plugin.getMetadata().getName())
.name(subjectRef.getName())
.url(linkUrl)
.build();

var owner = comment.getSpec().getOwner();
String finalLinkTitle = linkTitle;
notificationReasonEmitter.emit(NEW_COMMENT_ON_LINK,
builder -> {
var attributes = CommentReasonData.builder()
.linkTitle(finalLinkTitle)
.commenter(owner.getDisplayName())
.content(comment.getSpec().getContent())
.commentName(comment.getMetadata().getName())
.build();
builder.attributes(toAttributeMap(attributes))
.author(identityFrom(owner))
.subject(reasonSubject);
}).block();
}

static <T> Map<String, Object> toAttributeMap(T data) {
Assert.notNull(data, "Reason attributes must not be null");
return JsonUtils.mapper().convertValue(data, new TypeReference<>() {
});
}

static UserIdentity identityFrom(Comment.CommentOwner owner) {
if (Comment.CommentOwner.KIND_EMAIL.equals(owner.getKind())) {
return UserIdentity.anonymousWithEmail(owner.getName());
}
return UserIdentity.of(owner.getName());
}

@Builder
record CommentReasonData(String linkTitle, String commenter, String content,
String commentName) {
}
}
59 changes: 59 additions & 0 deletions src/main/java/run/halo/links/CommentReconciler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package run.halo.links;

import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import run.halo.app.core.extension.Plugin;
import run.halo.app.core.extension.content.Comment;
import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.ExtensionUtil;
import run.halo.app.extension.GroupVersionKind;
import run.halo.app.extension.Ref;
import run.halo.app.extension.controller.Controller;
import run.halo.app.extension.controller.ControllerBuilder;
import run.halo.app.extension.controller.Reconciler;

/**
* @author LIlGG
*/
@Component
@RequiredArgsConstructor
public class CommentReconciler implements Reconciler<Reconciler.Request> {

private static final String FINALIZER = "link.halo.run/finalizer";
private final ExtensionClient client;
private final ApplicationEventPublisher eventPublisher;

@Override
public Result reconcile(Request request) {
client.fetch(Comment.class, request.name()).ifPresent(comment -> {
if (comment.getMetadata().getDeletionTimestamp() != null) {
if (ExtensionUtil.removeFinalizers(comment.getMetadata(), Set.of(FINALIZER))) {
client.update(comment);
}
return;
}

var forLink = Ref.groupKindEquals(comment.getSpec().getSubjectRef(),
GroupVersionKind.fromExtension(Plugin.class));
if (!forLink) {
return;
}

if (ExtensionUtil.addFinalizers(comment.getMetadata(), Set.of(FINALIZER))) {
eventPublisher.publishEvent(new LinkHasNewCommentEvent(this, comment));
client.update(comment);
}
});
return null;
}

@Override
public Controller setupWith(ControllerBuilder builder) {
return builder
.extension(new Comment())
.syncAllOnStart(false)
.build();
}
}
6 changes: 4 additions & 2 deletions src/main/java/run/halo/links/LinkGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
*/
@Data
@EqualsAndHashCode(callSuper = true)
@GVK(group = "core.halo.run", version = "v1alpha1", kind = "LinkGroup", plural = "linkgroups", singular = "linkgroup")
@GVK(group = "core.halo.run", version = "v1alpha1", kind = "LinkGroup",
plural = "linkgroups", singular = "linkgroup")
public class LinkGroup extends AbstractExtension {

private LinkGroupSpec spec;
Expand All @@ -29,7 +30,8 @@ public static class LinkGroupSpec {

@Deprecated(since = "1.2.0", forRemoval = true)
@Schema(description = "Names of links below this group.")
@ArraySchema(arraySchema = @Schema(description = "Links of this group."), schema = @Schema(description = "Name of link."))
@ArraySchema(arraySchema = @Schema(description = "Links of this group."),
schema = @Schema(description = "Name of link."))
private LinkedHashSet<String> links;
}
}
19 changes: 19 additions & 0 deletions src/main/java/run/halo/links/LinkHasNewCommentEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package run.halo.links;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;
import run.halo.app.core.extension.content.Comment;

/**
* @author LIlGG
*/
@Getter
public class LinkHasNewCommentEvent extends ApplicationEvent {

private final Comment comment;

public LinkHasNewCommentEvent(Object source, Comment comment) {
super(source);
this.comment = comment;
}
}
15 changes: 8 additions & 7 deletions src/main/java/run/halo/links/LinkPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,16 @@ public void start() {
);
indexSpecs.add(new IndexSpec()
.setName("spec.priority")
.setIndexFunc(simpleAttribute(Link.class, link -> String.valueOf(link.getSpec().getPriority())))
);
});
schemeManager.register(LinkGroup.class, indexSpecs -> {
indexSpecs.add(new IndexSpec()
.setName("spec.priority")
.setIndexFunc(simpleAttribute(LinkGroup.class, group -> String.valueOf(group.getSpec().getPriority())))
.setIndexFunc(simpleAttribute(Link.class,
link -> String.valueOf(link.getSpec().getPriority())))
);
});
schemeManager.register(LinkGroup.class, indexSpecs -> indexSpecs.add(new IndexSpec()
.setName("spec.priority")
.setIndexFunc(simpleAttribute(LinkGroup.class,
group -> String.valueOf(group.getSpec().getPriority()))
)
));
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/run/halo/links/LinkRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
public class LinkRequest {

private final static String USER_AGENT =
private static final String USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
+ "Chrome/58.0.3029.110 Safari/537.3";

Expand Down
12 changes: 6 additions & 6 deletions src/main/java/run/halo/links/LinkRouter.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
import static run.halo.app.extension.index.query.QueryFactory.or;
import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToListOptions;

import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import java.util.Map;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.fn.builders.operation.Builder;
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
Expand All @@ -27,10 +29,6 @@
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException;

import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ListOptions;
import run.halo.app.extension.ListResult;
Expand All @@ -47,6 +45,8 @@
@RequiredArgsConstructor
public class LinkRouter {

public static final String LINKS_ROUTE_PATH = "/links";

private final LinkFinder linkFinder;
private final ReactiveExtensionClient client;
private final String tag = "api.plugin.halo.run/v1alpha1/Link";
Expand All @@ -55,7 +55,7 @@ public class LinkRouter {

@Bean
RouterFunction<ServerResponse> linkTemplateRoute() {
return route(GET("/links"),
return route(GET(LINKS_ROUTE_PATH),
request -> ServerResponse.ok().render("links",
Map.of("groups", linkGroups(),
"pluginName", pluginContext.getName(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package run.halo.links.finders.impl;

import static org.springframework.data.domain.Sort.Order.asc;
import static run.halo.app.extension.index.query.QueryFactory.*;
import static run.halo.app.extension.index.query.QueryFactory.and;
import static run.halo.app.extension.index.query.QueryFactory.equal;
import static run.halo.app.extension.index.query.QueryFactory.isNull;

import org.springframework.data.domain.Sort;
import reactor.core.publisher.Flux;
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/run/halo/links/vo/LinkGroupVo.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package run.halo.links.vo;

import java.util.List;
import lombok.Builder;
import lombok.Value;
import lombok.With;
import run.halo.app.extension.MetadataOperator;
import run.halo.app.theme.finders.vo.ExtensionVoOperator;
import run.halo.links.LinkGroup;

import java.util.List;

/**
* @author guqing
* @since 2.0.0
Expand Down
51 changes: 51 additions & 0 deletions src/main/resources/extensions/link-notification.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
apiVersion: notification.halo.run/v1alpha1
kind: ReasonType
metadata:
name: new-comment-on-link
spec:
displayName: "友情链接页面收到新评论"
description: "当你发布的友情链接页面收到新评论时,你将会收到一条通知,告诉你有新的评论。"
properties:
- name: linkTitle
type: string
description: "link page title."
- name: commenter
type: string
description: "The display name of the commenter."
- name: commentName
type: string
description: "The name of the comment."
- name: content
type: string
description: "The content of the comment."
---
apiVersion: notification.halo.run/v1alpha1
kind: NotificationTemplate
metadata:
name: template-new-comment-on-link
spec:
reasonSelector:
reasonType: new-comment-on-link
language: default
template:
title: "[(${commenter})] 在[(${linkTitle})]页面进行了评论"
rawBody: |
[(${subscriber.displayName})] 你好:

[(${commenter})] 在[(${linkTitle})]页面进行了评论, 以下是评论的具体内容:

[(${content})]
htmlBody: |
<div class="notification-content">
<div class="head">
<p class="honorific" th:text="|${subscriber.displayName} 你好:|"></p>
</div>
<div class="body">
<div>
<div th:text="|${commenter} 在[(${linkTitle})]页面进行了评论|"></div>
<div>以下是评论的具体内容:</div>
</div>
<pre class="content" th:text="${content}"></pre>
</div>
<div></div>
</div>