PluginProbe ʕ •ᴥ•ʔ
Hustle – Email Marketing, Lead Generation, Optins, Popups / 7.8.14.1
Hustle – Email Marketing, Lead Generation, Optins, Popups v7.8.14.1
7.8.14 7.8.14.1 7.8.13 7.8.13.1 trunk 3.0 3.1 3.1.1 3.1.2 3.1.3 3.1.4 4.3.2 4.4.4 4.4.5 4.4.5.1 4.4.5.4 4.6 4.6.1.1 4.6.1.4 4.7.0.2 4.7.0.3 4.7.0.7 4.7.0.9 4.7.1.0 4.7.1.1 4.8.0.0 5.0.0 5.0.1 5.0.1.1 5.0.1.2 5.1 5.1.1 5.1.2 5.1.3 5.1.3.1 5.1.3.2 5.1.4 5.1.5 6.0 6.0.1 6.0.2 6.0.3 6.0.4.2 6.0.5 6.0.6.1 6.0.7 6.0.8.1 6.0.9 7.0.0.1 7.0.2 7.0.3 7.0.4 7.1.0 7.1.1 7.2.0 7.2.1 7.3.0 7.3.1 7.3.3 7.3.5 7.3.6 7.3.7 7.4.0 7.4.1 7.4.11 7.4.13 7.4.13.1 7.4.2 7.4.3 7.4.4 7.4.5 7.4.5.1 7.4.5.2 7.4.6 7.4.7 7.5.0 7.6.0 7.6.1 7.6.3 7.6.4 7.6.6 7.7.0 7.7.1 7.8.0 7.8.1 7.8.10 7.8.10.1 7.8.10.2 7.8.11 7.8.12 7.8.12.1 7.8.2 7.8.3 7.8.4 7.8.5 7.8.6 7.8.7 7.8.8 7.8.9 7.8.9.1 7.8.9.2 7.8.9.3
wordpress-popup / inc / class-hustle-module-page-abstract.php
wordpress-popup / inc Last commit date
display-conditions 5 months ago front 2 days ago helpers 1 month ago metas 3 months ago multisite 5 months ago palettes 3 years ago provider 1 month ago providers 2 days ago templates 3 years ago update 5 months ago class-hustle-admin-page-abstract.php 1 month ago class-hustle-auth-token.php 5 months ago class-hustle-condition-factory.php 6 years ago class-hustle-cross-sell.php 10 months ago class-hustle-dashboard-admin.php 1 month ago class-hustle-data.php 1 month ago class-hustle-db.php 2 years ago class-hustle-installer.php 4 years ago class-hustle-meta.php 3 years ago class-hustle-module-admin.php 1 month ago class-hustle-module-collection.php 5 months ago class-hustle-module-fields.php 3 months ago class-hustle-module-page-abstract.php 1 month ago class-hustle-module-validator.php 3 months ago class-hustle-notifications.php 2 days ago class-hustle-settings-admin.php 1 month ago class-hustle-wp-dashboard-page.php 5 months ago class-opt-in.php 2 days ago hustle-background-conversion-log.php 3 months ago hustle-deletion.php 3 months ago hustle-embedded-admin.php 3 years ago hustle-entries-admin.php 5 months ago hustle-entry-model.php 1 month ago hustle-general-data-protection.php 5 months ago hustle-init.php 1 month ago hustle-mail.php 5 months ago hustle-migration.php 5 months ago hustle-model.php 2 days ago hustle-module-model.php 1 month ago hustle-module-widget-legacy.php 5 months ago hustle-module-widget.php 5 months ago hustle-modules-common-admin-ajax.php 1 month ago hustle-popup-admin.php 3 years ago hustle-providers-admin.php 1 month ago hustle-providers.php 5 months ago hustle-settings-admin-ajax.php 1 month ago hustle-settings-page.php 3 years ago hustle-slidein-admin.php 3 years ago hustle-sshare-admin.php 2 days ago hustle-sshare-model.php 2 days ago hustle-tracking-model.php 3 months ago interface-hustle-auth-provider.php 5 months ago opt-in-geo.php 5 months ago opt-in-utils.php 2 days ago opt-in-wpmudev-api.php 5 months ago
class-hustle-module-page-abstract.php
1366 lines
1 <?php
2 /**
3 * File for Hustle_Module_Page_Abstract class.
4 *
5 * @package Hustle
6 * @since 4.0.1
7 */
8
9 /**
10 * Class Hustle_Module_Page.
11 * Base model for the listing pages and their wizards.
12 *
13 * @since 4.0.1
14 */
15 abstract class Hustle_Module_Page_Abstract extends Hustle_Admin_Page_Abstract {
16
17 /**
18 * Edit page slug defined by WordPress when registering the page.
19 *
20 * @since 4.3.1
21 * @var string
22 */
23 private $page_edit_slug;
24
25 /**
26 * Wizard page slug assigned by us.
27 *
28 * @since 4.0.1
29 * @var string
30 */
31 protected $page_edit;
32
33 /**
34 * Wizard page title.
35 *
36 * @since 4.0.1
37 * @var string
38 */
39 protected $page_edit_title;
40
41 /**
42 * Capability required for the wizard page to be available.
43 *
44 * @since 4.0.1
45 * @var string
46 */
47 protected $page_edit_capability;
48
49 /**
50 * Path to the wizard's template page relative to the 'views' folder.
51 *
52 * @since 4.0.1
53 * @var string
54 */
55 protected $page_edit_template_path;
56
57 /**
58 * Current module. Only set on wizards when the module exists.
59 *
60 * @since 4.0.3
61 * @var integer
62 */
63 protected $module = false;
64
65 /**
66 * Count of the active module of the current type.
67 *
68 * @since 4.2.0
69 * @var integer
70 */
71 private $module_count_type;
72
73 /**
74 * Module type this page belongs to.
75 *
76 * @since 4.2.0
77 * @var string
78 */
79 public $module_type;
80
81 /**
82 * Established the properties for the page.
83 *
84 * @since 4.0.1
85 */
86 protected function init() {
87
88 $this->set_page_properties();
89
90 $this->page_menu_title = $this->page_title;
91
92 $this->page = Hustle_Data::get_listing_page_by_module_type( $this->module_type );
93
94 $this->page_capability = 'hustle_edit_module';
95
96 $this->page_edit = Hustle_Data::get_wizard_page_by_module_type( $this->module_type );
97
98 $this->page_edit_capability = 'hustle_edit_module';
99
100 /* translators: module's type */
101 $this->page_edit_title = sprintf( esc_html__( 'New %s', 'hustle' ), Opt_In_Utils::get_module_type_display_name( $this->module_type ) );
102
103 add_filter( 'submenu_file', array( $this, 'admin_submenu_file' ), 10, 2 );
104
105 add_action( 'admin_head', array( $this, 'hide_unwanted_submenus' ) );
106
107 // Admin-menu-editor compatibility.
108 add_action( 'admin_menu_editor-menu_replaced', array( $this, 'hide_unwanted_submenus' ) );
109
110 // Actions to perform when the current page is the listing or the wizard page.
111 if ( ! empty( $this->current_page ) && ( $this->current_page === $this->page || $this->current_page === $this->page_edit ) ) {
112 $this->on_listing_and_wizard_actions();
113 }
114 }
115
116 /**
117 * Perform actions during the 'load-{page}' hook.
118 *
119 * @since 7.8.11
120 */
121 public function current_page_loaded() {
122 Hustle_Background_Conversion_Log::get_instance()->process_task();
123 parent::current_page_loaded();
124 }
125
126 /**
127 * Set up the page's own properties
128 * Like the current module type, page title, path to the listing page and wizard page template.
129 *
130 * @since 4.0.2
131 */
132 abstract protected function set_page_properties();
133
134 /**
135 * Actions to be performed on Dashboard page.
136 *
137 * @since 4.0.4
138 */
139 protected function on_listing_and_wizard_actions() {
140
141 if ( $this->page_edit === $this->current_page ) {
142 $this->on_wizard_only_actions();
143 } else {
144 $this->on_listing_only_actions();
145 }
146 }
147
148 /**
149 * Actions to run on listing pages only
150 *
151 * @since 4.2.0
152 */
153 private function on_listing_only_actions() {
154 add_filter( 'removable_query_args', array( $this, 'maybe_remove_paged' ) );
155 add_action( 'admin_enqueue_scripts', array( $this, 'register_listing_scripts' ), 99 );
156 }
157
158 /**
159 * Remove paged get attribute if there isn't a module and it's not the first page
160 *
161 * @since 4.0.0
162 * @param array $removable_query_args URL query args to be removed.
163 * @return array
164 */
165 public function maybe_remove_paged( $removable_query_args ) {
166 $paged = filter_input( INPUT_GET, 'paged', FILTER_VALIDATE_INT );
167 $module_type = $this->module_type;
168
169 if ( $paged && 1 !== $paged && $module_type ) {
170 $args = array(
171 'module_type' => $module_type,
172 'page' => $paged,
173 );
174 $entries_per_page = Hustle_Settings_Admin::get_per_page( 'module' );
175 $modules = Hustle_Module_Collection::instance()->get_all( null, $args, $entries_per_page );
176 if ( empty( $modules ) ) {
177 $_SERVER['REQUEST_URI'] = remove_query_arg( 'paged' );
178 $removable_query_args[] = 'paged';
179 unset( $_GET['paged'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
180 }
181 }
182
183 if ( $module_type ) {
184 self::maybe_view_stats( $module_type );
185 }
186
187 return $removable_query_args;
188 }
189
190 /**
191 * Change pagination page to the relevant one for View Stats links
192 *
193 * @param string $module_type Module type.
194 */
195 private static function maybe_view_stats( $module_type ) {
196 $module_id = filter_input( INPUT_GET, 'view-stats', FILTER_VALIDATE_INT );
197 if ( ! $module_id ) {
198 return;
199 }
200 $module_id = (string) $module_id;
201 $args = array(
202 'module_type' => $module_type,
203 'fields' => 'ids',
204 );
205 $entries_per_page = Hustle_Settings_Admin::get_per_page( 'module' );
206 $modules = Hustle_Module_Collection::instance()->get_all( null, $args );
207 $i = array_search( $module_id, $modules, true );
208 if ( false === $i ) {
209 return;
210 }
211
212 $paged = ceil( ( $i + 1 ) / $entries_per_page );
213 if ( 1 < $paged ) {
214 $_GET['paged'] = $paged;
215 }
216 }
217
218 /**
219 * Actions to run on wizard pages only
220 *
221 * @since 4.0.3
222 */
223 private function on_wizard_only_actions() {
224
225 // Set the current module on Wizards, abort if invalid.
226 $module_id = filter_input( INPUT_GET, 'id', FILTER_VALIDATE_INT );
227 $module = Hustle_Module_Collection::instance()->return_model_from_id( $module_id );
228
229 if ( is_wp_error( $module ) ) {
230 // Redirect asap.
231 add_action( 'admin_init', array( $this, 'redirect_module_not_found' ) );
232 return;
233 }
234
235 $this->module = $module;
236
237 // Scripts for all wizards.
238 add_action( 'admin_enqueue_scripts', array( $this, 'register_wizard_scripts' ) );
239
240 if ( Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $this->module_type ) {
241
242 // Scripts for non ssharing wizards.
243 add_action( 'admin_enqueue_scripts', array( $this, 'register_non_sshare_wizard_scripts' ) );
244
245 // Allow rich editor in.
246 add_filter( 'user_can_richedit', '__return_true' );
247
248 // Set up all the filters and buttons for tinymce editors.
249 $this->set_up_tinymce();
250
251 // Add hustle's button to tinymce editor.
252 add_filter( 'mce_buttons', array( $this, 'register_tinymce_buttons' ) );
253
254 add_filter( 'mce_external_plugins', array( $this, 'add_hustle_tinymce_button_and_remove_externals' ) );
255 }
256 }
257
258 /**
259 * Scripts used in all wizards
260 * They used to be enqueued by Hustle_Module_Admin.
261 *
262 * @since 4.2.0
263 */
264 public function register_wizard_scripts() {
265
266 wp_enqueue_script( 'jquery-ui-sortable' );
267
268 wp_enqueue_script( 'fast_wistia', '//fast.wistia.com/assets/external/E-v1.js', array(), '1', true );
269
270 self::add_color_picker();
271 }
272
273 /**
274 * Scripts used in all listings.
275 * They used to be enqueued by Hustle_Module_Admin.
276 *
277 * @since 4.2.0
278 */
279 public function register_listing_scripts() {
280
281 wp_enqueue_script(
282 'chartjs',
283 Opt_In::$plugin_url . 'assets/js/vendor/chartjs/chart.min.js',
284 array(),
285 '2.7.2',
286 true
287 );
288 }
289
290 /**
291 * Register the scripts used in wizards, but not in the Ssharing one
292 * Scripts used to be enqueued by Hustle_Module_Admin.
293 *
294 * @since 4.2.0
295 */
296 public function register_non_sshare_wizard_scripts() {
297
298 wp_enqueue_script( 'thickbox' );
299 wp_enqueue_media();
300 wp_enqueue_script( 'media-upload' );
301
302 Opt_In_Utils::maybe_add_scripts_for_ie();
303
304 // Datepicker and timpicker for automated email in optins.
305 wp_enqueue_script( 'jquery-ui-datepicker' );
306 wp_enqueue_script(
307 'jquery-ui-timepicker',
308 Opt_In::$plugin_url . 'assets/js/vendor/jquery.timepicker.min.js',
309 array( 'jquery' ),
310 '1.3.5',
311 true
312 );
313
314 wp_enqueue_style(
315 'jquery-ui-timepicker',
316 Opt_In::$plugin_url . 'assets/css/jquery.timepicker.min.css',
317 array(),
318 '1.3.5'
319 );
320
321 // Register moment.js and its timezone extension.
322 // Used for schedule, to calculate time with timezones on client side.
323 wp_enqueue_script(
324 'hustle-moment',
325 Opt_In::$plugin_url . 'assets/js/vendor/moment.min.js',
326 array( 'jquery' ),
327 Opt_In::VERSION,
328 true
329 );
330
331 wp_enqueue_script(
332 'hustle-moment-timezone',
333 Opt_In::$plugin_url . 'assets/js/vendor/moment-timezone-with-data.min.js',
334 array( 'hustle-moment' ),
335 Opt_In::VERSION,
336 true
337 );
338 }
339
340 /**
341 * Removing all MCE external plugins which often break our pages and add Hustle's button
342 *
343 * @since 3.0.8
344 * @param array $external_plugins External plugins.
345 * @return array
346 */
347 public function add_hustle_tinymce_button_and_remove_externals( $external_plugins ) {
348 remove_all_filters( 'mce_external_plugins' );
349
350 $external_plugins = array();
351 $external_plugins['hustle'] = Opt_In::$plugin_url . 'assets/js/vendor/tiny-mce-button.js';
352 add_action( 'admin_footer', array( $this, 'add_tinymce_variables' ) );
353
354 return $external_plugins;
355 }
356
357 /**
358 * Add the current fields to the editor's selector
359 *
360 * @since 3.0.8
361 */
362 public function add_tinymce_variables() {
363
364 $var_button = array();
365 $saved_fields = $this->module->get_form_fields();
366 $var_button = array();
367
368 if ( is_array( $saved_fields ) && ! empty( $saved_fields ) ) {
369 $fields = array();
370 $ignored_fields = Hustle_Entry_Model::ignored_fields();
371
372 foreach ( $saved_fields as $field_name => $data ) {
373 if ( ! in_array( $data['type'], $ignored_fields, true ) ) {
374 $fields[ $field_name ] = esc_html( $data['label'] );
375 }
376 }
377
378 // Add Unsubscribe Link.
379 $fields['hustle_unsubscribe_link'] = esc_html__( 'Unsubscribe Link', 'hustle' );
380
381 $available_editors = array( 'success_message', 'email_body' );
382
383 /**
384 * Print JS details for the custom TinyMCE "Insert Variable" button
385 *
386 * @see assets/js/vendor/tiny-mce-button.js
387 */
388 $var_button = array(
389 /* translators: Plugin name */
390 'button_title' => sprintf( __( 'Add %s Fields', 'hustle' ), Opt_In_Utils::get_plugin_name() ),
391 'fields' => $fields,
392 'available_editors' => $available_editors,
393 );
394 }
395
396 printf(
397 '<script>window.hustleData = %s;</script>',
398 wp_json_encode( $var_button )
399 );
400 }
401
402 /**
403 * Register hustle's button for tinymce
404 *
405 * @since 4.0.0
406 * @since 4.2.0 Moved from Hustle_Module_Admin to this class.
407 *
408 * @param array $buttons Registered buttons.
409 * @return array
410 */
411 public function register_tinymce_buttons( $buttons ) {
412 array_unshift( $buttons, 'hustlefields' );
413 return $buttons;
414 }
415
416 /**
417 * Register the listing page and its wizard
418 *
419 * @return void
420 */
421 public function register_admin_menu() {
422 parent::register_admin_menu();
423
424 $this->page_edit_slug = add_submenu_page( 'hustle', $this->page_edit_title, $this->page_edit_title, $this->page_edit_capability, $this->page_edit, array( $this, 'render_edit_page' ) );
425
426 add_action( 'load-' . $this->page_edit_slug, array( $this, 'current_page_loaded' ) );
427 }
428
429 /**
430 * Get the arguments used when rendering the main page.
431 *
432 * @since 4.0.1
433 * @return array
434 */
435 protected function get_page_template_args() {
436
437 $entries_per_page = Hustle_Settings_Admin::get_per_page( 'module' );
438
439 $capability = array(
440 'hustle_create' => current_user_can( 'hustle_create' ),
441 'hustle_access_emails' => current_user_can( 'hustle_access_emails' ),
442 );
443
444 // Don't use filter_input() here, because of see Hustle_Module_Admin::maybe_remove_paged function.
445 $paged = ! empty( $_GET['paged'] ) ? (int) $_GET['paged'] : 1; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
446
447 $args = array(
448 'module_type' => $this->module_type,
449 'page' => $paged,
450 'filter' => array( 'can_edit' => true ),
451 );
452
453 $search = filter_input( INPUT_GET, 'q' );
454 if ( $search ) {
455 $args['filter']['q'] = $search;
456 }
457
458 $modules = Hustle_Module_Collection::instance()->get_all(
459 null,
460 $args,
461 $entries_per_page
462 );
463
464 $active_modules = Hustle_Module_Collection::instance()->get_all(
465 true,
466 array(
467 'module_type' => $this->module_type,
468 'count_only' => true,
469 )
470 );
471
472 return array(
473 'total' => $this->get_total_count_modules_current_type(),
474 'active' => $active_modules,
475 'modules' => $modules,
476 'is_free' => Opt_In_Utils::is_free(),
477 'capability' => $capability,
478 'entries_per_page' => $entries_per_page,
479 'message' => filter_input( INPUT_GET, 'message', FILTER_SANITIZE_SPECIAL_CHARS ),
480 'sui' => $this->get_sui_summary_config( '' ),
481 );
482 }
483
484 /**
485 * Hide module's edit pages from the submenu on dashboard.
486 *
487 * @since 4.0.1
488 */
489 public function hide_unwanted_submenus() {
490 remove_submenu_page( 'hustle', $this->page_edit );
491 }
492
493 /**
494 * Highlight submenu's parent on admin page.
495 *
496 * @since 4.0.1
497 *
498 * @param string $submenu_file The submenu file.
499 * @param string $parent_file The submenu item's parent file.
500 *
501 * @return string
502 */
503 public function admin_submenu_file( $submenu_file, $parent_file ) {
504 global $plugin_page;
505
506 if ( 'hustle' !== $parent_file ) {
507 return $submenu_file;
508 }
509
510 if ( $this->page_edit === $plugin_page ) {
511 $submenu_file = $this->page;
512 }
513
514 return $submenu_file;
515 }
516
517 /**
518 * Redirect to the listing page when in wizard and the module wasn't found.
519 *
520 * @since 4.0.0
521 */
522 public function redirect_module_not_found() {
523
524 // We're on wizard, but the current module isn't valid. Aborting.
525 $url = add_query_arg(
526 array(
527 'page' => $this->page,
528 'show-notice' => 'error',
529 'notice' => 'module-not-found',
530 'notice-close' => 'false',
531 ),
532 'admin.php'
533 );
534
535 wp_safe_redirect( $url );
536 exit;
537 }
538
539 /**
540 * Add data to the current json array.
541 *
542 * @since 4.3.1
543 *
544 * @return array
545 */
546 protected function get_vars_to_localize() {
547 $current_array = parent::get_vars_to_localize();
548
549 // Wizard page only.
550 if ( $this->module ) {
551
552 $data = $this->module->get_data();
553 $module_metas = $this->module->get_module_metas_as_array();
554
555 $current_array = $this->register_visibility_conditions_js_vars( $current_array );
556
557 $current_array += $this->get_wizard_js_variables_to_localize();
558
559 $current_array['current'] = array_merge(
560 $module_metas,
561 array(
562 'is_optin' => 'optin' === $this->module->module_mode,
563 'listing_page' => $this->page,
564 'wizard_page' => $this->page_edit,
565 'section' => $this->get_current_section(),
566 'data' => $data,
567 'shortcode_id' => $this->module->get_shortcode_id(),
568 )
569 );
570
571 $type_capitalized = Opt_In_Utils::get_module_type_display_name( $this->module_type, false, true );
572 $type_lowercase = Opt_In_Utils::get_module_type_display_name( $this->module_type );
573
574 $messages = array(
575 'module_error' => esc_html__( "Couldn't save your module settings because there were some errors on {page} tab(s). Please fix those errors and try again.", 'hustle' ),
576 'module_error_reload' => esc_html__( 'Something went wrong. Please reload this page and try saving again', 'hustle' ),
577 'module_created' => /* translators: 1. module type capitalized, 2. module type in lowercase */ sprintf( esc_html__( '%1$s created successfully. Get started by adding content to your new %2$s below.', 'hustle' ), $type_capitalized, $type_lowercase ), // only when 'is_new'.
578 );
579
580 $current_array['single_module_action_nonce'] = wp_create_nonce( 'hustle_single_action' );
581
582 $current_array['messages'] = array_merge( $current_array['messages'], $messages );
583
584 // Listing page only.
585 } elseif ( $this->page === $this->current_page ) {
586
587 $current_array['current'] = array(
588 'wizard_page' => $this->page_edit,
589 'module_type' => $this->module_type,
590 );
591
592 $current_array['labels'] = array(
593 'submissions' => Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $this->module_type
594 ? /* translators: number of conversions */ esc_html__( '%d Conversions', 'hustle' )
595 : /* translators: number of shares */ esc_html__( '%d Shares', 'hustle' ),
596 /* translators: number of views */
597 'views' => esc_html__( '%d Views', 'hustle' ),
598 );
599
600 // Also defined in dashboard.
601 $current_array['single_module_action_nonce'] = wp_create_nonce( 'hustle_single_action' );
602
603 $total_modules = $this->get_total_count_modules_current_type();
604 $module_not_found_message = esc_html__( "Oops! The module you are looking for doesn't exist.", 'hustle' );
605
606 if ( 0 < $total_modules && current_user_can( 'hustle_create' ) ) {
607 $module_not_found_message .= sprintf(
608 /* translators: 1. opening 'a' tag for adding a new module, 2. closing 'a' tag, 3. opening 'a' tag for importing */
609 esc_html__( ' You can %1$screate%2$s a new module or %3$simport%2$s an existing module.', 'hustle' ),
610 '<a href="#" class="hustle-create-module">',
611 '</a>',
612 '<a href="#" class="hustle-import-module-button">'
613 );
614 }
615
616 $messages = array(
617 'module_imported' => esc_html__( 'Module successfully imported.', 'hustle' ),
618 'module_duplicated' => esc_html__( 'Module successfully duplicated.', 'hustle' ),
619 'module_tracking_reset' => esc_html__( "Module's tracking data successfully reset.", 'hustle' ),
620 'module_purge_emails' => esc_html__( "Module's Email List successfully purged.", 'hustle' ),
621 'module-not-found' => $module_not_found_message,
622 );
623
624 $current_array['messages'] = array_merge( $current_array['messages'], $messages );
625 }
626
627 // Both Wizard and Listing pages.
628 $current_array['messages']['days_and_months'] = array(
629 'days_full' => Hustle_Time_Helper::get_week_days(),
630 'days_short' => Hustle_Time_Helper::get_week_days( 'short' ),
631 'days_min' => Hustle_Time_Helper::get_week_days( 'min' ),
632 'months_full' => Hustle_Time_Helper::get_months(),
633 'months_short' => Hustle_Time_Helper::get_months( 'short' ),
634 );
635
636 $current_array['module_tabs'] = array(
637 'services' => esc_html__( 'Services', 'hustle' ),
638 'display' => esc_html__( 'Display Options', 'hustle' ),
639 );
640
641 return $current_array;
642 }
643
644 /**
645 * Include the visibility conditions variables required in js side.
646 * These used to be registered in Hustle_Module_Admin before 4.0.3.
647 *
648 * @since 4.0.3
649 *
650 * @param array $vars Current registered variables.
651 * @return array
652 */
653 private function register_visibility_conditions_js_vars( $vars ) {
654
655 $post_ids = array();
656 $page_ids = array();
657 $tag_ids = array();
658 $cat_ids = array();
659 $wc_cat_ids = array();
660 $wc_tag_ids = array();
661 $tags = array();
662 $cats = array();
663 $wc_cats = array();
664 $wc_tags = array();
665
666 $module = Hustle_Module_Model::new_instance( filter_input( INPUT_GET, 'id', FILTER_VALIDATE_INT ) );
667 if ( ! is_wp_error( $module ) ) {
668 $settings = $module->get_visibility()->to_array();
669
670 $post_ids = $this->get_conditions_ids( $settings, 'posts' );
671 $page_ids = $this->get_conditions_ids( $settings, 'pages' );
672 $tag_ids = $this->get_conditions_ids( $settings, 'tags' );
673 $cat_ids = $this->get_conditions_ids( $settings, 'categories' );
674 if ( Opt_In_Utils::is_woocommerce_active() ) {
675 $wc_cat_ids = $this->get_conditions_ids( $settings, 'wc_categories' );
676 $wc_tag_ids = $this->get_conditions_ids( $settings, 'wc_tags' );
677 }
678 }
679
680 if ( $tag_ids ) {
681 $tags = array_map(
682 array( $this, 'terms_to_select2_data' ),
683 get_categories(
684 array(
685 'hide_empty' => false,
686 'include' => $tag_ids,
687 'taxonomy' => 'post_tag',
688 )
689 )
690 );
691 }
692
693 if ( $cat_ids ) {
694 $cats = array_map(
695 array( $this, 'terms_to_select2_data' ),
696 get_categories(
697 array(
698 'include' => $cat_ids,
699 'hide_empty' => false,
700 )
701 )
702 );
703 }
704
705 if ( $wc_cat_ids ) {
706 $wc_cats = array_map(
707 array( $this, 'terms_to_select2_data' ),
708 get_categories(
709 array(
710 'include' => $wc_cat_ids,
711 'hide_empty' => false,
712 'taxonomy' => 'product_cat',
713 )
714 )
715 );
716 }
717
718 if ( $wc_tag_ids ) {
719 $wc_tags = array_map(
720 array( $this, 'terms_to_select2_data' ),
721 get_categories(
722 array(
723 'include' => $wc_tag_ids,
724 'hide_empty' => false,
725 'taxonomy' => 'product_tag',
726 )
727 )
728 );
729 }
730
731 $posts = Opt_In_Utils::get_select2_data( 'post', $post_ids );
732
733 $pages = Opt_In_Utils::get_select2_data( 'page', $page_ids );
734
735 /**
736 * Add all custom post types
737 */
738 $post_types = array();
739 $cpts = get_post_types(
740 array(
741 'public' => true,
742 '_builtin' => false,
743 ),
744 'objects'
745 );
746 foreach ( $cpts as $cpt ) {
747
748 // Skip ms_invoice.
749 if ( 'ms_invoice' === $cpt->name ) {
750 continue;
751 }
752
753 $cpt_ids = $this->get_conditions_ids( $settings, $cpt->name );
754
755 $cpt_array['name'] = $cpt->name;
756 $cpt_array['label'] = $cpt->label;
757 $cpt_array['data'] = Opt_In_Utils::get_select2_data( $cpt->name, $cpt_ids );
758
759 $post_types[ $cpt->name ] = $cpt_array;
760 }
761
762 $vars['cats'] = $cats;
763 $vars['wc_cats'] = $wc_cats;
764 $vars['wc_tags'] = $wc_tags;
765 $vars['tags'] = $tags;
766 $vars['posts'] = $posts;
767 $vars['post_types'] = $post_types;
768 $vars['pages'] = $pages;
769
770 $vars['countries'] = Opt_In_Utils::get_countries();
771 $vars['roles'] = Opt_In_Utils::get_user_roles();
772 $vars['templates'] = Opt_In_Utils::hustle_get_page_templates();
773
774 $vars['type_singular_lower'] = Opt_In_Utils::get_module_type_display_name( $this->module_type );
775
776 // Visibility conditions titles, labels and bodies.
777 $vars['messages']['conditions'] = array(
778 'visitor_logged_in' => __( 'Logged in status', 'hustle' ),
779 'shown_less_than' => __( 'Number of times visitor has seen this module', 'hustle' ),
780 'only_on_mobile' => __( "Visitor's Device", 'hustle' ),
781 'from_specific_ref' => __( 'Referrer', 'hustle' ),
782 'from_search_engine' => __( 'Source of Arrival', 'hustle' ),
783 'on_specific_url' => __( 'Specific URL', 'hustle' ),
784 'on_specific_browser' => __( "Visitor's Browser", 'hustle' ),
785 'visitor_has_never_commented' => __( 'Visitor Commented Before', 'hustle' ),
786 'not_in_a_country' => __( "Visitor's Country", 'hustle' ),
787 'on_specific_roles' => __( 'User Roles', 'hustle' ),
788 'wp_conditions' => __( 'Static Pages', 'hustle' ),
789 'archive_pages' => __( 'Archive Pages', 'hustle' ),
790 'on_specific_templates' => __( 'Page Templates', 'hustle' ),
791 'user_registration' => __( 'After Registration', 'hustle' ),
792 'page_404' => __( '404 page', 'hustle' ),
793 'posts' => __( 'Posts', 'hustle' ),
794 'pages' => __( 'Pages', 'hustle' ),
795 'categories' => __( 'Categories', 'hustle' ),
796 'tags' => __( 'Tags', 'hustle' ),
797 'wc_pages' => __( 'WooCommerce Pages', 'hustle' ),
798 'wc_categories' => __( 'WooCommerce Categories', 'hustle' ),
799 'wc_tags' => __( 'WooCommerce Tags', 'hustle' ),
800 'wc_archive_pages' => __( 'WooCommerce Archives', 'hustle' ),
801 'wc_static_pages' => __( 'WooCommerce Static Pages', 'hustle' ),
802 'cookie_set' => __( 'Browser Cookie', 'hustle' ),
803 );
804
805 $vars['messages']['condition_labels'] = array(
806 'mobile_only' => __( 'Mobile only', 'hustle' ),
807 'desktop_only' => __( 'Desktop only', 'hustle' ),
808 'any_conditions' => __( '{number} condition(s)', 'hustle' ),
809 'number_views' => '< {number}',
810 'number_views_more' => '> {number}',
811 'any' => __( 'Any', 'hustle' ),
812 'all' => __( 'All', 'hustle' ),
813 'no' => __( 'No', 'hustle' ),
814 'none' => __( 'None', 'hustle' ),
815 'true' => __( 'True', 'hustle' ),
816 'false' => __( 'False', 'hustle' ),
817 'logged_in' => __( 'Logged in', 'hustle' ),
818 'logged_out' => __( 'Logged out', 'hustle' ),
819 'only_these' => __( 'Only {number}', 'hustle' ),
820 'except_these' => __( 'All except {number}', 'hustle' ),
821 'reg_date' => __( 'Day {number} ', 'hustle' ),
822 'immediately' => __( 'Immediately', 'hustle' ),
823 'forever' => __( 'Forever', 'hustle' ),
824 'cookie_anything' => __( '{name} is anything', 'hustle' ),
825 'cookie_doesnt_exist' => __( '{name} does not exist', 'hustle' ),
826 'cookie_value' => __( '{name} {value_condition} {value}', 'hustle' ),
827 );
828
829 $vars['wp_conditions'] = array(
830 'is_front_page' => __( 'Front page', 'hustle' ),
831 'is_404' => __( '404 page', 'hustle' ),
832 'is_search' => __( 'Search results', 'hustle' ),
833 );
834
835 $vars['wc_static_pages'] = array(
836 'is_cart' => __( 'Cart', 'hustle' ),
837 'is_checkout' => __( 'Checkout', 'hustle' ),
838 'is_order_received' => __( 'Order Received', 'hustle' ),
839 'is_account_page' => __( 'My account', 'hustle' ),
840 );
841
842 $vars['archive_pages'] = array(
843 'is_category' => __( 'Category archive', 'hustle' ),
844 'is_tag' => __( 'Tag archive', 'hustle' ),
845 'is_author' => __( 'Author archive', 'hustle' ),
846 'is_date' => __( 'Date archive', 'hustle' ),
847 'is_post_type_archive' => __( 'Custom post archive', 'hustle' ),
848 );
849
850 $vars['wc_archive_pages'] = array(
851 'is_shop' => __( 'Shop', 'hustle' ),
852 'is_product_category' => __( 'Product Category', 'hustle' ),
853 'is_product_tag' => __( 'Product Tag', 'hustle' ),
854 );
855
856 $vars['less_than_expiration'] = array(
857 1 => __( 'Day', 'hustle' ),
858 7 => __( 'Week', 'hustle' ),
859 30 => __( 'Month', 'hustle' ),
860 365 => __( 'Year', 'hustle' ),
861 );
862
863 $vars['wp_cookie_set'] = array(
864 'anything' => __( 'is anything', 'hustle' ),
865 'equals' => __( 'equals', 'hustle' ),
866 'contains' => __( 'contains', 'hustle' ),
867 'matches_pattern' => __( 'matches a pattern', 'hustle' ),
868 'doesnt_equals' => __( 'does not equals', 'hustle' ),
869 'doesnt_contain' => __( 'does not contain', 'hustle' ),
870 'doesnt_match_pattern' => __( 'does not match a pattern', 'hustle' ),
871 'less_than' => __( 'is less than', 'hustle' ),
872 'less_equal_than' => __( 'is less or equal to', 'hustle' ),
873 'greater_than' => __( 'is greater than', 'hustle' ),
874 'greater_equal_than' => __( 'is greater or equal to', 'hustle' ),
875 );
876
877 $vars['roles'] = Opt_In_Utils::get_user_roles();
878 $vars['browsers'] = Opt_In_Utils::get_browsers();
879 $vars['countries'] = Opt_In_Utils::get_countries();
880 $vars['templates'] = Opt_In_Utils::hustle_get_page_templates();
881
882 return $vars;
883 }
884
885 /**
886 * Gets the JS variables to be localized in Wizard for non-ssharing modules.
887 * This method is overwritten in Hustle_Sshare_Admin.
888 *
889 * @since 4.3.0
890 *
891 * @return array
892 */
893 protected function get_wizard_js_variables_to_localize() {
894 $is_optin = Hustle_Module_Model::OPTIN_MODE === $this->module->module_mode;
895
896 $variables = array(
897 'form_fields' => $this->get_form_fields_js_vars(),
898 'schedule' => $this->get_schedule_js_vars(),
899 'palettes' => Hustle_Palettes_Helper::get_all_palettes( $is_optin ),
900 'integrations' => array(
901 'action_nonce' => wp_create_nonce( 'hustle_provider_action' ), // Also defined in integrations.
902 'fetching_list' => esc_html__( 'Fetching integration list…', 'hustle' ),
903 ),
904 'typography' => array(
905 'global_font_applied' => esc_html__( 'Your font was correctly applied to all elements', 'hustle' ),
906 'fetch_nonce' => wp_create_nonce( 'hustle_fetch_font_families' ),
907 ),
908 'media_uploader' => array(
909 'select_or_upload' => esc_html__( 'Select or Upload Image', 'hustle' ),
910 'use_this_image' => esc_html__( 'Use this image', 'hustle' ),
911 ),
912 'triggers' => array(
913 'immediately_tag' => esc_html__( 'Immediately', 'hustle' ),
914 'seconds' => esc_html__( 'seconds', 'hustle' ),
915 'minutes' => esc_html__( 'minutes', 'hustle' ),
916 'hours' => esc_html__( 'hours', 'hustle' ),
917 /* translators: 1. {time} tag to replace, 2. {unit} tag to replace */
918 'delayed_tag' => sprintf( esc_html__( 'Delay %1$s %2$s', 'hustle' ), '{time}', '{unit}' ),
919 /* translators: {value}% tag to replace */
920 'scroll_percentage_tag' => sprintf( esc_html__( '%s page scroll', 'hustle' ), '{value}%' ),
921 /* translators: {value} tag to replace */
922 'scroll_element_tag' => sprintf( esc_html__( 'Scroll to %s', 'hustle' ), '{value}' ),
923 ),
924 );
925
926 $variables['defaults'] = $this->get_defaults_settings();
927
928 return $variables;
929 }
930
931 /**
932 * Return default design settings
933 *
934 * @return array
935 */
936 private function get_defaults_settings() {
937 $defaults = $this->module->get_design()->get_border_spacing_shadow_defaults( 'desktop' );
938 $defaults += $this->module->get_design()->get_typography_defaults( 'desktop' );
939 $defaults += $this->module->get_design()->get_border_spacing_shadow_defaults( 'mobile' );
940 $defaults += $this->module->get_design()->get_typography_defaults( 'mobile' );
941
942 $design_settings = $this->module->get_design()->to_array();
943 if ( ! empty( $design_settings['base_template'] ) ) {
944 // Return template settings if the module was created based on one.
945 $templates_helper = new Hustle_Templates_Helper();
946 $template_settings = $templates_helper->get_template( $design_settings['base_template'], $this->module->module_mode );
947 if ( ! empty( $template_settings['design'] ) ) {
948 $defaults = array_merge( $defaults, $template_settings['design'] );
949 }
950 }
951
952 if ( ! empty( $design_settings['base_template'] ) ) {
953 $defaults['base_template'] = $design_settings['base_template'];
954 }
955
956 return $defaults;
957 }
958
959 /**
960 * Include the form fields variables required in js side.
961 * These used to be registered in Hustle_Module_Admin before 4.0.3.
962 *
963 * @since 4.0.3
964 *
965 * @return array
966 */
967 private function get_form_fields_js_vars() {
968 $renderer = $this->get_renderer();
969
970 $no_fields_notice_args = array(
971 array(
972 'type' => 'inline_notice',
973 'icon' => 'info',
974 'value' => __( 'You don\'t have any {field_type} field in your opt-in form.', 'hustle' ),
975 ),
976 );
977
978 $variables = array(
979 'no_fields_of_type_notice' => $renderer->get_html_for_options( $no_fields_notice_args, true ),
980 'is_required' => __( '{field} is required.', 'hustle' ),
981 'cant_empty' => __( 'This field can\'t be empty.', 'hustle' ),
982 'url_required_error_message' => __( 'Your website url is required.', 'hustle' ),
983 'required_error_message' => __( 'Your {field} is required.', 'hustle' ),
984 'date_validation_message' => __( 'Please enter a valid date.', 'hustle' ),
985 'time_validation_message' => __( 'Please enter a valid time.', 'hustle' ),
986 'validation_message' => __( 'Please enter a valid {field}.', 'hustle' ),
987 'recaptcha_error_message' => __( 'reCAPTCHA verification failed. Please try again.', 'hustle' ),
988 'recaptcha_validation_message' => __( 'reCAPTCHA verification failed. Please try again.', 'hustle' ),
989 'turnstile_validation_message' => __( 'Turnstile verification failed. Please try again.', 'hustle' ),
990 'gdpr_required_error_message' => __( 'Please accept the terms and try again.', 'hustle' ),
991 /* translators: 1. opening 'a' tag, 2. closing 'a' tag */
992 'gdpr_message' => sprintf( __( 'I\'ve read and accept the %1$sterms & conditions%2$s', 'hustle' ), '<a href="#">', '</a>' ),
993 'label' => array(
994 'placeholder' => __( 'Enter placeholder here', 'hustle' ),
995 'name_label' => __( 'Name', 'hustle' ),
996 'name_placeholder' => __( 'E.g. John', 'hustle' ),
997 'email_label' => __( 'Email Address', 'hustle' ),
998 'enail_placeholder' => __( 'E.g. john@doe.com', 'hustle' ),
999 'phone_label' => __( 'Phone Number', 'hustle' ),
1000 'phone_placeholder' => __( 'E.g. +1 300 400 500', 'hustle' ),
1001 'address_label' => __( 'Address', 'hustle' ),
1002 'address_placeholder' => '',
1003 'hidden_label' => __( 'Hidden Field', 'hustle' ),
1004 'hidden_placeholder' => '',
1005 'url_label' => __( 'Website', 'hustle' ),
1006 'url_placeholder' => __( 'E.g. https://example.com', 'hustle' ),
1007 'text_label' => __( 'Text', 'hustle' ),
1008 'text_placeholder' => __( 'E.g. Enter your nick name', 'hustle' ),
1009 'number_label' => __( 'Number', 'hustle' ),
1010 'number_placeholder' => __( 'E.g. 1', 'hustle' ),
1011 'datepicker_label' => __( 'Date', 'hustle' ),
1012 'datepicker_placeholder' => __( 'Choose date', 'hustle' ),
1013 'timepicker_label' => __( 'Time', 'hustle' ),
1014 'timepicker_placeholder' => '',
1015 'recaptcha_label' => 'reCAPTCHA',
1016 'recaptcha_placeholder' => '',
1017 'turnstile_label' => __( 'Cloudflare Turnstile', 'hustle' ),
1018 'turnstile_placeholder' => '',
1019 'gdpr_label' => __( 'GDPR', 'hustle' ),
1020 ),
1021 'recaptcha_badge_replacement' => sprintf(
1022 /* translators: 1: closing 'a' tag, 2: opening privacy 'a' tag, 3: opening terms 'a' tag */
1023 __( 'This site is protected by reCAPTCHA and the Google %2$sPrivacy Policy%1$s and %3$sTerms of Service%1$s apply.', 'hustle' ),
1024 '</a>',
1025 '<a href="https://policies.google.com/privacy" target="_blank">',
1026 '<a href="https://policies.google.com/terms" target="_blank">'
1027 ),
1028 );
1029
1030 return $variables;
1031 }
1032
1033 /**
1034 * Includes the variables used for the Schedule functionality.
1035 *
1036 * @since 4.3.0
1037 *
1038 * @return array
1039 */
1040 private function get_schedule_js_vars() {
1041 $type_lowercase = Opt_In_Utils::get_module_type_display_name( $this->module_type );
1042
1043 return array(
1044 'wp_gmt_offset' => get_option( 'gmt_offset' ),
1045 'new_schedule_set' => sprintf(
1046 /* translators: 1. module type in lowercase */
1047 esc_html__( 'Successfully added a schedule for your %1$s. However, make sure to save changes and publish your %1$s for it to start appearing as per your schedule.', 'hustle' ),
1048 esc_html( $type_lowercase )
1049 ),
1050 'months' => Hustle_Time_Helper::get_months(),
1051 'week_days' => Hustle_Time_Helper::get_week_days( 'short' ),
1052 'meridiem' => Hustle_Time_Helper::get_meridiam_periods(),
1053 );
1054 }
1055
1056 /**
1057 * Gets the conditions ID.
1058 *
1059 * @since 3.0.7
1060 * @since 4.0.3 moved from Hustle_Modules_Admin to here.
1061 *
1062 * @param array $settings Display settings.
1063 * @param string $type posts|pages|tags|categories|{cpt}.
1064 * @return array
1065 */
1066 private function get_conditions_ids( $settings, $type ) {
1067 $ids = array();
1068 if ( ! empty( $settings['conditions'] ) ) {
1069 foreach ( $settings['conditions'] as $conditions ) {
1070 if ( ! empty( $conditions[ $type ] )
1071 && ( ! empty( $conditions[ $type ][ $type ] )
1072 || ! empty( $conditions[ $type ]['selected_cpts'] ) ) ) {
1073 $new_ids = ! empty( $conditions[ $type ][ $type ] )
1074 ? $conditions[ $type ][ $type ]
1075 : $conditions[ $type ]['selected_cpts'];
1076
1077 $ids = array_merge( $ids, $new_ids );
1078 }
1079 }
1080 }
1081
1082 return array_unique( $ids );
1083 }
1084
1085 /**
1086 * Converts term object to usable object for select2
1087 *
1088 * @since 4.0.3 moved from Hustle_Modules_Admin to here.
1089 * @param stdClass $term Term.
1090 * @return stdClass
1091 */
1092 public static function terms_to_select2_data( $term ) {
1093 $obj = new stdClass();
1094 $obj->id = $term->term_id;
1095 $obj->text = $term->name;
1096 return $obj;
1097 }
1098
1099 /**
1100 * Render the module's wizard page.
1101 *
1102 * @since 4.0.1
1103 */
1104 public function render_edit_page() {
1105
1106 $template_args = $this->get_page_edit_template_args();
1107 $allowed = Opt_In_Utils::is_user_allowed( 'hustle_edit_module', $template_args['module_id'] );
1108 if ( ! $allowed ) {
1109 wp_die( esc_html__( 'Sorry, you are not allowed to access this page.' ), 403 );
1110 }
1111
1112 if ( Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $this->module_type ) {
1113 wp_enqueue_editor();
1114 }
1115
1116 $main_class = $this->get_sui_wrap_class();
1117
1118 ?>
1119 <main class="<?php echo esc_attr( $main_class ); ?>">
1120
1121 <?php
1122 $renderer = $this->get_renderer();
1123 $renderer->render( $this->page_edit_template_path, $template_args );
1124 ?>
1125
1126 </main>
1127 <?php
1128 }
1129
1130 /**
1131 * Get the args for the wizard page.
1132 *
1133 * @since 4.0.1
1134 * @return array
1135 */
1136 protected function get_page_edit_template_args() {
1137 return array(
1138 'section' => $this->get_current_section( 'content' ),
1139 'module_id' => $this->module->module_id,
1140 'module' => $this->module,
1141 'is_active' => (bool) $this->module->active,
1142 'is_optin' => ( 'optin' === $this->module->module_mode ),
1143 );
1144 }
1145
1146 /**
1147 * Gets the count of the modules (both active and non active) of the current type.
1148 *
1149 * @since 4.2.0
1150 * @return int
1151 */
1152 private function get_total_count_modules_current_type() {
1153
1154 if ( is_null( $this->module_count_type ) ) {
1155 $args = array(
1156 'module_type' => $this->module_type,
1157 'count_only' => true,
1158 );
1159 $search = filter_input( INPUT_GET, 'q' );
1160 if ( $search ) {
1161 $args['filter']['q'] = $search;
1162 }
1163 $this->module_count_type = Hustle_Module_Collection::instance()->get_all(
1164 null,
1165 $args
1166 );
1167 }
1168 return $this->module_count_type;
1169 }
1170
1171 /**
1172 * Get data needed for rendering the tracking charts in the listing pages
1173 * Retrieved via AJAX.
1174 *
1175 * @since 4.0.4
1176 * @since 4.2.0 Moved from Hustle_Module_Model to this class, and $module_id param added.
1177 *
1178 * @param int $module_id ID of the requested module.
1179 */
1180 public static function get_tracking_charts_markup( $module_id ) {
1181
1182 $module = Hustle_Model::get_module( $module_id );
1183 if ( is_wp_error( $module ) ) {
1184 return '';
1185 }
1186
1187 $tracking_model = Hustle_Tracking_Model::get_instance();
1188 $total_module_conversions = $tracking_model->count_tracking_data( $module_id, 'all_conversion' );
1189 $total_module_views = $tracking_model->count_tracking_data( $module_id, 'view' );
1190 $last_entry_time = $tracking_model->get_latest_conversion_time_by_module_id( $module_id );
1191 $rate = $total_module_views ? round( ( $total_module_conversions * 100 ) / $total_module_views, 1 ) : 0;
1192 $module_sub_types = $module->get_sub_types( true );
1193
1194 $multiple_charts = array();
1195
1196 // Get each sub type's tracking data if the module type has sub types.
1197 if ( ! empty( $module_sub_types ) ) {
1198
1199 foreach ( $module_sub_types as $slug => $display_name ) {
1200
1201 $subtype = $module->module_type . '_' . $slug;
1202 $views = $tracking_model->count_tracking_data( $module_id, 'view', $subtype );
1203 $conversions = $tracking_model->count_tracking_data( $module_id, 'all_conversion', $subtype );
1204 $conversion_rate = $views ? round( ( $conversions * 100 ) / $views, 1 ) : 0;
1205
1206 $multiple_charts[ $slug ] = array(
1207 'display_name' => $display_name,
1208 'last_entry_time' => $tracking_model->get_latest_conversion_time_by_module_id( $module_id, $subtype ),
1209 'views' => $views,
1210 'conversions' => $conversions,
1211 'conversion_rate' => $conversion_rate,
1212 );
1213 }
1214 }
1215
1216 $render_arguments = array(
1217 'module' => $module,
1218 'total_module_views' => $total_module_views,
1219 'total_module_conversions' => $total_module_conversions,
1220 'tracking_types' => $module->get_tracking_types(),
1221 'last_entry_time' => $last_entry_time,
1222 'rate' => $rate,
1223 );
1224
1225 if ( $module->get_content()->has_cta() ) {
1226 $notice_for_old_data = $tracking_model->has_old_tracking_data( $module_id );
1227 $render_arguments['notice_for_old_data'] = $notice_for_old_data;
1228 }
1229
1230 ob_start();
1231
1232 $renderer = new Hustle_Layout_Helper();
1233
1234 // ELEMENT: Tracking data.
1235 $renderer->render(
1236 'admin/commons/sui-listing/elements/tracking-data',
1237 array(
1238 'render_arguments' => $render_arguments,
1239 'multiple_charts' => $multiple_charts,
1240 )
1241 );
1242
1243 $html = ob_get_clean();
1244
1245 $charts_data = self::get_charts_data( array_keys( $module_sub_types ), $total_module_views, $module );
1246
1247 $data = array(
1248 'html' => $html,
1249 'charts_data' => $charts_data,
1250 );
1251
1252 return $data;
1253 }
1254
1255 /**
1256 * Get tracking data for building charts on listing page
1257 *
1258 * @since 4.0.4
1259 * @since 4.2.0 Move from Hustle_Module_Model to this class. $module param added.
1260 *
1261 * @param array $sub_types Module's sub types.
1262 * @param int $views Module's views count.
1263 * @param Hustle_Model $module Instance of the module to get the charts data for.
1264 * @return array
1265 */
1266 private static function get_charts_data( $sub_types, $views, Hustle_Model $module ) {
1267
1268 $sql_month_start_date = date( 'Y-m-d H:i:s', strtotime( '-30 days midnight' ) );// phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
1269 $tracking_model = Hustle_Tracking_Model::get_instance();
1270 $days_array = array();
1271 $default_array = array();
1272
1273 for ( $h = 30; $h >= 0; $h-- ) {
1274 $time = strtotime( '-' . $h . ' days' );
1275 $date = date( 'Y-m-d', $time );// phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
1276 $default_array[ $date ] = 0;
1277 $days_array[] = date( 'M j, Y', $time );// phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
1278 }
1279
1280 $sub_types[] = 'overall';
1281 $conversion_types = array( 'all' );
1282
1283 if ( Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $module->module_type ) {
1284 $conversion_types[] = 'cta';
1285 $conversion_types[] = 'cta_2';
1286
1287 if ( Hustle_Module_Model::OPTIN_MODE === $module->module_mode ) {
1288 $conversion_types[] = 'optin';
1289 }
1290 }
1291
1292 $data = array();
1293 foreach ( $sub_types as $sub_type ) {
1294
1295 $chart_container_id = sprintf(
1296 'hustle-%1$s-%2$s-stats--%3$s',
1297 $module->module_type,
1298 $module->module_id,
1299 $sub_type
1300 );
1301 $data[ $sub_type ] = array(
1302 'id' => $chart_container_id,
1303 'days' => $days_array,
1304 );
1305
1306 foreach ( $conversion_types as $conversion_type ) {
1307
1308 $last_month_conversions = $tracking_model->get_form_latest_tracking_data_count_grouped_by_day( $module->module_id, $sql_month_start_date, $conversion_type . '_conversion', $module->module_type, $sub_type );
1309 $last_month_views = $tracking_model->get_form_latest_tracking_data_count_grouped_by_day( $module->module_id, $sql_month_start_date, 'view', $module->module_type, $sub_type );
1310
1311 if ( ! $last_month_conversions ) {
1312 $submissions_data = $default_array;
1313 } else {
1314 $submissions_array = wp_list_pluck( $last_month_conversions, 'tracked_count', 'date_created' );
1315 $submissions_data = array_merge( $default_array, array_intersect_key( $submissions_array, $default_array ) );
1316 }
1317
1318 if ( ! $last_month_views ) {
1319 $views_data = $default_array;
1320 } else {
1321 $views_array = wp_list_pluck( $last_month_views, 'tracked_count', 'date_created' );
1322 $views_data = array_merge( $default_array, array_intersect_key( $views_array, $default_array ) );
1323 }
1324
1325 $query_sub_type = 'overall' === $sub_type ? null : $module->module_type . '_' . $sub_type;
1326 $query_conversion_type = $conversion_type . '_conversion';
1327 $sub_type_conversions = $tracking_model->count_tracking_data( $module->module_id, $query_conversion_type, $query_sub_type );
1328
1329 $data[ $sub_type ][ $conversion_type ] = array(
1330 'conversions_count' => $sub_type_conversions,
1331 'conversion_rate' => $views ? round( ( $sub_type_conversions * 100 ) / $views, 1 ) : 0,
1332 'conversions' => array_values( $submissions_data ),
1333 );
1334 }
1335
1336 $data[ $sub_type ]['views'] = array_values( $views_data );
1337 $data[ $sub_type ]['conversions'] = array_values( $submissions_data );
1338 }
1339
1340 return $data;
1341 }
1342
1343 /**
1344 * Returns whether the current module is optin.
1345 * This method should be only used in Wizard pages
1346 * because $this->module is only defined within Wizards.
1347 *
1348 * @since 4.3.0
1349 * @return boolean
1350 */
1351 public function is_optin_module() {
1352 return ! empty( $this->module ) && 'optin' === $this->module->module_mode;
1353 }
1354
1355 /**
1356 * Gets the font family names.
1357 * Used for rendering the selects.
1358 *
1359 * @since 4.3.0
1360 * @return array
1361 */
1362 public function get_font_families() {
1363 return Hustle_Meta_Base_Design::get_font_families_names();
1364 }
1365 }
1366