Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
ruibaby committed Nov 30, 2023
2 parents 2b5b89e + 7a84f55 commit 4f59c65
Show file tree
Hide file tree
Showing 93 changed files with 5,804 additions and 309 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/halo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ jobs:
repo=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')
docker tag ghcr.io/${repo}/halo-dev:pr-${{ github.event.number }} ghcr.io/halo-dev/halo-dev:main
cd e2e && ./start.sh
cd e2e && make all
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* @param content is binary data of the attachment file.
* @param mediaType is media type of the attachment file.
*/
record SimpleFilePart(
public record SimpleFilePart(
String filename,
Flux<DataBuffer> content,
MediaType mediaType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import run.halo.app.extension.AbstractExtension;
import run.halo.app.extension.GVK;
Expand All @@ -27,6 +28,8 @@
public class Snapshot extends AbstractExtension {
public static final String KIND = "Snapshot";
public static final String KEEP_RAW_ANNO = "content.halo.run/keep-raw";
public static final String PATCHED_CONTENT_ANNO = "content.halo.run/patched-content";
public static final String PATCHED_RAW_ANNO = "content.halo.run/patched-raw";

@Schema(requiredMode = REQUIRED)
private SnapShotSpec spec;
Expand Down Expand Up @@ -67,4 +70,18 @@ public static void addContributor(Snapshot snapshot, String name) {
contributors.add(name);
}

/**
* Check if the given snapshot is a base snapshot.
*
* @param snapshot must not be null.
* @return true if the given snapshot is a base snapshot; false otherwise.
*/
public static boolean isBaseSnapshot(@NonNull Snapshot snapshot) {
var annotations = snapshot.getMetadata().getAnnotations();
if (annotations == null) {
return false;
}
return Boolean.parseBoolean(annotations.get(Snapshot.KEEP_RAW_ANNO));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import java.net.URI;
import java.time.Duration;
import java.util.function.Consumer;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import reactor.core.publisher.Flux;
Expand All @@ -18,6 +20,25 @@
*/
public interface AttachmentService {

/**
* Uploads the given attachment to specific storage using handlers in plugins.
* <p>
* If no handler can be found to upload the given attachment, ServerError exception will be
* thrown.
*
* @param policyName is attachment policy name.
* @param groupName is group name the attachment belongs.
* @param filePart contains filename, content and media type.
* @param beforeCreating is an attachment modifier before creating.
* @return attachment.
*/
Mono<Attachment> upload(
@NonNull String username,
@NonNull String policyName,
@Nullable String groupName,
@NonNull FilePart filePart,
@Nullable Consumer<Attachment> beforeCreating);

/**
* Uploads the given attachment to specific storage using handlers in plugins. Please note
* that we will make sure the request is authenticated, or an unauthorized exception throws.
Expand Down
15 changes: 15 additions & 0 deletions api/src/main/java/run/halo/app/extension/Ref.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
import lombok.Data;
import org.springframework.lang.NonNull;

@Data
@Schema(description = "Extension reference object. The name is mandatory")
Expand Down Expand Up @@ -59,4 +60,18 @@ public static boolean groupKindEquals(Ref ref, GroupVersionKind gvk) {
return Objects.equals(ref.getGroup(), gvk.group())
&& Objects.equals(ref.getKind(), gvk.kind());
}

/**
* Check if the extension is equal to the ref.
*
* @param ref must not be null.
* @param extension must not be null.
* @return true if they are equal; false otherwise.
*/
public static boolean equals(@NonNull Ref ref, @NonNull ExtensionOperator extension) {
var gvk = extension.groupVersionKind();
var name = extension.getMetadata().getName();
return groupKindEquals(ref, gvk) && Objects.equals(ref.getName(), name);
}

}
3 changes: 3 additions & 0 deletions api/src/main/java/run/halo/app/infra/SystemSetting.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ public static class Post {
Integer tagPageSize;
Boolean review;
String slugGenerationStrategy;

String attachmentPolicyName;
String attachmentGroupName;
}

@Data
Expand Down
45 changes: 45 additions & 0 deletions api/src/main/java/run/halo/app/theme/TemplateNameResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package run.halo.app.theme;

import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
* <p>The {@link TemplateNameResolver} is used to resolve template name.</p>
* <code>Halo</code> has a theme mechanism, template files are provided by different themes, so
* we need a method to determine whether the template file exists in the activated theme and if
* it does not exist, provide a default template name.
*
* @author guqing
* @since 2.11.0
*/
public interface TemplateNameResolver {

/**
* Resolve template name if exists or default template name in classpath.
*
* @param exchange exchange to resolve theme to use
* @param name template
* @return template name if exists or default template name in classpath
*/
Mono<String> resolveTemplateNameOrDefault(ServerWebExchange exchange, String name);

/**
* Resolve template name if exists or default template given.
*
* @param exchange exchange to resolve theme to use
* @param name template name
* @param defaultName default template name to use if given template name not exists
* @return template name if exists or default template name given
*/
Mono<String> resolveTemplateNameOrDefault(ServerWebExchange exchange, String name,
String defaultName);

/**
* Determine whether the template file exists in the current theme.
*
* @param exchange exchange to resolve theme to use
* @param name template name
* @return <code>true</code> if the template file exists in the current theme, false otherwise
*/
Mono<Boolean> isTemplateAvailableInTheme(ServerWebExchange exchange, String name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ GroupedOpenApi pluginCustomApi() {
.build();
}

@Bean
GroupedOpenApi userCenterApi() {
return GroupedOpenApi.builder()
.group("uc.api")
.displayName("User center APIs.")
.pathsToMatch("/apis/uc.api.*/**")
.build();
}

@Bean
GroupedOpenApi allApi() {
return GroupedOpenApi.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ public Mono<ContentWrapper> getContent(String snapshotName, String baseSnapshotN

protected void checkBaseSnapshot(Snapshot snapshot) {
Assert.notNull(snapshot, "The snapshot must not be null.");
String keepRawAnno =
MetadataUtil.nullSafeAnnotations(snapshot).get(Snapshot.KEEP_RAW_ANNO);
if (!StringUtils.equals(Boolean.TRUE.toString(), keepRawAnno)) {
if (!Snapshot.isBaseSnapshot(snapshot)) {
throw new IllegalArgumentException(
String.format("The snapshot [%s] is not a base snapshot.",
snapshot.getMetadata().getName()));
Expand All @@ -68,7 +66,7 @@ protected Mono<ContentWrapper> draftContent(@Nullable String baseSnapshotName,
snapshot.getSpec().setParentSnapshotName(parentSnapshotName);

final String baseSnapshotNameToUse =
StringUtils.defaultString(baseSnapshotName, snapshot.getMetadata().getName());
StringUtils.defaultIfBlank(baseSnapshotName, snapshot.getMetadata().getName());
return client.fetch(Snapshot.class, baseSnapshotName)
.doOnNext(this::checkBaseSnapshot)
.defaultIfEmpty(snapshot)
Expand Down Expand Up @@ -119,7 +117,8 @@ protected Mono<ContentWrapper> restoredContent(String baseSnapshotName, Snapshot
.map(baseSnapshot -> ContentWrapper.patchSnapshot(headSnapshot, baseSnapshot));
}

protected Snapshot determineRawAndContentPatch(Snapshot snapshotToUse, Snapshot baseSnapshot,
protected Snapshot determineRawAndContentPatch(Snapshot snapshotToUse,
Snapshot baseSnapshot,
ContentRequest contentRequest) {
Assert.notNull(baseSnapshot, "The baseSnapshot must not be null.");
Assert.notNull(contentRequest, "The contentRequest must not be null.");
Expand All @@ -130,7 +129,7 @@ protected Snapshot determineRawAndContentPatch(Snapshot snapshotToUse, Snapshot

snapshotToUse.getSpec().setLastModifyTime(Instant.now());
// it is the v1 snapshot, set the content directly
if (org.thymeleaf.util.StringUtils.equals(baseSnapshotName,
if (StringUtils.equals(baseSnapshotName,
snapshotToUse.getMetadata().getName())) {
snapshotToUse.getSpec().setRawPatch(contentRequest.raw());
snapshotToUse.getSpec().setContentPatch(contentRequest.content());
Expand Down
4 changes: 4 additions & 0 deletions application/src/main/java/run/halo/app/content/Content.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package run.halo.app.content;

public record Content(String raw, String content, String rawType) {
}
53 changes: 42 additions & 11 deletions application/src/main/java/run/halo/app/content/PostQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
import static java.util.Comparator.comparing;
import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToPredicate;

import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import run.halo.app.core.extension.content.Post;
Expand All @@ -31,9 +34,22 @@ public class PostQuery extends IListRequest.QueryListRequest {

private final ServerWebExchange exchange;

private final String username;

public PostQuery(ServerRequest request) {
this(request, null);
}

public PostQuery(ServerRequest request, @Nullable String username) {
super(request.queryParams());
this.exchange = request.exchange();
this.username = username;
}

@Schema(hidden = true)
@JsonIgnore
public String getUsername() {
return username;
}

@Nullable
Expand Down Expand Up @@ -131,14 +147,27 @@ public Comparator<Post> toComparator() {
* @return a predicate
*/
public Predicate<Post> toPredicate() {
Predicate<Post> paramPredicate = post ->
contains(getCategories(), post.getSpec().getCategories())
&& contains(getTags(), post.getSpec().getTags())
&& contains(getContributors(), post.getStatusOrDefault().getContributors());
Predicate<Post> predicate = labelAndFieldSelectorToPredicate(getLabelSelector(),
getFieldSelector());

if (!CollectionUtils.isEmpty(getCategories())) {
predicate =
predicate.and(post -> contains(getCategories(), post.getSpec().getCategories()));
}
if (!CollectionUtils.isEmpty(getTags())) {
predicate = predicate.and(post -> contains(getTags(), post.getSpec().getTags()));
}
if (!CollectionUtils.isEmpty(getContributors())) {
Predicate<Post> hasStatus = post -> post.getStatus() != null;
var containsContributors = hasStatus.and(
post -> contains(getContributors(), post.getStatus().getContributors())
);
predicate = predicate.and(containsContributors);
}

String keyword = getKeyword();
if (keyword != null) {
paramPredicate = paramPredicate.and(post -> {
predicate = predicate.and(post -> {
String excerpt = post.getStatusOrDefault().getExcerpt();
return StringUtils.containsIgnoreCase(excerpt, keyword)
|| StringUtils.containsIgnoreCase(post.getSpec().getSlug(), keyword)
Expand All @@ -148,7 +177,7 @@ && contains(getTags(), post.getSpec().getTags())

Post.PostPhase publishPhase = getPublishPhase();
if (publishPhase != null) {
paramPredicate = paramPredicate.and(post -> {
predicate = predicate.and(post -> {
if (Post.PostPhase.PENDING_APPROVAL.equals(publishPhase)) {
return !post.isPublished()
&& Post.PostPhase.PENDING_APPROVAL.name()
Expand All @@ -165,13 +194,15 @@ && contains(getTags(), post.getSpec().getTags())

Post.VisibleEnum visible = getVisible();
if (visible != null) {
paramPredicate =
paramPredicate.and(post -> visible.equals(post.getSpec().getVisible()));
predicate =
predicate.and(post -> visible.equals(post.getSpec().getVisible()));
}

Predicate<Post> predicate = labelAndFieldSelectorToPredicate(getLabelSelector(),
getFieldSelector());
return predicate.and(paramPredicate);
if (StringUtils.isNotBlank(username)) {
Predicate<Post> isOwner = post -> Objects.equals(username, post.getSpec().getOwner());
predicate = predicate.and(isOwner);
}
return predicate;
}

boolean contains(Collection<String> left, List<String> right) {
Expand Down
13 changes: 7 additions & 6 deletions application/src/main/java/run/halo/app/content/PostRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.lang.NonNull;
import run.halo.app.core.extension.content.Post;
import run.halo.app.extension.Ref;

/**
* Post and content data for creating and updating post.
*
* @author guqing
* @since 2.0.0
*/
public record PostRequest(@Schema(requiredMode = REQUIRED) Post post,
@Schema(requiredMode = REQUIRED) Content content) {
public record PostRequest(@Schema(requiredMode = REQUIRED) @NonNull Post post,
Content content) {

public ContentRequest contentRequest() {
Ref subjectRef = Ref.of(post);
return new ContentRequest(subjectRef, post.getSpec().getHeadSnapshot(), content.raw,
content.content, content.rawType);
return new ContentRequest(subjectRef, post.getSpec().getHeadSnapshot(), content.raw(),
content.content(), content.rawType());
}

public record Content(String raw, String content, String rawType) {
}
}
Loading

0 comments on commit 4f59c65

Please sign in to comment.