Skip to content

Commit

Permalink
PLT-6262 Add config setting to disable file attachments (mattermost#6301
Browse files Browse the repository at this point in the history
)

* Add config setting to disable file attachments

* Add unit tests

* Updating UI for no attachments (mattermost#6312)

* Update UI text on file upload System Console setting (mattermost#6313)

* Update storage_settings.jsx

* Update en.json
  • Loading branch information
jwilander committed May 4, 2017
1 parent cf05eba commit b1dca56
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 62 deletions.
3 changes: 2 additions & 1 deletion actions/websocket_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
20 changes: 20 additions & 0 deletions components/admin_console/storage_settings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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={
Expand All @@ -216,6 +235,7 @@ export default class StorageSettings extends AdminSettings {
}
value={this.state.maxFileSize}
onChange={this.handleChange}
disabled={!this.state.enableFileAttachments}
/>
</SettingsGroup>
);
Expand Down
7 changes: 6 additions & 1 deletion components/create_post.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
Expand Down
100 changes: 67 additions & 33 deletions components/file_upload.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -163,36 +168,47 @@ class FileUpload extends React.Component {
}
});

$(containerSelector).dragster({
enter(dragsterEvent, e) {
var files = e.originalEvent.dataTransfer;

if (Utils.isFileTransfer(files)) {
$(overlaySelector).removeClass('hidden');
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');
}
},
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');
}

dragTimeout.cancel();
},
over() {
dragTimeout.fireAfter(OverlayTimeout);
},
drop(dragsterEvent, e) {
if (!overlay.hasClass('hidden')) {
overlay.addClass('hidden');
};
} else {
dragsterActions = {
drop(dragsterEvent, e) {
self.handleDrop(e);
}
};
}

dragTimeout.cancel();

self.handleDrop(e);
}
});
$(containerSelector).dragster(dragsterActions);

this.props.onFileUploadChange();
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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'
Expand All @@ -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>
);
Expand Down
3 changes: 3 additions & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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}}",
Expand Down
14 changes: 12 additions & 2 deletions root.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
16 changes: 16 additions & 0 deletions sass/responsive/_tablet.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -71,6 +83,10 @@
padding: 0;
top: auto;
width: 25px;

.icon--emoji-picker {
display: none;
}
}
}

Expand Down
13 changes: 0 additions & 13 deletions stores/browser_store.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -160,10 +151,6 @@ class BrowserStoreClass {
sessionStorage.setItem('__logout__', logoutId);
}

if (serverVersion) {
this.setLastServerVersion(serverVersion);
}

if (landingPageSeen) {
this.setLandingPageSeen(landingPageSeen);
}
Expand Down
19 changes: 7 additions & 12 deletions utils/async_client.jsx
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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,
Expand All @@ -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) {
Expand Down

0 comments on commit b1dca56

Please sign in to comment.