Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Features: Offer the Protect switch through Jetpack when the Protect Dashboard feature flag is on.
6 changes: 5 additions & 1 deletion projects/packages/my-jetpack/src/class-main-features.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

namespace Automattic\Jetpack\My_Jetpack;

use Automattic\Jetpack\Modules;
use Automattic\Jetpack\Plugins_Installer;
use Automattic\Jetpack\Status\Host;

/**
* Describes the main Jetpack features: their copy, links and how a site owner gets each one.
Expand Down Expand Up @@ -364,7 +366,9 @@ public static function get_feature_definitions() {
__( 'Instant email alerts when a threat is found', 'jetpack-my-jetpack' ),
),
'delivery' => array(
'jetpack' => false,
// Only while the Jetpack plugin offers its flag-gated `protect-dashboard` module, and never on
// WordPress.com Simple, whose partial bootstrap skips the flag. Not is_module(): it passes any slug.
'jetpack' => ! ( new Host() )->is_wpcom_simple() && in_array( 'protect-dashboard', (array) ( new Modules() )->get_available(), true ),
'plugin' => 'jetpack-protect',
'plugin_name' => __( 'Jetpack Protect', 'jetpack-my-jetpack' ),
'plugin_url' => 'https://wordpress.org/plugins/jetpack-protect/',
Expand Down
3 changes: 2 additions & 1 deletion projects/packages/my-jetpack/src/products/class-protect.php
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ public static function get_post_checkout_urls_by_feature() {
* @return ?string
*/
public static function get_manage_url() {
if ( static::is_standalone_plugin_active() ) {
// The Protect package's dashboard, loaded by the Jetpack plugin's module, serves the same page as the standalone plugin.
if ( static::is_standalone_plugin_active() || did_action( 'jetpack_protect_dashboard_initialized' ) ) {
// Protect admin dashboard.
return admin_url( 'admin.php?page=jetpack-protect' );
}
Expand Down
51 changes: 51 additions & 0 deletions projects/packages/my-jetpack/tests/php/Main_Features_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Automattic\Jetpack\My_Jetpack;

use Automattic\Jetpack\Constants;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

/**
Expand Down Expand Up @@ -658,4 +660,53 @@ public function test_plans_list_complete_last() {
}
}
}

/**
* The modules on offer, whether the site is WordPress.com Simple, and whether Protect should then ship in Jetpack.
*
* @return array[]
*/
public static function provide_protect_dashboard_offers() {
return array(
'module on offer' => array( array( 'protect-dashboard' ), false, true ),
'nothing on offer' => array( array(), false, false ),
'module on offer on WPCOM Simple' => array( array( 'protect-dashboard' ), true, false ),
);
}

/**
* @dataProvider provide_protect_dashboard_offers
*
* @param string[] $modules Modules on offer.
* @param bool $is_simple Whether the site is WordPress.com Simple.
* @param bool $expected Whether Protect ships in Jetpack.
*/
#[DataProvider( 'provide_protect_dashboard_offers' )]
public function test_protect_ships_in_jetpack_only_while_its_module_is_on_offer( $modules, $is_simple, $expected ) {
if ( $is_simple ) {
Constants::set_constant( 'IS_WPCOM', true );
}
$offer = static function () use ( $modules ) {
return $modules;
};
// Earlier tests may load the mock Jetpack plugin, which moves get_available() off the standalone filter.
$jetpack_offer = static function () use ( $modules ) {
return array_fill_keys( $modules, '1.0' );
};
// Definitions are memoized per locale, so a locale nothing else uses gets a fresh build.
$locale = static function () use ( $modules, $is_simple ) {
return 'protect_dashboard_' . count( $modules ) . ( $is_simple ? '_simple' : '' );
};
add_filter( 'jetpack_get_available_standalone_modules', $offer );
add_filter( 'jetpack_get_available_modules', $jetpack_offer, PHP_INT_MAX );
add_filter( 'locale', $locale );

$delivery = Main_Features::get_feature_definitions()['protect-dashboard']['delivery']['jetpack'];

remove_filter( 'jetpack_get_available_standalone_modules', $offer );
remove_filter( 'jetpack_get_available_modules', $jetpack_offer, PHP_INT_MAX );
remove_filter( 'locale', $locale );
Constants::clear_single_constant( 'IS_WPCOM' );
$this->assertSame( $expected, $delivery );
}
}
54 changes: 54 additions & 0 deletions projects/packages/my-jetpack/tests/php/Protect_Product_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Automattic\Jetpack\My_Jetpack;

use Automattic\Jetpack\My_Jetpack\Products\Protect;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

/**
* Unit tests for the Protect product.
*
* @package automattic/my-jetpack
* @see \Automattic\Jetpack\My_Jetpack\Products\Protect
*/
class Protect_Product_Test extends TestCase {

/**
* Returning the environment into its initial state.
*/
public function tearDown(): void {
parent::tearDown();

unset( $GLOBALS['wp_actions']['jetpack_protect_dashboard_initialized'] );
}

/**
* Whether the Protect package's dashboard has loaded.
*
* @return array[]
*/
public static function provide_dashboard_states() {
return array(
'dashboard loaded' => array( true ),
'dashboard not loaded' => array( false ),
);
}

/**
* Tests Protect Manage URL with the Protect package's dashboard and no standalone plugin.
*
* @dataProvider provide_dashboard_states
*
* @param bool $loaded Whether the dashboard has loaded.
*/
#[DataProvider( 'provide_dashboard_states' )]
public function test_protect_manage_url_with_the_dashboard( $loaded ) {
if ( $loaded ) {
do_action( 'jetpack_protect_dashboard_initialized' );
}

$this->assertFalse( Protect::is_standalone_plugin_active() );
$this->assertSame( $loaded, admin_url( 'admin.php?page=jetpack-protect' ) === Protect::get_manage_url() );
}
}
24 changes: 8 additions & 16 deletions projects/packages/protect/.gitattributes
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
# Files to include in the mirror repo, but excluded via gitignore
# Remember to end all directories with `/**` to properly tag every file.
# /src/js/example.min.js production-include
# Files not needed to be distributed.

# Files to exclude from the mirror repo, but included in the monorepo.
# Files not needed in the production build.
# (see also entries in the monorepo root .gitattributes)
# Remember to end all directories with `/**` to properly tag every file.
#
# Source files compiled during build (uncomment as needed):
# src/**/*.ts production-exclude
# src/**/*.tsx production-exclude
# src/**/*.jsx production-exclude
# src/**/*.scss production-exclude
#
# wp-build stuff compiled during build (uncomment as needed):
# packages/** production-exclude
# routes/** production-exclude
# widgets/** production-exclude
/build/**/*.map production-exclude
packages/** production-exclude
routes/** production-exclude

# Files needed in the production build.
build/** production-include
10 changes: 8 additions & 2 deletions projects/packages/protect/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
vendor/
node_modules/
wordpress
node_modules
vendor
jetpack_vendor
.cache
build
build-module
/routes/**/.content-entry.js
9 changes: 8 additions & 1 deletion projects/packages/protect/.phan/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@
// Require base config.
require __DIR__ . '/../../../../.phan/config.base.php';

return make_phan_config( dirname( __DIR__ ) );
return make_phan_config(
dirname( __DIR__ ),
array(
'exclude_file_regex' => array(
'build/',
),
)
);
8 changes: 8 additions & 0 deletions projects/packages/protect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

Shared Protect code for the Jetpack and Jetpack Protect plugins.

It provides the Protect dashboard, shared by the Jetpack plugin's `protect-dashboard` module and, later, the Jetpack Protect plugin. The page is a `@wordpress/build` route in `routes/dashboard`, rendered under the `jetpack-protect` menu slug:

```php
\Automattic\Jetpack\Protect\Dashboard::init();
```

It takes that slug over from the Jetpack Protect plugin, replacing the plugin's dashboard, so keep the Jetpack plugin's `jetpack-protect-dashboard` flag off on sites using that plugin until this page matches it.

## How to install protect

### Installation From Git Repo
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add the Protect dashboard page.
44 changes: 40 additions & 4 deletions projects/packages/protect/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,45 @@
"type": "jetpack-library",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=7.4"
"php": ">=7.4",
"automattic/jetpack-admin-ui": "@dev",
"automattic/jetpack-wp-build-polyfills": "@dev"
},
"require-dev": {
"automattic/jetpack-changelogger": "@dev",
"yoast/phpunit-polyfills": "^4.0.0",
"automattic/jetpack-test-environment": "@dev",
"automattic/phpunit-select-config": "@dev"
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
},
"autoload": {
"classmap": [
"src/"
]
},
"scripts": {
"build-development": [
"pnpm run build"
],
"build-production": [
"pnpm run build-production"
],
"watch": [
"Composer\\Config::disableProcessTimeout",
"pnpm run watch"
],
"phpunit": [
"phpunit-select-config phpunit.#.xml.dist --colors=always"
],
"test-php": [
"@composer phpunit"
],
"test-php-coverage": [
"php -dpcov.directory=. ./vendor/bin/phpunit-select-config phpunit.#.xml.dist --coverage-php \"$COVERAGE_DIR/php.cov\""
]
},
"repositories": [
{
"type": "path",
Expand All @@ -33,10 +65,14 @@
"mirror-repo": "Automattic/jetpack-protect",
"textdomain": "jetpack-protect-pkg",
"version-constants": {
"::PACKAGE_VERSION": "src/class-package.php"
"::PACKAGE_VERSION": "src/class-dashboard.php"
}
},
"suggest": {
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
"config": {
"allow-plugins": {
"roots/wordpress-core-installer": true,
"automattic/jetpack-autoloader": true,
"automattic/jetpack-composer-plugin": true
}
}
}
3 changes: 3 additions & 0 deletions projects/packages/protect/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { makeBaseConfig, defineConfig } from 'jetpack-js-tools/eslintrc/base.mjs';

export default defineConfig( makeBaseConfig( import.meta.url ) );
48 changes: 43 additions & 5 deletions projects/packages/protect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,48 @@
"license": "GPL-2.0-or-later",
"author": "Automattic",
"scripts": {
"build": "echo 'Not implemented.'",
"build-js": "echo 'Not implemented.'",
"build-production": "echo 'Not implemented.'",
"build-production-js": "echo 'Not implemented.'",
"clean": "true"
"build": "pnpm run clean && pnpm run build:wp-build && pnpm run build:stamp-textdomains && pnpm run build:boot-asset",
"build:boot-asset": "provide-boot-asset-file",
"build:stamp-textdomains": "stamp-textdomains",
"build:strip-unminified-prod": "strip-unminified-prod",
"build:wp-build": "wp-build",
"build-production": "NODE_ENV=production BABEL_ENV=production pnpm run build && pnpm run build:strip-unminified-prod && pnpm run validate",
"clean": "rm -rf build/",
"typecheck": "tsgo --noEmit",
"validate": "pnpm exec validate-es --no-error-on-unmatched-pattern build/",
"watch": "wp-build --watch"
},
"browserslist": [
"extends @wordpress/browserslist-config"
],
"wpPlugin": {
"name": "jetpack_protect",
"scriptGlobal": "jetpackProtect",
"packageNamespace": "jetpack-protect",
"handlePrefix": "jetpack-protect",
"pages": [
{
"id": "jetpack-protect-dashboard",
"init": [
"@jetpack-protect/init"
]
}
]
},
"dependencies": {
"@jetpack-protect/init": "link:packages/init",
"@wordpress/admin-ui": "2.10.0",
"@wordpress/element": "8.7.0",
"react": "18.3.1",
"react-dom": "18.3.1"
},
"devDependencies": {
"@automattic/jetpack-wp-build-polyfills": "workspace:*",
"@babel/core": "7.29.7",
"@types/react": "18.3.31",
"@typescript/native-preview": "7.0.0-dev.20260707.2",
"@wordpress/browserslist-config": "6.55.0",
"@wordpress/build": "0.23.0",
"browserslist": "^4.24.0"
}
}
Loading
Loading