PluginProbe
Mailchimp List Subscribe Form / 1.9.0
Mailchimp List Subscribe Form v1.9.0
1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 All 55 releases
mailchimp / mailchimp.php

mailchimp.php in Mailchimp List Subscribe Form 1.9.0, at mailchimp.php

1,053 lines 31.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Mailchimp
4 * Plugin URI: https://mailchimp.com/help/connect-or-disconnect-list-subscribe-for-wordpress/
5 * Description: Add a Mailchimp signup form block, widget or shortcode to your WordPress site.
6 * Text Domain: mailchimp
7 * Version: 1.9.0
8 * Requires at least: 6.4
9 * Requires PHP: 7.0
10 * PHP tested up to: 8.3
11 * Author: Mailchimp
12 * Author URI: https://mailchimp.com/
13 * License: GPL-2.0-or-later
14 * License URI: https://spdx.org/licenses/GPL-2.0-or-later.html
15 *
16 * @package Mailchimp
17 **/
18
19 /**
20 * Copyright 2008-2012 Mailchimp.com (email : api@mailchimp.com)
21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
35 */
36
37 // Check if the autoload file exists
38 if ( is_readable( __DIR__ . '/vendor/autoload.php' ) ) {
39 require_once __DIR__ . '/vendor/autoload.php';
40 } else {
41 add_action(
42 'admin_notices',
43 function () {
44 ?>
45 <div class="notice notice-error">
46 <p>
47 <?php
48 echo wp_kses_post(
49 sprintf(
50 /* translators: 1: Command to run, e.g., <code>composer install</code>, 2: Support URL, e.g., https://wordpress.org/support/plugin/mailchimp/. */
51 __( 'The composer autoload file is not found or not readable. Please contact <a href="%2$s" target="_blank">support</a> if you\'re a user. Please run %1$s if you\'re a developer in a development environment.', 'mailchimp' ),
52 '<code>composer install</code>',
53 'https://wordpress.org/support/plugin/mailchimp/'
54 )
55 );
56 ?>
57 </p>
58 </div>
59 <?php
60 }
61 );
62
63 // Exit early.
64 return;
65 }
66
67 use function Mailchimp\WordPress\Includes\Admin\{admin_notice_error, admin_notice_success};
68
69 // Version constant for easy CSS refreshes
70 define( 'MCSF_VER', '1.9.0' );
71
72 // What's our permission (capability) threshold
73 define( 'MCSF_CAP_THRESHOLD', 'manage_options' );
74
75 // Define our location constants, both MCSF_DIR and MCSF_URL
76 mailchimp_sf_where_am_i();
77
78 // Get our Mailchimp API class in scope
79 if ( ! class_exists( 'MailChimp_API' ) ) {
80 $path = plugin_dir_path( __FILE__ );
81 require_once $path . 'lib/mailchimp/mailchimp.php';
82 }
83
84 // Encryption utility class.
85 require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-data-encryption.php';
86
87 // includes the widget code so it can be easily called either normally or via ajax
88 require_once 'mailchimp_widget.php';
89
90 // includes the backwards compatibility functions
91 require_once 'mailchimp_compat.php';
92
93 // Upgrade routines.
94 require_once 'mailchimp_upgrade.php';
95
96 // Init Admin functions.
97 require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-user-sync-backgroud-process.php';
98 require_once plugin_dir_path( __FILE__ ) . 'includes/admin/class-mailchimp-user-sync.php';
99 require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-admin.php';
100 $admin = new Mailchimp_Admin();
101 $admin->init();
102
103 // Init the blocks.
104 require_once plugin_dir_path( __FILE__ ) . 'includes/blocks/class-mailchimp-list-subscribe-form-blocks.php';
105 $block = new Mailchimp_List_Subscribe_Form_Blocks();
106 $block->init();
107
108 // Form submission handler class.
109 require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-form-submission.php';
110 $form_submission = new Mailchimp_Form_Submission();
111 $form_submission->init();
112
113 // Deprecated functions.
114 require_once plugin_dir_path( __FILE__ ) . 'includes/mailchimp-deprecated-functions.php';
115
116 /**
117 * Do the following plugin setup steps here
118 *
119 * Resource (JS & CSS) enqueuing
120 *
121 * @return void
122 */
123 function mailchimp_sf_plugin_init() {
124
125 if ( get_option( 'mc_list_id' ) && get_option( 'mc_merge_field_migrate' ) !== '1' && mailchimp_sf_get_api() !== false ) {
126 mailchimp_sf_update_merge_fields();
127 }
128
129 if ( mailchimp_sf_should_display_form() ) {
130 // Bring in our appropriate JS and CSS resources
131 add_action( 'wp_enqueue_scripts', 'mailchimp_sf_load_resources' );
132 }
133 }
134
135 add_action( 'init', 'mailchimp_sf_plugin_init' );
136
137 /**
138 * Add the settings link to the Mailchimp plugin row
139 *
140 * @param array $links - Links for the plugin
141 * @return array - Links
142 */
143 function mailchimp_sf_plugin_action_links( $links ) {
144 $settings_page = add_query_arg( array( 'page' => 'mailchimp_sf_options' ), admin_url( 'admin.php' ) );
145 $settings_link = '<a href="' . esc_url( $settings_page ) . '">' . esc_html__( 'Settings', 'mailchimp' ) . '</a>';
146 array_unshift( $links, $settings_link );
147 return $links;
148 }
149
150 add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'mailchimp_sf_plugin_action_links', 10, 1 );
151
152 /**
153 * Loads the appropriate JS and CSS resources depending on
154 * settings and context (admin or not)
155 *
156 * @return void
157 */
158 function mailchimp_sf_load_resources() {
159 // JS
160 wp_enqueue_script( 'mailchimp_sf_main_js', MCSF_URL . 'assets/js/mailchimp.js', array( 'jquery', 'jquery-form', 'jquery-ui-datepicker' ), MCSF_VER, true );
161 // some javascript to get ajax version submitting to the proper location
162 global $wp_scripts;
163 $wp_scripts->localize(
164 'mailchimp_sf_main_js',
165 'mailchimpSF',
166 array(
167 'ajax_url' => trailingslashit( home_url() ),
168 )
169 );
170
171 // Datepicker theme
172 wp_enqueue_style( 'flick', MCSF_URL . 'assets/css/flick/flick.css', array(), MCSF_VER );
173
174 // CSS
175 if ( get_option( 'mc_nuke_all_styles' ) !== '1' ) {
176 wp_enqueue_style( 'mailchimp_sf_main_css', MCSF_URL . 'assets/css/frontend.css', array(), MCSF_VER );
177
178 // Backwards compatibility. TODO: Remove this in a future version.
179 if ( get_option( 'mc_custom_style' ) === 'on' ) {
180 $custom_css = mailchimp_sf_custom_style_css();
181 wp_add_inline_style( 'mailchimp_sf_main_css', $custom_css );
182 }
183 }
184 }
185
186 /**
187 * Custom styles CSS
188 *
189 * @return string
190 */
191 function mailchimp_sf_custom_style_css() {
192 ob_start();
193 ?>
194 #mc_signup_form {
195 padding:5px;
196 border-width: <?php echo absint( get_option( 'mc_form_border_width' ) ); ?>px;
197 border-style: <?php echo ( get_option( 'mc_form_border_width' ) === 0 ) ? 'none' : 'solid'; ?>;
198 border-color: #<?php echo esc_attr( get_option( 'mc_form_border_color' ) ); ?>;
199 color: #<?php echo esc_attr( get_option( 'mc_form_text_color' ) ); ?>;
200 background-color: #<?php echo esc_attr( get_option( 'mc_form_background' ) ); ?>;
201 }
202 <?php
203 return ob_get_clean();
204 }
205
206 /**
207 * Request handler
208 *
209 * @return void
210 */
211 function mailchimp_sf_request_handler() {
212 if ( isset( $_POST['mcsf_action'] ) ) {
213 switch ( $_POST['mcsf_action'] ) {
214 case 'logout':
215 // Check capability & Verify nonce
216 if (
217 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
218 ! isset( $_POST['_mcsf_nonce_action'] ) ||
219 ! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'mc_logout' )
220 ) {
221 wp_die( 'Cheatin&rsquo; huh?' );
222 }
223
224 // erase auth information
225 $options = array( 'mc_api_key', 'mailchimp_sf_access_token', 'mc_datacenter', 'mailchimp_sf_auth_error', 'mailchimp_sf_waiting_for_login' );
226 mailchimp_sf_delete_options( $options );
227 break;
228 case 'change_form_settings':
229 if (
230 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
231 ! isset( $_POST['_mcsf_nonce_action'] ) ||
232 ! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'update_general_form_settings' )
233 ) {
234 wp_die( 'Cheatin&rsquo; huh?' );
235 }
236
237 // Update the form settings
238 mailchimp_sf_save_general_form_settings();
239 break;
240 }
241 }
242 }
243 add_action( 'init', 'mailchimp_sf_request_handler' );
244
245 /**
246 * Update merge fields
247 *
248 * @return void
249 */
250 function mailchimp_sf_update_merge_fields() {
251 mailchimp_sf_get_merge_vars( get_option( 'mc_list_id' ), true );
252 mailchimp_sf_get_interest_categories( get_option( 'mc_list_id' ), true );
253 update_option( 'mc_merge_field_migrate', true );
254 }
255
256 /**
257 * Get auth key
258 *
259 * @param mixed $salt Salt
260 * @return string
261 */
262 function mailchimp_sf_auth_nonce_key( $salt = null ) {
263 if ( is_null( $salt ) ) {
264 $salt = mailchimp_sf_auth_nonce_salt();
265 }
266 return 'social_authentication' . md5( AUTH_KEY . $salt );
267 }
268
269 /**
270 * Return auth nonce salt
271 *
272 * @return string
273 */
274 function mailchimp_sf_auth_nonce_salt() {
275 return md5( microtime() . isset( $_SERVER['SERVER_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_ADDR'] ) ) : '' );
276 }
277
278 /**
279 * Creates new Mailchimp API v3 object
280 *
281 * @return MailChimp_API | false
282 */
283 function mailchimp_sf_get_api() {
284 // Check for the access token first.
285 $access_token = mailchimp_sf_get_access_token();
286 $data_center = get_option( 'mc_datacenter' );
287 if ( ! empty( $access_token ) && ! empty( $data_center ) ) {
288 return new MailChimp_API( $access_token, $data_center );
289 }
290
291 // Check for the API key if the access token is not available.
292 $key = get_option( 'mc_api_key' );
293 if ( $key ) {
294 return new MailChimp_API( $key );
295 }
296
297 return false;
298 }
299
300 /**
301 * Checks to see if we're storing a password, if so, we need
302 * to upgrade to the API key
303 *
304 * @return bool
305 **/
306 function mailchimp_sf_needs_upgrade() {
307 $igs = get_option( 'mc_interest_groups' );
308
309 if ( false !== $igs // we have an option
310 && (
311 empty( $igs ) || // it can be an empty array (no interest groups)
312 ( is_array( $igs ) && isset( $igs[0]['id'] ) ) // OR it should be a populated array that's well-formed
313 )
314 ) {
315 return false; // no need to upgrade
316 } else {
317 return true; // yeah, let's do it
318 }
319 }
320
321 /**
322 * Deletes all Mailchimp options
323 *
324 * TODO: The options names should be moved to a config file
325 * or to a class dedicated to options
326 **/
327 function mailchimp_sf_delete_setup() {
328 $options = array(
329 'mc_user_id',
330 'mc_use_unsub_link',
331 'mc_list_id',
332 'mc_list_name',
333 'mc_interest_groups',
334 'mc_merge_vars',
335 );
336
337 $igs = get_option( 'mc_interest_groups' );
338 if ( is_array( $igs ) ) {
339 foreach ( $igs as $ig ) {
340 $opt = 'mc_show_interest_groups_' . $ig['id'];
341 $options[] = $opt;
342 }
343 }
344
345 $mv = get_option( 'mc_merge_vars' );
346 if ( is_array( $mv ) ) {
347 foreach ( $mv as $mv_var ) {
348 $opt = 'mc_mv_' . $mv_var['tag'];
349 $options[] = $opt;
350 }
351 }
352
353 mailchimp_sf_delete_options( $options );
354 }
355
356 /**
357 * Gets or sets a frontend message based on parameter passed to it
358 *
359 * Used to convey error messages to the user outside of the WP Admin
360 *
361 * On the plugin settings page, WP admin notices are used exclusively
362 * instead of the frontend message.
363 *
364 * @param mixed $msg Message
365 * @return string/bool depending on get/set
366 */
367 function mailchimp_sf_frontend_msg( $msg = null ) {
368 global $mcsf_msgs;
369
370 // Make sure we're formed properly
371 if ( ! is_array( $mcsf_msgs ) ) {
372 $mcsf_msgs = array();
373 }
374
375 // See if we're getting
376 if ( is_null( $msg ) ) {
377 return implode( '', $mcsf_msgs );
378 }
379
380 // Must be setting
381 $mcsf_msgs[] = $msg;
382 return true;
383 }
384
385 /**
386 * Gets or sets a frontend message based on parameter passed to it
387 *
388 * TODO: Deprecate this function in favor of mailchimp_sf_frontend_msg()
389 *
390 * @param mixed $msg Message
391 * @return string/bool depending on get/set
392 */
393 function mailchimp_sf_global_msg( $msg = null ) {
394 return mailchimp_sf_frontend_msg( $msg );
395 }
396
397 /**
398 * Sets the default options for the option form
399 *
400 * @param string $list_name The Mailchimp list name.
401 * @return void
402 */
403 function mailchimp_sf_set_form_defaults( $list_name = '' ) {
404 update_option( 'mc_header_content', esc_html__( 'Sign up for', 'mailchimp' ) . ' ' . $list_name );
405 update_option( 'mc_submit_text', esc_html__( 'Subscribe', 'mailchimp' ) );
406
407 update_option( 'mc_custom_style', 'off' );
408 update_option( 'mc_double_optin', true );
409 update_option( 'mc_use_unsub_link', 'off' );
410
411 update_option( 'mc_form_border_width', '1' );
412 update_option( 'mc_form_border_color', 'E0E0E0' );
413 update_option( 'mc_form_background', 'FFFFFF' );
414 update_option( 'mc_form_text_color', '3F3F3f' );
415 }
416
417 /**
418 * Saves the General Form settings on the options page
419 *
420 * @return void
421 **/
422 function mailchimp_sf_save_general_form_settings() {
423
424 /*Enable double optin toggle*/
425 if ( isset( $_POST['mc_double_optin'] ) ) {
426 update_option( 'mc_double_optin', true );
427 $msg = esc_html__( 'Double opt-in turned On!', 'mailchimp' );
428 admin_notice_success( $msg );
429 } elseif ( get_option( 'mc_double_optin' ) !== false ) {
430 update_option( 'mc_double_optin', false );
431 $msg = esc_html__( 'Double opt-in turned Off!', 'mailchimp' );
432 admin_notice_success( $msg );
433 }
434
435 /* NUKE the CSS! */
436 if ( isset( $_POST['mc_nuke_all_styles'] ) ) {
437 update_option( 'mc_nuke_all_styles', true );
438 $msg = esc_html__( 'Mailchimp CSS turned Off!', 'mailchimp' );
439 admin_notice_success( $msg );
440 } elseif ( get_option( 'mc_nuke_all_styles' ) !== false ) {
441 update_option( 'mc_nuke_all_styles', false );
442 $msg = esc_html__( 'Mailchimp CSS turned On!', 'mailchimp' );
443 admin_notice_success( $msg );
444 }
445
446 /* Update existing */
447 if ( isset( $_POST['mc_update_existing'] ) ) {
448 update_option( 'mc_update_existing', true );
449 $msg = esc_html__( 'Update existing subscribers turned On!' );
450 admin_notice_success( $msg );
451 } elseif ( get_option( 'mc_update_existing' ) !== false ) {
452 update_option( 'mc_update_existing', false );
453 $msg = esc_html__( 'Update existing subscribers turned Off!' );
454 admin_notice_success( $msg );
455 }
456
457 if ( isset( $_POST['mc_use_unsub_link'] ) ) {
458 update_option( 'mc_use_unsub_link', 'on' );
459 $msg = esc_html__( 'Unsubscribe link turned On!', 'mailchimp' );
460 admin_notice_success( $msg );
461 } elseif ( get_option( 'mc_use_unsub_link' ) !== 'off' ) {
462 update_option( 'mc_use_unsub_link', 'off' );
463 $msg = esc_html__( 'Unsubscribe link turned Off!', 'mailchimp' );
464 admin_notice_success( $msg );
465 }
466
467 $content = isset( $_POST['mc_header_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_header_content'] ) ) : '';
468 $content = str_replace( "\r\n", '<br/>', $content );
469 update_option( 'mc_header_content', $content );
470
471 $content = isset( $_POST['mc_subheader_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_subheader_content'] ) ) : '';
472 $content = str_replace( "\r\n", '<br/>', $content );
473 update_option( 'mc_subheader_content', $content );
474
475 $submit_text = isset( $_POST['mc_submit_text'] ) ? sanitize_text_field( wp_unslash( $_POST['mc_submit_text'] ) ) : '';
476 $submit_text = str_replace( "\r\n", '', $submit_text );
477 update_option( 'mc_submit_text', $submit_text );
478
479 // Set Custom Style option
480 update_option( 'mc_custom_style', isset( $_POST['mc_custom_style'] ) ? 'on' : 'off' );
481
482 // we told them not to put these things we are replacing in, but let's just make sure they are listening...
483 if ( isset( $_POST['mc_form_border_width'] ) ) {
484 update_option( 'mc_form_border_width', str_replace( 'px', '', absint( $_POST['mc_form_border_width'] ) ) );
485 }
486 if ( isset( $_POST['mc_form_border_color'] ) ) {
487 update_option( 'mc_form_border_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_border_color'] ) ) ) );
488 }
489 if ( isset( $_POST['mc_form_background'] ) ) {
490 update_option( 'mc_form_background', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_background'] ) ) ) );
491 }
492 if ( isset( $_POST['mc_form_text_color'] ) ) {
493 update_option( 'mc_form_text_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_text_color'] ) ) ) );
494 }
495
496 // IF NOT DEV MODE
497 $igs = get_option( 'mc_interest_groups' );
498 if ( is_array( $igs ) ) {
499 foreach ( $igs as $mv_var ) {
500 $opt = 'mc_show_interest_groups_' . $mv_var['id'];
501 if ( isset( $_POST[ $opt ] ) ) {
502 update_option( $opt, 'on' );
503 } else {
504 update_option( $opt, 'off' );
505 }
506 }
507 }
508
509 $mv = get_option( 'mc_merge_vars' );
510 if ( is_array( $mv ) ) {
511 foreach ( $mv as $mv_var ) {
512 $opt = 'mc_mv_' . $mv_var['tag'];
513 if ( isset( $_POST[ $opt ] ) || 'Y' === $mv_var['required'] ) {
514 update_option( $opt, 'on' );
515 } else {
516 update_option( $opt, 'off' );
517 }
518 }
519 }
520
521 $msg = esc_html__( 'Successfully Updated your List Subscribe Form Settings!', 'mailchimp' );
522 admin_notice_success( $msg );
523 }
524
525 /**
526 * Sees if the user changed the list, and updates options accordingly
527 **/
528 function mailchimp_sf_change_list_if_necessary() {
529 if ( ! isset( $_POST['mc_list_id'] ) ) {
530 return;
531 }
532
533 if ( empty( $_POST['mc_list_id'] ) ) {
534 $msg = esc_html__( 'Please choose a valid list', 'mailchimp' );
535 admin_notice_error( $msg );
536 return;
537 }
538
539 // Simple permission check before going through all this
540 if ( ! current_user_can( MCSF_CAP_THRESHOLD ) ) { return; }
541
542 $api = mailchimp_sf_get_api();
543 if ( ! $api ) { return; }
544
545 // we *could* support paging, but few users have that many lists (and shouldn't)
546 $lists = $api->get( 'lists', 100, array( 'fields' => 'lists.id,lists.name,lists.email_type_option' ) );
547
548 if ( ! isset( $lists['lists'] ) || is_wp_error( $lists['lists'] ) ) {
549 return;
550 }
551
552 $lists = $lists['lists'];
553
554 if ( is_array( $lists ) && ! empty( $lists ) ) {
555
556 /**
557 * If our incoming list ID (the one chosen in the select dropdown)
558 * is in our array of lists, the set it to be the active list
559 */
560 foreach ( $lists as $key => $list ) {
561 if ( isset( $_POST['mc_list_id'] ) && $list['id'] === $_POST['mc_list_id'] ) {
562 $list_id = sanitize_text_field( wp_unslash( $_POST['mc_list_id'] ) );
563 $list_name = $list['name'];
564 $list_key = $key;
565 }
566
567 $merge_fields = mailchimp_sf_get_merge_vars( $list['id'], false, false );
568 $interests = mailchimp_sf_get_interest_categories( $list['id'], false, false );
569 if ( ! empty( $merge_fields ) ) {
570 update_option( 'mailchimp_sf_merge_fields_' . $list['id'], $merge_fields );
571 }
572 if ( ! empty( $interests ) ) {
573 update_option( 'mailchimp_sf_interest_groups_' . $list['id'], $interests );
574 }
575 }
576
577 $orig_list = get_option( 'mc_list_id' );
578 if ( '' !== $list_id ) {
579 update_option( 'mc_list_id', $list_id );
580 update_option( 'mc_list_name', $list_name );
581 update_option( 'mc_email_type_option', $lists[ $list_key ]['email_type_option'] );
582
583 // See if the user changed the list
584 $new_list = false;
585 if ( $orig_list !== $list_id ) {
586 // The user changed the list, Reset the Form Defaults
587 mailchimp_sf_set_form_defaults( $list_name );
588
589 $new_list = true;
590 }
591
592 // Grab the merge vars and interest groups
593 $mv = mailchimp_sf_get_merge_vars( $list_id, $new_list );
594 $igs = mailchimp_sf_get_interest_categories( $list_id, $new_list );
595
596 $igs_text = ' ';
597 if ( is_array( $igs ) ) {
598 /* translators: %s: count (number) */
599 $igs_text .= sprintf( esc_html__( 'and %s Sets of Interest Groups', 'mailchimp' ), count( $igs ) );
600 }
601
602 $msg = sprintf(
603 /* translators: %s: count (number) */
604 __( '<b>Success!</b> Loaded and saved the info for %d Merge Variables', 'mailchimp' ) . $igs_text,
605 count( $mv )
606 ) . ' ' .
607 esc_html__( 'from your list' ) . ' "' . $list_name . '"<br/><br/>' .
608 esc_html__( 'Now you should either Turn On the Mailchimp Widget or change your options below, then turn it on.', 'mailchimp' );
609
610 admin_notice_success( $msg );
611 }
612
613 // Update the lists option.
614 update_option( 'mailchimp_sf_lists', $lists );
615 }
616 }
617
618 /**
619 * Get merge vars
620 *
621 * @param string $list_id List ID
622 * @param bool $new_list Whether this is a new list
623 * @param bool $update_option Whether to update the option
624 * @return array
625 */
626 function mailchimp_sf_get_merge_vars( $list_id, $new_list, $update_option = true ) {
627 $api = mailchimp_sf_get_api();
628 $mv = $api->get( 'lists/' . $list_id . '/merge-fields', 80 );
629
630 // if we get an error back from the api, exit this process.
631 if ( is_wp_error( $mv ) ) {
632 return;
633 }
634
635 $mv['merge_fields'] = mailchimp_sf_add_email_field( $mv['merge_fields'] );
636 if ( $update_option ) {
637 update_option( 'mc_merge_vars', $mv['merge_fields'] );
638 }
639
640 foreach ( $mv['merge_fields'] as $mv_var ) {
641 $opt = 'mc_mv_' . $mv_var['tag'];
642 if ( $new_list ) {
643 $public = $mv_var['public'] ?? false;
644 if ( ! $public ) {
645 // This is a hidden field, so we don't want to include it.
646 update_option( $opt, 'off' );
647 } else {
648 // We need to set the option to 'on' so that it shows up in the form.
649 update_option( $opt, 'on' );
650 }
651 }
652 }
653 return $mv['merge_fields'];
654 }
655
656 /**
657 * Add email field
658 *
659 * @param array $merge Merge
660 * @return array
661 */
662 function mailchimp_sf_add_email_field( $merge ) {
663 $email = array(
664 'tag' => 'EMAIL',
665 'name' => esc_html__( 'Email Address', 'mailchimp' ),
666 'type' => 'email',
667 'required' => true,
668 'public' => true,
669 'display_order' => 1,
670 'default_value' => null,
671 );
672 array_unshift( $merge, $email );
673 return $merge;
674 }
675
676 /**
677 * Get interest categories
678 *
679 * @param string $list_id List ID
680 * @param bool $new_list Whether this is a new list
681 * @param bool $update_option Whether to update the option
682 * @return array
683 */
684 function mailchimp_sf_get_interest_categories( $list_id, $new_list, $update_option = true ) {
685 $api = mailchimp_sf_get_api();
686 $igs = $api->get( 'lists/' . $list_id . '/interest-categories', 60 );
687
688 // if we get an error back from the api, exis
689 if ( is_wp_error( $igs ) ) {
690 return;
691 }
692
693 if ( is_array( $igs ) ) {
694 $key = 0;
695 foreach ( $igs['categories'] as $ig ) {
696 $groups = $api->get( 'lists/' . $list_id . '/interest-categories/' . $ig['id'] . '/interests', 60 );
697 $igs['categories'][ $key ]['groups'] = $groups['interests'];
698 $opt = 'mc_show_interest_groups_' . $ig['id'];
699
700 // turn them all on by default
701 if ( $new_list ) {
702 update_option( $opt, 'on' );
703 }
704 ++$key;
705 }
706 }
707
708 if ( $update_option ) {
709 update_option( 'mc_interest_groups', $igs['categories'] );
710 }
711
712 return $igs['categories'];
713 }
714
715 /**
716 * Register the widget.
717 *
718 * @return void
719 */
720 function mailchimp_sf_register_widgets() {
721 if ( mailchimp_sf_get_api() ) {
722 register_widget( 'Mailchimp_SF_Widget' );
723 }
724 }
725 add_action( 'widgets_init', 'mailchimp_sf_register_widgets' );
726
727 /**
728 * Add shortcode
729 *
730 * @return string
731 */
732 function mailchimp_sf_shortcode() {
733 ob_start();
734 mailchimp_sf_signup_form();
735 return ob_get_clean();
736 }
737 add_shortcode( 'mailchimpsf_form', 'mailchimp_sf_shortcode' );
738
739 /**
740 * Cleans up merge fields and interests to make them
741 * API 3.0-friendly.
742 *
743 * @param [type] $merge Merge fields
744 * @param [type] $igs Interest groups
745 * @param string $email_type Email type
746 * @param string $email Email
747 * @param string|false $status Status The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if an error occurred.
748 * @param string $double_optin Whether double opt-in is enabled. "1" for enabled and "" for disabled.
749 * @return stdClass
750 */
751 function mailchimp_sf_subscribe_body( $merge, $igs, $email_type, $email, $status, $double_optin ) {
752 $body = new stdClass();
753 $body->email_address = $email;
754 $body->email_type = $email_type;
755 $body->merge_fields = $merge;
756
757 if ( ! empty( $igs ) ) {
758 $body->interests = $igs;
759 }
760
761 // Early return for already subscribed users
762 if ( 'subscribed' === $status ) {
763 return $body;
764 }
765
766 // Subscribe the email immediately unless double opt-in is enabled
767 // "unsubscribed" and "subscribed" existing emails have been excluded at this stage
768 // "pending" emails should follow double opt-in rules
769 $body->status = $double_optin ? 'pending' : 'subscribed';
770
771 return $body;
772 }
773
774 /**
775 * Check the status of a subscriber in the list.
776 *
777 * @param string $endpoint API endpoint to check the status.
778 * @return string|false The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if the API returned 404 or an error occurred.
779 */
780 function mailchimp_sf_check_status( $endpoint ) {
781 $endpoint .= '?fields=status';
782 $api = mailchimp_sf_get_api();
783 $subscriber = $api->get( $endpoint, null );
784 if ( is_wp_error( $subscriber ) ) {
785 return false;
786 }
787 return $subscriber['status'];
788 }
789
790 /**
791 * Validate phone
792 *
793 * @param array $opt_val Option value
794 * @param array $data Data
795 * @return void
796 */
797 function mailchimp_sf_merge_validate_phone( $opt_val, $data ) {
798 // This filters out all 'falsey' elements
799 $opt_val = array_filter( $opt_val );
800 // If they weren't all empty
801 if ( ! $opt_val ) {
802 return;
803 }
804
805 $opt_val = implode( '-', $opt_val );
806 if ( strlen( $opt_val ) < 12 ) {
807 $opt_val = '';
808 }
809
810 if ( ! preg_match( '/[0-9]{0,3}-[0-9]{0,3}-[0-9]{0,4}/A', $opt_val ) ) {
811 /* translators: %s: field name */
812 $message = sprintf( esc_html__( '%s must consist of only numbers', 'mailchimp' ), esc_html( $data['name'] ) );
813 $error = new WP_Error( 'mc_phone_validation', $message );
814 return $error;
815 }
816
817 return $opt_val;
818 }
819
820 /**
821 * Validate address
822 *
823 * @param array $opt_val Option value
824 * @param array $data Data
825 * @return mixed
826 */
827 function mailchimp_sf_merge_validate_address( $opt_val, $data ) {
828 if ( 'Y' === $data['required'] ) {
829 if ( empty( $opt_val['addr1'] ) || empty( $opt_val['city'] ) ) {
830 /* translators: %s: field name */
831 $message = sprintf( esc_html__( 'You must fill in %s.', 'mailchimp' ), esc_html( $data['name'] ) );
832 $error = new WP_Error( 'invalid_address_merge', $message );
833 return $error;
834 }
835 } elseif ( empty( $opt_val['addr1'] ) || empty( $opt_val['city'] ) ) {
836 return false;
837 }
838
839 $merge = new stdClass();
840 $merge->addr1 = $opt_val['addr1'];
841 $merge->addr2 = $opt_val['addr2'];
842 $merge->city = $opt_val['city'];
843 $merge->state = $opt_val['state'];
844 $merge->zip = $opt_val['zip'];
845 $merge->country = $opt_val['country'];
846 return $merge;
847 }
848
849 /**
850 * Verify key
851 *
852 * @param MailChimp_API $api API instance
853 * @return mixed
854 */
855 function mailchimp_sf_verify_key( $api ) {
856 $user = $api->get( '' );
857 if ( is_wp_error( $user ) ) {
858 return $user;
859 }
860
861 // Might as well set this data if we have it already.
862 $valid_roles = array( 'owner', 'admin', 'manager' );
863 if ( isset( $user['role'] ) && in_array( $user['role'], $valid_roles, true ) ) {
864 update_option( 'mc_api_key', $api->key );
865 update_option( 'mc_user', $user );
866 update_option( 'mc_datacenter', $api->datacenter );
867
868 } else {
869 $msg = esc_html__( 'API Key must belong to "Owner", "Admin", or "Manager."', 'mailchimp' );
870 return new WP_Error( 'mc-invalid-role', $msg );
871 }
872 }
873
874 /**
875 * Update profile URL.
876 *
877 * @param string $email Email
878 * @return string
879 */
880 function mailchimp_sf_update_profile_url( $email ) {
881 $dc = get_option( 'mc_datacenter' );
882 // This is the expected encoding for emails.
883 $eid = base64_encode( $email ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- ignoring because this is the expected data for the endpoint
884 $user = get_option( 'mc_user' );
885 $list_id = get_option( 'mc_list_id' );
886 $url = 'http://' . $dc . '.list-manage.com/subscribe/send-email?u=' . $user['account_id'] . '&id=' . $list_id . '&e=' . $eid;
887 return $url;
888 }
889
890 /**
891 * Delete options
892 *
893 * @param array $options Options
894 * @return void
895 */
896 function mailchimp_sf_delete_options( $options = array() ) {
897 foreach ( $options as $option ) {
898 delete_option( $option );
899 }
900 }
901
902 /**********************
903 * Utility Functions *
904 **********************/
905 /**
906 * Utility function to allow placement of plugin in plugins, mu-plugins, child or parent theme's plugins folders
907 *
908 * This function must be ran _very early_ in the load process, as it sets up important constants for the rest of the plugin
909 */
910 function mailchimp_sf_where_am_i() {
911 $locations = array(
912 'plugins' => array(
913 'dir' => plugin_dir_path( __FILE__ ),
914 'url' => plugins_url(),
915 ),
916 'mu_plugins' => array(
917 'dir' => plugin_dir_path( __FILE__ ),
918 'url' => plugins_url(),
919 ),
920 'template' => array(
921 'dir' => trailingslashit( get_template_directory() ) . 'plugins/',
922 'url' => trailingslashit( get_template_directory_uri() ) . 'plugins/',
923 ),
924 'stylesheet' => array(
925 'dir' => trailingslashit( get_stylesheet_directory() ) . 'plugins/',
926 'url' => trailingslashit( get_stylesheet_directory_uri() ) . 'plugins/',
927 ),
928 );
929
930 // Set defaults
931 $mscf_dirbase = trailingslashit( basename( __DIR__ ) ); // Typically wp-mailchimp/ or mailchimp/
932 $mscf_dir = trailingslashit( plugin_dir_path( __FILE__ ) );
933 $mscf_url = trailingslashit( plugins_url( '', __FILE__ ) );
934
935 // Try our hands at finding the real location
936 foreach ( $locations as $key => $loc ) {
937 $dir = trailingslashit( $loc['dir'] ) . $mscf_dirbase;
938 $url = trailingslashit( $loc['url'] ) . $mscf_dirbase;
939 if ( is_file( $dir . basename( __FILE__ ) ) ) {
940 $mscf_dir = $dir;
941 $mscf_url = $url;
942 break;
943 }
944 }
945
946 // Define our complete filesystem path
947 define( 'MCSF_DIR', $mscf_dir );
948
949 // Define our complete URL to the plugin folder
950 define( 'MCSF_URL', $mscf_url );
951 }
952
953
954 /**
955 * MODIFIED VERSION of wp_verify_nonce from WP Core. Core was not overridden to prevent problems when replacing
956 * something universally.
957 *
958 * Verify that correct nonce was used with time limit.
959 *
960 * The user is given an amount of time to use the token, so therefore, since the
961 * UID and $action remain the same, the independent variable is the time.
962 *
963 * @param string $nonce Nonce that was used in the form to verify
964 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
965 * @return bool Whether the nonce check passed or failed.
966 */
967 function mailchimp_sf_verify_nonce( $nonce, $action = -1 ) {
968 $user = wp_get_current_user();
969 $uid = (int) $user->ID;
970 if ( ! $uid ) {
971 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
972 }
973
974 if ( empty( $nonce ) ) {
975 return false;
976 }
977
978 $token = 'MAILCHIMP';
979 $i = wp_nonce_tick();
980
981 // Nonce generated 0-12 hours ago
982 $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
983 if ( hash_equals( $expected, $nonce ) ) {
984 return 1;
985 }
986
987 // Nonce generated 12-24 hours ago
988 $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
989 if ( hash_equals( $expected, $nonce ) ) {
990 return 2;
991 }
992
993 // Invalid nonce
994 return false;
995 }
996
997
998 /**
999 * MODIFIED VERSION of wp_create_nonce from WP Core. Core was not overridden to prevent problems when replacing
1000 * something universally.
1001 *
1002 * Creates a cryptographic token tied to a specific action, user, and window of time.
1003 *
1004 * @param string $action Scalar value to add context to the nonce.
1005 * @return string The token.
1006 */
1007 function mailchimp_sf_create_nonce( $action = -1 ) {
1008 $user = wp_get_current_user();
1009 $uid = (int) $user->ID;
1010 if ( ! $uid ) {
1011 /** This filter is documented in wp-includes/pluggable.php */
1012 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
1013 }
1014
1015 $token = 'MAILCHIMP';
1016 $i = wp_nonce_tick();
1017
1018 return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
1019 }
1020
1021 /**
1022 * Get Mailchimp Access Token.
1023 *
1024 * @since 1.6.0
1025 * @return string|bool
1026 */
1027 function mailchimp_sf_get_access_token() {
1028 $access_token = get_option( 'mailchimp_sf_access_token' );
1029 if ( empty( $access_token ) ) {
1030 return false;
1031 }
1032
1033 $data_encryption = new Mailchimp_Data_Encryption();
1034 $access_token = $data_encryption->decrypt( $access_token );
1035
1036 // If decryption fails, display notice to user to re-authenticate.
1037 if ( false === $access_token ) {
1038 update_option( 'mailchimp_sf_auth_error', true );
1039 }
1040
1041 return $access_token;
1042 }
1043
1044 /**
1045 * Should display Mailchimp Signup form.
1046 *
1047 * @since 1.6.0
1048 * @return bool
1049 */
1050 function mailchimp_sf_should_display_form() {
1051 return mailchimp_sf_get_api() && ! get_option( 'mailchimp_sf_auth_error' ) && get_option( 'mc_list_id' );
1052 }
1053