summaryrefslogtreecommitdiffstats
path: root/webapp/components
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components')
-rw-r--r--webapp/components/admin_console/team_settings.jsx297
-rw-r--r--webapp/components/login/login.jsx66
2 files changed, 331 insertions, 32 deletions
diff --git a/webapp/components/admin_console/team_settings.jsx b/webapp/components/admin_console/team_settings.jsx
index 654f0085d..d361c989f 100644
--- a/webapp/components/admin_console/team_settings.jsx
+++ b/webapp/components/admin_console/team_settings.jsx
@@ -2,11 +2,11 @@
// See License.txt for license information.
import $ from 'jquery';
-import ReactDOM from 'react-dom';
import * as Client from 'utils/client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
+import * as Utils from 'utils/utils.jsx';
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
+import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
const holders = defineMessages({
siteNameExample: {
@@ -29,42 +29,91 @@ const holders = defineMessages({
import React from 'react';
+const ENABLE_BRAND_ACTION = 'enable_brand_action';
+const DISABLE_BRAND_ACTION = 'disable_brand_action';
+
class TeamSettings extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleImageChange = this.handleImageChange.bind(this);
+ this.handleImageSubmit = this.handleImageSubmit.bind(this);
+
+ this.uploading = false;
this.state = {
saveNeeded: false,
+ brandImageExists: false,
+ enableCustomBrand: this.props.config.TeamSettings.EnableCustomBrand,
serverError: null
};
}
- handleChange() {
- var s = {saveNeeded: true, serverError: this.state.serverError};
+ componentWillMount() {
+ if (global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.CustomBrand === 'true') {
+ $.get('/api/v1/admin/get_brand_image').done(() => this.setState({brandImageExists: true}));
+ }
+ }
+
+ componentDidUpdate() {
+ if (this.refs.image) {
+ const reader = new FileReader();
+
+ const img = this.refs.image;
+ reader.onload = (e) => {
+ $(img).attr('src', e.target.result);
+ };
+
+ reader.readAsDataURL(this.state.brandImage);
+ }
+ }
+
+ handleChange(action) {
+ var s = {saveNeeded: true};
+
+ if (action === ENABLE_BRAND_ACTION) {
+ s.enableCustomBrand = true;
+ }
+
+ if (action === DISABLE_BRAND_ACTION) {
+ s.enableCustomBrand = false;
+ }
+
this.setState(s);
}
+ handleImageChange() {
+ const element = $(this.refs.fileInput);
+ if (element.prop('files').length > 0) {
+ this.setState({fileSelected: true, brandImage: element.prop('files')[0]});
+ }
+ }
+
handleSubmit(e) {
e.preventDefault();
$('#save-button').button('loading');
var config = this.props.config;
- config.TeamSettings.SiteName = ReactDOM.findDOMNode(this.refs.SiteName).value.trim();
- config.TeamSettings.RestrictCreationToDomains = ReactDOM.findDOMNode(this.refs.RestrictCreationToDomains).value.trim();
- config.TeamSettings.EnableTeamCreation = ReactDOM.findDOMNode(this.refs.EnableTeamCreation).checked;
- config.TeamSettings.EnableUserCreation = ReactDOM.findDOMNode(this.refs.EnableUserCreation).checked;
- config.TeamSettings.RestrictTeamNames = ReactDOM.findDOMNode(this.refs.RestrictTeamNames).checked;
- config.TeamSettings.EnableTeamListing = ReactDOM.findDOMNode(this.refs.EnableTeamListing).checked;
+ config.TeamSettings.SiteName = this.refs.SiteName.value.trim();
+ config.TeamSettings.RestrictCreationToDomains = this.refs.RestrictCreationToDomains.value.trim();
+ config.TeamSettings.EnableTeamCreation = this.refs.EnableTeamCreation.checked;
+ config.TeamSettings.EnableUserCreation = this.refs.EnableUserCreation.checked;
+ config.TeamSettings.RestrictTeamNames = this.refs.RestrictTeamNames.checked;
+ config.TeamSettings.EnableTeamListing = this.refs.EnableTeamListing.checked;
+ config.TeamSettings.EnableCustomBrand = this.refs.EnableCustomBrand.checked;
+
+ if (this.refs.CustomBrandText) {
+ config.TeamSettings.CustomBrandText = this.refs.CustomBrandText.value;
+ }
var MaxUsersPerTeam = 50;
- if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10))) {
- MaxUsersPerTeam = parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10);
+ if (!isNaN(parseInt(this.refs.MaxUsersPerTeam.value, 10))) {
+ MaxUsersPerTeam = parseInt(this.refs.MaxUsersPerTeam.value, 10);
}
config.TeamSettings.MaxUsersPerTeam = MaxUsersPerTeam;
- ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value = MaxUsersPerTeam;
+ this.refs.MaxUsersPerTeam.value = MaxUsersPerTeam;
Client.saveConfig(
config,
@@ -86,6 +135,219 @@ class TeamSettings extends React.Component {
);
}
+ handleImageSubmit(e) {
+ e.preventDefault();
+
+ if (!this.state.brandImage) {
+ return;
+ }
+
+ if (this.uploading) {
+ return;
+ }
+
+ $('#upload-button').button('loading');
+ this.uploading = true;
+
+ Client.uploadBrandImage(this.state.brandImage,
+ () => {
+ $('#upload-button').button('complete');
+ this.setState({brandImageExists: true, brandImage: null});
+ this.uploading = false;
+ },
+ (err) => {
+ $('#upload-button').button('reset');
+ this.uploading = false;
+ this.setState({serverImageError: err.message});
+ }
+ );
+ }
+
+ createBrandSettings() {
+ var btnClass = 'btn';
+ if (this.state.fileSelected) {
+ btnClass = 'btn btn-primary';
+ }
+
+ var serverImageError = '';
+ if (this.state.serverImageError) {
+ serverImageError = <div className='form-group has-error'><label className='control-label'>{this.state.serverImageError}</label></div>;
+ }
+
+ let uploadImage;
+ let uploadText;
+ if (this.state.enableCustomBrand) {
+ let img;
+ if (this.state.brandImage) {
+ img = (
+ <img
+ ref='image'
+ className='brand-img'
+ src=''
+ />
+ );
+ } else if (this.state.brandImageExists) {
+ img = (
+ <img
+ className='brand-img'
+ src='/api/v1/admin/get_brand_image'
+ />
+ );
+ } else {
+ img = (
+ <p>
+ <FormattedMessage
+ id='admin.team.noBrandImage'
+ defaultMessage='No brand image uploaded'
+ />
+ </p>
+ );
+ }
+
+ uploadImage = (
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='CustomBrandImage'
+ >
+ <FormattedMessage
+ id='admin.team.brandImageTitle'
+ defaultMessage='Custom Brand Image:'
+ />
+ </label>
+ <div className='col-sm-8'>
+ {img}
+ </div>
+ <div className='col-sm-4'/>
+ <div className='col-sm-8'>
+ <div className='file__upload'>
+ <button className='btn btn-default'>
+ <FormattedMessage
+ id='admin.team.chooseImage'
+ defaultMessage='Choose New Image'
+ />
+ </button>
+ <input
+ ref='fileInput'
+ type='file'
+ accept='.jpg,.png,.bmp'
+ onChange={this.handleImageChange}
+ />
+ </div>
+ <button
+ className={btnClass}
+ disabled={!this.state.fileSelected}
+ onClick={this.handleImageSubmit}
+ id='upload-button'
+ data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + Utils.localizeMessage('admin.team.uploading', 'Uploading..')}
+ data-complete-text={'<span class=\'glyphicon glyphicon-ok\'></span> ' + Utils.localizeMessage('admin.team.uploaded', 'Uploaded!')}
+ >
+ <FormattedMessage
+ id='admin.team.upload'
+ defaultMessage='Upload'
+ />
+ </button>
+ <br/>
+ {serverImageError}
+ <p className='help-text no-margin'>
+ <FormattedHTMLMessage
+ id='admin.team.uploadDesc'
+ defaultMessage='Customize your user experience by adding a custom image to your login screen. See examples at <a href="http://docs.mattermost.com/administration/config-settings.html#custom-branding" target="_blank">docs.mattermost.com/administration/config-settings.html#custom-branding</a>.'
+ />
+ </p>
+ </div>
+ </div>
+ );
+
+ uploadText = (
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='CustomBrandText'
+ >
+ <FormattedMessage
+ id='admin.team.brandTextTitle'
+ defaultMessage='Custom Brand Text:'
+ />
+ </label>
+ <div className='col-sm-8'>
+ <textarea
+ type='text'
+ rows='5'
+ maxLength='1024'
+ className='form-control admin-textarea'
+ id='CustomBrandText'
+ ref='CustomBrandText'
+ onChange={this.handleChange}
+ >
+ {this.props.config.TeamSettings.CustomBrandText}
+ </textarea>
+ <p className='help-text'>
+ <FormattedMessage
+ id='admin.team.brandTextDescription'
+ defaultMessage='The custom branding Markdown-formatted text you would like to appear below your custom brand image on your login sreen.'
+ />
+ </p>
+ </div>
+ </div>
+ );
+ }
+
+ return (
+ <div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='EnableCustomBrand'
+ >
+ <FormattedMessage
+ id='admin.team.brandTitle'
+ defaultMessage='Enable Custom Branding: '
+ />
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableCustomBrand'
+ value='true'
+ ref='EnableCustomBrand'
+ defaultChecked={this.props.config.TeamSettings.EnableCustomBrand}
+ onChange={this.handleChange.bind(this, ENABLE_BRAND_ACTION)}
+ />
+ <FormattedMessage
+ id='admin.team.true'
+ defaultMessage='true'
+ />
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableCustomBrand'
+ value='false'
+ defaultChecked={!this.props.config.TeamSettings.EnableCustomBrand}
+ onChange={this.handleChange.bind(this, DISABLE_BRAND_ACTION)}
+ />
+ <FormattedMessage
+ id='admin.team.false'
+ defaultMessage='false'
+ />
+ </label>
+ <p className='help-text'>
+ <FormattedMessage
+ id='admin.team.brandDesc'
+ defaultMessage='Enable custom branding to show an image of your choice, uploaded below, and some help text, written below, on the login page.'
+ />
+ </p>
+ </div>
+ </div>
+
+ {uploadImage}
+ {uploadText}
+ </div>
+ );
+ }
+
render() {
const {formatMessage} = this.props.intl;
var serverError = '';
@@ -98,6 +360,11 @@ class TeamSettings extends React.Component {
saveClass = 'btn btn-primary';
}
+ let brand;
+ if (global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.CustomBrand === 'true') {
+ brand = this.createBrandSettings();
+ }
+
return (
<div className='wrapper--fixed'>
@@ -387,6 +654,8 @@ class TeamSettings extends React.Component {
</div>
</div>
+ {brand}
+
<div className='form-group'>
<div className='col-sm-12'>
{serverError}
@@ -417,4 +686,4 @@ TeamSettings.propTypes = {
config: React.PropTypes.object
};
-export default injectIntl(TeamSettings); \ No newline at end of file
+export default injectIntl(TeamSettings);
diff --git a/webapp/components/login/login.jsx b/webapp/components/login/login.jsx
index ed7495b13..a3dadbf36 100644
--- a/webapp/components/login/login.jsx
+++ b/webapp/components/login/login.jsx
@@ -9,6 +9,7 @@ import LoginMfa from './components/login_mfa.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
+import * as TextFormatting from 'utils/text_formatting.jsx';
import * as Client from 'utils/client.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -134,6 +135,24 @@ export default class Login extends React.Component {
);
}
}
+ createCustomLogin() {
+ if (global.window.mm_license.IsLicensed === 'true' &&
+ global.window.mm_license.CustomBrand === 'true' &&
+ global.window.mm_config.EnableCustomBrand === 'true') {
+ const text = global.window.mm_config.CustomBrandText || '';
+
+ return (
+ <div>
+ <img
+ src='/api/v1/admin/get_brand_image'
+ />
+ <p dangerouslySetInnerHTML={{__html: TextFormatting.formatText(text)}}/>
+ </div>
+ );
+ }
+
+ return null;
+ }
createLoginOptions(currentTeam) {
const extraParam = Utils.getUrlParameter('extra');
let extraBox = '';
@@ -364,6 +383,8 @@ export default class Login extends React.Component {
}
let content;
+ let customContent;
+ let customClass;
if (this.state.showMfa) {
content = (
<LoginMfa
@@ -375,6 +396,10 @@ export default class Login extends React.Component {
);
} else {
content = this.createLoginOptions(currentTeam);
+ customContent = this.createCustomLogin();
+ if (customContent) {
+ customClass = 'branded';
+ }
}
return (
@@ -388,24 +413,29 @@ export default class Login extends React.Component {
</Link>
</div>
<div className='col-sm-12'>
- <div className='signup-team__container'>
- <h5 className='margin--less'>
- <FormattedMessage
- id='login.signTo'
- defaultMessage='Sign in to:'
- />
- </h5>
- <h2 className='signup-team__name'>{currentTeam.display_name}</h2>
- <h2 className='signup-team__subdomain'>
- <FormattedMessage
- id='login.on'
- defaultMessage='on {siteName}'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h2>
- {content}
+ <div className={'signup-team__container ' + customClass}>
+ <div className='signup__markdown'>
+ {customContent}
+ </div>
+ <div className='signup__content'>
+ <h5 className='margin--less'>
+ <FormattedMessage
+ id='login.signTo'
+ defaultMessage='Sign in to:'
+ />
+ </h5>
+ <h2 className='signup-team__name'>{currentTeam.display_name}</h2>
+ <h2 className='signup-team__subdomain'>
+ <FormattedMessage
+ id='login.on'
+ defaultMessage='on {siteName}'
+ values={{
+ siteName: global.window.mm_config.SiteName
+ }}
+ />
+ </h2>
+ {content}
+ </div>
</div>
</div>
</div>