PLT-6262 Add config setting to disable file attachments (#6301)
* Add config setting to disable file attachments * Add unit tests * Updating UI for no attachments (#6312) * Update UI text on file upload System Console setting (#6313) * Update storage_settings.jsx * Update en.json
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
44a8f76d99
Коммит
49481caf6d
@@ -35,6 +35,7 @@ import store from 'stores/redux_store.jsx';
|
||||
const dispatch = store.dispatch;
|
||||
const getState = store.getState;
|
||||
import {viewChannel, getChannelAndMyMember, getChannelStats} from 'mattermost-redux/actions/channels';
|
||||
import {setServerVersion} from 'mattermost-redux/actions/general';
|
||||
import {ChannelTypes} from 'mattermost-redux/action_types';
|
||||
|
||||
const MAX_WEBSOCKET_FAILS = 7;
|
||||
@@ -390,7 +391,7 @@ function handleStatusChangedEvent(msg) {
|
||||
|
||||
function handleHelloEvent(msg) {
|
||||
Client.serverVersion = msg.data.server_version;
|
||||
AsyncClient.checkVersion();
|
||||
setServerVersion(msg.data.server_version)(dispatch, getState);
|
||||
}
|
||||
|
||||
function handleWebrtc(msg) {
|
||||
|
||||
@@ -25,6 +25,7 @@ export default class StorageSettings extends AdminSettings {
|
||||
}
|
||||
|
||||
getConfigFromState(config) {
|
||||
config.FileSettings.EnableFileAttachments = this.state.enableFileAttachments;
|
||||
config.FileSettings.MaxFileSize = this.parseInt(this.state.maxFileSize) * 1024 * 1024;
|
||||
config.FileSettings.DriverName = this.state.driverName;
|
||||
config.FileSettings.Directory = this.state.directory;
|
||||
@@ -39,6 +40,7 @@ export default class StorageSettings extends AdminSettings {
|
||||
|
||||
getStateFromConfig(config) {
|
||||
return {
|
||||
enableFileAttachments: config.FileSettings.EnableFileAttachments,
|
||||
maxFileSize: config.FileSettings.MaxFileSize / 1024 / 1024,
|
||||
driverName: config.FileSettings.DriverName,
|
||||
directory: config.FileSettings.Directory,
|
||||
@@ -199,6 +201,23 @@ export default class StorageSettings extends AdminSettings {
|
||||
onChange={this.handleChange}
|
||||
disabled={this.state.driverName !== DRIVER_S3}
|
||||
/>
|
||||
<BooleanSetting
|
||||
id='enableFileAttachments'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='admin.file.enableFileAttachments'
|
||||
defaultMessage='Enable File Attachments:'
|
||||
/>
|
||||
}
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id='admin.file.enableFileAttachmentsDesc'
|
||||
defaultMessage='When false, disable file and image uploads on messages.'
|
||||
/>
|
||||
}
|
||||
value={this.state.enableFileAttachments}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<TextSetting
|
||||
id='maxFileSize'
|
||||
label={
|
||||
@@ -216,6 +235,7 @@ export default class StorageSettings extends AdminSettings {
|
||||
}
|
||||
value={this.state.maxFileSize}
|
||||
onChange={this.handleChange}
|
||||
disabled={!this.state.enableFileAttachments}
|
||||
/>
|
||||
</SettingsGroup>
|
||||
);
|
||||
|
||||
@@ -630,6 +630,11 @@ export default class CreatePost extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let attachmentsDisabled = '';
|
||||
if (global.window.mm_config.EnableFileAttachments === 'false') {
|
||||
attachmentsDisabled = ' post-create--attachment-disabled';
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
id='create_post'
|
||||
@@ -638,7 +643,7 @@ export default class CreatePost extends React.Component {
|
||||
className={centerClass}
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
<div className='post-create'>
|
||||
<div className={'post-create' + attachmentsDisabled}>
|
||||
<div className='post-create-body'>
|
||||
<div className='post-body__cell'>
|
||||
<Textbox
|
||||
|
||||
@@ -132,6 +132,11 @@ class FileUpload extends React.Component {
|
||||
}
|
||||
|
||||
handleDrop(e) {
|
||||
if (global.window.mm_config.EnableFileAttachments === 'false') {
|
||||
this.props.onUploadError(Utils.localizeMessage('file_upload.disabled', 'File attachments are disabled.'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onUploadError(null);
|
||||
|
||||
var files = e.originalEvent.dataTransfer.files;
|
||||
@@ -163,36 +168,47 @@ class FileUpload extends React.Component {
|
||||
}
|
||||
});
|
||||
|
||||
$(containerSelector).dragster({
|
||||
enter(dragsterEvent, e) {
|
||||
var files = e.originalEvent.dataTransfer;
|
||||
let dragsterActions = {};
|
||||
if (global.window.mm_config.EnableFileAttachments === 'true') {
|
||||
dragsterActions = {
|
||||
enter(dragsterEvent, e) {
|
||||
var files = e.originalEvent.dataTransfer;
|
||||
|
||||
if (Utils.isFileTransfer(files)) {
|
||||
$(overlaySelector).removeClass('hidden');
|
||||
if (Utils.isFileTransfer(files)) {
|
||||
$(overlaySelector).removeClass('hidden');
|
||||
}
|
||||
},
|
||||
leave(dragsterEvent, e) {
|
||||
var files = e.originalEvent.dataTransfer;
|
||||
|
||||
if (Utils.isFileTransfer(files) && !overlay.hasClass('hidden')) {
|
||||
overlay.addClass('hidden');
|
||||
}
|
||||
|
||||
dragTimeout.cancel();
|
||||
},
|
||||
over() {
|
||||
dragTimeout.fireAfter(OverlayTimeout);
|
||||
},
|
||||
drop(dragsterEvent, e) {
|
||||
if (!overlay.hasClass('hidden')) {
|
||||
overlay.addClass('hidden');
|
||||
}
|
||||
|
||||
dragTimeout.cancel();
|
||||
|
||||
self.handleDrop(e);
|
||||
}
|
||||
},
|
||||
leave(dragsterEvent, e) {
|
||||
var files = e.originalEvent.dataTransfer;
|
||||
|
||||
if (Utils.isFileTransfer(files) && !overlay.hasClass('hidden')) {
|
||||
overlay.addClass('hidden');
|
||||
};
|
||||
} else {
|
||||
dragsterActions = {
|
||||
drop(dragsterEvent, e) {
|
||||
self.handleDrop(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
dragTimeout.cancel();
|
||||
},
|
||||
over() {
|
||||
dragTimeout.fireAfter(OverlayTimeout);
|
||||
},
|
||||
drop(dragsterEvent, e) {
|
||||
if (!overlay.hasClass('hidden')) {
|
||||
overlay.addClass('hidden');
|
||||
}
|
||||
|
||||
dragTimeout.cancel();
|
||||
|
||||
self.handleDrop(e);
|
||||
}
|
||||
});
|
||||
$(containerSelector).dragster(dragsterActions);
|
||||
|
||||
this.props.onFileUploadChange();
|
||||
}
|
||||
@@ -247,7 +263,12 @@ class FileUpload extends React.Component {
|
||||
|
||||
// This looks redundant, but must be done this way due to
|
||||
// setState being an asynchronous call
|
||||
if (items) {
|
||||
if (items && items.length > 0) {
|
||||
if (global.window.mm_config.EnableFileAttachments === 'false') {
|
||||
this.props.onUploadError(Utils.localizeMessage('file_upload.disabled', 'File attachments are disabled.'));
|
||||
return;
|
||||
}
|
||||
|
||||
var numToUpload = Math.min(Constants.MAX_UPLOAD_FILES - this.props.getFileCount(ChannelStore.getCurrentId()), items.length);
|
||||
|
||||
if (items.length > numToUpload) {
|
||||
@@ -305,6 +326,12 @@ class FileUpload extends React.Component {
|
||||
keyUpload(e) {
|
||||
if (Utils.cmdOrCtrlPressed(e) && e.keyCode === Constants.KeyCodes.U) {
|
||||
e.preventDefault();
|
||||
|
||||
if (global.window.mm_config.EnableFileAttachments === 'false') {
|
||||
this.props.onUploadError(Utils.localizeMessage('file_upload.disabled', 'File attachments are disabled.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((this.props.postType === 'post' && document.activeElement.id === 'post_textbox') ||
|
||||
(this.props.postType === 'comment' && document.activeElement.id === 'reply_textbox')) {
|
||||
$(this.refs.fileInput).focus().trigger('click');
|
||||
@@ -361,11 +388,9 @@ class FileUpload extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
ref='input'
|
||||
className={'btn btn-file' + (uploadsRemaining <= 0 ? ' btn-file__disabled' : '')}
|
||||
>
|
||||
let fileDiv;
|
||||
if (global.window.mm_config.EnableFileAttachments === 'true') {
|
||||
fileDiv = (
|
||||
<div className='icon--attachment'>
|
||||
<span
|
||||
className='icon'
|
||||
@@ -380,6 +405,15 @@ class FileUpload extends React.Component {
|
||||
accept={accept}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
ref='input'
|
||||
className={'btn btn-file' + (uploadsRemaining <= 0 ? ' btn-file__disabled' : '')}
|
||||
>
|
||||
{fileDiv}
|
||||
{emojiSpan}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -300,6 +300,8 @@
|
||||
"admin.email.smtpUsernameTitle": "SMTP Server Username:",
|
||||
"admin.email.testing": "Testing...",
|
||||
"admin.false": "false",
|
||||
"admin.file.enableFileAttachments": "Enable File Attachments:",
|
||||
"admin.file.enableFileAttachmentsDesc": "When false, disable file and image uploads on messages.",
|
||||
"admin.file_upload.chooseFile": "Choose File",
|
||||
"admin.file_upload.noFile": "No file uploaded",
|
||||
"admin.file_upload.uploadFile": "Upload",
|
||||
@@ -1324,6 +1326,7 @@
|
||||
"file_info_preview.type": "File type ",
|
||||
"file_upload.fileAbove": "File above {max}MB cannot be uploaded: {filename}",
|
||||
"file_upload.filesAbove": "Files above {max}MB cannot be uploaded: {filenames}",
|
||||
"file_upload.disabled": "File attachments are disabled.",
|
||||
"file_upload.limited": "Uploads limited to {count} files maximum. Please use additional posts for more files.",
|
||||
"file_upload.pasted": "Image Pasted at ",
|
||||
"filtered_channels_list.count": "{count} {count, plural, =0 {0 channels} one {channel} other {channels}}",
|
||||
|
||||
@@ -12,7 +12,6 @@ import PDFJS from 'pdfjs-dist';
|
||||
|
||||
import * as Websockets from 'actions/websocket_actions.jsx';
|
||||
import {loadMeAndConfig} from 'actions/user_actions.jsx';
|
||||
import BrowserStore from 'stores/browser_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import * as I18n from 'i18n/i18n.jsx';
|
||||
@@ -86,7 +85,6 @@ function preRenderSetup(callwhendone) {
|
||||
() => {
|
||||
// Turn off to prevent getting stuck in a loop
|
||||
$(window).off('beforeunload');
|
||||
BrowserStore.setLastServerVersion('');
|
||||
if (UserStore.getCurrentUser()) {
|
||||
viewChannel('', ChannelStore.getCurrentId() || '')(dispatch, getState);
|
||||
}
|
||||
@@ -120,6 +118,18 @@ function renderRootComponent() {
|
||||
document.getElementById('root'));
|
||||
}
|
||||
|
||||
let serverVersion = '';
|
||||
|
||||
store.subscribe(() => {
|
||||
const newServerVersion = getState().entities.general.serverVersion;
|
||||
if (serverVersion && serverVersion !== newServerVersion) {
|
||||
console.log('Detected version update refreshing the page'); //eslint-disable-line no-console
|
||||
window.location.reload(true);
|
||||
}
|
||||
|
||||
serverVersion = newServerVersion;
|
||||
});
|
||||
|
||||
global.window.setup_root = () => {
|
||||
// Do the pre-render setup and call renderRootComponent when done
|
||||
preRenderSetup(renderRootComponent);
|
||||
|
||||
@@ -32,6 +32,18 @@
|
||||
}
|
||||
|
||||
.post-create__container {
|
||||
.post-create {
|
||||
&.post-create--attachment-disabled {
|
||||
.post-body__cell {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.post-create-footer {
|
||||
padding-left: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
padding: .5em 0 0;
|
||||
}
|
||||
@@ -71,6 +83,10 @@
|
||||
padding: 0;
|
||||
top: auto;
|
||||
width: 25px;
|
||||
|
||||
.icon--emoji-picker {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,14 +75,6 @@ class BrowserStoreClass {
|
||||
}
|
||||
}
|
||||
|
||||
getLastServerVersion() {
|
||||
return this.getGlobalItem('last_server_version');
|
||||
}
|
||||
|
||||
setLastServerVersion(version) {
|
||||
this.setGlobalItem('last_server_version', version);
|
||||
}
|
||||
|
||||
signalLogout() {
|
||||
if (this.isLocalStorageSupported()) {
|
||||
// PLT-1285 store an identifier in session storage so we can catch if the logout came from this tab on IE11
|
||||
@@ -144,7 +136,6 @@ class BrowserStoreClass {
|
||||
clear() {
|
||||
// persist some values through logout since they're independent of which user is logged in
|
||||
const logoutId = sessionStorage.getItem('__logout__');
|
||||
const serverVersion = this.getLastServerVersion();
|
||||
const landingPageSeen = this.hasSeenLandingPage();
|
||||
const selectedTeams = this.getItem('selected_teams');
|
||||
const recentEmojis = localStorage.getItem(Constants.RECENT_EMOJI_KEY);
|
||||
@@ -160,10 +151,6 @@ class BrowserStoreClass {
|
||||
sessionStorage.setItem('__logout__', logoutId);
|
||||
}
|
||||
|
||||
if (serverVersion) {
|
||||
this.setLastServerVersion(serverVersion);
|
||||
}
|
||||
|
||||
if (landingPageSeen) {
|
||||
this.setLandingPageSeen(landingPageSeen);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import BrowserStore from 'stores/browser_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
|
||||
@@ -21,6 +20,12 @@ const callTracker = {};
|
||||
|
||||
const ASYNC_CLIENT_TIMEOUT = 5000;
|
||||
|
||||
// Redux actions
|
||||
import store from 'stores/redux_store.jsx';
|
||||
const dispatch = store.dispatch;
|
||||
const getState = store.getState;
|
||||
import {setServerVersion} from 'mattermost-redux/actions/general';
|
||||
|
||||
export function dispatchError(err, method) {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_ERROR,
|
||||
@@ -47,17 +52,7 @@ function isCallInProgress(callName) {
|
||||
}
|
||||
|
||||
export function checkVersion() {
|
||||
var serverVersion = Client.getServerVersion();
|
||||
|
||||
if (serverVersion !== BrowserStore.getLastServerVersion()) {
|
||||
if (!BrowserStore.getLastServerVersion() || BrowserStore.getLastServerVersion() === '') {
|
||||
BrowserStore.setLastServerVersion(serverVersion);
|
||||
} else {
|
||||
BrowserStore.setLastServerVersion(serverVersion);
|
||||
window.location.reload(true);
|
||||
console.log('Detected version update refreshing the page'); //eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
setServerVersion(Client.getServerVersion())(dispatch, getState);
|
||||
}
|
||||
|
||||
export function getUser(userId, success, error) {
|
||||
|
||||
Ссылка в новой задаче
Block a user