[NEW] Permission for uploading files (#3505)
Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
404c7cff07
commit
a1cee02fb0
|
@ -13,6 +13,7 @@ import { events, logEvent } from '../../utils/log';
|
||||||
|
|
||||||
interface IMessageBoxRecordAudioProps {
|
interface IMessageBoxRecordAudioProps {
|
||||||
theme: string;
|
theme: string;
|
||||||
|
permissionToUpload: boolean;
|
||||||
recordingCallback: Function;
|
recordingCallback: Function;
|
||||||
onFinish: Function;
|
onFinish: Function;
|
||||||
}
|
}
|
||||||
|
@ -192,9 +193,11 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { theme } = this.props;
|
const { theme, permissionToUpload } = this.props;
|
||||||
const { isRecording, isRecorderActive } = this.state;
|
const { isRecording, isRecorderActive } = this.state;
|
||||||
|
if (!permissionToUpload) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (!isRecording && !isRecorderActive) {
|
if (!isRecording && !isRecorderActive) {
|
||||||
return (
|
return (
|
||||||
<BorderlessButton
|
<BorderlessButton
|
||||||
|
|
|
@ -109,6 +109,8 @@ interface IMessageBoxProps {
|
||||||
sharing: boolean;
|
sharing: boolean;
|
||||||
isActionsEnabled: boolean;
|
isActionsEnabled: boolean;
|
||||||
usedCannedResponse: string;
|
usedCannedResponse: string;
|
||||||
|
uploadFilePermission: string[];
|
||||||
|
serverVersion: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageBoxState {
|
interface IMessageBoxState {
|
||||||
|
@ -124,6 +126,7 @@ interface IMessageBoxState {
|
||||||
};
|
};
|
||||||
tshow: boolean;
|
tshow: boolean;
|
||||||
mentionLoading: boolean;
|
mentionLoading: boolean;
|
||||||
|
permissionToUpload: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
@ -179,41 +182,13 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
showCommandPreview: false,
|
showCommandPreview: false,
|
||||||
command: {},
|
command: {},
|
||||||
tshow: false,
|
tshow: false,
|
||||||
mentionLoading: false
|
mentionLoading: false,
|
||||||
|
permissionToUpload: true
|
||||||
};
|
};
|
||||||
this.text = '';
|
this.text = '';
|
||||||
this.selection = { start: 0, end: 0 };
|
this.selection = { start: 0, end: 0 };
|
||||||
this.focused = false;
|
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 = {
|
const libPickerLabels = {
|
||||||
cropperChooseText: I18n.t('Choose'),
|
cropperChooseText: I18n.t('Choose'),
|
||||||
cropperCancelText: I18n.t('Cancel'),
|
cropperCancelText: I18n.t('Cancel'),
|
||||||
|
@ -277,6 +252,8 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
this.onChangeText(usedCannedResponse);
|
this.onChangeText(usedCannedResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setOptions();
|
||||||
|
|
||||||
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
||||||
// didFocus
|
// didFocus
|
||||||
// We should wait pushed views be dismissed
|
// We should wait pushed views be dismissed
|
||||||
|
@ -321,10 +298,20 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: any, nextState: any) {
|
shouldComponentUpdate(nextProps: IMessageBoxProps, nextState: IMessageBoxState) {
|
||||||
const { showEmojiKeyboard, showSend, recording, mentions, commandPreview, tshow, mentionLoading, trackingType } = this.state;
|
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) {
|
if (nextProps.theme !== theme) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -358,6 +345,9 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
if (nextState.tshow !== tshow) {
|
if (nextState.tshow !== tshow) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (nextState.permissionToUpload !== permissionToUpload) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!dequal(nextState.mentions, mentions)) {
|
if (!dequal(nextState.mentions, mentions)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -367,12 +357,22 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
if (!dequal(nextProps.message?.id, message?.id)) {
|
if (!dequal(nextProps.message?.id, message?.id)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!dequal(nextProps.uploadFilePermission, uploadFilePermission)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (nextProps.usedCannedResponse !== usedCannedResponse) {
|
if (nextProps.usedCannedResponse !== usedCannedResponse) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: IMessageBoxProps) {
|
||||||
|
const { uploadFilePermission } = this.props;
|
||||||
|
if (!dequal(prevProps.uploadFilePermission, uploadFilePermission)) {
|
||||||
|
this.setOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
console.countReset(`${this.constructor.name}.render calls`);
|
console.countReset(`${this.constructor.name}.render calls`);
|
||||||
if (this.onChangeText && this.onChangeText.stop) {
|
if (this.onChangeText && this.onChangeText.stop) {
|
||||||
|
@ -404,6 +404,19 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 => {
|
onChangeText: any = (text: string): void => {
|
||||||
const isTextEmpty = text.length === 0;
|
const isTextEmpty = text.length === 0;
|
||||||
this.setShowSend(!isTextEmpty);
|
this.setShowSend(!isTextEmpty);
|
||||||
|
@ -666,8 +679,9 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
canUploadFile = (file: any) => {
|
canUploadFile = (file: any) => {
|
||||||
|
const { permissionToUpload } = this.state;
|
||||||
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props;
|
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) {
|
if (result.success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -766,8 +780,41 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
showMessageBoxActions = () => {
|
showMessageBoxActions = () => {
|
||||||
logEvent(events.ROOM_SHOW_BOX_ACTIONS);
|
logEvent(events.ROOM_SHOW_BOX_ACTIONS);
|
||||||
|
const { permissionToUpload } = this.state;
|
||||||
const { showActionSheet } = this.props;
|
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 = () => {
|
editCancel = () => {
|
||||||
|
@ -968,8 +1015,17 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderContent = () => {
|
renderContent = () => {
|
||||||
const { recording, showEmojiKeyboard, showSend, mentions, trackingType, commandPreview, showCommandPreview, mentionLoading } =
|
const {
|
||||||
this.state;
|
recording,
|
||||||
|
showEmojiKeyboard,
|
||||||
|
showSend,
|
||||||
|
mentions,
|
||||||
|
trackingType,
|
||||||
|
commandPreview,
|
||||||
|
showCommandPreview,
|
||||||
|
mentionLoading,
|
||||||
|
permissionToUpload
|
||||||
|
} = this.state;
|
||||||
const {
|
const {
|
||||||
editing,
|
editing,
|
||||||
message,
|
message,
|
||||||
|
@ -995,7 +1051,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
const recordAudio =
|
const recordAudio =
|
||||||
showSend || !Message_AudioRecorderEnabled ? null : (
|
showSend || !Message_AudioRecorderEnabled ? null : (
|
||||||
<RecordAudio theme={theme} recordingCallback={this.recordingCallback} onFinish={this.finishAudioMessage} />
|
<RecordAudio
|
||||||
|
theme={theme}
|
||||||
|
recordingCallback={this.recordingCallback}
|
||||||
|
onFinish={this.finishAudioMessage}
|
||||||
|
permissionToUpload={permissionToUpload}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const commandsPreviewAndMentions = !recording ? (
|
const commandsPreviewAndMentions = !recording ? (
|
||||||
|
@ -1117,7 +1178,8 @@ const mapStateToProps = (state: any) => ({
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
|
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
|
||||||
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize,
|
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 = {
|
const dispatchToProps = {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"error-save-video": "Error while saving video",
|
"error-save-video": "Error while saving video",
|
||||||
"error-field-unavailable": "{{field}} is already in use :(",
|
"error-field-unavailable": "{{field}} is already in use :(",
|
||||||
"error-file-too-large": "File is too large",
|
"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-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-input-is-not-a-valid-field": "{{input}} is not a valid {{field}}",
|
||||||
"error-invalid-actionlink": "Invalid action link",
|
"error-invalid-actionlink": "Invalid action link",
|
||||||
|
|
|
@ -55,7 +55,8 @@ const PERMISSIONS = [
|
||||||
'convert-team',
|
'convert-team',
|
||||||
'edit-omnichannel-contact',
|
'edit-omnichannel-contact',
|
||||||
'edit-livechat-room-customfields',
|
'edit-livechat-room-customfields',
|
||||||
'view-canned-responses'
|
'view-canned-responses',
|
||||||
|
'mobile-upload-file'
|
||||||
];
|
];
|
||||||
|
|
||||||
export async function setPermissions() {
|
export async function setPermissions() {
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
export const canUploadFile = (file, allowList, maxFileSize) => {
|
export const canUploadFile = (file, allowList, maxFileSize, permissionToUploadFile) => {
|
||||||
if (!(file && file.path)) {
|
if (!(file && file.path)) {
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
if (maxFileSize > -1 && file.size > maxFileSize) {
|
if (maxFileSize > -1 && file.size > maxFileSize) {
|
||||||
return { success: false, error: 'error-file-too-large' };
|
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 white list is empty, all media types are enabled
|
||||||
if (!allowList || allowList === '*') {
|
if (!allowList || allowList === '*') {
|
||||||
return { success: true };
|
return { success: true };
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { RouteProp } from '@react-navigation/native';
|
||||||
import { NativeModules, Text, View } from 'react-native';
|
import { NativeModules, Text, View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ShareExtension from 'rn-extensions-share';
|
import ShareExtension from 'rn-extensions-share';
|
||||||
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
import { InsideStackParamList } from '../../stacks/types';
|
import { InsideStackParamList } from '../../stacks/types';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
@ -141,6 +142,17 @@ class ShareView extends Component<IShareViewProps, IShareViewState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 () => {
|
getReadOnly = async () => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { user } = this.props;
|
const { user } = this.props;
|
||||||
|
@ -150,10 +162,12 @@ class ShareView extends Component<IShareViewProps, IShareViewState> {
|
||||||
|
|
||||||
getAttachments = async () => {
|
getAttachments = async () => {
|
||||||
const { mediaAllowList, maxFileSize } = this.state;
|
const { mediaAllowList, maxFileSize } = this.state;
|
||||||
|
const permissionToUploadFile = await this.getPermissionMobileUpload();
|
||||||
|
|
||||||
const items = await Promise.all(
|
const items = await Promise.all(
|
||||||
this.files.map(async item => {
|
this.files.map(async item => {
|
||||||
// Check server settings
|
// Check server settings
|
||||||
const { success: canUpload, error } = canUploadFile(item, mediaAllowList, maxFileSize);
|
const { success: canUpload, error } = canUploadFile(item, mediaAllowList, maxFileSize, permissionToUploadFile);
|
||||||
item.canUpload = canUpload;
|
item.canUpload = canUpload;
|
||||||
item.error = error;
|
item.error = error;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue