# mailchimp/trunk/mailchimp.php

Mailchimp List Subscribe Form, version trunk. 1,051 lines.

- Page: https://pluginprobe.com/plugins/mailchimp/trunk/code/mailchimp.php
- Raw: https://pluginprobe.com/plugins/mailchimp/trunk/raw/mailchimp.php
- Modified: 2026-06-03T16:20:50+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/mailchimp/trunk/code/mailchimp.php#L10-L20`.

```php
<?php
/**
 * Plugin Name:       Mailchimp
 * Plugin URI:        https://mailchimp.com/help/connect-or-disconnect-list-subscribe-for-wordpress/
 * Description:       Add a Mailchimp signup form block, widget or shortcode to your WordPress site.
 * Text Domain:       mailchimp
 * Version:           2.1.0
 * Requires at least: 6.6
 * Requires PHP:      7.4
 * PHP tested up to:  8.3
 * Author:            Mailchimp
 * Author URI:        https://mailchimp.com/
 * License:           GPL-2.0-or-later
 * License URI:       https://spdx.org/licenses/GPL-2.0-or-later.html
 *
 * @package Mailchimp
 **/

/**
 * Copyright 2008-2012  Mailchimp.com  (email : api@mailchimp.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

// Check if the autoload file exists
if ( is_readable( __DIR__ . '/vendor/autoload.php' ) ) {
	require_once __DIR__ . '/vendor/autoload.php';
} else {
	add_action(
		'admin_notices',
		function () {
			?>
			<div class="notice notice-error">
				<p>
					<?php
					echo wp_kses_post(
						sprintf(
							/* translators: 1: Command to run, e.g., <code>composer install</code>, 2: Support URL, e.g., https://wordpress.org/support/plugin/mailchimp/. */
							__( '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' ),
							'<code>composer install</code>',
							'https://wordpress.org/support/plugin/mailchimp/'
						)
					);
					?>
				</p>
			</div>
			<?php
		}
	);

	// Exit early.
	return;
}

use function Mailchimp\WordPress\Includes\Admin\{admin_notice_error, admin_notice_success};

// Version constant for easy CSS refreshes
define( 'MCSF_VER', '2.1.0' );

// What's our permission (capability) threshold
define( 'MCSF_CAP_THRESHOLD', 'manage_options' );

// Define our location constants, both MCSF_DIR and MCSF_URL
mailchimp_sf_where_am_i();

// Get our Mailchimp API class in scope
if ( ! class_exists( 'MailChimp_API' ) ) {
	$path = plugin_dir_path( __FILE__ );
	require_once $path . 'lib/mailchimp/mailchimp.php';
}

// Encryption utility class.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-data-encryption.php';

// includes the widget code so it can be easily called either normally or via ajax
require_once 'mailchimp_widget.php';

// includes the backwards compatibility functions
require_once 'mailchimp_compat.php';

// Upgrade routines.
require_once 'mailchimp_upgrade.php';

// Init Admin functions.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-user-sync-backgroud-process.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/admin/class-mailchimp-user-sync.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/admin/class-mailchimp-admin-notices.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-admin.php';
$admin = new Mailchimp_Admin();
$admin->init();

// Init the blocks.
require_once plugin_dir_path( __FILE__ ) . 'includes/blocks/class-mailchimp-list-subscribe-form-blocks.php';
$block = new Mailchimp_List_Subscribe_Form_Blocks();
$block->init();

// Form submission handler class.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-form-submission.php';
$form_submission = new Mailchimp_Form_Submission();
$form_submission->init();

// Shared bucketing helpers used by both analytics chart data providers.
require_once plugin_dir_path( __FILE__ ) . 'includes/trait-mailchimp-analytics-bucketing.php';

// Init Analytics page.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-analytics.php';
$analytics = new Mailchimp_Analytics();
$analytics->init();

// Analytics data class.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-analytics-data.php';
$analytics_data = new Mailchimp_Analytics_Data();
$analytics_data->init();

// Subscriber activity (Mailchimp Activity API) data class.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-subscriber-activity.php';
$subscriber_activity = new Mailchimp_Subscriber_Activity();
$subscriber_activity->init();

// Form performance (local analytics DB) data class.
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-form-performance.php';
$form_performance = new Mailchimp_Form_Performance();
$form_performance->init();

// Deprecated functions.
require_once plugin_dir_path( __FILE__ ) . 'includes/mailchimp-deprecated-functions.php';

/**
 * Do the following plugin setup steps here
 *
 * Resource (JS & CSS) enqueuing
 *
 * @return void
 */
function mailchimp_sf_plugin_init() {

	if ( get_option( 'mc_list_id' ) && get_option( 'mc_merge_field_migrate' ) !== '1' && mailchimp_sf_get_api() !== false ) {
		mailchimp_sf_update_merge_fields();
	}

	if ( mailchimp_sf_should_display_form() ) {
		// Bring in our appropriate JS and CSS resources
		add_action( 'wp_enqueue_scripts', 'mailchimp_sf_load_resources' );
	}
}

add_action( 'init', 'mailchimp_sf_plugin_init' );

/**
 * Add the settings link to the Mailchimp plugin row
 *
 * @param array $links - Links for the plugin
 * @return array - Links
 */
function mailchimp_sf_plugin_action_links( $links ) {
	$settings_page = add_query_arg( array( 'page' => 'mailchimp_sf_options' ), admin_url( 'admin.php' ) );
	$settings_link = '<a href="' . esc_url( $settings_page ) . '">' . esc_html__( 'Settings', 'mailchimp' ) . '</a>';
	array_unshift( $links, $settings_link );
	return $links;
}

add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'mailchimp_sf_plugin_action_links', 10, 1 );

/**
 * Loads the appropriate JS and CSS resources depending on
 * settings and context (admin or not)
 *
 * @return void
 */
function mailchimp_sf_load_resources() {
	// JS
	wp_enqueue_script( 'mailchimp_sf_main_js', MCSF_URL . 'assets/js/mailchimp.js', array( 'jquery', 'jquery-form', 'jquery-ui-datepicker' ), MCSF_VER, true );
	// some javascript to get ajax version submitting to the proper location
	global $wp_scripts;
	$localize_data = array(
		'ajax_url'               => trailingslashit( home_url() ),
		'phone_validation_error' => esc_html__( 'Please enter a valid phone number.', 'mailchimp' ),
	);

	if ( ! is_admin() ) {
		$localize_data['analytics_ajax_url'] = admin_url( 'admin-ajax.php' );
		$localize_data['analytics_nonce']    = wp_create_nonce( 'mailchimp_sf_analytics_nonce' );
	}

	$wp_scripts->localize(
		'mailchimp_sf_main_js',
		'mailchimpSF',
		$localize_data
	);

	// Datepicker theme
	wp_enqueue_style( 'flick', MCSF_URL . 'assets/css/flick/flick.css', array(), MCSF_VER );

	// CSS
	if ( get_option( 'mc_nuke_all_styles' ) !== '1' ) {
		wp_enqueue_style( 'mailchimp_sf_main_css', MCSF_URL . 'assets/css/frontend.css', array(), MCSF_VER );

		// Backwards compatibility. TODO: Remove this in a future version.
		if ( get_option( 'mc_custom_style' ) === 'on' ) {
			$custom_css = mailchimp_sf_custom_style_css();
			wp_add_inline_style( 'mailchimp_sf_main_css', $custom_css );
		}
	}
}

/**
 * Custom styles CSS
 *
 * @return string
 */
function mailchimp_sf_custom_style_css() {
	ob_start();
	?>
	.mc_signup_form {
		padding:5px;
		border-width: <?php echo absint( get_option( 'mc_form_border_width' ) ); ?>px;
		border-style: <?php echo ( get_option( 'mc_form_border_width' ) === 0 ) ? 'none' : 'solid'; ?>;
		border-color: #<?php echo esc_attr( get_option( 'mc_form_border_color' ) ); ?>;
		color: #<?php echo esc_attr( get_option( 'mc_form_text_color' ) ); ?>;
		background-color: #<?php echo esc_attr( get_option( 'mc_form_background' ) ); ?>;
	}
	<?php
	return ob_get_clean();
}

/**
 * Request handler
 *
 * @return void
 */
function mailchimp_sf_request_handler() {
	if ( isset( $_POST['mcsf_action'] ) ) {
		switch ( $_POST['mcsf_action'] ) {
			case 'logout':
				// Check capability & Verify nonce
				if (
					! current_user_can( MCSF_CAP_THRESHOLD ) ||
					! isset( $_POST['_mcsf_nonce_action'] ) ||
					! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'mc_logout' )
				) {
					wp_die( 'Cheatin&rsquo; huh?' );
				}

				// erase auth information
				$options = array( 'mc_api_key', 'mailchimp_sf_access_token', 'mc_datacenter', 'mailchimp_sf_auth_error', 'mailchimp_sf_waiting_for_login' );
				mailchimp_sf_delete_options( $options );
				break;
			case 'change_form_settings':
				if (
					! current_user_can( MCSF_CAP_THRESHOLD ) ||
					! isset( $_POST['_mcsf_nonce_action'] ) ||
					! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'update_general_form_settings' )
				) {
					wp_die( 'Cheatin&rsquo; huh?' );
				}

				// Update the form settings
				mailchimp_sf_save_general_form_settings();
				break;
		}
	}
}
add_action( 'init', 'mailchimp_sf_request_handler' );

/**
 * Update merge fields
 *
 * @return void
 */
function mailchimp_sf_update_merge_fields() {
	mailchimp_sf_get_merge_vars( get_option( 'mc_list_id' ), true );
	mailchimp_sf_get_interest_categories( get_option( 'mc_list_id' ), true );
	update_option( 'mc_merge_field_migrate', true );
}

/**
 * Get auth key
 *
 * @param mixed $salt Salt
 * @return string
 */
function mailchimp_sf_auth_nonce_key( $salt = null ) {
	if ( is_null( $salt ) ) {
		$salt = mailchimp_sf_auth_nonce_salt();
	}
	return 'social_authentication' . md5( AUTH_KEY . $salt );
}

/**
 * Return auth nonce salt
 *
 * @return string
 */
function mailchimp_sf_auth_nonce_salt() {
	return md5( microtime() . isset( $_SERVER['SERVER_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_ADDR'] ) ) : '' );
}

/**
 * Creates new Mailchimp API v3 object
 *
 * @return MailChimp_API | false
 */
function mailchimp_sf_get_api() {
	// Check for the access token first.
	$access_token = mailchimp_sf_get_access_token();
	$data_center  = get_option( 'mc_datacenter' );
	if ( ! empty( $access_token ) && ! empty( $data_center ) ) {
		return new MailChimp_API( $access_token, $data_center );
	}

	// Check for the API key if the access token is not available.
	$key = get_option( 'mc_api_key' );
	if ( $key ) {
		return new MailChimp_API( $key );
	}

	return false;
}

/**
 * Checks to see if we're storing a password, if so, we need
 * to upgrade to the API key
 *
 * @return bool
 **/
function mailchimp_sf_needs_upgrade() {
	$igs = get_option( 'mc_interest_groups' );

	if ( false !== $igs // we have an option
		&& (
			empty( $igs ) || // it can be an empty array (no interest groups)
			( is_array( $igs ) && isset( $igs[0]['id'] ) ) // OR it should be a populated array that's well-formed
		)
	) {
		return false; // no need to upgrade
	} else {
		return true; // yeah, let's do it
	}
}

/**
 * Deletes all Mailchimp options
 *
 * TODO: The options names should be moved to a config file
 * or to a class dedicated to options
 **/
function mailchimp_sf_delete_setup() {
	$options = array(
		'mc_user_id',
		'mc_use_unsub_link',
		'mc_list_id',
		'mc_list_name',
		'mc_interest_groups',
		'mc_merge_vars',
	);

	$igs = get_option( 'mc_interest_groups' );
	if ( is_array( $igs ) ) {
		foreach ( $igs as $ig ) {
			$opt       = 'mc_show_interest_groups_' . $ig['id'];
			$options[] = $opt;
		}
	}

	$mv = get_option( 'mc_merge_vars' );
	if ( is_array( $mv ) ) {
		foreach ( $mv as $mv_var ) {
			$opt       = 'mc_mv_' . $mv_var['tag'];
			$options[] = $opt;
		}
	}

	mailchimp_sf_delete_options( $options );
}

/**
 * Gets or sets a frontend message based on parameter passed to it
 *
 * Used to convey error messages to the user outside of the WP Admin
 *
 * On the plugin settings page, WP admin notices are used exclusively
 * instead of the frontend message.
 *
 * @param mixed $msg Message
 * @return string/bool depending on get/set
 */
function mailchimp_sf_frontend_msg( $msg = null ) {
	global $mcsf_msgs;

	// Make sure we're formed properly
	if ( ! is_array( $mcsf_msgs ) ) {
		$mcsf_msgs = array();
	}

	// See if we're getting
	if ( is_null( $msg ) ) {
		return implode( '', $mcsf_msgs );
	}

	// Must be setting
	$mcsf_msgs[] = $msg;
	return true;
}

/**
 * Gets or sets a frontend message based on parameter passed to it
 *
 * TODO: Deprecate this function in favor of mailchimp_sf_frontend_msg()
 *
 * @param mixed $msg Message
 * @return string/bool depending on get/set
 */
function mailchimp_sf_global_msg( $msg = null ) {
	return mailchimp_sf_frontend_msg( $msg );
}

/**
 * Sets the default options for the option form
 *
 * @param string $list_name The Mailchimp list name.
 * @return void
 */
function mailchimp_sf_set_form_defaults( $list_name = '' ) {
	update_option( 'mc_header_content', esc_html__( 'Sign up for', 'mailchimp' ) . ' ' . $list_name );
	update_option( 'mc_submit_text', esc_html__( 'Subscribe', 'mailchimp' ) );

	update_option( 'mc_custom_style', 'off' );
	update_option( 'mc_double_optin', true );
	update_option( 'mc_use_unsub_link', 'off' );

	update_option( 'mc_form_border_width', '1' );
	update_option( 'mc_form_border_color', 'E0E0E0' );
	update_option( 'mc_form_background', 'FFFFFF' );
	update_option( 'mc_form_text_color', '3F3F3f' );
}

/**
 * Saves the General Form settings on the options page
 *
 * @return void
 **/
function mailchimp_sf_save_general_form_settings() {
	// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce check is already done in the mailchimp_sf_request_handler() function.
	/*Enable double optin toggle*/
	if ( isset( $_POST['mc_double_optin'] ) ) {
		update_option( 'mc_double_optin', true );
		$msg = esc_html__( 'Double opt-in turned On!', 'mailchimp' );
		admin_notice_success( $msg );
	} elseif ( get_option( 'mc_double_optin' ) !== false ) {
		update_option( 'mc_double_optin', false );
		$msg = esc_html__( 'Double opt-in turned Off!', 'mailchimp' );
		admin_notice_success( $msg );
	}

	/* NUKE the CSS! */
	if ( isset( $_POST['mc_nuke_all_styles'] ) ) {
		update_option( 'mc_nuke_all_styles', true );
		$msg = esc_html__( 'Mailchimp CSS turned Off!', 'mailchimp' );
		admin_notice_success( $msg );
	} elseif ( get_option( 'mc_nuke_all_styles' ) !== false ) {
		update_option( 'mc_nuke_all_styles', false );
		$msg = esc_html__( 'Mailchimp CSS turned On!', 'mailchimp' );
		admin_notice_success( $msg );
	}

	/* Update existing */
	if ( isset( $_POST['mc_update_existing'] ) ) {
		update_option( 'mc_update_existing', true );
		$msg = esc_html__( 'Update existing subscribers turned On!', 'mailchimp' );
		admin_notice_success( $msg );
	} elseif ( get_option( 'mc_update_existing' ) !== false ) {
		update_option( 'mc_update_existing', false );
		$msg = esc_html__( 'Update existing subscribers turned Off!', 'mailchimp' );
		admin_notice_success( $msg );
	}

	if ( isset( $_POST['mc_use_unsub_link'] ) ) {
		update_option( 'mc_use_unsub_link', 'on' );
		$msg = esc_html__( 'Unsubscribe link turned On!', 'mailchimp' );
		admin_notice_success( $msg );
	} elseif ( get_option( 'mc_use_unsub_link' ) !== 'off' ) {
		update_option( 'mc_use_unsub_link', 'off' );
		$msg = esc_html__( 'Unsubscribe link turned Off!', 'mailchimp' );
		admin_notice_success( $msg );
	}

	$content = isset( $_POST['mc_header_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_header_content'] ) ) : '';
	$content = str_replace( "\r\n", '<br/>', $content );
	update_option( 'mc_header_content', $content );

	$content = isset( $_POST['mc_subheader_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_subheader_content'] ) ) : '';
	$content = str_replace( "\r\n", '<br/>', $content );
	update_option( 'mc_subheader_content', $content );

	$submit_text = isset( $_POST['mc_submit_text'] ) ? sanitize_text_field( wp_unslash( $_POST['mc_submit_text'] ) ) : '';
	$submit_text = str_replace( "\r\n", '', $submit_text );
	update_option( 'mc_submit_text', $submit_text );

	// Set Custom Style option
	update_option( 'mc_custom_style', isset( $_POST['mc_custom_style'] ) ? 'on' : 'off' );

	// we told them not to put these things we are replacing in, but let's just make sure they are listening...
	if ( isset( $_POST['mc_form_border_width'] ) ) {
		update_option( 'mc_form_border_width', str_replace( 'px', '', absint( $_POST['mc_form_border_width'] ) ) );
	}
	if ( isset( $_POST['mc_form_border_color'] ) ) {
		update_option( 'mc_form_border_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_border_color'] ) ) ) );
	}
	if ( isset( $_POST['mc_form_background'] ) ) {
		update_option( 'mc_form_background', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_background'] ) ) ) );
	}
	if ( isset( $_POST['mc_form_text_color'] ) ) {
		update_option( 'mc_form_text_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_text_color'] ) ) ) );
	}

	// IF NOT DEV MODE
	$igs = get_option( 'mc_interest_groups' );
	if ( is_array( $igs ) ) {
		foreach ( $igs as $mv_var ) {
			$opt = 'mc_show_interest_groups_' . $mv_var['id'];
			if ( isset( $_POST[ $opt ] ) ) {
				update_option( $opt, 'on' );
			} else {
				update_option( $opt, 'off' );
			}
		}
	}

	$mv = get_option( 'mc_merge_vars' );
	if ( is_array( $mv ) ) {
		foreach ( $mv as $mv_var ) {
			$opt = 'mc_mv_' . $mv_var['tag'];
			if ( isset( $_POST[ $opt ] ) || true === (bool) $mv_var['required'] ) {
				update_option( $opt, 'on' );
			} else {
				update_option( $opt, 'off' );
			}
		}
	}

	$msg = esc_html__( 'Successfully Updated your List Subscribe Form Settings!', 'mailchimp' );
	admin_notice_success( $msg );
	// phpcs:enable WordPress.Security.NonceVerification.Missing
}

/**
 * Sees if the user changed the list, and updates options accordingly
 **/
function mailchimp_sf_change_list_if_necessary() {
	if ( ! isset( $_POST['mc_list_id'] ) ) {
		return;
	}

	if (
		! current_user_can( MCSF_CAP_THRESHOLD ) ||
		! isset( $_POST['update_mc_list_id_nonce'] ) ||
		! wp_verify_nonce( sanitize_key( $_POST['update_mc_list_id_nonce'] ), 'update_mc_list_id_action' )
	) {
		wp_die( 'Security check failed.' );
	}

	if ( empty( $_POST['mc_list_id'] ) ) {
		$msg = esc_html__( 'Please choose a valid list', 'mailchimp' );
		admin_notice_error( $msg );
		return;
	}

	$lists = mailchimp_sf_get_lists();
	if ( is_wp_error( $lists ) || ! isset( $lists['lists'] ) ) {
		return;
	}

	$lists = $lists['lists'];

	if ( is_array( $lists ) && ! empty( $lists ) ) {

		/**
		 * If our incoming list ID (the one chosen in the select dropdown)
		 * is in our array of lists, the set it to be the active list
		 */
		foreach ( $lists as $key => $list ) {
			if ( isset( $_POST['mc_list_id'] ) && $list['id'] === $_POST['mc_list_id'] ) {
				$list_id   = sanitize_text_field( wp_unslash( $_POST['mc_list_id'] ) );
				$list_name = $list['name'];
				$list_key  = $key;
			}

			$merge_fields = mailchimp_sf_get_merge_vars( $list['id'], false, false );
			$interests    = mailchimp_sf_get_interest_categories( $list['id'], false, false );
			if ( ! empty( $merge_fields ) ) {
				update_option( 'mailchimp_sf_merge_fields_' . $list['id'], $merge_fields );
			}
			if ( ! empty( $interests ) ) {
				update_option( 'mailchimp_sf_interest_groups_' . $list['id'], $interests );
			}
		}

		$orig_list = get_option( 'mc_list_id' );
		if ( '' !== $list_id ) {
			update_option( 'mc_list_id', $list_id );
			update_option( 'mc_list_name', $list_name );
			update_option( 'mc_email_type_option', $lists[ $list_key ]['email_type_option'] );

			// See if the user changed the list
			$new_list = false;
			if ( $orig_list !== $list_id ) {
				// The user changed the list, Reset the Form Defaults
				mailchimp_sf_set_form_defaults( $list_name );

				$new_list = true;
			}

			// Grab the merge vars and interest groups
			$mv  = mailchimp_sf_get_merge_vars( $list_id, $new_list );
			$igs = mailchimp_sf_get_interest_categories( $list_id, $new_list );

			$igs_text = ' ';
			if ( is_array( $igs ) ) {
				/* translators: %s: count (number) */
				$igs_text .= sprintf( esc_html__( 'and %s Sets of Interest Groups', 'mailchimp' ), count( $igs ) );
			}

			$msg = sprintf(
				/* translators: %s: count (number) */
				__( '<b>Success!</b> Loaded and saved the info for %d Merge Variables', 'mailchimp' ) . $igs_text,
				count( $mv )
			) . ' ' .
			esc_html__( 'from your list', 'mailchimp' ) . ' "' . $list_name . '"<br/><br/>' .
			esc_html__( 'Now you should either Turn On the Mailchimp Widget or change your options below, then turn it on.', 'mailchimp' );

			admin_notice_success( $msg );
		}

		// Update the lists option.
		update_option( 'mailchimp_sf_lists', $lists );
	}
}

/**
 * Get merge vars
 *
 * @param string $list_id List ID
 * @param bool   $new_list Whether this is a new list
 * @param bool   $update_option Whether to update the option
 * @return array
 */
function mailchimp_sf_get_merge_vars( $list_id, $new_list, $update_option = true ) {
	$api = mailchimp_sf_get_api();
	$mv  = $api->get( 'lists/' . $list_id . '/merge-fields', 80 );

	// if we get an error back from the api, exit this process.
	if ( is_wp_error( $mv ) ) {
		return;
	}

	$mv['merge_fields'] = mailchimp_sf_add_email_field( $mv['merge_fields'] );
	if ( $update_option ) {
		update_option( 'mc_merge_vars', $mv['merge_fields'] );
	}

	foreach ( $mv['merge_fields'] as $mv_var ) {
		$opt = 'mc_mv_' . $mv_var['tag'];
		if ( $new_list ) {
			$public = $mv_var['public'] ?? false;
			if ( ! $public ) {
				// This is a hidden field, so we don't want to include it.
				update_option( $opt, 'off' );
			} else {
				// We need to set the option to 'on' so that it shows up in the form.
				update_option( $opt, 'on' );
			}
		}
	}
	return $mv['merge_fields'];
}

/**
 * Add email field
 *
 * @param array $merge Merge
 * @return array
 */
function mailchimp_sf_add_email_field( $merge ) {
	$email = array(
		'tag'           => 'EMAIL',
		'name'          => esc_html__( 'Email Address', 'mailchimp' ),
		'type'          => 'email',
		'required'      => true,
		'public'        => true,
		'display_order' => 1,
		'default_value' => null,
	);
	array_unshift( $merge, $email );
	return $merge;
}

/**
 * Get interest categories
 *
 * @param string $list_id List ID
 * @param bool   $new_list Whether this is a new list
 * @param bool   $update_option Whether to update the option
 * @return array
 */
function mailchimp_sf_get_interest_categories( $list_id, $new_list, $update_option = true ) {
	$api = mailchimp_sf_get_api();
	$igs = $api->get( 'lists/' . $list_id . '/interest-categories', 60 );

	// if we get an error back from the api, exis
	if ( is_wp_error( $igs ) ) {
		return;
	}

	if ( is_array( $igs ) ) {
		$key = 0;
		foreach ( $igs['categories'] as $ig ) {
			$groups                              = $api->get( 'lists/' . $list_id . '/interest-categories/' . $ig['id'] . '/interests', 60 );
			$igs['categories'][ $key ]['groups'] = $groups['interests'];
			$opt                                 = 'mc_show_interest_groups_' . $ig['id'];

			// turn them all on by default
			if ( $new_list ) {
				update_option( $opt, 'on' );
			}
			++$key;
		}
	}

	if ( $update_option ) {
		update_option( 'mc_interest_groups', $igs['categories'] );
	}

	return $igs['categories'];
}

/**
 * Register the widget.
 *
 * @return void
 */
function mailchimp_sf_register_widgets() {
	if ( mailchimp_sf_get_api() ) {
		register_widget( 'Mailchimp_SF_Widget' );
	}
}
add_action( 'widgets_init', 'mailchimp_sf_register_widgets' );

/**
 * Add shortcode
 *
 * @return string
 */
function mailchimp_sf_shortcode() {
	ob_start();
	mailchimp_sf_signup_form();
	return ob_get_clean();
}
add_shortcode( 'mailchimpsf_form', 'mailchimp_sf_shortcode' );

/**
 * Cleans up merge fields and interests to make them
 * API 3.0-friendly.
 *
 * @param [type]       $merge Merge fields
 * @param [type]       $igs Interest groups
 * @param string       $email_type Email type
 * @param string       $email Email
 * @param string|false $status Status The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if an error occurred.
 * @param string       $double_optin Whether double opt-in is enabled. "1" for enabled and "" for disabled.
 * @return stdClass
 */
function mailchimp_sf_subscribe_body( $merge, $igs, $email_type, $email, $status, $double_optin ) {
	$body                = new stdClass();
	$body->email_address = $email;
	$body->email_type    = $email_type;
	$body->merge_fields  = $merge;

	if ( ! empty( $igs ) ) {
		$body->interests = $igs;
	}

	// Early return for already subscribed users
	if ( 'subscribed' === $status ) {
		return $body;
	}

	// Subscribe the email immediately unless double opt-in is enabled
	// "unsubscribed" and "subscribed" existing emails have been excluded at this stage
	// "pending" emails should follow double opt-in rules
	$body->status = $double_optin ? 'pending' : 'subscribed';

	return $body;
}

/**
 * Check the status of a subscriber in the list.
 *
 * @param string $endpoint API endpoint to check the status.
 * @return string|false The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if the API returned 404 or an error occurred.
 */
function mailchimp_sf_check_status( $endpoint ) {
	$endpoint  .= '?fields=status';
	$api        = mailchimp_sf_get_api();
	$subscriber = $api->get( $endpoint, null );
	if ( is_wp_error( $subscriber ) ) {
		return false;
	}
	return $subscriber['status'];
}

/**
 * Verify key
 *
 * @param MailChimp_API $api API instance
 * @return mixed
 */
function mailchimp_sf_verify_key( $api ) {
	$user = $api->get( '' );
	if ( is_wp_error( $user ) ) {
		return $user;
	}

	// Might as well set this data if we have it already.
	$valid_roles = array( 'owner', 'admin', 'manager' );
	if ( isset( $user['role'] ) && in_array( $user['role'], $valid_roles, true ) ) {
		update_option( 'mc_api_key', $api->key );
		update_option( 'mc_user', $user );
		update_option( 'mc_datacenter', $api->datacenter );

	} else {
		$msg = esc_html__( 'API Key must belong to "Owner", "Admin", or "Manager."', 'mailchimp' );
		return new WP_Error( 'mc-invalid-role', $msg );
	}
}

/**
 * Update profile URL.
 *
 * @param string $email Email
 * @return string
 */
function mailchimp_sf_update_profile_url( $email ) {
	$dc = get_option( 'mc_datacenter' );
	// This is the expected encoding for emails.
	$eid     = base64_encode( $email ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- ignoring because this is the expected data for the endpoint
	$user    = get_option( 'mc_user' );
	$list_id = get_option( 'mc_list_id' );
	$url     = 'http://' . $dc . '.list-manage.com/subscribe/send-email?u=' . $user['account_id'] . '&id=' . $list_id . '&e=' . $eid;
	return $url;
}

/**
 * Delete options
 *
 * @param array $options Options
 * @return void
 */
function mailchimp_sf_delete_options( $options = array() ) {
	foreach ( $options as $option ) {
		delete_option( $option );
	}
}

/**********************
 * Utility Functions *
 **********************/
/**
 * Utility function to allow placement of plugin in plugins, mu-plugins, child or parent theme's plugins folders
 *
 * This function must be ran _very early_ in the load process, as it sets up important constants for the rest of the plugin
 */
function mailchimp_sf_where_am_i() {
	$locations = array(
		'plugins'    => array(
			'dir' => plugin_dir_path( __FILE__ ),
			'url' => plugins_url(),
		),
		'mu_plugins' => array(
			'dir' => plugin_dir_path( __FILE__ ),
			'url' => plugins_url(),
		),
		'template'   => array(
			'dir' => trailingslashit( get_template_directory() ) . 'plugins/',
			'url' => trailingslashit( get_template_directory_uri() ) . 'plugins/',
		),
		'stylesheet' => array(
			'dir' => trailingslashit( get_stylesheet_directory() ) . 'plugins/',
			'url' => trailingslashit( get_stylesheet_directory_uri() ) . 'plugins/',
		),
	);

	// Set defaults
	$mscf_dirbase = trailingslashit( basename( __DIR__ ) );  // Typically wp-mailchimp/ or mailchimp/
	$mscf_dir     = trailingslashit( plugin_dir_path( __FILE__ ) );
	$mscf_url     = trailingslashit( plugins_url( '', __FILE__ ) );

	// Try our hands at finding the real location
	foreach ( $locations as $key => $loc ) {
		$dir = trailingslashit( $loc['dir'] ) . $mscf_dirbase;
		$url = trailingslashit( $loc['url'] ) . $mscf_dirbase;
		if ( is_file( $dir . basename( __FILE__ ) ) ) {
			$mscf_dir = $dir;
			$mscf_url = $url;
			break;
		}
	}

	// Define our complete filesystem path
	define( 'MCSF_DIR', $mscf_dir );

	// Define our complete URL to the plugin folder
	define( 'MCSF_URL', $mscf_url );
}


/**
 * MODIFIED VERSION of wp_verify_nonce from WP Core. Core was not overridden to prevent problems when replacing
 * something universally.
 *
 * Verify that correct nonce was used with time limit.
 *
 * The user is given an amount of time to use the token, so therefore, since the
 * UID and $action remain the same, the independent variable is the time.
 *
 * @param string     $nonce Nonce that was used in the form to verify
 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
 * @return bool Whether the nonce check passed or failed.
 */
function mailchimp_sf_verify_nonce( $nonce, $action = -1 ) {
	$user = wp_get_current_user();
	$uid  = (int) $user->ID;
	if ( ! $uid ) {
		$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
	}

	if ( empty( $nonce ) ) {
		return false;
	}

	$token = 'MAILCHIMP';
	$i     = wp_nonce_tick();

	// Nonce generated 0-12 hours ago
	$expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
	if ( hash_equals( $expected, $nonce ) ) {
		return 1;
	}

	// Nonce generated 12-24 hours ago
	$expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
	if ( hash_equals( $expected, $nonce ) ) {
		return 2;
	}

	// Invalid nonce
	return false;
}


/**
 * MODIFIED VERSION of wp_create_nonce from WP Core. Core was not overridden to prevent problems when replacing
 * something universally.
 *
 * Creates a cryptographic token tied to a specific action, user, and window of time.
 *
 * @param string $action Scalar value to add context to the nonce.
 * @return string The token.
 */
function mailchimp_sf_create_nonce( $action = -1 ) {
	$user = wp_get_current_user();
	$uid  = (int) $user->ID;
	if ( ! $uid ) {
		/** This filter is documented in wp-includes/pluggable.php */
		$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
	}

	$token = 'MAILCHIMP';
	$i     = wp_nonce_tick();

	return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
}

/**
 * Get Mailchimp Access Token.
 *
 * @since 1.6.0
 * @return string|bool
 */
function mailchimp_sf_get_access_token() {
	$access_token = get_option( 'mailchimp_sf_access_token' );
	if ( empty( $access_token ) ) {
		return false;
	}

	$data_encryption = new Mailchimp_Data_Encryption();
	$access_token    = $data_encryption->decrypt( $access_token );

	// If decryption fails, display notice to user to re-authenticate.
	if ( false === $access_token ) {
		update_option( 'mailchimp_sf_auth_error', true );
	}

	return $access_token;
}

/**
 * Should display Mailchimp Signup form.
 *
 * @since 1.6.0
 * @return bool
 */
function mailchimp_sf_should_display_form() {
	return mailchimp_sf_get_api() && ! get_option( 'mailchimp_sf_auth_error' ) && get_option( 'mc_list_id' );
}

/**
 * Get Mailchimp Lists.
 *
 * @since 2.1.0
 * @return array|WP_Error|false List of Mailchimp lists, or an error/false from the API request.
 */
function mailchimp_sf_get_lists() {
	/**
	 * Filter the limit of lists to fetch.
	 *
	 * This value is sanitized to a positive integer and clamped before the API request.
	 * Defaults to 100. 1000 is the maximum allowed by the API. 1 is the minimum allowed.
	 */
	$limit = apply_filters( 'mailchimp_sf_list_limit', 100 ); // Default to 100.
	$limit = max( 1, min( 1000, absint( $limit ) ) );

	$api = mailchimp_sf_get_api();
	if ( ! $api ) {
		return array();
	}

	return $api->get( 'lists', $limit, array( 'fields' => 'lists.id,lists.name,lists.email_type_option' ) );
}

```
