NOTIFYPRESS_ASSETS_DIR . 'main-dist/main.asset.php', 'editor' => NOTIFYPRESS_ASSETS_DIR . 'editor-dist/editor.asset.php', ); foreach ( $script_paths as $script_name => $script_path ) { if ( ! is_readable( $script_path ) ) { continue; } $script = require $script_path; $dependency = array_merge( $script['dependencies'], array( 'jquery' ) ); $script_version = $script['version']; $style_path = NOTIFYPRESS_ASSETS_DIR . "{$script_name}-dist/style-{$script_name}.css"; // wp-scripts derives $script['version'] from the JS bundle only, so a // CSS-only rebuild keeps the same query string and browsers keep // serving stale CSS from cache. Version the stylesheet by its own // mtime instead. $style_version = is_readable( $style_path ) ? (string) filemtime( $style_path ) : $script_version; wp_register_script( self::SCRIPT_HANDLE . "-{$script_name}", NOTIFYPRESS_ASSETS_URL . "{$script_name}-dist/{$script_name}.js", $dependency, $script_version, true ); wp_register_style( self::STYLE_HANDLE . "-{$script_name}", NOTIFYPRESS_ASSETS_URL . "{$script_name}-dist/style-{$script_name}.css", array(), $style_version ); // No app URL here — it is a fixed constant the JS bundle already // knows (assets/src/lib/app.ts). $localized_data = array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( self::NONCE_ACTION ), 'restUrl' => rest_url( 'notifypress/v1' ), 'restNonce' => wp_create_nonce( self::REST_NONCE ), 'siteUrl' => site_url(), ); // The 'main' script only runs on the manage_options-gated settings // page. It receives the full settings, but for_frontend() masks the // API token before they leave the server, so no credential is // embedded in the page. The 'editor' script runs for any user who // can open the block editor (Authors, Contributors) and only needs // the nonce, so it gets no settings at all. // // Uses for_frontend(), not $settings — this seeds react-query's // initial cache (see App.tsx), so it must carry the same // environment flags (e.g. wooCommerceActive) the REST endpoint // returns, or the UI shows stale/wrong values until the query's // staleTime lapses and it refetches. if ( 'main' === $script_name ) { $localized_data['settings'] = Settings::for_frontend(); } // The editor panel needs enough to render the notification preview // and decide whether the post-publish notification applies at all — // site identity, the app's field limits, and the two global // post-notification switches. No credentials. if ( 'editor' === $script_name ) { $localized_data['editor'] = array( 'siteName' => get_bloginfo( 'name' ), 'siteHost' => (string) wp_parse_url( home_url(), PHP_URL_HOST ), 'titleMax' => PostNotification::TITLE_MAX, 'messageMax' => PostNotification::MESSAGE_MAX, // Post types that actually notify, per the "all public post // types" / explicit-list setting — the panel hides itself // for anything not in here. 'postTypes' => array_values( array_filter( PostNotification::post_types(), array( PostNotification::class, 'post_type_is_selected' ) ) ), 'enabled' => (bool) Settings::get( 'postNotificationEnabled', true ), 'connected' => '' !== (string) Settings::get( 'apiToken', '' ), 'settingsUrl' => admin_url( 'admin.php?page=notifypress#/settings?tab=wordpress' ), ); } wp_localize_script( self::SCRIPT_HANDLE . "-{$script_name}", self::OBJECT_NAME, $localized_data ); } } /** * Enqueue the public-facing widget script from the NotifyPress app. * * @return void */ public function enqueue_widget(): void { if ( is_admin() || $this->is_turned_off() ) { return; } $settings = Settings::all(); $site_key = $settings['siteKey'] ?? ''; if ( empty( $site_key ) ) { return; } wp_enqueue_script( self::WIDGET_HANDLE, Api::widget_url( $site_key ), array(), null, // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- third-party asset; versioned by app.notifypress.com, not by this plugin. false ); } /** * Add the async attribute and the service worker path to the widget script tag. * * @param string $tag The full script tag markup. * @param string $handle The script handle being printed. * * @return string */ public function add_async_to_widget( string $tag, string $handle ): string { if ( self::WIDGET_HANDLE !== $handle ) { return $tag; } return str_replace( '