Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
73.17% covered (warning)
73.17%
30 / 41
80.00% covered (warning)
80.00%
12 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Forms
73.17% covered (warning)
73.17%
30 / 41
80.00% covered (warning)
80.00%
12 / 15
31.35
0.00% covered (danger)
0.00%
0 / 1
 register_feature_flags
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 load_contact_form
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
4.01
 plugin_url
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 assets_url
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_feedback_dashboard_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_legacy_menu_item_retired
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_mailpoet_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_hostinger_reach_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_integrations_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_conditional_logic_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_webhooks_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 should_honor_content_destinations
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 show_dashboard_integrations
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 show_block_integrations
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 show_integration_icons
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Package description here
4 *
5 * @package automattic/jetpack-forms
6 */
7
8namespace Automattic\Jetpack\Forms;
9
10use Automattic\Jetpack\Feature_Flags\Feature_Flags;
11use Automattic\Jetpack\Forms\ContactForm\Feedback_Source;
12use Automattic\Jetpack\Forms\ContactForm\Util;
13use Automattic\Jetpack\Forms\Dashboard\Dashboard;
14/**
15 * Understands the Jetpack Forms package.
16 */
17class Jetpack_Forms {
18
19    const PACKAGE_VERSION = '8.0.3';
20
21    /**
22     * Name of the feature flag gating field conditional logic.
23     */
24    const CONDITIONAL_LOGIC_FLAG = 'forms-conditional-logic';
25
26    /**
27     * Register the package's feature flags.
28     *
29     * Registration is unconditional and happens as the package loads, so the full set stays
30     * discoverable through `Feature_Flags::all()` and nothing can call `is_enabled()` on an
31     * unregistered flag.
32     *
33     * @return void
34     */
35    public static function register_feature_flags() {
36        Feature_Flags::register(
37            self::CONDITIONAL_LOGIC_FLAG,
38            array(
39                'default'     => false,
40                'description' => 'Show or hide a form field based on the answer to another field.',
41                'owner'       => 'jetpack-forms',
42            )
43        );
44    }
45
46    /**
47     * Load the contact form module.
48     */
49    public static function load_contact_form() {
50        self::register_feature_flags();
51
52        Util::init();
53
54        if ( self::is_feedback_dashboard_enabled() ) {
55            $dashboard = new Dashboard();
56            $dashboard->init();
57        }
58
59        if ( is_admin() && apply_filters_deprecated( 'tmp_grunion_allow_editor_view', array( true ), '0.30.5', '', 'This functionality will be removed in an upcoming version.' ) ) {
60            add_action( 'current_screen', '\Automattic\Jetpack\Forms\ContactForm\Editor_View::add_hooks' );
61        }
62
63        add_action( 'init', '\Automattic\Jetpack\Forms\ContactForm\Util::register_pattern' );
64
65        // Add hook to delete file attachments when a feedback post is deleted
66        add_action( 'before_delete_post', array( '\Automattic\Jetpack\Forms\ContactForm\Contact_Form', 'delete_feedback_files' ) );
67
68        // Invalidate the source post IDs cache when a feedback post is permanently deleted.
69        add_action( 'deleted_post', array( '\Automattic\Jetpack\Forms\ContactForm\Feedback', 'invalidate_source_ids_cache_on_delete' ), 10, 2 );
70
71        // Enforces the availability of block support controls in the UI for classic themes.
72        add_filter( 'wp_theme_json_data_default', array( '\Automattic\Jetpack\Forms\ContactForm\Contact_Form', 'add_theme_json_data_for_classic_themes' ) );
73
74        // Initialize abilities registration for WordPress Abilities API (WP 6.9+)
75        \Automattic\Jetpack\Forms\Abilities\Forms_Abilities::init();
76    }
77
78    /**
79     * Get the plugin URL.
80     */
81    public static function plugin_url() {
82        return plugin_dir_url( __FILE__ );
83    }
84
85    /**
86     * Get the assets URL.
87     */
88    public static function assets_url() {
89        return plugin_dir_url( __DIR__ ) . 'assets';
90    }
91
92    /**
93     * Returns true if the feedback dashboard is enabled.
94     *
95     * @return boolean
96     */
97    public static function is_feedback_dashboard_enabled() {
98        /**
99         * Enable the Jetpack Forms dashboard.
100         *
101         * Returning false hides the Forms admin menu entry and skips the dashboard
102         * entirely. Unrelated to which dashboard renders — there is only one.
103         *
104         * @module contact-form
105         * @since 0.3.0
106         *
107         * @param bool $enabled Should the Jetpack Forms dashboard be enabled? Default true.
108         */
109        return apply_filters( 'jetpack_forms_dashboard_enable', true );
110    }
111
112    /**
113     * Returns true if the legacy menu item is retired.
114     *
115     * @return boolean
116     */
117    public static function is_legacy_menu_item_retired() {
118        return apply_filters( 'jetpack_forms_retire_legacy_menu_item', true );
119    }
120
121    /**
122     * Returns true if MailPoet integration is enabled.
123     *
124     * @return boolean
125     */
126    public static function is_mailpoet_enabled() {
127        /**
128         * Enable MailPoet integration.
129         *
130         * @param bool false Whether MailPoet integration be enabled. Default is false.
131         */
132        return apply_filters( 'jetpack_forms_mailpoet_enable', true );
133    }
134
135    /**
136     * Returns true if Hostinger Reach integration is enabled.
137     *
138     * @return boolean
139     */
140    public static function is_hostinger_reach_enabled() {
141        /**
142         * Enable Hostinger Reach integration.
143         *
144         * @param bool false Whether Hostinger Reach integration be enabled. Default is false.
145         */
146        return apply_filters( 'jetpack_forms_hostinger_reach_enable', false );
147    }
148
149    /**
150     * Returns true if the Integrations UI should be enabled.
151     *
152     * @return boolean
153     */
154    public static function is_integrations_enabled() {
155        /**
156         * Whether to enable the Integrations UI.
157         *
158         * @param bool true Whether to enable the Integrations UI. Default true.
159         */
160        return apply_filters( 'jetpack_forms_is_integrations_enabled', true );
161    }
162
163    /**
164     * Returns true if field conditional logic is enabled.
165     *
166     * One switch for the whole feature: the editor panel, the front-end show/hide, and the
167     * submission-time enforcement in validation and storage. Gating them together means a
168     * form can never hide a field from the visitor while still requiring it on submit.
169     *
170     * Turning the flag off on a form that already has conditions is safe: the conditions are
171     * simply ignored, so every field renders, validates and stores as an ordinary field.
172     *
173     * @return boolean
174     */
175    public static function is_conditional_logic_enabled() {
176        return Feature_Flags::is_enabled( self::CONDITIONAL_LOGIC_FLAG );
177    }
178
179    /**
180     * Returns true if webhooks are enabled.
181     *
182     * @return boolean
183     */
184    public static function is_webhooks_enabled() {
185        /**
186         * Whether to enable webhooks for Jetpack Forms.
187         *
188         * @param bool true Whether webhooks should be enabled. Default true.
189         */
190        return apply_filters( 'jetpack_forms_webhooks_enabled', true );
191    }
192
193    /**
194     * Whether author-configured outbound destinations from a form source should be honored.
195     *
196     * Destinations declared in the form content (webhooks, the legacy postToUrl attribute and
197     * the Salesforce integration) carry submission data to an outbound URL, so they are only
198     * honored when whoever placed the form had an administrator-level capability:
199     *
200     * - For post/page forms (`single`, numeric source id) the source post's author must have
201     *   the `manage_options` capability. Editor/Author/Contributor-authored forms are dropped.
202     * - For block templates, block template parts and widgets the source id is not a numeric
203     *   post, so there is no author to check. Editing any of those surfaces already requires
204     *   the `edit_theme_options` capability — administrator-tier, strictly above the
205     *   capabilities an Editor/Author/Contributor holds — so their destinations are trusted.
206     *
207     * The source type is established server-side (from the signed JWT, or by re-rendering the
208     * real template/template part on the legacy path) and is not forgeable by the submitter.
209     *
210     * Returns false for any other unresolved source (non-numeric id of an unknown type, or a
211     * numeric id with no admin-capable author).
212     *
213     * @param int|string $source_id   The form source id: a post id for post/page forms, or a
214     *                                non-numeric value for widget or block-template sources.
215     * @param string     $source_type The form source type: single, widget, block_template or
216     *                                block_template_part. Defaults to 'single'.
217     * @return boolean
218     */
219    public static function should_honor_content_destinations( $source_id, $source_type = 'single' ) {
220        // Block templates, template parts and widgets can only be authored by users with the
221        // administrator-tier `edit_theme_options` capability, so their destinations are trusted
222        // even though the source id is not a numeric post.
223        if ( in_array( $source_type, Feedback_Source::ADMIN_TIER_SOURCE_TYPES, true ) ) {
224            return true;
225        }
226
227        if ( ! is_numeric( $source_id ) ) {
228            return false;
229        }
230
231        $source_id = (int) $source_id;
232        if ( $source_id <= 0 ) {
233            return false;
234        }
235
236        $author_id = (int) get_post_field( 'post_author', $source_id );
237
238        return $author_id > 0 && user_can( $author_id, 'manage_options' );
239    }
240
241    /**
242     * Returns true if the Integrations UI should be shown in the Forms dashboard.
243     *
244     * @since 6.22.0
245     *
246     * @return boolean
247     */
248    public static function show_dashboard_integrations() {
249        /**
250         * Whether to show Integrations UI in the Forms dashboard.
251         *
252         * @since 6.22.0
253         *
254         * @param bool true Whether to show the Integrations UI in the dashboard. Default true.
255         */
256        return apply_filters( 'jetpack_forms_show_dashboard_integrations', true );
257    }
258
259    /**
260     * Returns true if the Integrations UI should be shown in the Form block editor.
261     *
262     * @since 6.22.0
263     *
264     * @return boolean
265     */
266    public static function show_block_integrations() {
267        /**
268         * Whether to show Integrations UI in the Form block editor.
269         *
270         * @since 6.22.0
271         *
272         * @param bool true Whether to show the Integrations UI in the editor. Default true.
273         */
274        return apply_filters( 'jetpack_forms_show_block_integrations', true );
275    }
276
277    /**
278     * Returns true if integration icons should be shown (editor sidebar and integrations modal).
279     *
280     * @since 6.22.0
281     *
282     * @return boolean
283     */
284    public static function show_integration_icons() {
285        /**
286         * Whether to show integration icons in the UI.
287         *
288         * If set to false, the ActiveIntegrations component (editor sidebar) will be hidden
289         * and integration icons in the integrations modal will not be rendered.
290         *
291         * @since 6.22.0
292         *
293         * @param bool true Whether to show integration icons. Default true.
294         */
295        return apply_filters( 'jetpack_forms_show_integration_icons', true );
296    }
297}