mirror of https://github.com/halo-dev/halo
feat: add supports for ui permissions of notification reason type (#5286)
#### What type of PR is this? /kind feature /area core /area console /milestone 2.12.x #### What this PR does / why we need it: 为通知类型设置添加 UI 权限判断。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/4728 #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note 为通知类型设置添加 UI 权限判断。 ```pull/5310/head v2.12.0
parent
dff6522d43
commit
e85a55e416
|
@ -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.parameter.Builder.parameterBuilder;
|
||||||
import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder;
|
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.enums.ParameterIn;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
|
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.stereotype.Component;
|
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 org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.util.function.Tuples;
|
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.endpoint.CustomEndpoint;
|
||||||
import run.halo.app.core.extension.notification.NotifierDescriptor;
|
import run.halo.app.core.extension.notification.NotifierDescriptor;
|
||||||
import run.halo.app.core.extension.notification.ReasonType;
|
import run.halo.app.core.extension.notification.ReasonType;
|
||||||
import run.halo.app.extension.Comparators;
|
import run.halo.app.extension.Comparators;
|
||||||
import run.halo.app.extension.GroupVersion;
|
import run.halo.app.extension.GroupVersion;
|
||||||
|
import run.halo.app.extension.MetadataUtil;
|
||||||
import run.halo.app.extension.ReactiveExtensionClient;
|
import run.halo.app.extension.ReactiveExtensionClient;
|
||||||
|
import run.halo.app.infra.utils.JsonUtils;
|
||||||
import run.halo.app.notification.UserNotificationPreference;
|
import run.halo.app.notification.UserNotificationPreference;
|
||||||
import run.halo.app.notification.UserNotificationPreferenceService;
|
import run.halo.app.notification.UserNotificationPreferenceService;
|
||||||
|
|
||||||
|
@ -135,10 +141,7 @@ public class UserNotificationPreferencesEndpoint implements CustomEndpoint {
|
||||||
|
|
||||||
Mono<ReasonTypeNotifierMatrix> listReasonTypeNotifierMatrix(String username) {
|
Mono<ReasonTypeNotifierMatrix> listReasonTypeNotifierMatrix(String username) {
|
||||||
return client.list(ReasonType.class, null, Comparators.defaultComparator())
|
return client.list(ReasonType.class, null, Comparators.defaultComparator())
|
||||||
.map(reasonType -> new ReasonTypeInfo(reasonType.getMetadata().getName(),
|
.map(ReasonTypeInfo::from)
|
||||||
reasonType.getSpec().getDisplayName(),
|
|
||||||
reasonType.getSpec().getDescription())
|
|
||||||
)
|
|
||||||
.collectList()
|
.collectList()
|
||||||
.flatMap(reasonTypes -> client.list(NotifierDescriptor.class, null,
|
.flatMap(reasonTypes -> client.list(NotifierDescriptor.class, null,
|
||||||
Comparators.defaultComparator())
|
Comparators.defaultComparator())
|
||||||
|
@ -188,7 +191,23 @@ public class UserNotificationPreferencesEndpoint implements CustomEndpoint {
|
||||||
private boolean[][] stateMatrix;
|
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) {
|
record NotifierInfo(String name, String displayName, String description) {
|
||||||
|
|
|
@ -64,6 +64,9 @@ apiVersion: notification.halo.run/v1alpha1
|
||||||
kind: ReasonType
|
kind: ReasonType
|
||||||
metadata:
|
metadata:
|
||||||
name: new-comment-on-post
|
name: new-comment-on-post
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.halo.run/ui-permissions: |
|
||||||
|
[ "uc:posts:publish" ]
|
||||||
spec:
|
spec:
|
||||||
displayName: "我的文章收到新评论"
|
displayName: "我的文章收到新评论"
|
||||||
description: "如果有读者在你的文章下方留下了新的评论,你将会收到一条通知,告诉你有新的评论。
|
description: "如果有读者在你的文章下方留下了新的评论,你将会收到一条通知,告诉你有新的评论。
|
||||||
|
@ -90,6 +93,9 @@ apiVersion: notification.halo.run/v1alpha1
|
||||||
kind: ReasonType
|
kind: ReasonType
|
||||||
metadata:
|
metadata:
|
||||||
name: new-comment-on-single-page
|
name: new-comment-on-single-page
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.halo.run/ui-permissions: |
|
||||||
|
[ "system:singlepages:manage" ]
|
||||||
spec:
|
spec:
|
||||||
displayName: "我的自定义页面收到新评论"
|
displayName: "我的自定义页面收到新评论"
|
||||||
description: "当你创建的自定义页面收到新评论时,你将会收到一条通知,告诉你有新的评论。"
|
description: "当你创建的自定义页面收到新评论时,你将会收到一条通知,告诉你有新的评论。"
|
||||||
|
|
|
@ -58,6 +58,12 @@ export interface PostStatus {
|
||||||
* @memberof PostStatus
|
* @memberof PostStatus
|
||||||
*/
|
*/
|
||||||
lastModifyTime?: string;
|
lastModifyTime?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof PostStatus
|
||||||
|
*/
|
||||||
|
observedVersion?: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
|
|
@ -36,4 +36,10 @@ export interface ReasonTypeInfo {
|
||||||
* @memberof ReasonTypeInfo
|
* @memberof ReasonTypeInfo
|
||||||
*/
|
*/
|
||||||
name?: string;
|
name?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<string>}
|
||||||
|
* @memberof ReasonTypeInfo
|
||||||
|
*/
|
||||||
|
uiPermissions?: Array<string>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,12 @@ export interface SinglePageStatus {
|
||||||
* @memberof SinglePageStatus
|
* @memberof SinglePageStatus
|
||||||
*/
|
*/
|
||||||
lastModifyTime?: string;
|
lastModifyTime?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof SinglePageStatus
|
||||||
|
*/
|
||||||
|
observedVersion?: number;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { computed } from "vue";
|
||||||
import { inject } from "vue";
|
import { inject } from "vue";
|
||||||
import { cloneDeep } from "lodash-es";
|
import { cloneDeep } from "lodash-es";
|
||||||
import type { ReasonTypeNotifierRequest } from "@halo-dev/api-client";
|
import type { ReasonTypeNotifierRequest } from "@halo-dev/api-client";
|
||||||
|
import HasPermission from "@/components/permission/HasPermission.vue";
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
@ -114,37 +115,41 @@ const {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-gray-100 bg-white">
|
<tbody class="divide-y divide-gray-100 bg-white">
|
||||||
<tr
|
<template
|
||||||
v-for="(reasonType, index) in data?.reasonTypes"
|
v-for="(reasonType, index) in data?.reasonTypes"
|
||||||
:key="reasonType.name"
|
:key="reasonType.name"
|
||||||
>
|
>
|
||||||
<td
|
<HasPermission :permissions="reasonType.uiPermissions || []">
|
||||||
class="whitespace-nowrap px-4 py-3 text-sm font-medium text-gray-900"
|
<tr>
|
||||||
>
|
<td
|
||||||
{{ reasonType.displayName }}
|
class="whitespace-nowrap px-4 py-3 text-sm font-medium text-gray-900"
|
||||||
</td>
|
>
|
||||||
<td
|
{{ reasonType.displayName }}
|
||||||
v-for="(notifier, notifierIndex) in data?.notifiers"
|
</td>
|
||||||
:key="notifier.name"
|
<td
|
||||||
class="whitespace-nowrap px-4 py-3 text-sm text-gray-500"
|
v-for="(notifier, notifierIndex) in data?.notifiers"
|
||||||
>
|
:key="notifier.name"
|
||||||
<VSwitch
|
class="whitespace-nowrap px-4 py-3 text-sm text-gray-500"
|
||||||
:model-value="data?.stateMatrix?.[index][notifierIndex]"
|
>
|
||||||
:loading="
|
<VSwitch
|
||||||
mutating &&
|
:model-value="data?.stateMatrix?.[index][notifierIndex]"
|
||||||
variables?.reasonTypeIndex === index &&
|
:loading="
|
||||||
variables?.notifierIndex === notifierIndex
|
mutating &&
|
||||||
"
|
variables?.reasonTypeIndex === index &&
|
||||||
@change="
|
variables?.notifierIndex === notifierIndex
|
||||||
mutate({
|
"
|
||||||
state: !data?.stateMatrix?.[index][notifierIndex],
|
@change="
|
||||||
reasonTypeIndex: index,
|
mutate({
|
||||||
notifierIndex: notifierIndex,
|
state: !data?.stateMatrix?.[index][notifierIndex],
|
||||||
})
|
reasonTypeIndex: index,
|
||||||
"
|
notifierIndex: notifierIndex,
|
||||||
/>
|
})
|
||||||
</td>
|
"
|
||||||
</tr>
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</HasPermission>
|
||||||
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue