initial
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.11.1] - 2026-06-29
|
||||
### Changed
|
||||
- Settings: Link the "Button only" style hint to platform-appropriate support documentation. [#50009]
|
||||
- Update package dependencies. [#49271]
|
||||
|
||||
## [0.11.0] - 2026-06-25
|
||||
### Changed
|
||||
- Introduce the modernized dashboard and WP Admin subscriber management behind the `rsm_jetpack_ui_modernization_newsletter` and `jetpack_wp_admin_subscriber_management_enabled` filters, off by default. [#49036]
|
||||
- Update package dependencies. [#49831]
|
||||
|
||||
### Fixed
|
||||
- Align the settings DataForm field `Edit` controls, the import-subscribers notice `ActionLink`, and the dashboard tab panels with the `@wordpress/dataviews` 16 and `@wordpress/ui` 0.15 type APIs. [#49801]
|
||||
|
||||
## [0.10.0] - 2026-06-22
|
||||
### Added
|
||||
- Add a transitional Subscribers announcement page (wp-build) shown in place of the legacy Subscribers menu link when the Newsletter modernization filter is enabled. [#49496]
|
||||
- Register the transitional Subscribers announcement page directly under the Jetpack menu on WordPress.com sites. [#49675]
|
||||
|
||||
### Changed
|
||||
- Subscribers: Link the Substack importer button to the WordPress.com site-setup importer flow. [#49538]
|
||||
- Update package dependencies. [#49631] [#49691] [#49757]
|
||||
|
||||
### Fixed
|
||||
- Close the subscriber detail panel when its subscriber is removed instead of leaving it open with stale data. [#49598]
|
||||
|
||||
## [0.9.1] - 2026-06-15
|
||||
### Added
|
||||
- Add a setting for the Subscribe modal heading shown by Subscribe blocks using the Button-only style. [#49171]
|
||||
- Daily Writing Prompt: Record Tracks events when interacting with the widget's actions. [#49534]
|
||||
|
||||
### Changed
|
||||
- Daily Writing Prompt: Link to the WordPress.com Reader and move the responses button next to the Post Answer button. [#49462]
|
||||
- Label the subscription placement options with a "Homepage and posts" section header. [#49466]
|
||||
- Update package dependencies. [#49273] [#49492]
|
||||
|
||||
### Fixed
|
||||
- Daily Writing Prompt: Defer the connection-readiness check to Dashboard setup to avoid a fatal error on Atomic sites. [#49525]
|
||||
- Fix the "Add plans" link for WordPress installs in a subdirectory. [#49553]
|
||||
- Keep the Subscribers/Settings tab bar pinned while scrolling the modernized Settings tab. [#49460]
|
||||
- Only show the subscription placement "Preview and edit" link once the placement is enabled and saved. [#49532]
|
||||
- Render the same initials avatar in the subscriber row and detail panel when a subscriber has no Gravatar. [#49581]
|
||||
- Stop the modernized Settings tab from flashing a full-page loading spinner on every visit. [#49530]
|
||||
- Subscribers: Hide the "Comp a subscription" action when the site has no paid newsletter plans. [#49531]
|
||||
|
||||
## [0.9.0] - 2026-06-08
|
||||
### Added
|
||||
- Add Jetpack branding to the Daily Writing Prompt dashboard widget. [#49438]
|
||||
- Add the Daily Writing Prompt dashboard widget, moved here from the jetpack-mu-wpcom package. [#49425]
|
||||
|
||||
### Changed
|
||||
- Remove the per-page Hello Dolly rule, as it is now handled by `@automattic/jetpack-components`'s AdminPage component. [#48472]
|
||||
|
||||
### Fixed
|
||||
- Include the routes directory in the TypeScript config so dashboard route files type-check correctly. [#49368]
|
||||
- Hide the WordPress.com connection prompt on Simple sites, which are already connected. [#49365]
|
||||
- Restore body padding by wrapping content in Dialog.Content in Add subscribers and Comp modals. [#49327]
|
||||
- Subscribers Dashboard: Fix several Comp modal issues. [#49441]
|
||||
- Subscribers Dashboard: Show the empty state when the site owner is the only subscriber, and point the empty-state copy at the subscription form documentation. [#49410]
|
||||
|
||||
## [0.8.10] - 2026-06-01
|
||||
### Changed
|
||||
- Update package dependencies. [#48404] [#49152]
|
||||
|
||||
### Fixed
|
||||
- Record a `jetpack_newsletter_tab_view` Tracks event on initial page load in Newsletter dashboard (matching its tab-switch behavior). [#49243]
|
||||
|
||||
## [0.8.9] - 2026-05-25
|
||||
### Changed
|
||||
- Normalize page tabs onto the shared minimal variant and `jp-admin-page-tabs--minimal` wrapper modifier. [#48964]
|
||||
- Update package dependencies. [#48405] [#49012]
|
||||
|
||||
### Fixed
|
||||
- Drop dead `@wordpress/admin-ui` build-style import that no longer exists in 2.x. [#49007]
|
||||
- Fix Settings tab toggles that controlled the wrong setting due to duplicate element IDs. [#49102]
|
||||
|
||||
## [0.8.8] - 2026-05-19
|
||||
### Changed
|
||||
- Build: Run webpack and wp-build scripts concurrently. [#48794]
|
||||
- Exclude development files from production builds. [#47365]
|
||||
|
||||
### Fixed
|
||||
- Fix saving settings on the Newsletter admin page on Atomic and self-hosted sites. [#48813]
|
||||
|
||||
## [0.8.7] - 2026-05-11
|
||||
### Changed
|
||||
- Components: Use Link from `@wordpress/ui` instead of ExternalLink. [#48529]
|
||||
|
||||
## [0.8.6] - 2026-05-04
|
||||
### Changed
|
||||
- Internal: No longer require automattic/jetpack-changelogger as a per-project dev dependency. [#48225]
|
||||
|
||||
## [0.8.5] - 2026-04-27
|
||||
### Fixed
|
||||
- Reader Link: Only enqueue styles when the admin bar is visible. [#48145]
|
||||
|
||||
## [0.8.4] - 2026-04-20
|
||||
### Changed
|
||||
- Adopt the shared Jetpack admin-page-layout mixin on the Newsletter admin page: pinned header, scrolling middle, pinned footer, no window-level scroll. [#48109]
|
||||
- Update package dependencies. [#48106] [#48141]
|
||||
|
||||
### Removed
|
||||
- Remove the jetpack_wp_admin_newsletter_settings_enabled filter; the wp-admin newsletter settings page is now always enabled. [#48092]
|
||||
|
||||
## [0.8.3] - 2026-04-15
|
||||
### Changed
|
||||
- Update package dependencies. [#47907]
|
||||
|
||||
## [0.8.2] - 2026-04-10
|
||||
### Changed
|
||||
- Update dependencies. [#48049]
|
||||
|
||||
## [0.8.1] - 2026-04-09
|
||||
### Changed
|
||||
- Update package dependencies. [#47890] [#47998]
|
||||
|
||||
### Fixed
|
||||
- Defer is_connected() check from init to admin_menu callback to avoid caching a false result before External Storage providers are registered. [#48005]
|
||||
|
||||
## [0.8.0] - 2026-04-06
|
||||
### Added
|
||||
- Newsletter settings: Disable settings and show a connection notice when the site has no connected owner. [#47927]
|
||||
|
||||
### Fixed
|
||||
- Admin menu: Hide when a site is not connected to WordPress.com yet. [#47927]
|
||||
|
||||
## [0.7.0] - 2026-03-30
|
||||
### Changed
|
||||
- Enable WP Admin newsletter settings by default. [#47750]
|
||||
- Update DataViews dependency. [#46973]
|
||||
- Update legacy Node calls. [#47770]
|
||||
- Update package dependencies. [#47799]
|
||||
|
||||
### Fixed
|
||||
- Fix spacing between the send-default toggle description and the "Manage all subscribers" link in the Newsletter settings card. [#47830]
|
||||
- Move admin notices and JITMs below the page header. [#47714]
|
||||
- Reader Link: Add compatibility check for Urls::maybe_add_origin_site_id() to prevent fatal errors with older connection package versions. [#47808]
|
||||
- Settings: Avoid fatals for disconnected jetpack errors. [#47782]
|
||||
|
||||
## [0.6.2] - 2026-03-23
|
||||
### Changed
|
||||
- Add "Send newsletter by default" toggle to Newsletter settings section. [#47565]
|
||||
- Update newsletter settings max width to 660px to match MSD and future settings pages. [#47626]
|
||||
- Update package dependencies. [#47684]
|
||||
|
||||
### Fixed
|
||||
- Fix translator comment concatenation caused by dataviews function aliasing. [#47602]
|
||||
|
||||
## [0.6.1] - 2026-03-16
|
||||
### Added
|
||||
- Settings: Add privacy information link. [#47587]
|
||||
|
||||
### Changed
|
||||
- Settings: Fix inconsistent punctuation and casing on labels. [#47559]
|
||||
- Settings: Link to WordPress.com for all WordPress.com sites. [#47578]
|
||||
- Update dependencies. [#47472]
|
||||
|
||||
### Fixed
|
||||
- Add spacing below warning notice in Email content settings section. [#47516]
|
||||
- Subscriptions: Fix mobile alignment of Preview and edit links in settings. [#47561]
|
||||
- Ensure module configure URL points to the new newsletter settings page. [#47584]
|
||||
|
||||
## [0.6.0] - 2026-03-09
|
||||
### Added
|
||||
- Add `jetpack_show_newsletter_menu_item` filter to show menu regardless of subscriptions module state. [#47347]
|
||||
|
||||
### Changed
|
||||
- Migrate admin page header to use unified header pattern. [#47313]
|
||||
- Switch to Native TypeScript compiler based on Go. [#47375]
|
||||
- Update newsletter sections to use new component patterns. [#47406]
|
||||
- Update package dependencies. [#47496]
|
||||
|
||||
### Fixed
|
||||
- Settings: Fix Hello Dolly banner display and box-sizing on the newsletter settings page. [#47313]
|
||||
|
||||
## [0.5.2] - 2026-03-02
|
||||
### Changed
|
||||
- Update dependencies. [#47038]
|
||||
|
||||
## [0.5.1] - 2026-02-26
|
||||
### Added
|
||||
- Add clarifying note to Reading settings page linking to Newsletter settings for email content control. [#47299]
|
||||
|
||||
### Changed
|
||||
- Refactor settings to use `@automattic/jetpack-script-data` utilities for site info and admin URLs. [#47259]
|
||||
- Update package dependencies. [#47285] [#47300]
|
||||
- Use `Status` class methods for `isSitePublic` check (filterable and handles coming soon state). [#47321]
|
||||
|
||||
## [0.5.0] - 2026-02-23
|
||||
### Added
|
||||
- Add Tracks analytics to track user interactions on the newsletter settings page. [#47100]
|
||||
|
||||
### Changed
|
||||
- Reader Link: Open the Reader in a different tab on self-hosted sites. [#47246]
|
||||
- Settings: Wrap page with `AdminPage` component for consistency with other Jetpack pages. [#47086]
|
||||
- Update package dependencies. [#47173]
|
||||
|
||||
## [0.4.0] - 2026-02-16
|
||||
### Added
|
||||
- Automatically enable the admin bar link on sites newly connected to WordPress.com. [#47033]
|
||||
- WordPress.com Reader: Add new class to render a new navigation menu in the admin bar. [#46783]
|
||||
|
||||
### Changed
|
||||
- Update package dependencies. [#47099]
|
||||
|
||||
### Fixed
|
||||
- Settings: Improve feedback with toast notifications for save success/error and inline error for category load failures. [#46987]
|
||||
|
||||
## [0.3.2] - 2026-02-10
|
||||
### Changed
|
||||
- CSS: Ensure dataforms css is loaded. [#46877]
|
||||
- Update dependencies. [#46931] [#47002]
|
||||
|
||||
### Fixed
|
||||
- Fix settings and categories API endpoints on WordPress.com Simple sites. [#46930]
|
||||
|
||||
## [0.3.1] - 2026-02-02
|
||||
### Added
|
||||
- Add a newsletter categories section to the settings screen. [#46708]
|
||||
- Add settings sections: subscriptions, paid newsletter, and welcome message. [#46473]
|
||||
|
||||
### Changed
|
||||
- Update package dependencies. [#46430] [#46853] [#46854]
|
||||
|
||||
## [0.3.0] - 2026-01-26
|
||||
### Added
|
||||
- Add newsletter settings to the new screen. [#46471]
|
||||
|
||||
## [0.2.0] - 2026-01-19
|
||||
### Added
|
||||
- Implement bits of infrastructure for newsletter settings UI. [#46470]
|
||||
|
||||
### Changed
|
||||
- Update package dependencies. [#46552] [#46647]
|
||||
|
||||
## [0.1.4] - 2026-01-12
|
||||
### Changed
|
||||
- Update package dependencies. [#46456]
|
||||
|
||||
## [0.1.3] - 2025-12-22
|
||||
### Changed
|
||||
- Update dependencies. [#46381]
|
||||
|
||||
## [0.1.2] - 2025-12-15
|
||||
### Changed
|
||||
- Update package dependencies. [#46244]
|
||||
|
||||
## [0.1.1] - 2025-12-08
|
||||
### Changed
|
||||
- Update package dependencies. [#45914]
|
||||
|
||||
## 0.1.0 - 2025-12-01
|
||||
### Added
|
||||
- Initial version. [#46049]
|
||||
|
||||
### Changed
|
||||
- Update package dependencies. [#46143]
|
||||
|
||||
[0.11.1]: https://github.com/Automattic/jetpack-newsletter/compare/v0.11.0...v0.11.1
|
||||
[0.11.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.10.0...v0.11.0
|
||||
[0.10.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.9.1...v0.10.0
|
||||
[0.9.1]: https://github.com/Automattic/jetpack-newsletter/compare/v0.9.0...v0.9.1
|
||||
[0.9.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.10...v0.9.0
|
||||
[0.8.10]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.9...v0.8.10
|
||||
[0.8.9]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.8...v0.8.9
|
||||
[0.8.8]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.7...v0.8.8
|
||||
[0.8.7]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.6...v0.8.7
|
||||
[0.8.6]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.5...v0.8.6
|
||||
[0.8.5]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.4...v0.8.5
|
||||
[0.8.4]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.3...v0.8.4
|
||||
[0.8.3]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.2...v0.8.3
|
||||
[0.8.2]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.1...v0.8.2
|
||||
[0.8.1]: https://github.com/Automattic/jetpack-newsletter/compare/v0.8.0...v0.8.1
|
||||
[0.8.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.7.0...v0.8.0
|
||||
[0.7.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.6.2...v0.7.0
|
||||
[0.6.2]: https://github.com/Automattic/jetpack-newsletter/compare/v0.6.1...v0.6.2
|
||||
[0.6.1]: https://github.com/Automattic/jetpack-newsletter/compare/v0.6.0...v0.6.1
|
||||
[0.6.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.5.2...v0.6.0
|
||||
[0.5.2]: https://github.com/Automattic/jetpack-newsletter/compare/v0.5.1...v0.5.2
|
||||
[0.5.1]: https://github.com/Automattic/jetpack-newsletter/compare/v0.5.0...v0.5.1
|
||||
[0.5.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.4.0...v0.5.0
|
||||
[0.4.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.3.2...v0.4.0
|
||||
[0.3.2]: https://github.com/Automattic/jetpack-newsletter/compare/v0.3.1...v0.3.2
|
||||
[0.3.1]: https://github.com/Automattic/jetpack-newsletter/compare/v0.3.0...v0.3.1
|
||||
[0.3.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.2.0...v0.3.0
|
||||
[0.2.0]: https://github.com/Automattic/jetpack-newsletter/compare/v0.1.4...v0.2.0
|
||||
[0.1.4]: https://github.com/Automattic/jetpack-newsletter/compare/v0.1.3...v0.1.4
|
||||
[0.1.3]: https://github.com/Automattic/jetpack-newsletter/compare/v0.1.2...v0.1.3
|
||||
[0.1.2]: https://github.com/Automattic/jetpack-newsletter/compare/v0.1.1...v0.1.2
|
||||
[0.1.1]: https://github.com/Automattic/jetpack-newsletter/compare/v0.1.0...v0.1.1
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* Main entry point for auto-generated asset registration.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
// Load script module registration.
|
||||
$modules_file = __DIR__ . '/modules.php';
|
||||
if ( file_exists( $modules_file ) ) {
|
||||
require_once $modules_file;
|
||||
}
|
||||
|
||||
// Load script registration.
|
||||
$scripts_file = __DIR__ . '/scripts.php';
|
||||
if ( file_exists( $scripts_file ) ) {
|
||||
require_once $scripts_file;
|
||||
}
|
||||
|
||||
// Load style registration.
|
||||
$styles_file = __DIR__ . '/styles.php';
|
||||
if ( file_exists( $styles_file ) ) {
|
||||
require_once $styles_file;
|
||||
}
|
||||
|
||||
// Load routes registration.
|
||||
$routes_file = __DIR__ . '/routes.php';
|
||||
if ( file_exists( $routes_file ) ) {
|
||||
require_once $routes_file;
|
||||
}
|
||||
|
||||
// Load widgets registration.
|
||||
$widgets_file = __DIR__ . '/widgets.php';
|
||||
if ( file_exists( $widgets_file ) ) {
|
||||
require_once $widgets_file;
|
||||
}
|
||||
|
||||
// Load pages registration.
|
||||
$pages_file = __DIR__ . '/pages.php';
|
||||
if ( file_exists( $pages_file ) ) {
|
||||
require_once $pages_file;
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin constants - Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* Returns an array of constants for use in other templates.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
return array(
|
||||
'version' => '0.11.1',
|
||||
'build_url' => plugin_dir_url( __FILE__ ),
|
||||
);
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<svg width="380" height="183" viewBox="0 0 380 183" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.75" filter="url(#filter0_f_5953_11924)">
|
||||
<path d="M360 -60C360 7.93102 304.931 63 237 63C169.069 63 114 7.93102 114 -60C114 -127.931 169.069 -183 237 -183C304.931 -183 360 -127.931 360 -60Z" fill="url(#paint0_linear_5953_11924)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_5953_11924" x="-6" y="-303" width="486" height="486" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="60" result="effect1_foregroundBlur_5953_11924"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_5953_11924" x1="360" y1="-60" x2="114" y2="-60" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#48FF50"/>
|
||||
<stop offset="0.471846" stop-color="#108642"/>
|
||||
<stop offset="1" stop-color="#2E38FA"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 990 B |
+63
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* Script module registration - Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register all script modules.
|
||||
*/
|
||||
function jetpack_newsletter_register_script_modules() {
|
||||
// Ensure this only runs once. wp_default_scripts can fire multiple times,
|
||||
// and each wp_deregister_script_module() call also dequeues the module.
|
||||
// If a module was enqueued between calls, repeated deregister/register
|
||||
// cycles would lose the enqueue state.
|
||||
static $already_registered = false;
|
||||
if ( $already_registered ) {
|
||||
return;
|
||||
}
|
||||
$already_registered = true;
|
||||
|
||||
// Load build constants
|
||||
$constants_file = __DIR__ . '/constants.php';
|
||||
if ( ! file_exists( $constants_file ) ) {
|
||||
return;
|
||||
}
|
||||
$build_constants = require $constants_file;
|
||||
$modules_dir = __DIR__ . '/modules';
|
||||
$modules_file = $modules_dir . '/registry.php';
|
||||
|
||||
if ( ! file_exists( $modules_file ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$modules = require $modules_file;
|
||||
$base_url = $build_constants['build_url'] . 'modules/';
|
||||
|
||||
foreach ( $modules as $module ) {
|
||||
// WASM-only modules (e.g., vips worker) only have .min.js; use it even when SCRIPT_DEBUG is `true`.
|
||||
$extension = '.min.js';
|
||||
|
||||
$asset_path = $modules_dir . '/' . $module['asset'];
|
||||
$asset = file_exists( $asset_path ) ? require $asset_path : array();
|
||||
|
||||
// Deregister first to override any previously registered version
|
||||
// (e.g., Core's default modules when running as a plugin).
|
||||
wp_deregister_script_module( $module['id'] );
|
||||
|
||||
wp_register_script_module(
|
||||
$module['id'],
|
||||
$base_url . $module['path'] . $extension,
|
||||
$asset['module_dependencies'] ?? array(),
|
||||
$asset['version'] ?? false,
|
||||
array(
|
||||
'fetchpriority' => 'low',
|
||||
'in_footer' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'wp_default_scripts', 'jetpack_newsletter_register_script_modules' );
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-commands', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-keyboard-shortcuts', 'wp-keycodes', 'wp-notices', 'wp-primitives', 'wp-private-apis', 'wp-theme', 'wp-url'), 'module_dependencies' => array(array('id' => '@wordpress/a11y', 'import' => 'static'), array('id' => '@wordpress/lazy-editor', 'import' => 'dynamic'), array('id' => '@wordpress/route', 'import' => 'static')), 'version' => '188faf6c64fdd1b4');
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Script module registry - Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
return array(
|
||||
|
||||
);
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php return array('dependencies' => array('jetpack-connection', 'jetpack-script-data', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-polyfill', 'wp-primitives', 'wp-private-apis', 'wp-url'), 'version' => 'ffce2c4026fe2ea03c4f');
|
||||
+1
File diff suppressed because one or more lines are too long
+77
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* Pages loader - Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
foreach ( [
|
||||
__DIR__ . '/pages/jetpack-newsletter-dashboard/page.php',
|
||||
__DIR__ . '/pages/jetpack-newsletter-dashboard/page-wp-admin.php',
|
||||
__DIR__ . '/pages/jetpack-subscribers-announcement/page.php',
|
||||
__DIR__ . '/pages/jetpack-subscribers-announcement/page-wp-admin.php',
|
||||
] as $file ) {
|
||||
if ( file_exists( $file ) ) {
|
||||
require_once $file;
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
// Empty module loader for page dependencies
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
<?php
|
||||
/**
|
||||
* Page: jetpack-newsletter-dashboard (wp-admin integrated)
|
||||
* Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* This version integrates with the standard WordPress admin interface,
|
||||
* keeping the wp-admin sidebar and scripts/styles intact.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
// Global storage for jetpack-newsletter-dashboard routes and menu items
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_routes, $jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_menu_items;
|
||||
$jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_routes = array();
|
||||
$jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_menu_items = array();
|
||||
|
||||
/**
|
||||
* Register a route for the jetpack-newsletter-dashboard-wp-admin page.
|
||||
*
|
||||
* @param string $path Route path (e.g., '/types/$type/edit/$id').
|
||||
* @param string|null $content_module Script module ID for content (stage/inspector).
|
||||
* @param string|null $route_module Script module ID for route lifecycle hooks.
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_newsletter_dashboard_wp_admin_route( $path, $content_module = null, $route_module = null ) {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_routes;
|
||||
|
||||
$route = array( 'path' => $path );
|
||||
if ( ! empty( $content_module ) ) {
|
||||
$route['content_module'] = $content_module;
|
||||
}
|
||||
if ( ! empty( $route_module ) ) {
|
||||
$route['route_module'] = $route_module;
|
||||
}
|
||||
|
||||
$jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_routes[] = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a menu item for the jetpack-newsletter-dashboard-wp-admin page.
|
||||
* Note: Menu items are registered but not displayed in single-page mode.
|
||||
*
|
||||
* @param string $id Menu item ID.
|
||||
* @param string $label Display label.
|
||||
* @param string $to Route path to navigate to.
|
||||
* @param string $parent_id Optional. Parent menu item ID.
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_newsletter_dashboard_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_menu_items;
|
||||
|
||||
$menu_item = array(
|
||||
'id' => $id,
|
||||
'label' => $label,
|
||||
'to' => $to,
|
||||
);
|
||||
|
||||
if ( ! empty( $parent_id ) ) {
|
||||
$menu_item['parent'] = $parent_id;
|
||||
}
|
||||
|
||||
$jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_menu_items[] = $menu_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered routes for the jetpack-newsletter-dashboard-wp-admin page.
|
||||
*
|
||||
* @return array Array of route objects.
|
||||
*/
|
||||
function jetpack_newsletter_get_jetpack_newsletter_dashboard_wp_admin_routes() {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_routes;
|
||||
return $jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_routes ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered menu items for the jetpack-newsletter-dashboard-wp-admin page.
|
||||
*
|
||||
* @return array Array of menu item objects.
|
||||
*/
|
||||
function jetpack_newsletter_get_jetpack_newsletter_dashboard_wp_admin_menu_items() {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_menu_items;
|
||||
return $jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_menu_items ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload REST API data for the jetpack-newsletter-dashboard-wp-admin page.
|
||||
* Automatically called during page rendering.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_preload_data() {
|
||||
// Define paths to preload - same for all pages
|
||||
// Please also change packages/core-data/src/entities.js when changing this.
|
||||
$preload_paths = array(
|
||||
'/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front',
|
||||
array( '/wp/v2/settings', 'OPTIONS' ),
|
||||
);
|
||||
|
||||
// Use rest_preload_api_request to gather the preloaded data
|
||||
$preload_data = array_reduce(
|
||||
$preload_paths,
|
||||
'rest_preload_api_request',
|
||||
array()
|
||||
);
|
||||
|
||||
// Register the preloading middleware with wp-api-fetch
|
||||
wp_add_inline_script(
|
||||
'wp-api-fetch',
|
||||
sprintf(
|
||||
'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );',
|
||||
wp_json_encode( $preload_data )
|
||||
),
|
||||
'after'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles for the jetpack-newsletter-dashboard-wp-admin page.
|
||||
* Hooked to admin_enqueue_scripts.
|
||||
*
|
||||
* @param string $hook_suffix The current admin page.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_enqueue_scripts( $hook_suffix ) {
|
||||
// Check all possible ways this page can be accessed:
|
||||
// 1. Menu page via admin.php?page=jetpack-newsletter-dashboard-wp-admin (plugin)
|
||||
// 2. Direct file via jetpack-newsletter-dashboard.php (Core) - screen ID will be 'jetpack-newsletter-dashboard'
|
||||
$current_screen = get_current_screen();
|
||||
$is_our_page = (
|
||||
( isset( $_GET['page'] ) && 'jetpack-newsletter-dashboard-wp-admin' === $_GET['page'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
( $current_screen && 'jetpack-newsletter-dashboard' === $current_screen->id )
|
||||
);
|
||||
|
||||
if ( ! $is_our_page ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load build constants
|
||||
$build_constants = require __DIR__ . '/../../constants.php';
|
||||
|
||||
/**
|
||||
* Fires when the jetpack-newsletter-dashboard admin page is initialized so extensions can register routes and menu items.
|
||||
*/
|
||||
do_action( 'jetpack-newsletter-dashboard-wp-admin_init' );
|
||||
|
||||
// Preload REST API data
|
||||
jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_preload_data();
|
||||
|
||||
// Get all registered routes
|
||||
$routes = jetpack_newsletter_get_jetpack_newsletter_dashboard_wp_admin_routes();
|
||||
|
||||
// Get boot module asset file for dependencies
|
||||
$asset_file = __DIR__ . '/../../modules/boot/index.min.asset.php';
|
||||
if ( file_exists( $asset_file ) ) {
|
||||
$asset = require $asset_file;
|
||||
|
||||
// This script serves two purposes:
|
||||
// 1. It ensures all the globals that are made available to the modules are loaded.
|
||||
// 2. It initializes the boot module as an inline script.
|
||||
wp_register_script( 'jetpack-newsletter-dashboard-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true );
|
||||
|
||||
/*
|
||||
* Add inline script to initialize the app using initSinglePage (no menuItems).
|
||||
* The dynamic import is deferred until DOMContentLoaded so that all classic
|
||||
* script dependencies of @wordpress/boot (wp-private-apis, wp-components,
|
||||
* wp-theme, etc.) have finished parsing and executing before the boot module
|
||||
* evaluates. Otherwise, a modulepreloaded @wordpress/boot can win the race
|
||||
* against the classic-script-printing pass on fast CDN-fronted hosts in
|
||||
* Chrome, evaluating before wp.theme.privateApis is defined and throwing
|
||||
* "Cannot unlock an undefined object". See <https://core.trac.wordpress.org/ticket/65103>.
|
||||
*/
|
||||
$init_js_function = <<<'JS'
|
||||
( mountId, routes ) => {
|
||||
const run = async () => {
|
||||
const mod = await import( "@wordpress/boot" );
|
||||
mod.initSinglePage( { mountId, routes } );
|
||||
};
|
||||
if ( document.readyState === "loading" ) {
|
||||
document.addEventListener( "DOMContentLoaded", run );
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}
|
||||
JS;
|
||||
wp_add_inline_script(
|
||||
'jetpack-newsletter-dashboard-wp-admin-prerequisites',
|
||||
sprintf(
|
||||
'( %s )( %s, %s );',
|
||||
$init_js_function,
|
||||
wp_json_encode( 'jetpack-newsletter-dashboard-wp-admin-app', JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
|
||||
wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
|
||||
)
|
||||
);
|
||||
|
||||
// Register prerequisites style by filtering script dependencies to find registered styles
|
||||
$style_dependencies = array_filter(
|
||||
$asset['dependencies'],
|
||||
function ( $handle ) {
|
||||
return wp_style_is( $handle, 'registered' );
|
||||
}
|
||||
);
|
||||
wp_register_style( 'jetpack-newsletter-dashboard-wp-admin-prerequisites', false, $style_dependencies, $asset['version'] );
|
||||
|
||||
// Build dependencies for jetpack-newsletter-dashboard-wp-admin module
|
||||
$boot_dependencies = array(
|
||||
array(
|
||||
'import' => 'static',
|
||||
'id' => '@wordpress/boot',
|
||||
),
|
||||
);
|
||||
|
||||
// Add all registered routes as dependencies
|
||||
foreach ( $routes as $route ) {
|
||||
if ( isset( $route['route_module'] ) ) {
|
||||
$boot_dependencies[] = array(
|
||||
'import' => 'static',
|
||||
'id' => $route['route_module'],
|
||||
);
|
||||
}
|
||||
if ( isset( $route['content_module'] ) ) {
|
||||
$boot_dependencies[] = array(
|
||||
'import' => 'dynamic',
|
||||
'id' => $route['content_module'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the boot script-module dependencies for the
|
||||
* jetpack-newsletter-dashboard-wp-admin page.
|
||||
*
|
||||
* Surfaces extending this page can append entries to the boot
|
||||
* dependency list. Each entry is an array with 'import' (string
|
||||
* 'static' or 'dynamic') and 'id' (script-module handle) keys.
|
||||
*
|
||||
* @param array $boot_dependencies Boot dependencies for the page.
|
||||
*/
|
||||
$boot_dependencies = apply_filters(
|
||||
'jetpack-newsletter-dashboard-wp-admin_boot_dependencies',
|
||||
$boot_dependencies
|
||||
);
|
||||
|
||||
// Dummy script module to ensure dependencies are loaded
|
||||
wp_register_script_module(
|
||||
'jetpack-newsletter-dashboard-wp-admin',
|
||||
$build_constants['build_url'] . 'pages/jetpack-newsletter-dashboard/loader.js',
|
||||
$boot_dependencies
|
||||
);
|
||||
|
||||
// Enqueue the boot scripts and styles
|
||||
wp_enqueue_script( 'jetpack-newsletter-dashboard-wp-admin-prerequisites' );
|
||||
wp_enqueue_script_module( 'jetpack-newsletter-dashboard-wp-admin' );
|
||||
wp_enqueue_style( 'jetpack-newsletter-dashboard-wp-admin-prerequisites' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the jetpack-newsletter-dashboard-wp-admin page.
|
||||
* Call this function from add_menu_page or add_submenu_page.
|
||||
* This renders within the normal WordPress admin interface.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_render_page() {
|
||||
?>
|
||||
<style>
|
||||
/* Critical styles to prevent layout shifts - inlined for immediate application */
|
||||
|
||||
#wpwrap {
|
||||
overflow-y: auto;
|
||||
}
|
||||
body {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* Reset wp-admin padding */
|
||||
#wpcontent {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
#wpbody-content {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Hide legacy admin elements */
|
||||
#wpbody-content > div:not(.boot-layout-container):not(#screen-meta) {
|
||||
display: none;
|
||||
}
|
||||
#wpfooter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Accessibility regions */
|
||||
.a11y-speak-region {
|
||||
inset-inline-start: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/* Admin menu indicators */
|
||||
ul#adminmenu a.wp-has-current-submenu::after,
|
||||
ul#adminmenu > li.current > a.current::after {
|
||||
border-inline-end-color: #fff;
|
||||
}
|
||||
|
||||
/* Media frame fix */
|
||||
.media-frame select.attachment-filters:last-of-type {
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Responsive overflow fix for #wpwrap */
|
||||
@media (min-width: 782px) {
|
||||
#wpwrap {
|
||||
overflow-y: initial;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div id="jetpack-newsletter-dashboard-wp-admin-app" class="boot-layout-container"></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// Hook the enqueue function to admin_enqueue_scripts
|
||||
add_action( 'admin_enqueue_scripts', 'jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_enqueue_scripts' );
|
||||
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
<?php
|
||||
/**
|
||||
* Page: jetpack-newsletter-dashboard
|
||||
* Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
// Global storage for jetpack-newsletter-dashboard routes and menu items
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_routes, $jetpack_newsletter_jetpack_newsletter_dashboard_menu_items;
|
||||
$jetpack_newsletter_jetpack_newsletter_dashboard_routes = array();
|
||||
$jetpack_newsletter_jetpack_newsletter_dashboard_menu_items = array();
|
||||
|
||||
/**
|
||||
* Register a route for the jetpack-newsletter-dashboard page.
|
||||
*
|
||||
* @param string $path Route path (e.g., '/types/$type/edit/$id').
|
||||
* @param string|null $content_module Script module ID for content (stage/inspector).
|
||||
* @param string|null $route_module Script module ID for route lifecycle hooks.
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_newsletter_dashboard_route( $path, $content_module = null, $route_module = null ) {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_routes;
|
||||
|
||||
$route = array( 'path' => $path );
|
||||
if ( ! empty( $content_module ) ) {
|
||||
$route['content_module'] = $content_module;
|
||||
}
|
||||
if ( ! empty( $route_module ) ) {
|
||||
$route['route_module'] = $route_module;
|
||||
}
|
||||
|
||||
$jetpack_newsletter_jetpack_newsletter_dashboard_routes[] = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a menu item for the jetpack-newsletter-dashboard page.
|
||||
*
|
||||
* @param string $id Menu item ID.
|
||||
* @param string $label Display label.
|
||||
* @param string $to Route path to navigate to.
|
||||
* @param string $parent_id Optional. Parent menu item ID.
|
||||
* @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'.
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_newsletter_dashboard_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_menu_items;
|
||||
|
||||
$menu_item = array(
|
||||
'id' => $id,
|
||||
'label' => $label,
|
||||
'to' => $to,
|
||||
);
|
||||
|
||||
if ( ! empty( $parent_id ) ) {
|
||||
$menu_item['parent'] = $parent_id;
|
||||
}
|
||||
|
||||
if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) {
|
||||
$menu_item['parent_type'] = $parent_type;
|
||||
}
|
||||
|
||||
$jetpack_newsletter_jetpack_newsletter_dashboard_menu_items[] = $menu_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered routes for the jetpack-newsletter-dashboard page.
|
||||
*
|
||||
* @return array Array of route objects.
|
||||
*/
|
||||
function jetpack_newsletter_get_jetpack_newsletter_dashboard_routes() {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_routes;
|
||||
return $jetpack_newsletter_jetpack_newsletter_dashboard_routes ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered menu items for the jetpack-newsletter-dashboard page.
|
||||
*
|
||||
* @return array Array of menu item objects.
|
||||
*/
|
||||
function jetpack_newsletter_get_jetpack_newsletter_dashboard_menu_items() {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_menu_items;
|
||||
return $jetpack_newsletter_jetpack_newsletter_dashboard_menu_items ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload REST API data for the jetpack-newsletter-dashboard page.
|
||||
* Automatically called during page rendering.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_newsletter_dashboard_preload_data() {
|
||||
// Define paths to preload - same for all pages
|
||||
// Please also change packages/core-data/src/entities.js when changing this.
|
||||
$preload_paths = array(
|
||||
'/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front',
|
||||
array( '/wp/v2/settings', 'OPTIONS' ),
|
||||
);
|
||||
|
||||
// Use rest_preload_api_request to gather the preloaded data
|
||||
$preload_data = array_reduce(
|
||||
$preload_paths,
|
||||
'rest_preload_api_request',
|
||||
array()
|
||||
);
|
||||
|
||||
// Register the preloading middleware with wp-api-fetch
|
||||
wp_add_inline_script(
|
||||
'wp-api-fetch',
|
||||
sprintf(
|
||||
'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );',
|
||||
wp_json_encode( $preload_data )
|
||||
),
|
||||
'after'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the jetpack-newsletter-dashboard page.
|
||||
* Call this function from add_menu_page or add_submenu_page.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_newsletter_dashboard_render_page() {
|
||||
// Load build constants
|
||||
$build_constants = require __DIR__ . '/../../constants.php';
|
||||
|
||||
// Set current screen
|
||||
set_current_screen();
|
||||
|
||||
// Remove unwanted deprecated handler
|
||||
remove_action( 'admin_head', 'wp_admin_bar_header' );
|
||||
|
||||
// Remove unwanted scripts and styles that were enqueued during `admin_init`
|
||||
foreach ( wp_scripts()->queue as $script ) {
|
||||
wp_dequeue_script( $script );
|
||||
}
|
||||
foreach ( wp_styles()->queue as $style ) {
|
||||
wp_dequeue_style( $style );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when the jetpack-newsletter-dashboard page is initialized so extensions can register routes and menu items.
|
||||
*/
|
||||
do_action( 'jetpack-newsletter-dashboard_init' );
|
||||
|
||||
// Enqueue command palette assets for boot-based pages
|
||||
if ( function_exists( 'wp_enqueue_command_palette_assets' ) ) {
|
||||
wp_enqueue_command_palette_assets();
|
||||
}
|
||||
|
||||
// Preload REST API data
|
||||
jetpack_newsletter_jetpack_newsletter_dashboard_preload_data();
|
||||
|
||||
// Get all registered routes and menu items
|
||||
$menu_items = jetpack_newsletter_get_jetpack_newsletter_dashboard_menu_items();
|
||||
$routes = jetpack_newsletter_get_jetpack_newsletter_dashboard_routes();
|
||||
|
||||
// Get boot module asset file for dependencies
|
||||
$asset_file = __DIR__ . '/../../modules/boot/index.min.asset.php';
|
||||
if ( file_exists( $asset_file ) ) {
|
||||
$asset = require $asset_file;
|
||||
|
||||
// This script serves two purposes:
|
||||
// 1. It ensures all the globals that are made available to the modules are loaded.
|
||||
// 2. It initializes the boot module as an inline script.
|
||||
wp_register_script( 'jetpack-newsletter-dashboard-prerequisites', '', $asset['dependencies'], $asset['version'], true );
|
||||
|
||||
// Add inline script to initialize the app
|
||||
$init_modules = [];
|
||||
wp_add_inline_script(
|
||||
'jetpack-newsletter-dashboard-prerequisites',
|
||||
sprintf(
|
||||
'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s, initModules: %s, dashboardLink: "%s"}));',
|
||||
'jetpack-newsletter-dashboard-app',
|
||||
wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
|
||||
wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
|
||||
wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
|
||||
esc_url( admin_url( '/' ) )
|
||||
)
|
||||
);
|
||||
|
||||
// Register prerequisites style by filtering script dependencies to find registered styles
|
||||
$style_dependencies = array_filter(
|
||||
$asset['dependencies'],
|
||||
function ( $handle ) {
|
||||
return wp_style_is( $handle, 'registered' );
|
||||
}
|
||||
);
|
||||
wp_register_style( 'jetpack-newsletter-dashboard-prerequisites', false, $style_dependencies, $asset['version'] );
|
||||
|
||||
// Build dependencies for jetpack-newsletter-dashboard module
|
||||
$boot_dependencies = array(
|
||||
array(
|
||||
'import' => 'static',
|
||||
'id' => '@wordpress/boot',
|
||||
),
|
||||
);
|
||||
|
||||
// Add init modules as static dependencies
|
||||
// No init modules configured
|
||||
|
||||
// Add all registered routes as dependencies
|
||||
foreach ( $routes as $route ) {
|
||||
if ( isset( $route['route_module'] ) ) {
|
||||
$boot_dependencies[] = array(
|
||||
'import' => 'static',
|
||||
'id' => $route['route_module'],
|
||||
);
|
||||
}
|
||||
if ( isset( $route['content_module'] ) ) {
|
||||
$boot_dependencies[] = array(
|
||||
'import' => 'dynamic',
|
||||
'id' => $route['content_module'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the boot script-module dependencies for the
|
||||
* jetpack-newsletter-dashboard page.
|
||||
*
|
||||
* Surfaces extending this page can append entries to the boot
|
||||
* dependency list. Each entry is an array with 'import' (string
|
||||
* 'static' or 'dynamic') and 'id' (script-module handle) keys.
|
||||
*
|
||||
* @param array $boot_dependencies Boot dependencies for the page.
|
||||
*/
|
||||
$boot_dependencies = apply_filters(
|
||||
'jetpack-newsletter-dashboard_boot_dependencies',
|
||||
$boot_dependencies
|
||||
);
|
||||
|
||||
// Dummy script module to ensure dependencies are loaded
|
||||
wp_register_script_module(
|
||||
'jetpack-newsletter-dashboard',
|
||||
$build_constants['build_url'] . 'pages/jetpack-newsletter-dashboard/loader.js',
|
||||
$boot_dependencies
|
||||
);
|
||||
|
||||
// Enqueue the boot scripts and styles
|
||||
wp_enqueue_script( 'jetpack-newsletter-dashboard-prerequisites' );
|
||||
wp_enqueue_script_module( 'jetpack-newsletter-dashboard' );
|
||||
wp_enqueue_style( 'jetpack-newsletter-dashboard-prerequisites' );
|
||||
}
|
||||
|
||||
// Output the HTML
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html <?php language_attributes(); ?>>
|
||||
<head>
|
||||
<meta charset="<?php bloginfo( 'charset' ); ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?php echo esc_html( get_admin_page_title() ); ?></title>
|
||||
<style>
|
||||
html {
|
||||
background: #f1f1f1;
|
||||
color: #444;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
#wpadminbar { display: none; }
|
||||
</style>
|
||||
<?php
|
||||
global $hook_suffix;
|
||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$hook_suffix = 'jetpack-newsletter-dashboard';
|
||||
|
||||
// BEGIN see wp-admin/admin-header.php
|
||||
print_admin_styles();
|
||||
print_head_scripts();
|
||||
|
||||
/** This action is documented in wp-admin/admin-header.php */
|
||||
do_action( "admin_head-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
|
||||
/** This action is documented in wp-admin/admin-header.php */
|
||||
do_action( 'admin_head' );
|
||||
// END see wp-admin/admin-header.php
|
||||
?>
|
||||
</head>
|
||||
<body class="jetpack-newsletter-dashboard">
|
||||
<div id="jetpack-newsletter-dashboard-app" style="height: 100vh; box-sizing: border-box;"></div>
|
||||
<?php
|
||||
// BEGIN see wp-admin/admin-footer.php
|
||||
|
||||
/** This action is documented in wp-admin/admin-footer.php */
|
||||
do_action( 'admin_footer', '' );
|
||||
|
||||
// Print import map first so it's available for inline scripts
|
||||
wp_script_modules()->print_import_map();
|
||||
print_footer_scripts();
|
||||
wp_script_modules()->print_enqueued_script_modules();
|
||||
wp_script_modules()->print_script_module_preloads();
|
||||
wp_script_modules()->print_script_module_data();
|
||||
|
||||
/** This action is documented in wp-admin/admin-footer.php */
|
||||
do_action( "admin_footer-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
// END see wp-admin/admin-footer.php
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept admin_init to render the page early.
|
||||
* This bypasses the default WordPress admin template.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_newsletter_dashboard_intercept_render() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( isset( $_GET['page'] ) && 'jetpack-newsletter-dashboard' === $_GET['page'] ) {
|
||||
jetpack_newsletter_jetpack_newsletter_dashboard_render_page();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Hook the interceptor to admin_init
|
||||
add_action( 'admin_init', 'jetpack_newsletter_jetpack_newsletter_dashboard_intercept_render' );
|
||||
+1
@@ -0,0 +1 @@
|
||||
// Empty module loader for page dependencies
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
<?php
|
||||
/**
|
||||
* Page: jetpack-subscribers-announcement (wp-admin integrated)
|
||||
* Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* This version integrates with the standard WordPress admin interface,
|
||||
* keeping the wp-admin sidebar and scripts/styles intact.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
// Global storage for jetpack-subscribers-announcement routes and menu items
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_routes, $jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_menu_items;
|
||||
$jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_routes = array();
|
||||
$jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_menu_items = array();
|
||||
|
||||
/**
|
||||
* Register a route for the jetpack-subscribers-announcement-wp-admin page.
|
||||
*
|
||||
* @param string $path Route path (e.g., '/types/$type/edit/$id').
|
||||
* @param string|null $content_module Script module ID for content (stage/inspector).
|
||||
* @param string|null $route_module Script module ID for route lifecycle hooks.
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_subscribers_announcement_wp_admin_route( $path, $content_module = null, $route_module = null ) {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_routes;
|
||||
|
||||
$route = array( 'path' => $path );
|
||||
if ( ! empty( $content_module ) ) {
|
||||
$route['content_module'] = $content_module;
|
||||
}
|
||||
if ( ! empty( $route_module ) ) {
|
||||
$route['route_module'] = $route_module;
|
||||
}
|
||||
|
||||
$jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_routes[] = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a menu item for the jetpack-subscribers-announcement-wp-admin page.
|
||||
* Note: Menu items are registered but not displayed in single-page mode.
|
||||
*
|
||||
* @param string $id Menu item ID.
|
||||
* @param string $label Display label.
|
||||
* @param string $to Route path to navigate to.
|
||||
* @param string $parent_id Optional. Parent menu item ID.
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_subscribers_announcement_wp_admin_menu_item( $id, $label, $to, $parent_id = '' ) {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_menu_items;
|
||||
|
||||
$menu_item = array(
|
||||
'id' => $id,
|
||||
'label' => $label,
|
||||
'to' => $to,
|
||||
);
|
||||
|
||||
if ( ! empty( $parent_id ) ) {
|
||||
$menu_item['parent'] = $parent_id;
|
||||
}
|
||||
|
||||
$jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_menu_items[] = $menu_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered routes for the jetpack-subscribers-announcement-wp-admin page.
|
||||
*
|
||||
* @return array Array of route objects.
|
||||
*/
|
||||
function jetpack_newsletter_get_jetpack_subscribers_announcement_wp_admin_routes() {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_routes;
|
||||
return $jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_routes ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered menu items for the jetpack-subscribers-announcement-wp-admin page.
|
||||
*
|
||||
* @return array Array of menu item objects.
|
||||
*/
|
||||
function jetpack_newsletter_get_jetpack_subscribers_announcement_wp_admin_menu_items() {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_menu_items;
|
||||
return $jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_menu_items ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload REST API data for the jetpack-subscribers-announcement-wp-admin page.
|
||||
* Automatically called during page rendering.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_preload_data() {
|
||||
// Define paths to preload - same for all pages
|
||||
// Please also change packages/core-data/src/entities.js when changing this.
|
||||
$preload_paths = array(
|
||||
'/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front',
|
||||
array( '/wp/v2/settings', 'OPTIONS' ),
|
||||
);
|
||||
|
||||
// Use rest_preload_api_request to gather the preloaded data
|
||||
$preload_data = array_reduce(
|
||||
$preload_paths,
|
||||
'rest_preload_api_request',
|
||||
array()
|
||||
);
|
||||
|
||||
// Register the preloading middleware with wp-api-fetch
|
||||
wp_add_inline_script(
|
||||
'wp-api-fetch',
|
||||
sprintf(
|
||||
'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );',
|
||||
wp_json_encode( $preload_data )
|
||||
),
|
||||
'after'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles for the jetpack-subscribers-announcement-wp-admin page.
|
||||
* Hooked to admin_enqueue_scripts.
|
||||
*
|
||||
* @param string $hook_suffix The current admin page.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_enqueue_scripts( $hook_suffix ) {
|
||||
// Check all possible ways this page can be accessed:
|
||||
// 1. Menu page via admin.php?page=jetpack-subscribers-announcement-wp-admin (plugin)
|
||||
// 2. Direct file via jetpack-subscribers-announcement.php (Core) - screen ID will be 'jetpack-subscribers-announcement'
|
||||
$current_screen = get_current_screen();
|
||||
$is_our_page = (
|
||||
( isset( $_GET['page'] ) && 'jetpack-subscribers-announcement-wp-admin' === $_GET['page'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
( $current_screen && 'jetpack-subscribers-announcement' === $current_screen->id )
|
||||
);
|
||||
|
||||
if ( ! $is_our_page ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load build constants
|
||||
$build_constants = require __DIR__ . '/../../constants.php';
|
||||
|
||||
/**
|
||||
* Fires when the jetpack-subscribers-announcement admin page is initialized so extensions can register routes and menu items.
|
||||
*/
|
||||
do_action( 'jetpack-subscribers-announcement-wp-admin_init' );
|
||||
|
||||
// Preload REST API data
|
||||
jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_preload_data();
|
||||
|
||||
// Get all registered routes
|
||||
$routes = jetpack_newsletter_get_jetpack_subscribers_announcement_wp_admin_routes();
|
||||
|
||||
// Get boot module asset file for dependencies
|
||||
$asset_file = __DIR__ . '/../../modules/boot/index.min.asset.php';
|
||||
if ( file_exists( $asset_file ) ) {
|
||||
$asset = require $asset_file;
|
||||
|
||||
// This script serves two purposes:
|
||||
// 1. It ensures all the globals that are made available to the modules are loaded.
|
||||
// 2. It initializes the boot module as an inline script.
|
||||
wp_register_script( 'jetpack-subscribers-announcement-wp-admin-prerequisites', '', $asset['dependencies'], $asset['version'], true );
|
||||
|
||||
/*
|
||||
* Add inline script to initialize the app using initSinglePage (no menuItems).
|
||||
* The dynamic import is deferred until DOMContentLoaded so that all classic
|
||||
* script dependencies of @wordpress/boot (wp-private-apis, wp-components,
|
||||
* wp-theme, etc.) have finished parsing and executing before the boot module
|
||||
* evaluates. Otherwise, a modulepreloaded @wordpress/boot can win the race
|
||||
* against the classic-script-printing pass on fast CDN-fronted hosts in
|
||||
* Chrome, evaluating before wp.theme.privateApis is defined and throwing
|
||||
* "Cannot unlock an undefined object". See <https://core.trac.wordpress.org/ticket/65103>.
|
||||
*/
|
||||
$init_js_function = <<<'JS'
|
||||
( mountId, routes ) => {
|
||||
const run = async () => {
|
||||
const mod = await import( "@wordpress/boot" );
|
||||
mod.initSinglePage( { mountId, routes } );
|
||||
};
|
||||
if ( document.readyState === "loading" ) {
|
||||
document.addEventListener( "DOMContentLoaded", run );
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}
|
||||
JS;
|
||||
wp_add_inline_script(
|
||||
'jetpack-subscribers-announcement-wp-admin-prerequisites',
|
||||
sprintf(
|
||||
'( %s )( %s, %s );',
|
||||
$init_js_function,
|
||||
wp_json_encode( 'jetpack-subscribers-announcement-wp-admin-app', JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
|
||||
wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES )
|
||||
)
|
||||
);
|
||||
|
||||
// Register prerequisites style by filtering script dependencies to find registered styles
|
||||
$style_dependencies = array_filter(
|
||||
$asset['dependencies'],
|
||||
function ( $handle ) {
|
||||
return wp_style_is( $handle, 'registered' );
|
||||
}
|
||||
);
|
||||
wp_register_style( 'jetpack-subscribers-announcement-wp-admin-prerequisites', false, $style_dependencies, $asset['version'] );
|
||||
|
||||
// Build dependencies for jetpack-subscribers-announcement-wp-admin module
|
||||
$boot_dependencies = array(
|
||||
array(
|
||||
'import' => 'static',
|
||||
'id' => '@wordpress/boot',
|
||||
),
|
||||
);
|
||||
|
||||
// Add all registered routes as dependencies
|
||||
foreach ( $routes as $route ) {
|
||||
if ( isset( $route['route_module'] ) ) {
|
||||
$boot_dependencies[] = array(
|
||||
'import' => 'static',
|
||||
'id' => $route['route_module'],
|
||||
);
|
||||
}
|
||||
if ( isset( $route['content_module'] ) ) {
|
||||
$boot_dependencies[] = array(
|
||||
'import' => 'dynamic',
|
||||
'id' => $route['content_module'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the boot script-module dependencies for the
|
||||
* jetpack-subscribers-announcement-wp-admin page.
|
||||
*
|
||||
* Surfaces extending this page can append entries to the boot
|
||||
* dependency list. Each entry is an array with 'import' (string
|
||||
* 'static' or 'dynamic') and 'id' (script-module handle) keys.
|
||||
*
|
||||
* @param array $boot_dependencies Boot dependencies for the page.
|
||||
*/
|
||||
$boot_dependencies = apply_filters(
|
||||
'jetpack-subscribers-announcement-wp-admin_boot_dependencies',
|
||||
$boot_dependencies
|
||||
);
|
||||
|
||||
// Dummy script module to ensure dependencies are loaded
|
||||
wp_register_script_module(
|
||||
'jetpack-subscribers-announcement-wp-admin',
|
||||
$build_constants['build_url'] . 'pages/jetpack-subscribers-announcement/loader.js',
|
||||
$boot_dependencies
|
||||
);
|
||||
|
||||
// Enqueue the boot scripts and styles
|
||||
wp_enqueue_script( 'jetpack-subscribers-announcement-wp-admin-prerequisites' );
|
||||
wp_enqueue_script_module( 'jetpack-subscribers-announcement-wp-admin' );
|
||||
wp_enqueue_style( 'jetpack-subscribers-announcement-wp-admin-prerequisites' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the jetpack-subscribers-announcement-wp-admin page.
|
||||
* Call this function from add_menu_page or add_submenu_page.
|
||||
* This renders within the normal WordPress admin interface.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_render_page() {
|
||||
?>
|
||||
<style>
|
||||
/* Critical styles to prevent layout shifts - inlined for immediate application */
|
||||
|
||||
#wpwrap {
|
||||
overflow-y: auto;
|
||||
}
|
||||
body {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* Reset wp-admin padding */
|
||||
#wpcontent {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
#wpbody-content {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Hide legacy admin elements */
|
||||
#wpbody-content > div:not(.boot-layout-container):not(#screen-meta) {
|
||||
display: none;
|
||||
}
|
||||
#wpfooter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Accessibility regions */
|
||||
.a11y-speak-region {
|
||||
inset-inline-start: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/* Admin menu indicators */
|
||||
ul#adminmenu a.wp-has-current-submenu::after,
|
||||
ul#adminmenu > li.current > a.current::after {
|
||||
border-inline-end-color: #fff;
|
||||
}
|
||||
|
||||
/* Media frame fix */
|
||||
.media-frame select.attachment-filters:last-of-type {
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Responsive overflow fix for #wpwrap */
|
||||
@media (min-width: 782px) {
|
||||
#wpwrap {
|
||||
overflow-y: initial;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div id="jetpack-subscribers-announcement-wp-admin-app" class="boot-layout-container"></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// Hook the enqueue function to admin_enqueue_scripts
|
||||
add_action( 'admin_enqueue_scripts', 'jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_enqueue_scripts' );
|
||||
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
<?php
|
||||
/**
|
||||
* Page: jetpack-subscribers-announcement
|
||||
* Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
// Global storage for jetpack-subscribers-announcement routes and menu items
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_routes, $jetpack_newsletter_jetpack_subscribers_announcement_menu_items;
|
||||
$jetpack_newsletter_jetpack_subscribers_announcement_routes = array();
|
||||
$jetpack_newsletter_jetpack_subscribers_announcement_menu_items = array();
|
||||
|
||||
/**
|
||||
* Register a route for the jetpack-subscribers-announcement page.
|
||||
*
|
||||
* @param string $path Route path (e.g., '/types/$type/edit/$id').
|
||||
* @param string|null $content_module Script module ID for content (stage/inspector).
|
||||
* @param string|null $route_module Script module ID for route lifecycle hooks.
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_subscribers_announcement_route( $path, $content_module = null, $route_module = null ) {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_routes;
|
||||
|
||||
$route = array( 'path' => $path );
|
||||
if ( ! empty( $content_module ) ) {
|
||||
$route['content_module'] = $content_module;
|
||||
}
|
||||
if ( ! empty( $route_module ) ) {
|
||||
$route['route_module'] = $route_module;
|
||||
}
|
||||
|
||||
$jetpack_newsletter_jetpack_subscribers_announcement_routes[] = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a menu item for the jetpack-subscribers-announcement page.
|
||||
*
|
||||
* @param string $id Menu item ID.
|
||||
* @param string $label Display label.
|
||||
* @param string $to Route path to navigate to.
|
||||
* @param string $parent_id Optional. Parent menu item ID.
|
||||
* @param string $parent_type Optional. Parent type: 'drilldown' or 'dropdown'.
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_subscribers_announcement_menu_item( $id, $label, $to, $parent_id = '', $parent_type = '' ) {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_menu_items;
|
||||
|
||||
$menu_item = array(
|
||||
'id' => $id,
|
||||
'label' => $label,
|
||||
'to' => $to,
|
||||
);
|
||||
|
||||
if ( ! empty( $parent_id ) ) {
|
||||
$menu_item['parent'] = $parent_id;
|
||||
}
|
||||
|
||||
if ( ! empty( $parent_type ) && in_array( $parent_type, array( 'drilldown', 'dropdown' ), true ) ) {
|
||||
$menu_item['parent_type'] = $parent_type;
|
||||
}
|
||||
|
||||
$jetpack_newsletter_jetpack_subscribers_announcement_menu_items[] = $menu_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered routes for the jetpack-subscribers-announcement page.
|
||||
*
|
||||
* @return array Array of route objects.
|
||||
*/
|
||||
function jetpack_newsletter_get_jetpack_subscribers_announcement_routes() {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_routes;
|
||||
return $jetpack_newsletter_jetpack_subscribers_announcement_routes ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered menu items for the jetpack-subscribers-announcement page.
|
||||
*
|
||||
* @return array Array of menu item objects.
|
||||
*/
|
||||
function jetpack_newsletter_get_jetpack_subscribers_announcement_menu_items() {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_menu_items;
|
||||
return $jetpack_newsletter_jetpack_subscribers_announcement_menu_items ?? array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload REST API data for the jetpack-subscribers-announcement page.
|
||||
* Automatically called during page rendering.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_subscribers_announcement_preload_data() {
|
||||
// Define paths to preload - same for all pages
|
||||
// Please also change packages/core-data/src/entities.js when changing this.
|
||||
$preload_paths = array(
|
||||
'/?_fields=description,gmt_offset,home,image_sizes,image_size_threshold,name,site_icon,site_icon_url,site_logo,timezone_string,url,page_for_posts,page_on_front,show_on_front',
|
||||
array( '/wp/v2/settings', 'OPTIONS' ),
|
||||
);
|
||||
|
||||
// Use rest_preload_api_request to gather the preloaded data
|
||||
$preload_data = array_reduce(
|
||||
$preload_paths,
|
||||
'rest_preload_api_request',
|
||||
array()
|
||||
);
|
||||
|
||||
// Register the preloading middleware with wp-api-fetch
|
||||
wp_add_inline_script(
|
||||
'wp-api-fetch',
|
||||
sprintf(
|
||||
'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );',
|
||||
wp_json_encode( $preload_data )
|
||||
),
|
||||
'after'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the jetpack-subscribers-announcement page.
|
||||
* Call this function from add_menu_page or add_submenu_page.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_subscribers_announcement_render_page() {
|
||||
// Load build constants
|
||||
$build_constants = require __DIR__ . '/../../constants.php';
|
||||
|
||||
// Set current screen
|
||||
set_current_screen();
|
||||
|
||||
// Remove unwanted deprecated handler
|
||||
remove_action( 'admin_head', 'wp_admin_bar_header' );
|
||||
|
||||
// Remove unwanted scripts and styles that were enqueued during `admin_init`
|
||||
foreach ( wp_scripts()->queue as $script ) {
|
||||
wp_dequeue_script( $script );
|
||||
}
|
||||
foreach ( wp_styles()->queue as $style ) {
|
||||
wp_dequeue_style( $style );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when the jetpack-subscribers-announcement page is initialized so extensions can register routes and menu items.
|
||||
*/
|
||||
do_action( 'jetpack-subscribers-announcement_init' );
|
||||
|
||||
// Enqueue command palette assets for boot-based pages
|
||||
if ( function_exists( 'wp_enqueue_command_palette_assets' ) ) {
|
||||
wp_enqueue_command_palette_assets();
|
||||
}
|
||||
|
||||
// Preload REST API data
|
||||
jetpack_newsletter_jetpack_subscribers_announcement_preload_data();
|
||||
|
||||
// Get all registered routes and menu items
|
||||
$menu_items = jetpack_newsletter_get_jetpack_subscribers_announcement_menu_items();
|
||||
$routes = jetpack_newsletter_get_jetpack_subscribers_announcement_routes();
|
||||
|
||||
// Get boot module asset file for dependencies
|
||||
$asset_file = __DIR__ . '/../../modules/boot/index.min.asset.php';
|
||||
if ( file_exists( $asset_file ) ) {
|
||||
$asset = require $asset_file;
|
||||
|
||||
// This script serves two purposes:
|
||||
// 1. It ensures all the globals that are made available to the modules are loaded.
|
||||
// 2. It initializes the boot module as an inline script.
|
||||
wp_register_script( 'jetpack-subscribers-announcement-prerequisites', '', $asset['dependencies'], $asset['version'], true );
|
||||
|
||||
// Add inline script to initialize the app
|
||||
$init_modules = [];
|
||||
wp_add_inline_script(
|
||||
'jetpack-subscribers-announcement-prerequisites',
|
||||
sprintf(
|
||||
'import("@wordpress/boot").then(mod => mod.init({mountId: "%s", menuItems: %s, routes: %s, initModules: %s, dashboardLink: "%s"}));',
|
||||
'jetpack-subscribers-announcement-app',
|
||||
wp_json_encode( $menu_items, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
|
||||
wp_json_encode( $routes, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
|
||||
wp_json_encode( $init_modules, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ),
|
||||
esc_url( admin_url( '/' ) )
|
||||
)
|
||||
);
|
||||
|
||||
// Register prerequisites style by filtering script dependencies to find registered styles
|
||||
$style_dependencies = array_filter(
|
||||
$asset['dependencies'],
|
||||
function ( $handle ) {
|
||||
return wp_style_is( $handle, 'registered' );
|
||||
}
|
||||
);
|
||||
wp_register_style( 'jetpack-subscribers-announcement-prerequisites', false, $style_dependencies, $asset['version'] );
|
||||
|
||||
// Build dependencies for jetpack-subscribers-announcement module
|
||||
$boot_dependencies = array(
|
||||
array(
|
||||
'import' => 'static',
|
||||
'id' => '@wordpress/boot',
|
||||
),
|
||||
);
|
||||
|
||||
// Add init modules as static dependencies
|
||||
// No init modules configured
|
||||
|
||||
// Add all registered routes as dependencies
|
||||
foreach ( $routes as $route ) {
|
||||
if ( isset( $route['route_module'] ) ) {
|
||||
$boot_dependencies[] = array(
|
||||
'import' => 'static',
|
||||
'id' => $route['route_module'],
|
||||
);
|
||||
}
|
||||
if ( isset( $route['content_module'] ) ) {
|
||||
$boot_dependencies[] = array(
|
||||
'import' => 'dynamic',
|
||||
'id' => $route['content_module'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the boot script-module dependencies for the
|
||||
* jetpack-subscribers-announcement page.
|
||||
*
|
||||
* Surfaces extending this page can append entries to the boot
|
||||
* dependency list. Each entry is an array with 'import' (string
|
||||
* 'static' or 'dynamic') and 'id' (script-module handle) keys.
|
||||
*
|
||||
* @param array $boot_dependencies Boot dependencies for the page.
|
||||
*/
|
||||
$boot_dependencies = apply_filters(
|
||||
'jetpack-subscribers-announcement_boot_dependencies',
|
||||
$boot_dependencies
|
||||
);
|
||||
|
||||
// Dummy script module to ensure dependencies are loaded
|
||||
wp_register_script_module(
|
||||
'jetpack-subscribers-announcement',
|
||||
$build_constants['build_url'] . 'pages/jetpack-subscribers-announcement/loader.js',
|
||||
$boot_dependencies
|
||||
);
|
||||
|
||||
// Enqueue the boot scripts and styles
|
||||
wp_enqueue_script( 'jetpack-subscribers-announcement-prerequisites' );
|
||||
wp_enqueue_script_module( 'jetpack-subscribers-announcement' );
|
||||
wp_enqueue_style( 'jetpack-subscribers-announcement-prerequisites' );
|
||||
}
|
||||
|
||||
// Output the HTML
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html <?php language_attributes(); ?>>
|
||||
<head>
|
||||
<meta charset="<?php bloginfo( 'charset' ); ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?php echo esc_html( get_admin_page_title() ); ?></title>
|
||||
<style>
|
||||
html {
|
||||
background: #f1f1f1;
|
||||
color: #444;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
#wpadminbar { display: none; }
|
||||
</style>
|
||||
<?php
|
||||
global $hook_suffix;
|
||||
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
$hook_suffix = 'jetpack-subscribers-announcement';
|
||||
|
||||
// BEGIN see wp-admin/admin-header.php
|
||||
print_admin_styles();
|
||||
print_head_scripts();
|
||||
|
||||
/** This action is documented in wp-admin/admin-header.php */
|
||||
do_action( "admin_head-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
|
||||
/** This action is documented in wp-admin/admin-header.php */
|
||||
do_action( 'admin_head' );
|
||||
// END see wp-admin/admin-header.php
|
||||
?>
|
||||
</head>
|
||||
<body class="jetpack-subscribers-announcement">
|
||||
<div id="jetpack-subscribers-announcement-app" style="height: 100vh; box-sizing: border-box;"></div>
|
||||
<?php
|
||||
// BEGIN see wp-admin/admin-footer.php
|
||||
|
||||
/** This action is documented in wp-admin/admin-footer.php */
|
||||
do_action( 'admin_footer', '' );
|
||||
|
||||
// Print import map first so it's available for inline scripts
|
||||
wp_script_modules()->print_import_map();
|
||||
print_footer_scripts();
|
||||
wp_script_modules()->print_enqueued_script_modules();
|
||||
wp_script_modules()->print_script_module_preloads();
|
||||
wp_script_modules()->print_script_module_data();
|
||||
|
||||
/** This action is documented in wp-admin/admin-footer.php */
|
||||
do_action( "admin_footer-{$hook_suffix}" ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
// END see wp-admin/admin-footer.php
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept admin_init to render the page early.
|
||||
* This bypasses the default WordPress admin template.
|
||||
*/
|
||||
function jetpack_newsletter_jetpack_subscribers_announcement_intercept_render() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( isset( $_GET['page'] ) && 'jetpack-subscribers-announcement' === $_GET['page'] ) {
|
||||
jetpack_newsletter_jetpack_subscribers_announcement_render_page();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Hook the interceptor to admin_init
|
||||
add_action( 'admin_init', 'jetpack_newsletter_jetpack_subscribers_announcement_intercept_render' );
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php return array('dependencies' => array(), 'version' => '6569e23e23e0349e92b7');
|
||||
+1
@@ -0,0 +1 @@
|
||||
@media(max-width:360px){#wpadminbar .wp-admin-bar-reader{display:none!important}}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-item{padding:0}@media(max-width:782px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-item{display:flex;justify-content:center}}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon{align-items:center;display:flex;height:100%;margin:0;padding:0 6px}@media(max-width:782px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon{justify-content:center;padding:0}}@media(max-width:480px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon{max-width:40px}}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon:before{background-color:currentColor;content:"";display:flex;height:20px;margin:0;mask-image:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="11" fill="none" viewBox="0 0 24 11"><path fill="%23000" d="m22.875 4.607-.055-.25C22.335 2.175 20.276.584 17.924.584c-1.271 0-2.488.447-3.412 1.264-.32.283-.568.593-.769.894a9.5 9.5 0 0 0-1.711-.18h-.027c-.586 0-1.163.085-1.73.188a4.7 4.7 0 0 0-.777-.91C8.573 1.013 7.356.575 6.085.575 3.724.584 1.665 2.174 1.18 4.358l-.055.249H0v1.4h1.125l.055.25c.458 2.183 2.517 3.773 4.895 3.773 2.764 0 5.005-2.114 5.005-4.718 0 0 .01-.585-.228-1.341a9 9 0 0 1 1.153-.095c.366 0 .75.035 1.143.086-.247.765-.229 1.35-.229 1.35 0 2.604 2.242 4.718 5.005 4.718 2.38 0 4.438-1.581 4.896-3.773l.055-.25H24v-1.4zm-16.8 4.022c-1.94 0-3.522-1.487-3.522-3.326 0-1.84 1.583-3.326 3.522-3.326 1.94 0 3.523 1.487 3.523 3.326 0 1.84-1.583 3.326-3.523 3.326m11.85 0c-1.94 0-3.523-1.487-3.523-3.326 0-1.84 1.583-3.326 3.522-3.326 1.94 0 3.523 1.487 3.523 3.326 0 1.84-1.583 3.326-3.523 3.326"/></svg>');mask-position:center;mask-repeat:no-repeat;width:24px}@media(max-width:782px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon:before{height:36px;margin:0;mask-size:contain;width:36px}}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-label{padding-inline-end:8px}@media(max-width:782px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-label{display:none}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader{display:block}}@media(max-width:480px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader{width:46px}}
|
||||
+1
@@ -0,0 +1 @@
|
||||
@media(max-width:360px){#wpadminbar .wp-admin-bar-reader{display:none!important}}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-item{padding:0}@media(max-width:782px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-item{display:flex;justify-content:center}}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon{align-items:center;display:flex;height:100%;margin:0;padding:0 6px}@media(max-width:782px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon{justify-content:center;padding:0}}@media(max-width:480px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon{max-width:40px}}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon:before{background-color:currentColor;content:"";display:flex;height:20px;margin:0;mask-image:url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="11" fill="none" viewBox="0 0 24 11"><path fill="%23000" d="m22.875 4.607-.055-.25C22.335 2.175 20.276.584 17.924.584c-1.271 0-2.488.447-3.412 1.264-.32.283-.568.593-.769.894a9.5 9.5 0 0 0-1.711-.18h-.027c-.586 0-1.163.085-1.73.188a4.7 4.7 0 0 0-.777-.91C8.573 1.013 7.356.575 6.085.575 3.724.584 1.665 2.174 1.18 4.358l-.055.249H0v1.4h1.125l.055.25c.458 2.183 2.517 3.773 4.895 3.773 2.764 0 5.005-2.114 5.005-4.718 0 0 .01-.585-.228-1.341a9 9 0 0 1 1.153-.095c.366 0 .75.035 1.143.086-.247.765-.229 1.35-.229 1.35 0 2.604 2.242 4.718 5.005 4.718 2.38 0 4.438-1.581 4.896-3.773l.055-.25H24v-1.4zm-16.8 4.022c-1.94 0-3.522-1.487-3.522-3.326 0-1.84 1.583-3.326 3.522-3.326 1.94 0 3.523 1.487 3.523 3.326 0 1.84-1.583 3.326-3.523 3.326m11.85 0c-1.94 0-3.523-1.487-3.523-3.326 0-1.84 1.583-3.326 3.522-3.326 1.94 0 3.523 1.487 3.523 3.326 0 1.84-1.583 3.326-3.523 3.326"/></svg>');mask-position:center;mask-repeat:no-repeat;width:24px}@media(max-width:782px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-icon:before{height:36px;margin:0;mask-size:contain;width:36px}}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-label{padding-inline-end:8px}@media(max-width:782px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader .ab-label{display:none}#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader{display:block}}@media(max-width:480px){#wpadminbar #wp-admin-bar-top-secondary #wp-admin-bar-reader{width:46px}}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* Routes registration - Auto-generated by build process.
|
||||
* Registers all routes on their respective page init hooks.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
// Load routes registry
|
||||
$routes_file = __DIR__ . '/routes/registry.php';
|
||||
if ( ! file_exists( $routes_file ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$routes = require $routes_file;
|
||||
|
||||
// Group routes by page and store in globals for page-specific functions
|
||||
$routes_by_page = array();
|
||||
foreach ( $routes as $route ) {
|
||||
$page_slug = $route['page'];
|
||||
if ( ! isset( $routes_by_page[ $page_slug ] ) ) {
|
||||
$routes_by_page[ $page_slug ] = array();
|
||||
}
|
||||
$routes_by_page[ $page_slug ][] = $route;
|
||||
}
|
||||
|
||||
// Store routes data in globals for each page
|
||||
foreach ( $routes_by_page as $page_slug => $page_routes ) {
|
||||
$page_slug_underscore = str_replace( '-', '_', $page_slug );
|
||||
$global_name = 'jetpack_newsletter_' . $page_slug_underscore . '_routes_data';
|
||||
$GLOBALS[ $global_name ] = $page_routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic helper function to register routes for a page.
|
||||
*
|
||||
* @param array $page_routes Array of route data for the page.
|
||||
* @param string $register_function_name Name of the function to call for registering each route.
|
||||
*/
|
||||
function jetpack_newsletter_register_page_routes( $page_routes, $register_function_name ) {
|
||||
// Load build constants
|
||||
$build_constants = require __DIR__ . '/constants.php';
|
||||
|
||||
foreach ( $page_routes as $route ) {
|
||||
$content_handle = null;
|
||||
$route_handle = null;
|
||||
|
||||
// Register content module if exists
|
||||
if ( $route['has_content'] ) {
|
||||
$content_asset_path = __DIR__ . "/routes/{$route['name']}/content.min.asset.php";
|
||||
if ( file_exists( $content_asset_path ) ) {
|
||||
$content_asset = require $content_asset_path;
|
||||
$content_handle = 'jetpack-newsletter/routes/' . $route['name'] . '/content';
|
||||
$extension = '.min.js';
|
||||
// Deregister first to override any previously registered version
|
||||
// (e.g., Core's default modules when running as a plugin).
|
||||
wp_deregister_script_module( $content_handle );
|
||||
wp_register_script_module(
|
||||
$content_handle,
|
||||
$build_constants['build_url'] . 'routes/' . $route['name'] . '/content' . $extension,
|
||||
$content_asset['module_dependencies'] ?? array(),
|
||||
$content_asset['version'] ?? false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Register route module if exists
|
||||
if ( $route['has_route'] ) {
|
||||
$route_asset_path = __DIR__ . "/routes/{$route['name']}/route.min.asset.php";
|
||||
if ( file_exists( $route_asset_path ) ) {
|
||||
$route_asset = require $route_asset_path;
|
||||
$route_handle = 'jetpack-newsletter/routes/' . $route['name'] . '/route';
|
||||
$extension = '.min.js';
|
||||
// Deregister first to override any previously registered version
|
||||
// (e.g., Core's default modules when running as a plugin).
|
||||
wp_deregister_script_module( $route_handle );
|
||||
wp_register_script_module(
|
||||
$route_handle,
|
||||
$build_constants['build_url'] . 'routes/' . $route['name'] . '/route' . $extension,
|
||||
$route_asset['module_dependencies'] ?? array(),
|
||||
$route_asset['version'] ?? false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Register route with page
|
||||
if ( function_exists( $register_function_name ) ) {
|
||||
call_user_func( $register_function_name, $route['path'], $content_handle, $route_handle );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Page-specific route registration functions
|
||||
// Page-specific route registration functions for jetpack-newsletter-dashboard
|
||||
/**
|
||||
* Register routes for jetpack-newsletter-dashboard page (full-page mode).
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_newsletter_dashboard_page_routes() {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_routes_data;
|
||||
jetpack_newsletter_register_page_routes( $jetpack_newsletter_jetpack_newsletter_dashboard_routes_data, 'jetpack_newsletter_register_jetpack_newsletter_dashboard_route' );
|
||||
}
|
||||
add_action( 'jetpack-newsletter-dashboard_init', 'jetpack_newsletter_register_jetpack_newsletter_dashboard_page_routes' );
|
||||
|
||||
/**
|
||||
* Register routes for jetpack-newsletter-dashboard page (wp-admin mode).
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_newsletter_dashboard_wp_admin_page_routes() {
|
||||
global $jetpack_newsletter_jetpack_newsletter_dashboard_routes_data;
|
||||
jetpack_newsletter_register_page_routes( $jetpack_newsletter_jetpack_newsletter_dashboard_routes_data, 'jetpack_newsletter_register_jetpack_newsletter_dashboard_wp_admin_route' );
|
||||
}
|
||||
add_action( 'jetpack-newsletter-dashboard-wp-admin_init', 'jetpack_newsletter_register_jetpack_newsletter_dashboard_wp_admin_page_routes' );
|
||||
|
||||
// Page-specific route registration functions for jetpack-subscribers-announcement
|
||||
/**
|
||||
* Register routes for jetpack-subscribers-announcement page (full-page mode).
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_subscribers_announcement_page_routes() {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_routes_data;
|
||||
jetpack_newsletter_register_page_routes( $jetpack_newsletter_jetpack_subscribers_announcement_routes_data, 'jetpack_newsletter_register_jetpack_subscribers_announcement_route' );
|
||||
}
|
||||
add_action( 'jetpack-subscribers-announcement_init', 'jetpack_newsletter_register_jetpack_subscribers_announcement_page_routes' );
|
||||
|
||||
/**
|
||||
* Register routes for jetpack-subscribers-announcement page (wp-admin mode).
|
||||
*/
|
||||
function jetpack_newsletter_register_jetpack_subscribers_announcement_wp_admin_page_routes() {
|
||||
global $jetpack_newsletter_jetpack_subscribers_announcement_routes_data;
|
||||
jetpack_newsletter_register_page_routes( $jetpack_newsletter_jetpack_subscribers_announcement_routes_data, 'jetpack_newsletter_register_jetpack_subscribers_announcement_wp_admin_route' );
|
||||
}
|
||||
add_action( 'jetpack-subscribers-announcement-wp-admin_init', 'jetpack_newsletter_register_jetpack_subscribers_announcement_wp_admin_page_routes' );
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-deprecated', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-notices', 'wp-primitives', 'wp-private-apis', 'wp-theme', 'wp-url', 'wp-warning'), 'module_dependencies' => array(array('id' => '@wordpress/a11y', 'import' => 'static'), array('id' => '@wordpress/route', 'import' => 'static')), 'version' => 'aaece2843ec8199bb63f');
|
||||
+187
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
<?php return array('dependencies' => array(), 'version' => '214f7487552bc80b234d');
|
||||
+1
@@ -0,0 +1 @@
|
||||
var e={inspector:({search:r})=>r?.tab==="settings"?!1:!!(r?.subscriber||r?.u)};export{e as route};
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* Route registry - Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
return array(
|
||||
array(
|
||||
'name' => 'dashboard',
|
||||
'path' => '/',
|
||||
'page' => 'jetpack-newsletter-dashboard',
|
||||
'has_route' => true,
|
||||
'has_content' => true,
|
||||
),
|
||||
array(
|
||||
'name' => 'subscribers-announcement',
|
||||
'path' => '/',
|
||||
'page' => 'jetpack-subscribers-announcement',
|
||||
'has_route' => false,
|
||||
'has_content' => true,
|
||||
)
|
||||
);
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php return array('dependencies' => array('react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-private-apis', 'wp-theme', 'wp-url'), 'module_dependencies' => array(array('id' => '@wordpress/a11y', 'import' => 'static'), array('id' => '@wordpress/route', 'import' => 'static')), 'version' => 'ad0755c97021548579db');
|
||||
+25
File diff suppressed because one or more lines are too long
+85
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Script registration - Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers a script according to `wp_register_script`. Honors this request by
|
||||
* reassigning internal dependency properties of any script handle already
|
||||
* registered by that name. It does not deregister the original script, to
|
||||
* avoid losing inline scripts which may have been attached.
|
||||
*
|
||||
* @param WP_Scripts $scripts WP_Scripts instance.
|
||||
* @param string $handle Name of the script. Should be unique.
|
||||
* @param string $src Full URL of the script, or path of the script relative to the WordPress root directory.
|
||||
* @param array $deps Optional. An array of registered script handles this script depends on. Default empty array.
|
||||
* @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL
|
||||
* as a query string for cache busting purposes. If version is set to false, a version
|
||||
* number is automatically added equal to current installed WordPress version.
|
||||
* If set to null, no version is added.
|
||||
* @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
|
||||
* Default 'false'.
|
||||
*/
|
||||
function jetpack_newsletter_override_script( $scripts, $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
|
||||
$script = $scripts->query( $handle, 'registered' );
|
||||
if ( $script ) {
|
||||
/*
|
||||
* In many ways, this is a reimplementation of `wp_register_script` but
|
||||
* bypassing consideration of whether a script by the given handle had
|
||||
* already been registered.
|
||||
*/
|
||||
|
||||
// See: `_WP_Dependency::__construct` .
|
||||
$script->src = $src;
|
||||
$script->deps = $deps;
|
||||
$script->ver = $ver;
|
||||
$script->args = $in_footer ? 1 : null;
|
||||
} else {
|
||||
$scripts->add( $handle, $src, $deps, $ver, ( $in_footer ? 1 : null ) );
|
||||
}
|
||||
|
||||
if ( in_array( 'wp-i18n', $deps, true ) ) {
|
||||
$scripts->set_translations( $handle );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all package scripts.
|
||||
*/
|
||||
function jetpack_newsletter_register_package_scripts( $scripts ) {
|
||||
// Load build constants
|
||||
$build_constants = require __DIR__ . '/constants.php';
|
||||
$default_version = ! SCRIPT_DEBUG ? $build_constants['version'] : time();
|
||||
$extension = '.min.js';
|
||||
|
||||
$scripts_dir = __DIR__ . '/scripts';
|
||||
$scripts_file = $scripts_dir . '/registry.php';
|
||||
|
||||
if ( ! file_exists( $scripts_file ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$scripts_data = require $scripts_file;
|
||||
$plugin_dir = dirname( __FILE__ );
|
||||
|
||||
foreach ( $scripts_data as $script_data ) {
|
||||
$asset_file = $scripts_dir . '/' . $script_data['asset'];
|
||||
$asset = file_exists( $asset_file ) ? require $asset_file : array();
|
||||
$dependencies = $asset['dependencies'] ?? array();
|
||||
$version = $asset['version'] ?? $default_version;
|
||||
|
||||
jetpack_newsletter_override_script(
|
||||
$scripts,
|
||||
$script_data['handle'],
|
||||
$build_constants['build_url'] . 'scripts/' . $script_data['path'] . $extension,
|
||||
$dependencies,
|
||||
$version,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'wp_default_scripts', 'jetpack_newsletter_register_package_scripts' );
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Script registry - Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
return array(
|
||||
|
||||
);
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* Style registration - Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* Note: These styles can be overridden by calling jetpack_newsletter_override_style()
|
||||
* in lib/client-assets.php for complex cases (multiple files, conditional deps, etc.)
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers a style according to `wp_register_style`. Honors this request by
|
||||
* deregistering any style by the same handler before registration.
|
||||
*
|
||||
* @param WP_Styles $styles WP_Styles instance.
|
||||
* @param string $handle Name of the stylesheet. Should be unique.
|
||||
* @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory.
|
||||
* @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array.
|
||||
* @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL
|
||||
* as a query string for cache busting purposes. If version is set to false, a version
|
||||
* number is automatically added equal to current installed WordPress version.
|
||||
* If set to null, no version is added.
|
||||
* @param string $media Optional. The media for which this stylesheet has been defined.
|
||||
* Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like
|
||||
* '(orientation: portrait)' and '(max-width: 640px)'.
|
||||
*/
|
||||
function jetpack_newsletter_override_style( $styles, $handle, $src, $deps = array(), $ver = false, $media = 'all' ) {
|
||||
$style = $styles->query( $handle, 'registered' );
|
||||
if ( $style ) {
|
||||
$styles->remove( $handle );
|
||||
}
|
||||
$styles->add( $handle, $src, $deps, $ver, $media );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all package styles (simple cases only).
|
||||
* Complex cases should be manually registered in lib/client-assets.php.
|
||||
*
|
||||
* @param WP_Styles $styles WP_Styles instance.
|
||||
*/
|
||||
function jetpack_newsletter_register_package_styles( $styles ) {
|
||||
// Load build constants
|
||||
$build_constants = require __DIR__ . '/constants.php';
|
||||
$default_version = ! SCRIPT_DEBUG ? $build_constants['version'] : time();
|
||||
$suffix = '.min';
|
||||
|
||||
$styles_dir = __DIR__ . '/styles';
|
||||
$styles_file = $styles_dir . '/registry.php';
|
||||
|
||||
if ( ! file_exists( $styles_file ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$styles_data = require $styles_file;
|
||||
$plugin_dir = dirname( __FILE__ );
|
||||
|
||||
foreach ( $styles_data as $style_data ) {
|
||||
jetpack_newsletter_override_style(
|
||||
$styles,
|
||||
$style_data['handle'],
|
||||
$build_constants['build_url'] . 'styles/' . $style_data['path'] . $suffix . '.css',
|
||||
$style_data['dependencies'],
|
||||
$default_version
|
||||
);
|
||||
|
||||
// Enable RTL support (WordPress automatically loads -rtl.css variant)
|
||||
$styles->add_data( $style_data['handle'], 'rtl', 'replace' );
|
||||
$styles->add_data( $style_data['handle'], 'suffix', $suffix );
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'wp_default_styles', 'jetpack_newsletter_register_package_styles' );
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Style registry - Auto-generated by build process.
|
||||
* Do not edit this file manually.
|
||||
*
|
||||
* @package jetpack_newsletter
|
||||
*/
|
||||
|
||||
return array(
|
||||
|
||||
);
|
||||
+1
@@ -0,0 +1 @@
|
||||
<?php return array('dependencies' => array('jetpack-script-data', 'react', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives', 'wp-url'), 'version' => '2225f15b2a464a861605');
|
||||
+1
@@ -0,0 +1 @@
|
||||
.wpcom-daily-writing-prompt--prompt{background-color:var(--wpds-color-bg-surface-neutral-weak);border-radius:var(--wpds-border-radius-md);padding:var(--wpds-dimension-padding-md)}.wpcom-daily-writing-prompt--branding{border-block-start:var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral-weak);padding-block-start:var(--wpds-dimension-padding-md)}.wpcom-daily-writing-prompt--answered-users a{color:var(--wp-ui-button-foreground-color)}.wpcom-daily-writing-prompt--answered-users img{border:var(--wpds-border-width-sm) solid var(--wpds-color-bg-surface-neutral);border-radius:50%;margin-inline-start:calc((var(--wpds-dimension-gap-sm) + var(--wpds-border-width-sm))*-1);vertical-align:middle}.wpcom-daily-writing-prompt--answered-users img:first-child{margin-inline-start:0}
|
||||
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
||||
.wpcom-daily-writing-prompt--prompt{background-color:var(--wpds-color-bg-surface-neutral-weak);border-radius:var(--wpds-border-radius-md);padding:var(--wpds-dimension-padding-md)}.wpcom-daily-writing-prompt--branding{border-block-start:var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral-weak);padding-block-start:var(--wpds-dimension-padding-md)}.wpcom-daily-writing-prompt--answered-users a{color:var(--wp-ui-button-foreground-color)}.wpcom-daily-writing-prompt--answered-users img{border:var(--wpds-border-width-sm) solid var(--wpds-color-bg-surface-neutral);border-radius:50%;margin-inline-start:calc((var(--wpds-dimension-gap-sm) + var(--wpds-border-width-sm))*-1);vertical-align:middle}.wpcom-daily-writing-prompt--answered-users img:first-child{margin-inline-start:0}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/**
|
||||
* A class that adds a link to the WordPress.com Reader to the admin bar.
|
||||
*
|
||||
* @package automattic/jetpack-newsletter
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Newsletter;
|
||||
|
||||
use Automattic\Jetpack\Connection\Urls;
|
||||
use Automattic\Jetpack\Modules;
|
||||
use Automattic\Jetpack\Status\Host;
|
||||
use WP_Admin_Bar;
|
||||
|
||||
/**
|
||||
* Add a link to the WordPress.com Reader to the admin bar.
|
||||
*/
|
||||
class Reader_Link {
|
||||
/**
|
||||
* Whether the class has been initialized.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $initialized = false;
|
||||
|
||||
/**
|
||||
* Initialize the Reader Link functionality.
|
||||
*
|
||||
* This method sets up all necessary hooks for the Reader menu item
|
||||
* and its associated styles. It can be called multiple times safely
|
||||
* as it will only initialize once.
|
||||
*
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init() {
|
||||
if ( self::$initialized ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! is_user_logged_in() || ! is_admin_bar_showing() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$initialized = true;
|
||||
|
||||
$instance = new self();
|
||||
add_action( 'admin_bar_menu', array( $instance, 'add_reader_menu' ), 11 );
|
||||
add_action( 'wp_enqueue_scripts', array( $instance, 'enqueue_stylesheet' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $instance, 'enqueue_stylesheet' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the stylesheet used to display the Reader icon.
|
||||
*
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_stylesheet() {
|
||||
$build_path = dirname( __DIR__ ) . '/build/reader-link.css';
|
||||
if ( ! file_exists( $build_path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$asset_file = dirname( __DIR__ ) . '/build/reader-link.asset.php';
|
||||
$version = file_exists( $asset_file )
|
||||
? include $asset_file
|
||||
: array( 'version' => filemtime( $build_path ) );
|
||||
|
||||
wp_enqueue_style(
|
||||
'jetpack-newsletter-reader-link',
|
||||
plugins_url( '../build/reader-link.css', __FILE__ ),
|
||||
array(),
|
||||
$version['version'] ?? filemtime( $build_path )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the Reader menu.
|
||||
*
|
||||
* Hook into 'admin_bar_menu' to add to the wp-admin bar.
|
||||
*
|
||||
* @since 0.4.0
|
||||
*
|
||||
* @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar core object.
|
||||
*/
|
||||
public function add_reader_menu( $wp_admin_bar ) {
|
||||
$reader_menu_settings = array(
|
||||
'id' => 'reader',
|
||||
'title' => '<span class="ab-icon" title="' . __( 'Read the blogs and topics you follow', 'jetpack-newsletter' ) . '" aria-hidden="true"></span>' .
|
||||
'<span class="ab-label">' . __( 'Reader', 'jetpack-newsletter' ) . '</span>',
|
||||
'href' => method_exists( Urls::class, 'maybe_add_origin_site_id' )
|
||||
? Urls::maybe_add_origin_site_id( 'https://wordpress.com/reader' )
|
||||
: 'https://wordpress.com/reader',
|
||||
'meta' => array(
|
||||
'class' => 'wp-admin-bar-reader',
|
||||
),
|
||||
'parent' => 'top-secondary',
|
||||
);
|
||||
|
||||
/*
|
||||
* On self-hosted sites, open the Reader link in a new tab
|
||||
* since they're not necessarily logged in to WordPress.com
|
||||
* and may not want to navigate away from their site.
|
||||
*/
|
||||
if ( ! ( new Host() )->is_wpcom_platform() ) {
|
||||
$reader_menu_settings['meta']['target'] = '_blank';
|
||||
}
|
||||
|
||||
$wp_admin_bar->add_menu( $reader_menu_settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the wpcom-reader module when a site is first connected to WordPress.com.
|
||||
*
|
||||
* Only activates on truly fresh connections. If modules were previously initialized
|
||||
* (e.g., the user disconnected and reconnected), we respect their prior module choices.
|
||||
*
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public static function activate_on_connection() {
|
||||
if ( \Jetpack_Options::get_option( 'active_modules_initialized' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
( new Modules() )->activate( 'wpcom-reader', false, false );
|
||||
}
|
||||
}
|
||||
+604
@@ -0,0 +1,604 @@
|
||||
<?php
|
||||
/**
|
||||
* A class that adds a newsletter settings screen to wp-admin.
|
||||
*
|
||||
* @package automattic/jetpack-newsletter
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Newsletter;
|
||||
|
||||
use Automattic\Jetpack\Admin_UI\Admin_Menu;
|
||||
use Automattic\Jetpack\Assets;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\Modules;
|
||||
use Automattic\Jetpack\Redirect;
|
||||
use Automattic\Jetpack\Status;
|
||||
use Automattic\Jetpack\Status\Host;
|
||||
use Automattic\Jetpack\Status\Visitor;
|
||||
use Jetpack_Tracks_Client;
|
||||
|
||||
/**
|
||||
* A class responsible for adding a newsletter settings screen to wp-admin.
|
||||
*/
|
||||
class Settings {
|
||||
|
||||
const PACKAGE_VERSION = '0.11.1';
|
||||
|
||||
const ADMIN_PAGE_SLUG = 'jetpack-newsletter';
|
||||
|
||||
/**
|
||||
* Filter name that gates the wp-build–based dashboard.
|
||||
*
|
||||
* When this filter returns true, "Jetpack > Newsletter" renders the new
|
||||
* wp-build dashboard instead of the legacy Newsletter Settings React app.
|
||||
*/
|
||||
const MODERNIZATION_FILTER = 'rsm_jetpack_ui_modernization_newsletter';
|
||||
|
||||
/**
|
||||
* Percentage of sites the modernized Newsletter experience defaults on for
|
||||
* during the staged rollout.
|
||||
*
|
||||
* Currently 0: the Simple-site rollout is driven from the WordPress.com backend
|
||||
* instead (a server-side feature-flag value that can be rolled back instantly),
|
||||
* so the Jetpack-side cohort is held at zero. The cohort code stays in place for
|
||||
* Atomic (WoA) and self-hosted Jetpack sites; bumping this single number is the
|
||||
* one-line change that widens the rollout once the Simple cohort is validated.
|
||||
* Automatticians get the experience regardless of this percentage.
|
||||
*
|
||||
* Sites are bucketed deterministically by their wpcom blog ID, so every gate
|
||||
* that reads this lands on the same answer for a given site.
|
||||
*/
|
||||
const MODERNIZATION_ROLLOUT_PERCENTAGE = 0;
|
||||
|
||||
/**
|
||||
* Whether the class has been initialized
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private static $initialized = false;
|
||||
|
||||
/**
|
||||
* Init Newsletter Settings if it wasn't already.
|
||||
*/
|
||||
public static function init() {
|
||||
if ( ! self::$initialized ) {
|
||||
self::$initialized = true;
|
||||
( new self() )->init_hooks();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the subscriptions module is active.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_subscriptions_active() {
|
||||
return ( new Modules() )->is_active( 'subscriptions' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether to show the Newsletter menu item.
|
||||
* When true, shown regardless of subscriptions module state.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function should_show_menu_item() {
|
||||
/**
|
||||
* Filter to control Newsletter menu item visibility.
|
||||
* Defaults to true.
|
||||
*
|
||||
* @since 0.6.0
|
||||
* @param bool $show Whether to show the menu item.
|
||||
*/
|
||||
return apply_filters(
|
||||
'jetpack_show_newsletter_menu_item',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to necessary hooks.
|
||||
*/
|
||||
public function init_hooks() {
|
||||
// Transitional Subscribers announcement page (active only while the
|
||||
// modernization filter is on): registers its AJAX/admin-post handlers
|
||||
// and wp-build loading here so they exist on admin-ajax.php and
|
||||
// admin-post.php requests. The menu itself is added by the Jetpack
|
||||
// plugin's subscriptions module, which owns the Subscribers placement.
|
||||
Subscribers_Announcement::init();
|
||||
|
||||
// Add the Reading settings notice as long as subscriptions are active.
|
||||
if ( $this->is_subscriptions_active() ) {
|
||||
add_action( 'admin_init', array( $this, 'add_reading_page_notice' ) );
|
||||
}
|
||||
|
||||
// Hijack the config URLs to point to our settings page.
|
||||
// Priority 20 to override the default URL set in subscriptions.php.
|
||||
add_filter(
|
||||
'jetpack_module_configuration_url_subscriptions',
|
||||
function () {
|
||||
return Urls::get_newsletter_settings_url();
|
||||
},
|
||||
20
|
||||
);
|
||||
|
||||
// Defer wp-build loading to admin_menu (priority 1) on every host. The
|
||||
// modernization filter — which third parties typically register from a
|
||||
// plugins_loaded callback — needs to have been applied before we read it,
|
||||
// and the wp-build render function needs to be defined before any menu
|
||||
// callback runs (priority 999 on standalone Jetpack, priority 999999 on
|
||||
// wpcom Simple via wpcom-admin-menu.php's call to add_wp_admin_submenu).
|
||||
// Settings::init() runs synchronously from load-jetpack.php at
|
||||
// plugin-file-include time — before any plugins_loaded callback fires —
|
||||
// so an inline check here would always see the unfiltered default.
|
||||
add_action( 'admin_menu', array( __CLASS__, 'maybe_load_wp_build' ), 1 );
|
||||
|
||||
$host = new Host();
|
||||
|
||||
// On wpcom Simple, the Jetpack menu is created at priority 999999 by wpcom-admin-menu.php,
|
||||
// which will call add_wp_admin_submenu() directly. Skip adding the menu here to avoid
|
||||
// trying to add a submenu before the parent menu exists.
|
||||
if ( $host->is_wpcom_simple() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add admin menu item.
|
||||
// Use priority 999 to ensure menu items are queued BEFORE Admin_Menu::admin_menu_hook_callback
|
||||
// runs at priority 1000 to process all queued items.
|
||||
add_action( 'admin_menu', array( $this, 'add_wp_admin_menu' ), 999 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load wp-build for the Newsletter admin page when modernization is enabled.
|
||||
*
|
||||
* Hooked to `admin_menu` priority 1 so the modernization filter has been
|
||||
* registered by any opt-in code (mu-plugins, snippets, themes) before we
|
||||
* read it, and so the wp-build render function and enqueue hook are in
|
||||
* place before `add_wp_admin_menu` runs at priority 999.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function maybe_load_wp_build() {
|
||||
if ( ! self::is_modernized() || ! self::is_newsletter_admin_request() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::load_wp_build();
|
||||
add_action( 'current_screen', array( __CLASS__, 'alias_screen_id_for_wp_build' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the newsletter settings submenu to the Jetpack menu.
|
||||
*
|
||||
* Note: This method is NOT called on wpcom Simple sites. Simple sites use
|
||||
* add_wp_admin_submenu() called from wpcom-admin-menu.php instead.
|
||||
*/
|
||||
public function add_wp_admin_menu() {
|
||||
// On sites using Jetpack, only show the menu if the site is connected.
|
||||
if ( ! ( new Connection_Manager() )->is_connected() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// On the modernized dashboard, the Newsletter screen is only useful when the
|
||||
// subscriptions module is active, so skip registering the menu entirely when it
|
||||
// is off. Gated on the modernization flag to leave legacy behavior unchanged.
|
||||
if ( self::is_modernized() && ! $this->is_subscriptions_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$host = new Host();
|
||||
|
||||
// should_show_menu_item() controls visibility of the menu item.
|
||||
$show_menu = $this->should_show_menu_item();
|
||||
$parent_slug = $show_menu ? 'jetpack' : '';
|
||||
|
||||
// On Atomic, use add_submenu_page. On standalone Jetpack, use Admin_Menu when showing in menu.
|
||||
$use_jetpack_menu = ! $host->is_woa_site() && $show_menu;
|
||||
|
||||
$callback = self::is_modernized() && function_exists( 'jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_render_page' )
|
||||
? 'jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_render_page'
|
||||
: array( $this, 'render' );
|
||||
|
||||
// Register menu item.
|
||||
if ( $use_jetpack_menu ) {
|
||||
$page_suffix = Admin_Menu::add_menu(
|
||||
/** "Newsletter" is a product name, do not translate. */
|
||||
'Newsletter',
|
||||
'Newsletter',
|
||||
'manage_options',
|
||||
'jetpack-newsletter',
|
||||
$callback,
|
||||
10
|
||||
);
|
||||
} else {
|
||||
$page_suffix = add_submenu_page(
|
||||
$parent_slug,
|
||||
/** "Newsletter" is a product name, do not translate. */
|
||||
'Newsletter',
|
||||
'Newsletter',
|
||||
'manage_options',
|
||||
'jetpack-newsletter',
|
||||
$callback
|
||||
);
|
||||
}
|
||||
|
||||
if ( $page_suffix ) {
|
||||
add_action( 'load-' . $page_suffix, array( $this, 'admin_init' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the newsletter settings submenu directly under the Jetpack menu.
|
||||
*
|
||||
* This method is called from wpcom-admin-menu.php on Simple sites at late priority
|
||||
* (999999) when the Jetpack menu already exists.
|
||||
*/
|
||||
public function add_wp_admin_submenu() {
|
||||
// On the modernized dashboard, the Newsletter screen is only useful when the
|
||||
// subscriptions module is active, so skip registering the menu entirely when it
|
||||
// is off. Gated on the modernization flag to leave legacy behavior unchanged.
|
||||
if ( self::is_modernized() && ! $this->is_subscriptions_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent_slug = $this->should_show_menu_item() ? 'jetpack' : '';
|
||||
$callback = self::is_modernized() && function_exists( 'jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_render_page' )
|
||||
? 'jetpack_newsletter_jetpack_newsletter_dashboard_wp_admin_render_page'
|
||||
: array( $this, 'render' );
|
||||
$page_suffix = add_submenu_page(
|
||||
$parent_slug,
|
||||
/** "Newsletter" is a product name, do not translate. */
|
||||
'Newsletter',
|
||||
'Newsletter',
|
||||
'manage_options',
|
||||
'jetpack-newsletter',
|
||||
$callback
|
||||
);
|
||||
|
||||
if ( $page_suffix ) {
|
||||
add_action( 'load-' . $page_suffix, array( $this, 'admin_init' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin init actions.
|
||||
*/
|
||||
public function admin_init() {
|
||||
add_filter( 'jetpack_admin_js_script_data', array( $this, 'add_script_data' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add newsletter-specific data to the global JetpackScriptData object.
|
||||
*
|
||||
* @param array $data The existing script data.
|
||||
* @return array The modified script data.
|
||||
*/
|
||||
public function add_script_data( $data ) {
|
||||
$current_user = wp_get_current_user();
|
||||
$theme = wp_get_theme();
|
||||
|
||||
$host = new Host();
|
||||
$status = new Status();
|
||||
$site_suffix = $status->get_site_suffix();
|
||||
$blog_id = (int) $host->get_wpcom_site_id();
|
||||
$is_wpcom = $host->is_wpcom_platform();
|
||||
$is_block_theme = wp_is_block_theme();
|
||||
$setup_payment_plan_url = ( $is_wpcom ? 'https://wordpress.com/earn/payments/' : 'https://cloud.jetpack.com/monetize/payments/' ) . $site_suffix;
|
||||
|
||||
$wp_admin_subscriber_management_enabled = apply_filters( 'jetpack_wp_admin_subscriber_management_enabled', self::is_modernization_rollout_enabled() );
|
||||
|
||||
// Populate blog_id which is needed for API calls on Simple sites.
|
||||
$data['site']['wpcom']['blog_id'] = $blog_id;
|
||||
|
||||
// Add newsletter-specific data.
|
||||
// Note: Common data like admin_url, rest_nonce, rest_root, title, is_wpcom_platform,
|
||||
// and user.current_user.display_name are already provided by Script_Data.
|
||||
$data['newsletter'] = array(
|
||||
'isBlockTheme' => $is_block_theme,
|
||||
'themeStylesheet' => $theme->get_stylesheet(),
|
||||
'email' => $current_user->user_email,
|
||||
'gravatar' => get_avatar_url( $current_user->ID ),
|
||||
'dateExample' => gmdate( get_option( 'date_format' ), time() ),
|
||||
'subscriberManagementUrl' => $this->get_subscriber_management_url( $wp_admin_subscriber_management_enabled, $is_wpcom, $site_suffix, $blog_id ),
|
||||
'subscriberManagementEnabled' => (bool) $wp_admin_subscriber_management_enabled,
|
||||
'isSubscriptionSiteEditSupported' => $is_block_theme,
|
||||
'setupPaymentPlansUrl' => $setup_payment_plan_url,
|
||||
'isSitePublic' => ! $status->is_private_site() && ! $status->is_coming_soon(),
|
||||
'tracksUserData' => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the admin scripts.
|
||||
*/
|
||||
public function load_admin_scripts() {
|
||||
// This callback is registered via `admin_enqueue_scripts` from `admin_init`,
|
||||
// which itself fires on `load-{$page_suffix}` in `add_wp_admin_menu()` — so it
|
||||
// only fires on the Newsletter admin page; no need to re-check the page here.
|
||||
// The Tracks transport is required on both surfaces — `analytics.initialize`
|
||||
// only queues events into `window._tkq`; without `jp-tracks` loaded, no
|
||||
// pixel.gif requests fire and the queue grows forever.
|
||||
wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
|
||||
|
||||
if ( self::is_modernized() ) {
|
||||
// wp-build manages the rest of its enqueue pipeline. The legacy
|
||||
// newsletter script and JetpackScriptData are intentionally skipped
|
||||
// for the wp-build dashboard.
|
||||
return;
|
||||
}
|
||||
|
||||
Assets::register_script(
|
||||
'jetpack-newsletter',
|
||||
'../build/newsletter.js',
|
||||
__FILE__,
|
||||
array(
|
||||
'in_footer' => true,
|
||||
'textdomain' => 'jetpack-newsletter',
|
||||
'enqueue' => true,
|
||||
'dependencies' => array( 'jetpack-script-data' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subscriber management URL based on site type and filter settings.
|
||||
*
|
||||
* - If jetpack_wp_admin_subscriber_management_enabled filter is true: wp-admin subscribers page
|
||||
* - If filter is false AND wpcom site: wordpress.com/subscribers/$domain
|
||||
* - If filter is false AND Jetpack site: jetpack.com redirect URL
|
||||
*
|
||||
* @param bool $wp_admin_enabled Whether wp-admin subscriber management is enabled.
|
||||
* @param bool $is_wpcom Whether this is a WordPress.com site.
|
||||
* @param string $site_suffix The Calypso site suffix (home host, slashes as `::`).
|
||||
* @param int $blog_id The blog ID.
|
||||
* @return string The subscriber management URL.
|
||||
*/
|
||||
private function get_subscriber_management_url( $wp_admin_enabled, $is_wpcom, $site_suffix, $blog_id ) {
|
||||
// If wp-admin subscriber management is enabled, use the wp-admin page.
|
||||
if ( $wp_admin_enabled ) {
|
||||
return admin_url( 'admin.php?page=subscribers' );
|
||||
}
|
||||
|
||||
// For wpcom sites, use the wordpress.com URL.
|
||||
if ( $is_wpcom ) {
|
||||
return 'https://wordpress.com/subscribers/' . $site_suffix;
|
||||
}
|
||||
|
||||
// For Jetpack sites, use the jetpack.com redirect URL.
|
||||
$site_id = $blog_id ? (int) $blog_id : Connection_Manager::get_site_id( true );
|
||||
$args = ( ! empty( $site_id ) )
|
||||
? array( 'site' => $site_id )
|
||||
: array();
|
||||
|
||||
return Redirect::get_url(
|
||||
'jetpack-settings-jetpack-manage-subscribers',
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the newsletter settings page.
|
||||
*/
|
||||
public function render() {
|
||||
?>
|
||||
<div id="newsletter-settings-root"></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a notice on the Reading settings page to clarify that the RSS
|
||||
* excerpt setting does not control newsletter emails.
|
||||
*
|
||||
* @since 0.5.1
|
||||
*/
|
||||
public function add_reading_page_notice() {
|
||||
add_settings_field(
|
||||
'jetpack_newsletter_reading_notice',
|
||||
'',
|
||||
array( $this, 'render_reading_page_notice' ),
|
||||
'reading',
|
||||
'default'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the clarifying notice on the Reading settings page.
|
||||
*
|
||||
* Uses JavaScript to relocate the notice next to the "For each post in a feed"
|
||||
* (rss_use_excerpt) setting.
|
||||
*
|
||||
* @since 0.5.1
|
||||
*/
|
||||
public function render_reading_page_notice() {
|
||||
$newsletter_url = Urls::get_newsletter_settings_url();
|
||||
|
||||
printf(
|
||||
'<p class="description" id="jetpack-newsletter-reading-notice">%s</p>',
|
||||
sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s is a link to the Newsletter settings page. */
|
||||
__( 'To control what’s included in newsletter emails, visit your <a href="%s">Newsletter settings</a>.', 'jetpack-newsletter' ),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
esc_url( $newsletter_url )
|
||||
)
|
||||
);
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener( 'DOMContentLoaded', function() {
|
||||
var notice = document.getElementById( 'jetpack-newsletter-reading-notice' );
|
||||
var excerptInput = document.querySelector( 'input[name="rss_use_excerpt"]' );
|
||||
var excerptRow = excerptInput ? excerptInput.closest( 'tr' ) : null;
|
||||
|
||||
if ( ! notice || ! excerptRow ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remember the original parent before moving the notice.
|
||||
var originalTable = notice.closest( 'table' );
|
||||
var excerptTable = excerptRow.closest( 'table' );
|
||||
|
||||
// Move the notice into the rss_use_excerpt row's fieldset.
|
||||
excerptRow.querySelector( 'td' ).appendChild( notice );
|
||||
|
||||
// Remove the now-empty original table (if it's different from the excerpt's table).
|
||||
if ( originalTable && originalTable !== excerptTable ) {
|
||||
originalTable.remove();
|
||||
}
|
||||
} );
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the wp-build entry file and register its polyfills.
|
||||
*
|
||||
* Only called on `?page=jetpack-newsletter` admin requests when the
|
||||
* modernization filter is enabled. Keeps wp-build off every other request.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function load_wp_build() {
|
||||
$build_index = dirname( __DIR__ ) . '/build/build.php';
|
||||
|
||||
if ( ! file_exists( $build_index ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $build_index;
|
||||
|
||||
\Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::register(
|
||||
'jetpack-newsletter',
|
||||
array_merge(
|
||||
\Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::SCRIPT_HANDLES,
|
||||
\Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::MODULE_IDS
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias the current screen ID to satisfy wp-build's auto-generated enqueue check.
|
||||
*
|
||||
* Wp-build's `<page>-wp-admin` enqueue callback enqueues only when the screen ID
|
||||
* matches the wp-build page slug (`jetpack-newsletter-dashboard`). Our wp-admin
|
||||
* menu slug stays `jetpack-newsletter`, so we mutate the screen object in place
|
||||
* to make the check pass without changing the user-facing URL.
|
||||
*
|
||||
* Hooked only when modernization is on AND we're on the Newsletter admin page,
|
||||
* so this never affects any other request.
|
||||
*
|
||||
* @param \WP_Screen|null $screen The current screen object (passed by WP).
|
||||
* @return void
|
||||
*/
|
||||
public static function alias_screen_id_for_wp_build( $screen ) {
|
||||
if ( ! is_object( $screen ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$screen->id = 'jetpack-newsletter-dashboard';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the modernized Newsletter experience should default on for this site.
|
||||
*
|
||||
* The release is staged: the modernized dashboard, wp-admin subscriber
|
||||
* management, and the retired Calypso Subscribers submenu all default on for a
|
||||
* deterministic slice of sites, keyed on the wpcom blog ID, plus all
|
||||
* Automatticians.
|
||||
*
|
||||
* The percentage cohort (see `MODERNIZATION_ROLLOUT_PERCENTAGE`) spans *all*
|
||||
* sites — Simple, WoA (Atomic) and self-hosted Jetpack — and is bucketed on the
|
||||
* wpcom blog ID (`get_current_blog_id()` on Simple, `jetpack_options['id']`
|
||||
* elsewhere), which is preserved when a Simple site is upgraded to Atomic. Keying
|
||||
* on the wpcom blog ID rather than the transient `IS_WPCOM` constant means a site
|
||||
* keeps its cohort decision across the transfer. The percentage is currently 0:
|
||||
* the Simple-site rollout is driven from the WordPress.com backend instead, and
|
||||
* this gate stays at zero until the wider rollout is opened by bumping the
|
||||
* constant. A site with no resolvable wpcom blog ID (e.g. a self-hosted Jetpack
|
||||
* site that isn't connected) is never bucketed in.
|
||||
*
|
||||
* Automatticians get the modernized experience by default regardless of the
|
||||
* percentage cohort, so a12s can dogfood it and test fixes ahead of the wider
|
||||
* rollout. This is a dogfooding gate, not an authorization check, so the Simple
|
||||
* `is_automattician()` global is used without the usual proxied-request pairing;
|
||||
* Atomic has no non-proxied a12s signal, so it falls back to
|
||||
* `Visitor::is_automattician_feature_flags_only()` (true for proxied a8c requests).
|
||||
*
|
||||
* This is only the filter *default*: hosts (and a11ns who want the legacy view
|
||||
* back) can still force the experience on or off with the
|
||||
* `rsm_jetpack_ui_modernization_newsletter` /
|
||||
* `jetpack_wp_admin_subscriber_management_enabled` filters.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_modernization_rollout_enabled() {
|
||||
// Automatticians are enrolled regardless of the percentage cohort so they
|
||||
// can dogfood ahead of the wider rollout. Simple exposes the
|
||||
// `is_automattician()` global; Atomic has no non-proxied a12s signal, so we
|
||||
// fall back to the proxied-request check. (Dogfooding gate, so no
|
||||
// `wpcom_is_proxied_request()` pairing on the Simple branch.)
|
||||
if (
|
||||
( function_exists( 'is_automattician' ) && is_automattician() )
|
||||
|| ( new Visitor() )->is_automattician_feature_flags_only()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bucket on the wpcom blog ID, which is stable across a Simple→Atomic
|
||||
// transfer: the current blog ID on Simple, the stored wpcom ID elsewhere. We
|
||||
// read the WoA/Jetpack ID from Jetpack options directly rather than via
|
||||
// `Host::get_wpcom_site_id()`, which additionally requires the Jetpack
|
||||
// connection to be "ready" — that would drop a freshly transferred site out
|
||||
// of the cohort until its connection settles. Guard against an unresolvable
|
||||
// ID so a site without one isn't bucketed as blog ID 0 and enrolled by
|
||||
// accident once the percentage is non-zero.
|
||||
$host = new Host();
|
||||
$blog_id = $host->is_wpcom_simple()
|
||||
? (int) get_current_blog_id()
|
||||
: (int) \Jetpack_Options::get_option( 'id' );
|
||||
if ( $blog_id <= 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ( $blog_id % 100 ) < self::MODERNIZATION_ROLLOUT_PERCENTAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the wp-build modernization filter is enabled.
|
||||
*
|
||||
* Defaults to the staged-rollout cohort (see
|
||||
* `is_modernization_rollout_enabled()`): on for Automatticians and for the
|
||||
* percentage cohort (currently 0%), off everywhere else. Hosts can opt in or out
|
||||
* explicitly with
|
||||
* `add_filter( self::MODERNIZATION_FILTER, '__return_true' / '__return_false' );`.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_modernized() {
|
||||
return (bool) apply_filters( self::MODERNIZATION_FILTER, self::is_modernization_rollout_enabled() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the current request targets the Newsletter admin page.
|
||||
*
|
||||
* Used to scope wp-build loading to the one page that needs it. The
|
||||
* `$_GET['page']` value is populated by wp-admin/admin.php before any of
|
||||
* our hooks fire, so this check is reliable from `init_hooks()` onwards.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_newsletter_admin_request() {
|
||||
if ( ! is_admin() || ! isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return false;
|
||||
}
|
||||
|
||||
return sanitize_text_field( wp_unslash( $_GET['page'] ) ) === self::ADMIN_PAGE_SLUG; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
}
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
<?php
|
||||
/**
|
||||
* Transitional "Subscribers moved" announcement page.
|
||||
*
|
||||
* When the Newsletter modernization filter is enabled, the unified
|
||||
* Jetpack → Newsletter page owns subscriber management and the legacy
|
||||
* "Subscribers ↗" Calypso shortcut is retired. Instead of silently dropping
|
||||
* the menu item, this page takes its place so people who rely on the link
|
||||
* learn the new location before it disappears. They can also remove the
|
||||
* menu item themselves once they have adopted the new flow.
|
||||
*
|
||||
* The whole feature is temporary and kept deliberately small: this class
|
||||
* (menu, handlers, tracking) plus the `routes/subscribers-announcement`
|
||||
* wp-build route can be deleted wholesale once the transition period ends.
|
||||
*
|
||||
* @package automattic/jetpack-newsletter
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Newsletter;
|
||||
|
||||
use Automattic\Jetpack\Admin_UI\Admin_Menu;
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\Tracking;
|
||||
|
||||
/**
|
||||
* Renders the transitional Subscribers announcement page and handles its
|
||||
* "remove from sidebar" toggle and "Take me to Newsletter" redirect.
|
||||
*
|
||||
* The menu itself is registered by callers that own the Subscribers menu
|
||||
* placement (the Jetpack plugin's subscriptions module) via add_menu();
|
||||
* this class self-registers only request handlers and wp-build loading.
|
||||
*
|
||||
* @since 0.10.0
|
||||
*/
|
||||
class Subscribers_Announcement {
|
||||
|
||||
/**
|
||||
* Admin page slug (kept distinct from the wp-build page name; the screen
|
||||
* ID is aliased so wp-build's enqueue check still matches).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PAGE_SLUG = 'jetpack-subscribers';
|
||||
|
||||
/**
|
||||
* Wp-build page name, matching `routes/subscribers-announcement/package.json`.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const WP_BUILD_PAGE = 'jetpack-subscribers-announcement';
|
||||
|
||||
/**
|
||||
* Option storing whether the user removed the Subscribers menu item.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const REMOVED_OPTION = 'jetpack_subscribers_announcement_menu_removed';
|
||||
|
||||
/**
|
||||
* AJAX action toggling the menu item visibility.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TOGGLE_ACTION = 'jetpack_subscribers_announcement_toggle_menu';
|
||||
|
||||
/**
|
||||
* Admin-post action tracking the "Take me to Newsletter" click before redirecting.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const GO_ACTION = 'jetpack_subscribers_announcement_go_to_newsletter';
|
||||
|
||||
/**
|
||||
* Register request handlers and the wp-build loader.
|
||||
*
|
||||
* Called from Settings::init_hooks() so the AJAX/admin-post handlers exist
|
||||
* on admin-ajax.php / admin-post.php requests, where `admin_menu` (and so
|
||||
* add_menu()) never fires.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'wp_ajax_' . self::TOGGLE_ACTION, array( __CLASS__, 'handle_toggle_menu' ) );
|
||||
add_action( 'admin_post_' . self::GO_ACTION, array( __CLASS__, 'handle_go_to_newsletter' ) );
|
||||
|
||||
// Priority 1 mirrors Settings::maybe_load_wp_build(): the modernization
|
||||
// filter has been registered by opt-in code by then, and the wp-build
|
||||
// render function must exist before menu callbacks are resolved.
|
||||
add_action( 'admin_menu', array( __CLASS__, 'maybe_load_wp_build' ), 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the announcement page feature is active.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_enabled() {
|
||||
/** This filter is documented in projects/packages/newsletter/src/class-settings.php */
|
||||
return (bool) apply_filters( Settings::MODERNIZATION_FILTER, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load wp-build for the announcement page when the feature is enabled.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function maybe_load_wp_build() {
|
||||
if ( ! self::is_enabled() || ! self::is_announcement_request() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$build_index = dirname( __DIR__ ) . '/build/build.php';
|
||||
|
||||
if ( ! file_exists( $build_index ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $build_index;
|
||||
|
||||
\Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::register(
|
||||
'jetpack-newsletter',
|
||||
array_merge(
|
||||
\Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::SCRIPT_HANDLES,
|
||||
\Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::MODULE_IDS
|
||||
)
|
||||
);
|
||||
|
||||
add_action( 'current_screen', array( __CLASS__, 'alias_screen_id_for_wp_build' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias the current screen ID to satisfy wp-build's auto-generated enqueue check.
|
||||
*
|
||||
* Mirrors Settings::alias_screen_id_for_wp_build(): wp-build enqueues only
|
||||
* when the screen ID matches the wp-build page name, while our menu slug
|
||||
* stays `jetpack-subscribers`.
|
||||
*
|
||||
* @param \WP_Screen|null $screen The current screen object (passed by WP).
|
||||
* @return void
|
||||
*/
|
||||
public static function alias_screen_id_for_wp_build( $screen ) {
|
||||
if ( ! is_object( $screen ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$screen->id = self::WP_BUILD_PAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Subscribers announcement page under the Jetpack menu.
|
||||
*
|
||||
* When the user opted to remove the menu item, the page stays registered
|
||||
* (so the page remains reachable directly and the choice can be undone)
|
||||
* but the sidebar entry is removed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function add_menu() {
|
||||
$callback = function_exists( 'jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_render_page' )
|
||||
? 'jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_render_page'
|
||||
: array( __CLASS__, 'render_fallback' );
|
||||
|
||||
if ( get_option( self::REMOVED_OPTION ) ) {
|
||||
// Register as a hidden page (empty parent slug): it stays reachable
|
||||
// at its URL — so the choice can be undone from the page itself —
|
||||
// but never appears in the sidebar.
|
||||
$page_suffix = add_submenu_page(
|
||||
'',
|
||||
__( 'Subscribers', 'jetpack-newsletter' ),
|
||||
__( 'Subscribers', 'jetpack-newsletter' ),
|
||||
'manage_options',
|
||||
self::PAGE_SLUG,
|
||||
$callback
|
||||
);
|
||||
} else {
|
||||
$page_suffix = Admin_Menu::add_menu(
|
||||
__( 'Subscribers', 'jetpack-newsletter' ),
|
||||
__( 'Subscribers', 'jetpack-newsletter' ),
|
||||
'manage_options',
|
||||
self::PAGE_SLUG,
|
||||
$callback,
|
||||
15
|
||||
);
|
||||
}
|
||||
|
||||
if ( $page_suffix ) {
|
||||
add_action( 'load-' . $page_suffix, array( __CLASS__, 'on_page_load' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the announcement page directly under the Jetpack menu.
|
||||
*
|
||||
* Used on WordPress.com (Simple and WoA), where jetpack-mu-wpcom's
|
||||
* wpcom-admin-menu owns the Jetpack menu and registers submenus with the
|
||||
* core add_submenu_page() at a late priority — not the standalone plugin's
|
||||
* Admin_Menu wrapper. Mirrors Settings::add_wp_admin_submenu().
|
||||
*
|
||||
* As in add_menu(), an empty parent slug keeps the page reachable at its URL
|
||||
* (so the "remove from sidebar" choice can be undone) while hiding it from
|
||||
* the sidebar.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function add_wp_admin_submenu() {
|
||||
$callback = function_exists( 'jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_render_page' )
|
||||
? 'jetpack_newsletter_jetpack_subscribers_announcement_wp_admin_render_page'
|
||||
: array( __CLASS__, 'render_fallback' );
|
||||
|
||||
$parent_slug = get_option( self::REMOVED_OPTION ) ? '' : 'jetpack';
|
||||
|
||||
$page_suffix = add_submenu_page(
|
||||
$parent_slug,
|
||||
__( 'Subscribers', 'jetpack-newsletter' ),
|
||||
__( 'Subscribers', 'jetpack-newsletter' ),
|
||||
'manage_options',
|
||||
self::PAGE_SLUG,
|
||||
$callback
|
||||
);
|
||||
|
||||
if ( $page_suffix ) {
|
||||
add_action( 'load-' . $page_suffix, array( __CLASS__, 'on_page_load' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Page-load actions: record the page view and expose the app data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function on_page_load() {
|
||||
add_action( 'admin_head', array( __CLASS__, 'print_app_data' ) );
|
||||
|
||||
self::tracking()->record_user_event(
|
||||
'subscribers_announcement_page_view',
|
||||
array( 'menu_removed' => (bool) get_option( self::REMOVED_OPTION ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the data the announcement app needs (URLs, nonce, current state).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function print_app_data() {
|
||||
$data = array(
|
||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'toggleAction' => self::TOGGLE_ACTION,
|
||||
'toggleNonce' => wp_create_nonce( self::TOGGLE_ACTION ),
|
||||
// Built with add_query_arg (not wp_nonce_url, which HTML-escapes
|
||||
// the ampersands) because the app navigates to it via JS.
|
||||
'goToNewsletterUrl' => add_query_arg(
|
||||
array(
|
||||
'action' => self::GO_ACTION,
|
||||
'_wpnonce' => wp_create_nonce( self::GO_ACTION ),
|
||||
),
|
||||
admin_url( 'admin-post.php' )
|
||||
),
|
||||
'menuRemoved' => (bool) get_option( self::REMOVED_OPTION ),
|
||||
'menuSlug' => self::PAGE_SLUG,
|
||||
);
|
||||
|
||||
printf(
|
||||
'<script>window.JetpackSubscribersAnnouncementData = %s;</script>',
|
||||
wp_json_encode( $data, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal fallback when the wp-build bundle is unavailable.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function render_fallback() {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e( 'Subscribers moved', 'jetpack-newsletter' ); ?></h1>
|
||||
<p><?php esc_html_e( 'Now it’s part of Jetpack → Newsletter.', 'jetpack-newsletter' ); ?></p>
|
||||
<p>
|
||||
<a class="button button-primary" href="<?php echo esc_url( Urls::get_newsletter_settings_url() ); ?>">
|
||||
<?php esc_html_e( 'Take me to Newsletter', 'jetpack-newsletter' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler persisting the "remove Subscribers from the sidebar" choice.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function handle_toggle_menu() {
|
||||
check_ajax_referer( self::TOGGLE_ACTION );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) || ! self::is_enabled() ) {
|
||||
wp_send_json_error( 'unauthorized', 403, JSON_HEX_TAG | JSON_HEX_AMP );
|
||||
}
|
||||
|
||||
$removed = isset( $_POST['removed'] ) && '1' === $_POST['removed'];
|
||||
update_option( self::REMOVED_OPTION, $removed ? 1 : 0, false );
|
||||
|
||||
self::tracking()->record_user_event(
|
||||
'subscribers_announcement_remove_menu_click',
|
||||
array( 'removed' => $removed )
|
||||
);
|
||||
|
||||
wp_send_json_success( array( 'removed' => $removed ), 200, JSON_HEX_TAG | JSON_HEX_AMP );
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin-post handler recording the "Take me to Newsletter" click, then redirecting.
|
||||
*
|
||||
* Tracking the click server-side before the redirect avoids relying on a
|
||||
* JS tracking pipeline on a page that is otherwise static.
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public static function handle_go_to_newsletter() {
|
||||
check_admin_referer( self::GO_ACTION );
|
||||
|
||||
if ( current_user_can( 'manage_options' ) && self::is_enabled() ) {
|
||||
self::tracking()->record_user_event( 'subscribers_announcement_newsletter_click' );
|
||||
}
|
||||
|
||||
wp_safe_redirect( admin_url( 'admin.php?page=' . Settings::ADMIN_PAGE_SLUG ) );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Tracking instance.
|
||||
*
|
||||
* The product name stays `jetpack` so the events are recorded as
|
||||
* `jetpack_subscribers_announcement_*` regardless of which plugin
|
||||
* bundles this package.
|
||||
*
|
||||
* @return Tracking
|
||||
*/
|
||||
private static function tracking() {
|
||||
return new Tracking( 'jetpack', new Connection_Manager( 'jetpack' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the current request targets the announcement page.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_announcement_request() {
|
||||
if ( ! is_admin() || ! isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return false;
|
||||
}
|
||||
|
||||
return sanitize_text_field( wp_unslash( $_GET['page'] ) ) === self::PAGE_SLUG; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* URL helper for newsletter settings.
|
||||
*
|
||||
* @package automattic/jetpack-newsletter
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Newsletter;
|
||||
|
||||
/**
|
||||
* A class responsible for generating newsletter settings URLs.
|
||||
*/
|
||||
class Urls {
|
||||
|
||||
/**
|
||||
* Get the newsletter settings URL.
|
||||
*
|
||||
* @return string The newsletter settings URL.
|
||||
*/
|
||||
public static function get_newsletter_settings_url() {
|
||||
return admin_url( 'admin.php?page=jetpack-newsletter' );
|
||||
}
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
/**
|
||||
* A class that adds the Daily Writing Prompt dashboard widget to wp-admin.
|
||||
*
|
||||
* @package automattic/jetpack-newsletter
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\Newsletter;
|
||||
|
||||
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
|
||||
use Automattic\Jetpack\Status;
|
||||
use Automattic\Jetpack\Status\Host;
|
||||
use Automattic\Jetpack\Tracking;
|
||||
use Jetpack_Tracks_Client;
|
||||
|
||||
/**
|
||||
* Register and render the Daily Writing Prompt dashboard widget.
|
||||
*/
|
||||
class Writing_Prompt_Widget {
|
||||
/**
|
||||
* Whether the class has been initialized.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $initialized = false;
|
||||
|
||||
/**
|
||||
* Initialize the Daily Writing Prompt widget.
|
||||
*
|
||||
* Hooks the widget registration into `wp_dashboard_setup`. It can be called
|
||||
* multiple times safely as it will only initialize once.
|
||||
*
|
||||
* @since 0.9.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init() {
|
||||
if ( self::$initialized ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$initialized = true;
|
||||
|
||||
add_action( 'wp_dashboard_setup', array( __CLASS__, 'register_widget' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Daily Writing Prompt dashboard widget.
|
||||
*
|
||||
* @since 0.9.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register_widget() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! self::should_load() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_add_dashboard_widget(
|
||||
'wpcom_daily_writing_prompt',
|
||||
__( 'Daily Writing Prompt', 'jetpack-newsletter' ),
|
||||
array( __CLASS__, 'render_widget' ),
|
||||
null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- Core should ideally document null for no-callback arg. See https://core.trac.wordpress.org/ticket/52539.
|
||||
array(),
|
||||
'side',
|
||||
'high'
|
||||
);
|
||||
|
||||
// Enqueue here (on wp_dashboard_setup, which only fires on the dashboard
|
||||
// screen and runs before the header is printed) rather than in the render
|
||||
// callback, so the stylesheet lands in the head like it did previously.
|
||||
self::enqueue_assets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the Daily Writing Prompt widget should load.
|
||||
*
|
||||
* The widget fetches prompts from WordPress.com through the Jetpack proxy, so
|
||||
* it can only load where the site is able to reach WordPress.com: on Simple
|
||||
* sites, or on online, connected Jetpack sites (including Atomic).
|
||||
*
|
||||
* This runs on `wp_dashboard_setup` rather than at plugin-load time so that
|
||||
* the connection check happens after Core's pluggable functions are loaded.
|
||||
* Checking the connection any earlier triggers a fatal error on Atomic.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function should_load() {
|
||||
// Simple sites can always reach WordPress.com, with no Jetpack connection involved.
|
||||
if ( ( new Host() )->is_wpcom_simple() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Everywhere else the widget needs an online, connected site to fetch prompts.
|
||||
return ( new Connection_Manager() )->is_connected() && ! ( new Status() )->is_offline_mode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue the assets used to display the Daily Writing Prompt widget.
|
||||
*
|
||||
* @since 0.9.0
|
||||
*
|
||||
* @return string The script/style handle.
|
||||
*/
|
||||
public static function enqueue_assets() {
|
||||
$handle = 'jetpack-newsletter-writing-prompt';
|
||||
$js_path = dirname( __DIR__ ) . '/build/writing-prompt.js';
|
||||
$asset_php = dirname( __DIR__ ) . '/build/writing-prompt.asset.php';
|
||||
|
||||
if ( ! file_exists( $js_path ) ) {
|
||||
return $handle;
|
||||
}
|
||||
|
||||
$asset_file = file_exists( $asset_php ) ? include $asset_php : array();
|
||||
$dependencies = $asset_file['dependencies'] ?? array();
|
||||
$version = $asset_file['version'] ?? filemtime( $js_path );
|
||||
|
||||
wp_enqueue_script(
|
||||
$handle,
|
||||
plugins_url( '../build/writing-prompt.js', __FILE__ ),
|
||||
$dependencies,
|
||||
$version,
|
||||
true
|
||||
);
|
||||
wp_set_script_translations( $handle, 'jetpack-newsletter' );
|
||||
|
||||
// Load the Tracks transport so the widget's analytics events can fire.
|
||||
Tracking::register_tracks_functions_scripts( true );
|
||||
|
||||
// Expose the connected user's Tracks identity to the widget script.
|
||||
add_filter( 'jetpack_admin_js_script_data', array( __CLASS__, 'add_script_data' ) );
|
||||
|
||||
$css_ext = is_rtl() ? 'rtl.css' : 'css';
|
||||
$css_path = dirname( __DIR__ ) . "/build/writing-prompt.$css_ext";
|
||||
if ( file_exists( $css_path ) ) {
|
||||
wp_enqueue_style(
|
||||
$handle,
|
||||
plugins_url( "../build/writing-prompt.$css_ext", __FILE__ ),
|
||||
array(),
|
||||
$version
|
||||
);
|
||||
}
|
||||
|
||||
return $handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the connected user's Tracks identity to the admin script data.
|
||||
*
|
||||
* Mirrors the `newsletter.tracksUserData` shape provided by the Newsletter
|
||||
* settings page so the widget can reuse the same `getNewsletterScriptData()`
|
||||
* helper and `analytics.initialize()` flow.
|
||||
*
|
||||
* @since 0.9.1
|
||||
*
|
||||
* @param array $data The script data.
|
||||
* @return array The filtered script data.
|
||||
*/
|
||||
public static function add_script_data( $data ) {
|
||||
if ( ! isset( $data['newsletter'] ) || ! is_array( $data['newsletter'] ) ) {
|
||||
$data['newsletter'] = array();
|
||||
}
|
||||
|
||||
$data['newsletter']['tracksUserData'] = Jetpack_Tracks_Client::get_connected_user_tracks_identity();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the container of the Daily Writing Prompt widget.
|
||||
*
|
||||
* The widget is hydrated client-side by the `writing-prompt` JS entry, which
|
||||
* mounts the React app into the container rendered here.
|
||||
*
|
||||
* @since 0.9.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function render_widget() {
|
||||
$warning = __( 'Your Daily Writing Prompt widget requires JavaScript to function properly.', 'jetpack-newsletter' );
|
||||
|
||||
?>
|
||||
<div>
|
||||
<div class="hide-if-js">
|
||||
<?php echo esc_html( $warning ); ?>
|
||||
</div>
|
||||
<div
|
||||
id="wpcom_daily_writing_prompt_main"
|
||||
class="wpcom_daily_writing_prompt hide-if-no-js"
|
||||
style="height: 100%">
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user