*/ public function get_capability_gates(): array { return [ 'screen-views' => 'site-editor-view-config', ]; } protected function init_hooks(): void { $this->provenance = new Provenance(); // Recording — unconditional, every host. add_action( 'init', [ $this->provenance, 'register' ] ); add_filter( 'templately_fsi_item_meta', [ $this->provenance, 'contribute' ], 10, 3 ); // Single-template imports have no pipeline bag to contribute to; they announce // themselves through the producer-neutral action instead (spec 060 FR-002a). add_action( 'templately_blocks_imported', [ $this->provenance, 'stamp_single' ], 10, 2 ); // Presenting — only where the host can. Nothing below is registered // otherwise, so there is no filter to misbehave, no notice, and no // dead UI (FR-010). if ( ! $this->has_gate( 'screen-views' ) ) { return; } $this->add_component( 'view-config', new ViewConfig( $this->provenance ) ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_field_definitions' ] ); } /** * Register the field DEFINITIONS on the client. * * The server-side configuration only references field ids; what each field * IS — its label and how to read its value — is defined through the * editor's client registrar. Contributing ids without this produces views * that render and then filter nothing, which is what an E2E run on 7.1 * caught after the PHP-only implementation looked complete. * * Enqueued on the Site Editor screen only. It has no job anywhere else, and * the SPA bundle never loads there. * * @param string $hook The current admin page. * @return void */ public function enqueue_field_definitions( $hook ): void { if ( 'site-editor.php' !== $hook ) { return; } $handle = 'templately-site-editor-views'; $asset = TEMPLATELY_PATH . 'assets/js/site-editor-views.asset.php'; // These two are declared by hand and MERGED with whatever the build // extracted, never replaced by it. The script reads `wp.editor` and // `wp.i18n` off the global rather than importing the packages, so // dependency extraction correctly finds nothing to extract — and an // empty dependency list means WordPress is free to run this before // `wp-editor` exists, at which point the registrar is undefined and // nothing registers. That failure is silent: the views still render, // and then filter nothing. It cost an E2E run to find once already. $deps = [ 'wp-editor', 'wp-i18n' ]; $version = TEMPLATELY_VERSION; if ( file_exists( $asset ) ) { $meta = include $asset; if ( isset( $meta['dependencies'] ) && is_array( $meta['dependencies'] ) ) { $deps = array_values( array_unique( array_merge( $deps, $meta['dependencies'] ) ) ); } $version = isset( $meta['version'] ) ? $meta['version'] : $version; } wp_enqueue_script( $handle, TEMPLATELY_URL . 'assets/js/site-editor-views.js', $deps, $version, true ); } }