- { __( 'What is the problem?', 'jetpack-protect' ) }
+ { status === 'fixed' || status === 'ignored'
+ ? __( 'What was the problem?', 'jetpack-protect' )
+ : __( 'What is the problem?', 'jetpack-protect' ) }
{ description }
{ learnMoreButton }
@@ -121,12 +129,12 @@ const ThreatAccordionItem = ( {
) }
{ ! description &&
{ learnMoreButton }
}
- { ! viewingScanHistory && (
+ { status !== 'fixed' && status !== 'ignored' && (
) }
- { fixable && (
+ { fixable && status !== 'fixed' && status !== 'ignored' && (
@@ -137,25 +145,6 @@ const ThreatAccordionItem = ( {
};
const PaidList = ( { list } ) => {
- const { scan } = useDispatch( STORE_ID );
-
- const handleScanClick = () => {
- return event => {
- event.preventDefault();
- scan();
- };
- };
-
- const manualScan = createInterpolateElement(
- __(
- 'If you have manually fixed any of the threats listed above, you can run a manual scan now or wait for Jetpack to scan your site later today.',
- 'jetpack-protect'
- ),
- {
- manualScanLink: ,
- }
- );
-
const [ isSmall ] = useBreakpointMatch( [ 'sm', 'lg' ], [ null, '<' ] );
const getLabel = threat => {
@@ -164,6 +153,11 @@ const PaidList = ( { list } ) => {
return `${ threat.name } (${ threat.version })`;
}
+ if ( threat.extension && threat.extension.name && threat.extension.version ) {
+ // Extension threat i.e. "Woocommerce (3.0.0)"
+ return `${ threat.extension.name } (${ threat.extension.version })`;
+ }
+
if ( threat.filename ) {
// File threat i.e. "index.php"
return threat.filename.split( '/' ).pop();
@@ -175,7 +169,27 @@ const PaidList = ( { list } ) => {
}
};
- list = list.map( threat => ( { label: getLabel( threat ), ...threat } ) );
+ const getIcon = threat => {
+ if ( threat.extension && threat.extension.type === 'plugin' ) {
+ return pluginsIcon;
+ }
+ if ( threat.extension && threat.extension.type === 'theme' ) {
+ return themesIcon;
+ }
+ if ( threat.filename ) {
+ return filesIcon;
+ }
+ if ( threat.table ) {
+ return databaseIcon;
+ }
+ return coreIcon;
+ };
+
+ list = list.map( threat => ( {
+ label: getLabel( threat ),
+ icon: getIcon( threat ),
+ ...threat,
+ } ) );
return (
<>
@@ -195,11 +209,8 @@ const PaidList = ( { list } ) => {
diff,
filename,
firstDetected, // todo: still needs a proper fix
- first_detected,
fixedIn,
- fixed_in,
fixedOn,
- fixed_on,
icon,
fixable,
id,
@@ -211,15 +222,16 @@ const PaidList = ( { list } ) => {
title,
type,
version,
+ status,
} ) => (
{
title={ title }
type={ type }
version={ version }
+ status={ status }
/>
)
) }
-
- { manualScan }
-
>
);
};
diff --git a/projects/plugins/protect/src/js/components/threats-list/styles.module.scss b/projects/plugins/protect/src/js/components/threats-list/styles.module.scss
index f7adf047d2301..88e1a83090c0f 100644
--- a/projects/plugins/protect/src/js/components/threats-list/styles.module.scss
+++ b/projects/plugins/protect/src/js/components/threats-list/styles.module.scss
@@ -120,3 +120,29 @@
}
}
}
+
+.navigation-item-badge {
+ border: 1px solid var( --jp-red-60 );
+ color: var( --jp-red-60 );
+ border-radius: 50%;
+ padding: calc( var( --spacing-base ) / 2 ) var( --spacing-base ); // 4px | 8px
+ min-width: 30px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+}
+
+.navigation-item-badge--selected {
+ border: 1px solid var( --jp-red );
+ background-color: var( --jp-red );
+ color: var( --jp-white );
+}
+
+.navigation-item-check-badge {
+ fill: var( --jp-green-50 );
+}
+
+.navigation-item-info-badge {
+ fill: var( --jp-gray-20 );
+}
diff --git a/projects/plugins/protect/src/js/hooks/use-protect-data/index.js b/projects/plugins/protect/src/js/hooks/use-protect-data/index.js
index 8e7cddaa68512..94d6d4564ec42 100644
--- a/projects/plugins/protect/src/js/hooks/use-protect-data/index.js
+++ b/projects/plugins/protect/src/js/hooks/use-protect-data/index.js
@@ -1,7 +1,6 @@
import { useSelect } from '@wordpress/data';
import { useMemo } from 'react';
import { STORE_ID } from '../../state/store';
-import useScanHistory from '../use-scan-history';
/**
* Get parsed data from the initial state
@@ -9,45 +8,35 @@ import useScanHistory from '../use-scan-history';
* @returns {object} The information available in Protect's initial state.
*/
export default function useProtectData() {
- const { viewingScanHistory, scanHistory } = useScanHistory();
-
const { status, jetpackScan, hasRequiredPlan } = useSelect( select => ( {
status: select( STORE_ID ).getStatus(),
jetpackScan: select( STORE_ID ).getJetpackScan(),
hasRequiredPlan: select( STORE_ID ).hasRequiredPlan(),
} ) );
- const source = viewingScanHistory ? scanHistory : status;
-
const numCoreThreats = useMemo( () => {
- if ( viewingScanHistory ) {
- return ( source.core || [] ).reduce(
- ( numThreats, core ) => numThreats + core.threats.length,
- 0
- );
- }
- return source.core?.threats?.length || 0;
- }, [ viewingScanHistory, source.core ] );
+ return status.core?.threats?.length || 0;
+ }, [ status.core ] );
const numPluginsThreats = useMemo(
() =>
- ( source.plugins || [] ).reduce( ( numThreats, plugin ) => {
+ ( status.plugins || [] ).reduce( ( numThreats, plugin ) => {
return numThreats + plugin.threats.length;
}, 0 ),
- [ source.plugins ]
+ [ status.plugins ]
);
const numThemesThreats = useMemo(
() =>
- ( source.themes || [] ).reduce( ( numThreats, theme ) => {
+ ( status.themes || [] ).reduce( ( numThreats, theme ) => {
return numThreats + theme.threats.length;
}, 0 ),
- [ source.themes ]
+ [ status.themes ]
);
- const numFilesThreats = useMemo( () => source.files?.length || 0, [ source.files ] );
+ const numFilesThreats = useMemo( () => status.files?.length || 0, [ status.files ] );
- const numDatabaseThreats = useMemo( () => source.database?.length || 0, [ source.database ] );
+ const numDatabaseThreats = useMemo( () => status.database?.length || 0, [ status.database ] );
const numThreats =
numCoreThreats + numPluginsThreats + numThemesThreats + numFilesThreats + numDatabaseThreats;
@@ -59,16 +48,16 @@ export default function useProtectData() {
numThemesThreats,
numFilesThreats,
numDatabaseThreats,
- lastChecked: source.lastChecked || null,
- error: source.error || false,
- errorCode: source.errorCode || null,
- errorMessage: source.errorMessage || null,
- core: source.core || {},
- plugins: source.plugins || [],
- themes: source.themes || [],
- files: { threats: source.files || [] },
- database: { threats: source.database || [] },
- hasUncheckedItems: source.hasUncheckedItems,
+ lastChecked: status.lastChecked || null,
+ error: status.error || false,
+ errorCode: status.errorCode || null,
+ errorMessage: status.errorMessage || null,
+ core: status.core || {},
+ plugins: status.plugins || [],
+ themes: status.themes || [],
+ files: { threats: status.files || [] },
+ database: { threats: status.database || [] },
+ hasUncheckedItems: status.hasUncheckedItems,
jetpackScan,
hasRequiredPlan,
};
diff --git a/projects/plugins/protect/src/js/hooks/use-scan-history/index.js b/projects/plugins/protect/src/js/hooks/use-scan-history/index.js
deleted file mode 100644
index cdee0ea2380ec..0000000000000
--- a/projects/plugins/protect/src/js/hooks/use-scan-history/index.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import { useDispatch, useSelect } from '@wordpress/data';
-import { useCallback, useState } from 'react';
-import API from '../../api';
-import { STORE_ID } from '../../state/store';
-
-/**
- * Get parsed data from the initial state
- *
- * @returns {object} The information available in Protect's initial state.
- */
-export default function useScanHistory() {
- const { viewingScanHistory, scanHistory } = useSelect( select => ( {
- viewingScanHistory: select( STORE_ID ).getViewingScanHistory(),
- scanHistory: select( STORE_ID ).getScanHistory(),
- } ) );
-
- const { setViewingScanHistory, setScanHistory } = useDispatch( STORE_ID );
-
- const [ filter, setFilter ] = useState( 'all' );
- const [ allScanHistoryIsLoading, setAllScanHistoryIsLoading ] = useState( false );
- const [ ignoredScanHistoryIsLoading, setIgnoredScanHistoryIsLoading ] = useState( false );
- const [ fixedScanHistoryIsLoading, setFixedScanHistoryIsLoading ] = useState( false );
-
- const toggleAllScanHistory = useCallback( () => {
- setAllScanHistoryIsLoading( true );
- return API.fetchScanHistory( [ 'ignored', 'fixed' ] ).then( filteredScanHistory => {
- setScanHistory( filteredScanHistory );
- setFilter( 'all' );
- setAllScanHistoryIsLoading( false );
- } );
- }, [ setScanHistory, setAllScanHistoryIsLoading ] );
-
- const toggleIgnoredScanHistory = useCallback( () => {
- setIgnoredScanHistoryIsLoading( true );
- return API.fetchScanHistory( [ 'ignored' ] ).then( filteredScanHistory => {
- setScanHistory( filteredScanHistory );
- setFilter( 'ignored' );
- setIgnoredScanHistoryIsLoading( false );
- } );
- }, [ setScanHistory ] );
-
- const toggleFixedScanHistory = useCallback( () => {
- setFixedScanHistoryIsLoading( true );
- return API.fetchScanHistory( [ 'fixed' ] ).then( filteredScanHistory => {
- setScanHistory( filteredScanHistory );
- setFilter( 'fixed' );
- setFixedScanHistoryIsLoading( false );
- } );
- }, [ setScanHistory ] );
-
- const handleHistoryClick = useCallback( () => {
- toggleAllScanHistory().then( () => {
- setViewingScanHistory( true );
- } );
- }, [ toggleAllScanHistory, setViewingScanHistory ] );
-
- const handleCurrentClick = useCallback( () => {
- setViewingScanHistory( false );
- }, [ setViewingScanHistory ] );
-
- return {
- filter,
- viewingScanHistory,
- scanHistory,
- allScanHistoryIsLoading,
- ignoredScanHistoryIsLoading,
- fixedScanHistoryIsLoading,
- toggleIgnoredScanHistory,
- toggleFixedScanHistory,
- toggleAllScanHistory,
- handleHistoryClick,
- handleCurrentClick,
- };
-}
diff --git a/projects/plugins/protect/src/js/index.tsx b/projects/plugins/protect/src/js/index.tsx
index 2be68105d6fa2..23e9425d66068 100644
--- a/projects/plugins/protect/src/js/index.tsx
+++ b/projects/plugins/protect/src/js/index.tsx
@@ -1,28 +1,43 @@
-import { ThemeProvider } from '@automattic/jetpack-components';
import * as WPElement from '@wordpress/element';
-import React, { useEffect } from 'react';
-import { HashRouter, Routes, Route, useLocation } from 'react-router-dom';
+import { createHashRouter, Navigate, RouterProvider } from 'react-router-dom';
import FirewallPage from './components/firewall-page';
-import Modal from './components/modal';
+import ScanHistoryPage from './components/scan-history-page';
import ScanPage from './components/scan-page';
-import { OnboardingRenderedContextProvider } from './hooks/use-onboarding';
+import Layout from './layout';
import { initStore } from './state/store';
import './styles.module.scss';
// Initialize Jetpack Protect store
initStore();
-/**
- * Component to scroll window to top on route change.
- *
- * @returns {null} Null.
- */
-function ScrollToTop() {
- const location = useLocation();
- useEffect( () => window.scrollTo( 0, 0 ), [ location ] );
-
- return null;
-}
+const router = createHashRouter( [
+ {
+ path: '/',
+ element: ,
+ children: [
+ {
+ index: true,
+ element: ,
+ },
+ {
+ path: 'scan',
+ element: ,
+ },
+ {
+ path: 'scan/history',
+ element: ,
+ },
+ {
+ path: 'scan/history/:filter',
+ element: ,
+ },
+ {
+ path: 'firewall',
+ element: ,
+ },
+ ],
+ },
+] );
/**
* Initial render function.
@@ -34,21 +49,7 @@ function render() {
return;
}
- const component = (
-
-
-
-
-
- } />
- } />
-
-
-
-
-
- );
- WPElement.createRoot( container ).render( component );
+ WPElement.createRoot( container ).render( );
}
render();
diff --git a/projects/plugins/protect/src/js/layout.jsx b/projects/plugins/protect/src/js/layout.jsx
new file mode 100644
index 0000000000000..1bcb6f98b5f38
--- /dev/null
+++ b/projects/plugins/protect/src/js/layout.jsx
@@ -0,0 +1,34 @@
+import { ThemeProvider } from '@automattic/jetpack-components';
+import React, { useEffect } from 'react';
+import { Outlet, useLocation } from 'react-router-dom';
+import Modal from './components/modal';
+import { OnboardingRenderedContextProvider } from './hooks/use-onboarding';
+
+/**
+ * Component to scroll window to top on route change.
+ *
+ * @returns {null} Null.
+ */
+function ScrollToTop() {
+ const location = useLocation();
+ useEffect( () => window.scrollTo( 0, 0 ), [ location ] );
+
+ return null;
+}
+
+/**
+ * Root Layout
+ *
+ * @returns {React.Element} Layout.
+ */
+export default function Layout() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/projects/plugins/protect/src/js/state/actions.js b/projects/plugins/protect/src/js/state/actions.js
index adc4449c9b1f6..78423ee06049f 100644
--- a/projects/plugins/protect/src/js/state/actions.js
+++ b/projects/plugins/protect/src/js/state/actions.js
@@ -33,14 +33,6 @@ const SET_WAF_IS_TOGGLING = 'SET_WAF_IS_TOGGLING';
const SET_WAF_CONFIG = 'SET_WAF_CONFIG';
const SET_WAF_STATS = 'SET_WAF_STATS';
-const setViewingScanHistory = viewingScanHistory => {
- return { type: SET_VIEWING_SCAN_HISTORY, viewingScanHistory };
-};
-
-const setScanHistory = scanHistory => {
- return { type: SET_SCAN_HISTORY, scanHistory };
-};
-
const setStatus = status => {
return { type: SET_STATUS, status };
};
@@ -428,8 +420,6 @@ const actions = {
checkCredentials,
setCredentials,
setCredentialsIsFetching,
- setViewingScanHistory,
- setScanHistory,
setStatus,
setStatusProgress,
startScanOptimistically,
diff --git a/projects/plugins/protect/src/js/state/reducers.js b/projects/plugins/protect/src/js/state/reducers.js
index 30586bca084c6..eb92069b85d0b 100644
--- a/projects/plugins/protect/src/js/state/reducers.js
+++ b/projects/plugins/protect/src/js/state/reducers.js
@@ -2,8 +2,6 @@ import { combineReducers } from '@wordpress/data';
import {
SET_CREDENTIALS_STATE,
SET_CREDENTIALS_STATE_IS_FETCHING,
- SET_VIEWING_SCAN_HISTORY,
- SET_SCAN_HISTORY,
SET_STATUS,
SET_STATUS_PROGRESS,
START_SCAN_OPTIMISTICALLY,
@@ -46,22 +44,6 @@ const credentialsIsFetching = ( state = false, action ) => {
return state;
};
-const viewingScanHistory = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_VIEWING_SCAN_HISTORY:
- return action.viewingScanHistory;
- }
- return state;
-};
-
-const scanHistory = ( state = {}, action ) => {
- switch ( action.type ) {
- case SET_SCAN_HISTORY:
- return action.scanHistory;
- }
- return state;
-};
-
const status = ( state = {}, action ) => {
switch ( action.type ) {
case SET_STATUS:
@@ -214,8 +196,6 @@ const waf = ( state = defaultWaf, action ) => {
const reducers = combineReducers( {
credentials,
credentialsIsFetching,
- viewingScanHistory,
- scanHistory,
status,
statusIsFetching,
scanIsUnavailable,
diff --git a/projects/plugins/protect/src/js/state/selectors.js b/projects/plugins/protect/src/js/state/selectors.js
index 7f6ddda7501e8..3254d11bf2667 100644
--- a/projects/plugins/protect/src/js/state/selectors.js
+++ b/projects/plugins/protect/src/js/state/selectors.js
@@ -3,8 +3,6 @@ const selectors = {
getCredentialsIsFetching: state => state.credentialsIsFetching || false,
getInstalledPlugins: state => state.installedPlugins || {},
getInstalledThemes: state => state.installedThemes || {},
- getViewingScanHistory: state => state.viewingScanHistory || false,
- getScanHistory: state => state.scanHistory || {},
getStatus: state => state.status || {},
getStatusIsFetching: state => state.statusIsFetching || false,
getScanIsUnavailable: state => state.scanIsUnavailable || false,
diff --git a/projects/plugins/protect/src/models/class-history-model.php b/projects/plugins/protect/src/models/class-history-model.php
index 040d652f696f7..4c7321614d64b 100644
--- a/projects/plugins/protect/src/models/class-history-model.php
+++ b/projects/plugins/protect/src/models/class-history-model.php
@@ -12,81 +12,32 @@
*/
class History_Model {
/**
- * The date and time when the history was generated.
- *
- * @var string
- */
- public $last_checked;
-
- /**
- * The filter to apply to the history.
- *
- * @var array
- */
- public $filter = array( 'ignored', 'fixed' );
-
- /**
- * The number of threats.
+ * The number of all previously active threats.
*
* @var int
*/
- public $num_threats;
+ public $num_threats = 0;
/**
- * The number of core threats.
+ * The number of fixed threats.
*
* @var int
*/
- public $num_core_threats;
+ public $num_fixed_threats = 0;
/**
- * The number of plugin threats.
+ * The number of ignored threats.
*
* @var int
*/
- public $num_plugins_threats;
-
- /**
- * The number of theme threats.
- *
- * @var int
- */
- public $num_themes_threats;
-
- /**
- * WordPress core.
- *
- * @var array
- */
- public $core = array();
-
- /**
- * Status themes.
- *
- * @var array
- */
- public $themes = array();
-
- /**
- * Status plugins.
- *
- * @var array
- */
- public $plugins = array();
-
- /**
- * File threats.
- *
- * @var array
- */
- public $files = array();
+ public $num_ignored_threats = 0;
/**
- * Database threats.
+ * All previously active threats, sorted by most recent.
*
* @var array
*/
- public $database = array();
+ public $threats = array();
/**
* Whether there was an error loading the history.
diff --git a/projects/plugins/protect/src/models/class-threat-model.php b/projects/plugins/protect/src/models/class-threat-model.php
index 9461b11a5f59d..88ef454bf79a3 100644
--- a/projects/plugins/protect/src/models/class-threat-model.php
+++ b/projects/plugins/protect/src/models/class-threat-model.php
@@ -103,6 +103,20 @@ class Threat_Model {
*/
public $source;
+ /**
+ * Extension.
+ *
+ * @var null|object
+ */
+ public $extension;
+
+ /**
+ * Fixer.
+ *
+ * @var null|object
+ */
+ public $fixer;
+
/**
* Threat Constructor
*