* Remove guest badge

* Address feedback

* Fix lint, test and i18n

* Fix test
Этот коммит содержится в:
Daniel Espino García
2023-06-30 11:06:18 +02:00
коммит произвёл GitHub
родитель 05cd245e97
Коммит fda5dd8468
18 изменённых файлов: 66 добавлений и 22 удалений

Просмотреть файл

@@ -639,6 +639,7 @@ const defaultServerConfig: AdminConfig = {
}, },
GuestAccountsSettings: { GuestAccountsSettings: {
Enable: false, Enable: false,
HideTags: false,
AllowEmailAccounts: true, AllowEmailAccounts: true,
EnforceMultifactorAuthentication: false, EnforceMultifactorAuthentication: false,
RestrictCreationToDomains: '', RestrictCreationToDomains: '',

Просмотреть файл

@@ -322,6 +322,7 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m
props["EnableMultifactorAuthentication"] = strconv.FormatBool(*c.ServiceSettings.EnableMultifactorAuthentication) props["EnableMultifactorAuthentication"] = strconv.FormatBool(*c.ServiceSettings.EnableMultifactorAuthentication)
props["EnforceMultifactorAuthentication"] = "false" props["EnforceMultifactorAuthentication"] = "false"
props["EnableGuestAccounts"] = strconv.FormatBool(*c.GuestAccountsSettings.Enable) props["EnableGuestAccounts"] = strconv.FormatBool(*c.GuestAccountsSettings.Enable)
props["HideGuestTags"] = strconv.FormatBool(*c.GuestAccountsSettings.HideTags)
props["GuestAccountsEnforceMultifactorAuthentication"] = strconv.FormatBool(*c.GuestAccountsSettings.EnforceMultifactorAuthentication) props["GuestAccountsEnforceMultifactorAuthentication"] = strconv.FormatBool(*c.GuestAccountsSettings.EnforceMultifactorAuthentication)
if license != nil { if license != nil {

Просмотреть файл

@@ -860,6 +860,7 @@ func (ts *TelemetryService) trackConfig() {
ts.SendTelemetry(TrackConfigGuestAccounts, map[string]any{ ts.SendTelemetry(TrackConfigGuestAccounts, map[string]any{
"enable": *cfg.GuestAccountsSettings.Enable, "enable": *cfg.GuestAccountsSettings.Enable,
"hide_tag": *cfg.GuestAccountsSettings.HideTags,
"allow_email_accounts": *cfg.GuestAccountsSettings.AllowEmailAccounts, "allow_email_accounts": *cfg.GuestAccountsSettings.AllowEmailAccounts,
"enforce_multifactor_authentication": *cfg.GuestAccountsSettings.EnforceMultifactorAuthentication, "enforce_multifactor_authentication": *cfg.GuestAccountsSettings.EnforceMultifactorAuthentication,
"isdefault_restrict_creation_to_domains": isDefault(*cfg.GuestAccountsSettings.RestrictCreationToDomains, ""), "isdefault_restrict_creation_to_domains": isDefault(*cfg.GuestAccountsSettings.RestrictCreationToDomains, ""),

Просмотреть файл

@@ -3055,6 +3055,7 @@ func (s *DisplaySettings) SetDefaults() {
type GuestAccountsSettings struct { type GuestAccountsSettings struct {
Enable *bool `access:"authentication_guest_access"` Enable *bool `access:"authentication_guest_access"`
HideTags *bool `access:"authentication_guest_access"`
AllowEmailAccounts *bool `access:"authentication_guest_access"` AllowEmailAccounts *bool `access:"authentication_guest_access"`
EnforceMultifactorAuthentication *bool `access:"authentication_guest_access"` EnforceMultifactorAuthentication *bool `access:"authentication_guest_access"`
RestrictCreationToDomains *string `access:"authentication_guest_access"` RestrictCreationToDomains *string `access:"authentication_guest_access"`
@@ -3065,6 +3066,10 @@ func (s *GuestAccountsSettings) SetDefaults() {
s.Enable = NewBool(false) s.Enable = NewBool(false)
} }
if s.HideTags == nil {
s.HideTags = NewBool(false)
}
if s.AllowEmailAccounts == nil { if s.AllowEmailAccounts == nil {
s.AllowEmailAccounts = NewBool(true) s.AllowEmailAccounts = NewBool(true)
} }

Просмотреть файл

@@ -5727,6 +5727,16 @@ const AdminDefinition = {
key: 'GuestAccountsSettings.Enable', key: 'GuestAccountsSettings.Enable',
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.GUEST_ACCESS)), isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.GUEST_ACCESS)),
}, },
{
type: Constants.SettingsTypes.TYPE_BOOL,
key: 'GuestAccountsSettings.HideTags',
label: t('admin.guest_access.hideTags'),
label_default: 'Hide guest tag',
help_text: t('admin.guest_access.hideTagsDescription'),
help_text_default: 'When true, the "guest" tag will not be shown next to the name of all guest users in the Mattermost chat interface.',
help_text_markdown: false,
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.AUTHENTICATION.GUEST_ACCESS)),
},
{ {
type: Constants.SettingsTypes.TYPE_TEXT, type: Constants.SettingsTypes.TYPE_TEXT,
key: 'GuestAccountsSettings.RestrictCreationToDomains', key: 'GuestAccountsSettings.RestrictCreationToDomains',

Просмотреть файл

@@ -46,6 +46,7 @@ describe('components/ChannelHeader', () => {
'minute', 'minute',
'hour', 'hour',
], ],
hideGuestTags: false,
}; };
const populatedProps = { const populatedProps = {

Просмотреть файл

@@ -95,6 +95,7 @@ export type Props = {
isLastActiveEnabled: boolean; isLastActiveEnabled: boolean;
timestampUnits?: string[]; timestampUnits?: string[];
lastActivityTimestamp?: number; lastActivityTimestamp?: number;
hideGuestTags: boolean;
}; };
type State = { type State = {
@@ -292,12 +293,13 @@ class ChannelHeader extends React.PureComponent<Props, State> {
rhsState, rhsState,
hasGuests, hasGuests,
teammateNameDisplaySetting, teammateNameDisplaySetting,
hideGuestTags,
} = this.props; } = this.props;
const {formatMessage} = this.props.intl; const {formatMessage} = this.props.intl;
const ariaLabelChannelHeader = localizeMessage('accessibility.sections.channelHeader', 'channel header region'); const ariaLabelChannelHeader = localizeMessage('accessibility.sections.channelHeader', 'channel header region');
let hasGuestsText: ReactNode = ''; let hasGuestsText: ReactNode = '';
if (hasGuests) { if (hasGuests && !hideGuestTags) {
hasGuestsText = ( hasGuestsText = (
<span className='has-guest-header'> <span className='has-guest-header'>
<span tabIndex={0}> <span tabIndex={0}>
@@ -399,7 +401,7 @@ class ChannelHeader extends React.PureComponent<Props, State> {
); );
}); });
if (hasGuests) { if (hasGuests && !hideGuestTags) {
hasGuestsText = ( hasGuestsText = (
<span className='has-guest-header'> <span className='has-guest-header'>
<FormattedMessage <FormattedMessage

Просмотреть файл

@@ -116,6 +116,7 @@ function makeMapStateToProps() {
isFileAttachmentsEnabled: isFileAttachmentsEnabled(config), isFileAttachmentsEnabled: isFileAttachmentsEnabled(config),
isLastActiveEnabled, isLastActiveEnabled,
timestampUnits, timestampUnits,
hideGuestTags: config.HideGuestTags === 'true',
}; };
}; };
} }

Просмотреть файл

@@ -25,6 +25,7 @@ import {Channel} from '@mattermost/types/channels';
import {Post} from '@mattermost/types/posts'; import {Post} from '@mattermost/types/posts';
import PostMarkdown from './post_markdown'; import PostMarkdown from './post_markdown';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
export function makeGetMentionKeysForPost(): ( export function makeGetMentionKeysForPost(): (
state: GlobalState, state: GlobalState,
@@ -75,6 +76,7 @@ function makeMapStateToProps() {
mentionKeys: getMentionKeysForPost(state, ownProps.post, channel), mentionKeys: getMentionKeysForPost(state, ownProps.post, channel),
isMilitaryTime: getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false), isMilitaryTime: getBool(state, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false),
timezone: getCurrentTimezone(state), timezone: getCurrentTimezone(state),
hideGuestTags: getConfig(state).HideGuestTags === 'true',
}; };
}; };
} }

