@automattic/jetpack-components 0.62.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/components/threat-fixer-button/index.tsx +2 -2
- package/components/threat-modal/fixer-state-notice.tsx +63 -0
- package/components/threat-modal/index.tsx +44 -89
- package/components/threat-modal/styles.module.scss +50 -4
- package/components/threat-modal/threat-actions.tsx +44 -48
- package/components/threat-modal/threat-fix-confirmation.tsx +54 -0
- package/components/threat-modal/threat-fix-details.tsx +8 -14
- package/components/threat-modal/threat-notice.tsx +84 -0
- package/components/threat-modal/threat-summary.tsx +30 -0
- package/components/threat-modal/threat-technical-details.tsx +45 -14
- package/package.json +2 -2
- package/components/threat-modal/credentials-gate.tsx +0 -65
- package/components/threat-modal/user-connection-gate.tsx +0 -64
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
### This is a list detailing changes for the Jetpack RNA Components package releases.
|
|
4
4
|
|
|
5
|
+
## [0.63.0] - 2024-11-26
|
|
6
|
+
### Changed
|
|
7
|
+
- Updates ThreatModal flow [#40214]
|
|
8
|
+
|
|
5
9
|
## [0.62.0] - 2024-11-25
|
|
6
10
|
### Added
|
|
7
11
|
- Add Stats icon [#40236]
|
|
@@ -1226,6 +1230,7 @@
|
|
|
1226
1230
|
### Changed
|
|
1227
1231
|
- Update node version requirement to 14.16.1
|
|
1228
1232
|
|
|
1233
|
+
[0.63.0]: https://github.com/Automattic/jetpack-components/compare/0.62.0...0.63.0
|
|
1229
1234
|
[0.62.0]: https://github.com/Automattic/jetpack-components/compare/0.61.0...0.62.0
|
|
1230
1235
|
[0.61.0]: https://github.com/Automattic/jetpack-components/compare/0.60.0...0.61.0
|
|
1231
1236
|
[0.60.0]: https://github.com/Automattic/jetpack-components/compare/0.59.0...0.60.0
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
type Threat,
|
|
4
4
|
getFixerState,
|
|
5
5
|
getFixerAction,
|
|
6
|
-
|
|
6
|
+
getFixerDescription,
|
|
7
7
|
} from '@automattic/jetpack-scan';
|
|
8
8
|
import { Tooltip } from '@wordpress/components';
|
|
9
9
|
import { useCallback, useMemo } from '@wordpress/element';
|
|
@@ -50,7 +50,7 @@ export default function ThreatFixerButton( {
|
|
|
50
50
|
return __( 'An auto-fixer is in progress.', 'jetpack' );
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
return
|
|
53
|
+
return getFixerDescription( threat );
|
|
54
54
|
}, [ threat, fixerState ] );
|
|
55
55
|
|
|
56
56
|
const buttonText = useMemo( () => {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import styles from './styles.module.scss';
|
|
4
|
+
import ThreatNotice from './threat-notice';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* FixerStateNotice component
|
|
8
|
+
*
|
|
9
|
+
* @param {object} props - The component props.
|
|
10
|
+
* @param {object} props.fixerState - The state of the fixer (inProgress, error, stale).
|
|
11
|
+
* @param {boolean} props.fixerState.inProgress - Whether the fixer is in progress.
|
|
12
|
+
* @param {boolean} props.fixerState.error - Whether the fixer encountered an error.
|
|
13
|
+
* @param {boolean} props.fixerState.stale - Whether the fixer is stale.
|
|
14
|
+
*
|
|
15
|
+
* @return {JSX.Element | null} The rendered fixer notice or null if no notice is available.
|
|
16
|
+
*/
|
|
17
|
+
const FixerStateNotice = ( {
|
|
18
|
+
fixerState,
|
|
19
|
+
}: {
|
|
20
|
+
fixerState: { inProgress: boolean; error: boolean; stale: boolean };
|
|
21
|
+
} ) => {
|
|
22
|
+
const { status, title, content } = useMemo( () => {
|
|
23
|
+
if ( fixerState.error ) {
|
|
24
|
+
return {
|
|
25
|
+
status: 'error' as const,
|
|
26
|
+
title: __( 'An error occurred auto-fixing this threat', 'jetpack' ),
|
|
27
|
+
content: __(
|
|
28
|
+
'Jetpack encountered a filesystem error while attempting to auto-fix this threat. Please try again later or contact support.',
|
|
29
|
+
'jetpack'
|
|
30
|
+
),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if ( fixerState.stale ) {
|
|
35
|
+
return {
|
|
36
|
+
status: 'error' as const,
|
|
37
|
+
title: __( 'The auto-fixer is taking longer than expected', 'jetpack' ),
|
|
38
|
+
content: __(
|
|
39
|
+
'Jetpack has been attempting to auto-fix this threat for too long, and something may have gone wrong. Please try again later or contact support.',
|
|
40
|
+
'jetpack'
|
|
41
|
+
),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if ( fixerState.inProgress ) {
|
|
46
|
+
return {
|
|
47
|
+
status: 'success' as const,
|
|
48
|
+
title: __( 'An auto-fixer is in progress', 'jetpack' ),
|
|
49
|
+
content: __( 'Please wait while Jetpack auto-fixes the threat.', 'jetpack' ),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {};
|
|
54
|
+
}, [ fixerState ] );
|
|
55
|
+
|
|
56
|
+
return title ? (
|
|
57
|
+
<div className={ styles[ 'fixer-notice' ] }>
|
|
58
|
+
<ThreatNotice status={ status } title={ title } content={ content } />
|
|
59
|
+
</div>
|
|
60
|
+
) : null;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default FixerStateNotice;
|
|
@@ -1,15 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { __ } from '@wordpress/i18n';
|
|
5
|
-
import { useMemo } from 'react';
|
|
1
|
+
import { type Threat } from '@automattic/jetpack-scan';
|
|
2
|
+
import { Modal } from '@wordpress/components';
|
|
3
|
+
import { createContext } from 'react';
|
|
6
4
|
import Text from '../text';
|
|
7
|
-
import
|
|
5
|
+
import ThreatSeverityBadge from '../threat-severity-badge';
|
|
8
6
|
import styles from './styles.module.scss';
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
import ThreatFixConfirmation from './threat-fix-confirmation';
|
|
8
|
+
interface ThreatModalContextType {
|
|
9
|
+
closeModal: () => void;
|
|
10
|
+
threat: Threat;
|
|
11
|
+
handleUpgradeClick?: () => void;
|
|
12
|
+
userConnectionNeeded: boolean;
|
|
13
|
+
handleConnectUser: () => void;
|
|
14
|
+
userIsConnecting: boolean;
|
|
15
|
+
siteCredentialsNeeded: boolean;
|
|
16
|
+
credentialsIsFetching: boolean;
|
|
17
|
+
credentialsRedirectUrl: string;
|
|
18
|
+
handleFixThreatClick?: ( threats: Threat[] ) => void;
|
|
19
|
+
handleIgnoreThreatClick?: ( threats: Threat[] ) => void;
|
|
20
|
+
handleUnignoreThreatClick?: ( threats: Threat[] ) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const ThreatModalContext = createContext< ThreatModalContextType | null >( null );
|
|
13
24
|
|
|
14
25
|
/**
|
|
15
26
|
* ThreatModal component
|
|
@@ -61,92 +72,36 @@ export default function ThreatModal( {
|
|
|
61
72
|
const userConnectionNeeded = ! isUserConnected || ! hasConnectedOwner;
|
|
62
73
|
const siteCredentialsNeeded = ! credentials || credentials.length === 0;
|
|
63
74
|
|
|
64
|
-
const fixerState = useMemo( () => {
|
|
65
|
-
return getFixerState( threat.fixer );
|
|
66
|
-
}, [ threat.fixer ] );
|
|
67
|
-
|
|
68
|
-
const getModalTitle = useMemo( () => {
|
|
69
|
-
if ( userConnectionNeeded ) {
|
|
70
|
-
return <Text variant="title-small">{ __( 'User connection needed', 'jetpack' ) }</Text>;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if ( siteCredentialsNeeded ) {
|
|
74
|
-
return <Text variant="title-small">{ __( 'Site credentials needed', 'jetpack' ) }</Text>;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<>
|
|
79
|
-
<Text variant="title-small">{ threat.title }</Text>
|
|
80
|
-
{ !! threat.severity && <ThreatSeverityBadge severity={ threat.severity } /> }
|
|
81
|
-
</>
|
|
82
|
-
);
|
|
83
|
-
}, [ userConnectionNeeded, siteCredentialsNeeded, threat.title, threat.severity ] );
|
|
84
|
-
|
|
85
75
|
return (
|
|
86
76
|
<Modal
|
|
77
|
+
title={
|
|
78
|
+
<div className={ styles.title }>
|
|
79
|
+
<Text variant="title-small">{ threat.title }</Text>
|
|
80
|
+
{ !! threat.severity && <ThreatSeverityBadge severity={ threat.severity } /> }
|
|
81
|
+
</div>
|
|
82
|
+
}
|
|
87
83
|
size="large"
|
|
88
|
-
title={ <div className={ styles.title }>{ getModalTitle }</div> }
|
|
89
84
|
{ ...modalProps }
|
|
90
85
|
>
|
|
91
86
|
<div className={ styles[ 'threat-details' ] }>
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
87
|
+
<ThreatModalContext.Provider
|
|
88
|
+
value={ {
|
|
89
|
+
closeModal: modalProps.onRequestClose,
|
|
90
|
+
threat,
|
|
91
|
+
handleUpgradeClick,
|
|
92
|
+
userConnectionNeeded,
|
|
93
|
+
handleConnectUser,
|
|
94
|
+
userIsConnecting,
|
|
95
|
+
siteCredentialsNeeded,
|
|
96
|
+
credentialsIsFetching,
|
|
97
|
+
credentialsRedirectUrl,
|
|
98
|
+
handleFixThreatClick,
|
|
99
|
+
handleIgnoreThreatClick,
|
|
100
|
+
handleUnignoreThreatClick,
|
|
101
|
+
} }
|
|
96
102
|
>
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
credentialsIsFetching={ credentialsIsFetching }
|
|
100
|
-
credentialsRedirectUrl={ credentialsRedirectUrl }
|
|
101
|
-
>
|
|
102
|
-
<>
|
|
103
|
-
{ fixerState.error && (
|
|
104
|
-
<Notice isDismissible={ false } status="error">
|
|
105
|
-
<Text>{ __( 'An error occurred auto-fixing this threat.', 'jetpack' ) }</Text>
|
|
106
|
-
</Notice>
|
|
107
|
-
) }
|
|
108
|
-
{ fixerState.stale && (
|
|
109
|
-
<Notice isDismissible={ false } status="error">
|
|
110
|
-
<Text>{ __( 'The auto-fixer is taking longer than expected.', 'jetpack' ) }</Text>
|
|
111
|
-
</Notice>
|
|
112
|
-
) }
|
|
113
|
-
{ fixerState.inProgress && ! fixerState.stale && (
|
|
114
|
-
<Notice isDismissible={ false } status="success">
|
|
115
|
-
<Text>{ __( 'The auto-fixer is in progress.', 'jetpack' ) }</Text>
|
|
116
|
-
</Notice>
|
|
117
|
-
) }
|
|
118
|
-
<div className={ styles.section }>
|
|
119
|
-
{ !! threat.description && <Text>{ threat.description }</Text> }
|
|
120
|
-
|
|
121
|
-
{ !! threat.source && (
|
|
122
|
-
<div>
|
|
123
|
-
<Button
|
|
124
|
-
variant="link"
|
|
125
|
-
isExternalLink={ true }
|
|
126
|
-
weight="regular"
|
|
127
|
-
href={ threat.source }
|
|
128
|
-
>
|
|
129
|
-
{ __( 'See more technical details of this threat', 'jetpack' ) }
|
|
130
|
-
</Button>
|
|
131
|
-
</div>
|
|
132
|
-
) }
|
|
133
|
-
</div>
|
|
134
|
-
|
|
135
|
-
<ThreatFixDetails threat={ threat } handleUpgradeClick={ handleUpgradeClick } />
|
|
136
|
-
|
|
137
|
-
<ThreatTechnicalDetails threat={ threat } />
|
|
138
|
-
|
|
139
|
-
<ThreatActions
|
|
140
|
-
threat={ threat }
|
|
141
|
-
closeModal={ modalProps.onRequestClose }
|
|
142
|
-
handleFixThreatClick={ handleFixThreatClick }
|
|
143
|
-
handleIgnoreThreatClick={ handleIgnoreThreatClick }
|
|
144
|
-
handleUnignoreThreatClick={ handleUnignoreThreatClick }
|
|
145
|
-
fixerState={ fixerState }
|
|
146
|
-
/>
|
|
147
|
-
</>
|
|
148
|
-
</CredentialsGate>
|
|
149
|
-
</UserConnectionGate>
|
|
103
|
+
<ThreatFixConfirmation />
|
|
104
|
+
</ThreatModalContext.Provider>
|
|
150
105
|
</div>
|
|
151
106
|
</Modal>
|
|
152
107
|
);
|
|
@@ -10,6 +10,20 @@
|
|
|
10
10
|
gap: calc( var( --spacing-base ) * 2 ); // 16px
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
.section .section__toggle {
|
|
14
|
+
text-decoration: none;
|
|
15
|
+
|
|
16
|
+
&:hover {
|
|
17
|
+
text-decoration: underline;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&__content {
|
|
21
|
+
display: flex;
|
|
22
|
+
gap: calc( var( --spacing-base ) / 2 ); // 4px
|
|
23
|
+
align-items: center;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
13
27
|
.title {
|
|
14
28
|
display: flex;
|
|
15
29
|
align-items: center;
|
|
@@ -22,14 +36,46 @@
|
|
|
22
36
|
overflow-x: auto;
|
|
23
37
|
}
|
|
24
38
|
|
|
25
|
-
.modal-
|
|
26
|
-
display: flex;
|
|
27
|
-
justify-content: flex-end;
|
|
39
|
+
.modal-footer {
|
|
28
40
|
padding-top: calc( var( --spacing-base ) * 3 ); // 24px
|
|
29
|
-
border-top: 1px solid var( --jp-gray-
|
|
41
|
+
border-top: 1px solid var( --jp-gray-5 );
|
|
30
42
|
|
|
31
43
|
.threat-actions {
|
|
44
|
+
display: flex;
|
|
45
|
+
justify-content: flex-end;
|
|
46
|
+
gap: calc( var( --spacing-base ) * 2 ); // 16px;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.fixer-notice {
|
|
51
|
+
padding-bottom: calc( var( --spacing-base ) * 3 ); // 24px
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.notice {
|
|
55
|
+
&__title {
|
|
56
|
+
display: flex;
|
|
57
|
+
gap: calc( var( --spacing-base ) / 2 ); // 4px
|
|
58
|
+
|
|
59
|
+
p {
|
|
60
|
+
font-weight: bold;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
&__actions {
|
|
32
65
|
display: flex;
|
|
33
66
|
gap: calc( var( --spacing-base ) * 2 ); // 16px;
|
|
34
67
|
}
|
|
68
|
+
|
|
69
|
+
&__action {
|
|
70
|
+
margin-top: calc( var( --spacing-base ) * 2 ); // 16px;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
svg.spinner {
|
|
75
|
+
color: var( --jp-black );
|
|
76
|
+
height: 20px;
|
|
77
|
+
width: 20px;
|
|
78
|
+
margin-left: calc( var( --spacing-base ) / 2 ); // 4px;
|
|
79
|
+
margin-right: 6px;
|
|
80
|
+
|
|
35
81
|
}
|
|
@@ -1,41 +1,33 @@
|
|
|
1
1
|
import { Button } from '@automattic/jetpack-components';
|
|
2
|
-
import {
|
|
2
|
+
import { getFixerState, getDetailedFixerAction } from '@automattic/jetpack-scan';
|
|
3
3
|
import { __ } from '@wordpress/i18n';
|
|
4
|
-
import
|
|
4
|
+
import { useCallback, useContext, useMemo } from 'react';
|
|
5
|
+
import FixerStateNotice from './fixer-state-notice';
|
|
5
6
|
import styles from './styles.module.scss';
|
|
7
|
+
import { ThreatModalContext } from '.';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* ThreatActions component
|
|
9
11
|
*
|
|
10
|
-
* @param {object} props - The component props.
|
|
11
|
-
* @param {object} props.threat - The threat object containing action details.
|
|
12
|
-
* @param {Function} props.closeModal - Function to close the modal.
|
|
13
|
-
* @param {Function} [props.handleFixThreatClick] - Function to handle fixing the threat.
|
|
14
|
-
* @param {Function} [props.handleIgnoreThreatClick] - Function to handle ignoring the threat.
|
|
15
|
-
* @param {Function} [props.handleUnignoreThreatClick] - Function to handle unignoring the threat.
|
|
16
|
-
* @param {object} props.fixerState - The state of the fixer (inProgress, error, stale).
|
|
17
|
-
* @param {boolean} props.fixerState.inProgress - Whether the fixer is in progress.
|
|
18
|
-
* @param {boolean} props.fixerState.error - Whether the fixer encountered an error.
|
|
19
|
-
* @param {boolean} props.fixerState.stale - Whether the fixer is stale.
|
|
20
|
-
*
|
|
21
12
|
* @return {JSX.Element | null} The rendered action buttons or null if no actions are available.
|
|
22
13
|
*/
|
|
23
|
-
const ThreatActions = ( {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
14
|
+
const ThreatActions = (): JSX.Element => {
|
|
15
|
+
const {
|
|
16
|
+
closeModal,
|
|
17
|
+
threat,
|
|
18
|
+
handleFixThreatClick,
|
|
19
|
+
handleIgnoreThreatClick,
|
|
20
|
+
handleUnignoreThreatClick,
|
|
21
|
+
userConnectionNeeded,
|
|
22
|
+
siteCredentialsNeeded,
|
|
23
|
+
} = useContext( ThreatModalContext );
|
|
24
|
+
const disabled = userConnectionNeeded || siteCredentialsNeeded;
|
|
25
|
+
|
|
26
|
+
const fixerState = useMemo( () => {
|
|
27
|
+
return getFixerState( threat.fixer );
|
|
28
|
+
}, [ threat.fixer ] );
|
|
29
|
+
|
|
30
|
+
const detailedFixerAction = useMemo( () => getDetailedFixerAction( threat ), [ threat ] );
|
|
39
31
|
|
|
40
32
|
const onFixClick = useCallback( () => {
|
|
41
33
|
handleFixThreatClick?.( [ threat ] );
|
|
@@ -52,39 +44,43 @@ const ThreatActions = ( {
|
|
|
52
44
|
closeModal();
|
|
53
45
|
}, [ threat, handleUnignoreThreatClick, closeModal ] );
|
|
54
46
|
|
|
55
|
-
if ( !
|
|
47
|
+
if ( ! threat?.status || threat.status === 'fixed' ) {
|
|
56
48
|
return null;
|
|
57
49
|
}
|
|
58
50
|
|
|
59
51
|
return (
|
|
60
|
-
<div className={ styles[ 'modal-
|
|
52
|
+
<div className={ styles[ 'modal-footer' ] }>
|
|
53
|
+
<FixerStateNotice fixerState={ fixerState } />
|
|
61
54
|
<div className={ styles[ 'threat-actions' ] }>
|
|
62
|
-
{ threat.status === 'ignored' &&
|
|
63
|
-
<Button
|
|
64
|
-
{
|
|
55
|
+
{ threat.status === 'ignored' && (
|
|
56
|
+
<Button
|
|
57
|
+
disabled={ disabled }
|
|
58
|
+
isDestructive={ true }
|
|
59
|
+
variant="secondary"
|
|
60
|
+
onClick={ onUnignoreClick }
|
|
61
|
+
>
|
|
62
|
+
{ __( 'Un-ignore threat', 'jetpack' ) }
|
|
65
63
|
</Button>
|
|
66
64
|
) }
|
|
67
65
|
{ threat.status === 'current' && (
|
|
68
66
|
<>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
) }
|
|
79
|
-
{ threat.fixable && handleFixThreatClick && (
|
|
67
|
+
<Button
|
|
68
|
+
isDestructive={ true }
|
|
69
|
+
variant="secondary"
|
|
70
|
+
onClick={ onIgnoreClick }
|
|
71
|
+
disabled={ disabled || ( fixerState.inProgress && ! fixerState.stale ) }
|
|
72
|
+
>
|
|
73
|
+
{ __( 'Ignore threat', 'jetpack' ) }
|
|
74
|
+
</Button>
|
|
75
|
+
{ threat.fixable && (
|
|
80
76
|
<Button
|
|
81
77
|
isPrimary
|
|
82
|
-
disabled={ fixerState.inProgress && ! fixerState.stale }
|
|
78
|
+
disabled={ disabled || ( fixerState.inProgress && ! fixerState.stale ) }
|
|
83
79
|
onClick={ onFixClick }
|
|
84
80
|
>
|
|
85
81
|
{ fixerState.error || fixerState.stale
|
|
86
|
-
? __( 'Retry
|
|
87
|
-
:
|
|
82
|
+
? __( 'Retry fixer', 'jetpack' )
|
|
83
|
+
: detailedFixerAction }
|
|
88
84
|
</Button>
|
|
89
85
|
) }
|
|
90
86
|
</>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import ThreatActions from './threat-actions';
|
|
4
|
+
import ThreatFixDetails from './threat-fix-details';
|
|
5
|
+
import ThreatNotice from './threat-notice';
|
|
6
|
+
import ThreatSummary from './threat-summary';
|
|
7
|
+
import ThreatTechnicalDetails from './threat-technical-details';
|
|
8
|
+
import { ThreatModalContext } from '.';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ThreatFixConfirmation component
|
|
12
|
+
*
|
|
13
|
+
* @return {JSX.Element} The rendered fix confirmation.
|
|
14
|
+
*/
|
|
15
|
+
const ThreatFixConfirmation = () => {
|
|
16
|
+
const { userConnectionNeeded, siteCredentialsNeeded } = useContext( ThreatModalContext );
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<ThreatSummary />
|
|
20
|
+
<ThreatTechnicalDetails />
|
|
21
|
+
<ThreatFixDetails />
|
|
22
|
+
{ siteCredentialsNeeded && userConnectionNeeded && (
|
|
23
|
+
<ThreatNotice
|
|
24
|
+
title={ 'Additional connections needed' }
|
|
25
|
+
content={ __(
|
|
26
|
+
'A user connection and server credentials provide Jetpack the access necessary to ignore and auto-fix threats on your site.',
|
|
27
|
+
'jetpack'
|
|
28
|
+
) }
|
|
29
|
+
/>
|
|
30
|
+
) }
|
|
31
|
+
{ ! siteCredentialsNeeded && userConnectionNeeded && (
|
|
32
|
+
<ThreatNotice
|
|
33
|
+
title={ __( 'User connection needed', 'jetpack' ) }
|
|
34
|
+
content={ __(
|
|
35
|
+
'A user connection provides Jetpack the access necessary to ignore and auto-fix threats on your site.',
|
|
36
|
+
'jetpack'
|
|
37
|
+
) }
|
|
38
|
+
/>
|
|
39
|
+
) }
|
|
40
|
+
{ siteCredentialsNeeded && ! userConnectionNeeded && (
|
|
41
|
+
<ThreatNotice
|
|
42
|
+
title={ __( 'Site credentials needed', 'jetpack' ) }
|
|
43
|
+
content={ __(
|
|
44
|
+
'Your server credentials allow Jetpack to access the server that’s powering your website. This information is securely saved and only used to ignore and auto-fix threats detected on your site.',
|
|
45
|
+
'jetpack'
|
|
46
|
+
) }
|
|
47
|
+
/>
|
|
48
|
+
) }
|
|
49
|
+
<ThreatActions />
|
|
50
|
+
</>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default ThreatFixConfirmation;
|
|
@@ -1,26 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getFixerDescription } from '@automattic/jetpack-scan';
|
|
2
2
|
import { __, sprintf } from '@wordpress/i18n';
|
|
3
|
-
import React, { useMemo } from 'react';
|
|
3
|
+
import React, { useMemo, useContext } from 'react';
|
|
4
4
|
import ContextualUpgradeTrigger from '../contextual-upgrade-trigger';
|
|
5
5
|
import Text from '../text';
|
|
6
6
|
import styles from './styles.module.scss';
|
|
7
|
+
import { ThreatModalContext } from '.';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* ThreatFixDetails component
|
|
10
11
|
*
|
|
11
|
-
* @param {object} props - The component props.
|
|
12
|
-
* @param {object} props.threat - The threat object containing fix details.
|
|
13
|
-
* @param {Function} props.handleUpgradeClick - Function to handle upgrade click events.
|
|
14
|
-
*
|
|
15
12
|
* @return {JSX.Element | null} The rendered fix details or null if no fixable details are available.
|
|
16
13
|
*/
|
|
17
|
-
const ThreatFixDetails = ( {
|
|
18
|
-
threat,
|
|
19
|
-
|
|
20
|
-
}: {
|
|
21
|
-
threat: Threat;
|
|
22
|
-
handleUpgradeClick: () => void;
|
|
23
|
-
} ): JSX.Element => {
|
|
14
|
+
const ThreatFixDetails = (): JSX.Element => {
|
|
15
|
+
const { threat, handleUpgradeClick } = useContext( ThreatModalContext );
|
|
16
|
+
|
|
24
17
|
const title = useMemo( () => {
|
|
25
18
|
if ( threat.status === 'fixed' ) {
|
|
26
19
|
return __( 'How did Jetpack fix it?', 'jetpack' );
|
|
@@ -42,8 +35,9 @@ const ThreatFixDetails = ( {
|
|
|
42
35
|
threat.fixedIn
|
|
43
36
|
);
|
|
44
37
|
}
|
|
38
|
+
|
|
45
39
|
// The threat has an auto-fix available.
|
|
46
|
-
return
|
|
40
|
+
return getFixerDescription( threat );
|
|
47
41
|
}, [ threat ] );
|
|
48
42
|
|
|
49
43
|
if ( ! threat.fixable && ! threat.fixedIn ) {
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Text, Button } from '@automattic/jetpack-components';
|
|
2
|
+
import { Notice, Spinner } from '@wordpress/components';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
import { Icon, warning } from '@wordpress/icons';
|
|
5
|
+
import { useContext } from 'react';
|
|
6
|
+
import styles from './styles.module.scss';
|
|
7
|
+
import { ThreatModalContext } from '.';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ThreatNotice component
|
|
11
|
+
*
|
|
12
|
+
* @param {object} props - The component props.
|
|
13
|
+
* @param {string} props.status - The status of the notice.
|
|
14
|
+
* @param {string} props.title - The title of the notice.
|
|
15
|
+
* @param {string} props.content - The content of the notice.
|
|
16
|
+
*
|
|
17
|
+
* @return {JSX.Element} The rendered ThreatNotice component.
|
|
18
|
+
*/
|
|
19
|
+
const ThreatNotice = ( {
|
|
20
|
+
status = 'warning',
|
|
21
|
+
title,
|
|
22
|
+
content,
|
|
23
|
+
}: {
|
|
24
|
+
status?: 'warning' | 'error' | 'success' | undefined;
|
|
25
|
+
title: string;
|
|
26
|
+
content: string;
|
|
27
|
+
} ): JSX.Element => {
|
|
28
|
+
const {
|
|
29
|
+
userConnectionNeeded,
|
|
30
|
+
userIsConnecting,
|
|
31
|
+
handleConnectUser,
|
|
32
|
+
siteCredentialsNeeded,
|
|
33
|
+
credentialsRedirectUrl,
|
|
34
|
+
credentialsIsFetching,
|
|
35
|
+
} = useContext( ThreatModalContext );
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Notice
|
|
39
|
+
status={ status }
|
|
40
|
+
isDismissible={ false }
|
|
41
|
+
children={
|
|
42
|
+
<div className={ styles.notice }>
|
|
43
|
+
<div className={ styles.notice__title }>
|
|
44
|
+
{ status === 'success' ? (
|
|
45
|
+
<Spinner className={ styles.spinner } />
|
|
46
|
+
) : (
|
|
47
|
+
<Icon icon={ warning } size={ 30 } />
|
|
48
|
+
) }
|
|
49
|
+
<Text variant="title-small" mb={ 2 }>
|
|
50
|
+
{ title }
|
|
51
|
+
</Text>
|
|
52
|
+
</div>
|
|
53
|
+
<Text>{ content }</Text>
|
|
54
|
+
<div className={ styles.notice__actions }>
|
|
55
|
+
{ userConnectionNeeded && (
|
|
56
|
+
<Button
|
|
57
|
+
className={ styles.notice__action }
|
|
58
|
+
isExternalLink={ true }
|
|
59
|
+
weight="regular"
|
|
60
|
+
isLoading={ userIsConnecting }
|
|
61
|
+
onClick={ handleConnectUser }
|
|
62
|
+
>
|
|
63
|
+
{ __( 'Connect your user account', 'jetpack' ) }
|
|
64
|
+
</Button>
|
|
65
|
+
) }
|
|
66
|
+
{ siteCredentialsNeeded && (
|
|
67
|
+
<Button
|
|
68
|
+
className={ styles.notice__action }
|
|
69
|
+
isExternalLink={ true }
|
|
70
|
+
weight="regular"
|
|
71
|
+
href={ credentialsRedirectUrl }
|
|
72
|
+
isLoading={ credentialsIsFetching }
|
|
73
|
+
>
|
|
74
|
+
{ __( 'Enter server credentials', 'jetpack' ) }
|
|
75
|
+
</Button>
|
|
76
|
+
) }
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default ThreatNotice;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Button } from '@automattic/jetpack-components';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
import { useContext } from 'react';
|
|
4
|
+
import Text from '../text';
|
|
5
|
+
import styles from './styles.module.scss';
|
|
6
|
+
import { ThreatModalContext } from '.';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ThreatSummary component
|
|
10
|
+
*
|
|
11
|
+
* @return {JSX.Element} The rendered threat summary.
|
|
12
|
+
*/
|
|
13
|
+
const ThreatSummary = (): JSX.Element => {
|
|
14
|
+
const { threat } = useContext( ThreatModalContext );
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className={ styles.section }>
|
|
18
|
+
{ !! threat.description && <Text>{ threat.description }</Text> }
|
|
19
|
+
{ !! threat.source && (
|
|
20
|
+
<div>
|
|
21
|
+
<Button variant="link" isExternalLink={ true } weight="regular" href={ threat.source }>
|
|
22
|
+
{ __( 'See more technical details of this threat', 'jetpack' ) }
|
|
23
|
+
</Button>
|
|
24
|
+
</div>
|
|
25
|
+
) }
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default ThreatSummary;
|
|
@@ -1,34 +1,65 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Text, Button } from '@automattic/jetpack-components';
|
|
2
2
|
import { __ } from '@wordpress/i18n';
|
|
3
|
+
import { chevronDown, chevronUp, Icon } from '@wordpress/icons';
|
|
4
|
+
import { useState, useCallback, useContext } from 'react';
|
|
3
5
|
import DiffViewer from '../diff-viewer';
|
|
4
6
|
import MarkedLines from '../marked-lines';
|
|
5
|
-
import Text from '../text';
|
|
6
7
|
import styles from './styles.module.scss';
|
|
8
|
+
import { ThreatModalContext } from '.';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* ThreatTechnicalDetails component
|
|
10
12
|
*
|
|
11
|
-
* @param {object} props - The component props.
|
|
12
|
-
* @param {object} props.threat - The threat object containing technical details.
|
|
13
|
-
*
|
|
14
13
|
* @return {JSX.Element | null} The rendered technical details or null if no details are available.
|
|
15
14
|
*/
|
|
16
|
-
const ThreatTechnicalDetails = (
|
|
15
|
+
const ThreatTechnicalDetails = (): JSX.Element => {
|
|
16
|
+
const { threat } = useContext( ThreatModalContext );
|
|
17
|
+
|
|
18
|
+
const [ open, setOpen ] = useState( false );
|
|
19
|
+
|
|
20
|
+
const toggleOpen = useCallback( () => {
|
|
21
|
+
setOpen( ! open );
|
|
22
|
+
}, [ open ] );
|
|
23
|
+
|
|
17
24
|
if ( ! threat.filename && ! threat.context && ! threat.diff ) {
|
|
18
25
|
return null;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
return (
|
|
22
29
|
<div className={ styles.section }>
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
<div className={ styles.section__title }>
|
|
31
|
+
<Button
|
|
32
|
+
variant="link"
|
|
33
|
+
className={ styles.section__toggle }
|
|
34
|
+
aria-expanded={ open }
|
|
35
|
+
aria-controls={ `threat-details-${ threat.id }` }
|
|
36
|
+
onClick={ toggleOpen }
|
|
37
|
+
>
|
|
38
|
+
<div className={ styles.section__toggle__content }>
|
|
39
|
+
<Text variant="title-small" mb={ 0 }>
|
|
40
|
+
{ open
|
|
41
|
+
? __( 'Hide the technical details', 'jetpack' )
|
|
42
|
+
: __( 'Show the technical details', 'jetpack' ) }
|
|
43
|
+
</Text>
|
|
44
|
+
<Icon icon={ open ? chevronUp : chevronDown } size={ 24 } />
|
|
45
|
+
</div>
|
|
46
|
+
</Button>
|
|
47
|
+
</div>
|
|
48
|
+
{ open && (
|
|
49
|
+
<div
|
|
50
|
+
className={ open ? styles.section__open : styles.section__closed }
|
|
51
|
+
id={ `threat-details-${ threat.id }` }
|
|
52
|
+
>
|
|
53
|
+
{ threat.filename && (
|
|
54
|
+
<>
|
|
55
|
+
<Text>{ __( 'Threat found in file:', 'jetpack' ) }</Text>
|
|
56
|
+
<pre className={ styles.filename }>{ threat.filename }</pre>
|
|
57
|
+
</>
|
|
58
|
+
) }
|
|
59
|
+
{ threat.context && <MarkedLines context={ threat.context } /> }
|
|
60
|
+
{ threat.diff && <DiffViewer diff={ threat.diff } /> }
|
|
61
|
+
</div>
|
|
29
62
|
) }
|
|
30
|
-
{ threat.context && <MarkedLines context={ threat.context } /> }
|
|
31
|
-
{ threat.diff && <DiffViewer diff={ threat.diff } /> }
|
|
32
63
|
</div>
|
|
33
64
|
);
|
|
34
65
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/jetpack-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.0",
|
|
4
4
|
"description": "Jetpack Components Package",
|
|
5
5
|
"author": "Automattic",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@automattic/format-currency": "1.0.1",
|
|
18
18
|
"@automattic/jetpack-boost-score-api": "^0.1.47",
|
|
19
|
-
"@automattic/jetpack-scan": "^0.
|
|
19
|
+
"@automattic/jetpack-scan": "^0.4.0",
|
|
20
20
|
"@babel/runtime": "^7",
|
|
21
21
|
"@wordpress/browserslist-config": "6.12.0",
|
|
22
22
|
"@wordpress/components": "28.12.0",
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { Text, Button } from '@automattic/jetpack-components';
|
|
2
|
-
import { Notice } from '@wordpress/components';
|
|
3
|
-
import { __ } from '@wordpress/i18n';
|
|
4
|
-
import React, { ReactElement } from 'react';
|
|
5
|
-
import styles from './styles.module.scss';
|
|
6
|
-
|
|
7
|
-
const CredentialsGate = ( {
|
|
8
|
-
siteCredentialsNeeded,
|
|
9
|
-
credentialsIsFetching,
|
|
10
|
-
credentialsRedirectUrl,
|
|
11
|
-
children,
|
|
12
|
-
}: {
|
|
13
|
-
siteCredentialsNeeded: boolean;
|
|
14
|
-
credentialsIsFetching: boolean;
|
|
15
|
-
credentialsRedirectUrl: string;
|
|
16
|
-
children: ReactElement;
|
|
17
|
-
} ): JSX.Element => {
|
|
18
|
-
if ( ! siteCredentialsNeeded ) {
|
|
19
|
-
return children;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<>
|
|
24
|
-
<Notice
|
|
25
|
-
status="warning"
|
|
26
|
-
isDismissible={ false }
|
|
27
|
-
children={
|
|
28
|
-
<Text>
|
|
29
|
-
{ __(
|
|
30
|
-
'Before Jetpack can auto-fix threats on your site, it needs your server credentials.',
|
|
31
|
-
'jetpack'
|
|
32
|
-
) }
|
|
33
|
-
</Text>
|
|
34
|
-
}
|
|
35
|
-
/>
|
|
36
|
-
|
|
37
|
-
<Text>
|
|
38
|
-
{ __(
|
|
39
|
-
'Your server credentials allow Jetpack to access the server that’s powering your website. This information is securely saved and only used to perform fix threats detected on your site.',
|
|
40
|
-
'jetpack'
|
|
41
|
-
) }
|
|
42
|
-
</Text>
|
|
43
|
-
|
|
44
|
-
<Text>
|
|
45
|
-
{ __(
|
|
46
|
-
'Once you’ve entered server credentials, Jetpack will be fixing the selected threats.',
|
|
47
|
-
'jetpack'
|
|
48
|
-
) }
|
|
49
|
-
</Text>
|
|
50
|
-
|
|
51
|
-
<div className={ styles[ 'modal-actions' ] }>
|
|
52
|
-
<Button
|
|
53
|
-
isExternalLink={ true }
|
|
54
|
-
weight="regular"
|
|
55
|
-
href={ credentialsRedirectUrl }
|
|
56
|
-
isLoading={ credentialsIsFetching }
|
|
57
|
-
>
|
|
58
|
-
{ __( 'Enter server credentials', 'jetpack' ) }
|
|
59
|
-
</Button>
|
|
60
|
-
</div>
|
|
61
|
-
</>
|
|
62
|
-
);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export default CredentialsGate;
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { Text, Button } from '@automattic/jetpack-components';
|
|
2
|
-
import { Notice } from '@wordpress/components';
|
|
3
|
-
import { __ } from '@wordpress/i18n';
|
|
4
|
-
import React, { ReactElement } from 'react';
|
|
5
|
-
import styles from './styles.module.scss';
|
|
6
|
-
|
|
7
|
-
const UserConnectionGate = ( {
|
|
8
|
-
userConnectionNeeded,
|
|
9
|
-
userIsConnecting,
|
|
10
|
-
handleConnectUser,
|
|
11
|
-
children,
|
|
12
|
-
}: {
|
|
13
|
-
userConnectionNeeded: boolean;
|
|
14
|
-
userIsConnecting: boolean;
|
|
15
|
-
handleConnectUser: () => void;
|
|
16
|
-
children: ReactElement;
|
|
17
|
-
} ): JSX.Element => {
|
|
18
|
-
if ( ! userConnectionNeeded ) {
|
|
19
|
-
return children;
|
|
20
|
-
}
|
|
21
|
-
return (
|
|
22
|
-
<>
|
|
23
|
-
<Notice
|
|
24
|
-
status="warning"
|
|
25
|
-
isDismissible={ false }
|
|
26
|
-
children={
|
|
27
|
-
<Text>
|
|
28
|
-
{ __(
|
|
29
|
-
'Before Jetpack can ignore and auto-fix threats on your site, a user connection is needed.',
|
|
30
|
-
'jetpack'
|
|
31
|
-
) }
|
|
32
|
-
</Text>
|
|
33
|
-
}
|
|
34
|
-
/>
|
|
35
|
-
|
|
36
|
-
<Text>
|
|
37
|
-
{ __(
|
|
38
|
-
'A user connection provides Jetpack the access necessary to perform these tasks.',
|
|
39
|
-
'jetpack'
|
|
40
|
-
) }
|
|
41
|
-
</Text>
|
|
42
|
-
|
|
43
|
-
<Text>
|
|
44
|
-
{ __(
|
|
45
|
-
'Once you’ve secured a user connection, all Jetpack features will be available for use.',
|
|
46
|
-
'jetpack'
|
|
47
|
-
) }
|
|
48
|
-
</Text>
|
|
49
|
-
|
|
50
|
-
<div className={ styles[ 'modal-actions' ] }>
|
|
51
|
-
<Button
|
|
52
|
-
isExternalLink={ true }
|
|
53
|
-
weight="regular"
|
|
54
|
-
isLoading={ userIsConnecting }
|
|
55
|
-
onClick={ handleConnectUser }
|
|
56
|
-
>
|
|
57
|
-
{ __( 'Connect your user account', 'jetpack' ) }
|
|
58
|
-
</Button>
|
|
59
|
-
</div>
|
|
60
|
-
</>
|
|
61
|
-
);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export default UserConnectionGate;
|