Projects STRLCPY opencti Commits 69152d96
🤬
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/Countries.js
    1  -import React, { Component } from 'react';
    2  -import * as PropTypes from 'prop-types';
    3  -import { compose, propOr } from 'ramda';
    4  -import { withRouter } from 'react-router-dom';
    5  -import withStyles from '@mui/styles/withStyles';
    6  -import { QueryRenderer } from '../../../relay/environment';
    7  -import {
    8  - buildViewParamsFromUrlAndStorage,
    9  - saveViewParameters,
    10  -} from '../../../utils/ListParameters';
    11  -import inject18n from '../../../components/i18n';
    12  -import RegionsLines, { regionsLinesQuery } from './regions/RegionsLines';
    13  -import SearchInput from '../../../components/SearchInput';
    14  - 
    15  -const styles = () => ({
    16  - parameters: {
    17  - float: 'left',
    18  - marginTop: -10,
    19  - },
    20  -});
    21  - 
    22  -class Countries extends Component {
    23  - constructor(props) {
    24  - super(props);
    25  - const params = buildViewParamsFromUrlAndStorage(
    26  - props.history,
    27  - props.location,
    28  - 'view-regions',
    29  - );
    30  - this.state = {
    31  - searchTerm: propOr('', 'searchTerm', params),
    32  - openExports: false,
    33  - };
    34  - }
    35  - 
    36  - saveView() {
    37  - saveViewParameters(
    38  - this.props.history,
    39  - this.props.location,
    40  - 'view-regions',
    41  - this.state,
    42  - );
    43  - }
    44  - 
    45  - handleSearch(value) {
    46  - this.setState({ searchTerm: value }, () => this.saveView());
    47  - }
    48  - 
    49  - handleToggleExports() {
    50  - this.setState({ openExports: !this.state.openExports });
    51  - }
    52  - 
    53  - render() {
    54  - const { searchTerm } = this.state;
    55  - const { classes } = this.props;
    56  - return (
    57  - <div>
    58  - <div className={classes.parameters}>
    59  - <div style={{ float: 'left', marginRight: 20 }}>
    60  - <SearchInput
    61  - variant="small"
    62  - onSubmit={this.handleSearch.bind(this)}
    63  - keyword={searchTerm}
    64  - />
    65  - </div>
    66  - </div>
    67  - <div className="clearfix" />
    68  - <QueryRenderer
    69  - query={regionsLinesQuery}
    70  - variables={{ count: 500 }}
    71  - render={({ props }) => (
    72  - <RegionsLines data={props} keyword={searchTerm} />
    73  - )}
    74  - />
    75  - </div>
    76  - );
    77  - }
    78  -}
    79  - 
    80  -Countries.propTypes = {
    81  - t: PropTypes.func,
    82  - history: PropTypes.object,
    83  - location: PropTypes.object,
    84  - classes: PropTypes.object,
    85  -};
    86  - 
    87  -export default compose(inject18n, withRouter, withStyles(styles))(Countries);
    88  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/Countries.tsx
     1 +import React, { FunctionComponent, useState } from 'react';
     2 +import makeStyles from '@mui/styles/makeStyles';
     3 +import { QueryRenderer } from '../../../relay/environment';
     4 +import { useFormatter } from '../../../components/i18n';
     5 +import CountriesLines, { countriesLinesQuery } from './countries/CountriesLines';
     6 +import useLocalStorage, { localStorageToPaginationOptions } from '../../../utils/hooks/useLocalStorage';
     7 +import { PaginationOptions } from '../../../components/list_lines';
     8 +import ListLines from '../../../components/list_lines/ListLines';
     9 +import Security, { KNOWLEDGE_KNUPDATE } from '../../../utils/Security';
     10 +import CountryCreation from './countries/CountryCreation';
     11 + 
     12 +const useStyles = makeStyles(() => ({
     13 + parameters: {
     14 + float: 'left',
     15 + marginTop: -60,
     16 + },
     17 +}));
     18 + 
     19 +const LOCAL_STORAGE_KEY = 'view-country';
     20 + 
     21 +interface CountryProps {
     22 + history: History,
     23 + location: Location,
     24 + paginationOptions: PaginationOptions,
     25 + exportEntityType: string,
     26 +}
     27 + 
     28 +const Countries: FunctionComponent<CountryProps> = ({ history, location, exportEntityType }) => {
     29 + const classes = useStyles();
     30 + const { t } = useFormatter();
     31 + const [openExports, setOpenExports] = useState(false);
     32 + 
     33 + const [viewStorage, setViewStorage] = useLocalStorage(LOCAL_STORAGE_KEY, {
     34 + searchTerm: '',
     35 + sortBy: 'name',
     36 + orderAsc: false,
     37 + openExports: false,
     38 + });
     39 + const { searchTerm, sortBy, orderAsc } = viewStorage;
     40 + 
     41 + const queryProps = localStorageToPaginationOptions(viewStorage);
     42 + 
     43 + const handleSearch = (value: string) => {
     44 + setViewStorage((c) => ({ ...c, searchTerm: value }));
     45 + };
     46 + const handleSort = (field: string, order: boolean) => setViewStorage((c) => ({
     47 + ...c,
     48 + sortBy: field,
     49 + orderAsc: order,
     50 + }));
     51 + const handleToggleExports = () => {
     52 + setOpenExports((current) => !current);
     53 + };
     54 + const dataColumns = {
     55 + name: {
     56 + label: 'Name',
     57 + width: '100%',
     58 + isSortable: true,
     59 + },
     60 + };
     61 + const renderLines = (paginationOptions: PaginationOptions) => (
     62 + <ListLines
     63 + sortBy={sortBy}
     64 + orderAsc={orderAsc}
     65 + dataColumns={dataColumns}
     66 + handleSort={handleSort}
     67 + handleSearch={handleSearch}
     68 + handleToggleExports={handleToggleExports}
     69 + keyword={searchTerm}
     70 + paginationOptions={paginationOptions}
     71 + exportEntityType={'Country'}
     72 + openExports={openExports}
     73 + >
     74 + <QueryRenderer
     75 + query={countriesLinesQuery}
     76 + variables={{ count: 25, ...paginationOptions }}
     77 + render={({ props }: { props: any }) => (
     78 + <CountriesLines
     79 + data={props}
     80 + paginationOptions={paginationOptions}
     81 + dataColumns={dataColumns}
     82 + />
     83 + )}
     84 + />
     85 + </ListLines>
     86 + );
     87 + return (
     88 + <div>
     89 + {renderLines(queryProps)}
     90 + <Security needs={[KNOWLEDGE_KNUPDATE]}>
     91 + <CountryCreation paginationOptions={queryProps}/>
     92 + </Security>
     93 + </div>
     94 + );
     95 +};
     96 + 
     97 +export default Countries;
     98 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountriesLines.tsx
     1 +import React, { FunctionComponent } from 'react';
     2 +import { createPaginationContainer, graphql, RelayPaginationProp } from 'react-relay';
     3 +import { DataColumns, PaginationOptions } from '../../../../components/list_lines';
     4 +import ListLinesContent from '../../../../components/list_lines/ListLinesContent';
     5 +import { CountryLine, CountryLineDummy } from './CountryLine';
     6 +import { CountriesLines_data$data } from './__generated__/CountriesLines_data.graphql';
     7 + 
     8 +interface CountriesLinesProps {
     9 + data?: CountriesLines_data$data,
     10 + dataColumns: DataColumns,
     11 + paginationOptions: PaginationOptions,
     12 + initialLoading?: boolean
     13 + relay: RelayPaginationProp
     14 +}
     15 + 
     16 +const CountriesLines: FunctionComponent<CountriesLinesProps> = ({ relay, data, dataColumns, paginationOptions, initialLoading }) => {
     17 + return (
     18 + <ListLinesContent
     19 + initialLoading={initialLoading}
     20 + isLoading={relay.isLoading}
     21 + loadMore={relay.loadMore}
     22 + hasMore={relay.hasMore}
     23 + dataList={data?.countries?.edges ?? []}
     24 + LineComponent={ CountryLine }
     25 + DummyLineComponent={ <CountryLineDummy /> }
     26 + dataColumns={dataColumns}
     27 + paginationOptions={paginationOptions}
     28 + />
     29 + );
     30 +};
     31 + 
     32 +export const countriesLinesQuery = graphql`
     33 + query CountriesLinesPaginationQuery(
     34 + $search: String
     35 + $count: Int!
     36 + $cursor: ID
     37 + $orderBy: CountriesOrdering
     38 + $orderMode: OrderingMode
     39 + $filters: [CountriesFiltering]
     40 + ) {
     41 + ...CountriesLines_data @arguments(
     42 + search: $search
     43 + count: $count
     44 + cursor: $cursor
     45 + orderBy: $orderBy
     46 + orderMode: $orderMode
     47 + filters: $filters
     48 + )
     49 + }
     50 +`;
     51 + 
     52 +export default createPaginationContainer(
     53 + CountriesLines,
     54 + {
     55 + data: graphql`
     56 + fragment CountriesLines_data on Query
     57 + @argumentDefinitions(
     58 + search: { type: "String" }
     59 + count: { type: "Int", defaultValue: 25 }
     60 + cursor: { type: "ID" }
     61 + orderBy: { type: "CountriesOrdering", defaultValue: name }
     62 + orderMode: { type: "OrderingMode", defaultValue: asc }
     63 + filters: { type: "[CountriesFiltering]" }
     64 + ) {
     65 + countries(
     66 + search: $search
     67 + first: $count
     68 + after: $cursor
     69 + orderBy: $orderBy
     70 + orderMode: $orderMode
     71 + filters: $filters
     72 + )
     73 + @connection(key: "Pagination_countries") {
     74 + edges {
     75 + node {
     76 + id
     77 + name
     78 + description
     79 + ...CountryLine_node
     80 + }
     81 + }
     82 + pageInfo {
     83 + endCursor
     84 + hasNextPage
     85 + globalCount
     86 + }
     87 + }
     88 + }
     89 + `,
     90 + },
     91 + {
     92 + direction: 'forward',
     93 + getConnectionFromProps(props) {
     94 + return props.data && props.data.countries;
     95 + },
     96 + getFragmentVariables(prevVars, totalCount) {
     97 + return {
     98 + ...prevVars,
     99 + count: totalCount,
     100 + };
     101 + },
     102 + getVariables(props, { count, cursor }, fragmentVariables) {
     103 + return {
     104 + search: fragmentVariables.search,
     105 + count,
     106 + cursor,
     107 + orderBy: fragmentVariables.orderBy,
     108 + orderMode: fragmentVariables.orderMode,
     109 + filters: fragmentVariables.filters,
     110 + };
     111 + },
     112 + query: countriesLinesQuery,
     113 + },
     114 +);
     115 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryCreation.tsx
     1 +import React, { FunctionComponent, useState } from 'react';
     2 +import { Field, Form, Formik } from 'formik';
     3 +import Drawer from '@mui/material/Drawer';
     4 +import Typography from '@mui/material/Typography';
     5 +import Button from '@mui/material/Button';
     6 +import IconButton from '@mui/material/IconButton';
     7 +import makeStyles from '@mui/styles/makeStyles';
     8 +import Fab from '@mui/material/Fab';
     9 +import { Add, Close } from '@mui/icons-material';
     10 +import * as Yup from 'yup';
     11 +import { graphql } from 'react-relay';
     12 +import { FormikHelpers } from 'formik/dist/types';
     13 +import { ConnectionHandler, RecordProxy, RecordSourceSelectorProxy } from 'relay-runtime';
     14 +import { useFormatter } from '../../../../components/i18n';
     15 +import { commitMutation, handleErrorInForm } from '../../../../relay/environment';
     16 +import TextField from '../../../../components/TextField';
     17 +import CreatedByField, { CreatedBy } from '../../common/form/CreatedByField';
     18 +import ObjectLabelField, { ObjectLabel } from '../../common/form/ObjectLabelField';
     19 +import ObjectMarkingField, { ObjectMarking } from '../../common/form/ObjectMarkingField';
     20 +import MarkDownField from '../../../../components/MarkDownField';
     21 +import { Theme } from '../../../../components/Theme';
     22 +import { PaginationOptions } from '../../../../components/list_lines';
     23 +import ExternalReferencesField, { ExternalReference } from '../../common/form/ExternalReferencesField';
     24 + 
     25 +const styles = makeStyles<Theme>((theme) => ({
     26 + drawerPaper: {
     27 + minHeight: '100vh',
     28 + width: '50%',
     29 + position: 'fixed',
     30 + transition: theme.transitions.create('width', {
     31 + easing: theme.transitions.easing.sharp,
     32 + duration: theme.transitions.duration.enteringScreen,
     33 + }),
     34 + padding: 0,
     35 + },
     36 + dialogActions: {
     37 + padding: '0 17px 20px 0',
     38 + },
     39 + createButton: {
     40 + position: 'fixed',
     41 + bottom: 30,
     42 + right: 30,
     43 + },
     44 + createButtonContextual: {
     45 + position: 'fixed',
     46 + bottom: 30,
     47 + right: 30,
     48 + zIndex: 2000,
     49 + },
     50 + buttons: {
     51 + marginTop: 20,
     52 + textAlign: 'right',
     53 + },
     54 + button: {
     55 + marginLeft: theme.spacing(2),
     56 + },
     57 + header: {
     58 + backgroundColor: theme.palette.background.nav,
     59 + padding: '20px 20px 20px 60px',
     60 + },
     61 + closeButton: {
     62 + position: 'absolute',
     63 + top: 12,
     64 + left: 5,
     65 + color: 'inherit',
     66 + },
     67 + importButton: {
     68 + position: 'absolute',
     69 + top: 15,
     70 + right: 20,
     71 + },
     72 + container: {
     73 + padding: '10px 20px 20px 20px',
     74 + },
     75 +}));
     76 + 
     77 +const countryMutation = graphql`
     78 + mutation CountryCreationMutation($input: CountryAddInput!) {
     79 + countryAdd(input: $input) {
     80 + ...CountryLine_node
     81 + }
     82 + }
     83 +`;
     84 + 
     85 +const countryValidation = (t: (message: string) => string) => Yup.object()
     86 + .shape({
     87 + name: Yup.string()
     88 + .required(t('This field is required')),
     89 + description: Yup.string()
     90 + .min(3, t('The value is too short'))
     91 + .max(5000, t('The value is too long'))
     92 + .required(t('This field is required')),
     93 + });
     94 + 
     95 +const sharedUpdater = (store: RecordSourceSelectorProxy, userId: string, paginationOptions: PaginationOptions, newEdge: RecordProxy) => {
     96 + const userProxy = store.get(userId);
     97 + if (userProxy) {
     98 + const conn: RecordProxy | null | undefined = ConnectionHandler.getConnection(
     99 + userProxy,
     100 + 'Pagination_countries',
     101 + paginationOptions,
     102 + );
     103 + if (conn) {
     104 + ConnectionHandler.insertEdgeBefore(conn, newEdge);
     105 + }
     106 + }
     107 +};
     108 + 
     109 +interface CountryCreationProps {
     110 + paginationOptions: PaginationOptions
     111 +}
     112 + 
     113 +interface CountriesFormProps {
     114 + name: string,
     115 + description: string,
     116 + createdBy: CreatedBy,
     117 + objectMarking: Array<ObjectMarking>,
     118 + objectLabel: Array<ObjectLabel>,
     119 + externalReferences: Array<ExternalReference>
     120 +}
     121 + 
     122 +const CountryCreation: FunctionComponent<CountryCreationProps> = ({
     123 + paginationOptions,
     124 +}) => {
     125 + const { t } = useFormatter();
     126 + const classes = styles();
     127 + 
     128 + const [open, setOpen] = useState(false);
     129 + const initialValues: CountriesFormProps = {
     130 + name: '',
     131 + description: '',
     132 + createdBy: {} as CreatedBy,
     133 + objectMarking: [],
     134 + objectLabel: [],
     135 + externalReferences: [],
     136 + };
     137 + 
     138 + const handleOpen = () => {
     139 + setOpen(true);
     140 + };
     141 + 
     142 + const handleClose = () => {
     143 + setOpen(false);
     144 + };
     145 + 
     146 + const onSubmit = (
     147 + values: CountriesFormProps,
     148 + { setSubmitting, setErrors, resetForm }: FormikHelpers<CountriesFormProps>,
     149 + ) => {
     150 + const finalValues = {
     151 + name: values.name,
     152 + description: values.description,
     153 + createdBy: values.createdBy?.value,
     154 + objectMarking: values.objectMarking.map((v) => v.value),
     155 + objectLabel: values.objectLabel.map((v) => v.value),
     156 + externalReferences: values.objectLabel.map((v) => v.value),
     157 + };
     158 + commitMutation({
     159 + mutation: countryMutation,
     160 + variables: {
     161 + input: finalValues,
     162 + },
     163 + updater: (store: RecordSourceSelectorProxy) => {
     164 + const payload: RecordProxy | null = store.getRootField('countryAdd');
     165 + if (payload) {
     166 + const newEdge: RecordProxy = payload.setLinkedRecord(payload, 'node');
     167 + const container: RecordProxy = store.getRoot();
     168 + sharedUpdater(
     169 + store,
     170 + container.getDataID(),
     171 + paginationOptions,
     172 + newEdge,
     173 + );
     174 + }
     175 + },
     176 + onError: (error: Error) => {
     177 + handleErrorInForm(error, setErrors);
     178 + setSubmitting(false);
     179 + },
     180 + setSubmitting,
     181 + onCompleted: () => {
     182 + setSubmitting(false);
     183 + resetForm();
     184 + handleClose();
     185 + },
     186 + optimisticUpdater: undefined,
     187 + optimisticResponse: undefined,
     188 + });
     189 + };
     190 + 
     191 + const onReset = () => {
     192 + handleClose();
     193 + };
     194 + 
     195 + return (
     196 + <div>
     197 + <Fab
     198 + onClick={handleOpen}
     199 + color="secondary"
     200 + aria-label="Add"
     201 + className={classes.createButton}
     202 + >
     203 + <Add />
     204 + </Fab>
     205 + <Drawer
     206 + open={open}
     207 + anchor="right"
     208 + elevation={1}
     209 + sx={{ zIndex: 1202 }}
     210 + classes={{ paper: classes.drawerPaper }}
     211 + onClose={handleClose}
     212 + >
     213 + <div className={classes.header}>
     214 + <IconButton
     215 + aria-label="Close"
     216 + className={classes.closeButton}
     217 + onClick={handleClose}
     218 + size="large"
     219 + color="primary"
     220 + >
     221 + <Close fontSize="small" color="primary" />
     222 + </IconButton>
     223 + <Typography variant="h6">{t('Create a Country')}</Typography>
     224 + </div>
     225 + <div className={classes.container}>
     226 + <Formik
     227 + initialValues={initialValues}
     228 + validationSchema={countryValidation(t)}
     229 + onSubmit={onSubmit}
     230 + onReset={onReset}
     231 + >
     232 + {({
     233 + submitForm,
     234 + handleReset,
     235 + isSubmitting,
     236 + setFieldValue,
     237 + values,
     238 + }) => (
     239 + <Form style={{ margin: '20px 0 20px 0' }}>
     240 + <Field
     241 + component={TextField}
     242 + variant="standard"
     243 + name="name"
     244 + label={t('Name')}
     245 + fullWidth={true}
     246 + detectDuplicate={['Country']}
     247 + />
     248 + <Field
     249 + component={MarkDownField}
     250 + name="description"
     251 + label={t('Description')}
     252 + fullWidth={true}
     253 + multiline={true}
     254 + rows="4"
     255 + style={{ marginTop: 20 }}
     256 + />
     257 + <CreatedByField
     258 + name="createdBy"
     259 + style={{
     260 + marginTop: 20,
     261 + width: '100%',
     262 + }}
     263 + setFieldValue={setFieldValue}
     264 + />
     265 + <ObjectLabelField
     266 + name="objectLabel"
     267 + style={{
     268 + marginTop: 20,
     269 + width: '100%',
     270 + }}
     271 + setFieldValue={setFieldValue}
     272 + values={values.objectLabel}
     273 + />
     274 + <ObjectMarkingField
     275 + name="objectMarking"
     276 + style={{
     277 + marginTop: 20,
     278 + width: '100%',
     279 + }}
     280 + />
     281 + <ExternalReferencesField
     282 + name="externalReferences"
     283 + style={{
     284 + marginTop: 20,
     285 + width: '100%',
     286 + }}
     287 + setFieldValue={setFieldValue}
     288 + values={values.externalReferences}
     289 + />
     290 + <div className={classes.buttons}>
     291 + <Button
     292 + variant="contained"
     293 + onClick={handleReset}
     294 + disabled={isSubmitting}
     295 + classes={{ root: classes.button }}
     296 + >
     297 + {t('Cancel')}
     298 + </Button>
     299 + <Button
     300 + variant="contained"
     301 + color="secondary"
     302 + onClick={submitForm}
     303 + disabled={isSubmitting}
     304 + classes={{ root: classes.button }}
     305 + >
     306 + {t('Create')}
     307 + </Button>
     308 + </div>
     309 + </Form>
     310 + )}
     311 + </Formik>
     312 + </div>
     313 + </Drawer>
     314 + </div>
     315 + );
     316 +};
     317 + 
     318 +export default CountryCreation;
     319 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryLine.js
    skipped 7 lines
    8 8  import { KeyboardArrowRightOutlined, FlagOutlined } from '@mui/icons-material';
    9 9  import { compose } from 'ramda';
    10 10  import Skeleton from '@mui/material/Skeleton';
     11 +import { createFragmentContainer, graphql } from 'react-relay';
    11 12  import inject18n from '../../../../components/i18n';
    12 13   
    13 14  const styles = (theme) => ({
    skipped 48 lines
    62 63   classes: PropTypes.object,
    63 64   fd: PropTypes.func,
    64 65  };
     66 + 
     67 +const CountryLineFragment = createFragmentContainer(CountryLineComponent, {
     68 + node: graphql`
     69 + fragment CountryLine_node on Country {
     70 + id
     71 + name
     72 + created
     73 + modified
     74 + }
     75 + `,
     76 +});
    65 77   
    66 78  export const CountryLine = compose(
    67 79   inject18n,
    skipped 73 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/nav/TopMenuLocation.js
    skipped 53 lines
    54 54   <MapOutlined className={classes.icon} fontSize="small" />
    55 55   {t('Countries')}
    56 56   </Button>)}
    57  - {/* {!helper.isEntityTypeHidden('Regions') && (
     57 + {!helper.isEntityTypeHidden('Regions') && (
    58 58   <Button
    59 59   component={Link}
    60 60   to="/dashboard/locations/regions"
    skipped 17 lines
    78 78   {t('Regions')}
    79 79   </Button>
    80 80   )}
    81  - {!helper.isEntityTypeHidden('Areas') && (
     81 + {/*{!helper.isEntityTypeHidden('Areas') && (
    82 82   <Button
    83 83   component={Link}
    84 84   to="/dashboard/locations/areas"
    skipped 13 lines
    98 98   <ProgressWrench className={classes.icon} fontSize="small" />
    99 99   {t('Areas')}
    100 100   </Button>
    101  - )} */}
     101 + )}*/}
    102 102   {!helper.isEntityTypeHidden('Cities') && (
    103 103   <Button
    104 104   component={Link}
    skipped 59 lines
Please wait...
Page is in error, reload to recover