mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
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
This commit is contained in:
committed by
Harrison Healey
parent
40efd8367a
commit
1138dd6770
@@ -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);
|
||||
/*
|
||||
* Object representing the config file
|
||||
*/
|
||||
config: PropTypes.object.isRequired,
|
||||
|
||||
this.state = {
|
||||
config: AdminStore.getConfig()
|
||||
};
|
||||
actions: PropTypes.shape({
|
||||
|
||||
/*
|
||||
* Function to get the config file
|
||||
*/
|
||||
getConfig: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
AdminStore.addConfigChangeListener(this.handleConfigChange);
|
||||
AsyncClient.getConfig();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
AdminStore.removeConfigChangeListener(this.handleConfigChange);
|
||||
}
|
||||
|
||||
handleConfigChange() {
|
||||
this.setState({
|
||||
config: AdminStore.getConfig()
|
||||
});
|
||||
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'>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 AdminStore from 'stores/admin_store.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import LoadingScreen from 'components/loading_screen.jsx';
|
||||
import AuditTable from 'components/audit_table.jsx';
|
||||
import ComplianceReports from 'components/admin_console/compliance_reports';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
export default class Audits extends React.PureComponent {
|
||||
static propTypes = {
|
||||
|
||||
/*
|
||||
* Array of audits to render
|
||||
*/
|
||||
audits: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
|
||||
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();
|
||||
this.props.actions.getAudits().then(
|
||||
() => this.setState({loadingAudits: false})
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
AdminStore.removeAuditChangeListener(this.onAuditListenerChange);
|
||||
}
|
||||
|
||||
onAuditListenerChange() {
|
||||
this.setState({
|
||||
audits: AdminStore.getAudits()
|
||||
});
|
||||
}
|
||||
|
||||
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}
|
||||
27
webapp/components/admin_console/audits/index.js
Normal file
27
webapp/components/admin_console/audits/index.js
Normal file
@@ -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);
|
||||
@@ -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!')}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {FormattedMessage, FormattedDate, FormattedTime} from 'react-intl';
|
||||
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 ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import {FormattedMessage, FormattedDate, FormattedTime} from 'react-intl';
|
||||
|
||||
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});
|
||||
|
||||
this.props.actions.getComplianceReports().then(
|
||||
() => this.setState({loadingReports: false})
|
||||
);
|
||||
}
|
||||
|
||||
onComplianceReportsListenerChange() {
|
||||
this.setState({
|
||||
reports: AdminStore.getComplianceReports()
|
||||
});
|
||||
}
|
||||
|
||||
reload() {
|
||||
AdminStore.saveComplianceReports(null);
|
||||
this.setState({
|
||||
reports: null,
|
||||
serverError: null
|
||||
});
|
||||
|
||||
AsyncClient.getComplianceReports();
|
||||
}
|
||||
|
||||
runReport(e) {
|
||||
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);
|
||||
this.setState({runningReport: true});
|
||||
|
||||
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');
|
||||
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);
|
||||
|
||||
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'/>
|
||||
42
webapp/components/admin_console/compliance_reports/index.js
Normal file
42
webapp/components/admin_console/compliance_reports/index.js
Normal file
@@ -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);
|
||||
27
webapp/components/admin_console/index.js
Normal file
27
webapp/components/admin_console/index.js
Normal file
@@ -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);
|
||||
@@ -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() {
|
||||
|
||||
27
webapp/components/admin_console/server_logs/index.js
Normal file
27
webapp/components/admin_console/server_logs/index.js
Normal file
@@ -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);
|
||||
@@ -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 {FormattedMessage} from 'react-intl';
|
||||
import LoadingScreen from 'components/loading_screen.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user