diff --git a/src/actions/user.js b/src/actions/settings.js
similarity index 61%
rename from src/actions/user.js
rename to src/actions/settings.js
index 297f517bc..40cc7a8d2 100644
--- a/src/actions/user.js
+++ b/src/actions/settings.js
@@ -1,5 +1,10 @@
import * as types from '../constants/actionTypes';
+export const setSystemSettings = settings => ({
+ type: types.SYSTEM_SETTINGS_SET,
+ payload: settings,
+});
+
// Set user settings
export const setUserSettings = settings => ({
type: types.USER_SETTINGS_SET,
diff --git a/src/app.js b/src/app.js
index 265c075ec..b892843d6 100644
--- a/src/app.js
+++ b/src/app.js
@@ -10,12 +10,11 @@ import Root from './components/Root';
import { configI18n } from './util/i18n';
import { loadOrgUnitTree } from './actions/orgUnits';
import { loadExternalLayers } from './actions/externalLayers';
-import { setUserSettings } from './actions/user';
+import { setSystemSettings, setUserSettings } from './actions/settings';
import { resizeScreen } from './actions/ui';
import { loadFavorite } from './actions/favorites';
import { getAnalyticalObject } from './actions/analyticalObject';
-import { setBingMapsApiKey } from './actions/basemap';
-import { getUrlParameter } from './util/requests';
+import { getUrlParameter, getSystemSettings } from './util/requests';
log.setLevel(
process.env.NODE_ENV === 'production' ? log.levels.INFO : log.levels.TRACE
@@ -65,13 +64,9 @@ getManifest('manifest.webapp')
return userSettings;
})
.then(configI18n)
+ .then(getSystemSettings)
+ .then(systemSettings => store.dispatch(setSystemSettings(systemSettings)))
.then(init)
- .then(d2 =>
- d2.system.settings.get('keyBingMapsApiKey').then(key => {
- store.dispatch(setBingMapsApiKey(key));
- return d2;
- })
- )
.then(
d2 => {
const mapId = getUrlParameter('id');
diff --git a/src/components/edit/BoundaryDialog.js b/src/components/edit/BoundaryDialog.js
index 2a073ef2b..1eea6fee4 100644
--- a/src/components/edit/BoundaryDialog.js
+++ b/src/components/edit/BoundaryDialog.js
@@ -12,7 +12,7 @@ import OrgUnitLevelSelect from '../orgunits/OrgUnitLevelSelect';
import UserOrgUnitsSelect from '../orgunits/UserOrgUnitsSelect';
import Checkbox from '../core/Checkbox';
import FontStyle from '../core/FontStyle';
-import { layerDialogStyles } from './LayerDialogStyles';
+import layerDialogStyles from './LayerDialogStyles';
import {
setOrgUnitLevels,
@@ -56,10 +56,6 @@ const styles = {
marginLeft: 12,
width: 127,
},
- error: {
- marginTop: 10,
- color: 'red',
- },
};
class BoundaryDialog extends Component {
diff --git a/src/components/edit/EarthEngineDialog.js b/src/components/edit/EarthEngineDialog.js
index 112242ef3..c9d242a4d 100644
--- a/src/components/edit/EarthEngineDialog.js
+++ b/src/components/edit/EarthEngineDialog.js
@@ -12,7 +12,7 @@ import LegendItem from '../layers/legend/LegendItem';
import { setParams, setFilter, setPeriodName } from '../../actions/layerEdit';
import { getColorScale, getColorPalette } from '../../util/colors';
import { createLegend } from '../../loaders/earthEngineLoader';
-import { layerDialogStyles } from './LayerDialogStyles';
+import layerDialogStyles from './LayerDialogStyles';
import legendStyle from '../layers/legend/legendStyle';
const getDatasets = () => ({
diff --git a/src/components/edit/EventDialog.js b/src/components/edit/EventDialog.js
index 439ad7dbb..629983aad 100644
--- a/src/components/edit/EventDialog.js
+++ b/src/components/edit/EventDialog.js
@@ -8,7 +8,7 @@ import TextField from '../core/TextField';
import ProgramSelect from '../program/ProgramSelect';
import ProgramStageSelect from '../program/ProgramStageSelect';
import RelativePeriodSelect from '../periods/RelativePeriodSelect';
-import DatePicker from '../core/DatePicker';
+import StartEndDates from '../periods/StartEndDates';
import Checkbox from '../core/Checkbox';
import FilterGroup from '../filter/FilterGroup';
import ImageSelect from '../core/ImageSelect';
@@ -19,13 +19,11 @@ import OrgUnitTree from '../orgunits/OrgUnitTree';
import UserOrgUnitsSelect from '../orgunits/UserOrgUnitsSelect';
import SelectedOrgUnits from '../orgunits/SelectedOrgUnits';
import {
- DEFAULT_START_DATE,
- DEFAULT_END_DATE,
EVENT_COLOR,
EVENT_RADIUS,
EVENT_BUFFER,
} from '../../constants/layers';
-import { layerDialogStyles } from './LayerDialogStyles';
+import layerDialogStyles from './LayerDialogStyles';
import {
setProgram,
@@ -38,8 +36,6 @@ import {
setUserOrgUnits,
toggleOrgUnit,
setPeriod,
- setStartDate,
- setEndDate,
setAreaRadius,
} from '../../actions/layerEdit';
@@ -68,16 +64,13 @@ const styles = {
paddingTop: 8,
lineHeight: '22px',
},
- error: {
- marginTop: 10,
- color: 'red',
- },
};
export class EventDialog extends Component {
static propTypes = {
areaRadius: PropTypes.number,
columns: PropTypes.array,
+ defaultPeriod: PropTypes.string,
endDate: PropTypes.string,
eventClustering: PropTypes.bool,
eventCoordinateField: PropTypes.string,
@@ -103,8 +96,6 @@ export class EventDialog extends Component {
setUserOrgUnits: PropTypes.func.isRequired,
toggleOrgUnit: PropTypes.func.isRequired,
setPeriod: PropTypes.func.isRequired,
- setStartDate: PropTypes.func.isRequired,
- setEndDate: PropTypes.func.isRequired,
setAreaRadius: PropTypes.func.isRequired,
validateLayer: PropTypes.bool.isRequired,
};
@@ -121,11 +112,9 @@ export class EventDialog extends Component {
const {
rows,
filters,
- startDate,
- endDate,
- setStartDate,
- setEndDate,
+ defaultPeriod,
setOrgUnitRoot,
+ setPeriod,
} = this.props;
const orgUnits = getOrgUnitNodesFromRows(rows);
@@ -136,10 +125,11 @@ export class EventDialog extends Component {
setOrgUnitRoot();
}
- if (!period && !startDate && !endDate) {
- // Set default period (last year)
- setStartDate(DEFAULT_START_DATE);
- setEndDate(DEFAULT_END_DATE);
+ // Set default period from system settings
+ if (!period && defaultPeriod) {
+ setPeriod({
+ id: defaultPeriod,
+ });
}
}
@@ -185,8 +175,6 @@ export class EventDialog extends Component {
setUserOrgUnits,
toggleOrgUnit,
setPeriod,
- setStartDate,
- setEndDate,
setAreaRadius,
} = this.props;
@@ -254,27 +242,13 @@ export class EventDialog extends Component {
onChange={setPeriod}
style={styles.select}
/>
- {period.id === 'START_END_DATES' && [
- ,
- ,
- ]}
- {periodError ? (
-
- {periodError}
-
- ) : null}
+ {period && period.id === 'START_END_DATES' && (
+
+ )}
)}
{tab === 'orgunits' && (
@@ -491,8 +465,6 @@ export default connect(
setUserOrgUnits,
toggleOrgUnit,
setPeriod,
- setStartDate,
- setEndDate,
setAreaRadius,
},
null,
diff --git a/src/components/edit/FacilityDialog.js b/src/components/edit/FacilityDialog.js
index aea059978..dbce7b8b5 100644
--- a/src/components/edit/FacilityDialog.js
+++ b/src/components/edit/FacilityDialog.js
@@ -13,7 +13,7 @@ import OrgUnitTree from '../orgunits/OrgUnitTree';
import OrgUnitGroupSelect from '../orgunits/OrgUnitGroupSelect';
import OrgUnitLevelSelect from '../orgunits/OrgUnitLevelSelect';
import UserOrgUnitsSelect from '../orgunits/UserOrgUnitsSelect';
-import { layerDialogStyles } from './LayerDialogStyles';
+import layerDialogStyles from './LayerDialogStyles';
import {
setOrganisationUnitGroupSet,
@@ -61,10 +61,6 @@ const styles = {
marginTop: 10,
fontSize: 14,
},
- error: {
- marginTop: 10,
- color: 'red',
- },
};
class FacilityDialog extends Component {
diff --git a/src/components/edit/LayerDialogStyles.js b/src/components/edit/LayerDialogStyles.js
index 8bbb4a92e..ce1d0c66c 100644
--- a/src/components/edit/LayerDialogStyles.js
+++ b/src/components/edit/LayerDialogStyles.js
@@ -1,4 +1,4 @@
-export const layerDialogStyles = {
+const layerDialogStyles = {
select: {
width: 'auto',
maxWidth: 300,
@@ -33,4 +33,10 @@ export const layerDialogStyles = {
flex: 1,
margin: 8,
},
+ error: {
+ marginTop: 10,
+ color: 'red',
+ },
};
+
+export default layerDialogStyles;
diff --git a/src/components/edit/LayerEdit.js b/src/components/edit/LayerEdit.js
index 0a7ad6189..78c994b29 100644
--- a/src/components/edit/LayerEdit.js
+++ b/src/components/edit/LayerEdit.js
@@ -50,6 +50,7 @@ const styles = {
class LayerEdit extends Component {
static propTypes = {
layer: PropTypes.object,
+ defaultPeriod: PropTypes.string,
loadLayer: PropTypes.func.isRequired,
cancelLayer: PropTypes.func.isRequired,
setLayerLoading: PropTypes.func.isRequired,
@@ -88,7 +89,7 @@ class LayerEdit extends Component {
};
render() {
- const { layer, cancelLayer, classes } = this.props;
+ const { layer, defaultPeriod, cancelLayer, classes } = this.props;
if (!layer) {
return null;
@@ -114,6 +115,7 @@ class LayerEdit extends Component {
@@ -152,8 +154,9 @@ class LayerEdit extends Component {
}
export default connect(
- state => ({
- layer: state.layerEdit,
+ ({ layerEdit, settings }) => ({
+ layer: layerEdit,
+ defaultPeriod: settings.system.keyAnalysisRelativePeriod,
}),
{ loadLayer, cancelLayer, setLayerLoading }
)(withStyles(styles)(LayerEdit));
diff --git a/src/components/edit/TrackedEntityDialog.js b/src/components/edit/TrackedEntityDialog.js
index 6042bad88..66305a669 100644
--- a/src/components/edit/TrackedEntityDialog.js
+++ b/src/components/edit/TrackedEntityDialog.js
@@ -7,10 +7,10 @@ import Tabs from '../core/Tabs';
import Tab from '../core/Tab';
import TextField from '../core/TextField';
import SelectField from '../core/SelectField';
-import DatePicker from '../core/DatePicker';
import Checkbox from '../core/Checkbox';
import TrackedEntityTypeSelect from '../trackedEntity/TrackedEntityTypeSelect';
import ProgramSelect from '../program/ProgramSelect';
+import StartEndDates from '../periods/StartEndDates';
import OrgUnitTree from '../orgunits/OrgUnitTree';
import SelectedOrgUnits from '../orgunits/SelectedOrgUnits';
import ColorPicker from '../core/ColorPicker';
@@ -24,7 +24,7 @@ import {
TEI_RELATIONSHIP_LINE_COLOR,
TEI_RELATED_RADIUS,
} from '../../constants/layers';
-import { layerDialogStyles } from './LayerDialogStyles';
+import layerDialogStyles from './LayerDialogStyles';
import {
setTrackedEntityType,
@@ -63,10 +63,6 @@ const styles = {
indent: {
marginLeft: 24,
},
- error: {
- marginTop: 12,
- color: 'red',
- },
};
export class TrackedEntityDialog extends Component {
@@ -194,8 +190,6 @@ export class TrackedEntityDialog extends Component {
setProgramStatus,
setFollowUpStatus,
setTrackedEntityRelationshipType,
- setStartDate,
- setEndDate,
toggleOrgUnit,
setOrgUnitMode,
setEventPointColor,
@@ -366,23 +360,11 @@ export class TrackedEntityDialog extends Component {
{periodHelp}:
-
-
- {periodError ? (
- {periodError}
- ) : null}
)}
diff --git a/src/components/edit/thematic/ThematicDialog.js b/src/components/edit/thematic/ThematicDialog.js
index 47552c487..3f56e102b 100644
--- a/src/components/edit/thematic/ThematicDialog.js
+++ b/src/components/edit/thematic/ThematicDialog.js
@@ -27,14 +27,12 @@ import RenderingStrategy from '../../periods/RenderingStrategy';
import ProgramSelect from '../../program/ProgramSelect';
import ProgramIndicatorSelect from '../../program/ProgramIndicatorSelect';
import RelativePeriodSelect from '../../periods/RelativePeriodSelect';
-import DatePicker from '../../core/DatePicker';
+import StartEndDates from '../../periods/StartEndDates';
import UserOrgUnitsSelect from '../../orgunits/UserOrgUnitsSelect';
import DimensionFilter from '../../dimensions/DimensionFilter';
-import { layerDialogStyles } from '../LayerDialogStyles';
+import layerDialogStyles from '../LayerDialogStyles';
import { dimConf } from '../../../constants/dimension';
import {
- DEFAULT_START_DATE,
- DEFAULT_END_DATE,
DEFAULT_ORG_UNIT_LEVEL,
DEFAULT_RADIUS_LOW,
DEFAULT_RADIUS_HIGH,
@@ -59,8 +57,6 @@ import {
setPeriod,
setPeriodType,
setRenderingStrategy,
- setStartDate,
- setEndDate,
setProgram,
setRadiusLow,
setRadiusHigh,
@@ -99,10 +95,6 @@ const styles = {
font: {
float: 'left',
},
- error: {
- marginTop: 10,
- color: 'red',
- },
};
export class ThematicDialog extends Component {
@@ -121,6 +113,7 @@ export class ThematicDialog extends Component {
dataElementGroup: PropTypes.object,
program: PropTypes.object,
operand: PropTypes.string,
+ defaultPeriod: PropTypes.string,
startDate: PropTypes.string,
endDate: PropTypes.string,
periodType: PropTypes.string,
@@ -141,8 +134,6 @@ export class ThematicDialog extends Component {
setLegendSet: PropTypes.func.isRequired,
setOperand: PropTypes.func.isRequired,
setPeriod: PropTypes.func.isRequired,
- setStartDate: PropTypes.func.isRequired,
- setEndDate: PropTypes.func.isRequired,
setOrgUnitLevels: PropTypes.func.isRequired,
setOrgUnitGroups: PropTypes.func.isRequired,
toggleOrgUnit: PropTypes.func.isRequired,
@@ -165,16 +156,16 @@ export class ThematicDialog extends Component {
const {
valueType,
columns,
+ rows,
+ filters,
setValueType,
- startDate,
- endDate,
- setStartDate,
- setEndDate,
+ defaultPeriod,
+ setPeriod,
setOrgUnitLevels,
- rows,
} = this.props;
const dataItem = getDataItemFromColumns(columns);
+ const period = getPeriodFromFilters(filters);
// Set value type if favorite is loaded
if (!valueType) {
@@ -191,10 +182,11 @@ export class ThematicDialog extends Component {
}
}
- // Set default period (last year)
- if (!startDate && !endDate) {
- setStartDate(DEFAULT_START_DATE);
- setEndDate(DEFAULT_END_DATE);
+ // Set default period from system settings
+ if (!period && defaultPeriod) {
+ setPeriod({
+ id: defaultPeriod,
+ });
}
// Set default org unit level
@@ -282,8 +274,6 @@ export class ThematicDialog extends Component {
setOrgUnitLevels,
setOrgUnitGroups,
setPeriod,
- setStartDate,
- setEndDate,
setPeriodType,
setRenderingStrategy,
setProgram,
@@ -499,27 +489,13 @@ export class ThematicDialog extends Component {
errorText={periodError}
/>
)}
- {periodType === 'StartEndDates' && [
- ,
- ,
- periodError ? (
-
- {periodError}
-
- ) : null,
- ]}
+ {periodType === 'StartEndDates' && (
+
+ )}
{periodType === 'relativePeriods' && (
({
+export default connect(({ map, download, settings }) => ({
name: map.name,
interpretationDate: map.interpretationDate,
showName: download.showDialog ? download.showName : true,
- uiLocale: userSettings.keyUiLocale,
+ uiLocale: settings.user.keyUiLocale,
}))(withStyles(styles)(MapName));
diff --git a/src/components/periods/PeriodSelect.js b/src/components/periods/PeriodSelect.js
index 65cd9db26..f31e05459 100644
--- a/src/components/periods/PeriodSelect.js
+++ b/src/components/periods/PeriodSelect.js
@@ -157,6 +157,6 @@ class PeriodSelect extends Component {
};
}
-export default connect(state => ({
- locale: state.userSettings.keyUiLocale,
+export default connect(({ settings }) => ({
+ locale: settings.user.keyUiLocale,
}))(withStyles(styles)(PeriodSelect));
diff --git a/src/components/periods/StartEndDates.js b/src/components/periods/StartEndDates.js
new file mode 100644
index 000000000..94423e26e
--- /dev/null
+++ b/src/components/periods/StartEndDates.js
@@ -0,0 +1,51 @@
+import React, { Fragment, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import i18n from '@dhis2/d2-i18n';
+import DatePicker from '../core/DatePicker';
+import { setStartDate, setEndDate } from '../../actions/layerEdit';
+import { DEFAULT_START_DATE, DEFAULT_END_DATE } from '../../constants/layers';
+import styles from '../edit/LayerDialogStyles';
+
+const StartEndDates = props => {
+ const { startDate, endDate, setStartDate, setEndDate, errorText } = props;
+
+ useEffect(() => {
+ if (!startDate && !endDate) {
+ setStartDate(DEFAULT_START_DATE);
+ setEndDate(DEFAULT_END_DATE);
+ }
+ }, [startDate, endDate, setStartDate, setEndDate]);
+
+ return startDate && endDate ? (
+
+
+
+ {errorText && (
+
+ {errorText}
+
+ )}
+
+ ) : null;
+};
+
+StartEndDates.propTypes = {
+ startDate: PropTypes.string,
+ endDate: PropTypes.string,
+ errorText: PropTypes.string,
+ setStartDate: PropTypes.func.isRequired,
+ setEndDate: PropTypes.func.isRequired,
+};
+
+export default connect(null, { setStartDate, setEndDate })(StartEndDates);
diff --git a/src/constants/actionTypes.js b/src/constants/actionTypes.js
index 156b5fdac..c16bdc0ad 100644
--- a/src/constants/actionTypes.js
+++ b/src/constants/actionTypes.js
@@ -288,9 +288,6 @@ export const EARTH_ENGINE_COLLECTION_LOAD_ERROR =
'EARTH_ENGINE_COLLECTION_LOAD_ERROR';
export const EARTH_ENGINE_COLLECTION_SET = 'EARTH_ENGINE_COLLECTION_SET';
-/* USER */
-export const USER_SETTINGS_SET = 'USER_SETTINGS_SET';
-
/* MESSAGE (SNACKBAR) */
export const MESSAGE_SET = 'MESSAGE_SET';
export const MESSAGE_CLEAR = 'MESSAGE_CLEAR';
@@ -324,3 +321,7 @@ export const ANALYTICAL_OBJECT_GET = 'ANALYTICAL_OBJECT_GET';
export const ANALYTICAL_OBJECT_SET = 'ANALYTICAL_OBJECT_SET';
export const ANALYTICAL_OBJECT_CLEAR = 'ANALYTICAL_OBJECT_CLEAR';
export const ANALYTICAL_OBJECT_FAILURE = 'ANALYTICAL_OBJECT_FAILURE';
+
+/* SETTINGS */
+export const SYSTEM_SETTINGS_SET = 'SYSTEM_SETTINGS_SET';
+export const USER_SETTINGS_SET = 'USER_SETTINGS_SET';
diff --git a/src/constants/settings.js b/src/constants/settings.js
new file mode 100644
index 000000000..b23e715eb
--- /dev/null
+++ b/src/constants/settings.js
@@ -0,0 +1,4 @@
+export const SYSTEM_SETTINGS = [
+ 'keyAnalysisRelativePeriod',
+ 'keyBingMapsApiKey',
+];
diff --git a/src/reducers/basemaps.js b/src/reducers/basemaps.js
index 5d7a21975..101a2e5c6 100644
--- a/src/reducers/basemaps.js
+++ b/src/reducers/basemaps.js
@@ -3,6 +3,8 @@ import { defaultBasemaps } from '../constants/basemaps';
import * as types from '../constants/actionTypes';
const basemaps = (state = defaultBasemaps(), action) => {
+ let bingMapsKey;
+
switch (action.type) {
case types.BASEMAP_ADD:
return [...state, action.payload];
@@ -10,9 +12,11 @@ const basemaps = (state = defaultBasemaps(), action) => {
case types.BASEMAP_REMOVE:
return state.filter(basemap => basemap.id !== action.id);
- case types.BASEMAP_BING_KEY_SET:
+ case types.SYSTEM_SETTINGS_SET:
+ bingMapsKey = action.payload && action.payload.keyBingMapsApiKey;
+
// Remove Bing basemaps is no key is provided
- if (!action.key) {
+ if (!bingMapsKey) {
return state.filter(layer => layer.config.type !== 'bingLayer');
}
@@ -26,7 +30,7 @@ const basemaps = (state = defaultBasemaps(), action) => {
...layer,
config: {
...layer.config,
- apiKey: action.key,
+ apiKey: bingMapsKey,
},
};
});
diff --git a/src/reducers/index.js b/src/reducers/index.js
index 619f2a0a4..bc5382c84 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -34,7 +34,7 @@ import programStageDataElements from './programStageDataElements';
import programTrackedEntityAttributes from './programTrackedEntityAttributes';
import relocate from './relocate';
import ui from './ui';
-import userSettings from './userSettings';
+import settings from './settings';
import trackedEntityTypes from './trackedEntityTypes';
import dataDownload from './dataDownload';
@@ -74,7 +74,7 @@ export default combineReducers({
programTrackedEntityAttributes,
relocate,
ui,
- userSettings,
+ settings,
trackedEntityTypes,
dataDownload,
});
diff --git a/src/reducers/settings.js b/src/reducers/settings.js
new file mode 100644
index 000000000..f326a5784
--- /dev/null
+++ b/src/reducers/settings.js
@@ -0,0 +1,29 @@
+import * as types from '../constants/actionTypes';
+
+const defaultState = {
+ system: {
+ keyAnalysisRelativePeriod: 'LAST_12_MONTHS',
+ },
+ user: {},
+};
+
+const userSettings = (state = defaultState, action) => {
+ switch (action.type) {
+ case types.SYSTEM_SETTINGS_SET:
+ return {
+ ...state,
+ system: action.payload,
+ };
+
+ case types.USER_SETTINGS_SET:
+ return {
+ ...state,
+ user: action.payload,
+ };
+
+ default:
+ return state;
+ }
+};
+
+export default userSettings;
diff --git a/src/reducers/userSettings.js b/src/reducers/userSettings.js
deleted file mode 100644
index 801012462..000000000
--- a/src/reducers/userSettings.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import * as types from '../constants/actionTypes';
-
-const userSettings = (state = null, action) => {
- switch (action.type) {
- case types.USER_SETTINGS_SET:
- return action.payload;
-
- default:
- return state;
- }
-};
-
-export default userSettings;
diff --git a/src/util/requests.js b/src/util/requests.js
index 8877bf967..9a6557727 100644
--- a/src/util/requests.js
+++ b/src/util/requests.js
@@ -1,6 +1,8 @@
import { getInstance as getD2 } from 'd2';
import { mapFields } from './helpers';
import { isString, isObject, sortBy } from 'lodash/fp';
+import { apiFetch } from './api';
+import { SYSTEM_SETTINGS } from '../constants/settings';
// API requests
@@ -31,6 +33,10 @@ export const getBingMapsApiKey = async () => {
return d2.system.settings.get('keyBingMapsApiKey');
};
+// Returns system settings for keys (d2 returns one or all)
+export const getSystemSettings = () =>
+ apiFetch(`/systemSettings/?key=${SYSTEM_SETTINGS.join(',')}`);
+
// Different ways of specifying a basemap - TODO: simplify!
const getBasemap = config => {
const externalBasemap = config.mapViews.find(