From 6e3238577553d5af3f1d764371d474082886fd06 Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Tue, 29 Jul 2025 21:26:52 -0700 Subject: [PATCH 01/12] Forms: Depreacte Classic Admin --- projects/packages/forms/src/contact-form/class-admin.php | 2 ++ projects/packages/forms/src/contact-form/class-util.php | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/packages/forms/src/contact-form/class-admin.php b/projects/packages/forms/src/contact-form/class-admin.php index 4ff00bfc370b8..d046320882284 100644 --- a/projects/packages/forms/src/contact-form/class-admin.php +++ b/projects/packages/forms/src/contact-form/class-admin.php @@ -17,6 +17,8 @@ /** * Class Admin * + * @deprecated $$next-version$$ + * * Singleton for Grunion admin area support. */ class Admin { diff --git a/projects/packages/forms/src/contact-form/class-util.php b/projects/packages/forms/src/contact-form/class-util.php index a1dc680beac7c..12804274ee83b 100644 --- a/projects/packages/forms/src/contact-form/class-util.php +++ b/projects/packages/forms/src/contact-form/class-util.php @@ -17,9 +17,6 @@ class Util { * Registers all relevant actions and filters for this class. */ public static function init() { - if ( is_admin() ) { - Admin::init(); - } add_filter( 'template_include', '\Automattic\Jetpack\Forms\ContactForm\Util::grunion_contact_form_set_block_template_attribute' ); From bbd4a8620a7f255879fc0c7fa6e633b8825c7c43 Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Tue, 29 Jul 2025 21:28:42 -0700 Subject: [PATCH 02/12] changelog --- projects/packages/forms/changelog/remove-forms-old-admin | 4 ++++ projects/plugins/jetpack/changelog/remove-forms-old-admin | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 projects/packages/forms/changelog/remove-forms-old-admin create mode 100644 projects/plugins/jetpack/changelog/remove-forms-old-admin diff --git a/projects/packages/forms/changelog/remove-forms-old-admin b/projects/packages/forms/changelog/remove-forms-old-admin new file mode 100644 index 0000000000000..b64916d33cb4b --- /dev/null +++ b/projects/packages/forms/changelog/remove-forms-old-admin @@ -0,0 +1,4 @@ +Significance: major +Type: removed + +Forms: Removes classic Admin initialization code diff --git a/projects/plugins/jetpack/changelog/remove-forms-old-admin b/projects/plugins/jetpack/changelog/remove-forms-old-admin new file mode 100644 index 0000000000000..a9fa70aaad0e8 --- /dev/null +++ b/projects/plugins/jetpack/changelog/remove-forms-old-admin @@ -0,0 +1,4 @@ +Significance: major +Type: bugfix + +Forms: Removes the classis Admin initliazation call From 966d4bd17f5bbb1b5fad17dcf60109a26105d953 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Thu, 4 Sep 2025 13:04:43 -0300 Subject: [PATCH 03/12] move gdrive export to plugin class --- .../forms/src/contact-form/class-admin.php | 3 - .../class-contact-form-plugin.php | 114 ++++++++++++++++++ 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/projects/packages/forms/src/contact-form/class-admin.php b/projects/packages/forms/src/contact-form/class-admin.php index d046320882284..ae2e6014d360f 100644 --- a/projects/packages/forms/src/contact-form/class-admin.php +++ b/projects/packages/forms/src/contact-form/class-admin.php @@ -83,9 +83,6 @@ public function __construct() { add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); add_action( 'admin_footer-edit.php', array( $this, 'print_export_modal' ) ); - - add_action( 'wp_ajax_grunion_export_to_gdrive', array( $this, 'export_to_gdrive' ) ); - add_action( 'wp_ajax_grunion_gdrive_connection', array( $this, 'test_gdrive_connection' ) ); } /** diff --git a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php index 86dca7d957d1f..4a0791f5a215a 100644 --- a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php +++ b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php @@ -11,6 +11,7 @@ use Automattic\Jetpack\Constants; use Automattic\Jetpack\Extensions\Contact_Form\Contact_Form_Block; use Automattic\Jetpack\Forms\Jetpack_Forms; +use Automattic\Jetpack\Forms\Service\Google_Drive; use Automattic\Jetpack\Forms\Service\MailPoet_Integration; use Automattic\Jetpack\Forms\Service\Post_To_Url; use Automattic\Jetpack\Status; @@ -91,6 +92,13 @@ class Contact_Form_Plugin { 'feedback_id' => '', ); + /** + * GDrive export nonce field name + * + * @var string The nonce field name for GDrive export. + */ + private $export_nonce_field_gdrive = 'feedback_export_nonce_gdrive'; + /** * Initializing function. */ @@ -215,6 +223,8 @@ protected function __construct() { if ( is_admin() ) { add_action( 'wp_ajax_feedback_export', array( $this, 'download_feedback_as_csv' ) ); add_action( 'wp_ajax_create_new_form', array( $this, 'create_new_form' ) ); + add_action( 'wp_ajax_grunion_export_to_gdrive', array( $this, 'export_to_gdrive' ) ); + add_action( 'wp_ajax_grunion_gdrive_connection', array( $this, 'test_gdrive_connection' ) ); } add_action( 'admin_menu', array( $this, 'admin_menu' ) ); add_action( 'current_screen', array( $this, 'unread_count' ) ); @@ -3444,4 +3454,108 @@ public function redirect_edit_feedback_to_jetpack_forms() { wp_safe_redirect( $redirect_url ); exit; } + + /** + * Ajax handler for wp_ajax_grunion_export_to_gdrive. + * Exports data to Google Drive, based on POST data. + * + * @see Contact_Form_Plugin::get_feedback_entries_from_post + */ + public function export_to_gdrive() { + $post_data = wp_unslash( $_POST ); + + if ( + ! current_user_can( 'export' ) + || empty( sanitize_text_field( $post_data[ $this->export_nonce_field_gdrive ] ) ) + || ! wp_verify_nonce( sanitize_text_field( $post_data[ $this->export_nonce_field_gdrive ] ), 'feedback_export' ) + ) { + wp_send_json_error( + __( 'You aren\'t authorized to do that.', 'jetpack-forms' ), + 403 + ); + + return; + } + + $grunion = self::init(); + $export_data = $grunion->get_feedback_entries_from_post(); + + $fields = is_array( $export_data ) ? array_keys( $export_data ) : array(); + $row_count = ! is_array( $export_data ) || empty( $export_data ) ? 0 : count( reset( $export_data ) ); + + $sheet_data = array( $fields ); + + for ( $i = 0; $i < $row_count; $i++ ) { + + $current_row = array(); + + /** + * Put all the fields in `$current_row` array. + */ + foreach ( $fields as $single_field_name ) { + $current_row[] = $export_data[ $single_field_name ][ $i ]; + } + + $sheet_data[] = $current_row; + } + + $user_id = (int) get_current_user_id(); + + if ( ! empty( $post_data['post'] ) && $post_data['post'] !== 'all' ) { + $spreadsheet_title = sprintf( + '%1$s - %2$s', + Util::get_export_filename( get_the_title( (int) $post_data['post'] ) ), + gmdate( 'Y-m-d H:i' ) + ); + } else { + $spreadsheet_title = sprintf( '%s - %s', Util::get_export_filename(), gmdate( 'Y-m-d H:i' ) ); + } + + $sheet = Google_Drive::create_sheet( $user_id, $spreadsheet_title, $sheet_data ); + + $grunion->record_tracks_event( 'forms_export_responses', array( 'format' => 'gsheets' ) ); + + wp_send_json( + array( + 'success' => ! is_wp_error( $sheet ), + 'data' => $sheet, + ) + ); + } + + /** + * Ajax handler. Sends a payload with connection status and html to replace + * the Connect button with the Export button using get_gdrive_export_button + */ + public function test_gdrive_connection() { + $post_data = wp_unslash( $_POST ); + $user_id = (int) get_current_user_id(); + + if ( + ! $user_id || + ! current_user_can( 'export' ) || + empty( sanitize_text_field( $post_data[ $this->export_nonce_field_gdrive ] ) ) || + ! wp_verify_nonce( sanitize_text_field( $post_data[ $this->export_nonce_field_gdrive ] ), 'feedback_export' ) + ) { + wp_send_json_error( + __( 'You aren\'t authorized to do that.', 'jetpack-forms' ), + 403 + ); + + return; + } + + $has_valid_connection = Google_Drive::has_valid_connection(); + + $replacement_html = $has_valid_connection + ? $this->get_gdrive_export_button_markup() + : ''; + + wp_send_json( + array( + 'connection' => $has_valid_connection, + 'html' => $replacement_html, + ) + ); + } } From c67e019b72dd1a4aaf42bf20d351b023e7406854 Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Thu, 4 Sep 2025 11:42:38 -0700 Subject: [PATCH 04/12] We don't need to move this method. --- .../class-contact-form-plugin.php | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php index 4a0791f5a215a..5ddd64d848bcc 100644 --- a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php +++ b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php @@ -224,7 +224,6 @@ protected function __construct() { add_action( 'wp_ajax_feedback_export', array( $this, 'download_feedback_as_csv' ) ); add_action( 'wp_ajax_create_new_form', array( $this, 'create_new_form' ) ); add_action( 'wp_ajax_grunion_export_to_gdrive', array( $this, 'export_to_gdrive' ) ); - add_action( 'wp_ajax_grunion_gdrive_connection', array( $this, 'test_gdrive_connection' ) ); } add_action( 'admin_menu', array( $this, 'admin_menu' ) ); add_action( 'current_screen', array( $this, 'unread_count' ) ); @@ -3522,40 +3521,4 @@ public function export_to_gdrive() { ) ); } - - /** - * Ajax handler. Sends a payload with connection status and html to replace - * the Connect button with the Export button using get_gdrive_export_button - */ - public function test_gdrive_connection() { - $post_data = wp_unslash( $_POST ); - $user_id = (int) get_current_user_id(); - - if ( - ! $user_id || - ! current_user_can( 'export' ) || - empty( sanitize_text_field( $post_data[ $this->export_nonce_field_gdrive ] ) ) || - ! wp_verify_nonce( sanitize_text_field( $post_data[ $this->export_nonce_field_gdrive ] ), 'feedback_export' ) - ) { - wp_send_json_error( - __( 'You aren\'t authorized to do that.', 'jetpack-forms' ), - 403 - ); - - return; - } - - $has_valid_connection = Google_Drive::has_valid_connection(); - - $replacement_html = $has_valid_connection - ? $this->get_gdrive_export_button_markup() - : ''; - - wp_send_json( - array( - 'connection' => $has_valid_connection, - 'html' => $replacement_html, - ) - ); - } } From 3793d3d1b204dc064326f656b566b92707f96aa0 Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Thu, 4 Sep 2025 11:51:16 -0700 Subject: [PATCH 05/12] Deprecate the method. --- .../forms/src/contact-form/class-admin.php | 61 ++----------------- 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/projects/packages/forms/src/contact-form/class-admin.php b/projects/packages/forms/src/contact-form/class-admin.php index ae2e6014d360f..1f193ce775ce6 100644 --- a/projects/packages/forms/src/contact-form/class-admin.php +++ b/projects/packages/forms/src/contact-form/class-admin.php @@ -150,67 +150,14 @@ public function print_export_modal() { * Ajax handler for wp_ajax_grunion_export_to_gdrive. * Exports data to Google Drive, based on POST data. * + * @deprecated $$next-version$$ + * * @see Contact_Form_Plugin::get_feedback_entries_from_post */ public function export_to_gdrive() { - $post_data = wp_unslash( $_POST ); - if ( - ! current_user_can( 'export' ) - || empty( sanitize_text_field( $post_data[ $this->export_nonce_field_gdrive ] ) ) - || ! wp_verify_nonce( sanitize_text_field( $post_data[ $this->export_nonce_field_gdrive ] ), 'feedback_export' ) - ) { - wp_send_json_error( - __( 'You aren\'t authorized to do that.', 'jetpack-forms' ), - 403 - ); + _deprecated_function( __METHOD__, 'package-$$next-version$$', 'Automattic\Jetpack\Forms\ContactForm\Contact_Form_Plugin::init()->export_to_gdrive()' ); - return; - } - - $grunion = Contact_Form_Plugin::init(); - $export_data = $grunion->get_feedback_entries_from_post(); - - $fields = is_array( $export_data ) ? array_keys( $export_data ) : array(); - $row_count = ! is_array( $export_data ) || empty( $export_data ) ? 0 : count( reset( $export_data ) ); - - $sheet_data = array( $fields ); - - for ( $i = 0; $i < $row_count; $i++ ) { - - $current_row = array(); - - /** - * Put all the fields in `$current_row` array. - */ - foreach ( $fields as $single_field_name ) { - $current_row[] = $export_data[ $single_field_name ][ $i ]; - } - - $sheet_data[] = $current_row; - } - - $user_id = (int) get_current_user_id(); - - if ( ! empty( $post_data['post'] ) && $post_data['post'] !== 'all' ) { - $spreadsheet_title = sprintf( - '%1$s - %2$s', - $this->get_export_filename( get_the_title( (int) $post_data['post'] ) ), - gmdate( 'Y-m-d H:i' ) - ); - } else { - $spreadsheet_title = sprintf( '%s - %s', $this->get_export_filename(), gmdate( 'Y-m-d H:i' ) ); - } - - $sheet = Google_Drive::create_sheet( $user_id, $spreadsheet_title, $sheet_data ); - - $grunion->record_tracks_event( 'forms_export_responses', array( 'format' => 'gsheets' ) ); - - wp_send_json( - array( - 'success' => ! is_wp_error( $sheet ), - 'data' => $sheet, - ) - ); + return Contact_Form_Plugin::init()->export_to_gdrive(); } /** From 855ada511f8f972866af06ac6c6f9164bce9207b Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Thu, 4 Sep 2025 16:53:06 -0700 Subject: [PATCH 06/12] Fixes for phan --- projects/packages/forms/src/contact-form/class-admin.php | 7 +++++-- .../forms/src/contact-form/class-contact-form-plugin.php | 6 +++++- .../packages/forms/src/contact-form/class-editor-view.php | 1 - 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/projects/packages/forms/src/contact-form/class-admin.php b/projects/packages/forms/src/contact-form/class-admin.php index 1f193ce775ce6..53b6382da4b8f 100644 --- a/projects/packages/forms/src/contact-form/class-admin.php +++ b/projects/packages/forms/src/contact-form/class-admin.php @@ -17,9 +17,9 @@ /** * Class Admin * - * @deprecated $$next-version$$ - * * Singleton for Grunion admin area support. + * + * This class will be removed in a future version. */ class Admin { /** @@ -39,9 +39,12 @@ class Admin { /** * Instantiates this singleton class * + * @deprecated $$next-version$$ + * * @return Admin The Admin class instance. */ public static function init() { + _deprecated_function( __METHOD__, 'package-$$next-version$$' ); static $instance = false; if ( ! $instance ) { diff --git a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php index 5ddd64d848bcc..992a284662961 100644 --- a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php +++ b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php @@ -3492,7 +3492,11 @@ public function export_to_gdrive() { * Put all the fields in `$current_row` array. */ foreach ( $fields as $single_field_name ) { - $current_row[] = $export_data[ $single_field_name ][ $i ]; + if ( isset( $export_data[ $single_field_name ][ $i ] ) ) { + $current_row[] = $export_data[ $single_field_name ][ $i ]; + } else { + $current_row[] = ''; + } } $sheet_data[] = $current_row; diff --git a/projects/packages/forms/src/contact-form/class-editor-view.php b/projects/packages/forms/src/contact-form/class-editor-view.php index 9318e68c21cb2..5a6987d46df36 100644 --- a/projects/packages/forms/src/contact-form/class-editor-view.php +++ b/projects/packages/forms/src/contact-form/class-editor-view.php @@ -32,7 +32,6 @@ public static function add_hooks( $screen ) { * Admin header. */ public static function admin_head() { - remove_action( 'media_buttons', array( Admin::init(), 'grunion_media_button' ), 999 ); add_action( 'media_buttons', array( __CLASS__, 'grunion_media_button' ), 999 ); } From 00288aaf9007ec65e5c97bb419a514d983f1fa33 Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Fri, 5 Sep 2025 08:05:38 -0700 Subject: [PATCH 07/12] Update phan baseline --- projects/packages/forms/.phan/baseline.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/packages/forms/.phan/baseline.php b/projects/packages/forms/.phan/baseline.php index 0d4d756714afc..3b06d703c41ad 100644 --- a/projects/packages/forms/.phan/baseline.php +++ b/projects/packages/forms/.phan/baseline.php @@ -19,13 +19,12 @@ // PhanPluginMixedKeyNoKey : 1 occurrence // PhanPluginRedundantAssignment : 1 occurrence // PhanPossiblyNullTypeMismatchProperty : 1 occurrence - // PhanTypeArraySuspiciousNullable : 1 occurrence // PhanTypeMismatchReturnNullable : 1 occurrence // PhanUnreferencedUseNormal : 1 occurrence // Currently, file_suppressions and directory_suppressions are the only supported suppressions 'file_suppressions' => [ - 'src/contact-form/class-admin.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeArraySuspiciousNullable', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchReturn'], + 'src/contact-form/class-admin.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchReturn'], 'src/contact-form/class-contact-form-field.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanPossiblyNullTypeMismatchProperty', 'PhanTypeConversionFromArray', 'PhanTypeMismatchArgument', 'PhanTypeMismatchReturnProbablyReal'], 'src/contact-form/class-contact-form-plugin.php' => ['PhanPluginDuplicateAdjacentStatement', 'PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchReturnProbablyReal'], 'src/contact-form/class-contact-form-shortcode.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeMismatchReturnProbablyReal'], From d3a98e640165a86b32f3d1ae5c139e5c9589b00a Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Fri, 5 Sep 2025 10:27:44 -0700 Subject: [PATCH 08/12] Code clean up --- .../forms/src/contact-form/class-contact-form-plugin.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php index 992a284662961..cc4e4d3617646 100644 --- a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php +++ b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php @@ -3472,7 +3472,6 @@ public function export_to_gdrive() { __( 'You aren\'t authorized to do that.', 'jetpack-forms' ), 403 ); - return; } @@ -3494,8 +3493,6 @@ public function export_to_gdrive() { foreach ( $fields as $single_field_name ) { if ( isset( $export_data[ $single_field_name ][ $i ] ) ) { $current_row[] = $export_data[ $single_field_name ][ $i ]; - } else { - $current_row[] = ''; } } From ef1020c1d10a9c26505b4adacb93dc702e190bb5 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Tue, 9 Sep 2025 12:01:28 -0300 Subject: [PATCH 09/12] add tests and phan baseline to allow deprecated function call --- projects/packages/forms/.phan/baseline.php | 1 + .../tests/php/contact-form/Util_Test.php | 131 ++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 projects/packages/forms/tests/php/contact-form/Util_Test.php diff --git a/projects/packages/forms/.phan/baseline.php b/projects/packages/forms/.phan/baseline.php index 3b06d703c41ad..4b5d9d79baf91 100644 --- a/projects/packages/forms/.phan/baseline.php +++ b/projects/packages/forms/.phan/baseline.php @@ -32,6 +32,7 @@ 'src/dashboard/class-dashboard-view-switch.php' => ['PhanUnreferencedUseNormal'], 'src/service/class-google-drive.php' => ['PhanTypeMismatchReturnProbablyReal'], 'tests/php/contact-form/Contact_Form_Plugin_Test.php' => ['PhanPluginMixedKeyNoKey'], + 'tests/php/contact-form/Util_Test.php' => ['PhanDeprecatedFunction'], ], // 'directory_suppressions' => ['src/directory_name' => ['PhanIssueName1', 'PhanIssueName2']] can be manually added if needed. // (directory_suppressions will currently be ignored by subsequent calls to --save-baseline, but may be preserved in future Phan releases) diff --git a/projects/packages/forms/tests/php/contact-form/Util_Test.php b/projects/packages/forms/tests/php/contact-form/Util_Test.php new file mode 100644 index 0000000000000..c3e7a1618a773 --- /dev/null +++ b/projects/packages/forms/tests/php/contact-form/Util_Test.php @@ -0,0 +1,131 @@ +callbacks as $callbacks ) { + foreach ( $callbacks as $callback ) { + if ( is_array( $callback['function'] ) && + is_object( $callback['function'][0] ) && + $callback['function'][0] instanceof Admin ) { + $unexpected_hooks_found[] = $hook; + } + } + } + } + } + + $this->assertEmpty( + $unexpected_hooks_found, + 'No Admin class hooks should be registered after Util::init(). Found: ' . implode( ', ', $unexpected_hooks_found ) + ); + } + + /** + * Test that Admin::init() method is properly deprecated. + * + * This test verifies that calling Admin::init() directly triggers + * a deprecation notice as expected. + */ + public function test_admin_init_is_deprecated() { + // Test that the Admin::init method has the @deprecated annotation + $reflection = new \ReflectionMethod( Admin::class, 'init' ); + $doc_comment = $reflection->getDocComment(); + + $this->assertStringContainsString( '@deprecated', $doc_comment, 'Admin::init() method should have @deprecated annotation' ); + + // Call the deprecated method and verify it still works (for backward compatibility) + $admin_instance = Admin::init(); + $this->assertInstanceOf( Admin::class, $admin_instance, 'Admin::init() should still return an Admin instance for backward compatibility' ); + } + + /** + * Test that Util::init() sets up the expected hooks and filters. + * + * This test verifies that the Util::init() method properly registers + * the expected WordPress hooks and filters without initializing the + * deprecated Admin class. + */ + public function test_util_init_registers_expected_hooks() { + // Remove any existing hooks first to get a clean state + remove_all_filters( 'template_include' ); + remove_all_actions( 'render_block_core_template_part_post' ); + remove_all_actions( 'init' ); + remove_all_actions( 'grunion_scheduled_delete' ); + remove_all_actions( 'grunion_pre_message_sent' ); + + // Initialize Util + Util::init(); + + // Verify that the expected hooks are registered (has_filter/has_action return priority or false) + $this->assertNotFalse( + has_filter( 'template_include', '\Automattic\Jetpack\Forms\ContactForm\Util::grunion_contact_form_set_block_template_attribute' ), + 'template_include filter should be registered' + ); + + $this->assertNotFalse( + has_action( 'render_block_core_template_part_post', '\Automattic\Jetpack\Forms\ContactForm\Util::grunion_contact_form_set_block_template_part_id_global' ), + 'render_block_core_template_part_post action should be registered' + ); + + $this->assertNotFalse( + has_action( 'init', '\Automattic\Jetpack\Forms\ContactForm\Contact_Form_Plugin::init' ), + 'Contact_Form_Plugin::init should be registered on init action' + ); + + $this->assertNotFalse( + has_action( 'grunion_scheduled_delete', '\Automattic\Jetpack\Forms\ContactForm\Util::grunion_delete_old_spam' ), + 'grunion_scheduled_delete action should be registered' + ); + + $this->assertNotFalse( + has_action( 'grunion_pre_message_sent', '\Automattic\Jetpack\Forms\ContactForm\Util::jetpack_tracks_record_grunion_pre_message_sent' ), + 'grunion_pre_message_sent action should be registered' + ); + } +} From d0ea9223f46857b26f2484aa415a226e3f3bd490 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Tue, 9 Sep 2025 13:30:35 -0300 Subject: [PATCH 10/12] add tests for export_to_gdrive delegation --- .../tests/php/contact-form/Util_Test.php | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/projects/packages/forms/tests/php/contact-form/Util_Test.php b/projects/packages/forms/tests/php/contact-form/Util_Test.php index c3e7a1618a773..5e0cc7a1bc846 100644 --- a/projects/packages/forms/tests/php/contact-form/Util_Test.php +++ b/projects/packages/forms/tests/php/contact-form/Util_Test.php @@ -128,4 +128,98 @@ public function test_util_init_registers_expected_hooks() { 'grunion_pre_message_sent action should be registered' ); } + + /** + * Test that export_to_gdrive functionality has been moved from Admin to Contact_Form_Plugin. + * + * This test verifies that the export_to_gdrive method is available in Contact_Form_Plugin + * and that the deprecated Admin method properly delegates to it. + */ + public function test_export_to_gdrive_moved_from_admin_to_plugin() { + // Verify that Contact_Form_Plugin has the export_to_gdrive method + $plugin_reflection = new \ReflectionClass( Contact_Form_Plugin::class ); + $this->assertTrue( + $plugin_reflection->hasMethod( 'export_to_gdrive' ), + 'Contact_Form_Plugin should have export_to_gdrive method' + ); + + // Verify that Admin still has the deprecated method + $admin_reflection = new \ReflectionClass( Admin::class ); + $this->assertTrue( + $admin_reflection->hasMethod( 'export_to_gdrive' ), + 'Admin should still have deprecated export_to_gdrive method for backward compatibility' + ); + + // Verify that Admin method is marked as deprecated + $admin_method = $admin_reflection->getMethod( 'export_to_gdrive' ); + $doc_comment = $admin_method->getDocComment(); + $this->assertStringContainsString( '@deprecated', $doc_comment, 'Admin::export_to_gdrive() should be marked as deprecated' ); + } + + /** + * Test export_to_gdrive method security and validation. + * + * This test verifies that the export_to_gdrive method properly validates + * permissions and nonces before processing the export request. + */ + public function test_export_to_gdrive_security_validation() { + // Create a Contact_Form_Plugin instance + $plugin = Contact_Form_Plugin::init(); + + // Test without proper capabilities + $original_user = wp_get_current_user(); + wp_set_current_user( 0 ); // Set to no user + + // Mock $_POST data without proper nonce + $_POST = array( + 'feedback_export_nonce_gdrive' => 'invalid_nonce', + ); + + // Capture output to check for JSON error response + ob_start(); + $plugin->export_to_gdrive(); + $output = ob_get_clean(); + + // Verify that an error response was sent + $this->assertStringContainsString( 'You aren\'t authorized to do that.', $output ); + + // Restore original user + wp_set_current_user( $original_user->ID ); + + // Clean up $_POST + unset( $_POST['feedback_export_nonce_gdrive'] ); + } + + /** + * Test that deprecated Admin::export_to_gdrive properly delegates to Contact_Form_Plugin. + * + * This test ensures that calling the deprecated Admin method still works + * by delegating to the new implementation in Contact_Form_Plugin. + */ + public function test_deprecated_admin_export_delegates_to_plugin() { + // Verify that the Admin method contains the proper delegation code + $reflection = new \ReflectionMethod( Admin::class, 'export_to_gdrive' ); + + // Get the method source code to verify it delegates to Contact_Form_Plugin + $filename = $reflection->getFileName(); + $start_line = $reflection->getStartLine(); + $end_line = $reflection->getEndLine(); + + $file_contents = file( $filename ); + $method_source = implode( '', array_slice( $file_contents, $start_line - 1, $end_line - $start_line + 1 ) ); + + // Verify the method calls _deprecated_function with the correct replacement + $this->assertStringContainsString( + 'Contact_Form_Plugin::init()->export_to_gdrive()', + $method_source, + 'Admin::export_to_gdrive should reference Contact_Form_Plugin::init()->export_to_gdrive() in deprecation notice' + ); + + // Verify the method actually delegates to Contact_Form_Plugin + $this->assertStringContainsString( + 'return Contact_Form_Plugin::init()->export_to_gdrive()', + $method_source, + 'Admin::export_to_gdrive should delegate to Contact_Form_Plugin::init()->export_to_gdrive()' + ); + } } From 41cc37209a56ddd423c2c6598196f0127d3bfdbd Mon Sep 17 00:00:00 2001 From: Enej Bajgoric Date: Wed, 10 Sep 2025 11:55:58 -0700 Subject: [PATCH 11/12] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- projects/plugins/jetpack/changelog/remove-forms-old-admin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/plugins/jetpack/changelog/remove-forms-old-admin b/projects/plugins/jetpack/changelog/remove-forms-old-admin index a9fa70aaad0e8..d7945606b2206 100644 --- a/projects/plugins/jetpack/changelog/remove-forms-old-admin +++ b/projects/plugins/jetpack/changelog/remove-forms-old-admin @@ -1,4 +1,4 @@ Significance: major Type: bugfix -Forms: Removes the classis Admin initliazation call +Forms: Removes the classic Admin initialization call From 7d5b379bff877adf22ace82fe36193ed60468727 Mon Sep 17 00:00:00 2001 From: Christian Gastrell Date: Wed, 10 Sep 2025 16:05:42 -0300 Subject: [PATCH 12/12] reinstate csv column array building even on empty values --- .../forms/src/contact-form/class-contact-form-plugin.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php index cc4e4d3617646..2ae121069193d 100644 --- a/projects/packages/forms/src/contact-form/class-contact-form-plugin.php +++ b/projects/packages/forms/src/contact-form/class-contact-form-plugin.php @@ -3493,6 +3493,8 @@ public function export_to_gdrive() { foreach ( $fields as $single_field_name ) { if ( isset( $export_data[ $single_field_name ][ $i ] ) ) { $current_row[] = $export_data[ $single_field_name ][ $i ]; + } else { + $current_row[] = ''; } }