@automattic/jetpack-components 0.60.0 → 0.62.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 +19 -12
- package/components/diff-viewer/styles.module.scss +1 -1
- package/components/gridicon/index.tsx +9 -0
- package/components/icons/index.tsx +13 -0
- package/components/marked-lines/styles.module.scss +1 -1
- package/components/threat-fixer-button/index.tsx +7 -67
- package/components/threat-modal/credentials-gate.tsx +65 -0
- package/components/threat-modal/index.tsx +153 -0
- package/components/threat-modal/styles.module.scss +35 -0
- package/components/threat-modal/threat-actions.tsx +97 -0
- package/components/threat-modal/threat-fix-details.tsx +68 -0
- package/components/threat-modal/threat-technical-details.tsx +36 -0
- package/components/threat-modal/user-connection-gate.tsx +64 -0
- package/package.json +15 -15
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
### This is a list detailing changes for the Jetpack RNA Components package releases.
|
|
4
4
|
|
|
5
|
+
## [0.62.0] - 2024-11-25
|
|
6
|
+
### Added
|
|
7
|
+
- Add Stats icon [#40236]
|
|
8
|
+
- Adds ThreatModal component and stories [#40197]
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Updated package dependencies. [#40288]
|
|
12
|
+
|
|
13
|
+
## [0.61.0] - 2024-11-18
|
|
14
|
+
### Added
|
|
15
|
+
- Gridicon Component: Add support for help-outline icon. [#39867]
|
|
16
|
+
|
|
5
17
|
## [0.60.0] - 2024-11-14
|
|
6
18
|
### Added
|
|
7
19
|
- Adds tooltips for each ThreatFixerButton state [#40111]
|
|
@@ -17,9 +29,7 @@
|
|
|
17
29
|
|
|
18
30
|
### Changed
|
|
19
31
|
- Add ToggleGroupControl to ThreatsDataViews for easily toggling between Active and Historical threats [#39901]
|
|
20
|
-
- Updated package dependencies. [#39999]
|
|
21
|
-
- Updated package dependencies. [#40000]
|
|
22
|
-
- Updated package dependencies. [#40060]
|
|
32
|
+
- Updated package dependencies. [#39999] [#40000] [#40060]
|
|
23
33
|
|
|
24
34
|
## [0.58.1] - 2024-11-04
|
|
25
35
|
### Added
|
|
@@ -189,9 +199,7 @@
|
|
|
189
199
|
- Social | Wired up confirmation UI with connect button [#37295]
|
|
190
200
|
|
|
191
201
|
### Changed
|
|
192
|
-
- Updated package dependencies. [#37379]
|
|
193
|
-
- Updated package dependencies. [#37380]
|
|
194
|
-
- Updated package dependencies. [#37382]
|
|
202
|
+
- Updated package dependencies. [#37379] [#37380] [#37382]
|
|
195
203
|
|
|
196
204
|
## [0.53.2] - 2024-05-13
|
|
197
205
|
### Added
|
|
@@ -416,8 +424,7 @@
|
|
|
416
424
|
|
|
417
425
|
## [0.42.1] - 2023-09-04
|
|
418
426
|
### Changed
|
|
419
|
-
- Updated package dependencies. [#32803]
|
|
420
|
-
- Updated package dependencies. [#32804]
|
|
427
|
+
- Updated package dependencies. [#32803] [#32804]
|
|
421
428
|
|
|
422
429
|
## [0.42.0] - 2023-09-01
|
|
423
430
|
### Added
|
|
@@ -467,8 +474,7 @@
|
|
|
467
474
|
## [0.40.1] - 2023-07-05
|
|
468
475
|
### Changed
|
|
469
476
|
- Adjust component pricing slider border and box-shadow styling. [#31593]
|
|
470
|
-
- Updated package dependencies. [#31659]
|
|
471
|
-
- Updated package dependencies. [#31661]
|
|
477
|
+
- Updated package dependencies. [#31659] [#31661]
|
|
472
478
|
- Update storybook mdx to use `@storybook/blocks` directly rather than `@storybook/addon-docs`. [#31607]
|
|
473
479
|
|
|
474
480
|
### Fixed
|
|
@@ -746,8 +752,7 @@
|
|
|
746
752
|
- Fix ProductPrice layout for long prices [#26595]
|
|
747
753
|
- IconTooltip: Use click instead of mouseover for summoning [#26457]
|
|
748
754
|
- Refactor props for structure consistency with JetpackLogo component. [#26510]
|
|
749
|
-
- Updated package dependencies. [#26568]
|
|
750
|
-
- Updated package dependencies. [#26583]
|
|
755
|
+
- Updated package dependencies. [#26568] [#26583]
|
|
751
756
|
|
|
752
757
|
### Fixed
|
|
753
758
|
- Components: fix the positio of TOS component of the PricingTable cmp [#26509]
|
|
@@ -1221,6 +1226,8 @@
|
|
|
1221
1226
|
### Changed
|
|
1222
1227
|
- Update node version requirement to 14.16.1
|
|
1223
1228
|
|
|
1229
|
+
[0.62.0]: https://github.com/Automattic/jetpack-components/compare/0.61.0...0.62.0
|
|
1230
|
+
[0.61.0]: https://github.com/Automattic/jetpack-components/compare/0.60.0...0.61.0
|
|
1224
1231
|
[0.60.0]: https://github.com/Automattic/jetpack-components/compare/0.59.0...0.60.0
|
|
1225
1232
|
[0.59.0]: https://github.com/Automattic/jetpack-components/compare/0.58.1...0.59.0
|
|
1226
1233
|
[0.58.1]: https://github.com/Automattic/jetpack-components/compare/0.58.0...0.58.1
|
|
@@ -24,6 +24,7 @@ class Gridicon extends Component< GridiconProps > {
|
|
|
24
24
|
'gridicons-calendar',
|
|
25
25
|
'gridicons-cart',
|
|
26
26
|
'gridicons-folder',
|
|
27
|
+
'gridicons-help-outline',
|
|
27
28
|
'gridicons-info',
|
|
28
29
|
'gridicons-info-outline',
|
|
29
30
|
'gridicons-posts',
|
|
@@ -66,6 +67,8 @@ class Gridicon extends Component< GridiconProps > {
|
|
|
66
67
|
return __( 'Toggle search filters.', 'jetpack' );
|
|
67
68
|
case 'gridicons-folder':
|
|
68
69
|
return __( 'Category', 'jetpack' );
|
|
70
|
+
case 'gridicons-help-outline':
|
|
71
|
+
return __( 'Help', 'jetpack' );
|
|
69
72
|
case 'gridicons-info':
|
|
70
73
|
case 'gridicons-info-outline':
|
|
71
74
|
return __( 'Information.', 'jetpack' );
|
|
@@ -181,6 +184,12 @@ class Gridicon extends Component< GridiconProps > {
|
|
|
181
184
|
<path d="M18 19H6c-1.1 0-2-.9-2-2V7c0-1.1.9-2 2-2h3c1.1 0 2 .9 2 2h7c1.1 0 2 .9 2 2v8c0 1.1-.9 2-2 2z" />
|
|
182
185
|
</g>
|
|
183
186
|
);
|
|
187
|
+
case 'gridicons-help-outline':
|
|
188
|
+
return (
|
|
189
|
+
<g>
|
|
190
|
+
<path d="M12 4c4.41 0 8 3.59 8 8s-3.59 8-8 8-8-3.59-8-8 3.59-8 8-8m0-2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 13h-2v2h2v-2zm-1.962-2v-.528c0-.4.082-.74.246-1.017.163-.276.454-.546.87-.808.333-.21.572-.397.717-.565.146-.168.22-.36.22-.577 0-.172-.078-.308-.234-.41-.156-.1-.358-.15-.608-.15-.62 0-1.34.22-2.168.658l-.854-1.67c1.02-.58 2.084-.872 3.194-.872.913 0 1.63.202 2.15.603.52.4.78.948.78 1.64 0 .495-.116.924-.347 1.287-.23.362-.6.705-1.11 1.03-.43.278-.7.48-.807.61-.108.13-.163.282-.163.458V13h-1.885z" />
|
|
191
|
+
</g>
|
|
192
|
+
);
|
|
184
193
|
case 'gridicons-image':
|
|
185
194
|
return (
|
|
186
195
|
<g>
|
|
@@ -234,6 +234,18 @@ export const AiIcon: React.FC< BaseIconProps > = ( {
|
|
|
234
234
|
);
|
|
235
235
|
};
|
|
236
236
|
|
|
237
|
+
export const StatsIcon: React.FC< BaseIconProps > = ( { opacity = 1, size, color } ) => {
|
|
238
|
+
return (
|
|
239
|
+
<IconWrapper size={ size } opacity={ opacity } color={ color }>
|
|
240
|
+
<Path
|
|
241
|
+
fillRule="evenodd"
|
|
242
|
+
clipRule="evenodd"
|
|
243
|
+
d="M11.25 5H12.75V20H11.25V5ZM6 10H7.5V20H6V10ZM18 14H16.5V20H18V14Z"
|
|
244
|
+
/>
|
|
245
|
+
</IconWrapper>
|
|
246
|
+
);
|
|
247
|
+
};
|
|
248
|
+
|
|
237
249
|
const jetpackIcons = {
|
|
238
250
|
'anti-spam': AntiSpamIcon,
|
|
239
251
|
backup: BackupIcon,
|
|
@@ -249,6 +261,7 @@ const jetpackIcons = {
|
|
|
249
261
|
jetpack: JetpackIcon,
|
|
250
262
|
share: ShareIcon,
|
|
251
263
|
ai: AiIcon,
|
|
264
|
+
stats: StatsIcon,
|
|
252
265
|
};
|
|
253
266
|
|
|
254
267
|
const iconsMap = {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Button } from '@automattic/jetpack-components';
|
|
2
2
|
import {
|
|
3
3
|
type Threat,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
getFixerState,
|
|
5
|
+
getFixerAction,
|
|
6
|
+
getFixerMessage,
|
|
7
7
|
} from '@automattic/jetpack-scan';
|
|
8
8
|
import { Tooltip } from '@wordpress/components';
|
|
9
9
|
import { useCallback, useMemo } from '@wordpress/element';
|
|
@@ -30,10 +30,7 @@ export default function ThreatFixerButton( {
|
|
|
30
30
|
className?: string;
|
|
31
31
|
} ): JSX.Element {
|
|
32
32
|
const fixerState = useMemo( () => {
|
|
33
|
-
|
|
34
|
-
const error = threat.fixer && fixerIsInError( threat.fixer );
|
|
35
|
-
const stale = threat.fixer && fixerStatusIsStale( threat.fixer );
|
|
36
|
-
return { inProgress, error, stale };
|
|
33
|
+
return getFixerState( threat.fixer );
|
|
37
34
|
}, [ threat.fixer ] );
|
|
38
35
|
|
|
39
36
|
const tooltipText = useMemo( () => {
|
|
@@ -53,54 +50,7 @@ export default function ThreatFixerButton( {
|
|
|
53
50
|
return __( 'An auto-fixer is in progress.', 'jetpack' );
|
|
54
51
|
}
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
case 'delete':
|
|
58
|
-
if ( threat.filename ) {
|
|
59
|
-
if ( threat.filename.endsWith( '/' ) ) {
|
|
60
|
-
return __( 'Deletes the directory that the infected file is in.', 'jetpack' );
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if ( threat.signature === 'Core.File.Modification' ) {
|
|
64
|
-
return __( 'Deletes the unexpected file in a core WordPress directory.', 'jetpack' );
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return __( 'Deletes the infected file.', 'jetpack' );
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if ( threat.extension?.type === 'plugin' ) {
|
|
71
|
-
return __( 'Deletes the plugin directory to fix the threat.', 'jetpack' );
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if ( threat.extension?.type === 'theme' ) {
|
|
75
|
-
return __( 'Deletes the theme directory to fix the threat.', 'jetpack' );
|
|
76
|
-
}
|
|
77
|
-
break;
|
|
78
|
-
case 'update':
|
|
79
|
-
return __( 'Upgrades the plugin or theme to a newer version.', 'jetpack' );
|
|
80
|
-
case 'replace':
|
|
81
|
-
case 'rollback':
|
|
82
|
-
if ( threat.filename ) {
|
|
83
|
-
return threat.signature === 'Core.File.Modification'
|
|
84
|
-
? __(
|
|
85
|
-
'Replaces the modified core WordPress file with the original clean version from the WordPress source code.',
|
|
86
|
-
'jetpack'
|
|
87
|
-
)
|
|
88
|
-
: __(
|
|
89
|
-
'Replaces the infected file with a previously backed up version that is clean.',
|
|
90
|
-
'jetpack'
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if ( threat.signature === 'php_hardening_WP_Config_NoSalts_001' ) {
|
|
95
|
-
return __(
|
|
96
|
-
'Replaces the default salt keys in wp-config.php with unique ones.',
|
|
97
|
-
'jetpack'
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
break;
|
|
101
|
-
default:
|
|
102
|
-
return __( 'An auto-fixer is available.', 'jetpack' );
|
|
103
|
-
}
|
|
53
|
+
return getFixerMessage( threat );
|
|
104
54
|
}, [ threat, fixerState ] );
|
|
105
55
|
|
|
106
56
|
const buttonText = useMemo( () => {
|
|
@@ -112,18 +62,8 @@ export default function ThreatFixerButton( {
|
|
|
112
62
|
return __( 'Error', 'jetpack' );
|
|
113
63
|
}
|
|
114
64
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return __( 'Delete', 'jetpack' );
|
|
118
|
-
case 'update':
|
|
119
|
-
return __( 'Update', 'jetpack' );
|
|
120
|
-
case 'replace':
|
|
121
|
-
case 'rollback':
|
|
122
|
-
return __( 'Replace', 'jetpack' );
|
|
123
|
-
default:
|
|
124
|
-
return __( 'Fix', 'jetpack' );
|
|
125
|
-
}
|
|
126
|
-
}, [ threat.fixable, fixerState.error ] );
|
|
65
|
+
return getFixerAction( threat );
|
|
66
|
+
}, [ threat, fixerState.error ] );
|
|
127
67
|
|
|
128
68
|
const handleClick = useCallback(
|
|
129
69
|
( event: React.MouseEvent ) => {
|
|
@@ -0,0 +1,65 @@
|
|
|
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;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Button, ThreatSeverityBadge } from '@automattic/jetpack-components';
|
|
2
|
+
import { type Threat, getFixerState } from '@automattic/jetpack-scan';
|
|
3
|
+
import { Modal, Notice } from '@wordpress/components';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
import Text from '../text';
|
|
7
|
+
import CredentialsGate from './credentials-gate';
|
|
8
|
+
import styles from './styles.module.scss';
|
|
9
|
+
import ThreatActions from './threat-actions';
|
|
10
|
+
import ThreatFixDetails from './threat-fix-details';
|
|
11
|
+
import ThreatTechnicalDetails from './threat-technical-details';
|
|
12
|
+
import UserConnectionGate from './user-connection-gate';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* ThreatModal component
|
|
16
|
+
*
|
|
17
|
+
* @param {object} props - The props.
|
|
18
|
+
* @param {object} props.threat - The threat.
|
|
19
|
+
* @param {boolean} props.isUserConnected - Whether the user is connected.
|
|
20
|
+
* @param {boolean} props.hasConnectedOwner - Whether the user has a connected owner.
|
|
21
|
+
* @param {boolean} props.userIsConnecting - Whether the user is connecting.
|
|
22
|
+
* @param {Function} props.handleConnectUser - The handleConnectUser function.
|
|
23
|
+
* @param {object} props.credentials - The credentials.
|
|
24
|
+
* @param {boolean} props.credentialsIsFetching - Whether the credentials are fetching.
|
|
25
|
+
* @param {string} props.credentialsRedirectUrl - The credentials redirect URL.
|
|
26
|
+
* @param {Function} props.handleUpgradeClick - The handleUpgradeClick function.
|
|
27
|
+
* @param {Function} props.handleFixThreatClick - The handleFixThreatClick function.
|
|
28
|
+
* @param {Function} props.handleIgnoreThreatClick - The handleIgnoreThreatClick function.
|
|
29
|
+
* @param {Function} props.handleUnignoreThreatClick - The handleUnignoreThreatClick function.
|
|
30
|
+
*
|
|
31
|
+
* @return {JSX.Element} The threat modal.
|
|
32
|
+
*/
|
|
33
|
+
export default function ThreatModal( {
|
|
34
|
+
threat,
|
|
35
|
+
isUserConnected,
|
|
36
|
+
hasConnectedOwner,
|
|
37
|
+
userIsConnecting,
|
|
38
|
+
handleConnectUser,
|
|
39
|
+
credentials,
|
|
40
|
+
credentialsIsFetching,
|
|
41
|
+
credentialsRedirectUrl,
|
|
42
|
+
handleUpgradeClick,
|
|
43
|
+
handleFixThreatClick,
|
|
44
|
+
handleIgnoreThreatClick,
|
|
45
|
+
handleUnignoreThreatClick,
|
|
46
|
+
...modalProps
|
|
47
|
+
}: {
|
|
48
|
+
threat: Threat;
|
|
49
|
+
isUserConnected: boolean;
|
|
50
|
+
hasConnectedOwner: boolean;
|
|
51
|
+
userIsConnecting: boolean;
|
|
52
|
+
handleConnectUser: () => void;
|
|
53
|
+
credentials: false | Record< string, unknown >[];
|
|
54
|
+
credentialsIsFetching: boolean;
|
|
55
|
+
credentialsRedirectUrl: string;
|
|
56
|
+
handleUpgradeClick?: () => void;
|
|
57
|
+
handleFixThreatClick?: ( threats: Threat[] ) => void;
|
|
58
|
+
handleIgnoreThreatClick?: ( threats: Threat[] ) => void;
|
|
59
|
+
handleUnignoreThreatClick?: ( threats: Threat[] ) => void;
|
|
60
|
+
} & React.ComponentProps< typeof Modal > ): JSX.Element {
|
|
61
|
+
const userConnectionNeeded = ! isUserConnected || ! hasConnectedOwner;
|
|
62
|
+
const siteCredentialsNeeded = ! credentials || credentials.length === 0;
|
|
63
|
+
|
|
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
|
+
return (
|
|
86
|
+
<Modal
|
|
87
|
+
size="large"
|
|
88
|
+
title={ <div className={ styles.title }>{ getModalTitle }</div> }
|
|
89
|
+
{ ...modalProps }
|
|
90
|
+
>
|
|
91
|
+
<div className={ styles[ 'threat-details' ] }>
|
|
92
|
+
<UserConnectionGate
|
|
93
|
+
userConnectionNeeded={ userConnectionNeeded }
|
|
94
|
+
userIsConnecting={ userIsConnecting }
|
|
95
|
+
handleConnectUser={ handleConnectUser }
|
|
96
|
+
>
|
|
97
|
+
<CredentialsGate
|
|
98
|
+
siteCredentialsNeeded={ siteCredentialsNeeded }
|
|
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>
|
|
150
|
+
</div>
|
|
151
|
+
</Modal>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.threat-details {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: calc( var( --spacing-base ) * 3 ); // 24px
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.section {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
gap: calc( var( --spacing-base ) * 2 ); // 16px
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.title {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: calc( var( --spacing-base ) * 1.5 ); // 12px
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.filename {
|
|
20
|
+
background-color: var( --jp-gray-0 );
|
|
21
|
+
padding: calc( var( --spacing-base ) * 3 ); // 24px
|
|
22
|
+
overflow-x: auto;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.modal-actions {
|
|
26
|
+
display: flex;
|
|
27
|
+
justify-content: flex-end;
|
|
28
|
+
padding-top: calc( var( --spacing-base ) * 3 ); // 24px
|
|
29
|
+
border-top: 1px solid var( --jp-gray-0 );
|
|
30
|
+
|
|
31
|
+
.threat-actions {
|
|
32
|
+
display: flex;
|
|
33
|
+
gap: calc( var( --spacing-base ) * 2 ); // 16px;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Button } from '@automattic/jetpack-components';
|
|
2
|
+
import { Threat, getFixerAction } from '@automattic/jetpack-scan';
|
|
3
|
+
import { __ } from '@wordpress/i18n';
|
|
4
|
+
import React, { useCallback, useMemo } from 'react';
|
|
5
|
+
import styles from './styles.module.scss';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ThreatActions component
|
|
9
|
+
*
|
|
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
|
+
* @return {JSX.Element | null} The rendered action buttons or null if no actions are available.
|
|
22
|
+
*/
|
|
23
|
+
const ThreatActions = ( {
|
|
24
|
+
threat,
|
|
25
|
+
closeModal,
|
|
26
|
+
handleFixThreatClick,
|
|
27
|
+
handleIgnoreThreatClick,
|
|
28
|
+
handleUnignoreThreatClick,
|
|
29
|
+
fixerState,
|
|
30
|
+
}: {
|
|
31
|
+
threat: Threat;
|
|
32
|
+
closeModal: () => void;
|
|
33
|
+
handleFixThreatClick?: ( threats: Threat[] ) => void;
|
|
34
|
+
handleIgnoreThreatClick?: ( threats: Threat[] ) => void;
|
|
35
|
+
handleUnignoreThreatClick?: ( threats: Threat[] ) => void;
|
|
36
|
+
fixerState: { inProgress: boolean; error: boolean; stale: boolean };
|
|
37
|
+
} ): JSX.Element => {
|
|
38
|
+
const fixerAction = useMemo( () => getFixerAction( threat ), [ threat ] );
|
|
39
|
+
|
|
40
|
+
const onFixClick = useCallback( () => {
|
|
41
|
+
handleFixThreatClick?.( [ threat ] );
|
|
42
|
+
closeModal();
|
|
43
|
+
}, [ threat, handleFixThreatClick, closeModal ] );
|
|
44
|
+
|
|
45
|
+
const onIgnoreClick = useCallback( () => {
|
|
46
|
+
handleIgnoreThreatClick?.( [ threat ] );
|
|
47
|
+
closeModal();
|
|
48
|
+
}, [ threat, handleIgnoreThreatClick, closeModal ] );
|
|
49
|
+
|
|
50
|
+
const onUnignoreClick = useCallback( () => {
|
|
51
|
+
handleUnignoreThreatClick?.( [ threat ] );
|
|
52
|
+
closeModal();
|
|
53
|
+
}, [ threat, handleUnignoreThreatClick, closeModal ] );
|
|
54
|
+
|
|
55
|
+
if ( ! handleFixThreatClick && ! handleIgnoreThreatClick && ! handleUnignoreThreatClick ) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className={ styles[ 'modal-actions' ] }>
|
|
61
|
+
<div className={ styles[ 'threat-actions' ] }>
|
|
62
|
+
{ threat.status === 'ignored' && handleUnignoreThreatClick && (
|
|
63
|
+
<Button isDestructive={ true } variant="secondary" onClick={ onUnignoreClick }>
|
|
64
|
+
{ __( 'Un-ignore', 'jetpack' ) }
|
|
65
|
+
</Button>
|
|
66
|
+
) }
|
|
67
|
+
{ threat.status === 'current' && (
|
|
68
|
+
<>
|
|
69
|
+
{ handleIgnoreThreatClick && (
|
|
70
|
+
<Button
|
|
71
|
+
isDestructive={ true }
|
|
72
|
+
variant="secondary"
|
|
73
|
+
onClick={ onIgnoreClick }
|
|
74
|
+
disabled={ fixerState.inProgress && ! fixerState.stale }
|
|
75
|
+
>
|
|
76
|
+
{ __( 'Ignore', 'jetpack' ) }
|
|
77
|
+
</Button>
|
|
78
|
+
) }
|
|
79
|
+
{ threat.fixable && handleFixThreatClick && (
|
|
80
|
+
<Button
|
|
81
|
+
isPrimary
|
|
82
|
+
disabled={ fixerState.inProgress && ! fixerState.stale }
|
|
83
|
+
onClick={ onFixClick }
|
|
84
|
+
>
|
|
85
|
+
{ fixerState.error || fixerState.stale
|
|
86
|
+
? __( 'Retry fix', 'jetpack' )
|
|
87
|
+
: fixerAction }
|
|
88
|
+
</Button>
|
|
89
|
+
) }
|
|
90
|
+
</>
|
|
91
|
+
) }
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default ThreatActions;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Threat, getFixerMessage } from '@automattic/jetpack-scan';
|
|
2
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
3
|
+
import React, { useMemo } from 'react';
|
|
4
|
+
import ContextualUpgradeTrigger from '../contextual-upgrade-trigger';
|
|
5
|
+
import Text from '../text';
|
|
6
|
+
import styles from './styles.module.scss';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ThreatFixDetails component
|
|
10
|
+
*
|
|
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
|
+
* @return {JSX.Element | null} The rendered fix details or null if no fixable details are available.
|
|
16
|
+
*/
|
|
17
|
+
const ThreatFixDetails = ( {
|
|
18
|
+
threat,
|
|
19
|
+
handleUpgradeClick,
|
|
20
|
+
}: {
|
|
21
|
+
threat: Threat;
|
|
22
|
+
handleUpgradeClick: () => void;
|
|
23
|
+
} ): JSX.Element => {
|
|
24
|
+
const title = useMemo( () => {
|
|
25
|
+
if ( threat.status === 'fixed' ) {
|
|
26
|
+
return __( 'How did Jetpack fix it?', 'jetpack' );
|
|
27
|
+
}
|
|
28
|
+
if ( threat.status === 'current' && threat.fixable ) {
|
|
29
|
+
return __( 'How can Jetpack auto-fix this threat?', 'jetpack' );
|
|
30
|
+
}
|
|
31
|
+
return __( 'How to fix it?', 'jetpack' );
|
|
32
|
+
}, [ threat ] );
|
|
33
|
+
|
|
34
|
+
const fix = useMemo( () => {
|
|
35
|
+
// The threat has a fixed version available, but no auto-fix is available.
|
|
36
|
+
// The user needs to update the extension to the fixed version.
|
|
37
|
+
if ( ! threat.fixable && threat.fixedIn ) {
|
|
38
|
+
return sprintf(
|
|
39
|
+
/* translators: Translates to Updates to version. %1$s: Name. %2$s: Fixed version */
|
|
40
|
+
__( 'Update %1$s to version %2$s.', 'jetpack' ),
|
|
41
|
+
threat.extension.name,
|
|
42
|
+
threat.fixedIn
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
// The threat has an auto-fix available.
|
|
46
|
+
return getFixerMessage( threat );
|
|
47
|
+
}, [ threat ] );
|
|
48
|
+
|
|
49
|
+
if ( ! threat.fixable && ! threat.fixedIn ) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className={ styles.section }>
|
|
55
|
+
<Text variant="title-small">{ title }</Text>
|
|
56
|
+
<Text>{ fix }</Text>
|
|
57
|
+
{ handleUpgradeClick && (
|
|
58
|
+
<ContextualUpgradeTrigger
|
|
59
|
+
description={ __( 'Looking for advanced scan results and one-click fixes?', 'jetpack' ) }
|
|
60
|
+
cta={ __( 'Upgrade Jetpack now', 'jetpack' ) }
|
|
61
|
+
onClick={ handleUpgradeClick }
|
|
62
|
+
/>
|
|
63
|
+
) }
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default ThreatFixDetails;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Threat } from '@automattic/jetpack-scan';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
import DiffViewer from '../diff-viewer';
|
|
4
|
+
import MarkedLines from '../marked-lines';
|
|
5
|
+
import Text from '../text';
|
|
6
|
+
import styles from './styles.module.scss';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ThreatTechnicalDetails component
|
|
10
|
+
*
|
|
11
|
+
* @param {object} props - The component props.
|
|
12
|
+
* @param {object} props.threat - The threat object containing technical details.
|
|
13
|
+
*
|
|
14
|
+
* @return {JSX.Element | null} The rendered technical details or null if no details are available.
|
|
15
|
+
*/
|
|
16
|
+
const ThreatTechnicalDetails = ( { threat }: { threat: Threat } ): JSX.Element => {
|
|
17
|
+
if ( ! threat.filename && ! threat.context && ! threat.diff ) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className={ styles.section }>
|
|
23
|
+
<Text variant="title-small">{ __( 'The technical details', 'jetpack' ) }</Text>
|
|
24
|
+
{ threat.filename && (
|
|
25
|
+
<>
|
|
26
|
+
<Text>{ __( 'Threat found in file:', 'jetpack' ) }</Text>
|
|
27
|
+
<pre className={ styles.filename }>{ threat.filename }</pre>
|
|
28
|
+
</>
|
|
29
|
+
) }
|
|
30
|
+
{ threat.context && <MarkedLines context={ threat.context } /> }
|
|
31
|
+
{ threat.diff && <DiffViewer diff={ threat.diff } /> }
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default ThreatTechnicalDetails;
|
|
@@ -0,0 +1,64 @@
|
|
|
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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/jetpack-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.62.0",
|
|
4
4
|
"description": "Jetpack Components Package",
|
|
5
5
|
"author": "Automattic",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -15,29 +15,29 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@automattic/format-currency": "1.0.1",
|
|
18
|
-
"@automattic/jetpack-boost-score-api": "^0.1.
|
|
19
|
-
"@automattic/jetpack-scan": "^0.
|
|
18
|
+
"@automattic/jetpack-boost-score-api": "^0.1.47",
|
|
19
|
+
"@automattic/jetpack-scan": "^0.3.0",
|
|
20
20
|
"@babel/runtime": "^7",
|
|
21
|
-
"@wordpress/browserslist-config": "6.
|
|
22
|
-
"@wordpress/components": "28.
|
|
23
|
-
"@wordpress/compose": "7.
|
|
24
|
-
"@wordpress/data": "10.
|
|
25
|
-
"@wordpress/dataviews": "4.
|
|
26
|
-
"@wordpress/date": "5.
|
|
27
|
-
"@wordpress/element": "6.
|
|
28
|
-
"@wordpress/i18n": "5.
|
|
29
|
-
"@wordpress/icons": "10.
|
|
30
|
-
"@wordpress/notices": "5.
|
|
21
|
+
"@wordpress/browserslist-config": "6.12.0",
|
|
22
|
+
"@wordpress/components": "28.12.0",
|
|
23
|
+
"@wordpress/compose": "7.12.0",
|
|
24
|
+
"@wordpress/data": "10.12.0",
|
|
25
|
+
"@wordpress/dataviews": "4.8.0",
|
|
26
|
+
"@wordpress/date": "5.12.0",
|
|
27
|
+
"@wordpress/element": "6.12.0",
|
|
28
|
+
"@wordpress/i18n": "5.12.0",
|
|
29
|
+
"@wordpress/icons": "10.12.0",
|
|
30
|
+
"@wordpress/notices": "5.12.0",
|
|
31
31
|
"clsx": "2.1.1",
|
|
32
32
|
"prop-types": "^15.7.2",
|
|
33
33
|
"qrcode.react": "3.1.0",
|
|
34
34
|
"react-slider": "2.0.5",
|
|
35
|
-
"social-logos": "^3.1.
|
|
35
|
+
"social-logos": "^3.1.13",
|
|
36
36
|
"uplot": "1.6.31",
|
|
37
37
|
"uplot-react": "1.1.4"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@automattic/jetpack-base-styles": "^0.6.
|
|
40
|
+
"@automattic/jetpack-base-styles": "^0.6.37",
|
|
41
41
|
"@babel/core": "7.26.0",
|
|
42
42
|
"@babel/preset-react": "7.25.9",
|
|
43
43
|
"@jest/globals": "29.4.3",
|