From a442c9d4de89476b8647c9edc69e426b3076ea85 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Sun, 27 Nov 2022 22:24:48 +0800 Subject: [PATCH 01/16] chore: update issue templates for Halo 2.0 --- .github/ISSUE_TEMPLATE/bug_report.en.yml | 7 ++++--- .github/ISSUE_TEMPLATE/bug_report.zh.yml | 7 ++++--- .github/ISSUE_TEMPLATE/feature_request.en.yml | 1 - .github/ISSUE_TEMPLATE/feature_request.zh.yml | 1 - 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.en.yml b/.github/ISSUE_TEMPLATE/bug_report.en.yml index c4b35434b1..c61c0bd69f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.en.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.en.yml @@ -16,18 +16,18 @@ body: required: true attributes: label: What is version of Halo has the issue? - description: "Can be found in backstage management system." - type: dropdown id: database validations: required: true attributes: label: "What database are you using?" - description: "Can be found in backstage management system." options: - H2 + - PostgreSQL - MySQL 5.7 - MySQL 8.x + - MariaDB - Other - type: dropdown id: deployment-method @@ -36,8 +36,9 @@ body: attributes: label: "What is your deployment method?" options: - - Fat Jar - Docker + - Docker Compose + - Fat Jar - type: input id: site-url attributes: diff --git a/.github/ISSUE_TEMPLATE/bug_report.zh.yml b/.github/ISSUE_TEMPLATE/bug_report.zh.yml index 51d5422bf9..a960a1c680 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.zh.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.zh.yml @@ -17,18 +17,18 @@ body: required: true attributes: label: "是什么版本出现了此问题?" - description: "可以在管理后台的关于页面中找到。" - type: dropdown id: database validations: required: true attributes: label: "使用的什么数据库?" - description: "可以在管理后台的关于页面中找到。" options: - H2 + - PostgreSQL - MySQL 5.7 - MySQL 8.x + - MariaDB - Other - type: dropdown id: deployment-method @@ -37,8 +37,9 @@ body: attributes: label: "使用的哪种方式部署?" options: - - Fat Jar - Docker + - Docker Compose + - Fat Jar - type: input id: site-url attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.en.yml b/.github/ISSUE_TEMPLATE/feature_request.en.yml index 8d090c16f1..132fb2a0a6 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.en.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.en.yml @@ -13,7 +13,6 @@ body: id: version attributes: label: "Your current Halo version" - description: "Can be found in backstage management system." - type: markdown id: details attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.zh.yml b/.github/ISSUE_TEMPLATE/feature_request.zh.yml index 30e22ab06b..f3ea93eeed 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.zh.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.zh.yml @@ -13,7 +13,6 @@ body: id: version attributes: label: "你当前使用的版本" - description: "可以在管理后台的关于页面中找到。" - type: markdown id: details attributes: From 71b916a4169c43f11a79fb94851ca48ceb507344 Mon Sep 17 00:00:00 2001 From: John Niang Date: Sun, 9 Apr 2023 21:44:15 +0800 Subject: [PATCH 02/16] Enable mapper feature: accept case-insensitive enums (#3707) #### What type of PR is this? /kind improvement /area core #### What this PR does / why we need it: After enabling this mapper feature, we could pass a enum value with any case in request body(JSON format). See https://github.com/FasterXML/jackson-databind/blob/39fdb63607a0e7a6dbf9d6be84fe7e380e661dcb/src/test/java/com/fasterxml/jackson/databind/deser/enums/EnumDeserializationTest.java#L22 for more. #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../src/main/java/run/halo/app/config/HaloConfiguration.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/src/main/java/run/halo/app/config/HaloConfiguration.java b/application/src/main/java/run/halo/app/config/HaloConfiguration.java index fc67d21af1..ef7f8a2bca 100644 --- a/application/src/main/java/run/halo/app/config/HaloConfiguration.java +++ b/application/src/main/java/run/halo/app/config/HaloConfiguration.java @@ -1,6 +1,7 @@ package run.halo.app.config; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.MapperFeature; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -14,6 +15,7 @@ public class HaloConfiguration { Jackson2ObjectMapperBuilderCustomizer objectMapperCustomizer() { return builder -> { builder.serializationInclusion(JsonInclude.Include.NON_NULL); + builder.featuresToEnable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS); }; } } \ No newline at end of file From a8f67d3eaf42753256a57a693be7822b0a3e4f3d Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Sun, 26 Nov 2023 12:31:35 +0800 Subject: [PATCH 03/16] chore: update issue template Signed-off-by: Ryan Wang --- .github/ISSUE_TEMPLATE/bug_report.en.yml | 20 ++++---------------- .github/ISSUE_TEMPLATE/bug_report.zh.yml | 22 +++++----------------- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.en.yml b/.github/ISSUE_TEMPLATE/bug_report.en.yml index d9655561ce..44d13fdfd6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.en.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.en.yml @@ -10,25 +10,13 @@ body: id: environment attributes: value: "## Environment" - - type: input - id: version - validations: - required: true + - type: textarea + id: system-information attributes: - label: What is version of Halo has the issue? - - type: dropdown - id: database + label: "System information" + description: "Access the actuator page of the Console, click the copy button in the upper right corner, and paste the information here." validations: required: true - attributes: - label: "What database are you using?" - options: - - H2 - - PostgreSQL - - MySQL 5.7 - - MySQL 8.x - - MariaDB - - Other - type: dropdown id: operation-method validations: diff --git a/.github/ISSUE_TEMPLATE/bug_report.zh.yml b/.github/ISSUE_TEMPLATE/bug_report.zh.yml index cc1b5f04fb..9b1c0d003b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.zh.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.zh.yml @@ -11,25 +11,13 @@ body: id: environment attributes: value: "## 环境信息" - - type: input - id: version - validations: - required: true + - type: textarea + id: system-information attributes: - label: "是什么版本出现了此问题?" - - type: dropdown - id: database + label: "系统信息" + description: "访问 Console 的概览页面,点击右上角的复制按钮,将信息粘贴到此处。" validations: required: true - attributes: - label: "使用的什么数据库?" - options: - - H2 - - PostgreSQL - - MySQL 5.7 - - MySQL 8.x - - MariaDB - - Other - type: dropdown id: operation-method validations: @@ -64,7 +52,7 @@ body: id: logs attributes: label: "相关日志输出" - description: "请复制并粘贴任何相关的日志输出。 这将自动格式化为代码,因此无需反引号。" + description: "请复制并粘贴任何相关的日志输出。这将自动格式化为代码,因此无需反引号。" render: shell - type: textarea id: additional-information From b4582e3b41b4554cd19ec64d5767682760734f06 Mon Sep 17 00:00:00 2001 From: John Niang Date: Thu, 30 Nov 2023 18:46:08 +0800 Subject: [PATCH 04/16] Fix the problem of incorrect old data passed to watcher during updates (#4959) #### What type of PR is this? /kind bug /area core /milestone 2.11.0 #### What this PR does / why we need it: This PR resolves the problem of incorrect old data passed to watcher during updates. As shown in the following line, the old value should be `old` instead of `extension` from outside. https://github.com/halo-dev/halo/blob/7a84f553005b2d8047ccdb0acf473693384a7b51/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java#L172 #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../run/halo/app/extension/JsonExtension.java | 18 ++++ .../ReactiveExtensionClientImpl.java | 86 +++++++++++-------- .../run/halo/app/extension/FakeExtension.java | 13 +++ .../ReactiveExtensionClientTest.java | 38 ++++++++ 4 files changed, 118 insertions(+), 37 deletions(-) diff --git a/api/src/main/java/run/halo/app/extension/JsonExtension.java b/api/src/main/java/run/halo/app/extension/JsonExtension.java index 67ef64fc89..2bcfec3cd7 100644 --- a/api/src/main/java/run/halo/app/extension/JsonExtension.java +++ b/api/src/main/java/run/halo/app/extension/JsonExtension.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.time.Instant; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -117,6 +118,23 @@ public MetadataOperator getMetadataOrCreate() { return new ObjectNodeMetadata(metadataNode); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + JsonExtension that = (JsonExtension) o; + return Objects.equals(objectNode, that.objectNode); + } + + @Override + public int hashCode() { + return Objects.hash(objectNode); + } + class ObjectNodeMetadata implements MetadataOperator { private final ObjectNode objectNode; diff --git a/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java b/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java index a495356809..48eda2f221 100644 --- a/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java +++ b/application/src/main/java/run/halo/app/extension/ReactiveExtensionClientImpl.java @@ -4,12 +4,13 @@ import static org.springframework.util.StringUtils.hasText; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import java.time.Duration; import java.time.Instant; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.util.Predicates; @@ -137,42 +138,31 @@ public Mono create(E extension) { @SuppressWarnings("unchecked") public Mono update(E extension) { // Refactor the atomic reference if we have a better solution. - final var statusChangeOnly = new AtomicBoolean(false); - return getLatest(extension) - .map(old -> new JsonExtension(objectMapper, old)) - .flatMap(oldJsonExt -> { - var newJsonExt = new JsonExtension(objectMapper, extension); - // reset some mandatory fields - var oldMetadata = oldJsonExt.getMetadata(); - var newMetadata = newJsonExt.getMetadata(); - newMetadata.setCreationTimestamp(oldMetadata.getCreationTimestamp()); - newMetadata.setGenerateName(oldMetadata.getGenerateName()); - - var oldObjectNode = oldJsonExt.getInternal().deepCopy(); - var newObjectNode = newJsonExt.getInternal().deepCopy(); - if (Objects.equals(oldObjectNode, newObjectNode)) { - // if no data were changed, just skip updating. - return Mono.empty(); - } - // check status is changed - oldObjectNode.remove("status"); - newObjectNode.remove("status"); - if (Objects.equals(oldObjectNode, newObjectNode)) { - statusChangeOnly.set(true); - } - return Mono.just(newJsonExt); - }) - .map(converter::convertTo) - .flatMap(extensionStore -> client.update(extensionStore.getName(), - extensionStore.getVersion(), - extensionStore.getData())) - .map(updated -> converter.convertFrom((Class) extension.getClass(), updated)) - .doOnNext(updated -> { - if (!statusChangeOnly.get()) { - watchers.onUpdate(extension, updated); - } - }) - .switchIfEmpty(Mono.defer(() -> Mono.just(extension))); + return getLatest(extension).flatMap(old -> { + var oldJsonExt = new JsonExtension(objectMapper, old); + var newJsonExt = new JsonExtension(objectMapper, extension); + // reset some mandatory fields + var oldMetadata = oldJsonExt.getMetadata(); + var newMetadata = newJsonExt.getMetadata(); + newMetadata.setCreationTimestamp(oldMetadata.getCreationTimestamp()); + newMetadata.setGenerateName(oldMetadata.getGenerateName()); + + if (Objects.equals(oldJsonExt, newJsonExt)) { + // skip updating if not data changed. + return Mono.just(extension); + } + + var onlyStatusChanged = + isOnlyStatusChanged(oldJsonExt.getInternal(), newJsonExt.getInternal()); + + var store = this.converter.convertTo(newJsonExt); + var updated = client.update(store.getName(), store.getVersion(), store.getData()) + .map(ext -> converter.convertFrom((Class) extension.getClass(), ext)); + if (!onlyStatusChanged) { + updated = updated.doOnNext(ext -> watchers.onUpdate(old, ext)); + } + return updated; + }); } private Mono getLatest(Extension extension) { @@ -199,4 +189,26 @@ public void watch(Watcher watcher) { this.watchers.addWatcher(watcher); } + private static boolean isOnlyStatusChanged(ObjectNode oldNode, ObjectNode newNode) { + if (Objects.equals(oldNode, newNode)) { + return false; + } + // WARNING!!! + // Do not edit the ObjectNode + var oldFields = new HashSet(); + var newFields = new HashSet(); + oldNode.fieldNames().forEachRemaining(oldFields::add); + newNode.fieldNames().forEachRemaining(newFields::add); + oldFields.remove("status"); + newFields.remove("status"); + if (!Objects.equals(oldFields, newFields)) { + return false; + } + for (var field : oldFields) { + if (!Objects.equals(oldNode.get(field), newNode.get(field))) { + return false; + } + } + return true; + } } diff --git a/application/src/test/java/run/halo/app/extension/FakeExtension.java b/application/src/test/java/run/halo/app/extension/FakeExtension.java index d0e5cb03bf..5933c37a3c 100644 --- a/application/src/test/java/run/halo/app/extension/FakeExtension.java +++ b/application/src/test/java/run/halo/app/extension/FakeExtension.java @@ -1,12 +1,21 @@ package run.halo.app.extension; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + @GVK(group = "fake.halo.run", version = "v1alpha1", kind = "Fake", plural = "fakes", singular = "fake") +@Data +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) public class FakeExtension extends AbstractExtension { + private FakeStatus status = new FakeStatus(); + public static FakeExtension createFake(String name) { var metadata = new Metadata(); metadata.setName(name); @@ -15,4 +24,8 @@ public static FakeExtension createFake(String name) { return fake; } + @Data + public static class FakeStatus { + private String state; + } } diff --git a/application/src/test/java/run/halo/app/extension/ReactiveExtensionClientTest.java b/application/src/test/java/run/halo/app/extension/ReactiveExtensionClientTest.java index 7e60e74f6d..0f83fbe092 100644 --- a/application/src/test/java/run/halo/app/extension/ReactiveExtensionClientTest.java +++ b/application/src/test/java/run/halo/app/extension/ReactiveExtensionClientTest.java @@ -455,6 +455,37 @@ void shouldNotUpdateIfExtensionNotChange() { verify(storeClient, never()).update(any(), any(), any()); } + @Test + void shouldUpdateIfExtensionStatusChangedOnly() { + var fake = createFakeExtension("fake", 2L); + fake.getStatus().setState("new-state"); + var storeName = "/registry/fake.halo.run/fakes/fake"; + when(converter.convertTo(any())).thenReturn( + createExtensionStore(storeName, 2L)); + when(storeClient.update(any(), any(), any())).thenReturn( + Mono.just(createExtensionStore(storeName, 2L))); + when(storeClient.fetchByName(storeName)).thenReturn( + Mono.just(createExtensionStore(storeName, 1L))); + + var oldFake = createFakeExtension("fake", 2L); + oldFake.getStatus().setState("old-state"); + + var updatedFake = createFakeExtension("fake", 3L); + when(converter.convertFrom(same(FakeExtension.class), any())) + .thenReturn(oldFake) + .thenReturn(updatedFake); + + StepVerifier.create(client.update(fake)) + .expectNext(updatedFake) + .verifyComplete(); + + verify(storeClient).fetchByName(storeName); + verify(converter).convertTo(isA(JsonExtension.class)); + verify(converter, times(2)).convertFrom(same(FakeExtension.class), any()); + verify(storeClient) + .update(eq("/registry/fake.halo.run/fakes/fake"), eq(2L), any()); + } + @Test void shouldUpdateUnstructuredSuccessfully() throws JsonProcessingException { var fake = createUnstructured(); @@ -539,6 +570,13 @@ void shouldNotWatchOnUpdateIfExtensionNotChange() { verify(watcher, never()).onUpdate(any(), any()); } + @Test + void shouldNotWatchOnUpdateIfExtensionStatusChangeOnly() { + shouldUpdateIfExtensionStatusChangedOnly(); + + verify(watcher, never()).onUpdate(any(), any()); + } + @Test void shouldWatchOnDeleteSuccessfully() { doNothing().when(watcher).onDelete(any()); From 23fd3bf8ee3933995a53587cd32e65668074808f Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Thu, 30 Nov 2023 18:56:10 +0800 Subject: [PATCH 05/16] feat: refine i18n for uc (#4957) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /area console /kind improvement /milestone 2.11.x #### What this PR does / why we need it: 完善个人中心相关页面的 i18n。 #### Special notes for your reviewer: 测试各个语言的个人中心相关页面。 #### Does this PR introduce a user-facing change? ```release-note 完善个人中心相关页面的 i18n。 ``` --- console/console-src/layouts/BasicLayout.vue | 3 +- .../roles/components/RoleEditingModal.vue | 2 +- .../modules/system/users/UserDetail.vue | 2 +- .../users/widgets/NotificationWidget.vue | 4 +- .../packages/editor/src/locales/zh-CN.yaml | 2 +- .../src/components/user-avatar/UserAvatar.vue | 8 +- .../user-avatar/UserAvatarCropper.vue | 12 +- console/src/locales/en.yaml | 444 ++- console/src/locales/es.yaml | 2544 +++++++++-------- console/src/locales/zh-CN.yaml | 292 +- console/src/locales/zh-TW.yaml | 292 +- console/uc-src/layouts/BasicLayout.vue | 3 +- .../modules/contents/posts/PostEditor.vue | 4 +- .../modules/contents/posts/PostList.vue | 14 +- .../posts/components/PostListItem.vue | 12 +- .../posts/components/PostSettingEditModal.vue | 2 +- .../uc-src/modules/contents/posts/module.ts | 6 +- .../modules/notifications/Notifications.vue | 10 +- .../components/NotificationListItem.vue | 10 +- .../uc-src/modules/notifications/module.ts | 4 +- console/uc-src/modules/profile/Profile.vue | 10 +- .../profile/components/EmailVerifyModal.vue | 52 +- .../components/PasswordChangeModal.vue | 10 +- .../PersonalAccessTokenCreationModal.vue | 24 +- .../PersonalAccessTokenListItem.vue | 26 +- .../components/ProfileEditingModal.vue | 18 +- console/uc-src/modules/profile/module.ts | 4 +- .../uc-src/modules/profile/tabs/Detail.vue | 38 +- .../profile/tabs/NotificationPreferences.vue | 2 +- .../profile/tabs/PersonalAccessTokens.vue | 4 +- 30 files changed, 2155 insertions(+), 1703 deletions(-) diff --git a/console/console-src/layouts/BasicLayout.vue b/console/console-src/layouts/BasicLayout.vue index 60244afec4..31090de702 100644 --- a/console/console-src/layouts/BasicLayout.vue +++ b/console/console-src/layouts/BasicLayout.vue @@ -179,7 +179,7 @@ onMounted(() => {
@@ -188,6 +188,7 @@ onMounted(() => { />
diff --git a/console/console-src/modules/system/roles/components/RoleEditingModal.vue b/console/console-src/modules/system/roles/components/RoleEditingModal.vue index c093e5754c..35c8e8b823 100644 --- a/console/console-src/modules/system/roles/components/RoleEditingModal.vue +++ b/console/console-src/modules/system/roles/components/RoleEditingModal.vue @@ -161,7 +161,7 @@ const handleResetForm = () => { ] " type="text" - label="登录之后默认跳转位置" + :label="$t('core.role.editing_modal.fields.redirect_on_login')" >
diff --git a/console/console-src/modules/system/users/UserDetail.vue b/console/console-src/modules/system/users/UserDetail.vue index da3a3a6dfc..f7f607b5ab 100644 --- a/console/console-src/modules/system/users/UserDetail.vue +++ b/console/console-src/modules/system/users/UserDetail.vue @@ -123,7 +123,7 @@ function handleRouteToUC() { type="primary" @click="handleRouteToUC" > - 个人中心 + {{ $t("core.user.detail.actions.profile.title") }} diff --git a/console/console-src/modules/system/users/widgets/NotificationWidget.vue b/console/console-src/modules/system/users/widgets/NotificationWidget.vue index b9239e58a6..14d2880fce 100644 --- a/console/console-src/modules/system/users/widgets/NotificationWidget.vue +++ b/console/console-src/modules/system/users/widgets/NotificationWidget.vue @@ -44,7 +44,7 @@ function handleRouteToNotification(notification: Notification) {