PluginProbe ʕ •ᴥ•ʔ
Hostinger Reach – AI-Powered Email Marketing for WordPress / trunk
Hostinger Reach – AI-Powered Email Marketing for WordPress vtrunk
1.7.2 1.7.1 1.7.0 1.6.0 1.5.9 1.5.8 1.5.7 1.5.6 1.5.5 1.5.4 1.5.3 1.5.2 1.5.1 1.5.0 1.4.12 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 trunk 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6
hostinger-reach / src / Integrations / Elementor / ElementorIntegration.php
hostinger-reach / src / Integrations / Elementor Last commit date
ElementorIntegration.php 1 hour ago SubscriptionFormElementorWidget.php 1 hour ago
ElementorIntegration.php
748 lines
1 <?php
2
3 namespace Hostinger\Reach\Integrations\Elementor;
4
5 use Elementor\Core\Documents_Manager;
6 use Elementor\Plugin as ElementorPlugin;
7 use Elementor\Utils;
8 use Elementor\Widget_Base;
9 use ElementorPro\Modules\Forms\Classes\Form_Record;
10 use ElementorPro\Modules\Forms\Submissions\Database\Entities\Form_Snapshot;
11 use ElementorPro\Modules\Forms\Submissions\Database\Query;
12 use ElementorPro\Modules\Forms\Submissions\Database\Repositories\Form_Snapshot_Repository;
13 use Exception;
14 use Hostinger\Reach\Api\Handlers\ReachApiHandler;
15 use Hostinger\Reach\Api\ResourceIdManager;
16 use Hostinger\Reach\Dto\PluginData;
17 use Hostinger\Reach\Dto\ReachContact;
18 use Hostinger\Reach\Functions;
19 use Hostinger\Reach\Integrations\IntegrationInterface;
20 use Hostinger\Reach\Integrations\IntegrationWithForms;
21 use Hostinger\Reach\Repositories\FormRepository;
22 use WP_Post;
23
24 if ( ! DEFINED( 'ABSPATH' ) ) {
25 exit;
26 }
27
28 class ElementorIntegration extends IntegrationWithForms implements IntegrationInterface {
29
30 public const INTEGRATION_NAME = 'elementor';
31 public const AUTOLOAD_META_KEY = 'hostinger_reach_add_elementor_widget';
32 public const AUTOLOAD_FORM_BUILDER_ID = 'hostinger_reach_add_elementor_widget_form_builder_id';
33 public const ADD_BLOCK_QUERY_ARG = 'hostinger_reach_add_block';
34 public const ADD_BLOCK_NONCE = 'hostinger_reach_add_block';
35 public const EDITOR_SCROLL_SCRIPT_HANDLE = 'hostinger-reach-elementor-editor-scroll';
36 public const EDITOR_SCROLL_SCRIPT_FILE = 'elementor-editor-scroll.js';
37 public const EDITOR_FORM_SCRIPT_HANDLE = 'hostinger-reach-elementor-form-selector';
38 public const EDITOR_FORM_SCRIPT_FILE = 'elementor-reach-form.js';
39 public const EDITOR_FORM_STYLE_FILE = 'elementor-reach-form.css';
40
41 protected SubscriptionFormElementorWidget $widget;
42 private ReachApiHandler $reach_api_handler;
43
44 public function __construct( FormRepository $form_repository, ReachApiHandler $reach_api_handler ) {
45 parent::__construct( $form_repository );
46 $this->reach_api_handler = $reach_api_handler;
47 }
48
49 public function init(): void {
50 parent::init();
51 add_action( 'hostinger_reach_integration_activated', array( $this, 'on_integration_activated' ) );
52 }
53
54 public function enqueue_editor_scroll_script(): void {
55 $script_path = HOSTINGER_REACH_PLUGIN_DIR . 'frontend/dist/' . self::EDITOR_SCROLL_SCRIPT_FILE;
56 if ( ! file_exists( $script_path ) ) {
57 return;
58 }
59
60 wp_enqueue_script(
61 self::EDITOR_SCROLL_SCRIPT_HANDLE,
62 Functions::get_frontend_url() . self::EDITOR_SCROLL_SCRIPT_FILE,
63 array(),
64 filemtime( $script_path ),
65 true
66 );
67 }
68
69 public function enqueue_editor_form_selector(): void {
70 $script_path = HOSTINGER_REACH_PLUGIN_DIR . 'frontend/dist/' . self::EDITOR_FORM_SCRIPT_FILE;
71 if ( ! file_exists( $script_path ) ) {
72 return;
73 }
74
75 wp_enqueue_script(
76 self::EDITOR_FORM_SCRIPT_HANDLE,
77 Functions::get_frontend_url() . self::EDITOR_FORM_SCRIPT_FILE,
78 array(),
79 filemtime( $script_path ),
80 true
81 );
82
83 $style_path = HOSTINGER_REACH_PLUGIN_DIR . 'frontend/dist/' . self::EDITOR_FORM_STYLE_FILE;
84 if ( file_exists( $style_path ) ) {
85 wp_enqueue_style(
86 self::EDITOR_FORM_SCRIPT_HANDLE,
87 Functions::get_frontend_url() . self::EDITOR_FORM_STYLE_FILE,
88 array(),
89 filemtime( $style_path )
90 );
91 }
92
93 $resource_id = $this->reach_api_handler->get_resource_id();
94 if ( $resource_id === ResourceIdManager::NON_EXISTENT_RESOURCE_ID ) {
95 $resource_id = '';
96 }
97
98 wp_localize_script(
99 self::EDITOR_FORM_SCRIPT_HANDLE,
100 'hostinger_reach_elementor_data',
101 array(
102 'restUrl' => esc_url_raw( rest_url() ),
103 'nonce' => wp_create_nonce( 'wp_rest' ),
104 'resourceId' => $resource_id,
105 'reachDomain' => $this->reach_api_handler->get_reach_domain(),
106 'domain' => $this->reach_api_handler->get_functions()->get_host_info(),
107 'widgetName' => SubscriptionFormElementorWidget::WIDGET_NAME,
108 'embedScript' => HOSTINGER_REACH_EMBED_SCRIPT_URL,
109 'i18n' => array(
110 'useTemplate' => __( 'Use Form builder template', 'hostinger-reach' ),
111 'useClassic' => __( 'Use classic Reach block', 'hostinger-reach' ),
112 'changeSelection' => __( 'Change the selection', 'hostinger-reach' ),
113 'editInReach' => __( 'Edit in Reach', 'hostinger-reach' ),
114 'selectForm' => __( 'Select a form', 'hostinger-reach' ),
115 'createForm' => __( 'Create a new form', 'hostinger-reach' ),
116 'refresh' => __( 'Refresh forms', 'hostinger-reach' ),
117 'cancel' => __( 'Cancel', 'hostinger-reach' ),
118 'continue' => __( 'Continue', 'hostinger-reach' ),
119 'noForms' => __( 'No forms yet. Create your first form in Hostinger Reach.', 'hostinger-reach' ),
120 'loading' => __( 'Loading…', 'hostinger-reach' ),
121 ),
122 )
123 );
124 }
125
126 public function enqueue_preview_styles(): void {
127 $handle = self::EDITOR_FORM_SCRIPT_HANDLE . '-preview';
128
129 wp_register_style( $handle, false, array(), HOSTINGER_REACH_PLUGIN_VERSION );
130 wp_enqueue_style( $handle );
131
132 $widget_selector = '.elementor-widget-' . SubscriptionFormElementorWidget::WIDGET_NAME;
133 $css = sprintf(
134 '%1$s .hostinger-reach-block-subscription-form-wrapper,%1$s [data-reach-form]{pointer-events:none;}',
135 $widget_selector
136 );
137
138 wp_add_inline_style( $handle, $css );
139 }
140
141 public function active_integration_hooks(): void {
142 add_action( 'transition_post_status', array( $this, 'handle_transition_post_status' ), 10, 3 );
143 add_filter( 'hostinger_reach_get_group', array( $this, 'filter_hostinger_reach_get_group' ), 10, 2 );
144 add_action( 'elementor_pro/forms/new_record', array( $this, 'handle_elementor_pro_new_record' ) );
145 add_action( 'wp_insert_post', array( $this, 'flag_new_elementor_post' ), 10, 3 );
146 add_action( 'admin_init', array( $this, 'flag_existing_elementor_post' ) );
147 add_action( 'elementor/editor/before_enqueue_scripts', array( $this, 'maybe_insert_reach_widget' ) );
148 add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue_editor_scroll_script' ) );
149 add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue_editor_form_selector' ) );
150 add_action( 'elementor/preview/enqueue_styles', array( $this, 'enqueue_preview_styles' ) );
151 add_action( 'elementor/widgets/register', array( $this, 'register_new_widgets' ) );
152 }
153
154 public function on_integration_activated( string $integration_name ): void {
155 if ( $integration_name === self::INTEGRATION_NAME ) {
156 $this->set_elementor_onboarding();
157 $this->init_existing_forms();
158 }
159 }
160
161 public function handle_transition_post_status( string $new_status, string $old_status, WP_Post $post ): void {
162 if ( $new_status === 'publish' ) {
163 $this->set_forms( $post );
164 $this->maybe_unset_forms( $post );
165 } elseif ( $old_status === 'publish' ) {
166 $this->unset_all_forms( $post );
167 }
168 }
169
170 public function register_new_widgets(): void {
171 if ( ! class_exists( 'Elementor\Plugin' ) ) {
172 return;
173 }
174 ElementorPlugin::instance()->widgets_manager->register( $this->get_widget() );
175 }
176
177 public function filter_hostinger_reach_get_group( string $group, string $form_id ): string {
178 if ( ! empty( $group ) || ! $this->is_elementor_form_id( $form_id ) ) {
179 return $group;
180 }
181
182 try {
183 $form = $this->form_repository->get( $form_id );
184 $post = $form['post'] ?? null;
185 if ( $post ) {
186 return $post['post_title'] ?? self::INTEGRATION_NAME;
187 }
188 } catch ( Exception $e ) {
189 return self::INTEGRATION_NAME;
190 }
191
192 return self::INTEGRATION_NAME;
193 }
194
195 public static function get_name(): string {
196 return self::INTEGRATION_NAME;
197 }
198
199 public static function is_active(): bool {
200 return class_exists( 'Elementor\Plugin' );
201 }
202
203 public static function get_new_page_url(): string {
204 if ( ! class_exists( 'Elementor\Core\Documents_Manager' ) ) {
205 return '';
206 }
207
208 return Documents_Manager::get_create_new_post_url( 'page' );
209 }
210
211 public function get_form_ids( WP_Post $post ): array {
212 return array_values(
213 array_unique(
214 array_merge(
215 $this->get_reach_form_ids( $post ),
216 $this->get_elementor_form_ids_from_actions()
217 )
218 )
219 );
220 }
221
222 public function get_plugin_data(): PluginData {
223 if ( class_exists( 'Elementor\Core\Documents_Manager' ) ) {
224 $add_form_url = add_query_arg(
225 array(
226 self::ADD_BLOCK_QUERY_ARG => '1',
227 ),
228 Documents_Manager::get_create_new_post_url()
229 );
230 } else {
231 $add_form_url = '';
232 }
233
234 return PluginData::from_array(
235 array(
236 'id' => self::INTEGRATION_NAME,
237 'folder' => 'elementor',
238 'file' => 'elementor.php',
239 'admin_url' => 'admin.php?page=elementor',
240 'add_form_url' => $add_form_url,
241 'edit_url' => 'post.php?post={post_id}&action=elementor#elementor-element-{form_id}',
242 'url' => 'https://wordpress.org/plugins/elementor/',
243 'download_url' => 'https://downloads.wordpress.org/plugin/elementor.zip',
244 'title' => __( 'Elementor', 'hostinger-reach' ),
245 'icon' => null,
246 'is_view_form_hidden' => false,
247 'is_edit_form_hidden' => false,
248 'can_toggle_forms' => true,
249 'import_enabled' => $this->is_import_supported(),
250 )
251 );
252 }
253
254 public function handle_elementor_pro_new_record( Form_Record $record ): void {
255 $email = $this->find_email( $record->get_formatted_data() );
256 if ( $email ) {
257 $form = $this->get_form_by_request();
258 $form_repository_item = $this->form_repository->get( $form->id ?? '' );
259 if ( empty( $form_repository_item ) || $form_repository_item['is_active'] === false ) {
260 return;
261 }
262
263 do_action(
264 'hostinger_reach_submit',
265 array(
266 'group' => ! empty( $form ) ? $form->name : self::INTEGRATION_NAME,
267 'email' => $email,
268 'metadata' => array(
269 'plugin' => self::INTEGRATION_NAME . '-pro',
270 'form_id' => ! empty( $form ) ? $form->id : null,
271 ),
272 )
273 );
274 }
275 }
276
277 public function flag_new_elementor_post( int $post_id, WP_Post $post, bool $update ): void {
278 if ( $update ) {
279 return;
280 }
281
282 if ( empty( $_GET[ self::ADD_BLOCK_QUERY_ARG ] ) && empty( $_GET[ self::AUTOLOAD_META_KEY ] ) ) {
283 return;
284 }
285
286 $nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';
287 if ( ! wp_verify_nonce( $nonce, 'elementor_action_new_post' ) ) {
288 return;
289 }
290
291 if ( ! current_user_can( 'edit_post', $post_id ) ) {
292 return;
293 }
294
295 $form_builder_id = isset( $_GET[ self::ADD_BLOCK_QUERY_ARG ] ) ? sanitize_text_field( wp_unslash( $_GET[ self::ADD_BLOCK_QUERY_ARG ] ) ) : '';
296
297 // '1' is the legacy marker for "add the default form" and does not reference a Form Builder template.
298 if ( $form_builder_id === '1' ) {
299 $form_builder_id = '';
300 }
301
302 update_post_meta( $post_id, self::AUTOLOAD_META_KEY, '1' );
303 update_post_meta( $post_id, self::AUTOLOAD_FORM_BUILDER_ID, $form_builder_id );
304 }
305
306 public function flag_existing_elementor_post(): void {
307 if ( empty( $_GET['action'] ) || $_GET['action'] !== 'elementor' ) {
308 return;
309 }
310
311 if ( empty( $_GET[ self::ADD_BLOCK_QUERY_ARG ] ) ) {
312 return;
313 }
314
315 $post_id = isset( $_GET['post'] ) ? (int) $_GET['post'] : 0;
316 if ( ! $post_id ) {
317 return;
318 }
319
320 $nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';
321 if ( ! wp_verify_nonce( $nonce, self::ADD_BLOCK_NONCE ) ) {
322 return;
323 }
324
325 if ( ! current_user_can( 'edit_post', $post_id ) ) {
326 return;
327 }
328
329 if ( ! $this->is_elementor_post( $post_id ) ) {
330 return;
331 }
332
333 $form_builder_id = sanitize_text_field( wp_unslash( $_GET[ self::ADD_BLOCK_QUERY_ARG ] ) );
334
335 // '1' is the legacy marker for "add the default form" and does not reference a Form Builder template.
336 if ( $form_builder_id === '1' ) {
337 $form_builder_id = '';
338 }
339
340 update_post_meta( $post_id, self::AUTOLOAD_META_KEY, '1' );
341 update_post_meta( $post_id, self::AUTOLOAD_FORM_BUILDER_ID, $form_builder_id );
342 }
343
344 public function is_elementor_post( int $post_id ): bool {
345 $edit_mode = get_post_meta( $post_id, '_elementor_edit_mode', true );
346
347 return $edit_mode === 'builder';
348 }
349
350 public function maybe_insert_reach_widget(): void {
351 if ( ! $this->is_elementor_installed() ) {
352 return;
353 }
354
355 $post_id = get_the_ID();
356 if ( ! $post_id ) {
357 return;
358 }
359
360 $should_add_widget = get_post_meta( $post_id, self::AUTOLOAD_META_KEY, true ) === '1';
361 if ( ! $should_add_widget ) {
362 return;
363 }
364
365 $document = ElementorPlugin::instance()->documents->get( $post_id );
366 if ( ! $document ) {
367 return;
368 }
369
370 $form_builder_id = (string) get_post_meta( $post_id, self::AUTOLOAD_FORM_BUILDER_ID, true );
371
372 $elements = $document->get_elements_data();
373
374 $widget_data = array(
375 array(
376 'id' => Utils::generate_random_string(),
377 'elType' => 'section',
378 'settings' => array(),
379 'elements' => array(
380 array(
381 'id' => Utils::generate_random_string(),
382 'elType' => 'column',
383 'settings' => array(
384 '_column_size' => 100,
385 ),
386 'elements' => array(
387 array(
388 'id' => Utils::generate_random_string(),
389 'elType' => 'widget',
390 'widgetType' => SubscriptionFormElementorWidget::WIDGET_NAME,
391 'settings' => array(
392 'formId' => SubscriptionFormElementorWidget::FORM_ID_PREFIX . random_int( 1, PHP_INT_MAX ),
393 'formBuilderId' => $form_builder_id,
394 'showName' => 0,
395 'showSurname' => 0,
396 ),
397 ),
398 ),
399 ),
400 ),
401 ),
402 );
403
404 $document->save( array( 'elements' => array_merge( $elements, $widget_data ) ) );
405 delete_post_meta( $post_id, self::AUTOLOAD_META_KEY );
406 delete_post_meta( $post_id, self::AUTOLOAD_FORM_BUILDER_ID );
407 }
408
409 public function is_import_supported(): bool {
410 return class_exists( 'ElementorPro\Modules\Forms\Submissions\Database\Repositories\Form_Snapshot_Repository' )
411 && class_exists( 'ElementorPro\Modules\Forms\Submissions\Database\Query' );
412 }
413
414 public function get_import_summary(): array {
415 $summary = array();
416
417 if ( ! $this->is_import_supported() ) {
418 return $summary;
419 }
420
421 $forms = Form_Snapshot_Repository::instance()->all();
422 $query = Query::get_instance();
423
424 foreach ( $forms as $form ) {
425 $form_id = $form->id ?? null;
426 $post_id = $form->post_id ?? null;
427
428 if ( ! $form_id || ! $post_id ) {
429 continue;
430 }
431
432 $counts = $query->count_submissions_by_status(
433 array(
434 'form' => array(
435 'value' => $post_id . '_' . $form_id,
436 ),
437 )
438 );
439
440 $summary[ $form_id ] = array(
441 'title' => $form->name ?? $form_id,
442 'contacts' => (int) $counts->get( 'read', 0 ) + (int) $counts->get( 'unread', 0 ),
443 );
444 }
445
446 return $summary;
447 }
448
449 public function get_contacts( mixed $form_id = null, ?int $limit = 100, ?int $offset = 0 ): array {
450 if ( ! $this->is_import_supported() ) {
451 return array();
452 }
453
454 if ( empty( $form_id ) ) {
455 return array();
456 }
457
458 try {
459 $form = $this->form_repository->get( $form_id );
460 } catch ( Exception $e ) {
461 return array();
462 }
463
464 $post_id = $form['post']['ID'] ?? null;
465 $page = (int) floor( $offset / $limit ) + 1;
466 $result = Query::get_instance()->get_submissions(
467 array(
468 'page' => $page,
469 'per_page' => $limit,
470 'filters' => array(
471 'form' => array(
472 'value' => $post_id . '_' . $form_id,
473 ),
474 'status' => array(
475 'value' => 'all',
476 ),
477 ),
478 'with_meta' => true,
479 'with_form_fields' => false,
480 )
481 );
482
483 $submissions = $result['data'] ?? array();
484 $entries = array();
485
486 foreach ( $submissions as $submission ) {
487 $email = $this->find_email( array( $submission['main']['value'] ?? '' ) );
488
489 if ( ! $email ) {
490 $email = $this->find_email( array_column( $submission['values'], 'value' ) );
491 }
492
493 if ( ! $email ) {
494 continue;
495 }
496
497 $contact = new ReachContact(
498 $email,
499 '',
500 '',
501 array(
502 'plugin' => self::INTEGRATION_NAME,
503 'form_id' => $form_id,
504 'group' => $form->name ?? self::INTEGRATION_NAME,
505 )
506 );
507
508 $entries[] = $contact->to_array();
509 }
510
511 return $entries;
512 }
513
514 public function init_existing_forms(): void {
515 $post_ids = $this->get_elementor_posts();
516 foreach ( $post_ids as $post_id ) {
517 $this->update_forms_from_post( $post_id );
518 }
519 }
520
521 public function set_elementor_onboarding(): void {
522 update_option( 'elementor_onboarded', 1 );
523 }
524
525 private function get_widget(): ?Widget_Base {
526 if ( ! $this->is_elementor_installed() ) {
527 return null;
528 }
529 return new SubscriptionFormElementorWidget();
530 }
531
532 private function update_forms_from_post( int $post_id ): void {
533 $form_ids = $this->get_forms_from_post_id( $post_id );
534 $this->update_form_repository( $form_ids, $post_id );
535 }
536
537 private function set_forms( WP_Post $post ): void {
538 $elementor_reach_forms = $this->get_reach_form_ids( $post );
539 $elementor_pro_forms = $this->get_elementor_form_ids_from_actions();
540 $form_ids = array_values( array_unique( array_merge( $elementor_reach_forms, $elementor_pro_forms ) ) );
541
542 $this->update_form_repository( $form_ids, $post->ID );
543 }
544
545 private function get_reach_form_ids( WP_Post $post ): array {
546 return array_values(
547 array_unique(
548 array_merge(
549 $this->get_elementor_form_ids_from_content( $post->post_content ),
550 $this->get_reach_form_ids_from_post_id( $post->ID ),
551 $this->get_reach_form_ids_from_actions()
552 )
553 )
554 );
555 }
556
557 private function get_reach_form_ids_from_post_id( int $post_id ): array {
558 $form_ids = array();
559
560 $elementor_metadata = get_post_meta( $post_id, '_elementor_data', true );
561 if ( ! is_string( $elementor_metadata ) || $elementor_metadata === '' ) {
562 return $form_ids;
563 }
564
565 $elementor_metadata = json_decode( $elementor_metadata, true );
566 if ( ! is_array( $elementor_metadata ) ) {
567 return $form_ids;
568 }
569
570 foreach ( $elementor_metadata as $element ) {
571 $form_ids = array_merge( $form_ids, $this->find_reach_form_widget( $element ) );
572 }
573
574 return $form_ids;
575 }
576
577 private function get_reach_form_ids_from_actions(): array {
578 $form_ids = array();
579 $actions = json_decode( wp_unslash( $_POST['actions'] ?? '' ), true );
580 $status = $actions['save_builder']['data']['status'] ?? 'draft';
581 $elements = $actions['save_builder']['data']['elements'] ?? array();
582
583 if ( ! empty( $elements ) && $status === 'publish' ) {
584 foreach ( $elements as $element ) {
585 $form_ids = array_merge( $form_ids, $this->find_reach_form_widget( $element ) );
586 }
587 }
588
589 return $form_ids;
590 }
591
592 private function find_reach_form_widget( array $element ): array {
593 $form_ids = array();
594
595 $is_reach_widget = ( $element['widgetType'] ?? '' ) === SubscriptionFormElementorWidget::WIDGET_NAME;
596 $form_id = $element['settings']['formId'] ?? '';
597
598 if ( $is_reach_widget && ! empty( $form_id ) ) {
599 $form_ids[] = $form_id;
600 }
601
602 if ( isset( $element['elements'] ) && is_array( $element['elements'] ) ) {
603 foreach ( $element['elements'] as $nested_element ) {
604 $form_ids = array_merge( $form_ids, $this->find_reach_form_widget( $nested_element ) );
605 }
606 }
607
608 return $form_ids;
609 }
610
611 private function update_form_repository( array $form_ids, int $post_id ): void {
612 if ( empty( $form_ids ) ) {
613 update_option( Functions::HOSTINGER_REACH_HAS_FORMS, true );
614 }
615 foreach ( $form_ids as $form_id ) {
616 $form = array(
617 'form_id' => $form_id,
618 'type' => self::INTEGRATION_NAME,
619 );
620
621 if ( $this->form_repository->exists( $form_id ) ) {
622 $this->form_repository->update( $form );
623 } else {
624 $this->form_repository->insert( array_merge( $form, array( 'post_id' => $post_id ) ) );
625 }
626 }
627 }
628
629 private function get_elementor_form_ids_from_content( string $content ): array {
630 if ( ! $this->is_elementor_installed() ) {
631 return array();
632 }
633 $form_ids = array();
634 $pattern = '/<form id="' . SubscriptionFormElementorWidget::FORM_ID_PREFIX . '(\d+)"/';
635 preg_match_all( $pattern, $content, $matches );
636 foreach ( $matches[1] as $form_id ) {
637 $form_ids[] = SubscriptionFormElementorWidget::FORM_ID_PREFIX . $form_id;
638 }
639
640 return $form_ids;
641 }
642
643 private function get_elementor_form_ids_from_actions(): array {
644 $form_ids = array();
645 $actions = json_decode( wp_unslash( $_POST['actions'] ?? '' ), true );
646 $status = $actions['save_builder']['data']['status'] ?? 'draft';
647 $elements = $actions['save_builder']['data']['elements'] ?? array();
648
649 if ( ! empty( $elements ) && $status === 'publish' ) {
650 foreach ( $elements as $element ) {
651 $form_ids = array_merge( $form_ids, $this->find_form_widget( $element ) );
652 }
653 }
654
655 return $form_ids;
656 }
657
658 private function find_form_widget( array $element ): array {
659 $form_ids = array();
660
661 if ( isset( $element['widgetType'] ) && $element['widgetType'] === 'form' && isset( $element['id'] ) ) {
662 $form_ids[] = $element['id'];
663 }
664
665 if ( isset( $element['elements'] ) && is_array( $element['elements'] ) ) {
666 foreach ( $element['elements'] as $nested_element ) {
667 $form_ids = array_merge( $form_ids, $this->find_form_widget( $nested_element ) );
668 }
669 }
670
671 return $form_ids;
672 }
673
674 private function is_elementor_form_id( string $form_id ): bool {
675 if ( ! $this->is_elementor_installed() ) {
676 return false;
677 }
678 return str_starts_with( $form_id, SubscriptionFormElementorWidget::FORM_ID_PREFIX );
679 }
680
681 private function find_email( array $data ): string {
682 foreach ( $data as $value ) {
683 $sanitized_value = sanitize_email( $value );
684 if ( filter_var( $sanitized_value, FILTER_VALIDATE_EMAIL ) ) {
685 return $value;
686 }
687 }
688
689 return '';
690 }
691
692 private function get_form_by_request(): ?Form_Snapshot {
693 $post_id = sanitize_text_field( $_POST['post_id'] ?? '' );
694 $form_id = sanitize_text_field( $_POST['form_id'] ?? '' );
695
696 return Form_Snapshot_Repository::instance()->find( $post_id, $form_id );
697 }
698
699 private function get_forms_from_post_id( int $post_id ): array {
700 $form_ids = $this->get_reach_form_ids_from_post_id( $post_id );
701 $elementor_metadata = get_post_meta( $post_id, '_elementor_data', true );
702 if ( ! is_string( $elementor_metadata ) || $elementor_metadata === '' ) {
703 return $form_ids;
704 }
705
706 $elementor_metadata = json_decode( $elementor_metadata, true );
707 if ( ! is_array( $elementor_metadata ) ) {
708 return $form_ids;
709 }
710
711 foreach ( $elementor_metadata as $element ) {
712 $form_ids = array_merge( $form_ids, $this->find_form_widget( $element ) );
713 }
714
715 return array_values( array_unique( $form_ids ) );
716 }
717
718 private function get_elementor_posts(): array {
719 $args = array(
720 'post_type' => array( 'page', 'post', 'product', 'elementor_library' ),
721 'post_status' => 'publish',
722 'posts_per_page' => 100,
723 'fields' => 'ids',
724 'meta_query' => array(
725 'relation' => 'OR',
726 array(
727 'key' => '_elementor_data',
728 'value' => '"widgetType":"form"',
729 'compare' => 'LIKE',
730 ),
731 array(
732 'key' => '_elementor_data',
733 'value' => '"widgetType":"' . SubscriptionFormElementorWidget::WIDGET_NAME . '"',
734 'compare' => 'LIKE',
735 ),
736 ),
737 );
738
739 $post_ids = get_posts( $args );
740
741 return $post_ids;
742 }
743
744 private function is_elementor_installed(): bool {
745 return class_exists( 'Elementor\Plugin' ) && class_exists( 'Elementor\Widget_Base' ) && class_exists( 'Elementor\Utils' );
746 }
747 }
748