diff --git a/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationPreferencesEndpoint.java b/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationPreferencesEndpoint.java index 90948904d..9916d90ed 100644 --- a/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationPreferencesEndpoint.java +++ b/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationPreferencesEndpoint.java @@ -5,17 +5,20 @@ import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder; +import com.fasterxml.jackson.core.type.TypeReference; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Schema; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @@ -25,12 +28,15 @@ import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Mono; import reactor.util.function.Tuples; +import run.halo.app.core.extension.Role; import run.halo.app.core.extension.endpoint.CustomEndpoint; import run.halo.app.core.extension.notification.NotifierDescriptor; import run.halo.app.core.extension.notification.ReasonType; import run.halo.app.extension.Comparators; import run.halo.app.extension.GroupVersion; +import run.halo.app.extension.MetadataUtil; import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.utils.JsonUtils; import run.halo.app.notification.UserNotificationPreference; import run.halo.app.notification.UserNotificationPreferenceService; @@ -135,10 +141,7 @@ public class UserNotificationPreferencesEndpoint implements CustomEndpoint { Mono<ReasonTypeNotifierMatrix> listReasonTypeNotifierMatrix(String username) { return client.list(ReasonType.class, null, Comparators.defaultComparator()) - .map(reasonType -> new ReasonTypeInfo(reasonType.getMetadata().getName(), - reasonType.getSpec().getDisplayName(), - reasonType.getSpec().getDescription()) - ) + .map(ReasonTypeInfo::from) .collectList() .flatMap(reasonTypes -> client.list(NotifierDescriptor.class, null, Comparators.defaultComparator()) @@ -188,7 +191,23 @@ public class UserNotificationPreferencesEndpoint implements CustomEndpoint { private boolean[][] stateMatrix; } - record ReasonTypeInfo(String name, String displayName, String description) { + record ReasonTypeInfo(String name, String displayName, String description, + Set<String> uiPermissions) { + + public static ReasonTypeInfo from(ReasonType reasonType) { + var uiPermissions = Optional.of(MetadataUtil.nullSafeAnnotations(reasonType)) + .map(annotations -> annotations.get(Role.UI_PERMISSIONS_ANNO)) + .filter(StringUtils::isNotBlank) + .map(uiPermissionStr -> JsonUtils.jsonToObject(uiPermissionStr, + new TypeReference<Set<String>>() { + }) + ) + .orElse(Set.of()); + return new ReasonTypeInfo(reasonType.getMetadata().getName(), + reasonType.getSpec().getDisplayName(), + reasonType.getSpec().getDescription(), + uiPermissions); + } } record NotifierInfo(String name, String displayName, String description) { diff --git a/application/src/main/resources/extensions/notification.yaml b/application/src/main/resources/extensions/notification.yaml index 23429a95a..3fd11c919 100644 --- a/application/src/main/resources/extensions/notification.yaml +++ b/application/src/main/resources/extensions/notification.yaml @@ -64,6 +64,9 @@ apiVersion: notification.halo.run/v1alpha1 kind: ReasonType metadata: name: new-comment-on-post + annotations: + rbac.authorization.halo.run/ui-permissions: | + [ "uc:posts:publish" ] spec: displayName: "我的文章收到新评论" description: "如果有读者在你的文章下方留下了新的评论,你将会收到一条通知,告诉你有新的评论。 @@ -90,6 +93,9 @@ apiVersion: notification.halo.run/v1alpha1 kind: ReasonType metadata: name: new-comment-on-single-page + annotations: + rbac.authorization.halo.run/ui-permissions: | + [ "system:singlepages:manage" ] spec: displayName: "我的自定义页面收到新评论" description: "当你创建的自定义页面收到新评论时,你将会收到一条通知,告诉你有新的评论。" diff --git a/console/packages/api-client/src/models/post-status.ts b/console/packages/api-client/src/models/post-status.ts index dbc5bd93a..b59635bcc 100644 --- a/console/packages/api-client/src/models/post-status.ts +++ b/console/packages/api-client/src/models/post-status.ts @@ -58,6 +58,12 @@ export interface PostStatus { * @memberof PostStatus */ lastModifyTime?: string; + /** + * + * @type {number} + * @memberof PostStatus + */ + observedVersion?: number; /** * * @type {string} diff --git a/console/packages/api-client/src/models/reason-type-info.ts b/console/packages/api-client/src/models/reason-type-info.ts index 1008af2c5..422b2cabd 100644 --- a/console/packages/api-client/src/models/reason-type-info.ts +++ b/console/packages/api-client/src/models/reason-type-info.ts @@ -36,4 +36,10 @@ export interface ReasonTypeInfo { * @memberof ReasonTypeInfo */ name?: string; + /** + * + * @type {Array<string>} + * @memberof ReasonTypeInfo + */ + uiPermissions?: Array<string>; } diff --git a/console/packages/api-client/src/models/single-page-status.ts b/console/packages/api-client/src/models/single-page-status.ts index 29c74e359..6972c4237 100644 --- a/console/packages/api-client/src/models/single-page-status.ts +++ b/console/packages/api-client/src/models/single-page-status.ts @@ -58,6 +58,12 @@ export interface SinglePageStatus { * @memberof SinglePageStatus */ lastModifyTime?: string; + /** + * + * @type {number} + * @memberof SinglePageStatus + */ + observedVersion?: number; /** * * @type {string} diff --git a/console/uc-src/modules/profile/tabs/NotificationPreferences.vue b/console/uc-src/modules/profile/tabs/NotificationPreferences.vue index 9d9663554..32b3d457c 100644 --- a/console/uc-src/modules/profile/tabs/NotificationPreferences.vue +++ b/console/uc-src/modules/profile/tabs/NotificationPreferences.vue @@ -8,6 +8,7 @@ import { computed } from "vue"; import { inject } from "vue"; import { cloneDeep } from "lodash-es"; import type { ReasonTypeNotifierRequest } from "@halo-dev/api-client"; +import HasPermission from "@/components/permission/HasPermission.vue"; const queryClient = useQueryClient(); @@ -114,37 +115,41 @@ const { </tr> </thead> <tbody class="divide-y divide-gray-100 bg-white"> - <tr + <template v-for="(reasonType, index) in data?.reasonTypes" :key="reasonType.name" > - <td - class="whitespace-nowrap px-4 py-3 text-sm font-medium text-gray-900" - > - {{ reasonType.displayName }} - </td> - <td - v-for="(notifier, notifierIndex) in data?.notifiers" - :key="notifier.name" - class="whitespace-nowrap px-4 py-3 text-sm text-gray-500" - > - <VSwitch - :model-value="data?.stateMatrix?.[index][notifierIndex]" - :loading=" - mutating && - variables?.reasonTypeIndex === index && - variables?.notifierIndex === notifierIndex - " - @change=" - mutate({ - state: !data?.stateMatrix?.[index][notifierIndex], - reasonTypeIndex: index, - notifierIndex: notifierIndex, - }) - " - /> - </td> - </tr> + <HasPermission :permissions="reasonType.uiPermissions || []"> + <tr> + <td + class="whitespace-nowrap px-4 py-3 text-sm font-medium text-gray-900" + > + {{ reasonType.displayName }} + </td> + <td + v-for="(notifier, notifierIndex) in data?.notifiers" + :key="notifier.name" + class="whitespace-nowrap px-4 py-3 text-sm text-gray-500" + > + <VSwitch + :model-value="data?.stateMatrix?.[index][notifierIndex]" + :loading=" + mutating && + variables?.reasonTypeIndex === index && + variables?.notifierIndex === notifierIndex + " + @change=" + mutate({ + state: !data?.stateMatrix?.[index][notifierIndex], + reasonTypeIndex: index, + notifierIndex: notifierIndex, + }) + " + /> + </td> + </tr> + </HasPermission> + </template> </tbody> </table> </div>