PluginProbe ʕ •ᴥ•ʔ
Hustle – Email Marketing, Lead Generation, Optins, Popups / 7.4.6
Hustle – Email Marketing, Lead Generation, Optins, Popups v7.4.6
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 / hustle-module-model.php
wordpress-popup / inc Last commit date
display-conditions 4 years ago front 4 years ago helpers 4 years ago metas 4 years ago palettes 4 years ago provider 4 years ago providers 4 years ago templates 4 years ago update 5 years ago class-hustle-admin-page-abstract.php 4 years ago class-hustle-condition-factory.php 6 years ago class-hustle-dashboard-admin.php 4 years ago class-hustle-data.php 4 years ago class-hustle-db.php 5 years ago class-hustle-installer.php 5 years ago class-hustle-meta.php 5 years ago class-hustle-module-admin.php 5 years ago class-hustle-module-collection.php 5 years ago class-hustle-module-decorator.php 5 years ago class-hustle-module-page-abstract.php 4 years ago class-hustle-notifications.php 5 years ago class-hustle-settings-admin.php 5 years ago class-hustle-tutorials-page.php 4 years ago class-hustle-upsell-page.php 5 years ago class-hustle-wp-dashboard-page.php 4 years ago hustle-collection.php 6 years ago hustle-deletion.php 5 years ago hustle-embedded-admin.php 6 years ago hustle-entries-admin.php 5 years ago hustle-entry-model.php 5 years ago hustle-general-data-protection.php 6 years ago hustle-init.php 4 years ago hustle-mail.php 5 years ago hustle-migration.php 5 years ago hustle-model.php 4 years ago hustle-module-model.php 4 years ago hustle-module-widget-legacy.php 5 years ago hustle-module-widget.php 5 years ago hustle-modules-common-admin-ajax.php 5 years ago hustle-popup-admin.php 6 years ago hustle-providers-admin.php 5 years ago hustle-providers.php 6 years ago hustle-settings-admin-ajax.php 4 years ago hustle-settings-page.php 5 years ago hustle-slidein-admin.php 6 years ago hustle-sshare-admin.php 5 years ago hustle-sshare-model.php 4 years ago hustle-tracking-model.php 5 years ago opt-in-geo.php 5 years ago opt-in-utils.php 4 years ago opt-in-wpmudev-api.php 4 years ago
hustle-module-model.php
879 lines
1 <?php
2
3 /**
4 * Class Hustle_Module_Model
5 *
6 * @property Hustle_Module_Decorator $decorated
7 */
8
9 class Hustle_Module_Model extends Hustle_Model {
10
11 public static $use_count_cookie;
12
13 public static function instance() {
14 _deprecated_function( __METHOD__, '4.3.0', 'new Hustle_Module_Model' );
15 return new self();
16 }
17
18 /**
19 * Get the sub-types for embedded modules.
20 *
21 * @since the beggining of time
22 * @since 4.0 "after_content" changed to "inline"
23 *
24 * @return array
25 */
26 public static function get_embedded_types( $with_titles = false ) {
27 if ( ! $with_titles ) {
28 return array( 'inline', 'widget', 'shortcode' );
29 } else {
30 return array(
31 'inline' => __( 'Inline', 'hustle' ),
32 'widget' => __( 'Widget', 'hustle' ),
33 'shortcode' => __( 'Shortcode', 'hustle' ),
34 );
35 }
36 }
37
38 /**
39 * Get the sub-types for this module.
40 *
41 * @since 4.0
42 *
43 * @return array
44 */
45 public function get_sub_types( $with_titles = false ) {
46 if ( self::EMBEDDED_MODULE === $this->module_type ) {
47 return self::get_embedded_types( $with_titles );
48 }
49
50 return array();
51 }
52
53 /**
54 * Gets the instance of the decorator class for this module type.
55 *
56 * @since 4.3.0
57 *
58 * @return Hustle_Decorator_Non_Sshare
59 */
60 public function get_decorator_instance() {
61 return new Hustle_Decorator_Non_Sshare( $this );
62 }
63
64 /**
65 * Content Model based upon module type.
66 *
67 * @return Class
68 */
69 public function get_content() {
70 $data = $this->get_settings_meta( self::KEY_CONTENT );
71
72 if ( ! Opt_In_Utils::_is_free() ) {
73 if ( ! empty( $data['background_image'] ) ) {
74 $data['background_image'] = self::replace_free_to_pro_folder( $data['background_image'] );
75 }
76 if ( ! empty( $data['feature_image'] ) ) {
77 $data['feature_image'] = self::replace_free_to_pro_folder( $data['feature_image'] );
78 }
79 }
80
81 return new Hustle_Meta_Base_Content( $data, $this );
82 }
83
84 /**
85 * It applies for users which was upgraded from free to pro version
86 *
87 * @param string $subject String which includes an old path.
88 * @return string
89 */
90 private static function replace_free_to_pro_folder( $subject ) {
91 $free_plugin_name = 'wordpress-popup/';
92 $pro_plugin_name = 'hustle/';
93 return str_replace( '/plugins/' . $free_plugin_name, '/plugins/' . $pro_plugin_name, $subject );
94 }
95
96 /**
97 * Get the content of the data stored under 'emails' meta.
98 *
99 * @since 4.0
100 *
101 * @return Hustle_Popup_Emails
102 */
103 public function get_emails() {
104 $data = $this->get_settings_meta( self::KEY_EMAILS );
105
106 return new Hustle_Meta_Base_Emails( $data, $this );
107 }
108
109 /**
110 * Get the module's settings for the given provider.
111 *
112 * @since 4.0
113 *
114 * @param string $slug
115 * @param bool $get_cached
116 * @return array
117 */
118 public function get_provider_settings( $slug, $get_cached = true ) {
119 return $this->get_settings_meta( $slug . self::KEY_PROVIDER, array(), $get_cached );
120 }
121
122 /**
123 * Save the module's settings for the given provider.
124 *
125 * @since 4.0
126 *
127 * @param string $slug
128 * @param array $data
129 * @return array
130 */
131 public function set_provider_settings( $slug, $data ) {
132 return $this->update_meta( $slug . self::KEY_PROVIDER, $data );
133 }
134
135 /**
136 * Get the all-integrations module's settings.
137 * This is not each provider's settings. Instead, these are per module settings
138 * that are applied to all the active providers of this module.
139 *
140 * @since 4.0
141 *
142 * @return array
143 */
144 public function get_integrations_settings() {
145 $stored = $this->get_settings_meta( self::KEY_INTEGRATIONS_SETTINGS );
146 return new Hustle_Meta_Base_Integrations( $stored, $this );
147 }
148
149 public function get_design() {
150 $stored = $this->get_settings_meta( self::KEY_DESIGN );
151 return new Hustle_Meta_Base_Design( $stored, $this );
152 }
153
154 /**
155 * Get the stored settings for the "Display" tab.
156 * Used for Embedded.
157 *
158 * @since 4.0
159 *
160 * @return Hustle_Embedded_Display
161 */
162 public function get_display() {
163 return new Hustle_Meta_Base_Display( $this->get_settings_meta( self::KEY_DISPLAY_OPTIONS ), $this );
164 }
165
166 /**
167 * Used when populating data with "get".
168 */
169 public function get_settings() {
170 $saved = $this->get_settings_meta( self::KEY_SETTINGS );
171
172 // We made this an array in 4.1.0. Some sites didn't run the migration for some reason.
173 // This prevents the old string from triggering php errors and warnings.
174 // It'd be even better to find out why that migration didn't run in some sites.
175 if ( ! empty( $saved['triggers']['trigger'] ) && ! is_array( $saved['triggers']['trigger'] ) ) {
176 $saved['triggers']['trigger'] = array( $saved['triggers']['trigger'] );
177 }
178
179 if ( self::POPUP_MODULE === $this->module_type ) {
180 return new Hustle_Popup_Settings( $saved, $this );
181
182 } elseif ( self::EMBEDDED_MODULE === $this->module_type ) {
183 return new Hustle_Meta_Base_Settings( $saved, $this );
184
185 } elseif ( self::SLIDEIN_MODULE === $this->module_type ) {
186 return new Hustle_Slidein_Settings( $saved, $this );
187 }
188
189 return false;
190 }
191
192 /**
193 * Get the stored schedule flags
194 *
195 * @since 4.2.0
196 * @return array
197 */
198 public function get_schedule_flags() {
199 $default = array(
200 'is_currently_scheduled' => '1',
201 'check_schedule_at' => 1,
202 );
203
204 return $this->get_settings_meta( 'schedule_flags', $default );
205 }
206
207 /**
208 * Set the schedule flags.
209 *
210 * @since 4.2.0
211 * @param array $flags
212 * @return void
213 */
214 public function set_schedule_flags( $flags ) {
215 $this->update_meta( 'schedule_flags', $flags );
216 }
217
218 /**
219 * Get the module's data. Used to display it.
220 *
221 * @since 3.0.7
222 *
223 * @return array
224 */
225 public function get_module_data_to_display() {
226 $settings = array( 'settings' => $this->get_settings()->to_array() );
227 $data = array_merge( $settings, $this->get_data() );
228
229 if ( self::$use_count_cookie ) {
230 self::$use_count_cookie = null;
231 $data['useCountCookie'] = true;
232 }
233
234 return $data;
235 }
236
237 /**
238 * Get the form fields of this module, if any.
239 *
240 * @since 4.0
241 *
242 * @return null|array
243 */
244 public function get_form_fields() {
245
246 if ( 'social_sharing' === $this->module_type || 'informational' === $this->module_mode ) {
247 return null;
248 }
249
250 $emails_data = empty( $this->emails ) ? $this->get_emails()->to_array() : (array) $this->emails;
251 /**
252 * Edit module fields
253 *
254 * @since 4.1.1
255 * @param string $form_elements Current module fields.
256 */
257 $form_fields = apply_filters( 'hustle_form_elements', $emails_data['form_elements'] );
258
259 return $form_fields;
260
261 }
262
263 /**
264 * Create a new module of the provided mode and type.
265 *
266 * @since 4.0
267 *
268 * @param array $data Must contain the Module's 'mode', 'name' and 'type.
269 * @return int|false Module ID if successfully saved. False otherwise.
270 */
271 public function create_new( $data ) {
272 $module_populated = $this->populate_module_from_data( $data );
273 if ( ! $module_populated ) {
274 return false;
275 }
276
277 // Save to modules table.
278 $this->save();
279
280 // Save the new module's meta.
281 $this->store_new_module_meta( $data );
282
283 $this->activate_providers( $data );
284
285 return $this->id;
286 }
287
288 /**
289 * Populates the current model with the given data.
290 *
291 * @since 4.3.4
292 *
293 * @param array $data Data to populate the module with.
294 * @return bool
295 */
296 private function populate_module_from_data( $data ) {
297 // Verify it's a valid module type.
298 if ( ! in_array( $data['module_type'], array( self::POPUP_MODULE, self::SLIDEIN_MODULE, self::EMBEDDED_MODULE ), true ) ) {
299 return false;
300 }
301
302 // Abort if the mode isn't set.
303 if ( ! in_array( $data['module_mode'], array( 'optin', 'informational' ), true ) ) {
304 return false;
305 }
306
307 $this->module_name = sanitize_text_field( $data['module_name'] );
308 $this->module_type = $data['module_type'];
309 $this->active = 0;
310 $this->module_mode = $data['module_mode'];
311
312 return true;
313 }
314
315 /**
316 * Store the metas in the db when creating a new module.
317 *
318 * @since 4.0.0
319 *
320 * @param array $data Module's data to store.
321 */
322 private function store_new_module_meta( $data ) {
323 $def_content = apply_filters( 'hustle_module_get_' . self::KEY_CONTENT . '_defaults', $this->get_content()->to_array(), $this, $data );
324 $content = empty( $data['content'] ) ? $def_content : array_merge( $def_content, $data['content'] );
325
326 $def_emails = apply_filters( 'hustle_module_get_' . self::KEY_EMAILS . '_defaults', $this->get_emails()->to_array(), $this, $data );
327 $emails = empty( $data['emails'] ) ? $def_emails : array_merge( $def_emails, $data['emails'] );
328
329 $def_design = apply_filters( 'hustle_module_get_' . self::KEY_DESIGN . '_defaults', $this->get_design()->to_array(), $this, $data );
330 $design = empty( $data['design'] ) ? $def_design : array_merge( $def_design, $data['design'] );
331
332 $def_integrations_settings = apply_filters( 'hustle_module_get_' . self::KEY_INTEGRATIONS_SETTINGS . '_defaults', $this->get_integrations_settings()->to_array(), $this, $data );
333 $integrations_settings = empty( $data['integrations_settings'] ) ? $def_integrations_settings : array_merge( $def_integrations_settings, $data['integrations_settings'] );
334
335 $def_settings = apply_filters( 'hustle_module_get_' . self::KEY_SETTINGS . '_defaults', $this->get_settings()->to_array(), $this, $data );
336 $settings = empty( $data['settings'] ) ? $def_settings : array_merge( $def_settings, $data['settings'] );
337
338 $def_visibility = apply_filters( 'hustle_module_get_' . self::KEY_VISIBILITY . '_defaults', $this->get_visibility()->to_array(), $this, $data );
339 $visibility = empty( $data['visibility'] ) ? $def_visibility : array_merge( $def_visibility, $data['visibility'] );
340
341 $this->update_meta( self::KEY_CONTENT, $content );
342 $this->update_meta( self::KEY_EMAILS, $emails );
343 $this->update_meta( self::KEY_INTEGRATIONS_SETTINGS, $integrations_settings );
344 $this->update_meta( self::KEY_DESIGN, $design );
345 $this->update_meta( self::KEY_SETTINGS, $settings );
346 $this->update_meta( self::KEY_VISIBILITY, $visibility );
347
348 // Embedded only. Display options.
349 if ( self::EMBEDDED_MODULE === $this->module_type ) {
350 $def_display = apply_filters( 'hustle_module_get_' . self::KEY_DISPLAY_OPTIONS . '_defaults', $this->get_display()->to_array(), $this, $data );
351 $display = empty( $data['display'] ) ? $def_display : array_merge( $def_display, $data['display'] );
352
353 $this->update_meta( self::KEY_DISPLAY_OPTIONS, $display );
354 }
355 }
356
357 /**
358 * Populates an instance of a module for a template preview.
359 * This is only used for displaying a preview. We're using a saved
360 * module nor creating a new one, thus we don't have an ID.
361 *
362 * @since 4.3.4
363 *
364 * @param array $data Module data to be populated.
365 */
366 public function populate_module_for_template( $data ) {
367 $this->module_id = 0;
368
369 $this->populate_module_from_data( $data );
370 }
371
372 /**
373 * Creates and store the nonce used to validate email unsubscriptions.
374 *
375 * @since 3.0.5
376 * @param string $email Email to be unsubscribed.
377 * @param array $lists_id IDs of the modules to which it will be unsubscribed.
378 * @return boolean
379 */
380 public function create_unsubscribe_nonce( $email, array $lists_id ) {
381 // Since we're supporting php 5.2, random_bytes or other strong rng are not available. So using this instead.
382 $nonce = hash_hmac( 'md5', $email, wp_rand() . time() );
383
384 $data = get_option( self::KEY_UNSUBSCRIBE_NONCES, array() );
385
386 // If the email already created a nonce and didn't use it, replace its data.
387 $data[ $email ] = array(
388 'nonce' => $nonce,
389 'lists_id' => $lists_id,
390 'date_created' => time(),
391 );
392
393 $updated = update_option( self::KEY_UNSUBSCRIBE_NONCES, $data );
394 if ( $updated ) {
395 return $nonce;
396 } else {
397 return false;
398 }
399 }
400
401 /**
402 * Does the actual email unsubscription.
403 *
404 * @since 3.0.5
405 * @param string $email Email to be unsubscribed.
406 * @param string $nonce Nonce associated with the email for the unsubscription.
407 * @return boolean
408 */
409 public function unsubscribe_email( $email, $nonce ) {
410 $data = get_option( self::KEY_UNSUBSCRIBE_NONCES, false );
411 if ( ! $data ) {
412 return false;
413 }
414 if ( ! isset( $data[ $email ] ) || ! isset( $data[ $email ]['nonce'] ) || ! isset( $data[ $email ]['lists_id'] ) ) {
415 return false;
416 }
417 $email_data = $data[ $email ];
418 if ( ! hash_equals( (string) $email_data['nonce'], $nonce ) ) {
419 return false;
420 }
421 // Nonce expired. Remove it. Currently giving 1 day of life span.
422 if ( ( time() - (int) $email_data['date_created'] ) > DAY_IN_SECONDS ) {
423 unset( $data[ $email ] );
424 update_option( self::KEY_UNSUBSCRIBE_NONCES, $data );
425 return false;
426 }
427
428 // Proceed to unsubscribe
429 foreach ( $email_data['lists_id'] as $id ) {
430 $unsubscribed = $this->remove_local_subscription_by_email_and_module_id( $email, $id );
431 }
432
433 // The email was unsubscribed and the nonce was used. Remove it from the saved list.
434 unset( $data[ $email ] );
435 update_option( self::KEY_UNSUBSCRIBE_NONCES, $data );
436
437 return true;
438
439 }
440
441 /**
442 * Updates the metas specific for Non Social Sharing modules.
443 *
444 * @since 4.3.0
445 * @param array $data Data to save.
446 * @return void
447 */
448 protected function update_module_metas( $data ) {
449 // Meta used in all module types.
450 if ( isset( $data['content'] ) ) {
451 $this->update_meta( self::KEY_CONTENT, $data['content'] );
452 }
453 // Meta used in all module types.
454 if ( isset( $data['visibility'] ) ) {
455 $this->update_meta( self::KEY_VISIBILITY, $data['visibility'] );
456 }
457
458 // Design tab.
459 if ( isset( $data['design'] ) ) {
460 $saved_design = $this->get_design()->to_array();
461 $new_design = array_merge( $saved_design, $data['design'] );
462
463 $this->update_meta( self::KEY_DESIGN, $new_design );
464 }
465
466 // Emails tab.
467 if ( isset( $data['emails'] ) ) {
468 $emails = $data['emails'];
469 if ( isset( $emails['form_elements'] ) ) {
470 $emails['form_elements'] = $this->sanitize_form_elements( $emails['form_elements'] );
471 }
472 $this->update_meta( self::KEY_EMAILS, $emails );
473 }
474
475 // Settings tab.
476 if ( isset( $data['settings'] ) ) {
477 // Clear flags to skip cached schedule values.
478 $this->set_schedule_flags( array() );
479 $this->update_meta( self::KEY_SETTINGS, $data['settings'] );
480 }
481
482 // Integrations tab.
483 if ( isset( $data['integrations_settings'] ) ) {
484 $this->update_meta( self::KEY_INTEGRATIONS_SETTINGS, $data['integrations_settings'] );
485 }
486
487 // Embedded only meta.
488 if ( self::EMBEDDED_MODULE === $this->module_type && isset( $data['display'] ) ) {
489 $this->update_meta( self::KEY_DISPLAY_OPTIONS, $data['display'] );
490 }
491
492 // Activate integrations if provided.
493 if ( isset( $data['integrations'] ) ) {
494 $this->activate_providers( $data );
495 }
496
497 $this->maybe_update_custom_fields();
498 }
499
500 /**
501 * Sanitize/Replace the module's data.
502 *
503 * @param array $data Data to sanitize.
504 * @return array Sanitized data.
505 */
506 public function sanitize_module( $data ) {
507 $design_obj = $this->get_design();
508 $default_options = $design_obj->get_defaults();
509 $saved_options = $design_obj->to_array();
510 $new_options = ! empty( $data['design'] ) ? $data['design'] : array();
511 $typography_options = $design_obj->get_typography_defaults( 'desktop' );
512
513 // Check is `Border, Spacing and Shadow` enabled for desktop.
514 $spacing_on = $this->get_newest_value( 'customize_border_shadow_spacing', $new_options, $saved_options );
515 // Check is `Typography` enabled for desktop.
516 $typography_on = $this->get_newest_value( 'customize_typography', $new_options, $saved_options );
517
518 foreach ( $new_options as $option_name => $value ) {
519 if ( $spacing_on ) {
520 $data = $this->replace_empty_spacing_numbers( $data, $option_name, $value, $default_options );
521 }
522 if ( $typography_on ) {
523 $data = $this->replace_empty_typography_numbers( $data, $option_name, $value, $default_options, $typography_options );
524 }
525 }
526
527 if ( ! empty( $data['module']['module_name'] ) ) {
528 $data['module']['module_name'] = sanitize_text_field( $data['module']['module_name'] );
529 }
530
531 return $data;
532 }
533
534 /**
535 * Validates the module's data.
536 *
537 * @since 4.0.3
538 *
539 * @param array $data Data to validate.
540 * @return array
541 */
542 public function validate_module( $data ) {
543 $errors = array();
544
545 // Name validation.
546 if ( empty( $data['module']['module_name'] ) ) {
547 $errors['error']['name_error'] = __( 'This field is required', 'hustle' );
548
549 return $errors;
550 }
551
552 return true;
553 }
554
555 /**
556 * Get newest value.
557 *
558 * @param string $option_name Option name.
559 * @param array $new_options New options.
560 * @param array $saved_options Old options.
561 * @return bool
562 */
563 private function get_newest_value( $option_name, $new_options, $saved_options ) {
564 if ( isset( $new_options[ $option_name ] ) ) {
565 $value = $new_options[ $option_name ];
566 } elseif ( isset( $saved_options[ $option_name ] ) ) {
567 $value = $saved_options[ $option_name ];
568 } else {
569 $value = '';
570 }
571
572 return $value;
573 }
574
575 /**
576 * Check if it's spacing option and value isn't set
577 *
578 * @param string $key Option name.
579 * @param string $value Option value.
580 * @return bool
581 */
582 private function is_empty_spacing_number( $key, $value ) {
583 $needles = array( '_margin_', '_padding_', '_shadow_', '_radius_', '_border_' );
584 return $this->similar_in_array( $key, $needles ) && '' === $value;
585 }
586
587 /**
588 * Check if it's typography option and value isn't set
589 *
590 * @param string $key Option name.
591 * @param string $value Option value.
592 * @param array $typography_options Typography options.
593 * @return bool
594 */
595 private function is_empty_typography_number( $key, $value, $typography_options ) {
596 return '' === $value && isset( $typography_options[ $key ] )
597 && ( is_float( $typography_options[ $key ] ) || is_int( $typography_options[ $key ] ) );
598 }
599
600 /**
601 * If it's an empty number option from Destop section - replace it to relevant default value.
602 *
603 * @param string $data Data for sanitize.
604 * @param string $option_name Option name.
605 * @param string $option_value Option value.
606 * @param array $default_options Default options.
607 * @return string
608 */
609 private function replace_empty_spacing_numbers( $data, $option_name, $option_value, $default_options ) {
610 if ( $this->is_empty_spacing_number( $option_name, $option_value ) && '_mobile' !== substr( $option_name, -7 ) ) {
611 $data['design'][ $option_name ] = isset( $default_options[ $option_name ] ) ? $default_options[ $option_name ] : 0;
612 }
613
614 return $data;
615 }
616
617 /**
618 * If it's an empty number option from Typography section - replace it to relevant default value.
619 *
620 * @param string $data Data for sanitize.
621 * @param string $option_name Option name.
622 * @param string $option_value Option value.
623 * @param array $default_options Default options.
624 * @param array $typography_options Typography options.
625 * @return string
626 */
627 private function replace_empty_typography_numbers( $data, $option_name, $option_value, $default_options, $typography_options ) {
628 if ( $this->is_empty_typography_number( $option_name, $option_value, $typography_options ) && '_mobile' !== substr( $option_name, -7 ) ) {
629 $data['design'][ $option_name ] = isset( $default_options[ $option_name ] ) ? $default_options[ $option_name ] : 0;
630 }
631
632 return $data;
633 }
634
635 /**
636 * Checks if a value contains a part of any array item
637 *
638 * @param array $haystack The value.
639 * @param string $needles The array of pices.
640 * @return boolean
641 */
642 private function similar_in_array( $haystack, $needles ) {
643 foreach ( $needles as $needle ) {
644 if ( false !== strpos( $haystack, $needle ) ) {
645 return true;
646 }
647 }
648
649 return false;
650 }
651
652 public function get_renderer() {
653 return new Hustle_Module_Renderer();
654 }
655
656 /**
657 * Sanitize the form fields name replacing spaces by underscores.
658 * This way the data is handled properly along hustle.
659 *
660 * @since 4.0
661 * @param string $name
662 * @return string
663 */
664 private function sanitize_form_field_name( $name ) {
665 $sanitized_name = apply_filters( 'hustle_sanitize_form_field_name', str_replace( ' ', '_', trim( $name ) ), $name );
666
667 return sanitize_text_field( $sanitized_name );
668 }
669
670 public function sanitize_form_elements( $form_elements ) {
671 // Sanitize GDPR message
672 if ( isset( $form_elements['gdpr']['gdpr_message'] ) ) {
673 $allowed_html = array(
674 'a' => array(
675 'href' => true,
676 'title' => true,
677 'target' => true,
678 'alt' => true,
679 ),
680 'b' => array(),
681 'strong' => array(),
682 'i' => array(),
683 'em' => array(),
684 'del' => array(),
685 );
686 $form_elements['gdpr']['gdpr_message'] = wp_kses( wp_unslash( $form_elements['gdpr']['gdpr_message'] ), $allowed_html );
687 }
688
689 $sanitized_fields = array();
690
691 // Loop through each form field.
692 foreach ( $form_elements as $field_data ) {
693 $name = $this->sanitize_form_field_name( $field_data['name'] );
694
695 // After sanitize if name become empty then create array key from label.
696 if ( ! $name ) {
697 $name = $this->sanitize_form_field_name( $field_data['label'] );
698
699 // If still key is empty then go to next iteration.
700 if ( ! $name ) {
701 continue;
702 }
703 }
704
705 // Check field name already exists or not. If exists then create a new name.
706 $name = $this->get_unique_field_name( $sanitized_fields, $name );
707
708 // Sanitize necessary fields.
709 $field_data['name'] = $name;
710 $field_data['label'] = sanitize_text_field( $field_data['label'] );
711 $field_data['placeholder'] = sanitize_text_field( $field_data['placeholder'] );
712
713 // Add new item with field data.
714 $sanitized_fields[ $name ] = $field_data;
715 }
716
717 return $sanitized_fields;
718 }
719
720 /**
721 * Update Custom Fields for Sendgrid New Campaigns
722 */
723 private function maybe_update_custom_fields() {
724 $connected_addons = Hustle_Provider_Utils::get_addons_instance_connected_with_module( $this->module_id );
725
726 foreach ( $connected_addons as $addon ) {
727
728 // Change logic only for sendgrid for now.
729 if ( 'sendgrid' !== $addon->get_slug() ) {
730 continue;
731 }
732 $global_multi_id = $addon->selected_global_multi_id;
733 $new_campaigns = $addon->get_setting( 'new_campaigns', '', $global_multi_id );
734
735 // only if it's the New Sendgrid Campaigns.
736 if ( 'new_campaigns' !== $new_campaigns ) {
737 continue;
738 }
739 $emails = $this->get_emails()->to_array();
740 $custom_fields = array();
741
742 $api_key = $addon->get_setting( 'api_key', '', $global_multi_id );
743 $api = $addon::api( $api_key, $new_campaigns );
744
745 foreach ( $emails['form_elements'] as $element ) {
746 if ( empty( $element['type'] ) || in_array( $element['type'], array( 'submit', 'recaptcha' ), true ) ) {
747 continue;
748 }
749 $custom_fields[] = array(
750 'type' => 'text',
751 'name' => $element['name'],
752 );
753 }
754
755 if ( ! empty( $custom_fields ) ) {
756 $api->add_custom_fields( $custom_fields );
757 }
758 }
759 }
760
761 /**
762 * Gets the selected Google fonts for the active elements in the module.
763 * Used for non-ssharing modules only.
764 *
765 * @since 4.3.0
766 *
767 * @return array
768 */
769 public function get_google_fonts() {
770 $fonts = array();
771
772 if ( '1' === $this->design->use_vanilla ) {
773 return $fonts;
774 }
775
776 $elements = array(
777 'title' => '' !== $this->content->title,
778 'subtitle' => '' !== $this->content->sub_title,
779 'main_content_paragraph' => '' !== $this->content->main_content,
780 'main_content_heading_one' => '' !== $this->content->main_content,
781 'main_content_heading_two' => '' !== $this->content->main_content,
782 'main_content_heading_three' => '' !== $this->content->main_content,
783 'main_content_heading_four' => '' !== $this->content->main_content,
784 'main_content_heading_five' => '' !== $this->content->main_content,
785 'main_content_heading_six' => '' !== $this->content->main_content,
786 'cta' => '0' !== $this->content->show_cta,
787 'never_see_link' => '0' !== $this->content->show_never_see_link,
788 );
789
790 // Only list the font of the elements that are shown, and aren't using a 'custom' font.
791 foreach ( $elements as $element_name => $is_shown ) {
792 if ( ! $is_shown ) {
793 continue;
794 }
795
796 $font = $this->design->{ $element_name . '_font_family' };
797 if ( 'custom' !== $font ) {
798 $font_weight = $this->design->{ $element_name . '_font_weight' };
799 if ( ! isset( $fonts[ $font ] ) ) {
800 $fonts[ $font ] = array();
801 }
802 if ( ! in_array( $font_weight, $fonts[ $font ], true ) ) {
803 $fonts[ $font ][] = $font_weight;
804 }
805 }
806 }
807
808 // We're done here for informational modules.
809 if ( self::OPTIN_MODE !== $this->module_mode ) {
810 return $fonts;
811 }
812
813 $has_mailchimp = ! empty( $this->get_provider_settings( 'mailchimp' ) );
814
815 $form_fields = $this->get_form_fields();
816 $has_success_message = 'show_success' === $this->emails->after_successful_submission;
817
818 $elements_optin = array(
819 'form_extras' => $has_mailchimp,
820 'input' => true,
821 'select' => $has_mailchimp,
822 'checkbox' => $has_mailchimp,
823 'dropdown' => $has_mailchimp,
824 'gdpr' => ! empty( $form_fields['gdpr'] ),
825 'recaptcha' => ! empty( $form_fields['recaptcha'] ),
826 'submit_button' => true,
827 'success_message_paragraph' => $has_success_message,
828 'success_message_heading_one' => $has_success_message,
829 'success_message_heading_two' => $has_success_message,
830 'success_message_heading_three' => $has_success_message,
831 'success_message_heading_four' => $has_success_message,
832 'success_message_heading_five' => $has_success_message,
833 'success_message_heading_six' => $has_success_message,
834 'error_message' => true,
835 );
836
837 foreach ( $elements_optin as $element_name => $is_shown ) {
838 if ( ! $is_shown ) {
839 continue;
840 }
841
842 $font = $this->design->{ $element_name . '_font_family' };
843 if ( 'custom' !== $font ) {
844 $font_weight = $this->design->{ $element_name . '_font_weight' };
845 if ( ! isset( $fonts[ $font ] ) ) {
846 $fonts[ $font ] = array();
847 }
848 if ( ! in_array( $font_weight, $fonts[ $font ], true ) ) {
849 $fonts[ $font ][] = $font_weight;
850 }
851 }
852 }
853 return $fonts;
854 }
855
856 /**
857 * Find the unique name of field without overriding others.
858 *
859 * @since 4.3.3
860 *
861 * @param array $sanitized_fields Array for fields that are previously sanitized.
862 * @param string $field_name field name which will be compare.
863 *
864 * @return array
865 */
866 private function get_unique_field_name( $sanitized_fields, $field_name ) {
867 $new_name = $field_name;
868 $i = 0;
869
870 while ( array_key_exists( $new_name, $sanitized_fields ) ) {
871 $i++;
872 $new_name = $field_name . '-' . $i;
873 }
874
875 return $new_name;
876 }
877
878 }
879