composer install, 2: Support URL, e.g., https://wordpress.org/support/plugin/mailchimp/. */ __( 'The composer autoload file is not found or not readable. Please contact support if you\'re a user. Please run %1$s if you\'re a developer in a development environment.', 'mailchimp' ), 'composer install', 'https://wordpress.org/support/plugin/mailchimp/' ) ); ?>

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 = '' . esc_html__( 'Settings', 'mailchimp' ) . ''; 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: px; border-style: ; border-color: #; color: #; background-color: #; } ', $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", '
', $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) */ __( 'Success! Loaded and saved the info for %d Merge Variables', 'mailchimp' ) . $igs_text, count( $mv ) ) . ' ' . esc_html__( 'from your list', 'mailchimp' ) . ' "' . $list_name . '"

' . 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' ) ); }