From fbd7fedfbc5078138f453ae3ed20965ae9a73980 Mon Sep 17 00:00:00 2001 From: Martin Kraft Date: Fri, 13 Mar 2020 09:17:42 -0400 Subject: [PATCH] MM-21925: Enforces 'create_post' permission. (#3904) * MM-21925: Enforces 'create_post' permission. * MM-21925: Reverts adding 'can_post' check from post component. * MM-21925: Removes unused variable and some duplicate imports. * MM-21925: Removes unnecessary prop. * MM-21925: Removes unused test prop. --- app/components/post_textbox/index.js | 9 ++++++++ .../post_textbox/post_textbox.test.js | 11 +++++++++ .../post_textbox/post_textbox_base.js | 23 ++++++++++--------- app/screens/post_options/index.js | 12 ++++++++++ app/screens/post_options/index.test.js | 4 ++++ app/screens/post_options/post_options.test.js | 6 +++++ 6 files changed, 54 insertions(+), 11 deletions(-) diff --git a/app/components/post_textbox/index.js b/app/components/post_textbox/index.js index b4fe0934f..817962e2d 100644 --- a/app/components/post_textbox/index.js +++ b/app/components/post_textbox/index.js @@ -50,6 +50,14 @@ function mapStateToProps(state, ownProps) { const currentChannelStats = getCurrentChannelStats(state); const currentChannelMembersCount = currentChannelStats?.member_count || 0; // eslint-disable-line camelcase const isTimezoneEnabled = config?.ExperimentalTimezone === 'true'; + const canPost = haveIChannelPermission( + state, + { + channel: currentChannel.id, + team: currentChannel.team_id, + permission: Permissions.CREATE_POST, + }, + ); let useChannelMentions = true; if (isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)) { @@ -83,6 +91,7 @@ function mapStateToProps(state, ownProps) { currentChannelMembersCount, isTimezoneEnabled, isLandscape: isLandscape(state), + canPost, useChannelMentions, }; } diff --git a/app/components/post_textbox/post_textbox.test.js b/app/components/post_textbox/post_textbox.test.js index 930e2ab7e..c6aaa4dc6 100644 --- a/app/components/post_textbox/post_textbox.test.js +++ b/app/components/post_textbox/post_textbox.test.js @@ -63,6 +63,7 @@ describe('PostTextBox', () => { valueEvent: '', isLandscape: false, screenId: 'NavigationScreen1', + canPost: true, currentChannelMembersCount: 50, enableConfirmNotificationsToChannel: true, useChannelMentions: true, @@ -554,5 +555,15 @@ describe('PostTextBox', () => { wrapper.setProps({value: 'value', channelId: 'channel-id2'}); expect(wrapper.state('value')).toEqual('value'); }); + + test('should be disabled without the create_post permission', () => { + const props = {...baseProps, canPost: false}; + + const wrapper = shallowWithIntl( + , + ); + + expect(wrapper.find(PasteableTextInput).prop('editable')).toBe(false); + }); }); diff --git a/app/components/post_textbox/post_textbox_base.js b/app/components/post_textbox/post_textbox_base.js index 7ef2c4d07..96513e56d 100644 --- a/app/components/post_textbox/post_textbox_base.js +++ b/app/components/post_textbox/post_textbox_base.js @@ -97,6 +97,7 @@ export default class PostTextBoxBase extends PureComponent { currentChannel: PropTypes.object, isLandscape: PropTypes.bool.isRequired, screenId: PropTypes.string.isRequired, + canPost: PropTypes.bool.isRequired, useChannelMentions: PropTypes.bool.isRequired, }; @@ -257,7 +258,7 @@ export default class PostTextBoxBase extends PureComponent { }; getTextInputButton = (actionType) => { - const {channelIsReadOnly, theme} = this.props; + const {channelIsReadOnly, theme, canPost} = this.props; const style = getStyleSheet(theme); let button = null; @@ -265,7 +266,7 @@ export default class PostTextBoxBase extends PureComponent { let iconColor = changeOpacity(theme.centerChannelColor, 0.64); let isDisabled = false; - if (!channelIsReadOnly) { + if (!channelIsReadOnly && canPost) { switch (actionType) { case 'at': isDisabled = this.state.value[this.state.value.length - 1] === '@'; @@ -313,7 +314,7 @@ export default class PostTextBoxBase extends PureComponent { } getMediaButton = (actionType) => { - const {canUploadFiles, channelIsReadOnly, files, maxFileSize, theme} = this.props; + const {canUploadFiles, channelIsReadOnly, files, maxFileSize, theme, canPost} = this.props; const style = getStyleSheet(theme); let button = null; const props = { @@ -328,7 +329,7 @@ export default class PostTextBoxBase extends PureComponent { buttonContainerStyle: style.iconWrapper, }; - if (canUploadFiles && !channelIsReadOnly) { + if (canUploadFiles && !channelIsReadOnly && canPost) { switch (actionType) { case 'file': button = ( @@ -351,11 +352,11 @@ export default class PostTextBoxBase extends PureComponent { } getInputContainerStyle = () => { - const {channelIsReadOnly, theme} = this.props; + const {channelIsReadOnly, theme, canPost} = this.props; const style = getStyleSheet(theme); const inputContainerStyle = [style.inputContainer]; - if (channelIsReadOnly) { + if (channelIsReadOnly || !canPost) { inputContainerStyle.push(style.readonlyContainer); } @@ -363,10 +364,10 @@ export default class PostTextBoxBase extends PureComponent { }; getPlaceHolder = () => { - const {channelIsReadOnly, rootId} = this.props; + const {channelIsReadOnly, rootId, canPost} = this.props; let placeholder; - if (channelIsReadOnly) { + if (channelIsReadOnly || !canPost) { placeholder = {id: t('mobile.create_post.read_only'), defaultMessage: 'This channel is read-only.'}; } else if (rootId) { placeholder = {id: t('create_comment.addComment'), defaultMessage: 'Add a comment...'}; @@ -897,7 +898,7 @@ export default class PostTextBoxBase extends PureComponent { renderTextBox = () => { const {intl} = this.context; - const {channelDisplayName, channelIsArchived, channelIsReadOnly, theme, isLandscape, files, rootId} = this.props; + const {channelDisplayName, channelIsArchived, channelIsReadOnly, theme, isLandscape, files, rootId, canPost} = this.props; const style = getStyleSheet(theme); if (channelIsArchived) { @@ -949,12 +950,12 @@ export default class PostTextBoxBase extends PureComponent { keyboardType={this.state.keyboardType} onEndEditing={this.handleEndEditing} disableFullscreenUI={true} - editable={!channelIsReadOnly} + editable={!channelIsReadOnly && canPost} onPaste={this.handlePasteFiles} keyboardAppearance={getKeyboardAppearanceFromTheme(theme)} onContentSizeChange={Platform.OS === 'android' ? this.handleInputSizeChange : null} /> - {!channelIsReadOnly && + {!channelIsReadOnly && canPost && { general: { serverVersion: '5.18', }, + channels: {myMembers: {}}, + teams: {myMembers: {}}, + roles: {roles: {}}, + users: {profiles: {}}, }, }; diff --git a/app/screens/post_options/post_options.test.js b/app/screens/post_options/post_options.test.js index 53e112abe..df4b1ea57 100644 --- a/app/screens/post_options/post_options.test.js +++ b/app/screens/post_options/post_options.test.js @@ -122,4 +122,10 @@ describe('PostOptions', () => { expect(actions.deletePost).toBeCalled(); expect(actions.removePost).toBeCalled(); }); + + test('should not show reply option without create_post permission', () => { + const wrapper = getWrapper({canPost: false}); + + expect(wrapper.findWhere((node) => node.key() === 'reply')).toMatchObject({}); + }); });