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