Projects STRLCPY opencti Commits 372b394b
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
Showing first 31 files as there are too many
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/components/Theme.d.ts
    1  -import { ColorPartial, PaletteColorOptions, PaletteOptions, TypeBackground } from '@mui/material/styles/createPalette';
     1 +import {
     2 + ColorPartial,
     3 + PaletteColorOptions,
     4 + PaletteOptions,
     5 + TypeBackground,
     6 + TypeText,
     7 +} from '@mui/material/styles/createPalette';
    2 8  import { Theme as MuiTheme, ThemeOptions } from '@mui/material/styles/createTheme';
     9 +import useHelper from '../utils/hooks/useHelper';
    3 10   
    4 11  interface ExtendedColor extends PaletteColorOptions {
    5 12   main: string
     13 + palette: ExtendedPaletteOptions
     14 + text: Partial<TypeText>
    6 15  }
    7 16   
    8 17  interface ExtendedBackground extends TypeBackground {
    skipped 7 lines
    16 25   primary: Partial<ExtendedColor>
    17 26   grey: Partial<ColorPartial>
    18 27   error: Partial<ExtendedColor>
     28 + text: Partial<TypeText>
    19 29  }
    20 30   
    21 31  interface ExtendedThemeOptions extends ThemeOptions {
    skipped 8 lines
  • ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/Root.tsx
    skipped 48 lines
    49 49  const Root = () => {
    50 50   const data = useLazyLoadQuery<RootPrivateQuery>(rootPrivateQuery, {});
    51 51   const { me, settings } = data;
     52 + // TO DO : Use the hook useHelper when all project is pure function //
    52 53   const helper = platformModuleHelper(settings);
    53 54   return (
    54 55   <UserContext.Provider value={{ me, settings, helper }}>
    skipped 14 lines
  • ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/common/stix_domain_objects/StixDomainObjectHeader.js
    skipped 293 lines
    294 294   </Typography>
    295 295   <Security needs={[KNOWLEDGE_KNUPDATE]}>
    296 296   <div className={classes.popover}>
    297  - {React.cloneElement(PopoverComponent, {
     297 + {/* TODO remove this when all components are pure function without compose() */}
     298 + {!React.isValidElement(PopoverComponent) ? (
     299 + <PopoverComponent disabled={disablePopover} id={stixDomainObject.id} />
     300 + ) : React.cloneElement(PopoverComponent, {
    298 301   id: stixDomainObject.id,
    299 302   disabled: disablePopover,
    300 303   })}
    skipped 355 lines
  • ■ ■ ■ ■ ■ ■
    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';
     1 +import React, { FunctionComponent } from 'react';
    5 2  import CountriesLines, { countriesLinesQuery } from './countries/CountriesLines';
    6 3  import useLocalStorage, { localStorageToPaginationOptions } from '../../../utils/hooks/useLocalStorage';
    7 4  import { PaginationOptions } from '../../../components/list_lines';
    8 5  import ListLines from '../../../components/list_lines/ListLines';
    9 6  import Security, { KNOWLEDGE_KNUPDATE } from '../../../utils/Security';
    10 7  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';
     8 +import { convertFilters } from '../../../utils/ListParameters';
     9 +import {
     10 + CountriesLinesPaginationQuery,
     11 + CountriesLinesPaginationQuery$variables,
     12 +} from './countries/__generated__/CountriesLinesPaginationQuery.graphql';
     13 +import useQueryLoading from '../../../utils/hooks/useQueryLoading';
     14 +import type { Filters } from '../../../components/list_lines';
    20 15   
    21 16  interface CountryProps {
    22  - history: History,
    23  - location: Location,
    24 17   paginationOptions: PaginationOptions,
    25 18   exportEntityType: string,
    26 19  }
    27 20   
    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, {
     21 +const Countries: FunctionComponent<CountryProps> = ({ exportEntityType }) => {
     22 + const [viewStorage, _, storageHelpers] = useLocalStorage('view-countries', {
    34 23   searchTerm: '',
    35 24   sortBy: 'name',
    36 25   orderAsc: false,
    37 26   openExports: false,
     27 + filters: {},
     28 + numberOfElements: { number: 0, symbol: '' },
    38 29   });
    39  - const { searchTerm, sortBy, orderAsc } = viewStorage;
     30 + const finalFilters = convertFilters(viewStorage.filters) as unknown as Filters;
     31 + const paginationOptions = localStorageToPaginationOptions<CountriesLinesPaginationQuery$variables>({ ...viewStorage, filters: finalFilters, count: 25 });
     32 + const renderLines = () => {
     33 + const {
     34 + searchTerm,
     35 + sortBy,
     36 + orderAsc,
     37 + filters,
     38 + openExports,
     39 + numberOfElements,
     40 + } = viewStorage;
     41 + const dataColumns = {
     42 + name: {
     43 + label: 'Name',
     44 + width: '60%',
     45 + isSortable: true,
     46 + },
     47 + created: {
     48 + label: 'Creation date',
     49 + width: '20%',
     50 + isSortable: true,
     51 + },
     52 + modified: {
     53 + label: 'Modification date',
     54 + width: '20%',
     55 + isSortable: true,
     56 + },
     57 + };
     58 + const queryRef = useQueryLoading<CountriesLinesPaginationQuery>(countriesLinesQuery, paginationOptions);
    40 59   
    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: '60%',
    58  - isSortable: true,
    59  - },
    60  - created: {
    61  - label: 'Creation date',
    62  - width: '15%',
    63  - isSortable: true,
    64  - },
    65  - modified: {
    66  - label: 'Modification date',
    67  - width: '15%',
    68  - isSortable: true,
    69  - },
    70  - };
    71  - const renderLines = (paginationOptions: PaginationOptions) => (
    72  - <ListLines
    73  - sortBy={sortBy}
    74  - orderAsc={orderAsc}
    75  - dataColumns={dataColumns}
    76  - handleSort={handleSort}
    77  - handleSearch={handleSearch}
    78  - handleToggleExports={handleToggleExports}
    79  - keyword={searchTerm}
    80  - paginationOptions={paginationOptions}
    81  - exportEntityType={'Country'}
    82  - openExports={openExports}
    83  - >
    84  - <QueryRenderer
    85  - query={countriesLinesQuery}
    86  - variables={{ count: 25, ...paginationOptions }}
    87  - render={({ props }: { props: any }) => (
     60 + return (
     61 + <ListLines
     62 + sortBy={sortBy}
     63 + orderAsc={orderAsc}
     64 + dataColumns={dataColumns}
     65 + handleSort={storageHelpers.handleSort}
     66 + handleSearch={storageHelpers.handleSearch}
     67 + handleAddFilter={storageHelpers.handleAddFilter}
     68 + handleRemoveFilter={storageHelpers.handleRemoveFilter}
     69 + handleToggleExports={storageHelpers.handleToggleExports}
     70 + openExports={openExports}
     71 + exportEntityType="Country"
     72 + keyword={searchTerm}
     73 + filters={filters}
     74 + paginationOptions={paginationOptions}
     75 + numberOfElements={numberOfElements}
     76 + availableFilterKeys={[
     77 + 'created_start_date',
     78 + 'created_end_date',
     79 + 'createdBy',
     80 + ]}
     81 + >
     82 + {queryRef && (
    88 83   <CountriesLines
    89  - data={props}
     84 + queryRef={queryRef}
    90 85   paginationOptions={paginationOptions}
    91 86   dataColumns={dataColumns}
     87 + setNumberOfElements={storageHelpers.handleSetNumberOfElements}
    92 88   />
    93 89   )}
    94  - />
    95  - </ListLines>
    96  - );
     90 + </ListLines>
     91 + );
     92 + };
     93 + 
    97 94   return (
    98 95   <div>
    99  - {renderLines(queryProps)}
     96 + {renderLines()}
    100 97   <Security needs={[KNOWLEDGE_KNUPDATE]}>
    101  - <CountryCreation paginationOptions={queryProps}/>
     98 + <CountryCreation paginationOptions={paginationOptions} />
    102 99   </Security>
    103 100   </div>
    104 101   );
    skipped 4 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/Regions.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 Regions 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  -Regions.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))(Regions);
    88  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/Regions.tsx
     1 +import React, { FunctionComponent } from 'react';
     2 +import { convertFilters } from '../../../utils/ListParameters';
     3 +import RegionsLines, { regionsLinesQuery } from './regions/RegionsLines';
     4 +import useLocalStorage, { localStorageToPaginationOptions } from '../../../utils/hooks/useLocalStorage';
     5 +import { Filters, PaginationOptions } from '../../../components/list_lines';
     6 +import useQueryLoading from '../../../utils/hooks/useQueryLoading';
     7 +import ListLines from '../../../components/list_lines/ListLines';
     8 +import Security, { KNOWLEDGE_KNUPDATE } from '../../../utils/Security';
     9 +import {
     10 + RegionsLinesPaginationQuery,
     11 + RegionsLinesPaginationQuery$variables,
     12 +} from './regions/__generated__/RegionsLinesPaginationQuery.graphql';
     13 +import RegionCreation from './regions/RegionCreation';
     14 + 
     15 +interface RegionsProps {
     16 + paginationOptions: PaginationOptions,
     17 +}
     18 + 
     19 +const Regions: FunctionComponent<RegionsProps> = () => {
     20 + const [viewStorage, _, storageHelpers] = useLocalStorage('view-regions', {
     21 + searchTerm: '',
     22 + sortBy: 'name',
     23 + orderAsc: false,
     24 + openExports: false,
     25 + filters: {},
     26 + numberOfElements: { number: 0, symbol: '' },
     27 + });
     28 + const finalFilters = convertFilters(viewStorage.filters) as unknown as Filters;
     29 + const paginationOptions = localStorageToPaginationOptions<RegionsLinesPaginationQuery$variables>({ ...viewStorage, filters: finalFilters, count: 25 });
     30 + 
     31 + const renderLines = () => {
     32 + const {
     33 + searchTerm,
     34 + sortBy,
     35 + orderAsc,
     36 + filters,
     37 + openExports,
     38 + numberOfElements,
     39 + } = viewStorage;
     40 + const dataColumns = {
     41 + name: {
     42 + label: 'Name',
     43 + width: '60%',
     44 + isSortable: true,
     45 + },
     46 + created: {
     47 + label: 'Creation date',
     48 + width: '20%',
     49 + isSortable: true,
     50 + },
     51 + modified: {
     52 + label: 'Modification date',
     53 + width: '20%',
     54 + isSortable: true,
     55 + },
     56 + };
     57 + const queryRef = useQueryLoading<RegionsLinesPaginationQuery>(regionsLinesQuery, paginationOptions);
     58 + 
     59 + return (
     60 + <ListLines
     61 + sortBy={sortBy}
     62 + orderAsc={orderAsc}
     63 + dataColumns={dataColumns}
     64 + handleSort={storageHelpers.handleSort}
     65 + handleSearch={storageHelpers.handleSearch}
     66 + handleAddFilter={storageHelpers.handleAddFilter}
     67 + handleRemoveFilter={storageHelpers.handleRemoveFilter}
     68 + handleToggleExports={storageHelpers.handleToggleExports}
     69 + openExports={openExports}
     70 + exportEntityType="Region"
     71 + keyword={searchTerm}
     72 + filters={filters}
     73 + paginationOptions={paginationOptions}
     74 + numberOfElements={numberOfElements}
     75 + availableFilterKeys={[
     76 + 'created_start_date',
     77 + 'created_end_date',
     78 + 'createdBy',
     79 + ]}
     80 + >
     81 + {queryRef && (
     82 + <RegionsLines
     83 + queryRef={queryRef}
     84 + data={queryRef}
     85 + keyword={searchTerm}
     86 + paginationOptions={paginationOptions}
     87 + dataColumns={dataColumns}
     88 + setNumberOfElements={storageHelpers.handleSetNumberOfElements}
     89 + />
     90 + )}
     91 + </ListLines>
     92 + );
     93 + };
     94 + 
     95 + return (
     96 + <div>
     97 + {renderLines()}
     98 + <Security needs={[KNOWLEDGE_KNUPDATE]}>
     99 + <RegionCreation paginationOptions={paginationOptions} />
     100 + </Security>
     101 + </div>
     102 + );
     103 +};
     104 + 
     105 +export default Regions;
     106 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountriesLines.tsx
    1 1  import React, { FunctionComponent } from 'react';
    2  -import { createPaginationContainer, graphql, RelayPaginationProp } from 'react-relay';
    3  -import { DataColumns, PaginationOptions } from '../../../../components/list_lines';
     2 +import { graphql, PreloadedQuery } from 'react-relay';
     3 +import { DataColumns } from '../../../../components/list_lines';
    4 4  import ListLinesContent from '../../../../components/list_lines/ListLinesContent';
    5  -import { CountryLine, CountryLineDummy } from './CountryLine';
    6  -import { CountriesLines_data$data } from './__generated__/CountriesLines_data.graphql';
     5 +import { CountryLineComponent, CountryLineDummyComponent } from './CountryLine';
     6 +import { CountriesLines_data$key } from './__generated__/CountriesLines_data.graphql';
     7 +import {
     8 + CountriesLinesPaginationQuery,
     9 + CountriesLinesPaginationQuery$variables,
     10 +} from './__generated__/CountriesLinesPaginationQuery.graphql';
     11 +import { UseLocalStorage } from '../../../../utils/hooks/useLocalStorage';
     12 +import usePreloadedPaginationFragment from '../../../../utils/hooks/usePreloadedPaginationFragment';
     13 + 
     14 +const nbOfRowsToLoad = 50;
    7 15   
    8 16  interface CountriesLinesProps {
    9  - data?: CountriesLines_data$data,
     17 + queryRef: PreloadedQuery<CountriesLinesPaginationQuery>,
    10 18   dataColumns: DataColumns,
    11  - paginationOptions: PaginationOptions,
    12  - initialLoading?: boolean
    13  - relay: RelayPaginationProp
     19 + paginationOptions?: CountriesLinesPaginationQuery$variables,
     20 + setNumberOfElements: UseLocalStorage[2]['handleSetNumberOfElements'],
    14 21  }
    15 22   
    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 23  export const countriesLinesQuery = graphql`
    33 24   query CountriesLinesPaginationQuery(
    34 25   $search: String
    skipped 14 lines
    49 40   }
    50 41  `;
    51 42   
    52  -export default createPaginationContainer(
    53  - CountriesLines,
    54  - {
    55  - data: graphql`
    56  - fragment CountriesLines_data on Query
     43 +const countriesLinesFragment = graphql`
     44 + fragment CountriesLines_data on Query
    57 45   @argumentDefinitions(
    58 46   search: { type: "String" }
    59 47   count: { type: "Int", defaultValue: 25 }
    skipped 1 lines
    61 49   orderBy: { type: "CountriesOrdering", defaultValue: name }
    62 50   orderMode: { type: "OrderingMode", defaultValue: asc }
    63 51   filters: { type: "[CountriesFiltering]" }
    64  - ) {
     52 + ) @refetchable(queryName: "CountriesLinesRefetchQuery") {
    65 53   countries(
    66 54   search: $search
    67 55   first: $count
    skipped 1 lines
    69 57   orderBy: $orderBy
    70 58   orderMode: $orderMode
    71 59   filters: $filters
    72  - )
    73  - @connection(key: "Pagination_countries") {
     60 + ) @connection(key: "Pagination_countries") {
    74 61   edges {
    75 62   node {
    76 63   id
    skipped 9 lines
    86 73   }
    87 74   }
    88 75   }
    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  -);
     76 + `;
     77 + 
     78 +const CountriesLines: FunctionComponent<CountriesLinesProps> = ({ setNumberOfElements, queryRef, dataColumns, paginationOptions }) => {
     79 + const {
     80 + data,
     81 + hasMore,
     82 + loadMore,
     83 + isLoadingMore,
     84 + } = usePreloadedPaginationFragment<CountriesLinesPaginationQuery, CountriesLines_data$key>({
     85 + linesQuery: countriesLinesQuery,
     86 + linesFragment: countriesLinesFragment,
     87 + queryRef,
     88 + nodePath: ['countries', 'edges'],
     89 + setNumberOfElements,
     90 + });
     91 + return (
     92 + <ListLinesContent
     93 + initialLoading={!data}
     94 + isLoading={isLoadingMore}
     95 + loadMore={loadMore}
     96 + hasMore={hasMore}
     97 + dataList={data?.countries?.edges ?? []}
     98 + globalCount={data?.countries?.pageInfo?.globalCount ?? nbOfRowsToLoad}
     99 + LineComponent={ CountryLineComponent }
     100 + DummyLineComponent={ CountryLineDummyComponent }
     101 + dataColumns={dataColumns}
     102 + nbOfRowsToLoad={nbOfRowsToLoad}
     103 + paginationOptions={paginationOptions}
     104 + />
     105 + );
     106 +};
     107 + 
     108 +export default CountriesLines;
    115 109   
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/Country.tsx
    1 1  import React, { Component, FunctionComponent } from 'react';
    2  -import PropTypes from 'prop-types';
    3  -import { compose } from 'ramda';
    4 2  import { graphql, createFragmentContainer, useFragment } from 'react-relay';
    5  -import withStyles from '@mui/styles/withStyles';
    6 3  import Grid from '@mui/material/Grid';
    7 4  import makeStyles from '@mui/styles/makeStyles';
    8  -import inject18n from '../../../../components/i18n';
    9 5  import CountryEdition from './CountryEdition';
    10 6  import CountryPopover from './CountryPopover';
    11 7  import StixCoreObjectOrStixCoreRelationshipLastReports from '../../analysis/reports/StixCoreObjectOrStixCoreRelationshipLastReports';
    skipped 5 lines
    17 13  import StixCoreObjectLatestHistory from '../../common/stix_core_objects/StixCoreObjectLatestHistory';
    18 14  import SimpleStixObjectOrStixRelationshipStixCoreRelationships from '../../common/stix_core_relationships/SimpleStixObjectOrStixRelationshipStixCoreRelationships';
    19 15  import LocationMiniMap from '../../common/location/LocationMiniMap';
    20  -import { Country_country$data, Country_country$key } from './__generated__/Country_country.graphql';
     16 +import { Country_country$key } from './__generated__/Country_country.graphql';
    21 17   
    22 18  const useStyles = makeStyles(() => ({
    23 19   gridContainer: {
    skipped 4 lines
    28 24   },
    29 25  }));
    30 26   
    31  -const CountryFragment = graphql`
     27 +const countryFragment = graphql`
    32 28   fragment Country_country on Country {
    33 29   id
    34 30   standard_id
    skipped 52 lines
    87 83   
    88 84  const CountryComponent = ({ countryData }: { countryData: Country_country$key }) => {
    89 85   const classes = useStyles();
    90  - const country = useFragment<Country_country$key>(CountryFragment, countryData);
     86 + const country = useFragment<Country_country$key>(countryFragment, countryData);
    91 87   return (
    92 88   <div className={classes.container}>
    93 89   <StixDomainObjectHeader
    skipped 68 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryCreation.tsx
    skipped 7 lines
    8 8  import Fab from '@mui/material/Fab';
    9 9  import { Add, Close } from '@mui/icons-material';
    10 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';
     11 +import { graphql, useMutation } from 'react-relay';
     12 +import { FormikConfig } from 'formik/dist/types';
     13 +import * as R from 'ramda';
    14 14  import { useFormatter } from '../../../../components/i18n';
    15  -import { commitMutation, handleErrorInForm } from '../../../../relay/environment';
    16 15  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';
     16 +import CreatedByField from '../../common/form/CreatedByField';
     17 +import ObjectMarkingField from '../../common/form/ObjectMarkingField';
    20 18  import MarkDownField from '../../../../components/MarkDownField';
    21 19  import { Theme } from '../../../../components/Theme';
    22  -import { PaginationOptions } from '../../../../components/list_lines';
    23  -import ExternalReferencesField, { ExternalReference } from '../../common/form/ExternalReferencesField';
     20 +import ExternalReferencesField from '../../common/form/ExternalReferencesField';
     21 +import { CountriesLinesPaginationQuery$variables } from './__generated__/CountriesLinesPaginationQuery.graphql';
     22 +import { insertNode } from '../../../../utils/Store';
    24 23   
    25 24  const styles = makeStyles<Theme>((theme) => ({
    26 25   drawerPaper: {
    skipped 65 lines
    92 91   .required(t('This field is required')),
    93 92   });
    94 93   
    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 {
     94 +interface CountryAddInput {
    114 95   name: string,
    115 96   description: string,
    116  - createdBy: CreatedBy,
    117  - objectMarking: Array<ObjectMarking>,
    118  - objectLabel: Array<ObjectLabel>,
    119  - externalReferences: Array<ExternalReference>
     97 + latitude: string,
     98 + longitude: string,
     99 + createdBy?: { value: string, label?: string },
     100 + objectMarking: { value: string }[],
     101 + externalReferences: { value: string }[],
    120 102  }
    121 103   
    122  -const CountryCreation: FunctionComponent<CountryCreationProps> = ({
    123  - paginationOptions,
    124  -}) => {
     104 +const CountryCreation = ({ paginationOptions }: { paginationOptions: CountriesLinesPaginationQuery$variables }) => {
    125 105   const { t } = useFormatter();
    126 106   const classes = styles();
    127 107   
    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  - };
     108 + const [open, setOpen] = useState<boolean>(false);
    137 109   
    138 110   const handleOpen = () => {
    139 111   setOpen(true);
    skipped 3 lines
    143 115   setOpen(false);
    144 116   };
    145 117   
    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,
     118 + const [commit] = useMutation(countryMutation);
     119 + 
     120 + const onSubmit: FormikConfig<CountryAddInput>['onSubmit'] = (values, { setSubmitting, resetForm }) => {
     121 + const finalValues = R.pipe(
     122 + R.assoc('latitude', parseFloat(values.latitude)),
     123 + R.assoc('longitude', parseFloat(values.longitude)),
     124 + R.assoc('createdBy', values.createdBy?.value),
     125 + R.assoc('objectMarking', R.pluck('value', values.objectMarking)),
     126 + R.assoc('externalReferences', R.pluck('value', values.externalReferences)),
     127 + )(values);
     128 + commit({
    160 129   variables: {
    161 130   input: finalValues,
    162 131   },
    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  - }
     132 + updater: (store) => {
     133 + insertNode(store, 'Pagination_countries', paginationOptions, 'countryAdd');
    175 134   },
    176  - onError: (error: Error) => {
    177  - handleErrorInForm(error, setErrors);
    178  - setSubmitting(false);
    179  - },
    180  - setSubmitting,
    181 135   onCompleted: () => {
    182 136   setSubmitting(false);
    183 137   resetForm();
    184 138   handleClose();
    185 139   },
    186  - optimisticUpdater: undefined,
    187  - optimisticResponse: undefined,
    188 140   });
    189 141   };
    190 142   
    skipped 32 lines
    223 175   <Typography variant="h6">{t('Create a Country')}</Typography>
    224 176   </div>
    225 177   <div className={classes.container}>
    226  - <Formik
    227  - initialValues={initialValues}
     178 + <Formik<CountryAddInput>
     179 + initialValues={{
     180 + name: '',
     181 + description: '',
     182 + latitude: '',
     183 + longitude: '',
     184 + createdBy: { value: '', label: ''},
     185 + objectMarking: [],
     186 + externalReferences: [],
     187 + }}
    228 188   validationSchema={countryValidation(t)}
    229 189   onSubmit={onSubmit}
    230  - onReset={onReset}
     190 + onReset={handleClose}
    231 191   >
    232 192   {({
    233 193   submitForm,
    skipped 20 lines
    254 214   rows="4"
    255 215   style={{ marginTop: 20 }}
    256 216   />
     217 + <Field
     218 + component={TextField}
     219 + variant="standard"
     220 + name="latitude"
     221 + label={t('Latitude')}
     222 + fullWidth={true}
     223 + style={{ marginTop: 20 }}
     224 + />
     225 + <Field
     226 + component={TextField}
     227 + variant="standard"
     228 + name="longitude"
     229 + label={t('Longitude')}
     230 + fullWidth={true}
     231 + style={{ marginTop: 20 }}
     232 + />
    257 233   <CreatedByField
    258 234   name="createdBy"
    259 235   style={{
    skipped 1 lines
    261 237   width: '100%',
    262 238   }}
    263 239   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 240   />
    274 241   <ObjectMarkingField
    275 242   name="objectMarking"
    skipped 44 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryEdition.tsx
    1  -import React, { FunctionComponent, useState } from 'react';
     1 +import React, { useState } from 'react';
    2 2  import Drawer from '@mui/material/Drawer';
    3 3  import Fab from '@mui/material/Fab';
    4 4  import { Edit } from '@mui/icons-material';
    5  -import { graphql } from 'react-relay';
     5 +import { useMutation } from 'react-relay';
    6 6  import makeStyles from '@mui/styles/makeStyles';
    7  -import { commitMutation, QueryRenderer } from '../../../../relay/environment';
    8 7  import { useFormatter } from '../../../../components/i18n';
    9  -import CountryEditionContainer from './CountryEditionContainer';
     8 +import CountryEditionContainer, { countryEditionQuery } from './CountryEditionContainer';
    10 9  import Loader, { LoaderVariant } from '../../../../components/Loader';
    11 10  import { Theme } from '../../../../components/Theme';
    12  -import { dataComponentEditionOverviewFocus } from '../../techniques/data_components/DataComponentEditionOverview';
     11 +import useQueryLoading from '../../../../utils/hooks/useQueryLoading';
     12 +import { countryEditionOverviewFocus } from './CountryEditionOverview';
     13 +import { CountryEditionContainerQuery } from './__generated__/CountryEditionContainerQuery.graphql';
    13 14   
    14 15  const useStyles = makeStyles<Theme>((theme) => ({
    15 16   editButton: {
    skipped 15 lines
    31 32   },
    32 33  }));
    33 34   
    34  -export const countryEditionQuery = graphql`
    35  - query CountryEditionContainerQuery($id: String!) {
    36  - country(id: $id) {
    37  - ...CountryEditionContainer_country
    38  - }
    39  - settings {
    40  - platform_enable_reference
    41  - }
    42  - }
    43  -`;
    44  - 
    45  -interface CountryEditionProps {
    46  - countryId: string
    47  -}
    48  - 
    49  -const CountryEdition: FunctionComponent<CountryEditionProps> = ({ countryId }) => {
     35 +const CountryEdition = ({ countryId } : { countryId: string }) => {
    50 36   const classes = useStyles();
    51 37   const { t } = useFormatter();
    52  - const [open, setOpen] = useState(false);
     38 + const [open, setOpen] = useState<boolean>(false);
     39 + const [commit] = useMutation(countryEditionOverviewFocus);
    53 40   
    54 41   const handleOpen = () => {
    55 42   setOpen(true);
    56 43   };
    57 44   
    58 45   const handleClose = () => {
    59  - commitMutation({
    60  - mutation: dataComponentEditionOverviewFocus,
     46 + commit({
    61 47   variables: {
    62 48   id: countryId,
    63 49   input: { focusOn: '' },
    64 50   },
    65  - updater: undefined,
    66  - optimisticUpdater: undefined,
    67  - optimisticResponse: undefined,
    68  - onCompleted: undefined,
    69  - onError: undefined,
    70  - setSubmitting: undefined,
    71 51   });
    72 52   setOpen(false);
    73 53   };
     54 + 
     55 + const queryRef = useQueryLoading<CountryEditionContainerQuery>(countryEditionQuery, { id: countryId });
    74 56   
    75 57   return (
    76 58   <div>
    skipped 13 lines
    90 72   classes={{ paper: classes.drawerPaper }}
    91 73   onClose={handleClose}
    92 74   >
    93  - <QueryRenderer
    94  - query={countryEditionQuery}
    95  - variables={{ id: countryId }}
    96  - render={(result: { props: any }) => {
    97  - if (result.props) {
    98  - return (
    99  - <CountryEditionContainer
    100  - country={result.props.country}
    101  - enableReferences={result.props.settings.platform_enable_reference?.includes(
    102  - 'Country',
    103  - )}
    104  - handleClose={handleClose}
    105  - />
    106  - );
    107  - }
    108  - return <Loader variant={LoaderVariant.inElement} />;
    109  - }}
    110  - />
     75 + {queryRef && (
     76 + <React.Suspense fallback={<Loader variant={LoaderVariant.inElement} />}>
     77 + <CountryEditionContainer
     78 + queryRef={queryRef}
     79 + handleClose={handleClose}
     80 + />
     81 + </React.Suspense>
     82 + )}
    111 83   </Drawer>
    112 84   </div>
    113 85   );
    skipped 4 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryEditionContainer.tsx
    1 1  import React, { FunctionComponent } from 'react';
    2  -import { graphql, createFragmentContainer } from 'react-relay';
     2 +import { graphql, createFragmentContainer, PreloadedQuery, usePreloadedQuery } from 'react-relay';
    3 3  import Typography from '@mui/material/Typography';
    4 4  import IconButton from '@mui/material/IconButton';
    5 5  import { Close } from '@mui/icons-material';
    skipped 5 lines
    11 11  import {
    12 12   CountryEditionContainer_country$data,
    13 13  } from './__generated__/CountryEditionContainer_country.graphql';
     14 +import { CountryEditionContainerQuery } from './__generated__/CountryEditionContainerQuery.graphql';
     15 +import { cityEditionQuery } from '../cities/CityEdition';
     16 +import Loader, { LoaderVariant } from '../../../../components/Loader';
    14 17   
    15 18  const useStyles = makeStyles<Theme>((theme) => ({
    16 19   header: {
    skipped 25 lines
    42 45  }));
    43 46   
    44 47  interface CountryEditionContainerProps {
    45  - enablesReferences: boolean
    46 48   handleClose: () => void
    47  - country: CountryEditionContainer_country$data
     49 + queryRef: PreloadedQuery<CountryEditionContainerQuery>
    48 50  }
    49 51   
    50 52  export const countryEditionQuery = graphql`
    skipped 11 lines
    62 64   }
    63 65  `;
    64 66   
    65  -const CountryEditionContainer: FunctionComponent<CountryEditionContainerProps> = ({ enablesReferences, handleClose, country }) => {
     67 +const CountryEditionContainer: FunctionComponent<CountryEditionContainerProps> = ({ handleClose, queryRef }) => {
    66 68   const classes = useStyles();
    67 69   const { t } = useFormatter();
    68  - const { editContext } = country;
    69 70   
    70  - return (
     71 + const queryData = usePreloadedQuery(countryEditionQuery, queryRef);
     72 + 
     73 + if (queryData.country) {
     74 + return (
    71 75   <div>
    72 76   <div className={classes.header}>
    73 77   <IconButton
    skipped 8 lines
    82 86   <Typography variant="h6" classes={{ root: classes.title }}>
    83 87   {t('Update an country')}
    84 88   </Typography>
    85  - <SubscriptionAvatars context={editContext} />
     89 + <SubscriptionAvatars context={queryData.country.editContext} />
    86 90   <div className="clearfix" />
    87 91   </div>
    88 92   <div className={classes.container}>
    89 93   <CountryEditionOverview
    90  - country={country}
    91  - enableReferences={enablesReferences}
    92  - context={editContext}
     94 + countryRef={queryData.country}
     95 + enableReferences={queryData.settings.platform_enable_reference?.includes('Country')}
     96 + context={queryData.country.editContext}
    93 97   handleClose={handleClose}
    94 98   />
    95 99   </div>
    96 100   </div>
    97  - );
     101 + );
     102 + }
     103 + 
     104 + return <Loader variant={LoaderVariant.inElement} />;
    98 105  };
    99 106   
    100  -const CountryEditionFragment = createFragmentContainer(
    101  - CountryEditionContainer,
    102  - {
    103  - country: graphql`
    104  - fragment CountryEditionContainer_country on Country {
    105  - id
    106  - ...CountryEditionOverview_country
    107  - editContext {
    108  - name
    109  - focusOn
    110  - }
    111  - }
    112  - `,
    113  - },
    114  -);
    115  - 
    116  -export default CountryEditionFragment;
     107 +export default CountryEditionContainer;
    117 108   
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryEditionOverview.js
    1  -import React, { Component } from 'react';
    2  -import * as PropTypes from 'prop-types';
    3  -import { graphql, createFragmentContainer } from 'react-relay';
    4  -import { Formik, Form, Field } from 'formik';
    5  -import withStyles from '@mui/styles/withStyles';
    6  -import {
    7  - assoc,
    8  - compose,
    9  - map,
    10  - pathOr,
    11  - pipe,
    12  - pick,
    13  - difference,
    14  - head,
    15  -} from 'ramda';
    16  -import * as Yup from 'yup';
    17  -import inject18n from '../../../../components/i18n';
    18  -import TextField from '../../../../components/TextField';
    19  -import { SubscriptionFocus } from '../../../../components/Subscription';
    20  -import { commitMutation } from '../../../../relay/environment';
    21  -import CreatedByField from '../../common/form/CreatedByField';
    22  -import ObjectMarkingField from '../../common/form/ObjectMarkingField';
    23  -import MarkDownField from '../../../../components/MarkDownField';
    24  -import {
    25  - convertCreatedBy,
    26  - convertMarkings,
    27  - convertStatus,
    28  -} from '../../../../utils/Edition';
    29  -import StatusField from '../../common/form/StatusField';
    30  - 
    31  -const styles = (theme) => ({
    32  - drawerPaper: {
    33  - minHeight: '100vh',
    34  - width: '50%',
    35  - position: 'fixed',
    36  - overflow: 'hidden',
    37  - transition: theme.transitions.create('width', {
    38  - easing: theme.transitions.easing.sharp,
    39  - duration: theme.transitions.duration.enteringScreen,
    40  - }),
    41  - padding: '30px 30px 30px 30px',
    42  - },
    43  - createButton: {
    44  - position: 'fixed',
    45  - bottom: 30,
    46  - right: 30,
    47  - },
    48  - importButton: {
    49  - position: 'absolute',
    50  - top: 30,
    51  - right: 30,
    52  - },
    53  -});
    54  - 
    55  -const countryMutationFieldPatch = graphql`
    56  - mutation CountryEditionOverviewFieldPatchMutation(
    57  - $id: ID!
    58  - $input: [EditInput]!
    59  - ) {
    60  - countryEdit(id: $id) {
    61  - fieldPatch(input: $input) {
    62  - ...CountryEditionOverview_country
    63  - ...Country_country
    64  - }
    65  - }
    66  - }
    67  -`;
    68  - 
    69  -export const countryEditionOverviewFocus = graphql`
    70  - mutation CountryEditionOverviewFocusMutation($id: ID!, $input: EditContext!) {
    71  - countryEdit(id: $id) {
    72  - contextPatch(input: $input) {
    73  - id
    74  - }
    75  - }
    76  - }
    77  -`;
    78  - 
    79  -const countryMutationRelationAdd = graphql`
    80  - mutation CountryEditionOverviewRelationAddMutation(
    81  - $id: ID!
    82  - $input: StixMetaRelationshipAddInput!
    83  - ) {
    84  - countryEdit(id: $id) {
    85  - relationAdd(input: $input) {
    86  - from {
    87  - ...CountryEditionOverview_country
    88  - }
    89  - }
    90  - }
    91  - }
    92  -`;
    93  - 
    94  -const countryMutationRelationDelete = graphql`
    95  - mutation CountryEditionOverviewRelationDeleteMutation(
    96  - $id: ID!
    97  - $toId: StixRef!
    98  - $relationship_type: String!
    99  - ) {
    100  - countryEdit(id: $id) {
    101  - relationDelete(toId: $toId, relationship_type: $relationship_type) {
    102  - ...CountryEditionOverview_country
    103  - }
    104  - }
    105  - }
    106  -`;
    107  - 
    108  -const countryValidation = (t) => Yup.object().shape({
    109  - name: Yup.string().required(t('This field is required')),
    110  - description: Yup.string()
    111  - .min(3, t('The value is too short'))
    112  - .max(5000, t('The value is too long'))
    113  - .required(t('This field is required')),
    114  - x_opencti_workflow_id: Yup.object(),
    115  -});
    116  - 
    117  -class CountryEditionOverviewComponent extends Component {
    118  - handleChangeFocus(name) {
    119  - commitMutation({
    120  - mutation: countryEditionOverviewFocus,
    121  - variables: {
    122  - id: this.props.country.id,
    123  - input: {
    124  - focusOn: name,
    125  - },
    126  - },
    127  - });
    128  - }
    129  - 
    130  - handleSubmitField(name, value) {
    131  - let finalValue = value;
    132  - if (name === 'x_opencti_workflow_id') {
    133  - finalValue = value.value;
    134  - }
    135  - countryValidation(this.props.t)
    136  - .validateAt(name, { [name]: value })
    137  - .then(() => {
    138  - commitMutation({
    139  - mutation: countryMutationFieldPatch,
    140  - variables: {
    141  - id: this.props.country.id,
    142  - input: { key: name, value: finalValue ?? '' },
    143  - },
    144  - });
    145  - })
    146  - .catch(() => false);
    147  - }
    148  - 
    149  - handleChangeCreatedBy(name, value) {
    150  - if (!this.props.enableReferences) {
    151  - commitMutation({
    152  - mutation: countryMutationFieldPatch,
    153  - variables: {
    154  - id: this.props.country.id,
    155  - input: { key: 'createdBy', value: value.value || '' },
    156  - },
    157  - });
    158  - }
    159  - }
    160  - 
    161  - handleChangeObjectMarking(name, values) {
    162  - const { country } = this.props;
    163  - const currentMarkingDefinitions = pipe(
    164  - pathOr([], ['objectMarking', 'edges']),
    165  - map((n) => ({
    166  - label: n.node.definition,
    167  - value: n.node.id,
    168  - })),
    169  - )(country);
    170  - 
    171  - const added = difference(values, currentMarkingDefinitions);
    172  - const removed = difference(currentMarkingDefinitions, values);
    173  - 
    174  - if (added.length > 0) {
    175  - commitMutation({
    176  - mutation: countryMutationRelationAdd,
    177  - variables: {
    178  - id: this.props.country.id,
    179  - input: {
    180  - toId: head(added).value,
    181  - relationship_type: 'object-marking',
    182  - },
    183  - },
    184  - });
    185  - }
    186  - 
    187  - if (removed.length > 0) {
    188  - commitMutation({
    189  - mutation: countryMutationRelationDelete,
    190  - variables: {
    191  - id: this.props.country.id,
    192  - toId: head(removed).value,
    193  - relationship_type: 'object-marking',
    194  - },
    195  - });
    196  - }
    197  - }
    198  - 
    199  - render() {
    200  - const { t, country, context } = this.props;
    201  - const createdBy = convertCreatedBy(country);
    202  - const objectMarking = convertMarkings(country);
    203  - const status = convertStatus(t, country);
    204  - const initialValues = pipe(
    205  - assoc('createdBy', createdBy),
    206  - assoc('objectMarking', objectMarking),
    207  - assoc('x_opencti_workflow_id', status),
    208  - pick([
    209  - 'name',
    210  - 'description',
    211  - 'createdBy',
    212  - 'objectMarking',
    213  - 'x_opencti_workflow_id',
    214  - ]),
    215  - )(country);
    216  - return (
    217  - <Formik
    218  - enableReinitialize={true}
    219  - initialValues={initialValues}
    220  - validationSchema={countryValidation(t)}
    221  - onSubmit={() => true}
    222  - >
    223  - {({ setFieldValue }) => (
    224  - <Form style={{ margin: '20px 0 20px 0' }}>
    225  - <Field
    226  - component={TextField}
    227  - variant="standard"
    228  - name="name"
    229  - label={t('Name')}
    230  - fullWidth={true}
    231  - onFocus={this.handleChangeFocus.bind(this)}
    232  - onSubmit={this.handleSubmitField.bind(this)}
    233  - helperText={
    234  - <SubscriptionFocus context={context} fieldName="name" />
    235  - }
    236  - />
    237  - <Field
    238  - component={MarkDownField}
    239  - name="description"
    240  - label={t('Description')}
    241  - fullWidth={true}
    242  - multiline={true}
    243  - rows="4"
    244  - style={{ marginTop: 20 }}
    245  - onFocus={this.handleChangeFocus.bind(this)}
    246  - onSubmit={this.handleSubmitField.bind(this)}
    247  - helperText={
    248  - <SubscriptionFocus context={context} fieldName="description" />
    249  - }
    250  - />
    251  - {country.workflowEnabled && (
    252  - <StatusField
    253  - name="x_opencti_workflow_id"
    254  - type="Country"
    255  - onFocus={this.handleChangeFocus.bind(this)}
    256  - onChange={this.handleSubmitField.bind(this)}
    257  - setFieldValue={setFieldValue}
    258  - style={{ marginTop: 20 }}
    259  - helpertext={
    260  - <SubscriptionFocus
    261  - context={context}
    262  - fieldName="x_opencti_workflow_id"
    263  - />
    264  - }
    265  - />
    266  - )}
    267  - <CreatedByField
    268  - name="createdBy"
    269  - style={{ marginTop: 20, width: '100%' }}
    270  - setFieldValue={setFieldValue}
    271  - helpertext={
    272  - <SubscriptionFocus context={context} fieldName="createdBy" />
    273  - }
    274  - onChange={this.handleChangeCreatedBy.bind(this)}
    275  - />
    276  - <ObjectMarkingField
    277  - name="objectMarking"
    278  - style={{ marginTop: 20, width: '100%' }}
    279  - helpertext={
    280  - <SubscriptionFocus
    281  - context={context}
    282  - fieldname="objectMarking"
    283  - />
    284  - }
    285  - onChange={this.handleChangeObjectMarking.bind(this)}
    286  - />
    287  - </Form>
    288  - )}
    289  - </Formik>
    290  - );
    291  - }
    292  -}
    293  - 
    294  -CountryEditionOverviewComponent.propTypes = {
    295  - classes: PropTypes.object,
    296  - theme: PropTypes.object,
    297  - t: PropTypes.func,
    298  - country: PropTypes.object,
    299  - context: PropTypes.array,
    300  -};
    301  - 
    302  -const CountryEditionOverview = createFragmentContainer(
    303  - CountryEditionOverviewComponent,
    304  - {
    305  - country: graphql`
    306  - fragment CountryEditionOverview_country on Country {
    307  - id
    308  - name
    309  - description
    310  - createdBy {
    311  - ... on Identity {
    312  - id
    313  - name
    314  - entity_type
    315  - }
    316  - }
    317  - objectMarking {
    318  - edges {
    319  - node {
    320  - id
    321  - definition
    322  - definition_type
    323  - }
    324  - }
    325  - }
    326  - status {
    327  - id
    328  - order
    329  - template {
    330  - name
    331  - color
    332  - }
    333  - }
    334  - workflowEnabled
    335  - }
    336  - `,
    337  - },
    338  -);
    339  - 
    340  -export default compose(
    341  - inject18n,
    342  - withStyles(styles, { withTheme: true }),
    343  -)(CountryEditionOverview);
    344  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryEditionOverview.tsx
     1 +import React, { FunctionComponent } from 'react';
     2 +import { graphql, useFragment, useMutation } from 'react-relay';
     3 +import { Formik, Form, Field } from 'formik';
     4 +import * as R from 'ramda';
     5 +import * as Yup from 'yup';
     6 +import makeStyles from '@mui/styles/makeStyles';
     7 +import { FormikConfig } from 'formik/dist/types';
     8 +import { useFormatter } from '../../../../components/i18n';
     9 +import TextField from '../../../../components/TextField';
     10 +import { SubscriptionFocus } from '../../../../components/Subscription';
     11 +import CreatedByField from '../../common/form/CreatedByField';
     12 +import ObjectMarkingField from '../../common/form/ObjectMarkingField';
     13 +import MarkDownField from '../../../../components/MarkDownField';
     14 +import {
     15 + convertCreatedBy,
     16 + convertMarkings,
     17 + convertStatus,
     18 +} from '../../../../utils/Edition';
     19 +import StatusField from '../../common/form/StatusField';
     20 +import { Theme } from '../../../../components/Theme';
     21 +import { CountryEditionOverview_country$key } from './__generated__/CountryEditionOverview_country.graphql';
     22 +import { Option } from '../../common/form/ReferenceField';
     23 +import CommitMessage from '../../common/form/CommitMessage';
     24 +import { adaptFieldValue } from '../../../../utils/String';
     25 + 
     26 +const useStyles = makeStyles<Theme>((theme) => ({
     27 + drawerPaper: {
     28 + minHeight: '100vh',
     29 + width: '50%',
     30 + position: 'fixed',
     31 + overflow: 'hidden',
     32 + transition: theme.transitions.create('width', {
     33 + easing: theme.transitions.easing.sharp,
     34 + duration: theme.transitions.duration.enteringScreen,
     35 + }),
     36 + padding: '30px 30px 30px 30px',
     37 + },
     38 + createButton: {
     39 + position: 'fixed',
     40 + bottom: 30,
     41 + right: 30,
     42 + },
     43 + importButton: {
     44 + position: 'absolute',
     45 + top: 30,
     46 + right: 30,
     47 + },
     48 +}));
     49 + 
     50 +const countryMutationFieldPatch = graphql`
     51 + mutation CountryEditionOverviewFieldPatchMutation(
     52 + $id: ID!
     53 + $input: [EditInput]!
     54 + $commitMessage: String
     55 + $references: [String]
     56 + ) {
     57 + countryEdit(id: $id) {
     58 + fieldPatch(
     59 + input: $input
     60 + commitMessage: $commitMessage
     61 + references: $references
     62 + ) {
     63 + ...CountryEditionOverview_country
     64 + ...Country_country
     65 + }
     66 + }
     67 + }
     68 +`;
     69 + 
     70 +export const countryEditionOverviewFocus = graphql`
     71 + mutation CountryEditionOverviewFocusMutation($id: ID!, $input: EditContext!) {
     72 + countryEdit(id: $id) {
     73 + contextPatch(input: $input) {
     74 + id
     75 + }
     76 + }
     77 + }
     78 +`;
     79 + 
     80 +const countryMutationRelationAdd = graphql`
     81 + mutation CountryEditionOverviewRelationAddMutation(
     82 + $id: ID!
     83 + $input: StixMetaRelationshipAddInput!
     84 + ) {
     85 + countryEdit(id: $id) {
     86 + relationAdd(input: $input) {
     87 + from {
     88 + ...CountryEditionOverview_country
     89 + }
     90 + }
     91 + }
     92 + }
     93 +`;
     94 + 
     95 +const countryMutationRelationDelete = graphql`
     96 + mutation CountryEditionOverviewRelationDeleteMutation(
     97 + $id: ID!
     98 + $toId: StixRef!
     99 + $relationship_type: String!
     100 + ) {
     101 + countryEdit(id: $id) {
     102 + relationDelete(
     103 + toId: $toId,
     104 + relationship_type: $relationship_type
     105 + ) {
     106 + ...CountryEditionOverview_country
     107 + }
     108 + }
     109 + }
     110 +`;
     111 + 
     112 +const countryEditionOverviewFragment = graphql`
     113 + fragment CountryEditionOverview_country on Country {
     114 + id
     115 + name
     116 + description
     117 + latitude
     118 + longitude
     119 + createdBy {
     120 + ... on Identity {
     121 + id
     122 + name
     123 + entity_type
     124 + }
     125 + }
     126 + objectMarking {
     127 + edges {
     128 + node {
     129 + id
     130 + definition
     131 + definition_type
     132 + }
     133 + }
     134 + }
     135 + status {
     136 + id
     137 + order
     138 + template {
     139 + name
     140 + color
     141 + }
     142 + }
     143 + workflowEnabled
     144 + }
     145 +`;
     146 + 
     147 +const countryValidation = (t: (v: string) => string) => Yup.object().shape({
     148 + name: Yup.string().required(t('This field is required')),
     149 + description: Yup.string()
     150 + .min(3, t('The value is too short'))
     151 + .max(5000, t('The value is too long'))
     152 + .required(t('This field is required')),
     153 + latitude: Yup.number().typeError(t('This field must be a number')),
     154 + longitude: Yup.number().typeError(t('This field must be a number')),
     155 + x_opencti_workflow_id: Yup.object(),
     156 +});
     157 + 
     158 +interface CountryEditionOverviewProps {
     159 + countryRef: CountryEditionOverview_country$key,
     160 + context: ReadonlyArray<{
     161 + readonly focusOn: string | null;
     162 + readonly name: string;
     163 + } | null> | null
     164 + enableReferences?: boolean
     165 + handleClose: () => void
     166 +}
     167 + 
     168 +interface CountryEditionFormValues {
     169 + message: string
     170 + references: Option[],
     171 + x_opencti_workflow_id: Option
     172 + createdBy: Option
     173 + objectMarking: Option[]
     174 +}
     175 + 
     176 +const CountryEditionOverviewComponent: FunctionComponent<CountryEditionOverviewProps> = ({
     177 + countryRef,
     178 + context,
     179 + enableReferences = false,
     180 + handleClose,
     181 +}) => {
     182 + const { t } = useFormatter();
     183 + const country = useFragment(countryEditionOverviewFragment, countryRef);
     184 + 
     185 + const createdBy = convertCreatedBy(country);
     186 + const objectMarking = convertMarkings(country);
     187 + const status = convertStatus(t, country);
     188 + 
     189 + const [commitRelationAdd] = useMutation(countryMutationRelationAdd);
     190 + const [commitRelationDelete] = useMutation(countryMutationRelationDelete);
     191 + const [commitFieldPatch] = useMutation(countryMutationFieldPatch);
     192 + const [commitEditionFocus] = useMutation(countryEditionOverviewFocus);
     193 + 
     194 + const handleChangeFocus = (name: string) => {
     195 + commitEditionFocus({
     196 + variables: {
     197 + id: country.id,
     198 + input: {
     199 + focusOn: name,
     200 + },
     201 + },
     202 + });
     203 + };
     204 + 
     205 + const onSubmit: FormikConfig<CountryEditionFormValues>['onSubmit'] = (values, { setSubmitting }) => {
     206 + const commitMessage = values.message;
     207 + const references = R.pluck('value', values.references || []);
     208 + const inputValues = R.pipe(
     209 + R.dissoc('message'),
     210 + R.dissoc('references'),
     211 + R.assoc('x_opencti_workflow_id', values.x_opencti_workflow_id?.value),
     212 + R.assoc('createdBy', values.createdBy?.value),
     213 + R.assoc('objectMarking', R.pluck('value', values.objectMarking)),
     214 + R.toPairs,
     215 + R.map((n) => ({
     216 + key: n[0],
     217 + value: adaptFieldValue(n[1]),
     218 + })),
     219 + )(values);
     220 + commitFieldPatch({
     221 + variables: {
     222 + id: country.id,
     223 + input: inputValues,
     224 + commitMessage:
     225 + commitMessage && commitMessage.length > 0 ? commitMessage : null,
     226 + references,
     227 + },
     228 + onCompleted: () => {
     229 + setSubmitting(false);
     230 + handleClose();
     231 + },
     232 + });
     233 + };
     234 + 
     235 + const handleChangeCreatedBy = (_: string, value: Option) => {
     236 + if (!enableReferences) {
     237 + commitRelationAdd({
     238 + variables: {
     239 + id: country.id,
     240 + input: { key: 'createdBy', value: value.value ?? '' },
     241 + },
     242 + });
     243 + }
     244 + };
     245 + 
     246 + const handleChangeObjectMarking = (_: string, values: Option[]) => {
     247 + if (!enableReferences) {
     248 + const currentMarkingDefinitions = (country.objectMarking?.edges ?? []).map((n) => ({ label: n?.node.definition, value: n?.node.id }));
     249 + const added = R.difference(values, currentMarkingDefinitions).at(0);
     250 + const removed = R.difference(currentMarkingDefinitions, values).at(0);
     251 + if (added) {
     252 + commitRelationAdd({
     253 + variables: {
     254 + id: country.id,
     255 + input: {
     256 + toId: added.value,
     257 + relationship_type: 'object-marking',
     258 + },
     259 + },
     260 + });
     261 + }
     262 + if (removed) {
     263 + commitRelationDelete({
     264 + variables: {
     265 + id: country.id,
     266 + toId: removed.value,
     267 + relationship_type: 'object-marking',
     268 + },
     269 + });
     270 + }
     271 + }
     272 + };
     273 + const handleSubmitField = (name: string, value: Option | string) => {
     274 + if (!enableReferences) {
     275 + let finalValue: unknown = value as string;
     276 + if (name === 'x_opencti_workflow_id') {
     277 + finalValue = (value as Option).value;
     278 + }
     279 + countryValidation(t)
     280 + .validateAt(name, { [name]: value })
     281 + .then(() => {
     282 + commitFieldPatch({
     283 + variables: {
     284 + id: country.id,
     285 + input: { key: name, value: finalValue ?? '' },
     286 + },
     287 + });
     288 + })
     289 + .catch(() => false);
     290 + }
     291 + };
     292 + 
     293 + const initialValues = R.pipe(
     294 + R.assoc('createdBy', createdBy),
     295 + R.assoc('objectMarking', objectMarking),
     296 + R.assoc('x_opencti_workflow_id', status),
     297 + R.pick([
     298 + 'name',
     299 + 'description',
     300 + 'latitude',
     301 + 'longitude',
     302 + 'createdBy',
     303 + 'objectMarking',
     304 + 'x_opencti_workflow_id',
     305 + ]),
     306 + )(country);
     307 + 
     308 + return (
     309 + <Formik
     310 + enableReinitialize={true}
     311 + initialValues={initialValues as never}
     312 + validationSchema={countryValidation(t)}
     313 + onSubmit={onSubmit}
     314 + >
     315 + {({
     316 + submitForm,
     317 + isSubmitting,
     318 + validateForm,
     319 + setFieldValue,
     320 + values,
     321 + }) => (
     322 + <Form style={{ margin: '20px 0 20px 0' }}>
     323 + <Field
     324 + component={TextField}
     325 + variant="standard"
     326 + name="name"
     327 + label={t('Name')}
     328 + fullWidth={true}
     329 + onFocus={handleChangeFocus}
     330 + onSubmit={handleSubmitField}
     331 + helperText={
     332 + <SubscriptionFocus context={context} fieldName="name" />
     333 + }
     334 + />
     335 + <Field
     336 + component={MarkDownField}
     337 + name="description"
     338 + label={t('Description')}
     339 + fullWidth={true}
     340 + multiline={true}
     341 + rows="4"
     342 + style={{ marginTop: 20 }}
     343 + onFocus={handleChangeFocus}
     344 + onSubmit={handleSubmitField}
     345 + helperText={
     346 + <SubscriptionFocus context={context} fieldName="description" />
     347 + }
     348 + />
     349 + <Field
     350 + component={TextField}
     351 + variant="standard"
     352 + style={{ marginTop: 20 }}
     353 + name="latitude"
     354 + label={t('Latitude')}
     355 + fullWidth={true}
     356 + onFocus={handleChangeFocus}
     357 + onSubmit={handleSubmitField}
     358 + helperText={
     359 + <SubscriptionFocus context={context} fieldName="latitude" />
     360 + }
     361 + />
     362 + <Field
     363 + component={TextField}
     364 + variant="standard"
     365 + style={{ marginTop: 20 }}
     366 + name="longitude"
     367 + label={t('Longitude')}
     368 + fullWidth={true}
     369 + onFocus={handleChangeFocus}
     370 + onSubmit={handleSubmitField}
     371 + helperText={
     372 + <SubscriptionFocus context={context} fieldName="longitude" />
     373 + }
     374 + />
     375 + {country?.workflowEnabled && (
     376 + <StatusField
     377 + name="x_opencti_workflow_id"
     378 + type="Country"
     379 + onFocus={handleChangeFocus}
     380 + onChange={handleSubmitField}
     381 + setFieldValue={setFieldValue}
     382 + style={{ marginTop: 20 }}
     383 + helpertext={
     384 + <SubscriptionFocus
     385 + context={context}
     386 + fieldName="x_opencti_workflow_id"
     387 + />
     388 + }
     389 + />
     390 + )}
     391 + <CreatedByField
     392 + name="createdBy"
     393 + style={{ marginTop: 20, width: '100%' }}
     394 + setFieldValue={setFieldValue}
     395 + helpertext={
     396 + <SubscriptionFocus context={context} fieldName="createdBy" />
     397 + }
     398 + onChange={handleChangeCreatedBy}
     399 + />
     400 + <ObjectMarkingField
     401 + name="objectMarking"
     402 + style={{ marginTop: 20, width: '100%' }}
     403 + helpertext={
     404 + <SubscriptionFocus
     405 + context={context}
     406 + fieldname="objectMarking"
     407 + />
     408 + }
     409 + onChange={handleChangeObjectMarking}
     410 + />
     411 + {enableReferences && (
     412 + <CommitMessage
     413 + submitForm={submitForm}
     414 + disabled={isSubmitting}
     415 + validateForm={validateForm}
     416 + setFieldValue={setFieldValue}
     417 + values={values}
     418 + id={country.id}
     419 + />
     420 + )}
     421 + </Form>
     422 + )}
     423 + </Formik>
     424 + );
     425 +};
     426 + 
     427 +export default CountryEditionOverviewComponent;
     428 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryKnowledge.js
    1  -import React, { Component } from 'react';
    2  -import PropTypes from 'prop-types';
    3  -import { Route, Switch, withRouter } from 'react-router-dom';
    4  -import { compose } from 'ramda';
    5  -import { graphql, createFragmentContainer } from 'react-relay';
    6  -import withStyles from '@mui/styles/withStyles';
    7  -import inject18n from '../../../../components/i18n';
    8  -import EntityStixCoreRelationships from '../../common/stix_core_relationships/EntityStixCoreRelationships';
    9  -import StixDomainObjectKnowledge from '../../common/stix_domain_objects/StixDomainObjectKnowledge';
    10  -import StixCoreRelationship from '../../common/stix_core_relationships/StixCoreRelationship';
    11  -import CountryPopover from './CountryPopover';
    12  -import StixDomainObjectHeader from '../../common/stix_domain_objects/StixDomainObjectHeader';
    13  -import StixCoreObjectStixCyberObservables from '../../observations/stix_cyber_observables/StixCoreObjectStixCyberObservables';
    14  -import StixSightingRelationship from '../../events/stix_sighting_relationships/StixSightingRelationship';
    15  - 
    16  -const styles = () => ({
    17  - container: {
    18  - margin: 0,
    19  - padding: '0 200px 0 0',
    20  - },
    21  -});
    22  - 
    23  -class CountryKnowledgeComponent extends Component {
    24  - render() {
    25  - const { classes, country } = this.props;
    26  - const link = `/dashboard/locations/countries/${country.id}/knowledge`;
    27  - return (
    28  - <div className={classes.container}>
    29  - <StixDomainObjectHeader
    30  - disableSharing={true}
    31  - stixDomainObject={country}
    32  - PopoverComponent={<CountryPopover />}
    33  - variant="noaliases"
    34  - />
    35  - <Switch>
    36  - <Route
    37  - exact
    38  - path="/dashboard/locations/countries/:countryId/knowledge/relations/:relationId"
    39  - render={(routeProps) => (
    40  - <StixCoreRelationship
    41  - entityId={country.id}
    42  - paddingRight={true}
    43  - {...routeProps}
    44  - />
    45  - )}
    46  - />
    47  - <Route
    48  - exact
    49  - path="/dashboard/locations/countries/:countryId/knowledge/sightings/:sightingId"
    50  - render={(routeProps) => (
    51  - <StixSightingRelationship
    52  - entityId={country.id}
    53  - paddingRight={true}
    54  - {...routeProps}
    55  - />
    56  - )}
    57  - />
    58  - <Route
    59  - exact
    60  - path="/dashboard/locations/countries/:countryId/knowledge/overview"
    61  - render={(routeProps) => (
    62  - <StixDomainObjectKnowledge
    63  - stixDomainObjectId={country.id}
    64  - stixDomainObjectType="Country"
    65  - {...routeProps}
    66  - />
    67  - )}
    68  - />
    69  - <Route
    70  - exact
    71  - path="/dashboard/locations/countries/:countryId/knowledge/related"
    72  - render={(routeProps) => (
    73  - <EntityStixCoreRelationships
    74  - entityId={country.id}
    75  - relationshipTypes={['related-to']}
    76  - targetStixDomainObjectTypes={[
    77  - 'Threat-Actor',
    78  - 'Intrusion-Set',
    79  - 'Campaign',
    80  - 'Incident',
    81  - 'Malware',
    82  - 'Tool',
    83  - 'Vulnerability',
    84  - 'Individual',
    85  - 'Organization',
    86  - 'Sector',
    87  - 'Region',
    88  - 'Country',
    89  - 'City',
    90  - 'Position',
    91  - ]}
    92  - entityLink={link}
    93  - allDirections={true}
    94  - {...routeProps}
    95  - />
    96  - )}
    97  - />
    98  - <Route
    99  - exact
    100  - path="/dashboard/locations/countries/:countryId/knowledge/cities"
    101  - render={(routeProps) => (
    102  - <EntityStixCoreRelationships
    103  - entityId={country.id}
    104  - relationshipTypes={['located-at']}
    105  - targetStixDomainObjectTypes={['City']}
    106  - entityLink={link}
    107  - isRelationReversed={true}
    108  - {...routeProps}
    109  - />
    110  - )}
    111  - />
    112  - <Route
    113  - exact
    114  - path="/dashboard/locations/countries/:countryId/knowledge/organizations"
    115  - render={(routeProps) => (
    116  - <EntityStixCoreRelationships
    117  - entityId={country.id}
    118  - relationshipTypes={['located-at']}
    119  - targetStixDomainObjectTypes={['Organization']}
    120  - entityLink={link}
    121  - isRelationReversed={true}
    122  - {...routeProps}
    123  - />
    124  - )}
    125  - />
    126  - <Route
    127  - exact
    128  - path="/dashboard/locations/countries/:countryId/knowledge/threat_actors"
    129  - render={(routeProps) => (
    130  - <EntityStixCoreRelationships
    131  - entityId={country.id}
    132  - relationshipTypes={['targets']}
    133  - targetStixDomainObjectTypes={['Threat-Actor']}
    134  - entityLink={link}
    135  - isRelationReversed={true}
    136  - {...routeProps}
    137  - />
    138  - )}
    139  - />
    140  - <Route
    141  - exact
    142  - path="/dashboard/locations/countries/:countryId/knowledge/intrusion_sets"
    143  - render={(routeProps) => (
    144  - <EntityStixCoreRelationships
    145  - entityId={country.id}
    146  - relationshipTypes={['targets', 'originates-from']}
    147  - targetStixDomainObjectTypes={['Campaign', 'Intrusion-Set']}
    148  - entityLink={link}
    149  - isRelationReversed={true}
    150  - {...routeProps}
    151  - />
    152  - )}
    153  - />
    154  - <Route
    155  - exact
    156  - path="/dashboard/locations/countries/:countryId/knowledge/campaigns"
    157  - render={(routeProps) => (
    158  - <EntityStixCoreRelationships
    159  - entityId={country.id}
    160  - relationshipTypes={['targets']}
    161  - targetStixDomainObjectTypes={['Campaign']}
    162  - entityLink={link}
    163  - isRelationReversed={true}
    164  - {...routeProps}
    165  - />
    166  - )}
    167  - />
    168  - <Route
    169  - exact
    170  - path="/dashboard/locations/countries/:countryId/knowledge/incidents"
    171  - render={(routeProps) => (
    172  - <EntityStixCoreRelationships
    173  - entityId={country.id}
    174  - relationshipTypes={['targets']}
    175  - targetStixDomainObjectTypes={['Incident']}
    176  - entityLink={link}
    177  - isRelationReversed={true}
    178  - {...routeProps}
    179  - />
    180  - )}
    181  - />
    182  - <Route
    183  - exact
    184  - path="/dashboard/locations/countries/:countryId/knowledge/malwares"
    185  - render={(routeProps) => (
    186  - <EntityStixCoreRelationships
    187  - entityId={country.id}
    188  - relationshipTypes={['targets']}
    189  - targetStixDomainObjectTypes={['Malware']}
    190  - entityLink={link}
    191  - isRelationReversed={true}
    192  - {...routeProps}
    193  - />
    194  - )}
    195  - />
    196  - <Route
    197  - exact
    198  - path="/dashboard/locations/countries/:countryId/knowledge/attack_patterns"
    199  - render={(routeProps) => (
    200  - <EntityStixCoreRelationships
    201  - entityId={country.id}
    202  - relationshipTypes={['targets']}
    203  - targetStixDomainObjectTypes={['Attack-Pattern']}
    204  - entityLink={link}
    205  - isRelationReversed={true}
    206  - {...routeProps}
    207  - />
    208  - )}
    209  - />
    210  - <Route
    211  - exact
    212  - path="/dashboard/locations/countries/:countryId/knowledge/tools"
    213  - render={(routeProps) => (
    214  - <EntityStixCoreRelationships
    215  - entityId={country.id}
    216  - relationshipTypes={['targets']}
    217  - targetStixDomainObjectTypes={['Tool']}
    218  - entityLink={link}
    219  - isRelationReversed={true}
    220  - {...routeProps}
    221  - />
    222  - )}
    223  - />
    224  - <Route
    225  - exact
    226  - path="/dashboard/locations/countries/:countryId/knowledge/observables"
    227  - render={(routeProps) => (
    228  - <StixCoreObjectStixCyberObservables
    229  - stixCoreObjectId={country.id}
    230  - stixCoreObjectLink={link}
    231  - noRightBar={true}
    232  - {...routeProps}
    233  - />
    234  - )}
    235  - />
    236  - <Route
    237  - exact
    238  - path="/dashboard/locations/countries/:countryId/knowledge/sightings"
    239  - render={(routeProps) => (
    240  - <EntityStixSightingRelationships
    241  - entityId={country.id}
    242  - entityLink={link}
    243  - noRightBar={true}
    244  - isTo={true}
    245  - {...routeProps}
    246  - />
    247  - )}
    248  - />
    249  - </Switch>
    250  - </div>
    251  - );
    252  - }
    253  -}
    254  - 
    255  -CountryKnowledgeComponent.propTypes = {
    256  - country: PropTypes.object,
    257  - classes: PropTypes.object,
    258  - t: PropTypes.func,
    259  -};
    260  - 
    261  -const CountryKnowledge = createFragmentContainer(CountryKnowledgeComponent, {
    262  - country: graphql`
    263  - fragment CountryKnowledge_country on Country {
    264  - id
    265  - name
    266  - x_opencti_aliases
    267  - }
    268  - `,
    269  -});
    270  - 
    271  -export default compose(
    272  - inject18n,
    273  - withRouter,
    274  - withStyles(styles),
    275  -)(CountryKnowledge);
    276  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryKnowledge.tsx
     1 +// TODO Remove this when V6
     2 +// eslint-disable-next-line @typescript-eslint/ban-ts-comment
     3 +// @ts-nocheck
     4 +import React from 'react';
     5 +import { Route, Switch } from 'react-router-dom';
     6 +import { graphql, useFragment } from 'react-relay';
     7 +import makeStyles from '@mui/styles/makeStyles';
     8 +import EntityStixCoreRelationships from '../../common/stix_core_relationships/EntityStixCoreRelationships';
     9 +import StixDomainObjectKnowledge from '../../common/stix_domain_objects/StixDomainObjectKnowledge';
     10 +import StixCoreRelationship from '../../common/stix_core_relationships/StixCoreRelationship';
     11 +import CountryPopover from './CountryPopover';
     12 +import StixDomainObjectHeader from '../../common/stix_domain_objects/StixDomainObjectHeader';
     13 +import StixCoreObjectStixCyberObservables from '../../observations/stix_cyber_observables/StixCoreObjectStixCyberObservables';
     14 +import StixSightingRelationship from '../../events/stix_sighting_relationships/StixSightingRelationship';
     15 +import { CountryKnowledge_country$key } from './__generated__/CountryKnowledge_country.graphql';
     16 +import EntityStixSightingRelationships from '../../events/stix_sighting_relationships/EntityStixSightingRelationships';
     17 +import region from '../regions/Region';
     18 + 
     19 +const useStyles = makeStyles(() => ({
     20 + container: {
     21 + margin: 0,
     22 + padding: '0 200px 0 0',
     23 + },
     24 +}));
     25 + 
     26 +const countryKnowledgeFragment = graphql`
     27 + fragment CountryKnowledge_country on Country {
     28 + id
     29 + name
     30 + x_opencti_aliases
     31 + }
     32 +`;
     33 + 
     34 +const CountryKnowledgeComponent = ({ countryData }: { countryData: CountryKnowledge_country$key }) => {
     35 + const classes = useStyles();
     36 + const country = useFragment<CountryKnowledge_country$key>(countryKnowledgeFragment, countryData);
     37 + const link = `/dashboard/locations/countries/${country.id}/knowledge`;
     38 + 
     39 + return (
     40 + <div className={classes.container}>
     41 + <StixDomainObjectHeader
     42 + disableSharing={true}
     43 + stixDomainObject={country}
     44 + PopoverComponent={CountryPopover}
     45 + variant="noaliases"
     46 + />
     47 + <Switch>
     48 + <Route
     49 + exact
     50 + path="/dashboard/locations/countries/:countryId/knowledge/relations/:relationId"
     51 + render={(routeProps: any) => (
     52 + <StixCoreRelationship
     53 + entityId={country.id}
     54 + paddingRight={true}
     55 + {...routeProps}
     56 + />
     57 + )}
     58 + />
     59 + <Route
     60 + exact
     61 + path="/dashboard/locations/countries/:countryId/knowledge/sightings/:sightingId"
     62 + render={(routeProps: any) => (
     63 + <StixSightingRelationship
     64 + entityId={country.id}
     65 + paddingRight={true}
     66 + {...routeProps}
     67 + />
     68 + )}
     69 + />
     70 + <Route
     71 + exact
     72 + path="/dashboard/locations/countries/:countryId/knowledge/overview"
     73 + render={(routeProps: any) => (
     74 + <StixDomainObjectKnowledge
     75 + stixDomainObjectId={country.id}
     76 + stixDomainObjectType="Country"
     77 + {...routeProps}
     78 + />
     79 + )}
     80 + />
     81 + <Route
     82 + exact
     83 + path="/dashboard/locations/countries/:countryId/knowledge/related"
     84 + render={(routeProps: any) => (
     85 + <EntityStixCoreRelationships
     86 + entityId={country.id}
     87 + relationshipTypes={['related-to']}
     88 + targetStixDomainObjectTypes={[
     89 + 'Threat-Actor',
     90 + 'Intrusion-Set',
     91 + 'Campaign',
     92 + 'Incident',
     93 + 'Malware',
     94 + 'Tool',
     95 + 'Vulnerability',
     96 + 'Individual',
     97 + 'Organization',
     98 + 'Sector',
     99 + 'Region',
     100 + 'Country',
     101 + 'City',
     102 + 'Position',
     103 + ]}
     104 + entityLink={link}
     105 + allDirections={true}
     106 + {...routeProps}
     107 + />
     108 + )}
     109 + />
     110 + <Route
     111 + exact
     112 + path="/dashboard/locations/countries/:countryId/knowledge/countries"
     113 + render={(routeProps: any) => (
     114 + <EntityStixCoreRelationships
     115 + entityId={country.id}
     116 + relationshipTypes={['located-at']}
     117 + targetStixDomainObjectTypes={['City']}
     118 + entityLink={link}
     119 + isRelationReversed={true}
     120 + {...routeProps}
     121 + />
     122 + )}
     123 + />
     124 + <Route
     125 + exact
     126 + path="/dashboard/locations/countries/:countryId/knowledge/organizations"
     127 + render={(routeProps: any) => (
     128 + <EntityStixCoreRelationships
     129 + entityId={country.id}
     130 + relationshipTypes={['located-at']}
     131 + targetStixDomainObjectTypes={['Organization']}
     132 + entityLink={link}
     133 + isRelationReversed={true}
     134 + {...routeProps}
     135 + />
     136 + )}
     137 + />
     138 + <Route
     139 + exact
     140 + path="/dashboard/locations/countries/:countryId/knowledge/threat_actors"
     141 + render={(routeProps: any) => (
     142 + <EntityStixCoreRelationships
     143 + entityId={country.id}
     144 + relationshipTypes={['targets']}
     145 + targetStixDomainObjectTypes={['Threat-Actor']}
     146 + entityLink={link}
     147 + isRelationReversed={true}
     148 + {...routeProps}
     149 + />
     150 + )}
     151 + />
     152 + <Route
     153 + exact
     154 + path="/dashboard/locations/countries/:countryId/knowledge/intrusion_sets"
     155 + render={(routeProps: any) => (
     156 + <EntityStixCoreRelationships
     157 + entityId={country.id}
     158 + relationshipTypes={['targets', 'originates-from']}
     159 + targetStixDomainObjectTypes={['Campaign', 'Intrusion-Set']}
     160 + entityLink={link}
     161 + isRelationReversed={true}
     162 + {...routeProps}
     163 + />
     164 + )}
     165 + />
     166 + <Route
     167 + exact
     168 + path="/dashboard/locations/countries/:countryId/knowledge/campaigns"
     169 + render={(routeProps: any) => (
     170 + <EntityStixCoreRelationships
     171 + entityId={country.id}
     172 + relationshipTypes={['targets']}
     173 + targetStixDomainObjectTypes={['Campaign']}
     174 + entityLink={link}
     175 + isRelationReversed={true}
     176 + {...routeProps}
     177 + />
     178 + )}
     179 + />
     180 + <Route
     181 + exact
     182 + path="/dashboard/locations/countries/:countryId/knowledge/incidents"
     183 + render={(routeProps: any) => (
     184 + <EntityStixCoreRelationships
     185 + entityId={country.id}
     186 + relationshipTypes={['targets']}
     187 + targetStixDomainObjectTypes={['Incident']}
     188 + entityLink={link}
     189 + isRelationReversed={true}
     190 + {...routeProps}
     191 + />
     192 + )}
     193 + />
     194 + <Route
     195 + exact
     196 + path="/dashboard/locations/countries/:countryId/knowledge/malwares"
     197 + render={(routeProps: any) => (
     198 + <EntityStixCoreRelationships
     199 + entityId={country.id}
     200 + relationshipTypes={['targets']}
     201 + targetStixDomainObjectTypes={['Malware']}
     202 + entityLink={link}
     203 + isRelationReversed={true}
     204 + {...routeProps}
     205 + />
     206 + )}
     207 + />
     208 + <Route
     209 + exact
     210 + path="/dashboard/locations/countries/:countryId/knowledge/attack_patterns"
     211 + render={(routeProps: any) => (
     212 + <EntityStixCoreRelationships
     213 + entityId={country.id}
     214 + relationshipTypes={['targets']}
     215 + targetStixDomainObjectTypes={['Attack-Pattern']}
     216 + entityLink={link}
     217 + isRelationReversed={true}
     218 + {...routeProps}
     219 + />
     220 + )}
     221 + />
     222 + <Route
     223 + exact
     224 + path="/dashboard/locations/countries/:countryId/knowledge/tools"
     225 + render={(routeProps: any) => (
     226 + <EntityStixCoreRelationships
     227 + entityId={country.id}
     228 + relationshipTypes={['targets']}
     229 + targetStixDomainObjectTypes={['Tool']}
     230 + entityLink={link}
     231 + isRelationReversed={true}
     232 + {...routeProps}
     233 + />
     234 + )}
     235 + />
     236 + <Route
     237 + exact
     238 + path="/dashboard/locations/countries/:countryId/knowledge/observables"
     239 + render={(routeProps: any) => (
     240 + <StixCoreObjectStixCyberObservables
     241 + stixCoreObjectId={country.id}
     242 + stixCoreObjectLink={link}
     243 + noRightBar={true}
     244 + {...routeProps}
     245 + />
     246 + )}
     247 + />
     248 + <Route
     249 + exact
     250 + path="/dashboard/locations/countries/:countryId/knowledge/sightings"
     251 + render={(routeProps: any) => (
     252 + <EntityStixSightingRelationships
     253 + entityId={country.id}
     254 + entityLink={link}
     255 + noRightBar={true}
     256 + isTo={true}
     257 + {...routeProps}
     258 + />
     259 + )}
     260 + />
     261 + </Switch>
     262 + </div>
     263 + );
     264 +};
     265 + 
     266 +export default CountryKnowledgeComponent;
     267 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryLine.tsx
    skipped 4 lines
    5 5  import ListItemText from '@mui/material/ListItemText';
    6 6  import { KeyboardArrowRightOutlined, FlagOutlined } from '@mui/icons-material';
    7 7  import Skeleton from '@mui/material/Skeleton';
    8  -import { createFragmentContainer, graphql } from 'react-relay';
     8 +import { graphql, useFragment } from 'react-relay';
    9 9  import makeStyles from '@mui/styles/makeStyles';
    10 10  import { Theme } from '@mui/material/styles/createTheme';
    11 11  import { useFormatter } from '../../../../components/i18n';
    12  -import { CountryLine_node$data } from './__generated__/CountryLine_node.graphql';
     12 +import { CountryLine_node$key } from './__generated__/CountryLine_node.graphql';
    13 13  import { DataColumns } from '../../../../components/list_lines';
    14 14   
    15 15  const useStyles = makeStyles<Theme>((theme) => ({
    16  - item: {
    17  - paddingLeft: theme.spacing(8),
    18  - },
     16 + item: {},
    19 17   itemIcon: {
    20 18   color: theme.palette.primary.main,
    21 19   },
     20 + name: {
     21 + width: '60%',
     22 + height: 20,
     23 + lineHeight: '20px',
     24 + float: 'left',
     25 + whiteSpace: 'nowrap',
     26 + overflow: 'hidden',
     27 + textOverflow: 'ellipsis',
     28 + },
     29 + createdAt: {
     30 + width: '20%',
     31 + height: 20,
     32 + lineHeight: '20px',
     33 + float: 'left',
     34 + whiteSpace: 'nowrap',
     35 + overflow: 'hidden',
     36 + textOverflow: 'ellipsis',
     37 + color: '#a5a5a5',
     38 + },
     39 + modifiedAt: {
     40 + width: '20%',
     41 + height: 20,
     42 + lineHeight: '20px',
     43 + float: 'left',
     44 + whiteSpace: 'nowrap',
     45 + overflow: 'hidden',
     46 + textOverflow: 'ellipsis',
     47 + color: '#a5a5a5',
     48 + },
    22 49   text: {
    23 50   fontSize: 12,
    24 51   },
    skipped 12 lines
    37 64  }));
    38 65   
    39 66  interface CountryLineProps {
    40  - node: CountryLine_node$data
     67 + node: CountryLine_node$key
    41 68   dataColumns: DataColumns
    42 69  }
    43  -const classes = useStyles();
     70 + 
     71 +const countryLineFragment = graphql`
     72 + fragment CountryLine_node on Country {
     73 + id
     74 + name
     75 + created
     76 + modified
     77 + }
     78 +`;
    44 79   
    45  -const CountryLineComponent: FunctionComponent<CountryLineProps> = ({ node }) => {
     80 +export const CountryLineComponent: FunctionComponent<CountryLineProps> = ({ dataColumns, node }) => {
     81 + const classes = useStyles();
    46 82   const { fd } = useFormatter();
     83 + const data = useFragment(countryLineFragment, node);
    47 84   
    48 85   return (
    49 86   <ListItem
    skipped 1 lines
    51 88   divider={true}
    52 89   button={true}
    53 90   component={Link}
    54  - to={`/dashboard/locations/countries/${node.id}`}
     91 + to={`/dashboard/locations/countries/${data.id}`}
    55 92   >
    56 93   <ListItemIcon classes={{ root: classes.itemIcon }}>
    57 94   <FlagOutlined />
    58 95   </ListItemIcon>
    59  - <ListItemText classes={{ primary: classes.text }} primary={node.name} />
     96 + <ListItemText
     97 + primary={
     98 + <div>
     99 + <div className={classes.name}>
     100 + {data.name}
     101 + </div>
     102 + <div
     103 + className={classes.createdAt}
     104 + >
     105 + {fd(data.created)}
     106 + </div>
     107 + <div
     108 + className={classes.modifiedAt}
     109 + >
     110 + {fd(data.modified)}
     111 + </div>
     112 + </div>
     113 + }
     114 + />
    60 115   <ListItemIcon classes={{ root: classes.goIcon }}>
    61 116   <KeyboardArrowRightOutlined />
    62 117   </ListItemIcon>
    skipped 1 lines
    64 119   );
    65 120  };
    66 121   
    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  -});
    77  - 
    78  -const CountryLineDummyComponent: FunctionComponent<CountryLineProps> = ({ dataColumns }) => {
     122 +export const CountryLineDummyComponent = ({ dataColumns }: { dataColumns: DataColumns }) => {
     123 + const classes = useStyles();
    79 124   return (
    80 125   <ListItem classes={{ root: classes.item }} divider={true}>
    81 126   <ListItemIcon classes={{ root: classes.itemIcon }}>
    skipped 50 lines
    132 177   );
    133 178  };
    134 179   
    135  -export default { CountryLineComponent, CountryLineDummyComponent };
    136  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryOverview.js
    1  -import React, { Component } from 'react';
    2  -import * as PropTypes from 'prop-types';
    3  -import { compose, propOr } from 'ramda';
    4  -import { graphql, createFragmentContainer } from 'react-relay';
    5  -import Markdown from 'react-markdown';
    6  -import withStyles from '@mui/styles/withStyles';
    7  -import Paper from '@mui/material/Paper';
    8  -import Typography from '@mui/material/Typography';
    9  -import remarkGfm from 'remark-gfm';
    10  -import remarkParse from 'remark-parse';
    11  -import inject18n from '../../../../components/i18n';
    12  -import ItemAuthor from '../../../../components/ItemAuthor';
    13  - 
    14  -const styles = () => ({
    15  - paper: {
    16  - height: '100%',
    17  - minHeight: '100%',
    18  - margin: '10px 0 0 0',
    19  - padding: '15px',
    20  - borderRadius: 6,
    21  - },
    22  -});
    23  - 
    24  -class CountryOverviewComponent extends Component {
    25  - render() {
    26  - const { t, fldt, classes, country } = this.props;
    27  - return (
    28  - <div style={{ height: '100%' }} className="break">
    29  - <Typography variant="h4" gutterBottom={true}>
    30  - {t('Information')}
    31  - </Typography>
    32  - <Paper classes={{ root: classes.paper }} variant="outlined">
    33  - <Typography variant="h3" gutterBottom={true}>
    34  - {t('Creation date')}
    35  - </Typography>
    36  - {fldt(country.created)}
    37  - <Typography
    38  - variant="h3"
    39  - gutterBottom={true}
    40  - style={{ marginTop: 20 }}
    41  - >
    42  - {t('Modification date')}
    43  - </Typography>
    44  - {fldt(country.modified)}
    45  - <Typography
    46  - variant="h3"
    47  - gutterBottom={true}
    48  - style={{ marginTop: 20 }}
    49  - >
    50  - {t('Author')}
    51  - </Typography>
    52  - <ItemAuthor createdBy={propOr(null, 'createdBy', country)} />
    53  - <Typography
    54  - variant="h3"
    55  - gutterBottom={true}
    56  - style={{ marginTop: 20 }}
    57  - >
    58  - {t('Description')}
    59  - </Typography>
    60  - <Markdown
    61  - remarkPlugins={[remarkGfm, remarkParse]}
    62  - parserOptions={{ commonmark: true }}
    63  - className="markdown"
    64  - >
    65  - {country.description}
    66  - </Markdown>
    67  - </Paper>
    68  - </div>
    69  - );
    70  - }
    71  -}
    72  - 
    73  -CountryOverviewComponent.propTypes = {
    74  - country: PropTypes.object,
    75  - classes: PropTypes.object,
    76  - t: PropTypes.func,
    77  - fldt: PropTypes.func,
    78  -};
    79  - 
    80  -const CountryOverview = createFragmentContainer(CountryOverviewComponent, {
    81  - country: graphql`
    82  - fragment CountryOverview_country on Country {
    83  - id
    84  - name
    85  - description
    86  - created
    87  - modified
    88  - createdBy {
    89  - ... on Identity {
    90  - id
    91  - name
    92  - entity_type
    93  - }
    94  - }
    95  - }
    96  - `,
    97  -});
    98  - 
    99  -export default compose(inject18n, withStyles(styles))(CountryOverview);
    100  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryOverview.tsx
     1 +// TODO Remove this when V6
     2 +// eslint-disable-next-line @typescript-eslint/ban-ts-comment
     3 +// @ts-nocheck
     4 +import React, { FunctionComponent } from 'react';
     5 +import { propOr } from 'ramda';
     6 +import { graphql, useFragment } from 'react-relay';
     7 +import Markdown from 'react-markdown';
     8 +import Paper from '@mui/material/Paper';
     9 +import Typography from '@mui/material/Typography';
     10 +import remarkGfm from 'remark-gfm';
     11 +import remarkParse from 'remark-parse';
     12 +import makeStyles from '@mui/styles/makeStyles';
     13 +import { useFormatter } from '../../../../components/i18n';
     14 +import ItemAuthor from '../../../../components/ItemAuthor';
     15 +import { CountryOverview_country$key } from './__generated__/CountryOverview_country.graphql';
     16 + 
     17 +const useStyles = makeStyles(() => ({
     18 + paper: {
     19 + height: '100%',
     20 + minHeight: '100%',
     21 + margin: '10px 0 0 0',
     22 + padding: '15px',
     23 + borderRadius: 6,
     24 + },
     25 +}));
     26 + 
     27 +export const countryOverviewFragment = graphql`
     28 + fragment CountryOverview_country on Country {
     29 + id
     30 + name
     31 + description
     32 + created
     33 + modified
     34 + createdBy {
     35 + ... on Identity {
     36 + id
     37 + name
     38 + entity_type
     39 + }
     40 + }
     41 + }
     42 +`;
     43 + 
     44 +interface CountryOverviewProps {
     45 + countryRef: CountryOverview_country$key,
     46 +}
     47 + 
     48 +const CountryOverviewComponent: FunctionComponent<CountryOverviewProps> = ({ countryRef }) => {
     49 + const { t, fldt } = useFormatter();
     50 + const classes = useStyles();
     51 + const country = useFragment(countryOverviewFragment, countryRef);
     52 + 
     53 + return (
     54 + <div style={{ height: '100%' }} className="break">
     55 + <Typography variant="h4" gutterBottom={true}>
     56 + {t('Information')}
     57 + </Typography>
     58 + <Paper classes={{ root: classes.paper }} variant="outlined">
     59 + <Typography variant="h3" gutterBottom={true}>
     60 + {t('Creation date')}
     61 + </Typography>
     62 + {fldt(country.created)}
     63 + <Typography
     64 + variant="h3"
     65 + gutterBottom={true}
     66 + style={{ marginTop: 20 }}
     67 + >
     68 + {t('Modification date')}
     69 + </Typography>
     70 + {fldt(country.modified)}
     71 + <Typography
     72 + variant="h3"
     73 + gutterBottom={true}
     74 + style={{ marginTop: 20 }}
     75 + >
     76 + {t('Author')}
     77 + </Typography>
     78 + <ItemAuthor createdBy={propOr(null, 'createdBy', country)} />
     79 + <Typography
     80 + variant="h3"
     81 + gutterBottom={true}
     82 + style={{ marginTop: 20 }}
     83 + >
     84 + {t('Description')}
     85 + </Typography>
     86 + <Markdown
     87 + remarkPlugins={[remarkGfm, remarkParse]}
     88 + parserOptions={{ commonmark: true }}
     89 + className="markdown"
     90 + >
     91 + {country.description}
     92 + </Markdown>
     93 + </Paper>
     94 + </div>
     95 + );
     96 +};
     97 + 
     98 +export default CountryOverviewComponent;
     99 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryPopover.js
    1  -import React, { Component } from 'react';
    2  -import * as PropTypes from 'prop-types';
    3  -import { compose } from 'ramda';
    4  -import { withRouter } from 'react-router-dom';
    5  -import withStyles from '@mui/styles/withStyles';
    6  -import Menu from '@mui/material/Menu';
    7  -import MenuItem from '@mui/material/MenuItem';
    8  -import Button from '@mui/material/Button';
    9  -import IconButton from '@mui/material/IconButton';
    10  -import Drawer from '@mui/material/Drawer';
    11  -import Dialog from '@mui/material/Dialog';
    12  -import DialogActions from '@mui/material/DialogActions';
    13  -import DialogContent from '@mui/material/DialogContent';
    14  -import DialogContentText from '@mui/material/DialogContentText';
    15  -import Slide from '@mui/material/Slide';
    16  -import MoreVert from '@mui/icons-material/MoreVert';
    17  -import { graphql } from 'react-relay';
    18  -import inject18n from '../../../../components/i18n';
    19  -import { QueryRenderer, commitMutation } from '../../../../relay/environment';
    20  -import { countryEditionQuery } from './CountryEdition';
    21  -import CountryEditionContainer from './CountryEditionContainer';
    22  -import Loader from '../../../../components/Loader';
    23  -import Security, {
    24  - KNOWLEDGE_KNUPDATE_KNDELETE,
    25  -} from '../../../../utils/Security';
    26  - 
    27  -const styles = (theme) => ({
    28  - container: {
    29  - margin: 0,
    30  - },
    31  - drawerPaper: {
    32  - minHeight: '100vh',
    33  - width: '50%',
    34  - position: 'fixed',
    35  - overflow: 'auto',
    36  - transition: theme.transitions.create('width', {
    37  - easing: theme.transitions.easing.sharp,
    38  - duration: theme.transitions.duration.enteringScreen,
    39  - }),
    40  - padding: 0,
    41  - },
    42  -});
    43  - 
    44  -const Transition = React.forwardRef((props, ref) => (
    45  - <Slide direction="up" ref={ref} {...props} />
    46  -));
    47  -Transition.displayName = 'TransitionSlide';
    48  - 
    49  -const CountryPopoverDeletionMutation = graphql`
    50  - mutation CountryPopoverDeletionMutation($id: ID!) {
    51  - countryEdit(id: $id) {
    52  - delete
    53  - }
    54  - }
    55  -`;
    56  - 
    57  -class CountryPopover extends Component {
    58  - constructor(props) {
    59  - super(props);
    60  - this.state = {
    61  - anchorEl: null,
    62  - displayDelete: false,
    63  - displayEdit: false,
    64  - deleting: false,
    65  - };
    66  - }
    67  - 
    68  - handleOpen(event) {
    69  - this.setState({ anchorEl: event.currentTarget });
    70  - }
    71  - 
    72  - handleClose() {
    73  - this.setState({ anchorEl: null });
    74  - }
    75  - 
    76  - handleOpenDelete() {
    77  - this.setState({ displayDelete: true });
    78  - this.handleClose();
    79  - }
    80  - 
    81  - handleCloseDelete() {
    82  - this.setState({ displayDelete: false });
    83  - }
    84  - 
    85  - submitDelete() {
    86  - this.setState({ deleting: true });
    87  - commitMutation({
    88  - mutation: CountryPopoverDeletionMutation,
    89  - variables: {
    90  - id: this.props.id,
    91  - },
    92  - onCompleted: () => {
    93  - this.setState({ deleting: false });
    94  - this.handleClose();
    95  - this.props.history.push('/dashboard/locations/countries');
    96  - },
    97  - });
    98  - }
    99  - 
    100  - handleOpenEdit() {
    101  - this.setState({ displayEdit: true });
    102  - this.handleClose();
    103  - }
    104  - 
    105  - handleCloseEdit() {
    106  - this.setState({ displayEdit: false });
    107  - }
    108  - 
    109  - render() {
    110  - const { classes, t, id } = this.props;
    111  - return (
    112  - <div className={classes.container}>
    113  - <IconButton
    114  - onClick={this.handleOpen.bind(this)}
    115  - aria-haspopup="true"
    116  - style={{ marginTop: 3 }}
    117  - size="large"
    118  - >
    119  - <MoreVert />
    120  - </IconButton>
    121  - <Menu
    122  - anchorEl={this.state.anchorEl}
    123  - open={Boolean(this.state.anchorEl)}
    124  - onClose={this.handleClose.bind(this)}
    125  - >
    126  - <MenuItem onClick={this.handleOpenEdit.bind(this)}>
    127  - {t('Update')}
    128  - </MenuItem>
    129  - <Security needs={[KNOWLEDGE_KNUPDATE_KNDELETE]}>
    130  - <MenuItem onClick={this.handleOpenDelete.bind(this)}>
    131  - {t('Delete')}
    132  - </MenuItem>
    133  - </Security>
    134  - </Menu>
    135  - <Dialog
    136  - PaperProps={{ elevation: 1 }}
    137  - open={this.state.displayDelete}
    138  - keepMounted={true}
    139  - TransitionComponent={Transition}
    140  - onClose={this.handleCloseDelete.bind(this)}
    141  - >
    142  - <DialogContent>
    143  - <DialogContentText>
    144  - {t('Do you want to delete this country?')}
    145  - </DialogContentText>
    146  - </DialogContent>
    147  - <DialogActions>
    148  - <Button
    149  - onClick={this.handleCloseDelete.bind(this)}
    150  - disabled={this.state.deleting}
    151  - >
    152  - {t('Cancel')}
    153  - </Button>
    154  - <Button
    155  - color="secondary"
    156  - onClick={this.submitDelete.bind(this)}
    157  - disabled={this.state.deleting}
    158  - >
    159  - {t('Delete')}
    160  - </Button>
    161  - </DialogActions>
    162  - </Dialog>
    163  - <Drawer
    164  - open={this.state.displayEdit}
    165  - anchor="right"
    166  - elevation={1}
    167  - sx={{ zIndex: 1202 }}
    168  - classes={{ paper: classes.drawerPaper }}
    169  - onClose={this.handleCloseEdit.bind(this)}
    170  - >
    171  - <QueryRenderer
    172  - query={countryEditionQuery}
    173  - variables={{ id }}
    174  - render={({ props }) => {
    175  - if (props) {
    176  - return (
    177  - <CountryEditionContainer
    178  - country={props.country}
    179  - handleClose={this.handleCloseEdit.bind(this)}
    180  - />
    181  - );
    182  - }
    183  - return <Loader variant="inElement" />;
    184  - }}
    185  - />
    186  - </Drawer>
    187  - </div>
    188  - );
    189  - }
    190  -}
    191  - 
    192  -CountryPopover.propTypes = {
    193  - id: PropTypes.string,
    194  - classes: PropTypes.object,
    195  - t: PropTypes.func,
    196  - history: PropTypes.object,
    197  -};
    198  - 
    199  -export default compose(
    200  - inject18n,
    201  - withRouter,
    202  - withStyles(styles),
    203  -)(CountryPopover);
    204  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/CountryPopover.tsx
     1 +import React, { useState } from 'react';
     2 +import Menu from '@mui/material/Menu';
     3 +import MenuItem from '@mui/material/MenuItem';
     4 +import Button from '@mui/material/Button';
     5 +import IconButton from '@mui/material/IconButton';
     6 +import Drawer from '@mui/material/Drawer';
     7 +import Dialog from '@mui/material/Dialog';
     8 +import DialogActions from '@mui/material/DialogActions';
     9 +import DialogContent from '@mui/material/DialogContent';
     10 +import DialogContentText from '@mui/material/DialogContentText';
     11 +import MoreVert from '@mui/icons-material/MoreVert';
     12 +import { graphql, useMutation } from 'react-relay';
     13 +import makeStyles from '@mui/styles/makeStyles';
     14 +import { useNavigate } from 'react-router-dom-v5-compat';
     15 +import { useFormatter } from '../../../../components/i18n';
     16 +import Loader, { LoaderVariant } from '../../../../components/Loader';
     17 +import Security, { KNOWLEDGE_KNUPDATE_KNDELETE } from '../../../../utils/Security';
     18 +import { Theme } from '../../../../components/Theme';
     19 +import useQueryLoading from '../../../../utils/hooks/useQueryLoading';
     20 +import { CountryEditionContainerQuery } from './__generated__/CountryEditionContainerQuery.graphql';
     21 +import Transition from '../../../../components/Transition';
     22 +import CountryEditionContainer, { countryEditionQuery } from './CountryEditionContainer';
     23 + 
     24 +const useStyles = makeStyles<Theme>((theme) => ({
     25 + container: {
     26 + margin: 0,
     27 + },
     28 + drawerPaper: {
     29 + minHeight: '100vh',
     30 + width: '50%',
     31 + position: 'fixed',
     32 + overflow: 'auto',
     33 + transition: theme.transitions.create('width', {
     34 + easing: theme.transitions.easing.sharp,
     35 + duration: theme.transitions.duration.enteringScreen,
     36 + }),
     37 + padding: 0,
     38 + },
     39 +}));
     40 + 
     41 +const CountryPopoverDeletionMutation = graphql`
     42 + mutation CountryPopoverDeletionMutation($id: ID!) {
     43 + countryEdit(id: $id) {
     44 + delete
     45 + }
     46 + }
     47 +`;
     48 + 
     49 +const CountryPopover = ({ id }: { id: string }) => {
     50 + const classes = useStyles();
     51 + const { t } = useFormatter();
     52 + 
     53 + const navigate = useNavigate();
     54 + 
     55 + const [anchorEl, setAnchorEl] = useState<Element>();
     56 + const [displayEdit, setDisplayEdit] = useState<boolean>(false);
     57 + const [deleting, setDeleting] = useState<boolean>(false);
     58 + const [displayDelete, setDisplayDelete] = useState<boolean>(false);
     59 + 
     60 + const [commit] = useMutation(CountryPopoverDeletionMutation);
     61 + const queryRef = useQueryLoading<CountryEditionContainerQuery>(countryEditionQuery, { id });
     62 + 
     63 + const handleOpen = (event: React.SyntheticEvent) => {
     64 + setAnchorEl(event.currentTarget);
     65 + };
     66 + 
     67 + const handleClose = () => {
     68 + setAnchorEl(undefined);
     69 + };
     70 + 
     71 + const handleOpenDelete = () => {
     72 + setDisplayDelete(true);
     73 + handleClose();
     74 + };
     75 + 
     76 + const handleCloseDelete = () => {
     77 + setDisplayDelete(false);
     78 + };
     79 + 
     80 + const handleOpenEdit = () => {
     81 + setDisplayEdit(true);
     82 + handleClose();
     83 + };
     84 + 
     85 + const handleCloseEdit = () => {
     86 + setDisplayEdit(false);
     87 + };
     88 + 
     89 + const submitDelete = () => {
     90 + setDeleting(true);
     91 + commit({
     92 + variables: {
     93 + id,
     94 + },
     95 + onCompleted: () => {
     96 + setDeleting(false);
     97 + handleClose();
     98 + navigate('/dashboard/locations/countries');
     99 + },
     100 + });
     101 + };
     102 + 
     103 + return (
     104 + <div className={classes.container}>
     105 + <IconButton
     106 + onClick={handleOpen}
     107 + aria-haspopup="true"
     108 + style={{ marginTop: 3 }}
     109 + size="large"
     110 + >
     111 + <MoreVert />
     112 + </IconButton>
     113 + <Menu
     114 + anchorEl={anchorEl}
     115 + open={Boolean(anchorEl)}
     116 + onClose={handleClose}
     117 + >
     118 + <MenuItem onClick={handleOpenEdit}>
     119 + {t('Update')}
     120 + </MenuItem>
     121 + <Security needs={[KNOWLEDGE_KNUPDATE_KNDELETE]}>
     122 + <MenuItem onClick={handleOpenDelete}>
     123 + {t('Delete')}
     124 + </MenuItem>
     125 + </Security>
     126 + </Menu>
     127 + <Dialog
     128 + PaperProps={{ elevation: 1 }}
     129 + open={displayDelete}
     130 + keepMounted={true}
     131 + TransitionComponent={Transition}
     132 + onClose={handleCloseDelete}
     133 + >
     134 + <DialogContent>
     135 + <DialogContentText>
     136 + {t('Do you want to delete this country?')}
     137 + </DialogContentText>
     138 + </DialogContent>
     139 + <DialogActions>
     140 + <Button
     141 + onClick={handleCloseDelete}
     142 + disabled={deleting}
     143 + >
     144 + {t('Cancel')}
     145 + </Button>
     146 + <Button
     147 + color="secondary"
     148 + onClick={submitDelete}
     149 + disabled={deleting}
     150 + >
     151 + {t('Delete')}
     152 + </Button>
     153 + </DialogActions>
     154 + </Dialog>
     155 + <Drawer
     156 + open={displayEdit}
     157 + anchor="right"
     158 + elevation={1}
     159 + sx={{ zIndex: 1202 }}
     160 + classes={{ paper: classes.drawerPaper }}
     161 + onClose={handleCloseEdit}
     162 + >
     163 + {queryRef && (
     164 + <React.Suspense fallback={<Loader variant={LoaderVariant.inElement} />}>
     165 + <CountryEditionContainer
     166 + queryRef={queryRef}
     167 + handleClose={handleClose}
     168 + />
     169 + </React.Suspense>
     170 + )}
     171 + </Drawer>
     172 + </div>
     173 + );
     174 +};
     175 + 
     176 +export default CountryPopover;
     177 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/Root.js
    1  -import React, { Component } from 'react';
    2  -import * as PropTypes from 'prop-types';
    3  -import { Route, Redirect, withRouter, Switch } from 'react-router-dom';
    4  -import { graphql } from 'react-relay';
    5  -import {
    6  - QueryRenderer,
    7  - requestSubscription,
    8  -} from '../../../../relay/environment';
    9  -import TopBar from '../../nav/TopBar';
    10  -import Country from './Country';
    11  -import CountryKnowledge from './CountryKnowledge';
    12  -import StixDomainObjectHeader from '../../common/stix_domain_objects/StixDomainObjectHeader';
    13  -import FileManager from '../../common/files/FileManager';
    14  -import CountryPopover from './CountryPopover';
    15  -import Loader from '../../../../components/Loader';
    16  -import StixCoreObjectHistory from '../../common/stix_core_objects/StixCoreObjectHistory';
    17  -import StixCoreObjectOrStixCoreRelationshipContainers from '../../common/containers/StixCoreObjectOrStixCoreRelationshipContainers';
    18  -import StixCoreObjectKnowledgeBar from '../../common/stix_core_objects/StixCoreObjectKnowledgeBar';
    19  -import ErrorNotFound from '../../../../components/ErrorNotFound';
    20  -import EntityStixSightingRelationships from '../../events/stix_sighting_relationships/EntityStixSightingRelationships';
    21  - 
    22  -const subscription = graphql`
    23  - subscription RootCountriesSubscription($id: ID!) {
    24  - stixDomainObject(id: $id) {
    25  - ... on Country {
    26  - ...Country_country
    27  - ...CountryEditionContainer_country
    28  - }
    29  - ...FileImportViewer_entity
    30  - ...FileExportViewer_entity
    31  - ...FileExternalReferencesViewer_entity
    32  - ...WorkbenchFileViewer_entity
    33  - }
    34  - }
    35  -`;
    36  - 
    37  -const countryQuery = graphql`
    38  - query RootCountryQuery($id: String!) {
    39  - country(id: $id) {
    40  - id
    41  - name
    42  - x_opencti_aliases
    43  - x_opencti_graph_data
    44  - ...Country_country
    45  - ...CountryKnowledge_country
    46  - ...FileImportViewer_entity
    47  - ...FileExportViewer_entity
    48  - ...FileExternalReferencesViewer_entity
    49  - ...WorkbenchFileViewer_entity
    50  - }
    51  - connectorsForExport {
    52  - ...FileManager_connectorsExport
    53  - }
    54  - settings {
    55  - platform_enable_reference
    56  - }
    57  - }
    58  -`;
    59  - 
    60  -class RootCountry extends Component {
    61  - constructor(props) {
    62  - super(props);
    63  - const {
    64  - match: {
    65  - params: { countryId },
    66  - },
    67  - } = props;
    68  - this.sub = requestSubscription({
    69  - subscription,
    70  - variables: { id: countryId },
    71  - });
    72  - }
    73  - 
    74  - componentWillUnmount() {
    75  - this.sub.dispose();
    76  - }
    77  - 
    78  - render() {
    79  - const {
    80  - me,
    81  - match: {
    82  - params: { countryId },
    83  - },
    84  - } = this.props;
    85  - const link = `/dashboard/locations/countries/${countryId}/knowledge`;
    86  - return (
    87  - <div>
    88  - <TopBar me={me || null} />
    89  - <Route path="/dashboard/locations/countries/:countryId/knowledge">
    90  - <StixCoreObjectKnowledgeBar
    91  - stixCoreObjectLink={link}
    92  - availableSections={[
    93  - 'cities',
    94  - 'organizations',
    95  - 'threat_actors',
    96  - 'intrusion_sets',
    97  - 'campaigns',
    98  - 'incidents',
    99  - 'malwares',
    100  - 'attack_patterns',
    101  - 'tools',
    102  - 'observables',
    103  - ]}
    104  - />
    105  - </Route>
    106  - <QueryRenderer
    107  - query={countryQuery}
    108  - variables={{ id: countryId }}
    109  - render={({ props }) => {
    110  - if (props) {
    111  - if (props.country) {
    112  - return (
    113  - <Switch>
    114  - <Route
    115  - exact
    116  - path="/dashboard/locations/countries/:countryId"
    117  - render={(routeProps) => (
    118  - <Country {...routeProps} country={props.country} />
    119  - )}
    120  - />
    121  - <Route
    122  - exact
    123  - path="/dashboard/locations/countries/:countryId/knowledge"
    124  - render={() => (
    125  - <Redirect
    126  - to={`/dashboard/locations/countries/${countryId}/knowledge/overview`}
    127  - />
    128  - )}
    129  - />
    130  - <Route
    131  - path="/dashboard/locations/countries/:countryId/knowledge"
    132  - render={(routeProps) => (
    133  - <CountryKnowledge
    134  - {...routeProps}
    135  - country={props.country}
    136  - />
    137  - )}
    138  - />
    139  - <Route
    140  - exact
    141  - path="/dashboard/locations/countries/:countryId/analysis"
    142  - render={(routeProps) => (
    143  - <React.Fragment>
    144  - <StixDomainObjectHeader
    145  - disableSharing={true}
    146  - stixDomainObject={props.country}
    147  - PopoverComponent={<CountryPopover />}
    148  - />
    149  - <StixCoreObjectOrStixCoreRelationshipContainers
    150  - {...routeProps}
    151  - stixDomainObjectOrStixCoreRelationship={
    152  - props.country
    153  - }
    154  - />
    155  - </React.Fragment>
    156  - )}
    157  - />
    158  - <Route
    159  - exact
    160  - path="/dashboard/entities/countries/:countryId/sightings"
    161  - render={(routeProps) => (
    162  - <EntityStixSightingRelationships
    163  - entityId={props.country.id}
    164  - entityLink={link}
    165  - noPadding={true}
    166  - isTo={true}
    167  - {...routeProps}
    168  - />
    169  - )}
    170  - />
    171  - <Route
    172  - exact
    173  - path="/dashboard/locations/countries/:countryId/files"
    174  - render={(routeProps) => (
    175  - <React.Fragment>
    176  - <StixDomainObjectHeader
    177  - disableSharing={true}
    178  - stixDomainObject={props.country}
    179  - PopoverComponent={<CountryPopover />}
    180  - />
    181  - <FileManager
    182  - {...routeProps}
    183  - id={countryId}
    184  - connectorsImport={[]}
    185  - connectorsExport={props.connectorsForExport}
    186  - entity={props.country}
    187  - />
    188  - </React.Fragment>
    189  - )}
    190  - />
    191  - <Route
    192  - exact
    193  - path="/dashboard/locations/countries/:countryId/history"
    194  - render={(routeProps) => (
    195  - <React.Fragment>
    196  - <StixDomainObjectHeader
    197  - disableSharing={true}
    198  - stixDomainObject={props.country}
    199  - PopoverComponent={<CountryPopover />}
    200  - />
    201  - <StixCoreObjectHistory
    202  - {...routeProps}
    203  - stixCoreObjectId={countryId}
    204  - />
    205  - </React.Fragment>
    206  - )}
    207  - />
    208  - </Switch>
    209  - );
    210  - }
    211  - return <ErrorNotFound />;
    212  - }
    213  - return <Loader />;
    214  - }}
    215  - />
    216  - </div>
    217  - );
    218  - }
    219  -}
    220  - 
    221  -RootCountry.propTypes = {
    222  - children: PropTypes.node,
    223  - match: PropTypes.object,
    224  - me: PropTypes.object,
    225  -};
    226  - 
    227  -export default withRouter(RootCountry);
    228  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/countries/Root.tsx
     1 +// TODO Remove this when V6
     2 +// eslint-disable-next-line @typescript-eslint/ban-ts-comment
     3 +// @ts-nocheck
     4 +import React, { useEffect, useMemo } from 'react';
     5 +import { Route, Redirect, Switch, useParams } from 'react-router-dom';
     6 +import { graphql, useLazyLoadQuery, useSubscription } from 'react-relay';
     7 +import { GraphQLSubscriptionConfig } from 'relay-runtime';
     8 +import {
     9 + requestSubscription,
     10 +} from '../../../../relay/environment';
     11 +import TopBar from '../../nav/TopBar';
     12 +import Country from './Country';
     13 +import CountryKnowledge from './CountryKnowledge';
     14 +import StixDomainObjectHeader from '../../common/stix_domain_objects/StixDomainObjectHeader';
     15 +import FileManager from '../../common/files/FileManager';
     16 +import CountryPopover from './CountryPopover';
     17 +import StixCoreObjectHistory from '../../common/stix_core_objects/StixCoreObjectHistory';
     18 +import StixCoreObjectOrStixCoreRelationshipContainers from '../../common/containers/StixCoreObjectOrStixCoreRelationshipContainers';
     19 +import StixCoreObjectKnowledgeBar from '../../common/stix_core_objects/StixCoreObjectKnowledgeBar';
     20 +import ErrorNotFound from '../../../../components/ErrorNotFound';
     21 +import EntityStixSightingRelationships from '../../events/stix_sighting_relationships/EntityStixSightingRelationships';
     22 +import useAuth from '../../../../utils/hooks/useAuth';
     23 +import {
     24 + RootCountriesSubscription,
     25 +} from './__generated__/RootCountriesSubscription.graphql';
     26 +import { RootCountryQuery } from './__generated__/RootCountryQuery.graphql';
     27 + 
     28 +const subscription = graphql`
     29 + subscription RootCountriesSubscription($id: ID!) {
     30 + stixDomainObject(id: $id) {
     31 + ... on Country {
     32 + ...Country_country
     33 + ...CountryEditionOverview_country
     34 + }
     35 + ...FileImportViewer_entity
     36 + ...FileExportViewer_entity
     37 + ...FileExternalReferencesViewer_entity
     38 + ...WorkbenchFileViewer_entity
     39 + }
     40 + }
     41 +`;
     42 + 
     43 +const countryQuery = graphql`
     44 + query RootCountryQuery($id: String!) {
     45 + country(id: $id) {
     46 + id
     47 + name
     48 + x_opencti_aliases
     49 + x_opencti_graph_data
     50 + ...Country_country
     51 + ...CountryKnowledge_country
     52 + ...FileImportViewer_entity
     53 + ...FileExportViewer_entity
     54 + ...FileExternalReferencesViewer_entity
     55 + ...WorkbenchFileViewer_entity
     56 + }
     57 + connectorsForExport {
     58 + ...FileManager_connectorsExport
     59 + }
     60 + settings {
     61 + platform_enable_reference
     62 + }
     63 + }
     64 +`;
     65 + 
     66 +const RootCountry = () => {
     67 + const { me } = useAuth();
     68 + const { countryId } = useParams() as { countryId: string };
     69 + 
     70 + const link = `/dashboard/locations/countries/${countryId}/knowledge`;
     71 + const subConfig = useMemo<GraphQLSubscriptionConfig<RootCountriesSubscription>>(() => ({
     72 + subscription,
     73 + variables: { id: countryId },
     74 + }), [countryId]);
     75 + useSubscription(subConfig);
     76 + 
     77 + const data = useLazyLoadQuery<RootCountryQuery>(countryQuery, { id: countryId });
     78 + const { country, connectorsForExport } = data;
     79 + 
     80 + return (
     81 + <div>
     82 + <TopBar me={me || null} />
     83 + <Route path="/dashboard/locations/countries/:countryId/knowledge">
     84 + <StixCoreObjectKnowledgeBar
     85 + stixCoreObjectLink={link}
     86 + availableSections={[
     87 + 'cities',
     88 + 'organizations',
     89 + 'threat_actors',
     90 + 'intrusion_sets',
     91 + 'campaigns',
     92 + 'incidents',
     93 + 'malwares',
     94 + 'attack_patterns',
     95 + 'tools',
     96 + 'observables',
     97 + ]}
     98 + />
     99 + </Route>
     100 + <>
     101 + {country ? (
     102 + <Switch>
     103 + <Route
     104 + exact
     105 + path="/dashboard/locations/countries/:countryId"
     106 + render={() => (<Country countryData={country} />)}
     107 + />
     108 + <Route
     109 + exact
     110 + path="/dashboard/locations/countries/:countryId/knowledge"
     111 + render={() => (
     112 + <Redirect
     113 + to={`/dashboard/locations/countries/${countryId}/knowledge/overview`}
     114 + />
     115 + )}
     116 + />
     117 + <Route
     118 + path="/dashboard/locations/countries/:countryId/knowledge"
     119 + render={() => (<CountryKnowledge countryData={country} />)}
     120 + />
     121 + <Route
     122 + exact
     123 + path="/dashboard/locations/countries/:countryId/analysis"
     124 + render={(routeProps: any) => (
     125 + <React.Fragment>
     126 + <StixDomainObjectHeader
     127 + disableSharing={true}
     128 + stixDomainObject={country}
     129 + PopoverComponent={CountryPopover}
     130 + />
     131 + <StixCoreObjectOrStixCoreRelationshipContainers
     132 + {...routeProps}
     133 + stixDomainObjectOrStixCoreRelationship={country}
     134 + />
     135 + </React.Fragment>
     136 + )}
     137 + />
     138 + <Route
     139 + exact
     140 + path="/dashboard/entities/countries/:countryId/sightings"
     141 + render={(routeProps: any) => (
     142 + <EntityStixSightingRelationships
     143 + entityId={country.id}
     144 + entityLink={link}
     145 + noPadding={true}
     146 + isTo={true}
     147 + {...routeProps}
     148 + />
     149 + )}
     150 + />
     151 + <Route
     152 + exact
     153 + path="/dashboard/locations/countries/:countryId/files"
     154 + render={(routeProps: any) => (
     155 + <React.Fragment>
     156 + <StixDomainObjectHeader
     157 + disableSharing={true}
     158 + stixDomainObject={country}
     159 + PopoverComponent={CountryPopover}
     160 + />
     161 + <FileManager
     162 + {...routeProps}
     163 + id={countryId}
     164 + connectorsImport={[]}
     165 + connectorsExport={connectorsForExport}
     166 + entity={country}
     167 + />
     168 + </React.Fragment>
     169 + )}
     170 + />
     171 + <Route
     172 + exact
     173 + path="/dashboard/locations/countries/:countryId/history"
     174 + render={(routeProps: any) => (
     175 + <React.Fragment>
     176 + <StixDomainObjectHeader
     177 + disableSharing={true}
     178 + stixDomainObject={country}
     179 + PopoverComponent={CountryPopover}
     180 + />
     181 + <StixCoreObjectHistory
     182 + {...routeProps}
     183 + stixCoreObjectId={countryId}
     184 + />
     185 + </React.Fragment>
     186 + )}
     187 + />
     188 + </Switch>
     189 + ) : <ErrorNotFound />}
     190 + </>
     191 + </div>
     192 + );
     193 +};
     194 + 
     195 +export default RootCountry;
     196 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/regions/Region.js opencti-platform/opencti-front/src/private/components/locations/regions/Region.tsx
    1  -import React, { Component } from 'react';
    2  -import PropTypes from 'prop-types';
    3  -import { compose, map } from 'ramda';
    4  -import { graphql, createFragmentContainer } from 'react-relay';
    5  -import withStyles from '@mui/styles/withStyles';
     1 +import React from 'react';
     2 +import { map } from 'ramda';
     3 +import { graphql, useFragment } from 'react-relay';
    6 4  import Grid from '@mui/material/Grid';
    7  -import inject18n from '../../../../components/i18n';
     5 +import makeStyles from '@mui/styles/makeStyles';
    8 6  import RegionEdition from './RegionEdition';
    9 7  import RegionPopover from './RegionPopover';
    10 8  import StixCoreObjectOrStixCoreRelationshipLastReports from '../../analysis/reports/StixCoreObjectOrStixCoreRelationshipLastReports';
    skipped 5 lines
    16 14  import StixCoreObjectLatestHistory from '../../common/stix_core_objects/StixCoreObjectLatestHistory';
    17 15  import SimpleStixObjectOrStixRelationshipStixCoreRelationships from '../../common/stix_core_relationships/SimpleStixObjectOrStixRelationshipStixCoreRelationships';
    18 16  import LocationMiniMap from '../../common/location/LocationMiniMap';
     17 +import { Region_region$key } from './__generated__/Region_region.graphql';
    19 18   
    20  -const styles = () => ({
     19 +const useStyles = makeStyles(() => ({
    21 20   container: {
    22 21   margin: 0,
    23 22   },
    24 23   gridContainer: {
    25 24   marginBottom: 20,
    26 25   },
    27  -});
     26 +}));
     27 + 
     28 +const regionFragment = graphql`
     29 + fragment Region_region on Region {
     30 + id
     31 + standard_id
     32 + x_opencti_stix_ids
     33 + spec_version
     34 + revoked
     35 + confidence
     36 + created
     37 + modified
     38 + created_at
     39 + updated_at
     40 + createdBy {
     41 + ... on Identity {
     42 + id
     43 + name
     44 + entity_type
     45 + }
     46 + }
     47 + creator {
     48 + id
     49 + name
     50 + }
     51 + objectMarking {
     52 + edges {
     53 + node {
     54 + id
     55 + definition
     56 + x_opencti_color
     57 + }
     58 + }
     59 + }
     60 + objectLabel {
     61 + edges {
     62 + node {
     63 + id
     64 + value
     65 + color
     66 + }
     67 + }
     68 + }
     69 + countries {
     70 + edges {
     71 + node {
     72 + name
     73 + x_opencti_aliases
     74 + }
     75 + }
     76 + }
     77 + name
     78 + latitude
     79 + longitude
     80 + x_opencti_aliases
     81 + isSubRegion
     82 + status {
     83 + id
     84 + order
     85 + template {
     86 + name
     87 + color
     88 + }
     89 + }
     90 + workflowEnabled
     91 + }
     92 + `;
    28 93   
    29  -class RegionComponent extends Component {
    30  - render() {
    31  - const { classes, region } = this.props;
    32  - const countries = map((n) => n.node, region.countries.edges);
    33  - return (
     94 +const RegionComponent = ({ regionData }: { regionData: Region_region$key }) => {
     95 + const classes = useStyles();
     96 + const region = useFragment<Region_region$key>(regionFragment, regionData);
     97 + const countries = map(({ node }) => node?.region?.countries?.edges);
     98 + return (
    34 99   <div className={classes.container}>
    35 100   <StixDomainObjectHeader
    36 101   disableSharing={true}
    37 102   stixDomainObject={region}
    38 103   isOpenctiAlias={true}
    39  - PopoverComponent={<RegionPopover />}
     104 + PopoverComponent={RegionPopover}
    40 105   />
    41 106   <Grid
    42 107   container={true}
    skipped 11 lines
    54 119   : [48.8566969, 2.3514616]
    55 120   }
    56 121   countries={countries}
     122 + region={region}
    57 123   zoom={region.isSubRegion ? 4 : 3}
    58 124   />
    59 125   </Grid>
    skipped 36 lines
    96 162   <RegionEdition regionId={region.id} />
    97 163   </Security>
    98 164   </div>
    99  - );
    100  - }
    101  -}
    102  - 
    103  -RegionComponent.propTypes = {
    104  - region: PropTypes.object,
    105  - classes: PropTypes.object,
    106  - t: PropTypes.func,
     165 + );
    107 166  };
    108 167   
    109  -const Region = createFragmentContainer(RegionComponent, {
    110  - region: graphql`
    111  - fragment Region_region on Region {
    112  - id
    113  - standard_id
    114  - x_opencti_stix_ids
    115  - spec_version
    116  - revoked
    117  - confidence
    118  - created
    119  - modified
    120  - created_at
    121  - updated_at
    122  - createdBy {
    123  - ... on Identity {
    124  - id
    125  - name
    126  - entity_type
    127  - }
    128  - }
    129  - creator {
    130  - id
    131  - name
    132  - }
    133  - objectMarking {
    134  - edges {
    135  - node {
    136  - id
    137  - definition
    138  - x_opencti_color
    139  - }
    140  - }
    141  - }
    142  - objectLabel {
    143  - edges {
    144  - node {
    145  - id
    146  - value
    147  - color
    148  - }
    149  - }
    150  - }
    151  - countries {
    152  - edges {
    153  - node {
    154  - name
    155  - x_opencti_aliases
    156  - }
    157  - }
    158  - }
    159  - name
    160  - latitude
    161  - longitude
    162  - x_opencti_aliases
    163  - isSubRegion
    164  - status {
    165  - id
    166  - order
    167  - template {
    168  - name
    169  - color
    170  - }
    171  - }
    172  - workflowEnabled
    173  - }
    174  - `,
    175  -});
    176  - 
    177  -export default compose(inject18n, withStyles(styles))(Region);
     168 +export default RegionComponent;
    178 169   
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/regions/RegionCreation.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, useMutation } from 'react-relay';
     12 +import { FormikConfig } from 'formik/dist/types';
     13 +import * as R from 'ramda';
     14 +import { useFormatter } from '../../../../components/i18n';
     15 +import TextField from '../../../../components/TextField';
     16 +import CreatedByField from '../../common/form/CreatedByField';
     17 +import ObjectMarkingField from '../../common/form/ObjectMarkingField';
     18 +import MarkDownField from '../../../../components/MarkDownField';
     19 +import { Theme } from '../../../../components/Theme';
     20 +import ExternalReferencesField from '../../common/form/ExternalReferencesField';
     21 +import { insertNode } from '../../../../utils/Store';
     22 +import { RegionsLinesPaginationQuery$variables } from './__generated__/RegionsLinesPaginationQuery.graphql';
     23 + 
     24 +const styles = makeStyles<Theme>((theme) => ({
     25 + drawerPaper: {
     26 + minHeight: '100vh',
     27 + width: '50%',
     28 + position: 'fixed',
     29 + transition: theme.transitions.create('width', {
     30 + easing: theme.transitions.easing.sharp,
     31 + duration: theme.transitions.duration.enteringScreen,
     32 + }),
     33 + padding: 0,
     34 + },
     35 + dialogActions: {
     36 + padding: '0 17px 20px 0',
     37 + },
     38 + createButton: {
     39 + position: 'fixed',
     40 + bottom: 30,
     41 + right: 30,
     42 + },
     43 + createButtonContextual: {
     44 + position: 'fixed',
     45 + bottom: 30,
     46 + right: 30,
     47 + zIndex: 2000,
     48 + },
     49 + buttons: {
     50 + marginTop: 20,
     51 + textAlign: 'right',
     52 + },
     53 + button: {
     54 + marginLeft: theme.spacing(2),
     55 + },
     56 + header: {
     57 + backgroundColor: theme.palette.background.nav,
     58 + padding: '20px 20px 20px 60px',
     59 + },
     60 + closeButton: {
     61 + position: 'absolute',
     62 + top: 12,
     63 + left: 5,
     64 + color: 'inherit',
     65 + },
     66 + importButton: {
     67 + position: 'absolute',
     68 + top: 15,
     69 + right: 20,
     70 + },
     71 + container: {
     72 + padding: '10px 20px 20px 20px',
     73 + },
     74 +}));
     75 + 
     76 +const regionMutation = graphql`
     77 + mutation RegionCreationMutation($input: RegionAddInput!) {
     78 + regionAdd(input: $input) {
     79 + ...RegionLine_node
     80 + }
     81 + }
     82 +`;
     83 + 
     84 +const regionValidation = (t: (message: string) => string) => Yup.object()
     85 + .shape({
     86 + name: Yup.string()
     87 + .required(t('This field is required')),
     88 + description: Yup.string()
     89 + .min(3, t('The value is too short'))
     90 + .max(5000, t('The value is too long'))
     91 + .required(t('This field is required')),
     92 + });
     93 + 
     94 +interface RegionAddInput {
     95 + name: string,
     96 + description: string,
     97 + latitude: string,
     98 + longitude: string,
     99 + createdBy?: { value: string, label?: string },
     100 + objectMarking: { value: string }[],
     101 + externalReferences: { value: string }[],
     102 +}
     103 + 
     104 +const RegionCreation = ({ paginationOptions }: { paginationOptions: RegionsLinesPaginationQuery$variables }) => {
     105 + const { t } = useFormatter();
     106 + const classes = styles();
     107 + 
     108 + const [open, setOpen] = useState<boolean>(false);
     109 + 
     110 + const handleOpen = () => {
     111 + setOpen(true);
     112 + };
     113 + 
     114 + const handleClose = () => {
     115 + setOpen(false);
     116 + };
     117 + 
     118 + const [commit] = useMutation(regionMutation);
     119 + 
     120 + const onSubmit: FormikConfig<RegionAddInput>['onSubmit'] = (values, { setSubmitting, resetForm }) => {
     121 + const finalValues = R.pipe(
     122 + R.assoc('latitude', parseFloat(values.latitude)),
     123 + R.assoc('longitude', parseFloat(values.longitude)),
     124 + R.assoc('createdBy', values.createdBy?.value),
     125 + R.assoc('objectMarking', R.pluck('value', values.objectMarking)),
     126 + R.assoc('externalReferences', R.pluck('value', values.externalReferences)),
     127 + )(values);
     128 + commit({
     129 + variables: {
     130 + input: finalValues,
     131 + },
     132 + updater: (store) => {
     133 + insertNode(store, 'Pagination_regions', paginationOptions, 'regionAdd');
     134 + },
     135 + onCompleted: () => {
     136 + setSubmitting(false);
     137 + resetForm();
     138 + handleClose();
     139 + },
     140 + });
     141 + };
     142 + 
     143 + const onReset = () => {
     144 + handleClose();
     145 + };
     146 + 
     147 + return (
     148 + <div>
     149 + <Fab
     150 + onClick={handleOpen}
     151 + color="secondary"
     152 + aria-label="Add"
     153 + className={classes.createButton}
     154 + >
     155 + <Add />
     156 + </Fab>
     157 + <Drawer
     158 + open={open}
     159 + anchor="right"
     160 + elevation={1}
     161 + sx={{ zIndex: 1202 }}
     162 + classes={{ paper: classes.drawerPaper }}
     163 + onClose={handleClose}
     164 + >
     165 + <div className={classes.header}>
     166 + <IconButton
     167 + aria-label="Close"
     168 + className={classes.closeButton}
     169 + onClick={handleClose}
     170 + size="large"
     171 + color="primary"
     172 + >
     173 + <Close fontSize="small" color="primary" />
     174 + </IconButton>
     175 + <Typography variant="h6">{t('Create a Region')}</Typography>
     176 + </div>
     177 + <div className={classes.container}>
     178 + <Formik<RegionAddInput>
     179 + initialValues={{
     180 + name: '',
     181 + description: '',
     182 + latitude: '',
     183 + longitude: '',
     184 + createdBy: { value: '', label: ''},
     185 + objectMarking: [],
     186 + externalReferences: [],
     187 + }}
     188 + validationSchema={regionValidation(t)}
     189 + onSubmit={onSubmit}
     190 + onReset={handleClose}
     191 + >
     192 + {({
     193 + submitForm,
     194 + handleReset,
     195 + isSubmitting,
     196 + setFieldValue,
     197 + values,
     198 + }) => (
     199 + <Form style={{ margin: '20px 0 20px 0' }}>
     200 + <Field
     201 + component={TextField}
     202 + variant="standard"
     203 + name="name"
     204 + label={t('Name')}
     205 + fullWidth={true}
     206 + detectDuplicate={['Region']}
     207 + />
     208 + <Field
     209 + component={MarkDownField}
     210 + name="description"
     211 + label={t('Description')}
     212 + fullWidth={true}
     213 + multiline={true}
     214 + rows="4"
     215 + style={{ marginTop: 20 }}
     216 + />
     217 + <Field
     218 + component={TextField}
     219 + variant="standard"
     220 + name="latitude"
     221 + label={t('Latitude')}
     222 + fullWidth={true}
     223 + style={{ marginTop: 20 }}
     224 + />
     225 + <Field
     226 + component={TextField}
     227 + variant="standard"
     228 + name="longitude"
     229 + label={t('Longitude')}
     230 + fullWidth={true}
     231 + style={{ marginTop: 20 }}
     232 + />
     233 + <CreatedByField
     234 + name="createdBy"
     235 + style={{
     236 + marginTop: 20,
     237 + width: '100%',
     238 + }}
     239 + setFieldValue={setFieldValue}
     240 + />
     241 + <ObjectMarkingField
     242 + name="objectMarking"
     243 + style={{
     244 + marginTop: 20,
     245 + width: '100%',
     246 + }}
     247 + />
     248 + <ExternalReferencesField
     249 + name="externalReferences"
     250 + style={{
     251 + marginTop: 20,
     252 + width: '100%',
     253 + }}
     254 + setFieldValue={setFieldValue}
     255 + values={values.externalReferences}
     256 + />
     257 + <div className={classes.buttons}>
     258 + <Button
     259 + variant="contained"
     260 + onClick={handleReset}
     261 + disabled={isSubmitting}
     262 + classes={{ root: classes.button }}
     263 + >
     264 + {t('Cancel')}
     265 + </Button>
     266 + <Button
     267 + variant="contained"
     268 + color="secondary"
     269 + onClick={submitForm}
     270 + disabled={isSubmitting}
     271 + classes={{ root: classes.button }}
     272 + >
     273 + {t('Create')}
     274 + </Button>
     275 + </div>
     276 + </Form>
     277 + )}
     278 + </Formik>
     279 + </div>
     280 + </Drawer>
     281 + </div>
     282 + );
     283 +};
     284 + 
     285 +export default RegionCreation;
     286 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/regions/RegionEdition.js
    1  -import React, { Component } from 'react';
    2  -import * as PropTypes from 'prop-types';
    3  -import { compose } from 'ramda';
    4  -import withStyles from '@mui/styles/withStyles';
    5  -import Drawer from '@mui/material/Drawer';
    6  -import Fab from '@mui/material/Fab';
    7  -import { Edit } from '@mui/icons-material';
    8  -import { graphql } from 'react-relay';
    9  -import { commitMutation, QueryRenderer } from '../../../../relay/environment';
    10  -import inject18n from '../../../../components/i18n';
    11  -import RegionEditionContainer from './RegionEditionContainer';
    12  -import { regionEditionOverviewFocus } from './RegionEditionOverview';
    13  -import Loader from '../../../../components/Loader';
    14  - 
    15  -const styles = (theme) => ({
    16  - editButton: {
    17  - position: 'fixed',
    18  - bottom: 30,
    19  - right: 30,
    20  - zIndex: 400,
    21  - },
    22  - drawerPaper: {
    23  - minHeight: '100vh',
    24  - width: '50%',
    25  - position: 'fixed',
    26  - overflow: 'auto',
    27  - transition: theme.transitions.create('width', {
    28  - easing: theme.transitions.easing.sharp,
    29  - duration: theme.transitions.duration.enteringScreen,
    30  - }),
    31  - padding: 0,
    32  - },
    33  -});
    34  - 
    35  -export const regionEditionQuery = graphql`
    36  - query RegionEditionContainerQuery($id: String!) {
    37  - region(id: $id) {
    38  - ...RegionEditionContainer_region
    39  - }
    40  - settings {
    41  - platform_enable_reference
    42  - }
    43  - }
    44  -`;
    45  - 
    46  -class RegionEdition extends Component {
    47  - constructor(props) {
    48  - super(props);
    49  - this.state = { open: false };
    50  - }
    51  - 
    52  - handleOpen() {
    53  - this.setState({ open: true });
    54  - }
    55  - 
    56  - handleClose() {
    57  - commitMutation({
    58  - mutation: regionEditionOverviewFocus,
    59  - variables: {
    60  - id: this.props.regionId,
    61  - input: { focusOn: '' },
    62  - },
    63  - });
    64  - this.setState({ open: false });
    65  - }
    66  - 
    67  - render() {
    68  - const { classes, regionId } = this.props;
    69  - return (
    70  - <div>
    71  - <Fab
    72  - onClick={this.handleOpen.bind(this)}
    73  - color="secondary"
    74  - aria-label="Edit"
    75  - className={classes.editButton}
    76  - >
    77  - <Edit />
    78  - </Fab>
    79  - <Drawer
    80  - open={this.state.open}
    81  - anchor="right"
    82  - elevation={1}
    83  - sx={{ zIndex: 1202 }}
    84  - classes={{ paper: classes.drawerPaper }}
    85  - onClose={this.handleClose.bind(this)}
    86  - >
    87  - <QueryRenderer
    88  - query={regionEditionQuery}
    89  - variables={{ id: regionId }}
    90  - render={({ props }) => {
    91  - if (props) {
    92  - return (
    93  - <RegionEditionContainer
    94  - region={props.region}
    95  - enableReferences={props.settings.platform_enable_reference?.includes(
    96  - 'Region',
    97  - )}
    98  - handleClose={this.handleClose.bind(this)}
    99  - />
    100  - );
    101  - }
    102  - return <Loader variant="inElement" />;
    103  - }}
    104  - />
    105  - </Drawer>
    106  - </div>
    107  - );
    108  - }
    109  -}
    110  - 
    111  -RegionEdition.propTypes = {
    112  - regionId: PropTypes.string,
    113  - me: PropTypes.object,
    114  - classes: PropTypes.object,
    115  - theme: PropTypes.object,
    116  - t: PropTypes.func,
    117  -};
    118  - 
    119  -export default compose(
    120  - inject18n,
    121  - withStyles(styles, { withTheme: true }),
    122  -)(RegionEdition);
    123  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/regions/RegionEdition.tsx
     1 +import React, { useState } from 'react';
     2 +import Drawer from '@mui/material/Drawer';
     3 +import Fab from '@mui/material/Fab';
     4 +import { Edit } from '@mui/icons-material';
     5 +import makeStyles from '@mui/styles/makeStyles';
     6 +import { useMutation } from 'react-relay';
     7 +import RegionEditionContainer, { regionEditionQuery } from './RegionEditionContainer';
     8 +import { regionEditionOverviewFocus } from './RegionEditionOverview';
     9 +import Loader, { LoaderVariant } from '../../../../components/Loader';
     10 +import useQueryLoading from '../../../../utils/hooks/useQueryLoading';
     11 +import { Theme } from '../../../../components/Theme';
     12 +import { useFormatter } from '../../../../components/i18n';
     13 +import { RegionEditionContainerQuery } from './__generated__/RegionEditionContainerQuery.graphql';
     14 + 
     15 +const useStyles = makeStyles<Theme>((theme) => ({
     16 + editButton: {
     17 + position: 'fixed',
     18 + bottom: 30,
     19 + right: 30,
     20 + zIndex: 400,
     21 + },
     22 + drawerPaper: {
     23 + minHeight: '100vh',
     24 + width: '50%',
     25 + position: 'fixed',
     26 + overflow: 'auto',
     27 + transition: theme.transitions.create('width', {
     28 + easing: theme.transitions.easing.sharp,
     29 + duration: theme.transitions.duration.enteringScreen,
     30 + }),
     31 + padding: 0,
     32 + },
     33 +}));
     34 + 
     35 +const RegionEdition = ({ regionId }: { regionId: string }) => {
     36 + const classes = useStyles();
     37 + const { t } = useFormatter();
     38 + const [open, setOpen] = useState<boolean>(false);
     39 + const [commit] = useMutation(regionEditionOverviewFocus);
     40 + 
     41 + const handleOpen = () => {
     42 + setOpen(true);
     43 + };
     44 + 
     45 + const handleClose = () => {
     46 + commit({
     47 + variables: {
     48 + id: regionId,
     49 + input: { focusOn: '' },
     50 + },
     51 + });
     52 + setOpen(false);
     53 + };
     54 + 
     55 + const queryRef = useQueryLoading<RegionEditionContainerQuery>(regionEditionQuery, { id: regionId });
     56 + 
     57 + return (
     58 + <div>
     59 + <Fab
     60 + onClick={handleOpen}
     61 + color="secondary"
     62 + aria-label="Edit"
     63 + className={classes.editButton}
     64 + >
     65 + <Edit />
     66 + </Fab>
     67 + <Drawer
     68 + open={open}
     69 + anchor="right"
     70 + elevation={1}
     71 + sx={{ zIndex: 1202 }}
     72 + classes={{ paper: classes.drawerPaper }}
     73 + onClose={handleClose}
     74 + >
     75 + {queryRef && (
     76 + <React.Suspense fallback={<Loader variant={LoaderVariant.inElement} />}>
     77 + <RegionEditionContainer
     78 + queryRef={queryRef}
     79 + handleClose={handleClose}
     80 + />
     81 + </React.Suspense>
     82 + )}
     83 + </Drawer>
     84 + </div>
     85 + );
     86 +};
     87 + 
     88 +export default RegionEdition;
     89 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/regions/RegionEditionContainer.js
    1  -import React, { Component } from 'react';
    2  -import PropTypes from 'prop-types';
    3  -import { graphql, createFragmentContainer } from 'react-relay';
    4  -import { compose } from 'ramda';
    5  -import withStyles from '@mui/styles/withStyles';
    6  -import Typography from '@mui/material/Typography';
    7  -import IconButton from '@mui/material/IconButton';
    8  -import { Close } from '@mui/icons-material';
    9  -import inject18n from '../../../../components/i18n';
    10  -import { SubscriptionAvatars } from '../../../../components/Subscription';
    11  -import RegionEditionOverview from './RegionEditionOverview';
    12  - 
    13  -const styles = (theme) => ({
    14  - header: {
    15  - backgroundColor: theme.palette.background.nav,
    16  - padding: '20px 20px 20px 60px',
    17  - },
    18  - closeButton: {
    19  - position: 'absolute',
    20  - top: 12,
    21  - left: 5,
    22  - color: 'inherit',
    23  - },
    24  - importButton: {
    25  - position: 'absolute',
    26  - top: 15,
    27  - right: 20,
    28  - },
    29  - container: {
    30  - padding: '10px 20px 20px 20px',
    31  - },
    32  - appBar: {
    33  - width: '100%',
    34  - zIndex: theme.zIndex.drawer + 1,
    35  - borderBottom: '1px solid #5c5c5c',
    36  - },
    37  - title: {
    38  - float: 'left',
    39  - },
    40  -});
    41  - 
    42  -class RegionEditionContainer extends Component {
    43  - render() {
    44  - const { t, classes, handleClose, region } = this.props;
    45  - const { editContext } = region;
    46  - return (
    47  - <div>
    48  - <div className={classes.header}>
    49  - <IconButton
    50  - aria-label="Close"
    51  - className={classes.closeButton}
    52  - onClick={handleClose.bind(this)}
    53  - size="large"
    54  - color="primary"
    55  - >
    56  - <Close fontSize="small" color="primary" />
    57  - </IconButton>
    58  - <Typography variant="h6" classes={{ root: classes.title }}>
    59  - {t('Update a region')}
    60  - </Typography>
    61  - <SubscriptionAvatars context={editContext} />
    62  - <div className="clearfix" />
    63  - </div>
    64  - <div className={classes.container}>
    65  - <RegionEditionOverview
    66  - region={this.props.region}
    67  - enableReferences={this.props.enableReferences}
    68  - context={editContext}
    69  - handleClose={handleClose.bind(this)}
    70  - />
    71  - </div>
    72  - </div>
    73  - );
    74  - }
    75  -}
    76  - 
    77  -RegionEditionContainer.propTypes = {
    78  - handleClose: PropTypes.func,
    79  - classes: PropTypes.object,
    80  - region: PropTypes.object,
    81  - theme: PropTypes.object,
    82  - t: PropTypes.func,
    83  -};
    84  - 
    85  -const RegionEditionFragment = createFragmentContainer(RegionEditionContainer, {
    86  - region: graphql`
    87  - fragment RegionEditionContainer_region on Region {
    88  - id
    89  - ...RegionEditionOverview_region
    90  - editContext {
    91  - name
    92  - focusOn
    93  - }
    94  - }
    95  - `,
    96  -});
    97  - 
    98  -export default compose(
    99  - inject18n,
    100  - withStyles(styles, { withTheme: true }),
    101  -)(RegionEditionFragment);
    102  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/regions/RegionEditionContainer.tsx
     1 +import React, { FunctionComponent } from 'react';
     2 +import { graphql, PreloadedQuery, usePreloadedQuery } from 'react-relay';
     3 +import Typography from '@mui/material/Typography';
     4 +import IconButton from '@mui/material/IconButton';
     5 +import { Close } from '@mui/icons-material';
     6 +import makeStyles from '@mui/styles/makeStyles';
     7 +import inject18n, { useFormatter } from '../../../../components/i18n';
     8 +import { SubscriptionAvatars } from '../../../../components/Subscription';
     9 +import RegionEditionOverview from './RegionEditionOverview';
     10 +import Loader, { LoaderVariant } from '../../../../components/Loader';
     11 +import { RegionEditionContainerQuery } from './__generated__/RegionEditionContainerQuery.graphql';
     12 +import { Theme } from '../../../../components/Theme';
     13 + 
     14 +const useStyles = makeStyles<Theme>((theme) => ({
     15 + header: {
     16 + backgroundColor: theme.palette.background.nav,
     17 + padding: '20px 20px 20px 60px',
     18 + },
     19 + closeButton: {
     20 + position: 'absolute',
     21 + top: 12,
     22 + left: 5,
     23 + color: 'inherit',
     24 + },
     25 + importButton: {
     26 + position: 'absolute',
     27 + top: 15,
     28 + right: 20,
     29 + },
     30 + container: {
     31 + padding: '10px 20px 20px 20px',
     32 + },
     33 + appBar: {
     34 + width: '100%',
     35 + zIndex: theme.zIndex.drawer + 1,
     36 + borderBottom: '1px solid #5c5c5c',
     37 + },
     38 + title: {
     39 + float: 'left',
     40 + },
     41 +}));
     42 + 
     43 +interface RegionEditionContainerProps {
     44 + handleClose: () => void
     45 + queryRef: PreloadedQuery<RegionEditionContainerQuery>
     46 +}
     47 + 
     48 +export const regionEditionQuery = graphql`
     49 + query RegionEditionContainerQuery($id: String!) {
     50 + region(id: $id) {
     51 + ...RegionEditionOverview_region
     52 + editContext {
     53 + name
     54 + focusOn
     55 + }
     56 + }
     57 + settings {
     58 + platform_enable_reference
     59 + }
     60 + }
     61 +`;
     62 + 
     63 +const RegionEditionContainer: FunctionComponent<RegionEditionContainerProps> = ({ handleClose, queryRef }) => {
     64 + const classes = useStyles();
     65 + const { t } = useFormatter();
     66 + 
     67 + const queryData = usePreloadedQuery(regionEditionQuery, queryRef);
     68 + 
     69 + if (queryData.region) {
     70 + return (
     71 + <div>
     72 + <div className={classes.header}>
     73 + <IconButton
     74 + aria-label="Close"
     75 + className={classes.closeButton}
     76 + onClick={handleClose}
     77 + size="large"
     78 + color="primary"
     79 + >
     80 + <Close fontSize="small" color="primary" />
     81 + </IconButton>
     82 + <Typography variant="h6" classes={{ root: classes.title }}>
     83 + {t('Update a region')}
     84 + </Typography>
     85 + <SubscriptionAvatars context={queryData.region.editContext} />
     86 + <div className="clearfix" />
     87 + </div>
     88 + <div className={classes.container}>
     89 + <RegionEditionOverview
     90 + regionRef={queryData.region}
     91 + enableReferences={queryData.settings.platform_enable_reference?.includes('Region')}
     92 + context={queryData.region.editContext}
     93 + handleClose={handleClose}
     94 + />
     95 + </div>
     96 + </div>
     97 + );
     98 + }
     99 + 
     100 + return <Loader variant={LoaderVariant.inElement} />;
     101 +};
     102 + 
     103 +export default RegionEditionContainer;
     104 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/regions/RegionEditionOverview.js
    1  -import React, { Component } from 'react';
    2  -import * as PropTypes from 'prop-types';
    3  -import { graphql, createFragmentContainer } from 'react-relay';
    4  -import { Formik, Form, Field } from 'formik';
    5  -import withStyles from '@mui/styles/withStyles';
    6  -import {
    7  - assoc,
    8  - compose,
    9  - map,
    10  - pathOr,
    11  - pipe,
    12  - pick,
    13  - difference,
    14  - head,
    15  -} from 'ramda';
    16  -import * as Yup from 'yup';
    17  -import inject18n from '../../../../components/i18n';
    18  -import TextField from '../../../../components/TextField';
    19  -import { SubscriptionFocus } from '../../../../components/Subscription';
    20  -import { commitMutation } from '../../../../relay/environment';
    21  -import CreatedByField from '../../common/form/CreatedByField';
    22  -import ObjectMarkingField from '../../common/form/ObjectMarkingField';
    23  -import MarkDownField from '../../../../components/MarkDownField';
    24  -import {
    25  - convertCreatedBy,
    26  - convertMarkings,
    27  - convertStatus,
    28  -} from '../../../../utils/Edition';
    29  -import StatusField from '../../common/form/StatusField';
    30  - 
    31  -const styles = (theme) => ({
    32  - drawerPaper: {
    33  - minHeight: '100vh',
    34  - width: '50%',
    35  - position: 'fixed',
    36  - overflow: 'hidden',
    37  - transition: theme.transitions.create('width', {
    38  - easing: theme.transitions.easing.sharp,
    39  - duration: theme.transitions.duration.enteringScreen,
    40  - }),
    41  - padding: '30px 30px 30px 30px',
    42  - },
    43  - createButton: {
    44  - position: 'fixed',
    45  - bottom: 30,
    46  - right: 30,
    47  - },
    48  - importButton: {
    49  - position: 'absolute',
    50  - top: 30,
    51  - right: 30,
    52  - },
    53  -});
    54  - 
    55  -const regionMutationFieldPatch = graphql`
    56  - mutation RegionEditionOverviewFieldPatchMutation(
    57  - $id: ID!
    58  - $input: [EditInput]!
    59  - ) {
    60  - regionEdit(id: $id) {
    61  - fieldPatch(input: $input) {
    62  - ...RegionEditionOverview_region
    63  - ...Region_region
    64  - }
    65  - }
    66  - }
    67  -`;
    68  - 
    69  -export const regionEditionOverviewFocus = graphql`
    70  - mutation RegionEditionOverviewFocusMutation($id: ID!, $input: EditContext!) {
    71  - regionEdit(id: $id) {
    72  - contextPatch(input: $input) {
    73  - id
    74  - }
    75  - }
    76  - }
    77  -`;
    78  - 
    79  -const regionMutationRelationAdd = graphql`
    80  - mutation RegionEditionOverviewRelationAddMutation(
    81  - $id: ID!
    82  - $input: StixMetaRelationshipAddInput
    83  - ) {
    84  - regionEdit(id: $id) {
    85  - relationAdd(input: $input) {
    86  - from {
    87  - ...RegionEditionOverview_region
    88  - }
    89  - }
    90  - }
    91  - }
    92  -`;
    93  - 
    94  -const regionMutationRelationDelete = graphql`
    95  - mutation RegionEditionOverviewRelationDeleteMutation(
    96  - $id: ID!
    97  - $toId: StixRef!
    98  - $relationship_type: String!
    99  - ) {
    100  - regionEdit(id: $id) {
    101  - relationDelete(toId: $toId, relationship_type: $relationship_type) {
    102  - ...RegionEditionOverview_region
    103  - }
    104  - }
    105  - }
    106  -`;
    107  - 
    108  -const regionValidation = (t) => Yup.object().shape({
    109  - name: Yup.string().required(t('This field is required')),
    110  - description: Yup.string()
    111  - .min(3, t('The value is too short'))
    112  - .max(5000, t('The value is too long'))
    113  - .required(t('This field is required')),
    114  - x_opencti_workflow_id: Yup.object(),
    115  -});
    116  - 
    117  -class RegionEditionOverviewComponent extends Component {
    118  - handleChangeFocus(name) {
    119  - commitMutation({
    120  - mutation: regionEditionOverviewFocus,
    121  - variables: {
    122  - id: this.props.region.id,
    123  - input: {
    124  - focusOn: name,
    125  - },
    126  - },
    127  - });
    128  - }
    129  - 
    130  - handleSubmitField(name, value) {
    131  - let finalValue = value;
    132  - if (name === 'x_opencti_workflow_id') {
    133  - finalValue = value.value;
    134  - }
    135  - regionValidation(this.props.t)
    136  - .validateAt(name, { [name]: value })
    137  - .then(() => {
    138  - commitMutation({
    139  - mutation: regionMutationFieldPatch,
    140  - variables: {
    141  - id: this.props.region.id,
    142  - input: { key: name, value: finalValue ?? '' },
    143  - },
    144  - });
    145  - })
    146  - .catch(() => false);
    147  - }
    148  - 
    149  - handleChangeCreatedBy(name, value) {
    150  - if (!this.props.enableReferences) {
    151  - commitMutation({
    152  - mutation: regionMutationFieldPatch,
    153  - variables: {
    154  - id: this.props.region.id,
    155  - input: { key: 'createdBy', value: value.value || '' },
    156  - },
    157  - });
    158  - }
    159  - }
    160  - 
    161  - handleChangeObjectMarking(name, values) {
    162  - const { region } = this.props;
    163  - const currentMarkingDefinitions = pipe(
    164  - pathOr([], ['objectMarking', 'edges']),
    165  - map((n) => ({
    166  - label: n.node.definition,
    167  - value: n.node.id,
    168  - })),
    169  - )(region);
    170  - 
    171  - const added = difference(values, currentMarkingDefinitions);
    172  - const removed = difference(currentMarkingDefinitions, values);
    173  - 
    174  - if (added.length > 0) {
    175  - commitMutation({
    176  - mutation: regionMutationRelationAdd,
    177  - variables: {
    178  - id: this.props.region.id,
    179  - input: {
    180  - toId: head(added).value,
    181  - relationship_type: 'object-marking',
    182  - },
    183  - },
    184  - });
    185  - }
    186  - 
    187  - if (removed.length > 0) {
    188  - commitMutation({
    189  - mutation: regionMutationRelationDelete,
    190  - variables: {
    191  - id: this.props.region.id,
    192  - toId: head(removed).value,
    193  - relationship_type: 'object-marking',
    194  - },
    195  - });
    196  - }
    197  - }
    198  - 
    199  - render() {
    200  - const { t, region, context } = this.props;
    201  - const createdBy = convertCreatedBy(region);
    202  - const objectMarking = convertMarkings(region);
    203  - const status = convertStatus(t, region);
    204  - const initialValues = pipe(
    205  - assoc('createdBy', createdBy),
    206  - assoc('objectMarking', objectMarking),
    207  - assoc('x_opencti_workflow_id', status),
    208  - pick([
    209  - 'name',
    210  - 'description',
    211  - 'createdBy',
    212  - 'objectMarking',
    213  - 'x_opencti_workflow_id',
    214  - ]),
    215  - )(region);
    216  - return (
    217  - <div>
    218  - <Formik
    219  - enableReinitialize={true}
    220  - initialValues={initialValues}
    221  - validationSchema={regionValidation(t)}
    222  - onSubmit={() => true}
    223  - >
    224  - {({ setFieldValue }) => (
    225  - <Form style={{ margin: '20px 0 20px 0' }}>
    226  - <Field
    227  - component={TextField}
    228  - variant="standard"
    229  - name="name"
    230  - label={t('Name')}
    231  - fullWidth={true}
    232  - onFocus={this.handleChangeFocus.bind(this)}
    233  - onSubmit={this.handleSubmitField.bind(this)}
    234  - helperText={
    235  - <SubscriptionFocus context={context} fieldName="name" />
    236  - }
    237  - />
    238  - <Field
    239  - component={MarkDownField}
    240  - name="description"
    241  - label={t('Description')}
    242  - fullWidth={true}
    243  - multiline={true}
    244  - rows="4"
    245  - style={{ marginTop: 20 }}
    246  - onFocus={this.handleChangeFocus.bind(this)}
    247  - onSubmit={this.handleSubmitField.bind(this)}
    248  - helperText={
    249  - <SubscriptionFocus
    250  - context={context}
    251  - fieldName="description"
    252  - />
    253  - }
    254  - />
    255  - {region.workflowEnabled && (
    256  - <StatusField
    257  - name="x_opencti_workflow_id"
    258  - type="Region"
    259  - onFocus={this.handleChangeFocus.bind(this)}
    260  - onChange={this.handleSubmitField.bind(this)}
    261  - setFieldValue={setFieldValue}
    262  - style={{ marginTop: 20 }}
    263  - helpertext={
    264  - <SubscriptionFocus
    265  - context={context}
    266  - fieldName="x_opencti_workflow_id"
    267  - />
    268  - }
    269  - />
    270  - )}
    271  - <CreatedByField
    272  - name="createdBy"
    273  - style={{ marginTop: 20, width: '100%' }}
    274  - setFieldValue={setFieldValue}
    275  - helpertext={
    276  - <SubscriptionFocus context={context} fieldName="createdBy" />
    277  - }
    278  - onChange={this.handleChangeCreatedBy.bind(this)}
    279  - />
    280  - <ObjectMarkingField
    281  - name="objectMarking"
    282  - style={{ marginTop: 20, width: '100%' }}
    283  - helpertext={
    284  - <SubscriptionFocus
    285  - context={context}
    286  - fieldname="objectMarking"
    287  - />
    288  - }
    289  - onChange={this.handleChangeObjectMarking.bind(this)}
    290  - />
    291  - </Form>
    292  - )}
    293  - </Formik>
    294  - </div>
    295  - );
    296  - }
    297  -}
    298  - 
    299  -RegionEditionOverviewComponent.propTypes = {
    300  - classes: PropTypes.object,
    301  - theme: PropTypes.object,
    302  - t: PropTypes.func,
    303  - region: PropTypes.object,
    304  - context: PropTypes.array,
    305  -};
    306  - 
    307  -const RegionEditionOverview = createFragmentContainer(
    308  - RegionEditionOverviewComponent,
    309  - {
    310  - region: graphql`
    311  - fragment RegionEditionOverview_region on Region {
    312  - id
    313  - name
    314  - description
    315  - createdBy {
    316  - ... on Identity {
    317  - id
    318  - name
    319  - entity_type
    320  - }
    321  - }
    322  - objectMarking {
    323  - edges {
    324  - node {
    325  - id
    326  - definition
    327  - definition_type
    328  - }
    329  - }
    330  - }
    331  - status {
    332  - id
    333  - order
    334  - template {
    335  - name
    336  - color
    337  - }
    338  - }
    339  - workflowEnabled
    340  - }
    341  - `,
    342  - },
    343  -);
    344  - 
    345  -export default compose(
    346  - inject18n,
    347  - withStyles(styles, { withTheme: true }),
    348  -)(RegionEditionOverview);
    349  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/regions/RegionEditionOverview.tsx
     1 +import React, { FunctionComponent } from 'react';
     2 +import { graphql, useFragment, useMutation } from 'react-relay';
     3 +import { Formik, Form, Field } from 'formik';
     4 +import * as Yup from 'yup';
     5 +import { FormikConfig } from 'formik/dist/types';
     6 +import * as R from 'ramda';
     7 +import makeStyles from '@mui/styles/makeStyles';
     8 +import TextField from '../../../../components/TextField';
     9 +import { SubscriptionFocus } from '../../../../components/Subscription';
     10 +import CreatedByField from '../../common/form/CreatedByField';
     11 +import ObjectMarkingField from '../../common/form/ObjectMarkingField';
     12 +import MarkDownField from '../../../../components/MarkDownField';
     13 +import {
     14 + convertCreatedBy,
     15 + convertMarkings,
     16 + convertStatus,
     17 +} from '../../../../utils/Edition';
     18 +import StatusField from '../../common/form/StatusField';
     19 +import { adaptFieldValue } from '../../../../utils/String';
     20 +import { Option } from '../../common/form/ReferenceField';
     21 +import { useFormatter } from '../../../../components/i18n';
     22 +import { RegionEditionOverview_region$key } from './__generated__/RegionEditionOverview_region.graphql';
     23 +import { Theme } from '../../../../components/Theme';
     24 +import CommitMessage from '../../common/form/CommitMessage';
     25 + 
     26 +const useStyles = makeStyles <Theme>((theme) => ({
     27 + drawerPaper: {
     28 + minHeight: '100vh',
     29 + width: '50%',
     30 + position: 'fixed',
     31 + overflow: 'hidden',
     32 + transition: theme.transitions.create('width', {
     33 + easing: theme.transitions.easing.sharp,
     34 + duration: theme.transitions.duration.enteringScreen,
     35 + }),
     36 + padding: '30px 30px 30px 30px',
     37 + },
     38 + createButton: {
     39 + position: 'fixed',
     40 + bottom: 30,
     41 + right: 30,
     42 + },
     43 + importButton: {
     44 + position: 'absolute',
     45 + top: 30,
     46 + right: 30,
     47 + },
     48 +}));
     49 + 
     50 +const regionMutationFieldPatch = graphql`
     51 + mutation RegionEditionOverviewFieldPatchMutation(
     52 + $id: ID!
     53 + $input: [EditInput]!
     54 + $commitMessage: String
     55 + $references: [String]
     56 + ) {
     57 + regionEdit(id: $id) {
     58 + fieldPatch(
     59 + input: $input
     60 + commitMessage: $commitMessage
     61 + references: $references
     62 + ) {
     63 + ...RegionEditionOverview_region
     64 + ...Region_region
     65 + }
     66 + }
     67 + }
     68 +`;
     69 + 
     70 +export const regionEditionOverviewFocus = graphql`
     71 + mutation RegionEditionOverviewFocusMutation($id: ID!, $input: EditContext!) {
     72 + regionEdit(id: $id) {
     73 + contextPatch(input: $input) {
     74 + id
     75 + }
     76 + }
     77 + }
     78 +`;
     79 + 
     80 +const regionMutationRelationAdd = graphql`
     81 + mutation RegionEditionOverviewRelationAddMutation(
     82 + $id: ID!
     83 + $input: StixMetaRelationshipAddInput
     84 + ) {
     85 + regionEdit(id: $id) {
     86 + relationAdd(input: $input) {
     87 + from {
     88 + ...RegionEditionOverview_region
     89 + }
     90 + }
     91 + }
     92 + }
     93 +`;
     94 + 
     95 +const regionMutationRelationDelete = graphql`
     96 + mutation RegionEditionOverviewRelationDeleteMutation(
     97 + $id: ID!
     98 + $toId: StixRef!
     99 + $relationship_type: String!
     100 + ) {
     101 + regionEdit(id: $id) {
     102 + relationDelete(
     103 + toId: $toId,
     104 + relationship_type: $relationship_type
     105 + ) {
     106 + ...RegionEditionOverview_region
     107 + }
     108 + }
     109 + }
     110 +`;
     111 + 
     112 +const regionEditionOverviewFragment = graphql`
     113 + fragment RegionEditionOverview_region on Region {
     114 + id
     115 + name
     116 + description
     117 + latitude
     118 + longitude
     119 + createdBy {
     120 + ... on Identity {
     121 + id
     122 + name
     123 + entity_type
     124 + }
     125 + }
     126 + objectMarking {
     127 + edges {
     128 + node {
     129 + id
     130 + definition
     131 + definition_type
     132 + }
     133 + }
     134 + }
     135 + status {
     136 + id
     137 + order
     138 + template {
     139 + name
     140 + color
     141 + }
     142 + }
     143 + workflowEnabled
     144 + }
     145 +`;
     146 + 
     147 +const regionValidation = (t: (v: string) => string) => Yup.object().shape({
     148 + name: Yup.string().required(t('This field is required')),
     149 + description: Yup.string()
     150 + .min(3, t('The value is too short'))
     151 + .max(5000, t('The value is too long'))
     152 + .required(t('This field is required')),
     153 + latitude: Yup.number().typeError(t('This field must be a number')),
     154 + longitude: Yup.number().typeError(t('This field must be a number')),
     155 + x_opencti_workflow_id: Yup.object(),
     156 +});
     157 + 
     158 +interface RegionEdititionOverviewProps {
     159 + regionRef: RegionEditionOverview_region$key,
     160 + context: ReadonlyArray<{
     161 + readonly focusOn: string | null;
     162 + readonly name: string;
     163 + } | null> | null
     164 + enableReferences?: boolean
     165 + handleClose: () => void
     166 +}
     167 + 
     168 +interface RegionEditionFormValues {
     169 + message: string,
     170 + references: Option[],
     171 + x_opencti_workflow_id: Option
     172 + createdBy: Option
     173 + objectMarking: Option[]
     174 +}
     175 + 
     176 +const RegionEditionOverviewComponent: FunctionComponent<RegionEdititionOverviewProps> = ({
     177 + regionRef,
     178 + context,
     179 + enableReferences = false,
     180 + handleClose,
     181 +}) => {
     182 + const { t } = useFormatter();
     183 + const region = useFragment(regionEditionOverviewFragment, regionRef);
     184 + const createdBy = convertCreatedBy(region);
     185 + const objectMarking = convertMarkings(region);
     186 + const status = convertStatus(t, region);
     187 + 
     188 + const [commitRelationAdd] = useMutation(regionMutationRelationAdd);
     189 + const [commitRelationDelete] = useMutation(regionMutationRelationDelete);
     190 + const [commitFieldPatch] = useMutation(regionMutationFieldPatch);
     191 + const [commitEditionFocus] = useMutation(regionEditionOverviewFocus);
     192 + 
     193 + const handleChangeFocus = (name: string) => {
     194 + commitEditionFocus({
     195 + variables: {
     196 + id: region.id,
     197 + input: {
     198 + focusOn: name,
     199 + },
     200 + },
     201 + });
     202 + };
     203 + 
     204 + const onSubmit: FormikConfig<RegionEditionFormValues>['onSubmit'] = (values, { setSubmitting }) => {
     205 + const commitMessage = values.message;
     206 + const references = R.pluck('value', values.references || []);
     207 + const inputValues = R.pipe(
     208 + R.dissoc('message'),
     209 + R.dissoc('references'),
     210 + R.assoc('x_opencti_workflow_id', values.x_opencti_workflow_id?.value),
     211 + R.assoc('createdBy', values.createdBy?.value),
     212 + R.assoc('objectMarking', R.pluck('value', values.objectMarking)),
     213 + R.toPairs,
     214 + R.map((n) => ({
     215 + key: n[0],
     216 + value: adaptFieldValue(n[1]),
     217 + })),
     218 + )(values);
     219 + commitFieldPatch({
     220 + variables: {
     221 + id: region.id,
     222 + input: inputValues,
     223 + commitMessage:
     224 + commitMessage && commitMessage.length > 0 ? commitMessage : null,
     225 + references,
     226 + },
     227 + onCompleted: () => {
     228 + setSubmitting(false);
     229 + handleClose();
     230 + },
     231 + });
     232 + };
     233 + 
     234 + const handleChangeCreatedBy = (_: string, value: Option) => {
     235 + if (!enableReferences) {
     236 + commitRelationAdd({
     237 + variables: {
     238 + id: region.id,
     239 + input: { key: 'createdBy', value: value.value ?? '' },
     240 + },
     241 + });
     242 + }
     243 + };
     244 + 
     245 + const handleChangeObjectMarking = (_: string, values: Option[]) => {
     246 + if (!enableReferences) {
     247 + const currentMarkingDefinitions = (region.objectMarking?.edges ?? []).map((n) => ({ label: n?.node.definition, value: n?.node.id }));
     248 + const added = R.difference(values, currentMarkingDefinitions).at(0);
     249 + const removed = R.difference(currentMarkingDefinitions, values).at(0);
     250 + if (added) {
     251 + commitRelationAdd({
     252 + variables: {
     253 + id: region.id,
     254 + input: {
     255 + toId: added.value,
     256 + relationship_type: 'object-marking',
     257 + },
     258 + },
     259 + });
     260 + }
     261 + if (removed) {
     262 + commitRelationDelete({
     263 + variables: {
     264 + id: region.id,
     265 + toId: removed.value,
     266 + relationship_type: 'object-marking',
     267 + },
     268 + });
     269 + }
     270 + }
     271 + };
     272 + 
     273 + const handleSubmitField = (name: string, value: Option | string) => {
     274 + if (!enableReferences) {
     275 + let finalValue: unknown = value as string;
     276 + if (name === 'x_opencti_workflow_id') {
     277 + finalValue = (value as Option).value;
     278 + }
     279 + regionValidation(t)
     280 + .validateAt(name, { [name]: value })
     281 + .then(() => {
     282 + commitFieldPatch({
     283 + variables: {
     284 + id: region.id,
     285 + input: { key: name, value: finalValue ?? '' },
     286 + },
     287 + });
     288 + })
     289 + .catch(() => false);
     290 + }
     291 + };
     292 + 
     293 + const initialValues = R.pipe(
     294 + R.assoc('createdBy', createdBy),
     295 + R.assoc('objectMarking', objectMarking),
     296 + R.assoc('x_opencti_workflow_id', status),
     297 + R.pick([
     298 + 'name',
     299 + 'description',
     300 + 'latitude',
     301 + 'longitude',
     302 + 'createdBy',
     303 + 'objectMarking',
     304 + 'x_opencti_workflow_id',
     305 + ]),
     306 + )(region);
     307 + 
     308 + return (
     309 + <div>
     310 + <Formik
     311 + enableReinitialize={true}
     312 + initialValues={initialValues as never}
     313 + validationSchema={regionValidation(t)}
     314 + onSubmit={onSubmit}
     315 + >
     316 + {({
     317 + submitForm,
     318 + isSubmitting,
     319 + validateForm,
     320 + setFieldValue,
     321 + values,
     322 + }) => (
     323 + <Form style={{ margin: '20px 0 20px 0' }}>
     324 + <Field
     325 + component={TextField}
     326 + variant="standard"
     327 + name="name"
     328 + label={t('Name')}
     329 + fullWidth={true}
     330 + onFocus={handleChangeFocus}
     331 + onSubmit={handleSubmitField}
     332 + helperText={
     333 + <SubscriptionFocus context={context} fieldName="name" />
     334 + }
     335 + />
     336 + <Field
     337 + component={MarkDownField}
     338 + name="description"
     339 + label={t('Description')}
     340 + fullWidth={true}
     341 + multiline={true}
     342 + rows="4"
     343 + style={{ marginTop: 20 }}
     344 + onFocus={handleChangeFocus}
     345 + onSubmit={handleSubmitField}
     346 + helperText={
     347 + <SubscriptionFocus
     348 + context={context}
     349 + fieldName="description"
     350 + />
     351 + }
     352 + /><Field
     353 + component={TextField}
     354 + variant="standard"
     355 + style={{ marginTop: 20 }}
     356 + name="latitude"
     357 + label={t('Latitude')}
     358 + fullWidth={true}
     359 + onFocus={handleChangeFocus}
     360 + onSubmit={handleSubmitField}
     361 + helperText={
     362 + <SubscriptionFocus context={context} fieldName="latitude" />
     363 + }
     364 + />
     365 + <Field
     366 + component={TextField}
     367 + variant="standard"
     368 + style={{ marginTop: 20 }}
     369 + name="longitude"
     370 + label={t('Longitude')}
     371 + fullWidth={true}
     372 + onFocus={handleChangeFocus}
     373 + onSubmit={handleSubmitField}
     374 + helperText={
     375 + <SubscriptionFocus context={context} fieldName="longitude" />
     376 + }
     377 + />
     378 + 
     379 + {region.workflowEnabled && (
     380 + <StatusField
     381 + name="x_opencti_workflow_id"
     382 + type="Region"
     383 + onFocus={handleChangeFocus}
     384 + onChange={handleSubmitField}
     385 + setFieldValue={setFieldValue}
     386 + style={{ marginTop: 20 }}
     387 + helpertext={
     388 + <SubscriptionFocus
     389 + context={context}
     390 + fieldName="x_opencti_workflow_id"
     391 + />
     392 + }
     393 + />
     394 + )}
     395 + <CreatedByField
     396 + name="createdBy"
     397 + style={{ marginTop: 20, width: '100%' }}
     398 + setFieldValue={setFieldValue}
     399 + helpertext={
     400 + <SubscriptionFocus context={context} fieldName="createdBy" />
     401 + }
     402 + onChange={handleChangeCreatedBy}
     403 + />
     404 + <ObjectMarkingField
     405 + name="objectMarking"
     406 + style={{ marginTop: 20, width: '100%' }}
     407 + helpertext={
     408 + <SubscriptionFocus
     409 + context={context}
     410 + fieldname="objectMarking"
     411 + />
     412 + }
     413 + onChange={handleChangeObjectMarking}
     414 + />
     415 + {enableReferences && (
     416 + <CommitMessage
     417 + submitForm={submitForm}
     418 + disabled={isSubmitting}
     419 + validateForm={validateForm}
     420 + setFieldValue={setFieldValue}
     421 + values={values}
     422 + id={region.id}
     423 + />
     424 + )}
     425 + </Form>
     426 + )}
     427 + </Formik>
     428 + </div>
     429 + );
     430 +};
     431 + 
     432 +export default RegionEditionOverviewComponent;
     433 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/locations/regions/RegionKnowledge.js opencti-platform/opencti-front/src/private/components/locations/regions/RegionKnowledge.tsx
     1 +// TODO Remove this when V6
     2 +// eslint-disable-next-line @typescript-eslint/ban-ts-comment
     3 +// @ts-nocheck
    1 4  import React, { Component } from 'react';
    2 5  import * as PropTypes from 'prop-types';
    3 6  import { Route, Switch, withRouter } from 'react-router-dom';
    skipped 7 lines
    11 14  import RegionPopover from './RegionPopover';
    12 15  import StixDomainObjectHeader from '../../common/stix_domain_objects/StixDomainObjectHeader';
    13 16  import StixSightingRelationship from '../../events/stix_sighting_relationships/StixSightingRelationship';
     17 +import makeStyles from '@mui/styles/makeStyles';
    14 18   
    15  -const styles = () => ({
     19 +const useStyles = makeStyles(() => ({
    16 20   container: {
    17 21   margin: 0,
    18 22   padding: '0 200px 0 0',
    19 23   },
    20  -});
     24 +}));
     25 + 
     26 +const regionKnowledgeFragment = graphql`
     27 + fragment RegionKnowledge_region on Region {
     28 + id
     29 + name
     30 + x_opencti_aliases
     31 + }
     32 +`;
    21 33   
    22  -class RegionKnowledgeComponent extends Component {
    23  - render() {
    24  - const { classes, region } = this.props;
    25  - const link = `/dashboard/locations/regions/${region.id}/knowledge`;
     34 +const RegionKnowledgeComponent = ({ regionData }: { regionData: RegionKnowledge_region$key }) => {
     35 + const classes = useStyles();
     36 + const region = useFragment<RegionKnowledge_region$key>(regionKnowledgeFragment, regionData);
     37 + const link = `/dashboard/locations/regions/${region.id}/knowledge`;
    26 38   return (
    27 39   <div className={classes.container}>
    28 40   <StixDomainObjectHeader
    skipped 6 lines
    35 47   <Route
    36 48   exact
    37 49   path="/dashboard/locations/regions/:regionId/knowledge/relations/:relationId"
    38  - render={(routeProps) => (
     50 + render={(routeProps: any) => (
    39 51   <StixCoreRelationship
    40 52   entityId={region.id}
    41 53   paddingRight={true}
    skipped 4 lines
    46 58   <Route
    47 59   exact
    48 60   path="/dashboard/locations/regions/:regionId/knowledge/sightings/:sightingId"
    49  - render={(routeProps) => (
     61 + render={(routeProps: any) => (
    50 62   <StixSightingRelationship
    51 63   entityId={region.id}
    52 64   paddingRight={true}
    skipped 4 lines
    57 69   <Route
    58 70   exact
    59 71   path="/dashboard/locations/regions/:regionId/knowledge/overview"
    60  - render={(routeProps) => (
     72 + render={(routeProps: any) => (
    61 73   <StixDomainObjectKnowledge
    62 74   stixDomainObjectId={region.id}
    63 75   stixDomainObjectType="Region"
    skipped 4 lines
    68 80   <Route
    69 81   exact
    70 82   path="/dashboard/locations/regions/:regionId/knowledge/related"
    71  - render={(routeProps) => (
     83 + render={(routeProps: any) => (
    72 84   <EntityStixCoreRelationships
    73 85   entityId={region.id}
    74 86   relationshipTypes={['related-to']}
    skipped 22 lines
    97 109   <Route
    98 110   exact
    99 111   path="/dashboard/locations/regions/:regionId/knowledge/countries"
    100  - render={(routeProps) => (
     112 + render={(routeProps: any) => (
    101 113   <EntityStixCoreRelationships
    102 114   entityId={region.id}
    103 115   relationshipTypes={['located-at']}
    skipped 7 lines
    111 123   <Route
    112 124   exact
    113 125   path="/dashboard/locations/regions/:regionId/knowledge/cities"
    114  - render={(routeProps) => (
     126 + render={(routeProps: any) => (
    115 127   <EntityStixCoreRelationships
    116 128   entityId={region.id}
    117 129   relationshipTypes={['located-at']}
    skipped 7 lines
    125 137   <Route
    126 138   exact
    127 139   path="/dashboard/locations/regions/:regionId/knowledge/threat_actors"
    128  - render={(routeProps) => (
     140 + render={(routeProps: any) => (
    129 141   <EntityStixCoreRelationships
    130 142   entityId={region.id}
    131 143   relationshipTypes={['targets']}
    skipped 7 lines
    139 151   <Route
    140 152   exact
    141 153   path="/dashboard/locations/regions/:regionId/knowledge/intrusion_sets"
    142  - render={(routeProps) => (
     154 + render={(routeProps: any) => (
    143 155   <EntityStixCoreRelationships
    144 156   entityId={region.id}
    145 157   relationshipTypes={['targets']}
    skipped 7 lines
    153 165   <Route
    154 166   exact
    155 167   path="/dashboard/locations/regions/:regionId/knowledge/campaigns"
    156  - render={(routeProps) => (
     168 + render={(routeProps: any) => (
    157 169   <EntityStixCoreRelationships
    158 170   entityId={region.id}
    159 171   relationshipTypes={['targets']}
    skipped 7 lines
    167 179   <Route
    168 180   exact
    169 181   path="/dashboard/locations/regions/:regionId/knowledge/incidents"
    170  - render={(routeProps) => (
     182 + render={(routeProps: any) => (
    171 183   <EntityStixCoreRelationships
    172 184   entityId={region.id}
    173 185   relationshipTypes={['targets']}
    skipped 7 lines
    181 193   <Route
    182 194   exact
    183 195   path="/dashboard/locations/regions/:regionId/knowledge/malwares"
    184  - render={(routeProps) => (
     196 + render={(routeProps: any) => (
    185 197   <EntityStixCoreRelationships
    186 198   entityId={region.id}
    187 199   relationshipTypes={['targets']}
    skipped 7 lines
    195 207   <Route
    196 208   exact
    197 209   path="/dashboard/locations/regions/:regionId/knowledge/attack_patterns"
    198  - render={(routeProps) => (
     210 + render={(routeProps: any) => (
    199 211   <EntityStixCoreRelationships
    200 212   entityId={region.id}
    201 213   relationshipTypes={['targets']}
    skipped 7 lines
    209 221   <Route
    210 222   exact
    211 223   path="/dashboard/locations/regions/:regionId/knowledge/tools"
    212  - render={(routeProps) => (
     224 + render={(routeProps: any) => (
    213 225   <EntityStixCoreRelationships
    214 226   entityId={region.id}
    215 227   relationshipTypes={['targets']}
    skipped 7 lines
    223 235   </Switch>
    224 236   </div>
    225 237   );
    226  - }
    227 238  }
    228 239   
    229  -RegionKnowledgeComponent.propTypes = {
    230  - region: PropTypes.object,
    231  - classes: PropTypes.object,
    232  - t: PropTypes.func,
    233  -};
    234  - 
    235  -const RegionKnowledge = createFragmentContainer(RegionKnowledgeComponent, {
    236  - region: graphql`
    237  - fragment RegionKnowledge_region on Region {
    238  - id
    239  - name
    240  - x_opencti_aliases
    241  - }
    242  - `,
    243  -});
    244  - 
    245  -export default compose(
    246  - inject18n,
    247  - withRouter,
    248  - withStyles(styles),
    249  -)(RegionKnowledge);
     240 +export default RegionKnowledgeComponent;
    250 241   
Please wait...
Page is in error, reload to recover