mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-02-10 18:05:07 +08:00
[feature] Reset config settings
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
import {useState, useRef} from 'react';
|
||||
import {useState, useRef, useEffect} from 'react';
|
||||
import {useSelector, useDispatch} from 'react-redux';
|
||||
import {saveConfig, selectConfig} from '../../store/slices/configSlice';
|
||||
import {saveConfig, resetConfig, selectConfig} from '../../store/slices/configSlice';
|
||||
import {getNestedValue} from '../../utils/getNestedValue';
|
||||
import {mergeNestedObjects} from '../../utils/mergeNestedObjects';
|
||||
import {useFieldValidation} from '../../hooks/useFieldValidation';
|
||||
@ -8,7 +8,7 @@ import PageHeader from '../../components/PageHeader/PageHeader';
|
||||
import PageDescription from '../../components/PageDescription/PageDescription';
|
||||
import Tabs from '../../components/Tabs/Tabs';
|
||||
import Input from '../../components/Input/Input';
|
||||
import FixedSaveButton from '../../components/FixedSaveButton/FixedSaveButton';
|
||||
import FixedSaveButtonGroup from '../../components/FixedSaveButtonGroup/FixedSaveButtonGroup';
|
||||
import styles from './Expiration.module.scss';
|
||||
|
||||
const expirationTabs = [
|
||||
@ -46,11 +46,12 @@ function Expiration() {
|
||||
};
|
||||
|
||||
// Reset state and errors to global config
|
||||
const resetToGlobalConfig = () => {
|
||||
if (config) {
|
||||
const resetToGlobalConfig = source => {
|
||||
const src = source || config;
|
||||
if (src) {
|
||||
const settings = {};
|
||||
Object.keys(CONFIG_PATHS).forEach(key => {
|
||||
const value = getNestedValue(config, CONFIG_PATHS[key], '');
|
||||
const value = getNestedValue(src, CONFIG_PATHS[key], '');
|
||||
settings[key] = value;
|
||||
});
|
||||
setLocalSettings(settings);
|
||||
@ -74,6 +75,12 @@ function Expiration() {
|
||||
hasInitialized.current = true;
|
||||
}
|
||||
|
||||
// Sync from Redux when config changes (e.g., after reset), unless user has local edits
|
||||
useEffect(() => {
|
||||
if (config && !hasChanges) {
|
||||
resetToGlobalConfig();
|
||||
}
|
||||
}, [config]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
// Handle field changes
|
||||
const handleFieldChange = (field, value) => {
|
||||
setLocalSettings(prev => ({
|
||||
@ -129,6 +136,24 @@ function Expiration() {
|
||||
setHasChanges(false);
|
||||
};
|
||||
|
||||
// Handle reset for active tab
|
||||
const handleReset = async () => {
|
||||
const confirmed = window.confirm('Reset settings on this tab to defaults?');
|
||||
if (!confirmed) return;
|
||||
try {
|
||||
const paths =
|
||||
activeTab === 'garbage-collection'
|
||||
? [CONFIG_PATHS.filesCron, CONFIG_PATHS.documentsCron, CONFIG_PATHS.files, CONFIG_PATHS.filesremovedatonce]
|
||||
: [CONFIG_PATHS.sessionidle, CONFIG_PATHS.sessionabsolute];
|
||||
const merged = await dispatch(resetConfig(paths)).unwrap();
|
||||
resetToGlobalConfig(merged);
|
||||
setHasChanges(false);
|
||||
} catch (e) {
|
||||
console.error('Failed to reset expiration settings:', e);
|
||||
alert('Failed to reset settings. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
// Render tab content
|
||||
const renderTabContent = () => {
|
||||
switch (activeTab) {
|
||||
@ -224,9 +249,20 @@ function Expiration() {
|
||||
{renderTabContent()}
|
||||
</Tabs>
|
||||
|
||||
<FixedSaveButton onClick={handleSave} disabled={!hasChanges || hasValidationErrors()}>
|
||||
Save Changes
|
||||
</FixedSaveButton>
|
||||
<FixedSaveButtonGroup
|
||||
buttons={[
|
||||
{
|
||||
text: 'Save Changes',
|
||||
onClick: handleSave,
|
||||
disabled: !hasChanges || hasValidationErrors()
|
||||
},
|
||||
{
|
||||
text: 'Reset to Defaults',
|
||||
onClick: handleReset,
|
||||
disabled: false
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import {useState, useEffect} from 'react';
|
||||
import {useSelector, useDispatch} from 'react-redux';
|
||||
import {saveConfig, selectConfig} from '../../store/slices/configSlice';
|
||||
import {saveConfig, resetConfig, selectConfig} from '../../store/slices/configSlice';
|
||||
import {getNestedValue} from '../../utils/getNestedValue';
|
||||
import {mergeNestedObjects} from '../../utils/mergeNestedObjects';
|
||||
import {useFieldValidation} from '../../hooks/useFieldValidation';
|
||||
import PageHeader from '../../components/PageHeader/PageHeader';
|
||||
import PageDescription from '../../components/PageDescription/PageDescription';
|
||||
import Input from '../../components/Input/Input';
|
||||
import FixedSaveButton from '../../components/FixedSaveButton/FixedSaveButton';
|
||||
import FixedSaveButtonGroup from '../../components/FixedSaveButtonGroup/FixedSaveButtonGroup';
|
||||
import styles from './FileLimits.module.scss';
|
||||
|
||||
function FileLimits() {
|
||||
@ -163,6 +163,20 @@ function FileLimits() {
|
||||
setHasChanges(false);
|
||||
};
|
||||
|
||||
// Handle reset to defaults
|
||||
const handleReset = async () => {
|
||||
const confirmed = window.confirm('Reset file limits to defaults? This cannot be undone.');
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
await dispatch(resetConfig(['FileConverter.converter.maxDownloadBytes', 'FileConverter.converter.inputLimits'])).unwrap();
|
||||
setHasChanges(false);
|
||||
} catch (error) {
|
||||
console.error('Error resetting file limits:', error);
|
||||
alert('Failed to reset file limits. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${styles.fileLimits} ${styles.pageWithFixedSave}`}>
|
||||
<PageHeader>File Size Limits</PageHeader>
|
||||
@ -230,9 +244,20 @@ function FileLimits() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FixedSaveButton onClick={handleSave} disabled={!hasChanges || hasValidationErrors()}>
|
||||
Save Changes
|
||||
</FixedSaveButton>
|
||||
<FixedSaveButtonGroup
|
||||
buttons={[
|
||||
{
|
||||
text: 'Save Changes',
|
||||
onClick: handleSave,
|
||||
disabled: !hasChanges || hasValidationErrors()
|
||||
},
|
||||
{
|
||||
text: 'Reset to Defaults',
|
||||
onClick: handleReset,
|
||||
disabled: false
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import {useState, useRef} from 'react';
|
||||
import {useState, useRef, useEffect} from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
import {saveConfig, selectConfig} from '../../store/slices/configSlice';
|
||||
import {saveConfig, resetConfig, selectConfig} from '../../store/slices/configSlice';
|
||||
import {getNestedValue} from '../../utils/getNestedValue';
|
||||
import {mergeNestedObjects} from '../../utils/mergeNestedObjects';
|
||||
import {useFieldValidation} from '../../hooks/useFieldValidation';
|
||||
import Checkbox from '../../components/Checkbox/Checkbox';
|
||||
import FixedSaveButton from '../../components/FixedSaveButton/FixedSaveButton';
|
||||
import FixedSaveButtonGroup from '../../components/FixedSaveButtonGroup/FixedSaveButtonGroup';
|
||||
import PageHeader from '../../components/PageHeader/PageHeader';
|
||||
import PageDescription from '../../components/PageDescription/PageDescription';
|
||||
import styles from './RequestFiltering.module.scss';
|
||||
@ -28,12 +28,13 @@ function RequestFiltering() {
|
||||
};
|
||||
|
||||
const hasInitialized = useRef(false);
|
||||
const resetToGlobalConfig = () => {
|
||||
if (config) {
|
||||
const resetToGlobalConfig = source => {
|
||||
const src = source || config;
|
||||
if (src) {
|
||||
const newSettings = {};
|
||||
Object.keys(CONFIG_PATHS).forEach(key => {
|
||||
const path = CONFIG_PATHS[key];
|
||||
newSettings[key] = getNestedValue(config, path, false);
|
||||
newSettings[key] = getNestedValue(src, path, false);
|
||||
});
|
||||
setLocalSettings(newSettings);
|
||||
}
|
||||
@ -44,6 +45,12 @@ function RequestFiltering() {
|
||||
hasInitialized.current = true;
|
||||
}
|
||||
|
||||
// Sync from Redux when config changes (e.g., after reset), unless user has local edits
|
||||
useEffect(() => {
|
||||
if (config && !hasChanges) {
|
||||
resetToGlobalConfig();
|
||||
}
|
||||
}, [config]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
// Handle field changes
|
||||
const handleFieldChange = (field, value) => {
|
||||
setLocalSettings(prev => ({
|
||||
@ -82,6 +89,19 @@ function RequestFiltering() {
|
||||
setHasChanges(false);
|
||||
};
|
||||
|
||||
const handleReset = async () => {
|
||||
const confirmed = window.confirm('Reset request filtering settings to defaults?');
|
||||
if (!confirmed) return;
|
||||
try {
|
||||
const merged = await dispatch(resetConfig(Object.values(CONFIG_PATHS))).unwrap();
|
||||
resetToGlobalConfig(merged);
|
||||
setHasChanges(false);
|
||||
} catch (e) {
|
||||
console.error('Failed to reset request filtering:', e);
|
||||
alert('Failed to reset settings. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${styles.requestFiltering} ${styles.pageWithFixedSave}`}>
|
||||
<PageHeader>Request Filtering</PageHeader>
|
||||
@ -114,9 +134,20 @@ function RequestFiltering() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FixedSaveButton onClick={handleSave} disabled={!hasChanges || hasValidationErrors()}>
|
||||
Save Changes
|
||||
</FixedSaveButton>
|
||||
<FixedSaveButtonGroup
|
||||
buttons={[
|
||||
{
|
||||
text: 'Save Changes',
|
||||
onClick: handleSave,
|
||||
disabled: !hasChanges || hasValidationErrors()
|
||||
},
|
||||
{
|
||||
text: 'Reset to Defaults',
|
||||
onClick: handleReset,
|
||||
disabled: false
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {useState, useRef} from 'react';
|
||||
import {useState, useRef, useEffect} from 'react';
|
||||
import {useSelector, useDispatch} from 'react-redux';
|
||||
import {saveConfig, selectConfig} from '../../store/slices/configSlice';
|
||||
import {saveConfig, resetConfig, selectConfig} from '../../store/slices/configSlice';
|
||||
import {getNestedValue} from '../../utils/getNestedValue';
|
||||
import {mergeNestedObjects} from '../../utils/mergeNestedObjects';
|
||||
import {useFieldValidation} from '../../hooks/useFieldValidation';
|
||||
@ -8,7 +8,7 @@ import PageHeader from '../../components/PageHeader/PageHeader';
|
||||
import PageDescription from '../../components/PageDescription/PageDescription';
|
||||
import Tabs from '../../components/Tabs/Tabs';
|
||||
import AccessRules from '../../components/AccessRules/AccessRules';
|
||||
import FixedSaveButton from '../../components/FixedSaveButton/FixedSaveButton';
|
||||
import FixedSaveButtonGroup from '../../components/FixedSaveButtonGroup/FixedSaveButtonGroup';
|
||||
import styles from './SecuritySettings.module.scss';
|
||||
|
||||
const securityTabs = [{key: 'ip-filtering', label: 'IP Filtering'}];
|
||||
@ -23,9 +23,10 @@ function SecuritySettings() {
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
|
||||
// Reset state and errors to global config
|
||||
const resetToGlobalConfig = () => {
|
||||
if (config) {
|
||||
const ipFilterRules = getNestedValue(config, 'services.CoAuthoring.ipfilter.rules', []);
|
||||
const resetToGlobalConfig = source => {
|
||||
const src = source || config;
|
||||
if (src) {
|
||||
const ipFilterRules = getNestedValue(src, 'services.CoAuthoring.ipfilter.rules', []);
|
||||
const uiRules = ipFilterRules.map(rule => ({
|
||||
type: rule.allowed ? 'Allow' : 'Deny',
|
||||
value: rule.address
|
||||
@ -50,6 +51,12 @@ function SecuritySettings() {
|
||||
hasInitialized.current = true;
|
||||
}
|
||||
|
||||
// Sync from Redux when config changes (e.g., after reset), unless user has local edits
|
||||
useEffect(() => {
|
||||
if (config && !hasChanges) {
|
||||
resetToGlobalConfig();
|
||||
}
|
||||
}, [config]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
// Handle rules changes
|
||||
const handleRulesChange = newRules => {
|
||||
setLocalRules(newRules);
|
||||
@ -85,6 +92,23 @@ function SecuritySettings() {
|
||||
setHasChanges(false);
|
||||
};
|
||||
|
||||
const handleReset = async () => {
|
||||
const confirmed = window.confirm('Reset security settings on this tab to defaults?');
|
||||
if (!confirmed) return;
|
||||
try {
|
||||
// Only reset active tab settings
|
||||
const paths = activeTab === 'ip-filtering' ? ['services.CoAuthoring.ipfilter.rules'] : [];
|
||||
if (paths.length > 0) {
|
||||
const merged = await dispatch(resetConfig(paths)).unwrap();
|
||||
resetToGlobalConfig(merged);
|
||||
}
|
||||
setHasChanges(false);
|
||||
} catch (e) {
|
||||
console.error('Failed to reset security settings:', e);
|
||||
alert('Failed to reset settings. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
const renderTabContent = () => {
|
||||
switch (activeTab) {
|
||||
case 'ip-filtering':
|
||||
@ -110,9 +134,20 @@ function SecuritySettings() {
|
||||
{renderTabContent()}
|
||||
</Tabs>
|
||||
|
||||
<FixedSaveButton onClick={handleSave} disabled={!hasChanges || hasValidationErrors()}>
|
||||
Save Changes
|
||||
</FixedSaveButton>
|
||||
<FixedSaveButtonGroup
|
||||
buttons={[
|
||||
{
|
||||
text: 'Save Changes',
|
||||
onClick: handleSave,
|
||||
disabled: !hasChanges || hasValidationErrors()
|
||||
},
|
||||
{
|
||||
text: 'Reset to Defaults',
|
||||
onClick: handleReset,
|
||||
disabled: false
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {useState, useRef} from 'react';
|
||||
import {useState, useRef, useEffect} from 'react';
|
||||
import {useSelector, useDispatch} from 'react-redux';
|
||||
import {saveConfig, selectConfig, rotateWopiKeysAction} from '../../store/slices/configSlice';
|
||||
import {saveConfig, resetConfig, selectConfig, rotateWopiKeysAction} from '../../store/slices/configSlice';
|
||||
import {getNestedValue} from '../../utils/getNestedValue';
|
||||
import {mergeNestedObjects} from '../../utils/mergeNestedObjects';
|
||||
import {useFieldValidation} from '../../hooks/useFieldValidation';
|
||||
@ -10,7 +10,7 @@ import PageDescription from '../../components/PageDescription/PageDescription';
|
||||
import ToggleSwitch from '../../components/ToggleSwitch/ToggleSwitch';
|
||||
import Input from '../../components/Input/Input';
|
||||
import Checkbox from '../../components/Checkbox/Checkbox';
|
||||
import FixedSaveButton from '../../components/FixedSaveButton/FixedSaveButton';
|
||||
import FixedSaveButtonGroup from '../../components/FixedSaveButtonGroup/FixedSaveButtonGroup';
|
||||
import Note from '../../components/Note/Note';
|
||||
import styles from './WOPISettings.module.scss';
|
||||
|
||||
@ -31,14 +31,17 @@ function WOPISettings() {
|
||||
const wopiPublicKey = getNestedValue(config, 'wopi.publicKey', '');
|
||||
const configRefreshLockInterval = getNestedValue(config, 'wopi.refreshLockInterval', '10m');
|
||||
|
||||
const resetToGlobalConfig = () => {
|
||||
if (config) {
|
||||
setLocalWopiEnabled(configWopiEnabled);
|
||||
const resetToGlobalConfig = source => {
|
||||
const src = source || config;
|
||||
if (src) {
|
||||
const srcEnabled = getNestedValue(src, 'wopi.enable');
|
||||
const srcInterval = getNestedValue(src, 'wopi.refreshLockInterval');
|
||||
setLocalWopiEnabled(srcEnabled);
|
||||
setLocalRotateKeys(false);
|
||||
setLocalRefreshLockInterval(configRefreshLockInterval);
|
||||
setLocalRefreshLockInterval(srcInterval);
|
||||
setHasChanges(false);
|
||||
validateField('wopi.enable', configWopiEnabled);
|
||||
validateField('wopi.refreshLockInterval', configRefreshLockInterval);
|
||||
validateField('wopi.enable', srcEnabled);
|
||||
validateField('wopi.refreshLockInterval', srcInterval);
|
||||
}
|
||||
};
|
||||
|
||||
@ -48,6 +51,12 @@ function WOPISettings() {
|
||||
hasInitialized.current = true;
|
||||
}
|
||||
|
||||
// Sync from Redux when config changes (e.g., after reset), unless user has local edits
|
||||
useEffect(() => {
|
||||
if (config && !hasChanges) {
|
||||
resetToGlobalConfig();
|
||||
}
|
||||
}, [config]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
const handleWopiEnabledChange = enabled => {
|
||||
setLocalWopiEnabled(enabled);
|
||||
// If WOPI is disabled, uncheck rotate keys
|
||||
@ -118,6 +127,20 @@ function WOPISettings() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = async () => {
|
||||
const confirmed = window.confirm('Reset WOPI settings to defaults?');
|
||||
if (!confirmed) return;
|
||||
try {
|
||||
const merged = await dispatch(resetConfig(['wopi.enable', 'wopi.refreshLockInterval'])).unwrap();
|
||||
resetToGlobalConfig(merged);
|
||||
setHasChanges(false);
|
||||
setLocalRotateKeys(false);
|
||||
} catch (e) {
|
||||
console.error('Failed to reset WOPI settings:', e);
|
||||
alert('Failed to reset settings. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${styles.wopiSettings} ${styles.pageWithFixedSave}`}>
|
||||
<PageHeader>WOPI Settings</PageHeader>
|
||||
@ -175,9 +198,20 @@ function WOPISettings() {
|
||||
</>
|
||||
)}
|
||||
|
||||
<FixedSaveButton onClick={handleSave} disabled={!hasChanges || hasValidationErrors()}>
|
||||
Save Changes
|
||||
</FixedSaveButton>
|
||||
<FixedSaveButtonGroup
|
||||
buttons={[
|
||||
{
|
||||
text: 'Save Changes',
|
||||
onClick: handleSave,
|
||||
disabled: !hasChanges || hasValidationErrors()
|
||||
},
|
||||
{
|
||||
text: 'Reset to Defaults',
|
||||
onClick: handleReset,
|
||||
disabled: false
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user