Add custom branding functionality (#2667)
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
5b96ad59c5
Коммит
49ab8b2161
@@ -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);
|
||||
export default injectIntl(TeamSettings);
|
||||
|
||||
Ссылка в новой задаче
Block a user