summaryrefslogtreecommitdiffstats
path: root/web/react/components/admin_console
diff options
context:
space:
mode:
author=Corey Hulen <corey@hulen.com>2016-01-14 09:08:13 -0600
committer=Corey Hulen <corey@hulen.com>2016-01-14 09:08:13 -0600
commit6d6cada0970a2b341f36dac9b0fed8262ada1865 (patch)
treefc3728f15deaebd0c870838a63735659a33456e7 /web/react/components/admin_console
parent0b986ed3147c885af6b2f33e1ff3eb6754e8f274 (diff)
parenta341dbad2b8a4564b6f270c79f2f9932e499ac80 (diff)
downloadchat-6d6cada0970a2b341f36dac9b0fed8262ada1865.tar.gz
chat-6d6cada0970a2b341f36dac9b0fed8262ada1865.tar.bz2
chat-6d6cada0970a2b341f36dac9b0fed8262ada1865.zip
Merge branch 'master' into PLT-1429
Diffstat (limited to 'web/react/components/admin_console')
-rw-r--r--web/react/components/admin_console/admin_controller.jsx3
-rw-r--r--web/react/components/admin_console/admin_sidebar.jsx32
-rw-r--r--web/react/components/admin_console/email_settings.jsx6
-rw-r--r--web/react/components/admin_console/ldap_settings.jsx32
-rw-r--r--web/react/components/admin_console/license_settings.jsx237
-rw-r--r--web/react/components/admin_console/team_analytics.jsx44
6 files changed, 345 insertions, 9 deletions
diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx
index 32b2e9bb7..0f85c238d 100644
--- a/web/react/components/admin_console/admin_controller.jsx
+++ b/web/react/components/admin_console/admin_controller.jsx
@@ -22,6 +22,7 @@ import LegalAndSupportSettingsTab from './legal_and_support_settings.jsx';
import TeamUsersTab from './team_users.jsx';
import TeamAnalyticsTab from './team_analytics.jsx';
import LdapSettingsTab from './ldap_settings.jsx';
+import LicenseSettingsTab from './license_settings.jsx';
export default class AdminController extends React.Component {
constructor(props) {
@@ -154,6 +155,8 @@ export default class AdminController extends React.Component {
tab = <LegalAndSupportSettingsTab config={this.state.config} />;
} else if (this.state.selected === 'ldap_settings') {
tab = <LdapSettingsTab config={this.state.config} />;
+ } else if (this.state.selected === 'license') {
+ tab = <LicenseSettingsTab />;
} else if (this.state.selected === 'team_users') {
if (this.state.teams) {
tab = <TeamUsersTab team={this.state.teams[this.state.selectedTeam]} />;
diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx
index 1279f4d22..5a5eaa055 100644
--- a/web/react/components/admin_console/admin_sidebar.jsx
+++ b/web/react/components/admin_console/admin_sidebar.jsx
@@ -155,6 +155,36 @@ export default class AdminSidebar extends React.Component {
}
}
+ let ldapSettings;
+ let licenseSettings;
+ if (global.window.mm_config.BuildEnterpriseReady === 'true') {
+ if (global.window.mm_license.IsLicensed === 'true') {
+ ldapSettings = (
+ <li>
+ <a
+ href='#'
+ className={this.isSelected('ldap_settings')}
+ onClick={this.handleClick.bind(this, 'ldap_settings', null)}
+ >
+ {'LDAP Settings'}
+ </a>
+ </li>
+ );
+ }
+
+ licenseSettings = (
+ <li>
+ <a
+ href='#'
+ className={this.isSelected('license')}
+ onClick={this.handleClick.bind(this, 'license', null)}
+ >
+ {'Edition and License'}
+ </a>
+ </li>
+ );
+ }
+
return (
<div className='sidebar--left sidebar--collapsable'>
<div>
@@ -252,6 +282,7 @@ export default class AdminSidebar extends React.Component {
{'GitLab Settings'}
</a>
</li>
+ {ldapSettings}
<li>
<a
href='#'
@@ -300,6 +331,7 @@ export default class AdminSidebar extends React.Component {
</li>
</ul>
<ul className='nav nav__sub-menu padded'>
+ {licenseSettings}
<li>
<a
href='#'
diff --git a/web/react/components/admin_console/email_settings.jsx b/web/react/components/admin_console/email_settings.jsx
index 91d73dccd..c568c5a77 100644
--- a/web/react/components/admin_console/email_settings.jsx
+++ b/web/react/components/admin_console/email_settings.jsx
@@ -254,7 +254,7 @@ export default class EmailSettings extends React.Component {
/>
{'false'}
</label>
- <p className='help-text'>{'Typically set to true in production. When true, Mattermost attempts to send email notifications. Developers may set this field to false to skip email setup for faster development.'}</p>
+ <p className='help-text'>{'Typically set to true in production. When true, Mattermost attempts to send email notifications. Developers may set this field to false to skip email setup for faster development.\nSetting this to true removes the Preview Mode banner (requires logging out and logging back in after setting is changed).'}</p>
</div>
</div>
@@ -581,12 +581,12 @@ export default class EmailSettings extends React.Component {
className='form-control'
id='PushNotificationServer'
ref='PushNotificationServer'
- placeholder='E.g.: "https://push.mattermost.com"'
+ placeholder='E.g.: "https://push-test.mattermost.com"'
defaultValue={this.props.config.EmailSettings.PushNotificationServer}
onChange={this.handleChange}
disabled={!this.state.sendPushNotifications}
/>
- <p className='help-text'>{'Location of Mattermost push notification service you can set up behind your firewall using https://github.com/mattermost/push-proxy. For testing you can use https://push.mattermost.com, which connects to the sample Mattermost iOS app in the public Apple AppStore. Please do not use test service for production deployments.'}</p>
+ <p className='help-text'>{'Location of Mattermost push notification service you can set up behind your firewall using https://github.com/mattermost/push-proxy. For testing you can use https://push-test.mattermost.com, which connects to the sample Mattermost iOS app in the public Apple AppStore. Please do not use test service for production deployments.'}</p>
</div>
</div>
diff --git a/web/react/components/admin_console/ldap_settings.jsx b/web/react/components/admin_console/ldap_settings.jsx
index 6e3da2f72..1447f3bd7 100644
--- a/web/react/components/admin_console/ldap_settings.jsx
+++ b/web/react/components/admin_console/ldap_settings.jsx
@@ -90,14 +90,41 @@ export default class LdapSettings extends React.Component {
saveClass = 'btn btn-primary';
}
- return (
- <div className='wrapper--fixed'>
+ const licenseEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.LDAP === 'true';
+
+ let bannerContent;
+ if (licenseEnabled) {
+ bannerContent = (
<div className='banner'>
<div className='banner__content'>
<h4 className='banner__heading'>{'Note:'}</h4>
<p>{'If a user attribute changes on the LDAP server it will be updated the next time the user enters their credentials to log in to Mattermost. This includes if a user is made inactive or removed from an LDAP server. Synchronization with LDAP servers is planned in a future release.'}</p>
</div>
</div>
+ );
+ } else {
+ bannerContent = (
+ <div className='banner warning'>
+ <div className='banner__content'>
+ <h4 className='banner__heading'>{'Note:'}</h4>
+ <p>
+ {'LDAP is an enterprise feature. Your current license does not support LDAP. Click '}
+ <a
+ href='http://mattermost.com'
+ target='_blank'
+ >
+ {'here'}
+ </a>
+ {' for information and pricing on enterprise licenses.'}
+ </p>
+ </div>
+ </div>
+ );
+ }
+
+ return (
+ <div className='wrapper--fixed'>
+ {bannerContent}
<h3>{'LDAP Settings'}</h3>
<form
className='form-horizontal'
@@ -119,6 +146,7 @@ export default class LdapSettings extends React.Component {
ref='Enable'
defaultChecked={this.props.config.LdapSettings.Enable}
onChange={this.handleEnable}
+ disabled={!licenseEnabled}
/>
{'true'}
</label>
diff --git a/web/react/components/admin_console/license_settings.jsx b/web/react/components/admin_console/license_settings.jsx
new file mode 100644
index 000000000..ba953f3bd
--- /dev/null
+++ b/web/react/components/admin_console/license_settings.jsx
@@ -0,0 +1,237 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import * as Utils from '../../utils/utils.jsx';
+import * as Client from '../../utils/client.jsx';
+
+export default class LicenseSettings extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleChange = this.handleChange.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleRemove = this.handleRemove.bind(this);
+
+ this.state = {
+ fileSelected: false,
+ serverError: null
+ };
+ }
+
+ handleChange() {
+ const element = $(ReactDOM.findDOMNode(this.refs.fileInput));
+ if (element.prop('files').length > 0) {
+ this.setState({fileSelected: true});
+ }
+ }
+
+ handleSubmit(e) {
+ e.preventDefault();
+
+ const element = $(ReactDOM.findDOMNode(this.refs.fileInput));
+ if (element.prop('files').length === 0) {
+ return;
+ }
+ const file = element.prop('files')[0];
+
+ $('#upload-button').button('loading');
+
+ const formData = new FormData();
+ formData.append('license', file, file.name);
+
+ Client.uploadLicenseFile(formData,
+ () => {
+ Utils.clearFileInput(element[0]);
+ $('#upload-button').button('reset');
+ this.setState({serverError: null});
+ window.location.reload(true);
+ },
+ (error) => {
+ Utils.clearFileInput(element[0]);
+ $('#upload-button').button('reset');
+ this.setState({serverError: error.message});
+ }
+ );
+ }
+
+ handleRemove(e) {
+ e.preventDefault();
+
+ $('#remove-button').button('loading');
+
+ Client.removeLicenseFile(
+ () => {
+ $('#remove-button').button('reset');
+ this.setState({serverError: null});
+ window.location.reload(true);
+ },
+ (error) => {
+ $('#remove-button').button('reset');
+ this.setState({serverError: error.message});
+ }
+ );
+ }
+
+ render() {
+ var serverError = '';
+ if (this.state.serverError) {
+ serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
+ }
+
+ var btnClass = 'btn';
+ if (this.state.fileSelected) {
+ btnClass = 'btn btn-primary';
+ }
+
+ let edition;
+ let licenseType;
+ let licenseKey;
+
+ if (global.window.mm_license.IsLicensed === 'true') {
+ edition = 'Mattermost Enterprise Edition. Designed for enterprise-scale communication.';
+ licenseType = (
+ <div>
+ <p>
+ {'This compiled release of Mattermost platform is provided under a '}
+ <a
+ href='http://mattermost.com'
+ target='_blank'
+ >
+ {'commercial license'}
+ </a>
+ {' from Mattermost, Inc. based on your subscription level and is subject to the '}
+ <a
+ href={global.window.mm_config.TermsOfServiceLink}
+ target='_blank'
+ >
+ {'Terms of Service.'}
+ </a>
+ </p>
+ <p>{'Your subscription details are as follows:'}</p>
+ {'Name: ' + global.window.mm_license.Name}
+ <br/>
+ {'Company or organization name: ' + global.window.mm_license.Company}
+ <br/>
+ {'Number of users: ' + global.window.mm_license.Users}
+ <br/>
+ {`License issued: ${Utils.displayDate(parseInt(global.window.mm_license.IssuedAt, 10))} ${Utils.displayTime(parseInt(global.window.mm_license.IssuedAt, 10), true)}`}
+ <br/>
+ {'Start date of license: ' + Utils.displayDate(parseInt(global.window.mm_license.StartsAt, 10))}
+ <br/>
+ {'Expiry date of license: ' + Utils.displayDate(parseInt(global.window.mm_license.ExpiresAt, 10))}
+ <br/>
+ {'LDAP: ' + global.window.mm_license.LDAP}
+ <br/>
+ </div>
+ );
+
+ licenseKey = (
+ <div className='col-sm-8'>
+ <button
+ className='btn btn-danger'
+ onClick={this.handleRemove}
+ id='remove-button'
+ data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Removing License...'}
+ >
+ {'Remove Enterprise License and Downgrade Server'}
+ </button>
+ <br/>
+ <br/>
+ <p className='help-text'>
+ {'If you’re migrating servers you may need to remove your license key from this server in order to install it on a new server. To start, '}
+ <a
+ href='http://mattermost.com'
+ target='_blank'
+ >
+ {'disable all Enterprise Edition features on this server'}
+ </a>
+ {'. This will enable the ability to remove the license key and downgrade this server from Enterprise Edition to Team Edition.'}
+ </p>
+ </div>
+ );
+ } else {
+ edition = 'Mattermost Team Edition. Designed for teams from 5 to 50 users.';
+
+ licenseType = (
+ <span>
+ <p>{'This compiled release of Mattermost platform is offered under an MIT license.'}</p>
+ <p>{'See MIT-COMPILED-LICENSE.txt in your root install directory for details. See NOTICES.txt for information about open source software used in this system.'}</p>
+ </span>
+ );
+
+ licenseKey = (
+ <div className='col-sm-8'>
+ <input
+ className='pull-left'
+ ref='fileInput'
+ type='file'
+ accept='.mattermost-license'
+ onChange={this.handleChange}
+ />
+ <button
+ className={btnClass + ' pull-left'}
+ disabled={!this.state.fileSelected}
+ onClick={this.handleSubmit}
+ id='upload-button'
+ data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Uploading License...'}
+ >
+ {'Upload'}
+ </button>
+ <br/>
+ <br/>
+ <br/>
+ {serverError}
+ <p className='help-text'>
+ {'Upload a license key for Mattermost Enterprise Edition to upgrade this server. '}
+ <a
+ href='http://mattermost.com'
+ target='_blank'
+ >
+ {'Visit us online'}
+ </a>
+ {' to learn more about the benefits of Enterprise Edition or to purchase a key.'}
+ </p>
+ </div>
+ );
+ }
+
+ return (
+ <div className='wrapper--fixed'>
+ <h3>{'Edition and License'}</h3>
+ <form
+ className='form-horizontal'
+ role='form'
+ >
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ >
+ {'Edition: '}
+ </label>
+ <div className='col-sm-8'>
+ {edition}
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ >
+ {'License: '}
+ </label>
+ <div className='col-sm-8'>
+ {licenseType}
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ >
+ {'License Key: '}
+ </label>
+ {licenseKey}
+ </div>
+ </form>
+ </div>
+ );
+ }
+}
diff --git a/web/react/components/admin_console/team_analytics.jsx b/web/react/components/admin_console/team_analytics.jsx
index e28699d3c..fe7230946 100644
--- a/web/react/components/admin_console/team_analytics.jsx
+++ b/web/react/components/admin_console/team_analytics.jsx
@@ -3,8 +3,12 @@
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
+import Constants from '../../utils/constants.jsx';
import LineChart from './line_chart.jsx';
+var Tooltip = ReactBootstrap.Tooltip;
+var OverlayTrigger = ReactBootstrap.OverlayTrigger;
+
export default class TeamAnalytics extends React.Component {
constructor(props) {
super(props);
@@ -314,9 +318,25 @@ export default class TeamAnalytics extends React.Component {
<tbody>
{
this.state.recent_active_users.map((user) => {
+ const tooltip = (
+ <Tooltip id={'recent-user-email-tooltip-' + user.id}>
+ {user.email}
+ </Tooltip>
+ );
+
return (
- <tr key={user.id}>
- <td>{user.email}</td>
+ <tr key={'recent-user-table-entry-' + user.id}>
+ <td>
+ <OverlayTrigger
+ delayShow={Constants.OVERLAY_TIME_DELAY}
+ placement='top'
+ overlay={tooltip}
+ >
+ <time>
+ {user.username}
+ </time>
+ </OverlayTrigger>
+ </td>
<td>{Utils.displayDateTime(user.last_activity_at)}</td>
</tr>
);
@@ -347,9 +367,25 @@ export default class TeamAnalytics extends React.Component {
<tbody>
{
this.state.newly_created_users.map((user) => {
+ const tooltip = (
+ <Tooltip id={'new-user-email-tooltip-' + user.id}>
+ {user.email}
+ </Tooltip>
+ );
+
return (
- <tr key={user.id}>
- <td>{user.email}</td>
+ <tr key={'new-user-table-entry-' + user.id}>
+ <td>
+ <OverlayTrigger
+ delayShow={Constants.OVERLAY_TIME_DELAY}
+ placement='top'
+ overlay={tooltip}
+ >
+ <time>
+ {user.username}
+ </time>
+ </OverlayTrigger>
+ </td>
<td>{Utils.displayDateTime(user.create_at)}</td>
</tr>
);