Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.42% covered (warning)
77.42%
72 / 93
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Form_Editor
77.42% covered (warning)
77.42%
72 / 93
60.00% covered (warning)
60.00%
3 / 5
17.59
0.00% covered (danger)
0.00%
0 / 1
 init
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 allowed_blocks_for_jetpack_form
100.00% covered (success)
100.00%
61 / 61
100.00% covered (success)
100.00%
1 / 1
3
 block_editor_settings_all
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 disable_block_directory
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 enqueue_admin_scripts
15.00% covered (danger)
15.00%
3 / 20
0.00% covered (danger)
0.00%
0 / 1
20.35
1<?php
2/**
3 * Jetpack forms editor.
4 *
5 * @package automattic/jetpack-forms
6 */
7
8namespace Automattic\Jetpack\Forms\Editor;
9
10use Automattic\Jetpack\Assets;
11use Automattic\Jetpack\Forms\ContactForm\Contact_Form;
12
13/**
14 * Class Form_Editor
15 *
16 * Handles the form editor functionality for jetpack-form post type.
17 */
18class Form_Editor {
19
20    /**
21     * Script handle for the form editor.
22     *
23     * @var string
24     */
25    const SCRIPT_HANDLE = 'jetpack-form-editor';
26
27    /**
28     * Initialize the form editor.
29     */
30    public static function init() {
31        add_filter( 'allowed_block_types_all', array( __CLASS__, 'allowed_blocks_for_jetpack_form' ), 10, 2 );
32        add_filter( 'block_editor_settings_all', array( __CLASS__, 'block_editor_settings_all' ), 10, 2 );
33        add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin_scripts' ) );
34        add_action( 'current_screen', array( __CLASS__, 'disable_block_directory' ) );
35    }
36
37    /**
38     * Restrict allowed blocks when editing jetpack-form posts.
39     *
40     * Only allows field blocks and supporting blocks. The contact-form block is excluded
41     * because visual wrapping is handled via DOM manipulation in the editor script.
42     *
43     * @param bool|array $allowed_block_types Array of block type slugs, or boolean to enable/disable all.
44     * @param object     $editor_context       The current editor context.
45     * @return bool|array Array of allowed block types for jetpack-form posts.
46     */
47    public static function allowed_blocks_for_jetpack_form( $allowed_block_types, $editor_context ) {
48        // Only apply to jetpack-form post type.
49        if ( ! isset( $editor_context->post ) || Contact_Form::POST_TYPE !== $editor_context->post->post_type ) {
50            return $allowed_block_types;
51        }
52
53        // Allow only field blocks, button, and core blocks.
54        // Visual wrapping is handled by JavaScript DOM manipulation.
55        return array(
56            // Field blocks.
57            'jetpack/field-name',
58            'jetpack/field-email',
59            'jetpack/field-url',
60            'jetpack/field-telephone',
61            'jetpack/field-textarea',
62            'jetpack/field-checkbox',
63            'jetpack/field-checkbox-multiple',
64            'jetpack/field-radio',
65            'jetpack/field-select',
66            'jetpack/field-date',
67            'jetpack/field-consent',
68            'jetpack/field-rating',
69            'jetpack/field-text',
70            'jetpack/field-number',
71            'jetpack/field-hidden',
72            'jetpack/field-file',
73            'jetpack/field-time',
74            'jetpack/field-slider',
75            'jetpack/field-image-select',
76
77            // Supporting blocks.
78            'jetpack/button', // Used for the submit button previously.
79            'jetpack/label',
80            'jetpack/input',
81            'jetpack/options',
82            'jetpack/option',
83            'jetpack/phone-input',
84            'jetpack/dropzone',
85            'jetpack/input-range',
86            'jetpack/input-rating',
87            'jetpack/fieldset-image-options',
88            'jetpack/input-image-option',
89
90            // Multistep blocks.
91            'jetpack/form-step',
92            'jetpack/form-step-container',
93            'jetpack/form-step-divider',
94            'jetpack/form-step-navigation',
95            'jetpack/form-progress-indicator',
96
97            // Core blocks for rich content.
98            'core/accordion',
99            'core/audio',
100            'core/button', // Used for the submit button.
101            'core/code',
102            'core/column',
103            'core/columns',
104            'core/details',
105            'core/group',
106            'core/heading',
107            'core/html',
108            'core/icon',
109            'core/image',
110            'core/list-item',
111            'core/list',
112            'core/math',
113            'core/paragraph',
114            'core/row',
115            'core/separator',
116            'core/spacer',
117            'core/stack',
118            'core/subhead',
119            'core/video',
120        );
121    }
122
123    /**
124     * Modify block editor settings for jetpack-form posts.
125     *
126     * @param array  $settings       Block editor settings.
127     * @param object $editor_context The current editor context.
128     * @return array Modified block editor settings for jetpack-form posts.
129     */
130    public static function block_editor_settings_all( $settings, $editor_context ) {
131        // Only apply to jetpack-form post type.
132        if ( ! isset( $editor_context->post ) || Contact_Form::POST_TYPE !== $editor_context->post->post_type ) {
133            return $settings;
134        }
135
136        // Disable block locking capability.
137        $settings['canLockBlocks'] = false;
138
139        return $settings;
140    }
141
142    /**
143     * Disable the block directory in the form editor.
144     *
145     * Removes the block directory assets (install blocks from the inserter)
146     * since this feature is not needed in the form editor.
147     * Hooked to `current_screen` so it runs before scripts are enqueued.
148     *
149     * @param \WP_Screen $screen The current screen object.
150     */
151    public static function disable_block_directory( $screen ) {
152        if ( ! isset( $screen->post_type ) ) {
153            return;
154        }
155        if ( Contact_Form::POST_TYPE === $screen->post_type ) {
156            remove_action( 'enqueue_block_editor_assets', 'wp_enqueue_editor_block_directory_assets' );
157        }
158    }
159
160    /**
161     * Enqueue admin scripts for block editor.
162     * Loads in all post block editor contexts (excluding the site editor) so that the
163     * rename command is available and can be used when a form block is selected.
164     */
165    public static function enqueue_admin_scripts() {
166        $screen = get_current_screen();
167
168        // Only load in block editor contexts, not site editor
169        if ( ! $screen || $screen->id === 'site-editor' || ! $screen->is_block_editor ) {
170            return;
171        }
172        $asset_file = __DIR__ . '/../../dist/form-editor/jetpack-form-editor.asset.php';
173        if ( ! file_exists( $asset_file ) ) {
174            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
175            error_log( 'Form Editor asset file not found: ' . $asset_file );
176            return;
177        }
178        $asset = require $asset_file;
179        Assets::register_script(
180            self::SCRIPT_HANDLE,
181            '../../dist/form-editor/jetpack-form-editor.js',
182            __FILE__,
183            array(
184                'in_footer'    => true,
185                'textdomain'   => 'jetpack-forms',
186                'enqueue'      => true,
187                'dependencies' => $asset['dependencies'],
188                'version'      => $asset['version'],
189            )
190        );
191    }
192}