summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2017-06-14 08:56:56 -0400
committerHarrison Healey <harrisonmhealey@gmail.com>2017-06-14 08:56:56 -0400
commit1138dd67705829a6af0d6c610cf3dbe09082187c (patch)
tree23bdc3db76221bead172be1c51eb52a4987636f1
parent40efd8367a85e3333e9b7cc45c390259d412088c (diff)
downloadchat-1138dd67705829a6af0d6c610cf3dbe09082187c.tar.gz
chat-1138dd67705829a6af0d6c610cf3dbe09082187c.tar.bz2
chat-1138dd67705829a6af0d6c610cf3dbe09082187c.zip
PLT-6657 Move system console to use v4 endpoints and redux (#6572)
* Move system console to use v4 endpoints and redux * Rename logs dir to get past gitignore * Fix test email * Update brand unit test * Updates per feedback
-rw-r--r--api4/brand.go1
-rw-r--r--api4/brand_test.go24
-rw-r--r--api4/compliance.go2
-rw-r--r--api4/system.go6
-rw-r--r--api4/user.go2
-rw-r--r--webapp/actions/admin_actions.jsx302
-rw-r--r--webapp/components/admin_console/admin_console.jsx49
-rw-r--r--webapp/components/admin_console/admin_settings.jsx21
-rw-r--r--webapp/components/admin_console/admin_sidebar_header.jsx8
-rw-r--r--webapp/components/admin_console/audits/audits.jsx (renamed from webapp/components/admin_console/audits.jsx)66
-rw-r--r--webapp/components/admin_console/audits/index.js27
-rw-r--r--webapp/components/admin_console/brand_image_setting.jsx38
-rw-r--r--webapp/components/admin_console/cluster_settings.jsx7
-rw-r--r--webapp/components/admin_console/compliance_reports/compliance_reports.jsx (renamed from webapp/components/admin_console/compliance_reports.jsx)158
-rw-r--r--webapp/components/admin_console/compliance_reports/index.js42
-rw-r--r--webapp/components/admin_console/index.js27
-rw-r--r--webapp/components/admin_console/saml_settings.jsx70
-rw-r--r--webapp/components/admin_console/server_logs/index.js27
-rw-r--r--webapp/components/admin_console/server_logs/logs.jsx (renamed from webapp/components/admin_console/logs.jsx)69
-rw-r--r--webapp/components/admin_console/system_users/index.js3
-rw-r--r--webapp/components/admin_console/system_users/system_users.jsx37
-rw-r--r--webapp/components/analytics/team_analytics/index.js10
-rw-r--r--webapp/components/analytics/team_analytics/team_analytics.jsx89
-rw-r--r--webapp/components/user_list_row.jsx4
-rw-r--r--webapp/package.json2
-rw-r--r--webapp/routes/route_admin_console.jsx4
-rw-r--r--webapp/routes/route_root.jsx2
-rw-r--r--webapp/stores/admin_store.jsx177
-rw-r--r--webapp/yarn.lock4
29 files changed, 615 insertions, 663 deletions
diff --git a/api4/brand.go b/api4/brand.go
index ac69f623b..de81b8a3b 100644
--- a/api4/brand.go
+++ b/api4/brand.go
@@ -23,6 +23,7 @@ func getBrandImage(c *Context, w http.ResponseWriter, r *http.Request) {
// No permission check required
if img, err := app.GetBrandImage(); err != nil {
+ w.WriteHeader(http.StatusNotFound)
w.Write(nil)
} else {
w.Header().Set("Content-Type", "image/png")
diff --git a/api4/brand_test.go b/api4/brand_test.go
index 98a539574..6ecb41a83 100644
--- a/api4/brand_test.go
+++ b/api4/brand_test.go
@@ -13,27 +13,15 @@ func TestGetBrandImage(t *testing.T) {
defer TearDown()
Client := th.Client
- data, resp := Client.GetBrandImage()
- CheckNoError(t, resp)
-
- if len(data) != 0 {
- t.Fatal("no image uploaded - should be empty")
- }
+ _, resp := Client.GetBrandImage()
+ CheckNotFoundStatus(t, resp)
Client.Logout()
- data, resp = Client.GetBrandImage()
- CheckNoError(t, resp)
-
- if len(data) != 0 {
- t.Fatal("no image uploaded - should be empty")
- }
-
- data, resp = th.SystemAdminClient.GetBrandImage()
- CheckNoError(t, resp)
+ _, resp = Client.GetBrandImage()
+ CheckNotFoundStatus(t, resp)
- if len(data) != 0 {
- t.Fatal("no image uploaded - should be empty")
- }
+ _, resp = th.SystemAdminClient.GetBrandImage()
+ CheckNotFoundStatus(t, resp)
}
func TestUploadBrandImage(t *testing.T) {
diff --git a/api4/compliance.go b/api4/compliance.go
index cabac6e21..733abeecf 100644
--- a/api4/compliance.go
+++ b/api4/compliance.go
@@ -20,7 +20,7 @@ func InitCompliance() {
BaseRoutes.Compliance.Handle("/reports", ApiSessionRequired(createComplianceReport)).Methods("POST")
BaseRoutes.Compliance.Handle("/reports", ApiSessionRequired(getComplianceReports)).Methods("GET")
BaseRoutes.Compliance.Handle("/reports/{report_id:[A-Za-z0-9]+}", ApiSessionRequired(getComplianceReport)).Methods("GET")
- BaseRoutes.Compliance.Handle("/reports/{report_id:[A-Za-z0-9]+}/download", ApiSessionRequired(downloadComplianceReport)).Methods("GET")
+ BaseRoutes.Compliance.Handle("/reports/{report_id:[A-Za-z0-9]+}/download", ApiSessionRequiredTrustRequester(downloadComplianceReport)).Methods("GET")
}
func createComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) {
diff --git a/api4/system.go b/api4/system.go
index 3a077283c..465f4e71d 100644
--- a/api4/system.go
+++ b/api4/system.go
@@ -52,13 +52,17 @@ func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
}
func testEmail(c *Context, w http.ResponseWriter, r *http.Request) {
+ cfg := model.ConfigFromJson(r.Body)
+ if cfg == nil {
+ cfg = utils.Cfg
+ }
if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
return
}
- err := app.TestEmail(c.Session.UserId, utils.Cfg)
+ err := app.TestEmail(c.Session.UserId, cfg)
if err != nil {
c.Err = err
return
diff --git a/api4/user.go b/api4/user.go
index d06dd2882..69bea945a 100644
--- a/api4/user.go
+++ b/api4/user.go
@@ -27,7 +27,7 @@ func InitUser() {
BaseRoutes.Users.Handle("/autocomplete", ApiSessionRequired(autocompleteUsers)).Methods("GET")
BaseRoutes.User.Handle("", ApiSessionRequired(getUser)).Methods("GET")
- BaseRoutes.User.Handle("/image", ApiSessionRequired(getProfileImage)).Methods("GET")
+ BaseRoutes.User.Handle("/image", ApiSessionRequiredTrustRequester(getProfileImage)).Methods("GET")
BaseRoutes.User.Handle("/image", ApiSessionRequired(setProfileImage)).Methods("POST")
BaseRoutes.User.Handle("", ApiSessionRequired(updateUser)).Methods("PUT")
BaseRoutes.User.Handle("/patch", ApiSessionRequired(patchUser)).Methods("PUT")
diff --git a/webapp/actions/admin_actions.jsx b/webapp/actions/admin_actions.jsx
index 9a522caf9..04d0d65bb 100644
--- a/webapp/actions/admin_actions.jsx
+++ b/webapp/actions/admin_actions.jsx
@@ -2,201 +2,154 @@
// See License.txt for license information.
import Client from 'client/web_client.jsx';
-import * as AsyncClient from 'utils/async_client.jsx';
import {browserHistory} from 'react-router/es6';
-// Redux actions
import store from 'stores/redux_store.jsx';
const dispatch = store.dispatch;
const getState = store.getState;
-import {getUser} from 'mattermost-redux/actions/users';
+import {updateUserMfa, updateUserPassword} from 'mattermost-redux/actions/users';
+import * as AdminActions from 'mattermost-redux/actions/admin';
export function saveConfig(config, success, error) {
- Client.saveConfig(
- config,
- () => {
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ AdminActions.updateConfig(config)(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.updateConfig.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function reloadConfig(success, error) {
- Client.reloadConfig(
- () => {
- AsyncClient.getConfig();
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ AdminActions.reloadConfig()(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ AdminActions.getConfig()(dispatch, getState);
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.reloadConfig.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function adminResetMfa(userId, success, error) {
- Client.adminResetMfa(
- userId,
- () => {
- getUser(userId)(dispatch, getState);
-
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ updateUserMfa(userId, false)(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.users.updateUser.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function getClusterStatus(success, error) {
- Client.getClusterStatus(
+ AdminActions.getClusterStatus()(dispatch, getState).then(
(data) => {
- if (success) {
+ if (data && success) {
success(data);
- }
- },
- (err) => {
- AsyncClient.dispatchError(err, 'getClusterStatus');
- if (error) {
- error(err);
- }
- }
- );
-}
-
-export function saveComplianceReports(job, success, error) {
- Client.saveComplianceReports(
- job,
- () => {
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.getClusterStatus.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function testEmail(config, success, error) {
- Client.testEmail(
- config,
- () => {
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ AdminActions.testEmail(config)(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.testEmail.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function ldapTest(success, error) {
- Client.ldapTest(
- () => {
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ AdminActions.testLdap()(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.testLdap.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function invalidateAllCaches(success, error) {
- Client.invalidateAllCaches(
- () => {
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ AdminActions.invalidateCaches()(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.invalidateCaches.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function recycleDatabaseConnection(success, error) {
- Client.recycleDatabaseConnection(
- () => {
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ AdminActions.recycleDatabase()(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.recycleDatabase.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function adminResetPassword(userId, password, success, error) {
- Client.adminResetPassword(
- userId,
- password,
- () => {
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ updateUserPassword(userId, '', password)(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.users.updateUser.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function samlCertificateStatus(success, error) {
- Client.samlCertificateStatus(
+ AdminActions.getSamlCertificateStatus()(dispatch, getState).then(
(data) => {
- if (success) {
+ if (data && success) {
success(data);
- }
- },
- (err) => {
- if (error) {
- error(err);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.getSamlCertificateStatus.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
export function ldapSyncNow(success, error) {
- Client.ldapSyncNow(
- () => {
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ AdminActions.syncLdap()(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.syncLdap.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
@@ -316,16 +269,13 @@ export function regenerateOAuthAppSecret(oauthAppId, success, error) {
}
export function uploadBrandImage(brandImage, success, error) {
- Client.uploadBrandImage(
- brandImage,
- () => {
- if (success) {
- success();
- }
- },
- (err) => {
- if (error) {
- error(err);
+ AdminActions.uploadBrandImage(brandImage)(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.uploadBrandImage.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
@@ -362,33 +312,79 @@ export function removeLicenseFile(success, error) {
);
}
-export function uploadCertificateFile(certificateFile, success, error) {
- Client.uploadCertificateFile(
- certificateFile,
- () => {
- if (success) {
- success();
+export function uploadPublicSamlCertificate(file, success, error) {
+ AdminActions.uploadPublicSamlCertificate(file)(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.uploadPublicSamlCertificate.error;
+ error({id: serverError.server_error_id, ...serverError});
}
- },
- (err) => {
- if (error) {
- error(err);
+ }
+ );
+}
+
+export function uploadPrivateSamlCertificate(file, success, error) {
+ AdminActions.uploadPrivateSamlCertificate(file)(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.uploadPrivateSamlCertificate.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
}
-export function removeCertificateFile(certificateId, success, error) {
- Client.removeCertificateFile(
- certificateId,
- () => {
- if (success) {
- success();
+export function uploadIdpSamlCertificate(file, success, error) {
+ AdminActions.uploadIdpSamlCertificate(file)(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.uploadIdpSamlCertificate.error;
+ error({id: serverError.server_error_id, ...serverError});
}
- },
- (err) => {
- if (error) {
- error(err);
+ }
+ );
+}
+
+export function removePublicSamlCertificate(success, error) {
+ AdminActions.removePublicSamlCertificate()(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.removePublicSamlCertificate.error;
+ error({id: serverError.server_error_id, ...serverError});
+ }
+ }
+ );
+}
+
+export function removePrivateSamlCertificate(success, error) {
+ AdminActions.removePrivateSamlCertificate()(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.removePrivateSamlCertificate.error;
+ error({id: serverError.server_error_id, ...serverError});
+ }
+ }
+ );
+}
+
+export function removeIdpSamlCertificate(success, error) {
+ AdminActions.removeIdpSamlCertificate()(dispatch, getState).then(
+ (data) => {
+ if (data && success) {
+ success(data);
+ } else if (data == null && error) {
+ const serverError = getState().requests.admin.removeIdpSamlCertificate.error;
+ error({id: serverError.server_error_id, ...serverError});
}
}
);
diff --git a/webapp/components/admin_console/admin_console.jsx b/webapp/components/admin_console/admin_console.jsx
index b8250bab2..99256d7d4 100644
--- a/webapp/components/admin_console/admin_console.jsx
+++ b/webapp/components/admin_console/admin_console.jsx
@@ -6,46 +6,37 @@ import PropTypes from 'prop-types';
import 'bootstrap';
import AnnouncementBar from 'components/announcement_bar';
-import AdminStore from 'stores/admin_store.jsx';
-import * as AsyncClient from 'utils/async_client.jsx';
-
import AdminSidebar from './admin_sidebar.jsx';
export default class AdminConsole extends React.Component {
- static get propTypes() {
- return {
- children: PropTypes.node.isRequired
- };
- }
+ static propTypes = {
- constructor(props) {
- super(props);
+ /*
+ * Children components to render
+ */
+ children: PropTypes.node.isRequired,
- this.handleConfigChange = this.handleConfigChange.bind(this);
-
- this.state = {
- config: AdminStore.getConfig()
- };
- }
+ /*
+ * Object representing the config file
+ */
+ config: PropTypes.object.isRequired,
- componentWillMount() {
- AdminStore.addConfigChangeListener(this.handleConfigChange);
- AsyncClient.getConfig();
- }
+ actions: PropTypes.shape({
- componentWillUnmount() {
- AdminStore.removeConfigChangeListener(this.handleConfigChange);
+ /*
+ * Function to get the config file
+ */
+ getConfig: PropTypes.func.isRequired
+ }).isRequired
}
- handleConfigChange() {
- this.setState({
- config: AdminStore.getConfig()
- });
+ componentWillMount() {
+ this.props.actions.getConfig();
}
render() {
- const config = this.state.config;
- if (!config) {
+ const config = this.props.config;
+ if (Object.keys(config).length === 0) {
return <div/>;
}
if (config && Object.keys(config).length === 0 && config.constructor === 'Object') {
@@ -59,7 +50,7 @@ export default class AdminConsole extends React.Component {
// not every page in the system console will need the config, but the vast majority will
const children = React.cloneElement(this.props.children, {
- config: this.state.config
+ config
});
return (
<div className='admin-console__wrapper'>
diff --git a/webapp/components/admin_console/admin_settings.jsx b/webapp/components/admin_console/admin_settings.jsx
index 180e6e5b9..2411fbdb8 100644
--- a/webapp/components/admin_console/admin_settings.jsx
+++ b/webapp/components/admin_console/admin_settings.jsx
@@ -1,11 +1,8 @@
-import PropTypes from 'prop-types';
-
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
-
-import * as AsyncClient from 'utils/async_client.jsx';
+import PropTypes from 'prop-types';
import FormError from 'components/form_error.jsx';
import SaveButton from 'components/admin_console/save_button.jsx';
@@ -13,10 +10,12 @@ import SaveButton from 'components/admin_console/save_button.jsx';
import {saveConfig} from 'actions/admin_actions.jsx';
export default class AdminSettings extends React.Component {
- static get propTypes() {
- return {
- config: PropTypes.object
- };
+ static propTypes = {
+
+ /*
+ * Object representing the config file
+ */
+ config: PropTypes.object
}
constructor(props) {
@@ -58,10 +57,8 @@ export default class AdminSettings extends React.Component {
saveConfig(
config,
- () => {
- AsyncClient.getConfig((savedConfig) => {
- this.setState(this.getStateFromConfig(savedConfig));
- });
+ (savedConfig) => {
+ this.setState(this.getStateFromConfig(savedConfig));
this.setState({
saveNeeded: false,
diff --git a/webapp/components/admin_console/admin_sidebar_header.jsx b/webapp/components/admin_console/admin_sidebar_header.jsx
index 87a1170dc..301186917 100644
--- a/webapp/components/admin_console/admin_sidebar_header.jsx
+++ b/webapp/components/admin_console/admin_sidebar_header.jsx
@@ -4,7 +4,7 @@
import $ from 'jquery';
import AdminNavbarDropdown from './admin_navbar_dropdown.jsx';
import UserStore from 'stores/user_store.jsx';
-import Client from 'client/web_client.jsx';
+import {Client4} from 'mattermost-redux/client';
import {FormattedMessage} from 'react-intl';
@@ -14,12 +14,10 @@ export default class SidebarHeader extends React.Component {
constructor(props) {
super(props);
- this.toggleDropdown = this.toggleDropdown.bind(this);
-
this.state = {};
}
- toggleDropdown(e) {
+ toggleDropdown = (e) => {
e.preventDefault();
if (this.refs.dropdown.blockToggle) {
@@ -42,7 +40,7 @@ export default class SidebarHeader extends React.Component {
profilePicture = (
<img
className='user__picture'
- src={Client.getUsersRoute() + '/' + me.id + '/image?time=' + me.last_picture_update}
+ src={Client4.getProfilePictureUrl(me.id, me.last_picture_update)}
/>
);
}
diff --git a/webapp/components/admin_console/audits.jsx b/webapp/components/admin_console/audits/audits.jsx
index 594e55e39..0811c216f 100644
--- a/webapp/components/admin_console/audits.jsx
+++ b/webapp/components/admin_console/audits/audits.jsx
@@ -1,68 +1,66 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import LoadingScreen from '../loading_screen.jsx';
-import AuditTable from '../audit_table.jsx';
-import ComplianceReports from './compliance_reports.jsx';
+import LoadingScreen from 'components/loading_screen.jsx';
+import AuditTable from 'components/audit_table.jsx';
+import ComplianceReports from 'components/admin_console/compliance_reports';
-import AdminStore from 'stores/admin_store.jsx';
+import React from 'react';
+import PropTypes from 'prop-types';
+import {FormattedMessage} from 'react-intl';
-import * as AsyncClient from 'utils/async_client.jsx';
+export default class Audits extends React.PureComponent {
+ static propTypes = {
-import {FormattedMessage} from 'react-intl';
+ /*
+ * Array of audits to render
+ */
+ audits: PropTypes.arrayOf(PropTypes.object).isRequired,
-import React from 'react';
+ actions: PropTypes.shape({
+
+ /*
+ * Function to fetch audits
+ */
+ getAudits: PropTypes.func.isRequired
+ }).isRequired
+ }
-export default class Audits extends React.Component {
constructor(props) {
super(props);
- this.onAuditListenerChange = this.onAuditListenerChange.bind(this);
- this.reload = this.reload.bind(this);
-
this.state = {
- audits: AdminStore.getAudits()
+ loadingAudits: true
};
}
componentDidMount() {
- AdminStore.addAuditChangeListener(this.onAuditListenerChange);
- AsyncClient.getServerAudits();
- }
-
- componentWillUnmount() {
- AdminStore.removeAuditChangeListener(this.onAuditListenerChange);
- }
-
- onAuditListenerChange() {
- this.setState({
- audits: AdminStore.getAudits()
- });
+ this.props.actions.getAudits().then(
+ () => this.setState({loadingAudits: false})
+ );
}
- reload() {
- AdminStore.saveAudits(null);
- this.setState({
- audits: null
- });
-
- AsyncClient.getServerAudits();
+ reload = () => {
+ this.setState({loadingAudits: true});
+ this.props.actions.getAudits().then(
+ () => this.setState({loadingAudits: false})
+ );
}
render() {
- var content = null;
+ let content = null;
if (global.window.mm_license.IsLicensed !== 'true') {
return <div/>;
}
- if (this.state.audits === null) {
+ if (this.state.loadingAudits) {
content = <LoadingScreen/>;
} else {
content = (
<div style={{margin: '10px'}}>
<AuditTable
- audits={this.state.audits}
+ audits={this.props.audits}
showUserId={true}
showIp={true}
showSession={true}
diff --git a/webapp/components/admin_console/audits/index.js b/webapp/components/admin_console/audits/index.js
new file mode 100644
index 000000000..a48e33538
--- /dev/null
+++ b/webapp/components/admin_console/audits/index.js
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+import {getAudits} from 'mattermost-redux/actions/admin';
+
+import * as Selectors from 'mattermost-redux/selectors/entities/admin';
+
+import Audits from './audits.jsx';
+
+function mapStateToProps(state, ownProps) {
+ return {
+ ...ownProps,
+ audits: Object.values(Selectors.getAudits(state))
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ getAudits
+ }, dispatch)
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(Audits);
diff --git a/webapp/components/admin_console/brand_image_setting.jsx b/webapp/components/admin_console/brand_image_setting.jsx
index eae5ad922..d2eae3f6e 100644
--- a/webapp/components/admin_console/brand_image_setting.jsx
+++ b/webapp/components/admin_console/brand_image_setting.jsx
@@ -4,20 +4,23 @@
import $ from 'jquery';
import PropTypes from 'prop-types';
import React from 'react';
-import ReactDOM from 'react-dom';
-import Client from 'client/web_client.jsx';
+import {Client4} from 'mattermost-redux/client';
import * as Utils from 'utils/utils.jsx';
import {uploadBrandImage} from 'actions/admin_actions.jsx';
import FormError from 'components/form_error.jsx';
import {FormattedHTMLMessage, FormattedMessage} from 'react-intl';
-export default class BrandImageSetting extends React.Component {
- static get propTypes() {
- return {
- disabled: PropTypes.bool.isRequired
- };
+const HTTP_STATUS_OK = 200;
+
+export default class BrandImageSetting extends React.PureComponent {
+ static propTypes = {
+
+ /*
+ * Set to disable the setting
+ */
+ disabled: PropTypes.bool.isRequired
}
constructor(props) {
@@ -37,9 +40,15 @@ export default class BrandImageSetting extends React.Component {
}
componentWillMount() {
- $.get(Client.getAdminRoute() + '/get_brand_image?t=' + this.state.brandImageTimestamp).done(() => {
- this.setState({brandImageExists: true});
- });
+ fetch(Client4.getBrandImageUrl(this.state.brandImageTimestamp)).then(
+ (resp) => {
+ if (resp.status === HTTP_STATUS_OK) {
+ this.setState({brandImageExists: true});
+ } else {
+ this.setState({brandImageExists: false});
+ }
+ }
+ );
}
componentDidUpdate() {
@@ -76,7 +85,7 @@ export default class BrandImageSetting extends React.Component {
return;
}
- $(ReactDOM.findDOMNode(this.refs.upload)).button('loading');
+ $(this.refs.upload).button('loading');
this.setState({
uploading: true,
@@ -86,7 +95,7 @@ export default class BrandImageSetting extends React.Component {
uploadBrandImage(
this.state.brandImage,
() => {
- $(ReactDOM.findDOMNode(this.refs.upload)).button('complete');
+ $(this.refs.upload).button('complete');
this.setState({
brandImageExists: true,
@@ -96,7 +105,7 @@ export default class BrandImageSetting extends React.Component {
});
},
(err) => {
- $(ReactDOM.findDOMNode(this.refs.upload)).button('reset');
+ $(this.refs.upload).button('reset');
this.setState({
uploading: false,
@@ -130,7 +139,7 @@ export default class BrandImageSetting extends React.Component {
img = (
<img
className='brand-img'
- src={Client.getAdminRoute() + '/get_brand_image?t=' + this.state.brandImageTimestamp}
+ src={Client4.getBrandImageUrl(this.state.brandImageTimestamp)}
/>
);
} else {
@@ -180,6 +189,7 @@ export default class BrandImageSetting extends React.Component {
disabled={this.props.disabled || !this.state.brandImage}
onClick={this.handleImageSubmit}
id='upload-button'
+ ref='upload'
data-loading-text={'<span class=\'fa fa-refresh fa-rotate\'></span> ' + Utils.localizeMessage('admin.team.uploading', 'Uploading..')}
data-complete-text={'<span class=\'fa fa-check\'></span> ' + Utils.localizeMessage('admin.team.uploaded', 'Uploaded!')}
>
diff --git a/webapp/components/admin_console/cluster_settings.jsx b/webapp/components/admin_console/cluster_settings.jsx
index 895a87ce1..14bc46240 100644
--- a/webapp/components/admin_console/cluster_settings.jsx
+++ b/webapp/components/admin_console/cluster_settings.jsx
@@ -11,9 +11,10 @@ import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import SettingsGroup from './settings_group.jsx';
import ClusterTableContainer from './cluster_table_container.jsx';
-import AdminStore from 'stores/admin_store.jsx';
import * as Utils from 'utils/utils.jsx';
+import {Client4} from 'mattermost-redux/client';
+
export default class ClusterSettings extends AdminSettings {
constructor(props) {
super(props);
@@ -74,7 +75,7 @@ export default class ClusterSettings extends AdminSettings {
var configLoadedFromCluster = null;
- if (AdminStore.getClusterId()) {
+ if (Client4.clusterId) {
configLoadedFromCluster = (
<div
style={{marginBottom: '10px'}}
@@ -85,7 +86,7 @@ export default class ClusterSettings extends AdminSettings {
id='admin.cluster.loadedFrom'
defaultMessage='This configuration file was loaded from Node ID {clusterId}. Please see the Troubleshooting Guide in our <a href="http://docs.mattermost.com/deployment/cluster.html" target="_blank">documentation</a> if you are accessing the System Console through a load balancer and experiencing issues.'
values={{
- clusterId: AdminStore.getClusterId()
+ clusterId: Client4.clusterId
}}
/>
</div>
diff --git a/webapp/components/admin_console/compliance_reports.jsx b/webapp/components/admin_console/compliance_reports/compliance_reports.jsx
index 567a6ca04..af361bace 100644
--- a/webapp/components/admin_console/compliance_reports.jsx
+++ b/webapp/components/admin_console/compliance_reports/compliance_reports.jsx
@@ -1,92 +1,96 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import $ from 'jquery';
-import LoadingScreen from '../loading_screen.jsx';
-import * as Utils from '../../utils/utils.jsx';
-import AdminStore from '../../stores/admin_store.jsx';
-import UserStore from '../../stores/user_store.jsx';
+import LoadingScreen from 'components/loading_screen.jsx';
-import Client from 'client/web_client.jsx';
-import * as AsyncClient from '../../utils/async_client.jsx';
-import {saveComplianceReports} from 'actions/admin_actions.jsx';
+import * as Utils from 'utils/utils.jsx';
+import UserStore from 'stores/user_store.jsx';
+import {Client4} from 'mattermost-redux/client';
+import React from 'react';
+import PropTypes from 'prop-types';
import {FormattedMessage, FormattedDate, FormattedTime} from 'react-intl';
-import React from 'react';
-import ReactDOM from 'react-dom';
+export default class ComplianceReports extends React.PureComponent {
+ static propTypes = {
+
+ /*
+ * Set if compliance reports are enabled in the config
+ */
+ enabled: PropTypes.bool.isRequired,
+
+ /*
+ * Array of reports to render
+ */
+ reports: PropTypes.arrayOf(PropTypes.object).isRequired,
+
+ /*
+ * Error message to display
+ */
+ serverError: PropTypes.string,
+
+ actions: PropTypes.shape({
+
+ /*
+ * Function to get compliance reports
+ */
+ getComplianceReports: PropTypes.func.isRequired,
+
+ /*
+ * Function to save compliance reports
+ */
+ createComplianceReport: PropTypes.func.isRequired
+ }).isRequired
+ }
-export default class ComplianceReports extends React.Component {
constructor(props) {
super(props);
- this.onComplianceReportsListenerChange = this.onComplianceReportsListenerChange.bind(this);
- this.reload = this.reload.bind(this);
- this.runReport = this.runReport.bind(this);
- this.getDateTime = this.getDateTime.bind(this);
-
this.state = {
- enabled: AdminStore.getConfig().ComplianceSettings.Enable,
- reports: AdminStore.getComplianceReports(),
- serverError: null
+ loadingReports: true
};
}
componentDidMount() {
- AdminStore.addComplianceReportsChangeListener(this.onComplianceReportsListenerChange);
-
- if (global.window.mm_license.IsLicensed !== 'true' || !this.state.enabled) {
+ if (global.window.mm_license.IsLicensed !== 'true' || !this.props.enabled) {
return;
}
- AsyncClient.getComplianceReports();
+ this.props.actions.getComplianceReports().then(
+ () => this.setState({loadingReports: false})
+ );
}
- componentWillUnmount() {
- AdminStore.removeComplianceReportsChangeListener(this.onComplianceReportsListenerChange);
- }
+ reload = () => {
+ this.setState({loadingReports: true});
- onComplianceReportsListenerChange() {
- this.setState({
- reports: AdminStore.getComplianceReports()
- });
+ this.props.actions.getComplianceReports().then(
+ () => this.setState({loadingReports: false})
+ );
}
- reload() {
- AdminStore.saveComplianceReports(null);
- this.setState({
- reports: null,
- serverError: null
- });
+ runReport = (e) => {
+ e.preventDefault();
+
+ this.setState({runningReport: true});
- AsyncClient.getComplianceReports();
- }
+ const job = {};
+ job.desc = this.refs.desc.value;
+ job.emails = this.refs.emails.value;
+ job.keywords = this.refs.keywords.value;
+ job.start_at = Date.parse(this.refs.from.value);
+ job.end_at = Date.parse(this.refs.to.value);
- runReport(e) {
- e.preventDefault();
- $('#run-button').button('loading');
-
- var job = {};
- job.desc = ReactDOM.findDOMNode(this.refs.desc).value;
- job.emails = ReactDOM.findDOMNode(this.refs.emails).value;
- job.keywords = ReactDOM.findDOMNode(this.refs.keywords).value;
- job.start_at = Date.parse(ReactDOM.findDOMNode(this.refs.from).value);
- job.end_at = Date.parse(ReactDOM.findDOMNode(this.refs.to).value);
-
- saveComplianceReports(
- job,
- () => {
- ReactDOM.findDOMNode(this.refs.emails).value = '';
- ReactDOM.findDOMNode(this.refs.keywords).value = '';
- ReactDOM.findDOMNode(this.refs.desc).value = '';
- ReactDOM.findDOMNode(this.refs.from).value = '';
- ReactDOM.findDOMNode(this.refs.to).value = '';
- this.reload();
- $('#run-button').button('reset');
- },
- (err) => {
- this.setState({serverError: err.message});
- $('#run-button').button('reset');
+ this.props.actions.createComplianceReport(job).then(
+ (data) => {
+ if (data) {
+ this.refs.emails.value = '';
+ this.refs.keywords.value = '';
+ this.refs.desc.value = '';
+ this.refs.from.value = '';
+ this.refs.to.value = '';
+ }
+ this.setState({runningReport: false});
}
);
}
@@ -112,21 +116,20 @@ export default class ComplianceReports extends React.Component {
}
render() {
- var content = null;
-
- if (global.window.mm_license.IsLicensed !== 'true' || !this.state.enabled) {
+ if (global.window.mm_license.IsLicensed !== 'true' || !this.props.enabled) {
return <div/>;
}
- if (this.state.reports === null) {
+ let content = null;
+ if (this.state.loadingReports) {
content = <LoadingScreen/>;
} else {
var list = [];
- for (var i = 0; i < this.state.reports.length; i++) {
- const report = this.state.reports[i];
+ for (var i = 0; i < this.props.reports.length; i++) {
+ const report = this.props.reports[i];
- var params = '';
+ let params = '';
if (report.type === 'adhoc') {
params = (
<span>
@@ -152,10 +155,10 @@ export default class ComplianceReports extends React.Component {
</span>);
}
- var download = '';
+ let download = '';
if (report.status === 'finished') {
download = (
- <a href={Client.getAdminRoute() + '/download_compliance_report/' + report.id}>
+ <a href={`${Client4.getBaseRoute()}/compliance/reports/${report.id}/download`}>
<FormattedMessage
id='admin.compliance_table.download'
defaultMessage='Download'
@@ -164,7 +167,7 @@ export default class ComplianceReports extends React.Component {
);
}
- var status = report.status;
+ let status = report.status;
if (report.status === 'finished') {
status = (
<span style={{color: 'green'}}>{report.status}</span>
@@ -177,8 +180,8 @@ export default class ComplianceReports extends React.Component {
);
}
- var user = report.user_id;
- var profile = UserStore.getProfile(report.user_id);
+ let user = report.user_id;
+ const profile = UserStore.getProfile(report.user_id);
if (profile) {
user = profile.email;
}
@@ -256,13 +259,13 @@ export default class ComplianceReports extends React.Component {
}
let serverError = '';
- if (this.state.serverError) {
+ if (this.props.serverError) {
serverError = (
<div
className='form-group has-error'
style={{marginTop: '10px'}}
>
- <label className='control-label'>{this.state.serverError}</label>
+ <label className='control-label'>{this.props.serverError}</label>
</div>
);
}
@@ -372,6 +375,7 @@ export default class ComplianceReports extends React.Component {
<button
type='submit'
className='btn btn-link'
+ disabled={this.state.runningReport}
onClick={this.reload}
>
<i className='fa fa-refresh'/>
diff --git a/webapp/components/admin_console/compliance_reports/index.js b/webapp/components/admin_console/compliance_reports/index.js
new file mode 100644
index 000000000..8534c1fda
--- /dev/null
+++ b/webapp/components/admin_console/compliance_reports/index.js
@@ -0,0 +1,42 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+import {getComplianceReports, createComplianceReport} from 'mattermost-redux/actions/admin';
+
+import {getComplianceReports as selectComplianceReports, getConfig} from 'mattermost-redux/selectors/entities/admin';
+
+import ComplianceReports from './compliance_reports.jsx';
+
+function mapStateToProps(state, ownProps) {
+ let enabled = false;
+ const config = getConfig(state);
+ if (config && config.ComplianceSettings) {
+ enabled = config.ComplianceSettings.Enable;
+ }
+
+ let serverError;
+ const error = state.requests.admin.createCompliance.error;
+ if (error) {
+ serverError = error.message;
+ }
+
+ return {
+ ...ownProps,
+ enabled,
+ reports: Object.values(selectComplianceReports(state)),
+ serverError
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ getComplianceReports,
+ createComplianceReport
+ }, dispatch)
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(ComplianceReports);
diff --git a/webapp/components/admin_console/index.js b/webapp/components/admin_console/index.js
new file mode 100644
index 000000000..4b333e65c
--- /dev/null
+++ b/webapp/components/admin_console/index.js
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+import {getConfig} from 'mattermost-redux/actions/admin';
+
+import * as Selectors from 'mattermost-redux/selectors/entities/admin';
+
+import AdminConsole from './admin_console.jsx';
+
+function mapStateToProps(state, ownProps) {
+ return {
+ ...ownProps,
+ config: Selectors.getConfig(state)
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ getConfig
+ }, dispatch)
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(AdminConsole);
diff --git a/webapp/components/admin_console/saml_settings.jsx b/webapp/components/admin_console/saml_settings.jsx
index a02ab4a8a..4c0c0c8fd 100644
--- a/webapp/components/admin_console/saml_settings.jsx
+++ b/webapp/components/admin_console/saml_settings.jsx
@@ -14,7 +14,7 @@ import SettingsGroup from './settings_group.jsx';
import * as Utils from 'utils/utils.jsx';
-import {samlCertificateStatus, uploadCertificateFile, removeCertificateFile} from 'actions/admin_actions.jsx';
+import * as AdminActions from 'actions/admin_actions.jsx';
export default class SamlSettings extends AdminSettings {
constructor(props) {
@@ -74,7 +74,7 @@ export default class SamlSettings extends AdminSettings {
}
componentWillMount() {
- samlCertificateStatus(
+ AdminActions.samlCertificateStatus(
(data) => {
const files = {};
if (!data.IdpCertificateFile) {
@@ -94,38 +94,50 @@ export default class SamlSettings extends AdminSettings {
}
uploadCertificate(id, file, callback) {
- uploadCertificateFile(
- file,
- () => {
- const fileName = file.name;
- this.handleChange(id, fileName);
- this.setState({[id]: fileName, [`${id}Error`]: null});
- if (callback && typeof callback === 'function') {
- callback();
- }
- },
- (error) => {
- if (callback && typeof callback === 'function') {
- callback(error.message);
- }
+ const complete = () => {
+ const fileName = file.name;
+ this.handleChange(id, fileName);
+ this.setState({[id]: fileName, [`${id}Error`]: null});
+ if (callback && typeof callback === 'function') {
+ callback();
}
- );
+ };
+
+ function fail(error) {
+ if (callback && typeof callback === 'function') {
+ callback(error.message);
+ }
+ }
+
+ if (id === 'idpCertificateFile') {
+ AdminActions.uploadIdpSamlCertificate(file, complete, fail);
+ } else if (id === 'publicCertificateFile') {
+ AdminActions.uploadPublicSamlCertificate(file, complete, fail);
+ } else if (id === 'privateKeyFile') {
+ AdminActions.uploadPrivateSamlCertificate(file, complete, fail);
+ }
}
removeCertificate(id, callback) {
- removeCertificateFile(
- this.state[id],
- () => {
- this.handleChange(id, '');
- this.setState({[id]: null, [`${id}Error`]: null});
- },
- (error) => {
- if (callback && typeof callback === 'function') {
- callback();
- }
- this.setState({[id]: null, [`${id}Error`]: error.message});
+ const complete = () => {
+ this.handleChange(id, '');
+ this.setState({[id]: null, [`${id}Error`]: null});
+ };
+
+ const fail = (error) => {
+ if (callback && typeof callback === 'function') {
+ callback();
}
- );
+ this.setState({[id]: null, [`${id}Error`]: error.message});
+ };
+
+ if (id === 'idpCertificateFile') {
+ AdminActions.removeIdpSamlCertificate(complete, fail);
+ } else if (id === 'publicCertificateFile') {
+ AdminActions.removePublicSamlCertificate(complete, fail);
+ } else if (id === 'privateKeyFile') {
+ AdminActions.removePrivateSamlCertificate(complete, fail);
+ }
}
renderTitle() {
diff --git a/webapp/components/admin_console/server_logs/index.js b/webapp/components/admin_console/server_logs/index.js
new file mode 100644
index 000000000..3adacaf1a
--- /dev/null
+++ b/webapp/components/admin_console/server_logs/index.js
@@ -0,0 +1,27 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+import {getLogs} from 'mattermost-redux/actions/admin';
+
+import * as Selectors from 'mattermost-redux/selectors/entities/admin';
+
+import Logs from './logs.jsx';
+
+function mapStateToProps(state, ownProps) {
+ return {
+ ...ownProps,
+ logs: Selectors.getLogs(state)
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ getLogs
+ }, dispatch)
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(Logs);
diff --git a/webapp/components/admin_console/logs.jsx b/webapp/components/admin_console/server_logs/logs.jsx
index d3fa67f55..b60a66ce6 100644
--- a/webapp/components/admin_console/logs.jsx
+++ b/webapp/components/admin_console/server_logs/logs.jsx
@@ -1,30 +1,43 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import AdminStore from 'stores/admin_store.jsx';
-import LoadingScreen from '../loading_screen.jsx';
-import * as AsyncClient from 'utils/async_client.jsx';
+import LoadingScreen from 'components/loading_screen.jsx';
+import React from 'react';
+import PropTypes from 'prop-types';
import {FormattedMessage} from 'react-intl';
-import React from 'react';
+export default class Logs extends React.PureComponent {
+ static propTypes = {
+
+ /*
+ * Array of logs to render
+ */
+ logs: PropTypes.arrayOf(PropTypes.string).isRequired,
+
+ actions: PropTypes.shape({
+
+ /*
+ * Function to fetch logs
+ */
+ getLogs: PropTypes.func.isRequired
+ }).isRequired
+ }
-export default class Logs extends React.Component {
constructor(props) {
super(props);
- this.onLogListenerChange = this.onLogListenerChange.bind(this);
- this.reload = this.reload.bind(this);
-
this.state = {
- logs: AdminStore.getLogs()
+ loadingLogs: true
};
}
componentDidMount() {
- AdminStore.addLogChangeListener(this.onLogListenerChange);
- AsyncClient.getLogs();
this.refs.logPanel.focus();
+
+ this.props.actions.getLogs().then(
+ () => this.setState({loadingLogs: false})
+ );
}
componentDidUpdate() {
@@ -34,40 +47,28 @@ export default class Logs extends React.Component {
node.focus();
}
- componentWillUnmount() {
- AdminStore.removeLogChangeListener(this.onLogListenerChange);
- }
-
- onLogListenerChange() {
- this.setState({
- logs: AdminStore.getLogs()
- });
- }
-
- reload() {
- AdminStore.saveLogs(null);
- this.setState({
- logs: null
- });
-
- AsyncClient.getLogs();
+ reload = () => {
+ this.setState({loadingLogs: true});
+ this.props.actions.getLogs().then(
+ () => this.setState({loadingLogs: false})
+ );
}
render() {
- var content = null;
+ let content = null;
- if (this.state.logs === null) {
+ if (this.state.loadingLogs) {
content = <LoadingScreen/>;
} else {
content = [];
- for (var i = 0; i < this.state.logs.length; i++) {
- var style = {
+ for (let i = 0; i < this.props.logs.length; i++) {
+ const style = {
whiteSpace: 'nowrap',
fontFamily: 'monospace'
};
- if (this.state.logs[i].indexOf('[EROR]') > 0) {
+ if (this.props.logs[i].indexOf('[EROR]') > 0) {
style.color = 'red';
}
@@ -77,7 +78,7 @@ export default class Logs extends React.Component {
key={'log_' + i}
style={style}
>
- {this.state.logs[i]}
+ {this.props.logs[i]}
</span>
);
}
diff --git a/webapp/components/admin_console/system_users/index.js b/webapp/components/admin_console/system_users/index.js
index 24144d701..8f1c0dc35 100644
--- a/webapp/components/admin_console/system_users/index.js
+++ b/webapp/components/admin_console/system_users/index.js
@@ -6,10 +6,13 @@ import {bindActionCreators} from 'redux';
import {getTeams, getTeamStats} from 'mattermost-redux/actions/teams';
import {getUser} from 'mattermost-redux/actions/users';
+import {getTeamsList} from 'mattermost-redux/selectors/entities/teams';
+
import SystemUsers from './system_users.jsx';
function mapStateToProps(state, ownProps) {
return {
+ teams: getTeamsList(state),
...ownProps
};
}
diff --git a/webapp/components/admin_console/system_users/system_users.jsx b/webapp/components/admin_console/system_users/system_users.jsx
index 645d1e9e6..f0b3edac7 100644
--- a/webapp/components/admin_console/system_users/system_users.jsx
+++ b/webapp/components/admin_console/system_users/system_users.jsx
@@ -1,9 +1,8 @@
-import PropTypes from 'prop-types';
-
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
+import PropTypes from 'prop-types';
import {FormattedMessage} from 'react-intl';
import {
@@ -13,14 +12,12 @@ import {
searchUsers
} from 'actions/user_actions.jsx';
-import AdminStore from 'stores/admin_store.jsx';
import AnalyticsStore from 'stores/analytics_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import {getStandardAnalytics} from 'utils/async_client.jsx';
import {Constants, StatTypes, UserSearchOptions} from 'utils/constants.jsx';
-import {convertTeamMapToList} from 'utils/team_utils.jsx';
import * as Utils from 'utils/utils.jsx';
import SystemUsersList from './system_users_list.jsx';
@@ -36,9 +33,27 @@ const USERS_PER_PAGE = 50;
export default class SystemUsers extends React.Component {
static propTypes = {
+
+ /*
+ * Array of team objects
+ */
+ teams: PropTypes.arrayOf(PropTypes.object).isRequired,
+
actions: PropTypes.shape({
+
+ /*
+ * Function to get teams
+ */
getTeams: PropTypes.func.isRequired,
+
+ /*
+ * Function to get statistics for a team
+ */
getTeamStats: PropTypes.func.isRequired,
+
+ /*
+ * Function to get a user
+ */
getUser: PropTypes.func.isRequired
}).isRequired
}
@@ -46,7 +61,6 @@ export default class SystemUsers extends React.Component {
constructor(props) {
super(props);
- this.updateTeamsFromStore = this.updateTeamsFromStore.bind(this);
this.updateTotalUsersFromStore = this.updateTotalUsersFromStore.bind(this);
this.updateUsersFromStore = this.updateUsersFromStore.bind(this);
@@ -64,7 +78,6 @@ export default class SystemUsers extends React.Component {
this.renderFilterRow = this.renderFilterRow.bind(this);
this.state = {
- teams: convertTeamMapToList(AdminStore.getAllTeams()),
totalUsers: AnalyticsStore.getAllSystem()[StatTypes.TOTAL_USERS],
users: UserStore.getProfileList(),
@@ -76,8 +89,6 @@ export default class SystemUsers extends React.Component {
}
componentDidMount() {
- AdminStore.addAllTeamsChangeListener(this.updateTeamsFromStore);
-
AnalyticsStore.addChangeListener(this.updateTotalUsersFromStore);
TeamStore.addStatsChangeListener(this.updateTotalUsersFromStore);
@@ -101,8 +112,6 @@ export default class SystemUsers extends React.Component {
}
componentWillUnmount() {
- AdminStore.removeAllTeamsChangeListener(this.updateTeamsFromStore);
-
AnalyticsStore.removeChangeListener(this.updateTotalUsersFromStore);
TeamStore.removeStatsChangeListener(this.updateTotalUsersFromStore);
@@ -111,10 +120,6 @@ export default class SystemUsers extends React.Component {
UserStore.removeWithoutTeamChangeListener(this.updateUsersFromStore);
}
- updateTeamsFromStore() {
- this.setState({teams: convertTeamMapToList(AdminStore.getAllTeams())});
- }
-
updateTotalUsersFromStore(teamId = this.state.teamId) {
if (teamId === ALL_USERS) {
this.setState({
@@ -271,7 +276,7 @@ export default class SystemUsers extends React.Component {
}
renderFilterRow(doSearch) {
- const teams = this.state.teams.map((team) => {
+ const teams = this.props.teams.map((team) => {
return (
<option
key={team.id}
@@ -339,7 +344,7 @@ export default class SystemUsers extends React.Component {
users={users}
usersPerPage={USERS_PER_PAGE}
total={this.state.totalUsers}
- teams={this.state.teams}
+ teams={this.props.teams}
teamId={this.state.teamId}
term={this.state.term}
onTermChange={this.handleTermChange}
diff --git a/webapp/components/analytics/team_analytics/index.js b/webapp/components/analytics/team_analytics/index.js
index 270967a1b..0620a8fdb 100644
--- a/webapp/components/analytics/team_analytics/index.js
+++ b/webapp/components/analytics/team_analytics/index.js
@@ -5,10 +5,20 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {getTeams} from 'mattermost-redux/actions/teams';
+import {getTeamsList} from 'mattermost-redux/selectors/entities/teams';
+import BrowserStore from 'stores/browser_store.jsx';
+
import TeamAnalytics from './team_analytics.jsx';
+const LAST_ANALYTICS_TEAM = 'last_analytics_team';
+
function mapStateToProps(state, ownProps) {
+ const teams = getTeamsList(state);
+ const teamId = BrowserStore.getGlobalItem(LAST_ANALYTICS_TEAM, teams.length > 0 ? teams[0].id : '');
+
return {
+ initialTeam: state.entities.teams.teams[teamId],
+ teams,
...ownProps
};
}
diff --git a/webapp/components/analytics/team_analytics/team_analytics.jsx b/webapp/components/analytics/team_analytics/team_analytics.jsx
index eff19a309..6591d293a 100644
--- a/webapp/components/analytics/team_analytics/team_analytics.jsx
+++ b/webapp/components/analytics/team_analytics/team_analytics.jsx
@@ -1,21 +1,18 @@
-import PropTypes from 'prop-types';
-
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
+import PropTypes from 'prop-types';
import {FormattedDate, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import Banner from 'components/admin_console/banner.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
-import AdminStore from 'stores/admin_store.jsx';
import AnalyticsStore from 'stores/analytics_store.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {StatTypes} from 'utils/constants.jsx';
-import {convertTeamMapToList} from 'utils/team_utils.jsx';
import LineChart from 'components/analytics/line_chart.jsx';
import StatisticCount from 'components/analytics/statistic_count.jsx';
@@ -26,7 +23,22 @@ const LAST_ANALYTICS_TEAM = 'last_analytics_team';
export default class TeamAnalytics extends React.Component {
static propTypes = {
+
+ /*
+ * Array of team objects
+ */
+ teams: PropTypes.arrayOf(PropTypes.object).isRequired,
+
+ /*
+ * Initial team to load analytics for
+ */
+ initialTeam: PropTypes.object,
+
actions: PropTypes.shape({
+
+ /*
+ * Function to get teams
+ */
getTeams: PropTypes.func.isRequired
}).isRequired
}
@@ -34,37 +46,27 @@ export default class TeamAnalytics extends React.Component {
constructor(props) {
super(props);
- this.onChange = this.onChange.bind(this);
- this.onAllTeamsChange = this.onAllTeamsChange.bind(this);
- this.handleTeamChange = this.handleTeamChange.bind(this);
-
- const teams = convertTeamMapToList(AdminStore.getAllTeams());
- const teamId = BrowserStore.getGlobalItem(LAST_ANALYTICS_TEAM, teams.length > 0 ? teams[0].id : '');
+ const teamId = props.initialTeam ? props.initialTeam.id : '';
this.state = {
- teams,
- teamId,
- team: AdminStore.getTeam(teamId),
+ team: props.initialTeam,
stats: AnalyticsStore.getAllTeam(teamId)
};
}
componentDidMount() {
AnalyticsStore.addChangeListener(this.onChange);
- AdminStore.addAllTeamsChangeListener(this.onAllTeamsChange);
- if (this.state.teamId !== '') {
- this.getData(this.state.teamId);
+ if (this.state.team) {
+ this.getData(this.state.team.id);
}
- if (this.state.teams.length === 0) {
- this.props.actions.getTeams(0, 1000);
- }
+ this.props.actions.getTeams(0, 1000);
}
componentWillUpdate(nextProps, nextState) {
- if (nextState.teamId !== this.state.teamId) {
- this.getData(nextState.teamId);
+ if (nextState.team && nextState.team !== this.state.team) {
+ this.getData(nextState.team.id);
}
}
@@ -77,53 +79,38 @@ export default class TeamAnalytics extends React.Component {
componentWillUnmount() {
AnalyticsStore.removeChangeListener(this.onChange);
- AdminStore.removeAllTeamsChangeListener(this.onAllTeamsChange);
}
- onChange() {
+ onChange = () => {
+ const teamId = this.state.team ? this.state.team.id : '';
this.setState({
- stats: AnalyticsStore.getAllTeam(this.state.teamId)
+ stats: AnalyticsStore.getAllTeam(teamId)
});
}
- onAllTeamsChange() {
- const teams = convertTeamMapToList(AdminStore.getAllTeams());
-
- if (teams.length > 0) {
- if (this.state.teamId) {
- this.setState({
- team: AdminStore.getTeam(this.state.teamId)
- });
- } else {
- this.setState({
- teamId: teams[0].id,
- team: teams[0]
- });
- }
- }
+ handleTeamChange = (e) => {
+ const teamId = e.target.value;
- this.setState({
- teams
+ let team;
+ this.props.teams.forEach((t) => {
+ if (t.id === teamId) {
+ team = t;
+ }
});
- }
-
- handleTeamChange(e) {
- const teamId = e.target.value;
this.setState({
- teamId,
- team: AdminStore.getTeam(teamId)
+ team
});
BrowserStore.setGlobalItem(LAST_ANALYTICS_TEAM, teamId);
}
render() {
- if (this.state.teams.length === 0 || !this.state.team || !this.state.stats) {
+ if (this.props.teams.length === 0 || !this.state.team || !this.state.stats) {
return <LoadingScreen/>;
}
- if (this.state.teamId === '') {
+ if (this.state.team == null) {
return (
<Banner
description={
@@ -217,7 +204,7 @@ export default class TeamAnalytics extends React.Component {
const recentActiveUsers = formatRecentUsersData(stats[StatTypes.RECENTLY_ACTIVE_USERS]);
const newlyCreatedUsers = formatNewUsersData(stats[StatTypes.NEWLY_CREATED_USERS]);
- const teams = this.state.teams.map((team) => {
+ const teams = this.props.teams.map((team) => {
return (
<option
key={team.id}
@@ -246,7 +233,7 @@ export default class TeamAnalytics extends React.Component {
<select
className='form-control team-statistics__team-filter__dropdown'
onChange={this.handleTeamChange}
- value={this.state.teamId}
+ value={this.state.team.id}
>
{teams}
</select>
diff --git a/webapp/components/user_list_row.jsx b/webapp/components/user_list_row.jsx
index 1076752a9..ac26aace7 100644
--- a/webapp/components/user_list_row.jsx
+++ b/webapp/components/user_list_row.jsx
@@ -8,7 +8,7 @@ import PreferenceStore from 'stores/preference_store.jsx';
import Constants from 'utils/constants.jsx';
import * as Utils from 'utils/utils.jsx';
-import Client from 'client/web_client.jsx';
+import {Client4} from 'mattermost-redux/client';
import PropTypes from 'prop-types';
@@ -73,7 +73,7 @@ export default function UserListRow({user, extraInfo, actions, actionProps, acti
className='more-modal__row'
>
<ProfilePicture
- src={`${Client.getUsersRoute()}/${user.id}/image?time=${user.last_picture_update}`}
+ src={Client4.getProfilePictureUrl(user.id, user.last_picture_update)}
status={status}
width='32'
height='32'
diff --git a/webapp/package.json b/webapp/package.json
index eba2b8b00..729a3c7bf 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -22,7 +22,7 @@
"localforage": "1.5.0",
"marked": "mattermost/marked#8f5902fff9bad793cd6c66e0c44002c9e79e1317",
"match-at": "0.1.0",
- "mattermost-redux": "mattermost/mattermost-redux#webapp-3.10",
+ "mattermost-redux": "mattermost/mattermost-redux#webapp-master",
"object-assign": "4.1.1",
"pdfjs-dist": "1.8.361",
"perfect-scrollbar": "0.7.0",
diff --git a/webapp/routes/route_admin_console.jsx b/webapp/routes/route_admin_console.jsx
index f7244f02b..2a01d4115 100644
--- a/webapp/routes/route_admin_console.jsx
+++ b/webapp/routes/route_admin_console.jsx
@@ -44,8 +44,8 @@ import DeveloperSettings from 'components/admin_console/developer_settings.jsx';
import SystemUsers from 'components/admin_console/system_users';
import TeamAnalytics from 'components/analytics/team_analytics';
import LicenseSettings from 'components/admin_console/license_settings.jsx';
-import Audits from 'components/admin_console/audits.jsx';
-import Logs from 'components/admin_console/logs.jsx';
+import Audits from 'components/admin_console/audits';
+import Logs from 'components/admin_console/server_logs';
export default (
<Route>
diff --git a/webapp/routes/route_root.jsx b/webapp/routes/route_root.jsx
index 52727c275..f633049ce 100644
--- a/webapp/routes/route_root.jsx
+++ b/webapp/routes/route_root.jsx
@@ -133,7 +133,7 @@ export default {
{
path: 'admin_console',
getComponents: (location, callback) => {
- System.import('components/admin_console/admin_console.jsx').then(RouteUtils.importComponentSuccess(callback));
+ System.import('components/admin_console').then(RouteUtils.importComponentSuccess(callback));
},
indexRoute: {onEnter: (nextState, replace) => replace('/admin_console/system_analytics')},
getChildRoutes: (location, callback) => {
diff --git a/webapp/stores/admin_store.jsx b/webapp/stores/admin_store.jsx
deleted file mode 100644
index 2826e6ddf..000000000
--- a/webapp/stores/admin_store.jsx
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import EventEmitter from 'events';
-
-import Constants from 'utils/constants.jsx';
-const ActionTypes = Constants.ActionTypes;
-
-const LOG_CHANGE_EVENT = 'log_change';
-const SERVER_AUDIT_CHANGE_EVENT = 'server_audit_change';
-const CONFIG_CHANGE_EVENT = 'config_change';
-const ALL_TEAMS_EVENT = 'all_team_change';
-const SERVER_COMPLIANCE_REPORT_CHANGE_EVENT = 'server_compliance_reports_change';
-
-import store from 'stores/redux_store.jsx';
-
-class AdminStoreClass extends EventEmitter {
- constructor() {
- super();
-
- this.logs = null;
- this.audits = null;
- this.config = null;
- this.clusterId = null;
- this.complianceReports = null;
-
- this.entities = store.getState().entities.teams;
-
- store.subscribe(() => {
- const newEntities = store.getState().entities.teams;
-
- if (newEntities.teams !== this.entities.teams) {
- this.emitAllTeamsChange();
- }
-
- this.entities = newEntities;
- });
- }
-
- emitLogChange() {
- this.emit(LOG_CHANGE_EVENT);
- }
-
- addLogChangeListener(callback) {
- this.on(LOG_CHANGE_EVENT, callback);
- }
-
- removeLogChangeListener(callback) {
- this.removeListener(LOG_CHANGE_EVENT, callback);
- }
-
- emitAuditChange() {
- this.emit(SERVER_AUDIT_CHANGE_EVENT);
- }
-
- addAuditChangeListener(callback) {
- this.on(SERVER_AUDIT_CHANGE_EVENT, callback);
- }
-
- removeAuditChangeListener(callback) {
- this.removeListener(SERVER_AUDIT_CHANGE_EVENT, callback);
- }
-
- emitComplianceReportsChange() {
- this.emit(SERVER_COMPLIANCE_REPORT_CHANGE_EVENT);
- }
-
- addComplianceReportsChangeListener(callback) {
- this.on(SERVER_COMPLIANCE_REPORT_CHANGE_EVENT, callback);
- }
-
- removeComplianceReportsChangeListener(callback) {
- this.removeListener(SERVER_COMPLIANCE_REPORT_CHANGE_EVENT, callback);
- }
-
- emitConfigChange() {
- this.emit(CONFIG_CHANGE_EVENT);
- }
-
- addConfigChangeListener(callback) {
- this.on(CONFIG_CHANGE_EVENT, callback);
- }
-
- removeConfigChangeListener(callback) {
- this.removeListener(CONFIG_CHANGE_EVENT, callback);
- }
-
- emitAllTeamsChange() {
- this.emit(ALL_TEAMS_EVENT);
- }
-
- addAllTeamsChangeListener(callback) {
- this.on(ALL_TEAMS_EVENT, callback);
- }
-
- removeAllTeamsChangeListener(callback) {
- this.removeListener(ALL_TEAMS_EVENT, callback);
- }
-
- getClusterId() {
- return this.clusterId;
- }
-
- saveClusterId(clusterId) {
- this.clusterId = clusterId;
- }
-
- getLogs() {
- return this.logs;
- }
-
- saveLogs(logs) {
- this.logs = logs;
- }
-
- getAudits() {
- return this.audits;
- }
-
- saveAudits(audits) {
- this.audits = audits;
- }
-
- getComplianceReports() {
- return this.complianceReports;
- }
-
- saveComplianceReports(complianceReports) {
- this.complianceReports = complianceReports;
- }
-
- getConfig() {
- return this.config;
- }
-
- saveConfig(config) {
- this.config = config;
- }
-
- getAllTeams() {
- return store.getState().entities.teams.teams;
- }
-
- getTeam(id) {
- return this.getAllTeams()[id];
- }
-}
-
-var AdminStore = new AdminStoreClass();
-
-AdminStoreClass.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
-
- switch (action.type) {
- case ActionTypes.RECEIVED_LOGS:
- AdminStore.saveLogs(action.logs);
- AdminStore.emitLogChange();
- break;
- case ActionTypes.RECEIVED_SERVER_AUDITS:
- AdminStore.saveAudits(action.audits);
- AdminStore.emitAuditChange();
- break;
- case ActionTypes.RECEIVED_SERVER_COMPLIANCE_REPORTS:
- AdminStore.saveComplianceReports(action.complianceReports);
- AdminStore.emitComplianceReportsChange();
- break;
- case ActionTypes.RECEIVED_CONFIG:
- AdminStore.saveConfig(action.config);
- AdminStore.saveClusterId(action.clusterId);
- AdminStore.emitConfigChange();
- break;
- default:
- }
-});
-
-export default AdminStore;
diff --git a/webapp/yarn.lock b/webapp/yarn.lock
index fe03edcc8..1755bc675 100644
--- a/webapp/yarn.lock
+++ b/webapp/yarn.lock
@@ -4882,9 +4882,9 @@ math-expression-evaluator@^1.2.14:
version "1.2.16"
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9"
-mattermost-redux@mattermost/mattermost-redux#webapp-3.10:
+mattermost-redux@mattermost/mattermost-redux#webapp-master:
version "0.0.1"
- resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/30af9bdf41aeac4ca5a37817773bd8a9b8372a4a"
+ resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/dc9c164de3153a4c33922a8a3f996490e8949e02"
dependencies:
deep-equal "1.0.1"
harmony-reflect "1.5.1"