diff --git a/res/css/_components.scss b/res/css/_components.scss
index a2c6d4bb77b..69edd301d04 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -144,6 +144,7 @@
@import "./views/settings/tabs/_HelpSettingsTab.scss";
@import "./views/settings/tabs/_PreferencesSettingsTab.scss";
@import "./views/settings/tabs/_RolesRoomSettingsTab.scss";
+@import "./views/settings/tabs/_SecurityRoomSettingsTab.scss";
@import "./views/settings/tabs/_SecuritySettingsTab.scss";
@import "./views/settings/tabs/_SettingsTab.scss";
@import "./views/settings/tabs/_VoiceSettingsTab.scss";
diff --git a/res/css/views/elements/_ToggleSwitch.scss b/res/css/views/elements/_ToggleSwitch.scss
index 4076414086f..1bb3a74ab11 100644
--- a/res/css/views/elements/_ToggleSwitch.scss
+++ b/res/css/views/elements/_ToggleSwitch.scss
@@ -21,10 +21,12 @@ limitations under the License.
border-radius: 14px;
background-color: $togglesw-off-color;
position: relative;
+ opacity: 0.5;
}
.mx_ToggleSwitch_enabled {
cursor: pointer;
+ opacity: 1;
}
.mx_ToggleSwitch.mx_ToggleSwitch_on {
diff --git a/res/css/views/settings/tabs/_SecurityRoomSettingsTab.scss b/res/css/views/settings/tabs/_SecurityRoomSettingsTab.scss
new file mode 100644
index 00000000000..beab1254de7
--- /dev/null
+++ b/res/css/views/settings/tabs/_SecurityRoomSettingsTab.scss
@@ -0,0 +1,34 @@
+/*
+Copyright 2019 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+.mx_SecurityRoomSettingsTab label {
+ display: block;
+}
+
+.mx_SecurityRoomSettingsTab_warning {
+ display: block;
+
+ img {
+ vertical-align: middle;
+ margin-right: 5px;
+ margin-left: 3px;
+ margin-bottom: 5px;
+ }
+}
+
+.mx_SecurityRoomSettingsTab_encryptionSection {
+ margin-bottom: 25px;
+}
\ No newline at end of file
diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js
index df35d8fadc1..6be2676d72e 100644
--- a/src/components/views/dialogs/RoomSettingsDialog.js
+++ b/src/components/views/dialogs/RoomSettingsDialog.js
@@ -23,6 +23,7 @@ import AccessibleButton from "../elements/AccessibleButton";
import dis from '../../../dispatcher';
import RolesRoomSettingsTab from "../settings/tabs/RolesRoomSettingsTab";
import GeneralRoomSettingsTab from "../settings/tabs/GeneralRoomSettingsTab";
+import SecurityRoomSettingsTab from "../settings/tabs/SecurityRoomSettingsTab";
// TODO: Ditch this whole component
export class TempTab extends React.Component {
@@ -70,7 +71,7 @@ export default class RoomSettingsDialog extends React.Component {
tabs.push(new Tab(
_td("Security & Privacy"),
"mx_RoomSettingsDialog_securityIcon",
-
Security Test
,
+ ,
));
tabs.push(new Tab(
_td("Roles & Permissions"),
diff --git a/src/components/views/elements/LabelledToggleSwitch.js b/src/components/views/elements/LabelledToggleSwitch.js
index 3d3025ad065..292c978e88e 100644
--- a/src/components/views/elements/LabelledToggleSwitch.js
+++ b/src/components/views/elements/LabelledToggleSwitch.js
@@ -28,6 +28,9 @@ export default class LabelledToggleSwitch extends React.Component {
// The translated label for the switch
label: PropTypes.string.isRequired,
+
+ // Whether or not to disable the toggle switch
+ disabled: PropTypes.bool,
};
render() {
@@ -35,7 +38,8 @@ export default class LabelledToggleSwitch extends React.Component {
return (
{this.props.label}
-
+
);
}
diff --git a/src/components/views/settings/tabs/SecurityRoomSettingsTab.js b/src/components/views/settings/tabs/SecurityRoomSettingsTab.js
new file mode 100644
index 00000000000..d7645cd0ac0
--- /dev/null
+++ b/src/components/views/settings/tabs/SecurityRoomSettingsTab.js
@@ -0,0 +1,272 @@
+/*
+Copyright 2019 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import {_t} from "../../../../languageHandler";
+import MatrixClientPeg from "../../../../MatrixClientPeg";
+import sdk from "../../../../index";
+import LabelledToggleSwitch from "../../elements/LabelledToggleSwitch";
+import {SettingLevel} from "../../../../settings/SettingsStore";
+import Modal from "../../../../Modal";
+
+export default class SecurityRoomSettingsTab extends React.Component {
+ static propTypes = {
+ roomId: PropTypes.string.isRequired,
+ };
+
+ componentWillMount(): void {
+ MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
+ }
+
+ componentWillUnmount(): void {
+ MatrixClientPeg.get().removeListener("RoomState.events", this._onStateEvent);
+ }
+
+ _onStateEvent = (e) => {
+ const refreshWhenTypes = ['m.room.join_rules', 'm.room.guest_access', 'm.room.history_visibility'];
+ if (refreshWhenTypes.includes(e.getType())) this.forceUpdate();
+ };
+
+ _onEncryptionChange = (e) => {
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ Modal.createTrackedDialog('E2E Enable Warning', '', QuestionDialog, {
+ title: _t('Warning!'),
+ description: (
+
+
{ _t('End-to-end encryption is in beta and may not be reliable') }.
+
{ _t('You should not yet trust it to secure data') }.
+
{ _t('Devices will not yet be able to decrypt history from before they joined the room') }.
+
{ _t('Once encryption is enabled for a room it cannot be turned off again (for now)') }.
+
{ _t('Encrypted messages will not be visible on clients that do not yet implement encryption') }.
+
+ ),
+ onFinished: (confirm)=>{
+ if (confirm) {
+ return MatrixClientPeg.get().sendStateEvent(
+ this.props.roomId, "m.room.encryption",
+ { algorithm: "m.megolm.v1.aes-sha2" },
+ );
+ }
+ },
+ });
+ };
+
+ _fixGuestAccess = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ const client = MatrixClientPeg.get();
+ client.sendStateEvent(this.props.roomId, "m.room.join_rules", {join_rule: "invite"}, "");
+ client.sendStateEvent(this.props.roomId, "m.room.guest_access", {guest_access: "can_join"}, "");
+ };
+
+ _onRoomAccessRadioToggle = (ev) => {
+ // join_rule
+ // INVITE | PUBLIC
+ // ----------------------+----------------
+ // guest CAN_JOIN | inv_only | pub_with_guest
+ // access ----------------------+----------------
+ // FORBIDDEN | inv_only | pub_no_guest
+ // ----------------------+----------------
+
+ // we always set guests can_join here as it makes no sense to have
+ // an invite-only room that guests can't join. If you explicitly
+ // invite them, you clearly want them to join, whether they're a
+ // guest or not. In practice, guest_access should probably have
+ // been implemented as part of the join_rules enum.
+ let joinRule = "invite";
+ let guestAccess = "can_join";
+
+ switch (ev.target.value) {
+ case "invite_only":
+ // no change - use defaults above
+ break;
+ case "public_no_guests":
+ joinRule = "public";
+ guestAccess = "forbidden";
+ break;
+ case "public_with_guests":
+ joinRule = "public";
+ guestAccess = "can_join";
+ break;
+ }
+
+ const client = MatrixClientPeg.get();
+ client.sendStateEvent(this.props.roomId, "m.room.join_rules", {join_rule: joinRule}, "");
+ client.sendStateEvent(this.props.roomId, "m.room.guest_access", {guest_access: guestAccess}, "");
+ };
+
+ _onHistoryRadioToggle = (ev) => {
+ MatrixClientPeg.get().sendStateEvent(this.props.roomId, "m.room.history_visibility", {
+ history_visibility: ev.target.value,
+ }, "");
+ };
+
+ _renderRoomAccess() {
+ const client = MatrixClientPeg.get();
+ const room = client.getRoom(this.props.roomId);
+ const joinRule = room.currentState.getStateEvents("m.room.join_rules", "").getContent()['join_rule'];
+ const guestAccess = room.currentState.getStateEvents("m.room.guest_access", "").getContent()['guest_access'];
+ const aliasEvents = room.currentState.getStateEvents("m.room.aliases") || [];
+ const hasAliases = aliasEvents.includes((ev) => (ev.getContent().aliases || []).length);
+
+ const canChangeAccess = room.currentState.mayClientSendStateEvent("m.room.join_rules", client)
+ && room.currentState.mayClientSendStateEvent("m.room.guest_access", client);
+
+ let guestWarning = null;
+ if (joinRule !== 'public' && guestAccess === 'forbidden') {
+ guestWarning = (
+
+
+
+ {_t("Guests cannot join this room even if explicitly invited.")}
+ {_t("Click here to fix")}
+
+
+ {_t('Changes to who can read history will only apply to future messages in this room. ' +
+ 'The visibility of existing history will be unchanged.')}
+
+ {_t("Once enabled, encryption cannot be disabled.")}
+
+
+
+ {encryptionSettings}
+
+
+ {_t("Who can access this room?")}
+
+ {this._renderRoomAccess()}
+
+
+ {_t("Who can read history?")}
+
+ {this._renderHistory()}
+
+
+ );
+ }
+}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index a0b37a61e1d..341b9754a5c 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -515,6 +515,28 @@
"To send events of type , you must be a": "To send events of type , you must be a",
"Roles & Permissions": "Roles & Permissions",
"Permissions": "Permissions",
+ "End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
+ "You should not yet trust it to secure data": "You should not yet trust it to secure data",
+ "Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room",
+ "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)",
+ "Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
+ "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.",
+ "Click here to fix": "Click here to fix",
+ "To link to this room, please add an alias.": "To link to this room, please add an alias.",
+ "Only people who have been invited": "Only people who have been invited",
+ "Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
+ "Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests",
+ "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.",
+ "Anyone": "Anyone",
+ "Members only (since the point in time of selecting this option)": "Members only (since the point in time of selecting this option)",
+ "Members only (since they were invited)": "Members only (since they were invited)",
+ "Members only (since they joined)": "Members only (since they joined)",
+ "Security & Privacy": "Security & Privacy",
+ "Encryption": "Encryption",
+ "Once enabled, encryption cannot be disabled.": "Once enabled, encryption cannot be disabled.",
+ "Encrypted": "Encrypted",
+ "Who can access this room?": "Who can access this room?",
+ "Who can read history?": "Who can read history?",
"Unignore": "Unignore",
"": "",
"Import E2E room keys": "Import E2E room keys",
@@ -525,7 +547,6 @@
"Bulk options": "Bulk options",
"Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites",
"Key backup": "Key backup",
- "Security & Privacy": "Security & Privacy",
"Devices": "Devices",
"Riot collects anonymous analytics to allow us to improve the application.": "Riot collects anonymous analytics to allow us to improve the application.",
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.",
@@ -724,11 +745,6 @@
"The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged",
"unknown error code": "unknown error code",
"Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",
- "End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
- "You should not yet trust it to secure data": "You should not yet trust it to secure data",
- "Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room",
- "Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)",
- "Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
"Enable encryption": "Enable encryption",
"(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)",
"Encryption is enabled in this room": "Encryption is enabled in this room",
@@ -736,18 +752,7 @@
"Favourite": "Favourite",
"Tagged as: ": "Tagged as: ",
"To link to a room it must have an address.": "To link to a room it must have an address.",
- "Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.",
- "Click here to fix": "Click here to fix",
- "Who can access this room?": "Who can access this room?",
- "Only people who have been invited": "Only people who have been invited",
- "Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
- "Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests",
"Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?",
- "Who can read history?": "Who can read history?",
- "Anyone": "Anyone",
- "Members only (since the point in time of selecting this option)": "Members only (since the point in time of selecting this option)",
- "Members only (since they were invited)": "Members only (since they were invited)",
- "Members only (since they joined)": "Members only (since they joined)",
"Internal room ID: ": "Internal room ID: ",
"Room version number: ": "Room version number: ",
"Add a topic": "Add a topic",