Просмотреть файл

@@ -23,6 +23,7 @@ describe('components/PostMarkdown', () => {
channelId: 'channel-id', channelId: 'channel-id',
channel: TestHelper.getChannelMock(), channel: TestHelper.getChannelMock(),
currentTeam: TestHelper.getTeamMock(), currentTeam: TestHelper.getTeamMock(),
hideGuestTags: false,
}; };
const state = {entities: { const state = {entities: {

Просмотреть файл

@@ -66,6 +66,8 @@ type Props = {
*/ */
isMilitaryTime?: boolean; isMilitaryTime?: boolean;
timezone?: string; timezone?: string;
hideGuestTags: boolean;
} }
export default class PostMarkdown extends React.PureComponent<Props> { export default class PostMarkdown extends React.PureComponent<Props> {
@@ -94,6 +96,7 @@ export default class PostMarkdown extends React.PureComponent<Props> {
const renderedSystemMessage = renderSystemMessage(post, const renderedSystemMessage = renderSystemMessage(post,
this.props.currentTeam, this.props.currentTeam,
this.props.channel, this.props.channel,
this.props.hideGuestTags,
this.props.isUserCanManageMembers, this.props.isUserCanManageMembers,
this.props.isMilitaryTime, this.props.isMilitaryTime,
this.props.timezone); this.props.timezone);

Просмотреть файл

@@ -50,7 +50,10 @@ function renderJoinChannelMessage(post: Post): ReactNode {
); );
} }
function renderGuestJoinChannelMessage(post: Post): ReactNode { function renderGuestJoinChannelMessage(post: Post, hideGuestTags: boolean): ReactNode {
if (hideGuestTags) {
return renderJoinChannelMessage(post);
}
const username = renderUsername(post.props.username); const username = renderUsername(post.props.username);
return ( return (
@@ -90,7 +93,10 @@ function renderAddToChannelMessage(post: Post): ReactNode {
); );
} }
function renderAddGuestToChannelMessage(post: Post): ReactNode { function renderAddGuestToChannelMessage(post: Post, hideGuestTags: boolean): ReactNode {
if (hideGuestTags) {
return renderAddToChannelMessage(post);
}
const username = renderUsername(post.props.username); const username = renderUsername(post.props.username);
const addedUsername = renderUsername(post.props.addedUsername); const addedUsername = renderUsername(post.props.addedUsername);
@@ -363,10 +369,9 @@ function renderMeMessage(post: Post): ReactNode {
const systemMessageRenderers = { const systemMessageRenderers = {
[Posts.POST_TYPES.JOIN_CHANNEL]: renderJoinChannelMessage, [Posts.POST_TYPES.JOIN_CHANNEL]: renderJoinChannelMessage,
[Posts.POST_TYPES.GUEST_JOIN_CHANNEL]: renderGuestJoinChannelMessage,
[Posts.POST_TYPES.LEAVE_CHANNEL]: renderLeaveChannelMessage, [Posts.POST_TYPES.LEAVE_CHANNEL]: renderLeaveChannelMessage,
[Posts.POST_TYPES.ADD_TO_CHANNEL]: renderAddToChannelMessage, [Posts.POST_TYPES.ADD_TO_CHANNEL]: renderAddToChannelMessage,
[Posts.POST_TYPES.ADD_GUEST_TO_CHANNEL]: renderAddGuestToChannelMessage, [Posts.POST_TYPES.EPHEMERAL_ADD_TO_CHANNEL]: renderAddToChannelMessage,
[Posts.POST_TYPES.REMOVE_FROM_CHANNEL]: renderRemoveFromChannelMessage, [Posts.POST_TYPES.REMOVE_FROM_CHANNEL]: renderRemoveFromChannelMessage,
[Posts.POST_TYPES.JOIN_TEAM]: renderJoinTeamMessage, [Posts.POST_TYPES.JOIN_TEAM]: renderJoinTeamMessage,
[Posts.POST_TYPES.LEAVE_TEAM]: renderLeaveTeamMessage, [Posts.POST_TYPES.LEAVE_TEAM]: renderLeaveTeamMessage,
@@ -381,7 +386,7 @@ const systemMessageRenderers = {
[Posts.POST_TYPES.ME]: renderMeMessage, [Posts.POST_TYPES.ME]: renderMeMessage,
}; };
export function renderSystemMessage(post: Post, currentTeam: Team, channel: Channel, isUserCanManageMembers?: boolean, isMilitaryTime?: boolean, timezone?: string): ReactNode { export function renderSystemMessage(post: Post, currentTeam: Team, channel: Channel, hideGuestTags: boolean, isUserCanManageMembers?: boolean, isMilitaryTime?: boolean, timezone?: string): ReactNode {
const isEphemeral = Utils.isPostEphemeral(post); const isEphemeral = Utils.isPostEphemeral(post);
if (isEphemeral && post.props?.type === Posts.POST_TYPES.REMINDER) { if (isEphemeral && post.props?.type === Posts.POST_TYPES.REMINDER) {
return renderReminderACKMessage(post, currentTeam, Boolean(isMilitaryTime), timezone); return renderReminderACKMessage(post, currentTeam, Boolean(isMilitaryTime), timezone);
@@ -405,8 +410,10 @@ export function renderSystemMessage(post: Post, currentTeam: Team, channel: Chan
return null; return null;
} else if (systemMessageRenderers[post.type]) { } else if (systemMessageRenderers[post.type]) {
return systemMessageRenderers[post.type](post); return systemMessageRenderers[post.type](post);
} else if (post.type === Posts.POST_TYPES.EPHEMERAL_ADD_TO_CHANNEL) { } else if (post.type === Posts.POST_TYPES.GUEST_JOIN_CHANNEL) {
return renderAddToChannelMessage(post); return renderGuestJoinChannelMessage(post, hideGuestTags);
} else if (post.type === Posts.POST_TYPES.ADD_GUEST_TO_CHANNEL) {
return renderAddGuestToChannelMessage(post, hideGuestTags);
} else if (post.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY) { } else if (post.type === Posts.POST_TYPES.COMBINED_USER_ACTIVITY) {
const {allUserIds, allUsernames, messageData} = post.props.user_activity; const {allUserIds, allUsernames, messageData} = post.props.user_activity;

Просмотреть файл

@@ -1,9 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/widgets/tag/GuestTag should match the snapshot 1`] = `
<Memo(Tag)
className="GuestTag test"
size="xs"
text="GUEST"
/>
`;

Просмотреть файл

@@ -2,13 +2,18 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react'; import React from 'react';
import {shallow} from 'enzyme';
import GuestTag from './guest_tag'; import GuestTag from './guest_tag';
import {renderWithIntlAndStore, screen} from 'tests/react_testing_utils';
describe('components/widgets/tag/GuestTag', () => { describe('components/widgets/tag/GuestTag', () => {
test('should match the snapshot', () => { test('should match the snapshot', () => {
const wrapper = shallow(<GuestTag className={'test'}/>); renderWithIntlAndStore(<GuestTag className={'test'}/>);
expect(wrapper).toMatchSnapshot(); screen.getByText('GUEST');
});
test('should not render when hideTags is true', () => {
renderWithIntlAndStore(<GuestTag className={'test'}/>, {entities: {general: {config: {HideGuestTags: 'true'}}}});
expect(() => screen.getByText('GUEST')).toThrow();
}); });
}); });

Просмотреть файл

@@ -6,6 +6,9 @@ import classNames from 'classnames';
import {useIntl} from 'react-intl'; import {useIntl} from 'react-intl';
import Tag, {TagSize} from './tag'; import Tag, {TagSize} from './tag';
import {useSelector} from 'react-redux';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {GlobalState} from '@mattermost/types/store';
type Props = { type Props = {
className?: string; className?: string;
@@ -14,6 +17,12 @@ type Props = {
const GuestTag = ({className = '', size = 'xs'}: Props) => { const GuestTag = ({className = '', size = 'xs'}: Props) => {
const {formatMessage} = useIntl(); const {formatMessage} = useIntl();
const shouldHideTag = useSelector((state: GlobalState) => getConfig(state).HideGuestTags === 'true');
if (shouldHideTag) {
return null;
}
return ( return (
<Tag <Tag
className={classNames('GuestTag', className)} className={classNames('GuestTag', className)}

Просмотреть файл

@@ -1127,6 +1127,8 @@
"admin.guest_access.disableConfirmWarning": "All current guest account sessions will be revoked, and marked as inactive", "admin.guest_access.disableConfirmWarning": "All current guest account sessions will be revoked, and marked as inactive",
"admin.guest_access.enableDescription": "When true, external guest can be invited to channels within teams. Please see [Permissions Schemes](../user_management/permissions/system_scheme) for which roles can invite guests.", "admin.guest_access.enableDescription": "When true, external guest can be invited to channels within teams. Please see [Permissions Schemes](../user_management/permissions/system_scheme) for which roles can invite guests.",
"admin.guest_access.enableTitle": "Enable Guest Access: ", "admin.guest_access.enableTitle": "Enable Guest Access: ",
"admin.guest_access.hideTags": "Hide guest tag",
"admin.guest_access.hideTagsDescription": "When true, the \"guest\" tag will not be shown next to the name of all guest users in the Mattermost chat interface.",
"admin.guest_access.mfaDescription": "When true, <link>multi-factor authentication</link> for guests is required for login. New guest users will be required to configure MFA on signup. Logged in guest users without MFA configured are redirected to the MFA setup page until configuration is complete.\n \nIf your system has guest users with login methods other than AD/LDAP and email, MFA must be enforced with the authentication provider outside of Mattermost.", "admin.guest_access.mfaDescription": "When true, <link>multi-factor authentication</link> for guests is required for login. New guest users will be required to configure MFA on signup. Logged in guest users without MFA configured are redirected to the MFA setup page until configuration is complete.\n \nIf your system has guest users with login methods other than AD/LDAP and email, MFA must be enforced with the authentication provider outside of Mattermost.",
"admin.guest_access.mfaDescriptionMFANotEnabled": "[Multi-factor authentication](./mfa) is currently not enabled.", "admin.guest_access.mfaDescriptionMFANotEnabled": "[Multi-factor authentication](./mfa) is currently not enabled.",
"admin.guest_access.mfaDescriptionMFANotEnforced": "[Multi-factor authentication](./mfa) is currently not enforced.", "admin.guest_access.mfaDescriptionMFANotEnforced": "[Multi-factor authentication](./mfa) is currently not enforced.",

Просмотреть файл

@@ -26,7 +26,7 @@ export const renderWithIntl = (component: React.ReactNode | React.ReactNodeArray
return render(<IntlProvider locale={locale}>{component}</IntlProvider>); return render(<IntlProvider locale={locale}>{component}</IntlProvider>);
}; };
export const renderWithIntlAndStore = (component: React.ReactNode | React.ReactNodeArray, initialState: DeepPartial<GlobalState>, locale = 'en') => { export const renderWithIntlAndStore = (component: React.ReactNode | React.ReactNodeArray, initialState: DeepPartial<GlobalState> = {}, locale = 'en') => {
// We use a redux-mock-store store for testing, but we set up a real store to ensure the initial state is complete // We use a redux-mock-store store for testing, but we set up a real store to ensure the initial state is complete
const realStore = configureStore(initialState); const realStore = configureStore(initialState);

Просмотреть файл

@@ -127,6 +127,7 @@ export type ClientConfig = {
GuestAccountsEnforceMultifactorAuthentication: string; GuestAccountsEnforceMultifactorAuthentication: string;
HasImageProxy: string; HasImageProxy: string;
HelpLink: string; HelpLink: string;
HideGuestTags: string;
IosAppDownloadLink: string; IosAppDownloadLink: string;
IosLatestVersion: string; IosLatestVersion: string;
IosMinVersion: string; IosMinVersion: string;
@@ -846,6 +847,7 @@ export type DisplaySettings = {
export type GuestAccountsSettings = { export type GuestAccountsSettings = {
Enable: boolean; Enable: boolean;
HideTags: boolean;
AllowEmailAccounts: boolean; AllowEmailAccounts: boolean;
EnforceMultifactorAuthentication: boolean; EnforceMultifactorAuthentication: boolean;
RestrictCreationToDomains: string; RestrictCreationToDomains: string;