Skip to content
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
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,39 @@ List<[#LinkGroupVo](#linkgroupvo)>
</th:block>
```

#### listBy(groupNames, limit)

##### 描述

同时获取指定多个分组下的链接,并限制返回的总数量。

##### 参数

1. `groupNames:List<string>` - 分组 `metadata.name` 列表。传入 `null` 时不限制分组。
2. `limit:int` - 限制返回的链接数量,`0` 时不限制。

##### 返回值

List<[#LinkVo](#linkvo)>

##### 示例

```html
<!-- 获取 friends 和 tools 两个分组,最多显示 10 条链接 -->
<th:block th:each="link : ${linkFinder.listBy({'friends','tools'}, 10)}">
<a th:href="${link.spec.url}" target="_blank">
<span th:text="${link.spec.displayName}"></span>
</a>
</th:block>

<!-- 获取所有链接,最多显示 20 条 -->
<th:block th:each="link : ${linkFinder.listBy(null, 20)}">
<a th:href="${link.spec.url}" target="_blank">
<span th:text="${link.spec.displayName}"></span>
</a>
</th:block>
```

### 评论适配

主题开发者可以参考 [自定义标签](https://docs.halo.run/developer-guide/theme/template-tag/#halocomment),来为友情链接接入评论功能。
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/run/halo/links/finders/LinkFinder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package run.halo.links.finders;

import java.util.List;
import reactor.core.publisher.Flux;
import run.halo.links.vo.LinkGroupVo;
import run.halo.links.vo.LinkVo;
Expand All @@ -14,5 +15,17 @@ public interface LinkFinder {

Flux<LinkVo> listBy(String group);

/**
* Lists links from specified groups with a total limit on the number of
* results.
*
* @param groupNames the list of group names to include, empty or null means all
* groups
* @param limit the maximum total number of links to return, 0 or negative
* means no limit
* @return a flux of link VOs
*/
Flux<LinkVo> listBy(List<String> groupNames, int limit);

Flux<LinkGroupVo> groupBy();
}
92 changes: 65 additions & 27 deletions src/main/java/run/halo/links/finders/impl/LinkFinderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.springframework.data.domain.Sort.Order.asc;
import static run.halo.app.extension.index.query.QueryFactory.*;

import java.util.List;
import org.springframework.data.domain.Sort;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand Down Expand Up @@ -34,36 +35,75 @@ public LinkFinderImpl(ReactiveExtensionClient client) {

@Override
public Flux<LinkVo> listBy(String groupName) {
return listByInternal(groupName);
}

@Override
public Flux<LinkVo> listBy(List<String> groupNames, int limit) {
var listOptions = new ListOptions();
var query = isNull("metadata.deletionTimestamp");
if (UNGROUPED_NAME.equals(groupName)) {
query = and(query, isNull("spec.groupName"));
} else {
query = and(query, equal("spec.groupName", groupName));

if (groupNames != null && !groupNames.isEmpty()) {
boolean hasUngrouped = groupNames.contains(UNGROUPED_NAME);
List<String> validGroupNames = groupNames.stream()
.filter(name -> !UNGROUPED_NAME.equals(name))
.toList();

var groupQuery = validGroupNames.isEmpty() ? null : in("spec.groupName", validGroupNames);

if (hasUngrouped) {
var ungroupedQuery = isNull("spec.groupName");
if (groupQuery != null) {
query = and(query, or(ungroupedQuery, groupQuery));
} else {
query = and(query, ungroupedQuery);
}
} else if (groupQuery != null) {
query = and(query, groupQuery);
}
}

listOptions.setFieldSelector(FieldSelector.of(query));
return client.listAll(Link.class, listOptions, defaultLinkSort())
.map(LinkVo::from);

if (limit > 0) {
var pageRequest = new run.halo.app.extension.PageRequestImpl(0, limit, defaultLinkSort());
return client.listBy(Link.class, listOptions, pageRequest)
.flatMapIterable(run.halo.app.extension.ListResult::getItems)
.map(LinkVo::from);
} else {
return client.listAll(Link.class, listOptions, defaultLinkSort())
.map(LinkVo::from);
}
}

@Override
public Flux<LinkGroupVo> groupBy() {
return client.listAll(LinkGroup.class, new ListOptions(), defaultGroupSort())
.map(LinkGroupVo::from)
.concatMap(group -> listBy(group.getMetadata().getName())
.collectList()
.map(group::withLinks)
.defaultIfEmpty(group)
)
.mergeWith(Mono.defer(() -> listBy(UNGROUPED_NAME)
.collectList()
// do not return ungrouped group if no links
.filter(links -> !links.isEmpty())
.flatMap(links -> ungrouped()
.map(LinkGroupVo::from)
.map(group -> group.withLinks(links))
)
));
.map(LinkGroupVo::from)
.concatMap(group -> listByInternal(group.getMetadata().getName())
.collectList()
.map(group::withLinks)
.defaultIfEmpty(group))
.mergeWith(Mono.defer(() -> listByInternal(UNGROUPED_NAME)
.collectList()
// do not return ungrouped group if no links
.filter(links -> !links.isEmpty())
.flatMap(links -> ungrouped()
.map(LinkGroupVo::from)
.map(group -> group.withLinks(links)))));
}

private Flux<LinkVo> listByInternal(String groupName) {
var listOptions = new ListOptions();
var query = isNull("metadata.deletionTimestamp");
if (UNGROUPED_NAME.equals(groupName)) {
query = and(query, isNull("spec.groupName"));
} else {
query = and(query, equal("spec.groupName", groupName));
}
listOptions.setFieldSelector(FieldSelector.of(query));
return client.listAll(Link.class, listOptions, defaultLinkSort())
.map(LinkVo::from);
}

Mono<LinkGroup> ungrouped() {
Expand All @@ -78,15 +118,13 @@ Mono<LinkGroup> ungrouped() {

static Sort defaultGroupSort() {
return Sort.by(asc("spec.priority"),
asc("metadata.creationTimestamp"),
asc("metadata.name")
);
asc("metadata.creationTimestamp"),
asc("metadata.name"));
}

static Sort defaultLinkSort() {
return Sort.by(asc("spec.priority"),
asc("metadata.creationTimestamp"),
asc("metadata.name")
);
asc("metadata.creationTimestamp"),
asc("metadata.name"));
}
}