|
@@ -1,13 +1,10 @@
|
|
|
import React from 'react';
|
|
|
import _ from 'underscore';
|
|
|
|
|
|
-import AlertActions from '../actions/alertActions';
|
|
|
-import ApiMixin from '../mixins/apiMixin';
|
|
|
-import IndicatorStore from '../stores/indicatorStore';
|
|
|
-import LoadingIndicator from '../components/loadingIndicator';
|
|
|
+import AsyncView from './asyncView';
|
|
|
import {t} from '../locale';
|
|
|
import {getOption, getOptionField} from '../options';
|
|
|
-import {Form} from '../components/forms';
|
|
|
+import {ApiForm} from '../components/forms';
|
|
|
|
|
|
const optionsAvailable = [
|
|
|
'system.url-prefix',
|
|
@@ -21,190 +18,52 @@ const optionsAvailable = [
|
|
|
'api.rate-limit.org-create'
|
|
|
];
|
|
|
|
|
|
-const SettingsList = React.createClass({
|
|
|
- propTypes: {
|
|
|
- formDisabled: React.PropTypes.bool,
|
|
|
- options: React.PropTypes.object.isRequired,
|
|
|
- onSubmit: React.PropTypes.func.isRequired
|
|
|
- },
|
|
|
+export default class AdminSettings extends AsyncView {
|
|
|
+ getEndpoint() {
|
|
|
+ return '/internal/options/';
|
|
|
+ }
|
|
|
+
|
|
|
+ renderBody() {
|
|
|
+ let {data} = this.state;
|
|
|
|
|
|
- getInitialState() {
|
|
|
- let options = this.props.options;
|
|
|
- let formData = {};
|
|
|
- let required = [];
|
|
|
+ let initialData = {};
|
|
|
let fields = {};
|
|
|
for (let key of optionsAvailable) {
|
|
|
// TODO(dcramer): we should not be mutating options
|
|
|
- let option = options[key] || {field: {}};
|
|
|
+ let option = data[key] || {field: {}};
|
|
|
if (_.isUndefined(option.value) || option.value === '') {
|
|
|
let defn = getOption(key);
|
|
|
- formData[key] = defn.defaultValue ? defn.defaultValue() : '';
|
|
|
+ initialData[key] = defn.defaultValue ? defn.defaultValue() : '';
|
|
|
} else {
|
|
|
- formData[key] = option.value;
|
|
|
- }
|
|
|
- if (option.field.required) {
|
|
|
- required.push(key);
|
|
|
+ initialData[key] = option.value;
|
|
|
}
|
|
|
- fields[key] = getOptionField(
|
|
|
- key,
|
|
|
- this.onFieldChange.bind(this, key),
|
|
|
- formData[key],
|
|
|
- option.field
|
|
|
- );
|
|
|
+ fields[key] = getOptionField(key, option.field);
|
|
|
}
|
|
|
|
|
|
- return {
|
|
|
- required: required,
|
|
|
- formData: formData,
|
|
|
- fields: fields
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
- onFieldChange(name, value) {
|
|
|
- let formData = this.state.formData;
|
|
|
- formData[name] = value;
|
|
|
- this.setState({
|
|
|
- formData: formData
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- onSubmit(e) {
|
|
|
- this.props.onSubmit(this.state.formData);
|
|
|
- },
|
|
|
-
|
|
|
- render() {
|
|
|
- let {fields, required, formData} = this.state;
|
|
|
- let formValid = !required.filter(option => !formData[option]).length;
|
|
|
- let submitDisabled = !formValid || this.props.formDisabled;
|
|
|
-
|
|
|
- return (
|
|
|
- <Form onSubmit={this.onSubmit} submitDisabled={submitDisabled}>
|
|
|
- <h4>General</h4>
|
|
|
- {fields['system.url-prefix']}
|
|
|
- {fields['system.admin-email']}
|
|
|
- {fields['system.support-email']}
|
|
|
- {fields['system.security-email']}
|
|
|
- {fields['system.rate-limit']}
|
|
|
-
|
|
|
- <h4>Security & Abuse</h4>
|
|
|
- {fields['auth.allow-registration']}
|
|
|
- {fields['auth.ip-rate-limit']}
|
|
|
- {fields['auth.user-rate-limit']}
|
|
|
- {fields['api.rate-limit.org-create']}
|
|
|
- </Form>
|
|
|
- );
|
|
|
- }
|
|
|
-});
|
|
|
-
|
|
|
-const AdminSettings = React.createClass({
|
|
|
- mixins: [ApiMixin],
|
|
|
-
|
|
|
- getInitialState() {
|
|
|
- return {
|
|
|
- loading: true,
|
|
|
- error: false,
|
|
|
- submitInProgress: false,
|
|
|
- submitError: null,
|
|
|
- options: {}
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
- componentWillMount() {
|
|
|
- this.fetchData();
|
|
|
- },
|
|
|
-
|
|
|
- remountComponent() {
|
|
|
- this.setState(this.getInitialState(), this.fetchData);
|
|
|
- },
|
|
|
-
|
|
|
- fetchData(callback) {
|
|
|
- this.api.request('/internal/options/', {
|
|
|
- method: 'GET',
|
|
|
- success: data => {
|
|
|
- this.setState({
|
|
|
- options: data,
|
|
|
- loading: false,
|
|
|
- error: false
|
|
|
- });
|
|
|
- },
|
|
|
- error: () => {
|
|
|
- this.setState({
|
|
|
- loading: false,
|
|
|
- error: true
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- onSubmit(formData) {
|
|
|
- this.setState({
|
|
|
- submitInProgress: true,
|
|
|
- submitError: false
|
|
|
- });
|
|
|
- let loadingIndicator = IndicatorStore.add(t('Saving changes..'));
|
|
|
-
|
|
|
- // We only want to send back the values which weren't disabled
|
|
|
- formData = _.pick(formData, (value, key) => {
|
|
|
- return !this.state.options[key].field.disabled;
|
|
|
- });
|
|
|
- this.api.request('/internal/options/', {
|
|
|
- method: 'PUT',
|
|
|
- data: formData,
|
|
|
- success: () => {
|
|
|
- this.setState({
|
|
|
- submitInProgress: false
|
|
|
- });
|
|
|
- AlertActions.addAlert({
|
|
|
- message: t('Your changes were saved, and will propagate to services shortly.'),
|
|
|
- type: 'success'
|
|
|
- });
|
|
|
- },
|
|
|
- error: () => {
|
|
|
- this.setState({
|
|
|
- submitInProgress: false,
|
|
|
- submitError: true
|
|
|
- });
|
|
|
- },
|
|
|
- complete: () => {
|
|
|
- IndicatorStore.remove(loadingIndicator);
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- render() {
|
|
|
- let {error, loading, options, submitError, submitInProgress} = this.state;
|
|
|
-
|
|
|
return (
|
|
|
<div>
|
|
|
<h3>{t('Settings')}</h3>
|
|
|
|
|
|
- {loading
|
|
|
- ? <LoadingIndicator>
|
|
|
- {t('Please wait while we load configuration.')}
|
|
|
- </LoadingIndicator>
|
|
|
- : error
|
|
|
- ? <div className="loading-error">
|
|
|
- <span className="icon" />
|
|
|
- {t(
|
|
|
- 'We were unable to load the required configuration from the Sentry server. Please take a look at the service logs.'
|
|
|
- )}
|
|
|
- </div>
|
|
|
- : <div>
|
|
|
- {submitError &&
|
|
|
- <div className="alert alert-block alert-error">
|
|
|
- {t(
|
|
|
- 'We were unable to submit your changes to the Sentry server. Please take a look at the service logs.'
|
|
|
- )}
|
|
|
- </div>}
|
|
|
- <SettingsList
|
|
|
- options={options}
|
|
|
- onSubmit={this.onSubmit}
|
|
|
- formDisabled={submitInProgress}
|
|
|
- />
|
|
|
- </div>}
|
|
|
+ <ApiForm
|
|
|
+ apiMethod="PUT"
|
|
|
+ apiEndpoint={this.getEndpoint()}
|
|
|
+ onSubmit={this.onSubmit}
|
|
|
+ initialData={initialData}
|
|
|
+ requireChanges={true}>
|
|
|
+ <h4>General</h4>
|
|
|
+ {fields['system.url-prefix']}
|
|
|
+ {fields['system.admin-email']}
|
|
|
+ {fields['system.support-email']}
|
|
|
+ {fields['system.security-email']}
|
|
|
+ {fields['system.rate-limit']}
|
|
|
+
|
|
|
+ <h4>Security & Abuse</h4>
|
|
|
+ {fields['auth.allow-registration']}
|
|
|
+ {fields['auth.ip-rate-limit']}
|
|
|
+ {fields['auth.user-rate-limit']}
|
|
|
+ {fields['api.rate-limit.org-create']}
|
|
|
+ </ApiForm>
|
|
|
</div>
|
|
|
);
|
|
|
}
|
|
|
-});
|
|
|
-
|
|
|
-export default AdminSettings;
|
|
|
+}
|