mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-06 04:36:21 +02:00
Add labs feature for MSC4284 policy server configuration
This commit is contained in:
parent
7398a83ae4
commit
3675b5c652
@ -15,6 +15,13 @@ dropped. Ask in the room if you are unclear about any details here.**
|
||||
A new version of the "Report" dialog that lets users send abuse reports directly to room moderators,
|
||||
if the room supports it.
|
||||
|
||||
## Enable options to set up Policy Servers in rooms (`feature_msc4284_setup`)
|
||||
|
||||
Allows configuring a room's policy server ([MSC4284](https://github.com/matrix-org/matrix-spec-proposals/pull/4284)).
|
||||
|
||||
Users can see the current configuration from the 'Roles & Permissions' tab of room settings. If the user has permission
|
||||
to change the policy server, they can also do that there.
|
||||
|
||||
## Render LaTeX maths in messages (`feature_latex_maths`)
|
||||
|
||||
Enables rendering of LaTeX maths in messages using [KaTeX](https://katex.org/). LaTeX between single dollar-signs is interpreted as inline maths and double dollar-signs as display maths (i.e. centred on its own line).
|
||||
|
||||
119
src/components/views/settings/PolicyServerConfig.tsx
Normal file
119
src/components/views/settings/PolicyServerConfig.tsx
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type FormEvent, type JSX, useContext, useState } from "react";
|
||||
import { EventType, type Room } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { useRoomState } from "../../../hooks/useRoomState.ts";
|
||||
import SettingsFieldset from "./SettingsFieldset.tsx";
|
||||
import Field from "../elements/Field.tsx";
|
||||
import AccessibleButton from "../elements/AccessibleButton.tsx";
|
||||
import { useAsyncMemo } from "../../../hooks/useAsyncMemo.ts";
|
||||
import ExternalLink from "../elements/ExternalLink.tsx";
|
||||
|
||||
interface PolicyServerConfigProps {
|
||||
room: Room;
|
||||
}
|
||||
|
||||
export const PolicyServerConfig: React.FC<PolicyServerConfigProps> = ({ room }) => {
|
||||
const client = useContext(MatrixClientContext);
|
||||
const { policyServerEvent, canChange } = useRoomState(room, (roomState) => ({
|
||||
policyServerEvent: roomState.events.get(EventType.RoomPolicy)?.get(""),
|
||||
canChange: roomState.maySendStateEvent(EventType.RoomPolicy, client.getSafeUserId()),
|
||||
}));
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const currentPolicyServerName = policyServerEvent?.getContent()?.["via"] ?? "";
|
||||
const [serverName, setServerName] = useState<string>(currentPolicyServerName);
|
||||
const [error, setError] = useState<boolean>(false);
|
||||
const supportUrl = useAsyncMemo(async (): Promise<string | undefined> => {
|
||||
if (!currentPolicyServerName) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const res = await (await fetch(`https://${currentPolicyServerName}/.well-known/matrix/support`)).json();
|
||||
if (!!res["support_page"] && typeof res["support_page"] === "string") {
|
||||
return res["support_page"];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [serverName, currentPolicyServerName], undefined);
|
||||
|
||||
const onSubmit = async (event: FormEvent): Promise<void> => {
|
||||
event.preventDefault();
|
||||
await applyChange();
|
||||
};
|
||||
|
||||
const applyChange = async (): Promise<void> => {
|
||||
setIsLoading(true);
|
||||
setError(false);
|
||||
|
||||
try {
|
||||
if (serverName.trim().length > 0) {
|
||||
const res = await (await fetch(`https://${serverName.trim()}/.well-known/matrix/org.matrix.msc4284.policy_server`)).json();
|
||||
if (!!res["public_key"] && typeof res["public_key"] === "string") {
|
||||
await client.sendStateEvent(room.roomId, EventType.RoomPolicy, {
|
||||
via: serverName,
|
||||
public_key: res["public_key"],
|
||||
}, "");
|
||||
} else {
|
||||
logger.error("Policy server returned non-string public key (or returned an error)")
|
||||
setError(true);
|
||||
}
|
||||
} else {
|
||||
// Empty object == remove
|
||||
await client.sendStateEvent(room.roomId, EventType.RoomPolicy, {}, "");
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
setError(true);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
let supportSection: JSX.Element | undefined;
|
||||
if (!!currentPolicyServerName) {
|
||||
if (!!supportUrl) {
|
||||
supportSection = <span>{ _t("room_settings|permissions|policy_server_support_page", {},{
|
||||
a: (sub) => <ExternalLink href={supportUrl}>{sub}</ExternalLink>,
|
||||
}) }</span>;
|
||||
} else {
|
||||
supportSection = <span>{ _t("room_settings|permissions|policy_server_generic_support") }</span>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form style={{ display: "flex" }} onSubmit={onSubmit}>
|
||||
<SettingsFieldset
|
||||
legend={_t("room_settings|permissions|policy_server_title")}
|
||||
description={_t("room_settings|permissions|policy_server_description")}
|
||||
style={{ flexGrow: 1 }}
|
||||
>
|
||||
<Field
|
||||
label={_t("room_settings|permissions|policy_server_field_label")}
|
||||
type="text"
|
||||
value={serverName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setServerName(e.target.value)}
|
||||
autoComplete="off"
|
||||
disabled={isLoading || !canChange}
|
||||
/>
|
||||
<AccessibleButton
|
||||
onClick={onSubmit}
|
||||
kind="primary"
|
||||
disabled={isLoading || !canChange || serverName === currentPolicyServerName}
|
||||
>
|
||||
{_t("action|apply")}
|
||||
</AccessibleButton>
|
||||
{ error ? <span className="error">{ _t("room_settings|permissions|policy_server_error") }</span> : undefined }
|
||||
{ supportSection }
|
||||
</SettingsFieldset>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@ -26,6 +26,7 @@ import { SettingsSection } from "../../shared/SettingsSection";
|
||||
import MatrixClientContext from "../../../../../contexts/MatrixClientContext";
|
||||
import { PowerLevelSelector } from "../../PowerLevelSelector";
|
||||
import { ElementCallEventType, ElementCallMemberEventType } from "../../../../../call-types";
|
||||
import { PolicyServerConfig } from "../../PolicyServerConfig.tsx";
|
||||
|
||||
interface IEventShowOpts {
|
||||
isState?: boolean;
|
||||
@ -59,6 +60,9 @@ const plEventsToShow: Record<string, IEventShowOpts> = {
|
||||
[ElementCallEventType.name]: { isState: true, hideForSpace: true },
|
||||
[ElementCallMemberEventType.name]: { isState: true, hideForSpace: true },
|
||||
|
||||
// MSC4284: Policy servers
|
||||
[EventType.RoomPolicy]: { isState: true, hideForSpace: true },
|
||||
|
||||
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
|
||||
"im.vector.modular.widgets": { isState: true, hideForSpace: true },
|
||||
};
|
||||
@ -283,6 +287,13 @@ export default class RolesRoomSettingsTab extends React.Component<IProps, RolesR
|
||||
plEventsToLabels[ElementCallMemberEventType.name] = _td("room_settings|permissions|m.call.member");
|
||||
}
|
||||
|
||||
// MSC4284: Policy servers
|
||||
let policyServerSection: JSX.Element | undefined;
|
||||
if (SettingsStore.getValue("feature_msc4284_setup")) {
|
||||
plEventsToLabels[EventType.RoomPolicy] = _td("room_settings|permissions|m.room.policy");
|
||||
policyServerSection = <PolicyServerConfig room={this.props.room} />;
|
||||
}
|
||||
|
||||
const powerLevelDescriptors: Record<string, IPowerLevelDescriptor> = {
|
||||
"users_default": {
|
||||
desc: _t("room_settings|permissions|users_default"),
|
||||
@ -454,6 +465,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps, RolesR
|
||||
return (
|
||||
<SettingsTab>
|
||||
<SettingsSection heading={_t("room_settings|permissions|title")}>
|
||||
{policyServerSection}
|
||||
{privilegedUsersSection}
|
||||
{canChangeLevels && <AddPrivilegedUsers room={room} defaultUserLevel={defaultUserLevel} />}
|
||||
{mutedUsersSection}
|
||||
|
||||
@ -1564,6 +1564,7 @@
|
||||
"location_share_live_description": "Temporary implementation. Locations persist in room history.",
|
||||
"mjolnir": "New ways to ignore people",
|
||||
"msc3531_hide_messages_pending_moderation": "Let moderators hide messages pending moderation.",
|
||||
"msc4284_setup": "Enable options to set up Policy Servers in rooms",
|
||||
"new_room_list": "Enable new room list",
|
||||
"notification_settings": "New Notification Settings",
|
||||
"notification_settings_beta_caption": "Introducing a simpler way to change your notification settings. Customize your %(brand)s, just the way you like.",
|
||||
@ -2392,6 +2393,7 @@
|
||||
"m.room.name": "Change room name",
|
||||
"m.room.name_space": "Change space name",
|
||||
"m.room.pinned_events": "Manage pinned events",
|
||||
"m.room.policy": "Configure the room's policy server",
|
||||
"m.room.power_levels": "Change permissions",
|
||||
"m.room.redaction": "Remove messages sent by me",
|
||||
"m.room.server_acl": "Change server ACLs",
|
||||
@ -2406,6 +2408,12 @@
|
||||
"permissions_section": "Permissions",
|
||||
"permissions_section_description_room": "Select the roles required to change various parts of the room",
|
||||
"permissions_section_description_space": "Select the roles required to change various parts of the space",
|
||||
"policy_server_description": "Policy servers are a proactive moderation tool which compliment reactive tooling like moderation bots. If you have access to one, set its server name here. To remove the policy server, set this to an empty value.",
|
||||
"policy_server_error": "There was an error changing the policy server name. Ensure the server name is a valid policy server and try again.",
|
||||
"policy_server_field_label": "Policy server name",
|
||||
"policy_server_generic_support": "This policy server may require additional setup before it will work. Consult your policy server's documentation for details.",
|
||||
"policy_server_support_page": "This policy server may require additional setup before it will work. Click <a>here</a> to visit your policy server's support page.",
|
||||
"policy_server_title": "Policy server",
|
||||
"privileged_users_section": "Privileged Users",
|
||||
"redact": "Remove messages sent by others",
|
||||
"send_event_type": "Send %(eventType)s events",
|
||||
|
||||
@ -208,6 +208,7 @@ export interface Settings {
|
||||
[Features.NotificationSettings2]: IFeature;
|
||||
[Features.ReleaseAnnouncement]: IFeature;
|
||||
"feature_msc3531_hide_messages_pending_moderation": IFeature;
|
||||
"feature_msc4284_setup": IFeature;
|
||||
"feature_report_to_moderators": IFeature;
|
||||
"feature_latex_maths": IFeature;
|
||||
"feature_wysiwyg_composer": IFeature;
|
||||
@ -450,6 +451,14 @@ export const SETTINGS: Settings = {
|
||||
supportedLevelsAreOrdered: true,
|
||||
default: false,
|
||||
},
|
||||
"feature_msc4284_setup": {
|
||||
isFeature: true,
|
||||
labsGroup: LabGroup.Moderation,
|
||||
displayName: _td("labs|msc4284_setup"),
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
|
||||
supportedLevelsAreOrdered: true,
|
||||
default: false,
|
||||
},
|
||||
"mediaPreviewConfig": {
|
||||
controller: new MediaPreviewConfigController(),
|
||||
supportedLevels: LEVELS_ROOM_SETTINGS,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user