PluginProbe
Mailchimp List Subscribe Form / 2.0.0
Mailchimp List Subscribe Form v2.0.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 2.0.0, at mailchimp.php

995 lines 29.7 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: 2.0.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', '2.0.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 'phone_validation_error' => esc_html__( 'Please enter a valid phone number.', 'mailchimp' ),
169 )
170 );
171
172 // Datepicker theme
173 wp_enqueue_style( 'flick', MCSF_URL . 'assets/css/flick/flick.css', array(), MCSF_VER );
174
175 // CSS
176 if ( get_option( 'mc_nuke_all_styles' ) !== '1' ) {
177 wp_enqueue_style( 'mailchimp_sf_main_css', MCSF_URL . 'assets/css/frontend.css', array(), MCSF_VER );
178
179 // Backwards compatibility. TODO: Remove this in a future version.
180 if ( get_option( 'mc_custom_style' ) === 'on' ) {
181 $custom_css = mailchimp_sf_custom_style_css();
182 wp_add_inline_style( 'mailchimp_sf_main_css', $custom_css );
183 }
184 }
185 }
186
187 /**
188 * Custom styles CSS
189 *
190 * @return string
191 */
192 function mailchimp_sf_custom_style_css() {
193 ob_start();
194 ?>
195 .mc_signup_form {
196 padding:5px;
197 border-width: <?php echo absint( get_option( 'mc_form_border_width' ) ); ?>px;
198 border-style: <?php echo ( get_option( 'mc_form_border_width' ) === 0 ) ? 'none' : 'solid'; ?>;
199 border-color: #<?php echo esc_attr( get_option( 'mc_form_border_color' ) ); ?>;
200 color: #<?php echo esc_attr( get_option( 'mc_form_text_color' ) ); ?>;
201 background-color: #<?php echo esc_attr( get_option( 'mc_form_background' ) ); ?>;
202 }
203 <?php
204 return ob_get_clean();
205 }
206
207 /**
208 * Request handler
209 *
210 * @return void
211 */
212 function mailchimp_sf_request_handler() {
213 if ( isset( $_POST['mcsf_action'] ) ) {
214 switch ( $_POST['mcsf_action'] ) {
215 case 'logout':
216 // Check capability & Verify nonce
217 if (
218 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
219 ! isset( $_POST['_mcsf_nonce_action'] ) ||
220 ! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'mc_logout' )
221 ) {
222 wp_die( 'Cheatin&rsquo; huh?' );
223 }
224
225 // erase auth information
226 $options = array( 'mc_api_key', 'mailchimp_sf_access_token', 'mc_datacenter', 'mailchimp_sf_auth_error', 'mailchimp_sf_waiting_for_login' );
227 mailchimp_sf_delete_options( $options );
228 break;
229 case 'change_form_settings':
230 if (
231 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
232 ! isset( $_POST['_mcsf_nonce_action'] ) ||
233 ! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'update_general_form_settings' )
234 ) {
235 wp_die( 'Cheatin&rsquo; huh?' );
236 }
237
238 // Update the form settings
239 mailchimp_sf_save_general_form_settings();
240 break;
241 }
242 }
243 }
244 add_action( 'init', 'mailchimp_sf_request_handler' );
245
246 /**
247 * Update merge fields
248 *
249 * @return void
250 */
251 function mailchimp_sf_update_merge_fields() {
252 mailchimp_sf_get_merge_vars( get_option( 'mc_list_id' ), true );
253 mailchimp_sf_get_interest_categories( get_option( 'mc_list_id' ), true );
254 update_option( 'mc_merge_field_migrate', true );
255 }
256
257 /**
258 * Get auth key
259 *
260 * @param mixed $salt Salt
261 * @return string
262 */
263 function mailchimp_sf_auth_nonce_key( $salt = null ) {
264 if ( is_null( $salt ) ) {
265 $salt = mailchimp_sf_auth_nonce_salt();
266 }
267 return 'social_authentication' . md5( AUTH_KEY . $salt );
268 }
269
270 /**
271 * Return auth nonce salt
272 *
273 * @return string
274 */
275 function mailchimp_sf_auth_nonce_salt() {
276 return md5( microtime() . isset( $_SERVER['SERVER_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_ADDR'] ) ) : '' );
277 }
278
279 /**
280 * Creates new Mailchimp API v3 object
281 *
282 * @return MailChimp_API | false
283 */
284 function mailchimp_sf_get_api() {
285 // Check for the access token first.
286 $access_token = mailchimp_sf_get_access_token();
287 $data_center = get_option( 'mc_datacenter' );
288 if ( ! empty( $access_token ) && ! empty( $data_center ) ) {
289 return new MailChimp_API( $access_token, $data_center );
290 }
291
292 // Check for the API key if the access token is not available.
293 $key = get_option( 'mc_api_key' );
294 if ( $key ) {
295 return new MailChimp_API( $key );
296 }
297
298 return false;
299 }
300
301 /**
302 * Checks to see if we're storing a password, if so, we need
303 * to upgrade to the API key
304 *
305 * @return bool
306 **/
307 function mailchimp_sf_needs_upgrade() {
308 $igs = get_option( 'mc_interest_groups' );
309
310 if ( false !== $igs // we have an option
311 && (
312 empty( $igs ) || // it can be an empty array (no interest groups)
313 ( is_array( $igs ) && isset( $igs[0]['id'] ) ) // OR it should be a populated array that's well-formed
314 )
315 ) {
316 return false; // no need to upgrade
317 } else {
318 return true; // yeah, let's do it
319 }
320 }
321
322 /**
323 * Deletes all Mailchimp options
324 *
325 * TODO: The options names should be moved to a config file
326 * or to a class dedicated to options
327 **/
328 function mailchimp_sf_delete_setup() {
329 $options = array(
330 'mc_user_id',
331 'mc_use_unsub_link',
332 'mc_list_id',
333 'mc_list_name',
334 'mc_interest_groups',
335 'mc_merge_vars',
336 );
337
338 $igs = get_option( 'mc_interest_groups' );
339 if ( is_array( $igs ) ) {
340 foreach ( $igs as $ig ) {
341 $opt = 'mc_show_interest_groups_' . $ig['id'];
342 $options[] = $opt;
343 }
344 }
345
346 $mv = get_option( 'mc_merge_vars' );
347 if ( is_array( $mv ) ) {
348 foreach ( $mv as $mv_var ) {
349 $opt = 'mc_mv_' . $mv_var['tag'];
350 $options[] = $opt;
351 }
352 }
353
354 mailchimp_sf_delete_options( $options );
355 }
356
357 /**
358 * Gets or sets a frontend message based on parameter passed to it
359 *
360 * Used to convey error messages to the user outside of the WP Admin
361 *
362 * On the plugin settings page, WP admin notices are used exclusively
363 * instead of the frontend message.
364 *
365 * @param mixed $msg Message
366 * @return string/bool depending on get/set
367 */
368 function mailchimp_sf_frontend_msg( $msg = null ) {
369 global $mcsf_msgs;
370
371 // Make sure we're formed properly
372 if ( ! is_array( $mcsf_msgs ) ) {
373 $mcsf_msgs = array();
374 }
375
376 // See if we're getting
377 if ( is_null( $msg ) ) {
378 return implode( '', $mcsf_msgs );
379 }
380
381 // Must be setting
382 $mcsf_msgs[] = $msg;
383 return true;
384 }
385
386 /**
387 * Gets or sets a frontend message based on parameter passed to it
388 *
389 * TODO: Deprecate this function in favor of mailchimp_sf_frontend_msg()
390 *
391 * @param mixed $msg Message
392 * @return string/bool depending on get/set
393 */
394 function mailchimp_sf_global_msg( $msg = null ) {
395 return mailchimp_sf_frontend_msg( $msg );
396 }
397
398 /**
399 * Sets the default options for the option form
400 *
401 * @param string $list_name The Mailchimp list name.
402 * @return void
403 */
404 function mailchimp_sf_set_form_defaults( $list_name = '' ) {
405 update_option( 'mc_header_content', esc_html__( 'Sign up for', 'mailchimp' ) . ' ' . $list_name );
406 update_option( 'mc_submit_text', esc_html__( 'Subscribe', 'mailchimp' ) );
407
408 update_option( 'mc_custom_style', 'off' );
409 update_option( 'mc_double_optin', true );
410 update_option( 'mc_use_unsub_link', 'off' );
411
412 update_option( 'mc_form_border_width', '1' );
413 update_option( 'mc_form_border_color', 'E0E0E0' );
414 update_option( 'mc_form_background', 'FFFFFF' );
415 update_option( 'mc_form_text_color', '3F3F3f' );
416 }
417
418 /**
419 * Saves the General Form settings on the options page
420 *
421 * @return void
422 **/
423 function mailchimp_sf_save_general_form_settings() {
424
425 /*Enable double optin toggle*/
426 if ( isset( $_POST['mc_double_optin'] ) ) {
427 update_option( 'mc_double_optin', true );
428 $msg = esc_html__( 'Double opt-in turned On!', 'mailchimp' );
429 admin_notice_success( $msg );
430 } elseif ( get_option( 'mc_double_optin' ) !== false ) {
431 update_option( 'mc_double_optin', false );
432 $msg = esc_html__( 'Double opt-in turned Off!', 'mailchimp' );
433 admin_notice_success( $msg );
434 }
435
436 /* NUKE the CSS! */
437 if ( isset( $_POST['mc_nuke_all_styles'] ) ) {
438 update_option( 'mc_nuke_all_styles', true );
439 $msg = esc_html__( 'Mailchimp CSS turned Off!', 'mailchimp' );
440 admin_notice_success( $msg );
441 } elseif ( get_option( 'mc_nuke_all_styles' ) !== false ) {
442 update_option( 'mc_nuke_all_styles', false );
443 $msg = esc_html__( 'Mailchimp CSS turned On!', 'mailchimp' );
444 admin_notice_success( $msg );
445 }
446
447 /* Update existing */
448 if ( isset( $_POST['mc_update_existing'] ) ) {
449 update_option( 'mc_update_existing', true );
450 $msg = esc_html__( 'Update existing subscribers turned On!' );
451 admin_notice_success( $msg );
452 } elseif ( get_option( 'mc_update_existing' ) !== false ) {
453 update_option( 'mc_update_existing', false );
454 $msg = esc_html__( 'Update existing subscribers turned Off!' );
455 admin_notice_success( $msg );
456 }
457
458 if ( isset( $_POST['mc_use_unsub_link'] ) ) {
459 update_option( 'mc_use_unsub_link', 'on' );
460 $msg = esc_html__( 'Unsubscribe link turned On!', 'mailchimp' );
461 admin_notice_success( $msg );
462 } elseif ( get_option( 'mc_use_unsub_link' ) !== 'off' ) {
463 update_option( 'mc_use_unsub_link', 'off' );
464 $msg = esc_html__( 'Unsubscribe link turned Off!', 'mailchimp' );
465 admin_notice_success( $msg );
466 }
467
468 $content = isset( $_POST['mc_header_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_header_content'] ) ) : '';
469 $content = str_replace( "\r\n", '<br/>', $content );
470 update_option( 'mc_header_content', $content );
471
472 $content = isset( $_POST['mc_subheader_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_subheader_content'] ) ) : '';
473 $content = str_replace( "\r\n", '<br/>', $content );
474 update_option( 'mc_subheader_content', $content );
475
476 $submit_text = isset( $_POST['mc_submit_text'] ) ? sanitize_text_field( wp_unslash( $_POST['mc_submit_text'] ) ) : '';
477 $submit_text = str_replace( "\r\n", '', $submit_text );
478 update_option( 'mc_submit_text', $submit_text );
479
480 // Set Custom Style option
481 update_option( 'mc_custom_style', isset( $_POST['mc_custom_style'] ) ? 'on' : 'off' );
482
483 // we told them not to put these things we are replacing in, but let's just make sure they are listening...
484 if ( isset( $_POST['mc_form_border_width'] ) ) {
485 update_option( 'mc_form_border_width', str_replace( 'px', '', absint( $_POST['mc_form_border_width'] ) ) );
486 }
487 if ( isset( $_POST['mc_form_border_color'] ) ) {
488 update_option( 'mc_form_border_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_border_color'] ) ) ) );
489 }
490 if ( isset( $_POST['mc_form_background'] ) ) {
491 update_option( 'mc_form_background', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_background'] ) ) ) );
492 }
493 if ( isset( $_POST['mc_form_text_color'] ) ) {
494 update_option( 'mc_form_text_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_text_color'] ) ) ) );
495 }
496
497 // IF NOT DEV MODE
498 $igs = get_option( 'mc_interest_groups' );
499 if ( is_array( $igs ) ) {
500 foreach ( $igs as $mv_var ) {
501 $opt = 'mc_show_interest_groups_' . $mv_var['id'];
502 if ( isset( $_POST[ $opt ] ) ) {
503 update_option( $opt, 'on' );
504 } else {
505 update_option( $opt, 'off' );
506 }
507 }
508 }
509
510 $mv = get_option( 'mc_merge_vars' );
511 if ( is_array( $mv ) ) {
512 foreach ( $mv as $mv_var ) {
513 $opt = 'mc_mv_' . $mv_var['tag'];
514 if ( isset( $_POST[ $opt ] ) || true === (bool) $mv_var['required'] ) {
515 update_option( $opt, 'on' );
516 } else {
517 update_option( $opt, 'off' );
518 }
519 }
520 }
521
522 $msg = esc_html__( 'Successfully Updated your List Subscribe Form Settings!', 'mailchimp' );
523 admin_notice_success( $msg );
524 }
525
526 /**
527 * Sees if the user changed the list, and updates options accordingly
528 **/
529 function mailchimp_sf_change_list_if_necessary() {
530 if ( ! isset( $_POST['mc_list_id'] ) ) {
531 return;
532 }
533
534 if ( empty( $_POST['mc_list_id'] ) ) {
535 $msg = esc_html__( 'Please choose a valid list', 'mailchimp' );
536 admin_notice_error( $msg );
537 return;
538 }
539
540 // Simple permission check before going through all this
541 if ( ! current_user_can( MCSF_CAP_THRESHOLD ) ) { return; }
542
543 $api = mailchimp_sf_get_api();
544 if ( ! $api ) { return; }
545
546 // we *could* support paging, but few users have that many lists (and shouldn't)
547 $lists = $api->get( 'lists', 100, array( 'fields' => 'lists.id,lists.name,lists.email_type_option' ) );
548
549 if ( ! isset( $lists['lists'] ) || is_wp_error( $lists['lists'] ) ) {
550 return;
551 }
552
553 $lists = $lists['lists'];
554
555 if ( is_array( $lists ) && ! empty( $lists ) ) {
556
557 /**
558 * If our incoming list ID (the one chosen in the select dropdown)
559 * is in our array of lists, the set it to be the active list
560 */
561 foreach ( $lists as $key => $list ) {
562 if ( isset( $_POST['mc_list_id'] ) && $list['id'] === $_POST['mc_list_id'] ) {
563 $list_id = sanitize_text_field( wp_unslash( $_POST['mc_list_id'] ) );
564 $list_name = $list['name'];
565 $list_key = $key;
566 }
567
568 $merge_fields = mailchimp_sf_get_merge_vars( $list['id'], false, false );
569 $interests = mailchimp_sf_get_interest_categories( $list['id'], false, false );
570 if ( ! empty( $merge_fields ) ) {
571 update_option( 'mailchimp_sf_merge_fields_' . $list['id'], $merge_fields );
572 }
573 if ( ! empty( $interests ) ) {
574 update_option( 'mailchimp_sf_interest_groups_' . $list['id'], $interests );
575 }
576 }
577
578 $orig_list = get_option( 'mc_list_id' );
579 if ( '' !== $list_id ) {
580 update_option( 'mc_list_id', $list_id );
581 update_option( 'mc_list_name', $list_name );
582 update_option( 'mc_email_type_option', $lists[ $list_key ]['email_type_option'] );
583
584 // See if the user changed the list
585 $new_list = false;
586 if ( $orig_list !== $list_id ) {
587 // The user changed the list, Reset the Form Defaults
588 mailchimp_sf_set_form_defaults( $list_name );
589
590 $new_list = true;
591 }
592
593 // Grab the merge vars and interest groups
594 $mv = mailchimp_sf_get_merge_vars( $list_id, $new_list );
595 $igs = mailchimp_sf_get_interest_categories( $list_id, $new_list );
596
597 $igs_text = ' ';
598 if ( is_array( $igs ) ) {
599 /* translators: %s: count (number) */
600 $igs_text .= sprintf( esc_html__( 'and %s Sets of Interest Groups', 'mailchimp' ), count( $igs ) );
601 }
602
603 $msg = sprintf(
604 /* translators: %s: count (number) */
605 __( '<b>Success!</b> Loaded and saved the info for %d Merge Variables', 'mailchimp' ) . $igs_text,
606 count( $mv )
607 ) . ' ' .
608 esc_html__( 'from your list' ) . ' "' . $list_name . '"<br/><br/>' .
609 esc_html__( 'Now you should either Turn On the Mailchimp Widget or change your options below, then turn it on.', 'mailchimp' );
610
611 admin_notice_success( $msg );
612 }
613
614 // Update the lists option.
615 update_option( 'mailchimp_sf_lists', $lists );
616 }
617 }
618
619 /**
620 * Get merge vars
621 *
622 * @param string $list_id List ID
623 * @param bool $new_list Whether this is a new list
624 * @param bool $update_option Whether to update the option
625 * @return array
626 */
627 function mailchimp_sf_get_merge_vars( $list_id, $new_list, $update_option = true ) {
628 $api = mailchimp_sf_get_api();
629 $mv = $api->get( 'lists/' . $list_id . '/merge-fields', 80 );
630
631 // if we get an error back from the api, exit this process.
632 if ( is_wp_error( $mv ) ) {
633 return;
634 }
635
636 $mv['merge_fields'] = mailchimp_sf_add_email_field( $mv['merge_fields'] );
637 if ( $update_option ) {
638 update_option( 'mc_merge_vars', $mv['merge_fields'] );
639 }
640
641 foreach ( $mv['merge_fields'] as $mv_var ) {
642 $opt = 'mc_mv_' . $mv_var['tag'];
643 if ( $new_list ) {
644 $public = $mv_var['public'] ?? false;
645 if ( ! $public ) {
646 // This is a hidden field, so we don't want to include it.
647 update_option( $opt, 'off' );
648 } else {
649 // We need to set the option to 'on' so that it shows up in the form.
650 update_option( $opt, 'on' );
651 }
652 }
653 }
654 return $mv['merge_fields'];
655 }
656
657 /**
658 * Add email field
659 *
660 * @param array $merge Merge
661 * @return array
662 */
663 function mailchimp_sf_add_email_field( $merge ) {
664 $email = array(
665 'tag' => 'EMAIL',
666 'name' => esc_html__( 'Email Address', 'mailchimp' ),
667 'type' => 'email',
668 'required' => true,
669 'public' => true,
670 'display_order' => 1,
671 'default_value' => null,
672 );
673 array_unshift( $merge, $email );
674 return $merge;
675 }
676
677 /**
678 * Get interest categories
679 *
680 * @param string $list_id List ID
681 * @param bool $new_list Whether this is a new list
682 * @param bool $update_option Whether to update the option
683 * @return array
684 */
685 function mailchimp_sf_get_interest_categories( $list_id, $new_list, $update_option = true ) {
686 $api = mailchimp_sf_get_api();
687 $igs = $api->get( 'lists/' . $list_id . '/interest-categories', 60 );
688
689 // if we get an error back from the api, exis
690 if ( is_wp_error( $igs ) ) {
691 return;
692 }
693
694 if ( is_array( $igs ) ) {
695 $key = 0;
696 foreach ( $igs['categories'] as $ig ) {
697 $groups = $api->get( 'lists/' . $list_id . '/interest-categories/' . $ig['id'] . '/interests', 60 );
698 $igs['categories'][ $key ]['groups'] = $groups['interests'];
699 $opt = 'mc_show_interest_groups_' . $ig['id'];
700
701 // turn them all on by default
702 if ( $new_list ) {
703 update_option( $opt, 'on' );
704 }
705 ++$key;
706 }
707 }
708
709 if ( $update_option ) {
710 update_option( 'mc_interest_groups', $igs['categories'] );
711 }
712
713 return $igs['categories'];
714 }
715
716 /**
717 * Register the widget.
718 *
719 * @return void
720 */
721 function mailchimp_sf_register_widgets() {
722 if ( mailchimp_sf_get_api() ) {
723 register_widget( 'Mailchimp_SF_Widget' );
724 }
725 }
726 add_action( 'widgets_init', 'mailchimp_sf_register_widgets' );
727
728 /**
729 * Add shortcode
730 *
731 * @return string
732 */
733 function mailchimp_sf_shortcode() {
734 ob_start();
735 mailchimp_sf_signup_form();
736 return ob_get_clean();
737 }
738 add_shortcode( 'mailchimpsf_form', 'mailchimp_sf_shortcode' );
739
740 /**
741 * Cleans up merge fields and interests to make them
742 * API 3.0-friendly.
743 *
744 * @param [type] $merge Merge fields
745 * @param [type] $igs Interest groups
746 * @param string $email_type Email type
747 * @param string $email Email
748 * @param string|false $status Status The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if an error occurred.
749 * @param string $double_optin Whether double opt-in is enabled. "1" for enabled and "" for disabled.
750 * @return stdClass
751 */
752 function mailchimp_sf_subscribe_body( $merge, $igs, $email_type, $email, $status, $double_optin ) {
753 $body = new stdClass();
754 $body->email_address = $email;
755 $body->email_type = $email_type;
756 $body->merge_fields = $merge;
757
758 if ( ! empty( $igs ) ) {
759 $body->interests = $igs;
760 }
761
762 // Early return for already subscribed users
763 if ( 'subscribed' === $status ) {
764 return $body;
765 }
766
767 // Subscribe the email immediately unless double opt-in is enabled
768 // "unsubscribed" and "subscribed" existing emails have been excluded at this stage
769 // "pending" emails should follow double opt-in rules
770 $body->status = $double_optin ? 'pending' : 'subscribed';
771
772 return $body;
773 }
774
775 /**
776 * Check the status of a subscriber in the list.
777 *
778 * @param string $endpoint API endpoint to check the status.
779 * @return string|false The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if the API returned 404 or an error occurred.
780 */
781 function mailchimp_sf_check_status( $endpoint ) {
782 $endpoint .= '?fields=status';
783 $api = mailchimp_sf_get_api();
784 $subscriber = $api->get( $endpoint, null );
785 if ( is_wp_error( $subscriber ) ) {
786 return false;
787 }
788 return $subscriber['status'];
789 }
790
791 /**
792 * Verify key
793 *
794 * @param MailChimp_API $api API instance
795 * @return mixed
796 */
797 function mailchimp_sf_verify_key( $api ) {
798 $user = $api->get( '' );
799 if ( is_wp_error( $user ) ) {
800 return $user;
801 }
802
803 // Might as well set this data if we have it already.
804 $valid_roles = array( 'owner', 'admin', 'manager' );
805 if ( isset( $user['role'] ) && in_array( $user['role'], $valid_roles, true ) ) {
806 update_option( 'mc_api_key', $api->key );
807 update_option( 'mc_user', $user );
808 update_option( 'mc_datacenter', $api->datacenter );
809
810 } else {
811 $msg = esc_html__( 'API Key must belong to "Owner", "Admin", or "Manager."', 'mailchimp' );
812 return new WP_Error( 'mc-invalid-role', $msg );
813 }
814 }
815
816 /**
817 * Update profile URL.
818 *
819 * @param string $email Email
820 * @return string
821 */
822 function mailchimp_sf_update_profile_url( $email ) {
823 $dc = get_option( 'mc_datacenter' );
824 // This is the expected encoding for emails.
825 $eid = base64_encode( $email ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- ignoring because this is the expected data for the endpoint
826 $user = get_option( 'mc_user' );
827 $list_id = get_option( 'mc_list_id' );
828 $url = 'http://' . $dc . '.list-manage.com/subscribe/send-email?u=' . $user['account_id'] . '&id=' . $list_id . '&e=' . $eid;
829 return $url;
830 }
831
832 /**
833 * Delete options
834 *
835 * @param array $options Options
836 * @return void
837 */
838 function mailchimp_sf_delete_options( $options = array() ) {
839 foreach ( $options as $option ) {
840 delete_option( $option );
841 }
842 }
843
844 /**********************
845 * Utility Functions *
846 **********************/
847 /**
848 * Utility function to allow placement of plugin in plugins, mu-plugins, child or parent theme's plugins folders
849 *
850 * This function must be ran _very early_ in the load process, as it sets up important constants for the rest of the plugin
851 */
852 function mailchimp_sf_where_am_i() {
853 $locations = array(
854 'plugins' => array(
855 'dir' => plugin_dir_path( __FILE__ ),
856 'url' => plugins_url(),
857 ),
858 'mu_plugins' => array(
859 'dir' => plugin_dir_path( __FILE__ ),
860 'url' => plugins_url(),
861 ),
862 'template' => array(
863 'dir' => trailingslashit( get_template_directory() ) . 'plugins/',
864 'url' => trailingslashit( get_template_directory_uri() ) . 'plugins/',
865 ),
866 'stylesheet' => array(
867 'dir' => trailingslashit( get_stylesheet_directory() ) . 'plugins/',
868 'url' => trailingslashit( get_stylesheet_directory_uri() ) . 'plugins/',
869 ),
870 );
871
872 // Set defaults
873 $mscf_dirbase = trailingslashit( basename( __DIR__ ) ); // Typically wp-mailchimp/ or mailchimp/
874 $mscf_dir = trailingslashit( plugin_dir_path( __FILE__ ) );
875 $mscf_url = trailingslashit( plugins_url( '', __FILE__ ) );
876
877 // Try our hands at finding the real location
878 foreach ( $locations as $key => $loc ) {
879 $dir = trailingslashit( $loc['dir'] ) . $mscf_dirbase;
880 $url = trailingslashit( $loc['url'] ) . $mscf_dirbase;
881 if ( is_file( $dir . basename( __FILE__ ) ) ) {
882 $mscf_dir = $dir;
883 $mscf_url = $url;
884 break;
885 }
886 }
887
888 // Define our complete filesystem path
889 define( 'MCSF_DIR', $mscf_dir );
890
891 // Define our complete URL to the plugin folder
892 define( 'MCSF_URL', $mscf_url );
893 }
894
895
896 /**
897 * MODIFIED VERSION of wp_verify_nonce from WP Core. Core was not overridden to prevent problems when replacing
898 * something universally.
899 *
900 * Verify that correct nonce was used with time limit.
901 *
902 * The user is given an amount of time to use the token, so therefore, since the
903 * UID and $action remain the same, the independent variable is the time.
904 *
905 * @param string $nonce Nonce that was used in the form to verify
906 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
907 * @return bool Whether the nonce check passed or failed.
908 */
909 function mailchimp_sf_verify_nonce( $nonce, $action = -1 ) {
910 $user = wp_get_current_user();
911 $uid = (int) $user->ID;
912 if ( ! $uid ) {
913 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
914 }
915
916 if ( empty( $nonce ) ) {
917 return false;
918 }
919
920 $token = 'MAILCHIMP';
921 $i = wp_nonce_tick();
922
923 // Nonce generated 0-12 hours ago
924 $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
925 if ( hash_equals( $expected, $nonce ) ) {
926 return 1;
927 }
928
929 // Nonce generated 12-24 hours ago
930 $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
931 if ( hash_equals( $expected, $nonce ) ) {
932 return 2;
933 }
934
935 // Invalid nonce
936 return false;
937 }
938
939
940 /**
941 * MODIFIED VERSION of wp_create_nonce from WP Core. Core was not overridden to prevent problems when replacing
942 * something universally.
943 *
944 * Creates a cryptographic token tied to a specific action, user, and window of time.
945 *
946 * @param string $action Scalar value to add context to the nonce.
947 * @return string The token.
948 */
949 function mailchimp_sf_create_nonce( $action = -1 ) {
950 $user = wp_get_current_user();
951 $uid = (int) $user->ID;
952 if ( ! $uid ) {
953 /** This filter is documented in wp-includes/pluggable.php */
954 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
955 }
956
957 $token = 'MAILCHIMP';
958 $i = wp_nonce_tick();
959
960 return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
961 }
962
963 /**
964 * Get Mailchimp Access Token.
965 *
966 * @since 1.6.0
967 * @return string|bool
968 */
969 function mailchimp_sf_get_access_token() {
970 $access_token = get_option( 'mailchimp_sf_access_token' );
971 if ( empty( $access_token ) ) {
972 return false;
973 }
974
975 $data_encryption = new Mailchimp_Data_Encryption();
976 $access_token = $data_encryption->decrypt( $access_token );
977
978 // If decryption fails, display notice to user to re-authenticate.
979 if ( false === $access_token ) {
980 update_option( 'mailchimp_sf_auth_error', true );
981 }
982
983 return $access_token;
984 }
985
986 /**
987 * Should display Mailchimp Signup form.
988 *
989 * @since 1.6.0
990 * @return bool
991 */
992 function mailchimp_sf_should_display_form() {
993 return mailchimp_sf_get_api() && ! get_option( 'mailchimp_sf_auth_error' ) && get_option( 'mc_list_id' );
994 }
995