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