From a1cee02fb0e018e8851adff1e492c6504e38257a Mon Sep 17 00:00:00 2001 From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Date: Mon, 13 Dec 2021 13:27:01 -0300 Subject: [PATCH] [NEW] Permission for uploading files (#3505) Co-authored-by: Diego Mello --- app/containers/MessageBox/RecordAudio.tsx | 7 +- app/containers/MessageBox/index.tsx | 140 ++++++++++++++++------ app/i18n/locales/en.json | 1 + app/lib/methods/getPermissions.js | 3 +- app/utils/media.js | 5 +- app/views/ShareView/index.tsx | 16 ++- 6 files changed, 128 insertions(+), 44 deletions(-) diff --git a/app/containers/MessageBox/RecordAudio.tsx b/app/containers/MessageBox/RecordAudio.tsx index fa6c509e..e219e642 100644 --- a/app/containers/MessageBox/RecordAudio.tsx +++ b/app/containers/MessageBox/RecordAudio.tsx @@ -13,6 +13,7 @@ import { events, logEvent } from '../../utils/log'; interface IMessageBoxRecordAudioProps { theme: string; + permissionToUpload: boolean; recordingCallback: Function; onFinish: Function; } @@ -192,9 +193,11 @@ export default class RecordAudio extends React.PureComponent { @@ -179,41 +182,13 @@ class MessageBox extends Component { showCommandPreview: false, command: {}, tshow: false, - mentionLoading: false + mentionLoading: false, + permissionToUpload: true }; this.text = ''; this.selection = { start: 0, end: 0 }; this.focused = false; - // MessageBox Actions - this.options = [ - { - title: I18n.t('Take_a_photo'), - icon: 'camera-photo', - onPress: this.takePhoto - }, - { - title: I18n.t('Take_a_video'), - icon: 'camera', - onPress: this.takeVideo - }, - { - title: I18n.t('Choose_from_library'), - icon: 'image', - onPress: this.chooseFromLibrary - }, - { - title: I18n.t('Choose_file'), - icon: 'attach', - onPress: this.chooseFile - }, - { - title: I18n.t('Create_Discussion'), - icon: 'discussions', - onPress: this.createDiscussion - } - ]; - const libPickerLabels = { cropperChooseText: I18n.t('Choose'), cropperCancelText: I18n.t('Cancel'), @@ -277,6 +252,8 @@ class MessageBox extends Component { this.onChangeText(usedCannedResponse); } + this.setOptions(); + this.unsubscribeFocus = navigation.addListener('focus', () => { // didFocus // We should wait pushed views be dismissed @@ -321,10 +298,20 @@ class MessageBox extends Component { } } - shouldComponentUpdate(nextProps: any, nextState: any) { - const { showEmojiKeyboard, showSend, recording, mentions, commandPreview, tshow, mentionLoading, trackingType } = this.state; + shouldComponentUpdate(nextProps: IMessageBoxProps, nextState: IMessageBoxState) { + const { + showEmojiKeyboard, + showSend, + recording, + mentions, + commandPreview, + tshow, + mentionLoading, + trackingType, + permissionToUpload + } = this.state; - const { roomType, replying, editing, isFocused, message, theme, usedCannedResponse } = this.props; + const { roomType, replying, editing, isFocused, message, theme, usedCannedResponse, uploadFilePermission } = this.props; if (nextProps.theme !== theme) { return true; } @@ -358,6 +345,9 @@ class MessageBox extends Component { if (nextState.tshow !== tshow) { return true; } + if (nextState.permissionToUpload !== permissionToUpload) { + return true; + } if (!dequal(nextState.mentions, mentions)) { return true; } @@ -367,12 +357,22 @@ class MessageBox extends Component { if (!dequal(nextProps.message?.id, message?.id)) { return true; } + if (!dequal(nextProps.uploadFilePermission, uploadFilePermission)) { + return true; + } if (nextProps.usedCannedResponse !== usedCannedResponse) { return true; } return false; } + componentDidUpdate(prevProps: IMessageBoxProps) { + const { uploadFilePermission } = this.props; + if (!dequal(prevProps.uploadFilePermission, uploadFilePermission)) { + this.setOptions(); + } + } + componentWillUnmount() { console.countReset(`${this.constructor.name}.render calls`); if (this.onChangeText && this.onChangeText.stop) { @@ -404,6 +404,19 @@ class MessageBox extends Component { } } + setOptions = async () => { + const { uploadFilePermission, rid } = this.props; + + // Servers older than 4.2 + if (!uploadFilePermission) { + this.setState({ permissionToUpload: true }); + return; + } + + const permissionToUpload = await RocketChat.hasPermission([uploadFilePermission], rid); + this.setState({ permissionToUpload: permissionToUpload[0] }); + }; + onChangeText: any = (text: string): void => { const isTextEmpty = text.length === 0; this.setShowSend(!isTextEmpty); @@ -666,8 +679,9 @@ class MessageBox extends Component { }; canUploadFile = (file: any) => { + const { permissionToUpload } = this.state; const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props; - const result = canUploadFile(file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize); + const result = canUploadFile(file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize, permissionToUpload); if (result.success) { return true; } @@ -766,8 +780,41 @@ class MessageBox extends Component { showMessageBoxActions = () => { logEvent(events.ROOM_SHOW_BOX_ACTIONS); + const { permissionToUpload } = this.state; const { showActionSheet } = this.props; - showActionSheet({ options: this.options }); + + const options = []; + if (permissionToUpload) { + options.push( + { + title: I18n.t('Take_a_photo'), + icon: 'camera-photo', + onPress: this.takePhoto + }, + { + title: I18n.t('Take_a_video'), + icon: 'camera', + onPress: this.takeVideo + }, + { + title: I18n.t('Choose_from_library'), + icon: 'image', + onPress: this.chooseFromLibrary + }, + { + title: I18n.t('Choose_file'), + icon: 'attach', + onPress: this.chooseFile + } + ); + } + + options.push({ + title: I18n.t('Create_Discussion'), + icon: 'discussions', + onPress: this.createDiscussion + }); + showActionSheet({ options }); }; editCancel = () => { @@ -968,8 +1015,17 @@ class MessageBox extends Component { }; renderContent = () => { - const { recording, showEmojiKeyboard, showSend, mentions, trackingType, commandPreview, showCommandPreview, mentionLoading } = - this.state; + const { + recording, + showEmojiKeyboard, + showSend, + mentions, + trackingType, + commandPreview, + showCommandPreview, + mentionLoading, + permissionToUpload + } = this.state; const { editing, message, @@ -995,7 +1051,12 @@ class MessageBox extends Component { const recordAudio = showSend || !Message_AudioRecorderEnabled ? null : ( - + ); const commandsPreviewAndMentions = !recording ? ( @@ -1117,7 +1178,8 @@ const mapStateToProps = (state: any) => ({ user: getUserSelector(state), FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize, - Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled + Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled, + uploadFilePermission: state.permissions['mobile-upload-file'] }); const dispatchToProps = { diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json index 6787b3fd..39c56be7 100644 --- a/app/i18n/locales/en.json +++ b/app/i18n/locales/en.json @@ -21,6 +21,7 @@ "error-save-video": "Error while saving video", "error-field-unavailable": "{{field}} is already in use :(", "error-file-too-large": "File is too large", + "error-not-permission-to-upload-file": "You don't have permission to upload files", "error-importer-not-defined": "The importer was not defined correctly, it is missing the Import class.", "error-input-is-not-a-valid-field": "{{input}} is not a valid {{field}}", "error-invalid-actionlink": "Invalid action link", diff --git a/app/lib/methods/getPermissions.js b/app/lib/methods/getPermissions.js index 589aa6bd..b680a919 100644 --- a/app/lib/methods/getPermissions.js +++ b/app/lib/methods/getPermissions.js @@ -55,7 +55,8 @@ const PERMISSIONS = [ 'convert-team', 'edit-omnichannel-contact', 'edit-livechat-room-customfields', - 'view-canned-responses' + 'view-canned-responses', + 'mobile-upload-file' ]; export async function setPermissions() { diff --git a/app/utils/media.js b/app/utils/media.js index b05f95a9..07f6f58d 100644 --- a/app/utils/media.js +++ b/app/utils/media.js @@ -1,10 +1,13 @@ -export const canUploadFile = (file, allowList, maxFileSize) => { +export const canUploadFile = (file, allowList, maxFileSize, permissionToUploadFile) => { if (!(file && file.path)) { return { success: true }; } if (maxFileSize > -1 && file.size > maxFileSize) { return { success: false, error: 'error-file-too-large' }; } + if (!permissionToUploadFile) { + return { success: false, error: 'error-not-permission-to-upload-file' }; + } // if white list is empty, all media types are enabled if (!allowList || allowList === '*') { return { success: true }; diff --git a/app/views/ShareView/index.tsx b/app/views/ShareView/index.tsx index 4061626e..a3ad287a 100644 --- a/app/views/ShareView/index.tsx +++ b/app/views/ShareView/index.tsx @@ -4,6 +4,7 @@ import { RouteProp } from '@react-navigation/native'; import { NativeModules, Text, View } from 'react-native'; import { connect } from 'react-redux'; import ShareExtension from 'rn-extensions-share'; +import { Q } from '@nozbe/watermelondb'; import { InsideStackParamList } from '../../stacks/types'; import { themes } from '../../constants/colors'; @@ -141,6 +142,17 @@ class ShareView extends Component { } }; + getPermissionMobileUpload = async () => { + const { room } = this.state; + const db = database.active; + const permissionsCollection = db.get('permissions'); + const uploadFilePermissionFetch = await permissionsCollection.query(Q.where('id', Q.like('mobile-upload-file'))).fetch(); + const uploadFilePermission = uploadFilePermissionFetch[0]?.roles; + const permissionToUpload = await RocketChat.hasPermission([uploadFilePermission], room.rid); + // uploadFilePermission as undefined is considered that there isn't this permission, so all can upload file. + return !uploadFilePermission || permissionToUpload[0]; + }; + getReadOnly = async () => { const { room } = this.state; const { user } = this.props; @@ -150,10 +162,12 @@ class ShareView extends Component { getAttachments = async () => { const { mediaAllowList, maxFileSize } = this.state; + const permissionToUploadFile = await this.getPermissionMobileUpload(); + const items = await Promise.all( this.files.map(async item => { // Check server settings - const { success: canUpload, error } = canUploadFile(item, mediaAllowList, maxFileSize); + const { success: canUpload, error } = canUploadFile(item, mediaAllowList, maxFileSize, permissionToUploadFile); item.canUpload = canUpload; item.error = error;