PluginProbe ʕ •ᴥ•ʔ
Hustle – Email Marketing, Lead Generation, Optins, Popups / 7.8.9
Hustle – Email Marketing, Lead Generation, Optins, Popups v7.8.9
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 / front / hustle-module-front.php
wordpress-popup / inc / front Last commit date
non-sshare-styles 9 months ago class-hustle-decorator-non-sshare.php 3 years ago class-hustle-decorator-sshare.php 9 months ago class-hustle-decorator_abstract.php 3 years ago class-hustle-module-preview.php 1 year ago hustle-module-front-ajax.php 1 year ago hustle-module-front.php 2 years ago hustle-module-renderer.php 9 months ago hustle-renderer-abstract.php 3 years ago hustle-renderer-sshare.php 3 years ago
hustle-module-front.php
1118 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Hustle_Module_Front
4 *
5 * @package Hustle
6 */
7
8 /**
9 * Class Hustle_Module_Front
10 */
11 class Hustle_Module_Front {
12
13 /**
14 * Modules
15 *
16 * @var array
17 */
18 private $modules = array();
19
20 /**
21 * Contains the queued modules types as keys, 1 as the value.
22 * Used to queue the required styles only.
23 *
24 * @since 4.0.1
25 * @var array
26 */
27 private $module_types_to_display = array();
28 /**
29 * Non inline modules
30 *
31 * @var array
32 */
33 private $non_inline_modules = array();
34 /**
35 * Inline modules
36 *
37 * @var array
38 */
39 private $inline_modules = array();
40
41 /**
42 * Array with data about the modules.
43 * This is used to conditionally add scripts.
44 *
45 * @since 4.0.4
46 * @var array
47 */
48 private $modules_data_for_scripts = array();
49
50 /**
51 * Filter property for the_content
52 *
53 * @var int
54 */
55 private static $the_content_filter_priority = 20;
56
57 const SHORTCODE = 'wd_hustle';
58
59 /**
60 * Hustle_Module_Front constructor.
61 *
62 * @since unknown
63 */
64 public function __construct() {
65 // Schedule email cron action.
66 add_action( 'hustle_send_email', array( 'Hustle_Mail', 'send_email' ), 10, 3 );
67 add_action( 'hustle_aweber_token_refresh', array( 'Hustle_Aweber', 'refresh_token' ) );
68
69 // Used for Gutenberg.
70 add_action( 'wp_ajax_hustle_render_unsubscribe_form', array( $this, 'get_unsubscribe_form' ) );
71
72 $is_preview = filter_input( INPUT_GET, 'hustle_preview', FILTER_VALIDATE_BOOLEAN ) && Opt_In_Utils::is_user_allowed( 'hustle_edit_module' );
73
74 // Don't render Hustle's widgets and shortcodes on preview mode.
75 if ( ! $is_preview ) {
76 // These are used on admin side.
77 $this->register_shortcodes_and_widget();
78 }
79
80 /**
81 * Allow third-party devs to prevent Hustle from initializing on their frontend pages.
82 * This is useful on previews, for example.
83 *
84 * @since 4.4.5
85 */
86 $prevent_front_init = apply_filters( 'hustle_prevent_front_initialization', false );
87
88 // Abort if it's admin or is a preview.
89 // phpcs:disable WordPress.Security.NonceVerification.Recommended
90 if (
91 is_admin() ||
92 isset( $_GET['widgetPreview'] ) || // For the widgets preview.
93 isset( $_GET['ct_builder'] ) || // For Oxygen builder.
94 isset( $_GET['elementor-preview'] ) || // prevent initial in elementor.
95 $prevent_front_init
96 ) {
97 return;
98 }
99 // phpcs:enable
100
101 if ( ! $is_preview ) {
102 $this->prepare_for_front();
103 } else {
104 new Hustle_Module_Preview();
105 }
106
107 add_action( 'post_updated', array( __CLASS__, 'maybe_unsubscribe_page' ), 10, 3 );
108 }
109
110 /**
111 * Prepare for front
112 */
113 private function prepare_for_front() {
114 Hustle_Provider_Autoload::initiate_providers();
115 Hustle_Provider_Autoload::load_block_editor();
116
117 add_action(
118 'wp_enqueue_scripts',
119 array( $this, 'register_scripts' )
120 );
121
122 // Enqueue it in the footer to overrider all the css that comes with the popup.
123 add_action(
124 'wp_footer',
125 array( $this, 'register_styles' )
126 );
127
128 add_action( 'wp_head', array( $this, 'preload_custom_font' ) );
129
130 add_action(
131 'template_redirect',
132 array( $this, 'create_modules' ),
133 0
134 );
135
136 add_action( 'template_redirect', array( $this, 'render_non_inline_modules' ) );
137
138 add_filter( 'get_the_excerpt', array( $this, 'remove_the_content_filter' ), 9 );
139 add_filter( 'wp_trim_excerpt', array( $this, 'restore_the_content_filter' ) );
140
141 add_filter(
142 'the_content',
143 array( $this, 'show_after_page_post_content' ),
144 self::$the_content_filter_priority
145 );
146
147 // NextGEN Gallery compat.
148 add_filter(
149 'run_ngg_resource_manager',
150 array( $this, 'nextgen_compat' )
151 );
152 }
153
154 /**
155 * Register shortcodes and widget.
156 *
157 * @since 4.3.1
158 *
159 * @return void
160 */
161 private function register_shortcodes_and_widget() {
162 if ( Hustle_Settings_Admin::global_tracking() ) {
163 add_action( 'widgets_init', array( $this, 'register_widget' ) );
164 }
165 add_shortcode( self::SHORTCODE, array( $this, 'shortcode' ) );
166
167 // Legacy custom content support.
168 add_shortcode(
169 'wd_hustle_cc',
170 array( $this, 'shortcode' )
171 );
172
173 // Legacy social sharing support.
174 add_shortcode(
175 'wd_hustle_ss',
176 array( $this, 'shortcode' )
177 );
178
179 // Unsubscribe shortcode.
180 add_shortcode(
181 'wd_hustle_unsubscribe',
182 array( $this, 'unsubscribe_shortcode' )
183 );
184 }
185
186 /**
187 * Don't apply the_content filter for excerpts.
188 *
189 * @param string $post_excerpt The post's excerpt.
190 */
191 public function remove_the_content_filter( $post_excerpt ) {
192 remove_filter( 'the_content', array( $this, 'show_after_page_post_content' ), self::$the_content_filter_priority );
193
194 return $post_excerpt;
195 }
196
197 /**
198 * Restore the content filter
199 *
200 * @param string $text Text.
201 * @return string
202 */
203 public function restore_the_content_filter( $text ) {
204 add_filter( 'the_content', array( $this, 'show_after_page_post_content' ), self::$the_content_filter_priority );
205
206 return $text;
207 }
208
209 /**
210 * Register widget
211 */
212 public function register_widget() {
213 register_widget( 'Hustle_Module_Widget' );
214 register_widget( 'Hustle_Module_Widget_Legacy' );
215 }
216
217 /**
218 * Register scripts
219 *
220 * @return null
221 */
222 public function register_scripts() {
223 global $post;
224 $unsubscribe_shortcode = false;
225 // Check for shortcode wd_hustle_unsubscribe.
226 if ( $post && preg_match( '/wd_hustle_unsubscribe/', $post->post_content ) ) {
227 $unsubscribe_shortcode = true;
228 }
229
230 // There aren't any published modules. We don't need scripts.
231 if ( ! count( $this->modules ) && ! $unsubscribe_shortcode ) {
232 return;
233 }
234
235 $is_on_upfront_builder = class_exists( 'UpfrontThemeExporter' ) && function_exists( 'upfront_exporter_is_running' ) && upfront_exporter_is_running();
236 if ( ! $is_on_upfront_builder ) {
237 if ( is_customize_preview() || isset( $_REQUEST['fl_builder'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
238 if ( ! is_singular() ) {
239 return;
240 }
241 if ( ! $unsubscribe_shortcode ) {
242 return;
243 }
244 }
245 }
246
247 // Fix for YITH Frontend Manager for WooCommerce.
248 if ( class_exists( 'YITH_Frontend_Manager' ) ) {
249 $default_page_id = get_option( 'yith_wcfm_main_page_id', false );
250 if ( $default_page_id && is_page( $default_page_id ) ) {
251 return;
252 }
253 }
254
255 // Register popup requirements.
256 wp_register_script(
257 'hustle_front',
258 Opt_In::$plugin_url . 'assets/js/front.min.js',
259 array( 'jquery', 'underscore' ),
260 Opt_In::VERSION,
261 true
262 );
263
264 $modules = apply_filters( 'hustle_front_modules', $this->modules );
265 wp_localize_script( 'hustle_front', 'Modules', $modules );
266
267 // force set archive page slug.
268 global $wp;
269 $slug = is_home() && is_front_page() ? 'hustle-front-blog-page' : sanitize_title( $wp->request );
270
271 $conditional_tags = array(
272 'is_single' => is_single(),
273 'is_singular' => is_singular(),
274 'is_tag' => is_tag(),
275 'is_category' => is_category(),
276 'is_author' => is_author(),
277 'is_date' => is_date(),
278 'is_post_type_archive' => is_post_type_archive(),
279 'is_404' => is_404(),
280 'is_front_page' => is_front_page(),
281 'is_search' => is_search(),
282 );
283
284 if ( Opt_In_Utils::is_woocommerce_active() ) {
285 $conditional_tags['is_product_tag'] = is_product_tag();
286 $conditional_tags['is_product_category'] = is_product_category();
287 $conditional_tags['is_shop'] = is_shop();
288 $conditional_tags['is_woocommerce'] = is_woocommerce();
289 $conditional_tags['is_checkout'] = is_checkout();
290 $conditional_tags['is_cart'] = is_cart();
291 $conditional_tags['is_account_page'] = is_account_page();
292 $conditional_tags['order-received'] = is_wc_endpoint_url( 'order-received' );
293 }
294
295 $vars = apply_filters(
296 'hustle_front_vars',
297 array(
298 'conditional_tags' => $conditional_tags,
299 'is_admin' => is_admin(),
300 'real_page_id' => Opt_In_Utils::get_real_page_id(),
301 'thereferrer' => Opt_In_Utils::get_referrer(),
302 'actual_url' => Opt_In_Utils::get_current_actual_url(),
303 'full_actual_url' => Opt_In_Utils::get_current_actual_url( true ),
304 'native_share_enpoints' => Hustle_SShare_Model::get_sharing_endpoints( false ),
305 'ajaxurl' => admin_url( 'admin-ajax.php', is_ssl() ? 'https' : 'http' ),
306 'page_id' => get_queried_object_id(), // Used in many places to decide whether to show the module and cookies.
307 'page_slug' => $slug, // Used in many places to decide whether to show the module and cookies on archive pages.
308 'is_upfront' => class_exists( 'Upfront' ) && isset( $_GET['editmode'] ) && 'true' === $_GET['editmode'], // phpcs:ignore WordPress.Security.NonceVerification.Recommended
309 'script_delay' => apply_filters( 'hustle_lazy_load_script_delay', 3000 ), // to lazyload script for later on added elements.
310 )
311 );
312
313 $modules_deps = $this->modules_data_for_scripts;
314
315 // Datepicker. Add translated strings only if some module has a datepicker.
316 if ( ! empty( $modules_deps['datepicker'] ) ) {
317 $vars['days_and_months'] = array(
318 'days_full' => Hustle_Time_Helper::get_week_days(),
319 'days_short' => Hustle_Time_Helper::get_week_days( 'short' ),
320 'days_min' => Hustle_Time_Helper::get_week_days( 'min' ),
321 'months_full' => Hustle_Time_Helper::get_months(),
322 'months_short' => Hustle_Time_Helper::get_months( 'short' ),
323 );
324 wp_enqueue_script( 'jquery-ui-datepicker' );
325 }
326 wp_localize_script( 'hustle_front', 'incOpt', $vars );
327
328 do_action( 'hustle_register_scripts' );
329
330 // Queue adblocker if a module requires it.
331 if ( ! empty( $modules_deps['adblocker'] ) ) {
332 wp_enqueue_script(
333 'hustle_front_ads',
334 Opt_In::$plugin_url . 'assets/js/ad.min.js',
335 array(),
336 Opt_In::VERSION,
337 true
338 );
339 }
340
341 // Queue recaptchas if required. Only added if the keys are set.
342 if ( ! empty( $modules_deps['recaptcha'] ) ) {
343 $this->add_recaptcha_script( $modules_deps['recaptcha']['language'] );
344 }
345
346 // Queue Pinteres if required.
347 if ( ! empty( $modules_deps['pinterest'] ) ) {
348 wp_enqueue_script(
349 'hustle_sshare_pinterest',
350 '//assets.pinterest.com/js/pinit.js',
351 array(),
352 Opt_In::VERSION,
353 true
354 );
355 }
356
357 self::add_hui_scripts();
358 wp_enqueue_script( 'hustle_front' );
359
360 Opt_In_Utils::maybe_add_scripts_for_ie();
361 }
362
363 /**
364 * Add Hustle UI scripts.
365 * Used for displaying and previewing modules.
366 *
367 * @since 4.0
368 */
369 public static function add_hui_scripts() {
370 // Register Hustle UI functions.
371 wp_register_script(
372 'hui_scripts',
373 Opt_In::$plugin_url . 'assets/hustle-ui/js/hustle-ui.min.js',
374 array( 'jquery' ),
375 Opt_In::VERSION,
376 true
377 );
378
379 $settings = array(
380 'mobile_breakpoint' => Hustle_Settings_Admin::get_mobile_breakpoint(),
381 );
382 wp_localize_script( 'hui_scripts', 'hustleSettings', $settings );
383
384 wp_enqueue_script( 'hui_scripts' );
385 }
386
387 /**
388 * Enqueue the recaptcha script if recaptcha is globally configured.
389 *
390 * @since 4.0
391 * @since 4.0.3 param $recaptcha_versions and $is_preview added
392 *
393 * @param string $language reCAPTCHA language.
394 * @param bool $is_preview if it's preview.
395 * @param bool $is_return Is return.
396 */
397 public static function add_recaptcha_script( $language = '', $is_preview = false, $is_return = false ) {
398
399 $recaptcha_settings = Hustle_Settings_Admin::get_recaptcha_settings();
400
401 if ( empty( $language ) || 'automatic' === $language ) {
402 $language = ! empty( $recaptcha_settings['language'] ) && 'automatic' !== $recaptcha_settings['language']
403 ? $recaptcha_settings['language'] : get_locale();
404 }
405 $script_url = 'https://www.google.com/recaptcha/api.js?render=explicit&hl=' . $language;
406
407 if ( ! $is_return ) {
408 wp_enqueue_script( 'recaptcha', $script_url, array(), 1, true );
409
410 } elseif ( $is_preview ) {
411 return $script_url;
412 }
413 }
414
415 /**
416 * Preload fonts
417 *
418 * @return string
419 */
420 public function preload_custom_font() {
421 if ( ! count( $this->modules ) ) {
422 // There aren't any published modules. We don't need to load fonts.
423 return;
424 }
425 $font_name = Opt_In::$plugin_url . 'assets/hustle-ui/fonts/hustle-icons-font';
426 ?>
427 <link rel="preload" href="<?php echo esc_url( $font_name . '.woff2' ); ?>" as="font" type="font/woff2" crossorigin>
428 <?php
429 }
430
431 /**
432 * Registeres front styles and fonts
433 */
434 public function register_styles() {
435
436 // There aren't any published modules. We don't need styles.
437 if ( ! count( $this->modules ) ) {
438 return;
439 }
440
441 $is_on_upfront_builder = class_exists( 'UpfrontThemeExporter' ) && function_exists( 'upfront_exporter_is_running' ) && upfront_exporter_is_running();
442
443 if ( ! $is_on_upfront_builder ) {
444 if ( ! $this->has_modules() || isset( $_REQUEST['fl_builder'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
445 return;
446 }
447 }
448
449 $module_types_to_display = array_keys( $this->module_types_to_display );
450
451 self::print_front_styles( $module_types_to_display, $this->modules_data_for_scripts );
452 self::print_front_fonts( $this->modules_data_for_scripts['fonts'] );
453 }
454
455 /**
456 * Register and enqueue the required styles according to the given module's types.
457 * The accepted module's types are:
458 * popup, slidein, embedded, social_sharing, optin, informational, floating (ssharing), inline (ssharing).
459 *
460 * @since 4.0
461 * @since 4.0.1 enequeues only the given module's types.
462 * @since 4.2.0 $dependencies param added.
463 *
464 * @param array $module_types_to_display Array with the module's type to be displayed.
465 * @param array $dependencies Array with the module's style dependencies.
466 */
467 public static function print_front_styles( $module_types_to_display = array(), $dependencies = array() ) {
468
469 if ( ! empty( $dependencies['select2'] ) ) {
470 wp_register_style(
471 'select2',
472 Opt_In::$plugin_url . 'assets/css/select2.min.css',
473 array(),
474 '4.0.6'
475 );
476 wp_enqueue_style( 'select2' );
477 }
478
479 wp_register_style(
480 'hustle_icons',
481 Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-icons.min.css',
482 array(),
483 Opt_In::VERSION
484 );
485 wp_enqueue_style( 'hustle_icons' );
486
487 wp_register_style(
488 'hustle_global',
489 Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-global.min.css',
490 array(),
491 Opt_In::VERSION
492 );
493 wp_enqueue_style( 'hustle_global' );
494
495 // Informational mode.
496 if ( ! $module_types_to_display || in_array( Hustle_Module_Model::INFORMATIONAL_MODE, $module_types_to_display, true ) ) {
497
498 wp_register_style(
499 'hustle_info',
500 Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-info.min.css',
501 array(),
502 Opt_In::VERSION
503 );
504 wp_enqueue_style( 'hustle_info' );
505 }
506
507 // Optin mode.
508 if ( ! $module_types_to_display || in_array( Hustle_Module_Model::OPTIN_MODE, $module_types_to_display, true ) ) {
509
510 wp_register_style(
511 'hustle_optin',
512 Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-optin.min.css',
513 array(),
514 Opt_In::VERSION
515 );
516 wp_enqueue_style( 'hustle_optin' );
517 }
518
519 // Popup type.
520 if ( ! $module_types_to_display || in_array( Hustle_Module_Model::POPUP_MODULE, $module_types_to_display, true ) ) {
521
522 wp_register_style(
523 'hustle_popup',
524 Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-popup.min.css',
525 array(),
526 Opt_In::VERSION
527 );
528 wp_enqueue_style( 'hustle_popup' );
529 }
530
531 // Slidein type.
532 if ( ! $module_types_to_display || in_array( Hustle_Module_Model::SLIDEIN_MODULE, $module_types_to_display, true ) ) {
533
534 wp_register_style(
535 'hustle_slidein',
536 Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-slidein.min.css',
537 array(),
538 Opt_In::VERSION
539 );
540 wp_enqueue_style( 'hustle_slidein' );
541 }
542
543 $has_social_sharing_module = in_array( Hustle_Module_Model::SOCIAL_SHARING_MODULE, $module_types_to_display, true );
544 $has_embedded_module = in_array( Hustle_Module_Model::EMBEDDED_MODULE, $module_types_to_display, true );
545
546 // Social share and Embedded both need hustle-inline CSS.
547 if ( ! $module_types_to_display || $has_social_sharing_module || $has_embedded_module ) {
548 wp_register_style(
549 'hustle_inline',
550 Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-inline.min.css',
551 array(),
552 Opt_In::VERSION
553 );
554 }
555
556 if ( ! $module_types_to_display || $has_embedded_module ) {
557 wp_enqueue_style( 'hustle_inline' );
558 }
559
560 // SSharing type.
561 if ( ! $module_types_to_display || $has_social_sharing_module ) {
562
563 wp_register_style(
564 'hustle_social',
565 Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-social.min.css',
566 array(),
567 Opt_In::VERSION
568 );
569 wp_enqueue_style( 'hustle_social' );
570
571 // Inline display.
572 if ( ! $module_types_to_display || in_array( Hustle_SShare_Model::INLINE_MODULE, $module_types_to_display, true ) ) {
573
574 wp_enqueue_style( 'hustle_inline' );
575 }
576
577 // Floating display.
578 if ( ! $module_types_to_display || in_array( Hustle_SShare_Model::FLOAT_MODULE, $module_types_to_display, true ) ) {
579
580 wp_register_style(
581 'hustle_float',
582 Opt_In::$plugin_url . 'assets/hustle-ui/css/hustle-float.min.css',
583 array(),
584 Opt_In::VERSION
585 );
586 wp_enqueue_style( 'hustle_float' );
587 }
588 }
589 }
590
591 /**
592 * Enqueues the required Google fonts to be included in front.
593 *
594 * @since unknown
595 * @param array $fonts Fonts.
596 * @param bool $is_ajax Is ajax.
597 * @return void|string
598 */
599 public static function print_front_fonts( $fonts, $is_ajax = false ) {
600 if ( empty( $fonts ) ) {
601 return;
602 }
603
604 $families_args = array();
605 foreach ( $fonts as $font_family => $variations ) {
606 $families_args[] = $font_family . ':' . implode( ',', array_unique( $variations ) );
607 }
608
609 // The final URL must have a 'family' parameter with all font families and variations
610 // formatted like ?family=Tangerine:bold,bolditalic|Inconsolata:italic|Droid+Sans .
611 $google_font_url = add_query_arg(
612 array(
613 'family' => implode( '|', $families_args ),
614 'display' => 'swap',
615 ),
616 'https://fonts.bunny.net/css'
617 );
618
619 $id = 'hustle-fonts';
620 if ( ! $is_ajax ) {
621 wp_enqueue_style( $id, $google_font_url, array(), '1.0' );
622 } else {
623 // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
624 return '<link rel="stylesheet" id="' . $id . '" href="' . esc_url( $google_font_url ) . '" media="all">';
625 }
626 }
627
628 /**
629 * Enqueue modules to be displayed on Frontend.
630 */
631 public function create_modules() {
632
633 // Retrieve all active modules.
634 $modules = apply_filters( 'hustle_sort_modules', Hustle_Module_Collection::instance()->get_all( true ) );
635 $datepicker_found = false;
636 $recaptcha_found = false;
637 $select2_found = false;
638 $recaptcha_language = '';
639 $enqueue_adblock = false;
640 $pinterest_found = false;
641
642 /**
643 * Disables the load of Google fonts in frontend.
644 *
645 * @since unknown
646 *
647 * @param bool Whether Google fonts should be used.
648 */
649 $use_google_fonts = apply_filters( 'hustle_load_google_fonts', true );
650 $google_fonts = array();
651
652 foreach ( $modules as $module ) {
653
654 if ( ! $module instanceof Hustle_Model || ! $module->active ) {
655 continue;
656 }
657
658 $is_non_inline_module = ( Hustle_Module_Model::POPUP_MODULE === $module->module_type || Hustle_Module_Model::SLIDEIN_MODULE === $module->module_type );
659
660 $avoid_static_cache = Opt_In_Utils::is_static_cache_enabled();
661 // Check `is_condition_allow` first to set self::$use_count_cookie.
662 if ( ! $module->is_condition_allow() && ! $avoid_static_cache ) {
663 // If shortcode is enabled for inline modules, don't abort.
664 // Shortcodes shouldn't follow the visibility conditions.
665 if ( ! $is_non_inline_module ) {
666 $display_options = $module->get_display()->to_array();
667 if ( '1' !== $display_options['shortcode_enabled'] ) {
668 continue;
669 }
670 } else {
671 continue;
672 }
673 }
674
675 // Setting up stuff for all modules except social sharing.
676 if ( Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $module->module_type ) {
677 $settings = $module->get_settings();
678 // Check the schedule. Ssharing modules don't have schedules.
679 if ( ! $avoid_static_cache && ! $settings->is_currently_scheduled() ) {
680 continue;
681 }
682 $module->load();
683
684 // Skip if Google fonts were deativated via hook.
685 if ( $use_google_fonts ) {
686 $google_fonts = array_merge_recursive( $google_fonts, $module->get_google_fonts() );
687 }
688
689 if ( 'optin' === $module->module_mode ) {
690
691 if ( ! $datepicker_found || empty( $recaptcha_language ) ) {
692 $form_fields = $module->get_form_fields();
693
694 // Datepicker.
695 // Check if the module has a datepicker unless we already found one in other modules.
696 // We'll localize some variables if the modules have a datepicker.
697 if ( ! $datepicker_found ) {
698 $field_types = wp_list_pluck( $form_fields, 'type', true );
699 $datepicker_found = in_array( 'datepicker', $field_types, true );
700 }
701
702 // Recaptcha.
703 // Check if the module has a recaptcha to enqueue scripts unless we already found one.
704 // We'll queue the script afterwards.
705 if ( ! empty( $form_fields['recaptcha'] ) && empty( $recaptcha_language ) ) {
706
707 $recaptcha_found = true;
708
709 $recaptcha_field = $form_fields['recaptcha'];
710 // Get only first recaptcha language. Skip if not set or it's "automatic".
711 if ( ! empty( $recaptcha_field['recaptcha_language'] ) && 'automatic' !== $recaptcha_field['recaptcha_language'] ) {
712 $recaptcha_language = $recaptcha_field['recaptcha_language'];
713 }
714 }
715 }
716
717 // Select2.
718 // We're only using select2 for Mailchimp dropdown groups.
719 if ( ! $select2_found ) {
720 $mailchimp_settings = $module->get_provider_settings( 'mailchimp' );
721 if (
722 ! empty( $mailchimp_settings ) &&
723 ! is_null( $mailchimp_settings['group'] ) &&
724 '-1' !== $mailchimp_settings['group'] &&
725 'dropdown' === $mailchimp_settings['group_type']
726 ) {
727 $select2_found = true;
728 }
729 }
730 }
731
732 // For popups and slideins.
733 if ( $is_non_inline_module ) {
734 $this->non_inline_modules[ $module->module_id ] = $module;
735
736 if ( ! $enqueue_adblock ) {
737
738 $settings = $settings->to_array();
739
740 // If trigger is adblock.
741 if ( in_array( 'adblock', $settings['triggers']['trigger'], true ) ) {
742 $enqueue_adblock = true;
743 }
744 }
745 } elseif ( Hustle_Module_Model::EMBEDDED_MODULE === $module->module_type ) {
746 $this->inline_modules[ $module->module_id ] = $module;
747 }
748 } else { // Social sharing modules.
749 $ssharing_networks = $module->get_content()->get_social_icons();
750 if (
751 ! empty( $ssharing_networks )
752 && in_array( 'pinterest', array_keys( $ssharing_networks ), true )
753 && empty( $ssharing_networks['pinterest']['link'] )
754 ) {
755 $pinterest_found = true;
756 }
757 $this->inline_modules[ $module->module_id ] = $module;
758
759 $this->non_inline_modules[ $module->module_id ] = $module;
760 }
761
762 $this->log_module_type_to_load_styles( $module );
763
764 $this->modules[] = $module->get_module_data_to_display();
765
766 } // End looping through the modules.
767
768 // Set flag for scripts: datepicker field.
769 if ( $datepicker_found ) {
770 $this->modules_data_for_scripts['datepicker'] = true;
771 }
772
773 // Set flag for scripts: adblocker.
774 if ( $enqueue_adblock ) {
775 $this->modules_data_for_scripts['adblocker'] = true;
776 }
777
778 // Set flag for scripts: select2.
779 if ( $select2_found ) {
780 $this->modules_data_for_scripts['select2'] = true;
781 }
782
783 // Set flag for scripts: recaptcha field.
784 if ( $recaptcha_found ) {
785 $this->modules_data_for_scripts['recaptcha'] = array( 'language' => $recaptcha_language );
786 }
787
788 if ( $pinterest_found ) {
789 $this->modules_data_for_scripts['pinterest'] = true;
790 }
791
792 // Before removing it in future - check shortcode method - it's a flag there.
793 $this->modules_data_for_scripts['fonts'] = $google_fonts;
794 }
795
796 /**
797 * Store the modules' types to be displayed in order to enqueue
798 * their required styles.
799 * Called within self::create_modules() method.
800 *
801 * @since 4.0.1
802 *
803 * @param Hustle_Model $module Current module to check.
804 */
805 private function log_module_type_to_load_styles( Hustle_Model $module ) {
806
807 // Keep track of the of the modules types and modes to display
808 // in order to queue the required styles only.
809 $this->module_types_to_display[ $module->module_type ] = 1;
810
811 // Register the module mode for non SSharing modules.
812 if ( Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $module->module_type ) {
813 $this->module_types_to_display[ $module->module_mode ] = 1;
814
815 } else { // Register the module display type for SSharing modules.
816
817 // Floating display.
818 if (
819 $module->is_display_type_active( Hustle_SShare_Model::FLOAT_MOBILE ) ||
820 $module->is_display_type_active( Hustle_SShare_Model::FLOAT_DESKTOP )
821 ) {
822 $this->module_types_to_display[ Hustle_SShare_Model::FLOAT_MODULE ] = 1;
823 }
824
825 // Inline display.
826 if (
827 $module->is_display_type_active( Hustle_SShare_Model::INLINE_MODULE ) ||
828 $module->is_display_type_active( Hustle_SShare_Model::WIDGET_MODULE ) ||
829 $module->is_display_type_active( Hustle_SShare_Model::SHORTCODE_MODULE )
830 ) {
831 $this->module_types_to_display[ Hustle_SShare_Model::INLINE_MODULE ] = 1;
832 }
833 }
834 }
835
836 /**
837 * Check if current page has renderable opt-ins.
838 **/
839 public function has_modules() {
840 $has_modules = ! empty( $this->non_inline_modules ) || ! empty( $this->inline_modules );
841 return apply_filters( 'hustle_front_handler', $has_modules );
842 }
843
844 /**
845 * By-pass NextGEN Gallery resource manager
846 *
847 * @return false
848 */
849 public function nextgen_compat() {
850 return false;
851 }
852
853 /**
854 * Render non inline modules
855 */
856 public function render_non_inline_modules() {
857 $html = '';
858
859 foreach ( $this->non_inline_modules as $module ) {
860
861 if ( Hustle_Module_Model::SOCIAL_SHARING_MODULE !== $module->module_type ) {
862 $html .= $module->display();
863 } elseif ( $module->is_display_type_active( Hustle_SShare_Model::FLOAT_DESKTOP ) || $module->is_display_type_active( Hustle_SShare_Model::FLOAT_MOBILE ) ) {
864 $html .= $module->display( Hustle_SShare_Model::FLOAT_MODULE );
865 }
866 }
867
868 add_action(
869 'wp_footer',
870 function() use ( $html ) {
871 echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
872 }
873 );
874 }
875
876 /**
877 * Handles the data for the unsubscribe shortcode
878 *
879 * @since 3.0.5
880 * @param array $atts The values passed through the shortcode attributes.
881 * @return string The content to be rendered within the shortcode.
882 */
883 public function unsubscribe_shortcode( $atts ) {
884 $messages = Hustle_Settings_Admin::get_unsubscribe_messages();
885 $email = filter_input( INPUT_GET, 'email', FILTER_VALIDATE_EMAIL );
886 $nonce = filter_input( INPUT_GET, 'token', FILTER_SANITIZE_SPECIAL_CHARS );
887 if ( $nonce && $email ) {
888 $error_message = $messages['invalid_data'];
889
890 $entry = new Hustle_Entry_Model();
891 $unsubscribed = $entry->unsubscribe_email( $email, $nonce );
892 if ( $unsubscribed ) {
893 return $messages['successful_unsubscription'];
894 } else {
895 return $error_message;
896 }
897 }
898 // Show all modules' lists by default.
899 $attributes = shortcode_atts(
900 array(
901 'id' => '-1',
902 'skip_confirmation' => false,
903 ),
904 $atts
905 );
906 $params = array(
907 'ajax_step' => false,
908 'shortcode_attr_id' => $attributes['id'],
909 'skip_confirmation' => $attributes['skip_confirmation'],
910 'messages' => $messages,
911 );
912
913 $renderer = new Hustle_Layout_Helper();
914 $html = $renderer->render( 'general/unsubscribe-form', $params, true );
915 $html = apply_filters( 'hustle_render_unsubscribe_form_html', $html, $params );
916 return $html;
917 }
918
919 /**
920 * Get unsubscribe form.
921 */
922 public function get_unsubscribe_form() {
923 Opt_In_Utils::validate_ajax_call( 'hustle_gutenberg_get_unsubscribe_form' );
924
925 $atts = array();
926 $ids = filter_input( INPUT_GET, 'module_ids', FILTER_SANITIZE_SPECIAL_CHARS );
927 $skip = filter_input( INPUT_GET, 'skip_confirmation', FILTER_VALIDATE_BOOLEAN );
928
929 if ( $ids ) {
930 $atts['id'] = $ids;
931 }
932
933 if ( $skip ) {
934 $atts['skip_confirmation'] = true;
935 }
936
937 $html = $this->unsubscribe_shortcode( $atts );
938
939 wp_send_json_success( $html );
940 }
941
942 /**
943 * Render the modules' wrapper to render the actual module using their shortcodes.
944 *
945 * @since the beginning of time.
946 *
947 * @param array $atts Attrs.
948 * @param string $content Content.
949 * @return string
950 */
951 public function shortcode( $atts, $content ) {
952 $atts = shortcode_atts(
953 array(
954 'id' => '',
955 'type' => 'embedded',
956 'css_class' => '',
957 ),
958 $atts,
959 self::SHORTCODE
960 );
961
962 if ( empty( $atts['id'] ) ) {
963 return '';
964 }
965
966 if ( ! $this->modules_data_for_scripts ) {
967 // This case for AJAX.
968 $this->create_modules();
969 }
970
971 $type = $atts['type'];
972
973 // If shortcode type is not embed or sshare.
974 if ( 'embedded' !== $type && 'social_sharing' !== $type ) {
975 // Do not enforce embedded/social_sharing type.
976 $enforce_type = false;
977 } else {
978 // Enforce embedded/social_sharing type.
979 $enforce_type = true;
980 }
981
982 $module_id = Hustle_Model::get_module_id_by_shortcode_id( $atts['id'] );
983
984 $custom_classes = esc_attr( $atts['css_class'] );
985
986 if ( isset( $this->inline_modules[ $module_id ] ) ) {
987 $module = $this->inline_modules[ $module_id ];
988
989 if ( ! $module->is_display_type_active( Hustle_Module_Model::SHORTCODE_MODULE ) ) {
990 return '';
991 }
992
993 // Display the module.
994 return $module->display( Hustle_Module_Model::SHORTCODE_MODULE, $custom_classes );
995 }
996
997 if ( isset( $this->non_inline_modules[ $module_id ] ) && ! empty( $content ) ) {
998 $module = $this->non_inline_modules[ $module_id ];
999
1000 // If shortcode click trigger is disabled, print nothing.
1001 $settings = $module->get_settings()->to_array();
1002 if ( ! isset( $settings['triggers']['enable_on_click_shortcode'] ) || '0' === $settings['triggers']['enable_on_click_shortcode'] ) {
1003 return '';
1004 }
1005
1006 return sprintf(
1007 '<a href="javascript:void(0)" class="%s hustle_module_%s %s" data-id="%s" data-type="%s">%s</a>',
1008 'hustle_module_shortcode_trigger',
1009 esc_attr( $module->id ),
1010 esc_attr( $custom_classes ),
1011 esc_attr( $module->id ),
1012 esc_attr( $type ),
1013 wp_kses_post( $content )
1014 );
1015 }
1016
1017 return '';
1018 }
1019
1020 /**
1021 * Display inline modules.
1022 * Embedded and Social Sharing modules only.
1023 *
1024 * @since the beginning of time.
1025 *
1026 * @param string $content Content.
1027 * @return string
1028 */
1029 public function show_after_page_post_content( $content ) {
1030
1031 // Return the content immediately if there are no modules or the page doesn't have a content to embed into.
1032 if ( ! count( $this->inline_modules ) || isset( $_REQUEST['fl_builder'] ) || is_home() || is_archive() ) {// phpcs:ignore WordPress.Security.NonceVerification.Recommended
1033 return $content;
1034 }
1035
1036 $in_the_loop = in_the_loop();
1037
1038 /**
1039 * Filters whether to skip inline modules because we're not in da loop.
1040 * This can also be used to prevent loading the inline modules conditionally.
1041 *
1042 * @param bool $in_the_loop Returned value from WordPress' in_the_loop() function.
1043 * @since 4.2.0
1044 */
1045 $is_in_da_loop = apply_filters( 'hustle_inline_modules_render_in_the_loop', $in_the_loop );
1046
1047 // We only render the inline modules in the first call of 'the_content' filter.
1048 // Prevent the modules from being printed when they shouldn't and
1049 // leaving the page's main content without them.
1050 if ( ! $is_in_da_loop ) {
1051 return $content;
1052 }
1053
1054 $modules = apply_filters( 'hustle_inline_modules_to_display', $this->inline_modules );
1055
1056 foreach ( $modules as $module ) {
1057
1058 // Skip if "inline" display is disabled.
1059 if ( ! $module->is_display_type_active( Hustle_Module_Model::INLINE_MODULE ) ) {
1060 continue;
1061 }
1062
1063 $custom_classes = apply_filters( 'hustle_inline_module_custom_classes', '', $module );
1064 $module_markup = $module->display( Hustle_Module_Model::INLINE_MODULE, $custom_classes );
1065
1066 $display = $module->get_display()->to_array();
1067 $display_position = $display['inline_position'];
1068
1069 if ( 'both' === $display_position ) {
1070 $content = $module_markup . $content . $module_markup;
1071
1072 } elseif ( 'above' === $display_position ) {
1073 $content = $module_markup . $content;
1074
1075 } else { // Below.
1076 $content .= $module_markup;
1077
1078 }
1079 }
1080
1081 $is_render_inline_once = true;
1082 /**
1083 * Filters whether to render the inline modules once.
1084 * By default, we only render the inline modules in the first call of 'the_content' filter.
1085 * Allow rendering them in following calls of the filter if needed.
1086 *
1087 * @param bool $is_render_inline_once Whether to render the inline modules in several calls.
1088 * @since 4.2.0
1089 */
1090 $is_render_inline_once = apply_filters( 'hustle_inline_modules_render_once', $is_render_inline_once );
1091
1092 if ( $is_render_inline_once ) {
1093 remove_filter( 'the_content', array( $this, 'show_after_page_post_content' ), self::$the_content_filter_priority );
1094 }
1095
1096 return $content;
1097 }
1098
1099 /**
1100 * If new post content includes unsubscribe shortcode - safe the post URL.
1101 *
1102 * @param int $post_ID Post ID.
1103 * @param WP_Post $post_after Post object following the update.
1104 * @param WP_Post $post_before Post object before the update.
1105 * @return null|void
1106 */
1107 public static function maybe_unsubscribe_page( $post_ID, $post_after, $post_before ) {
1108 if ( ! strpos( $post_after->post_content, 'wd_hustle_unsubscribe' ) ) {
1109 return;
1110 }
1111
1112 $post_url = get_permalink( $post_after );
1113 if ( $post_url ) {
1114 update_option( 'hustle_unsubscribe_page', $post_url );
1115 }
1116 }
1117 }
1118