PluginProbe
Mailchimp List Subscribe Form / 1.7.1
Mailchimp List Subscribe Form v1.7.1
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.7.1, at mailchimp.php

1,429 lines 46.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: 1.7.1
8 * Requires at least: 6.3
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.7.1' );
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-admin.php';
98 $admin = new Mailchimp_Admin();
99 $admin->init();
100
101 // Init the blocks.
102 require_once plugin_dir_path( __FILE__ ) . 'includes/blocks/class-mailchimp-list-subscribe-form-blocks.php';
103 $block = new Mailchimp_List_Subscribe_Form_Blocks();
104 $block->init();
105
106 // Block form submission handler class.
107 require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-block-form-submission.php';
108
109 /**
110 * Do the following plugin setup steps here
111 *
112 * Resource (JS & CSS) enqueuing
113 *
114 * @return void
115 */
116 function mailchimp_sf_plugin_init() {
117
118 if ( get_option( 'mc_list_id' ) && get_option( 'mc_merge_field_migrate' ) !== '1' && mailchimp_sf_get_api() !== false ) {
119 mailchimp_sf_update_merge_fields();
120 }
121
122 // Bring in our appropriate JS and CSS resources
123 mailchimp_sf_load_resources();
124 }
125
126 add_action( 'init', 'mailchimp_sf_plugin_init' );
127
128 /**
129 * Add the settings link to the Mailchimp plugin row
130 *
131 * @param array $links - Links for the plugin
132 * @return array - Links
133 */
134 function mailchimp_sf_plugin_action_links( $links ) {
135 $settings_page = add_query_arg( array( 'page' => 'mailchimp_sf_options' ), admin_url( 'admin.php' ) );
136 $settings_link = '<a href="' . esc_url( $settings_page ) . '">' . esc_html__( 'Settings', 'mailchimp' ) . '</a>';
137 array_unshift( $links, $settings_link );
138 return $links;
139 }
140
141 add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'mailchimp_sf_plugin_action_links', 10, 1 );
142
143 /**
144 * Loads the appropriate JS and CSS resources depending on
145 * settings and context (admin or not)
146 *
147 * @return void
148 */
149 function mailchimp_sf_load_resources() {
150 // JS
151 if ( ! is_admin() ) {
152 wp_enqueue_script( 'mailchimp_sf_main_js', MCSF_URL . 'assets/js/mailchimp.js', array( 'jquery', 'jquery-form' ), MCSF_VER, true );
153 // some javascript to get ajax version submitting to the proper location
154 global $wp_scripts;
155 $wp_scripts->localize(
156 'mailchimp_sf_main_js',
157 'mailchimpSF',
158 array(
159 'ajax_url' => trailingslashit( home_url() ),
160 )
161 );
162
163 // Datepicker theme
164 wp_enqueue_style( 'flick', MCSF_URL . 'assets/css/flick/flick.css', array(), MCSF_VER );
165
166 // Datepicker JS
167 wp_enqueue_script( 'jquery-ui-datepicker' );
168 }
169
170 if ( get_option( 'mc_nuke_all_styles' ) !== '1' ) {
171 wp_enqueue_style( 'mailchimp_sf_main_css', home_url( '?mcsf_action=main_css&ver=' . MCSF_VER, 'relative' ), array(), MCSF_VER );
172 global $wp_styles;
173 $wp_styles->add_data( 'mailchimp_sf_ie_css', 'conditional', 'IE' );
174 }
175 }
176
177 /**
178 * Loads jQuery Datepicker for the date-pick class
179 **/
180 function mc_datepicker_load() {
181 require_once MCSF_DIR . '/views/datepicker.php';
182 }
183
184 if ( ! is_admin() ) {
185 add_action( 'wp_head', 'mc_datepicker_load' );
186 }
187
188 /**
189 * Handles requests that as light-weight a load as possible.
190 * typically, JS or CSS
191 *
192 * @return void
193 */
194 function mailchimp_sf_early_request_handler() {
195 if ( isset( $_GET['mcsf_action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- ignoring because this is only adding CSS
196 switch ( $_GET['mcsf_action'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- ignoring because this is only adding CSS
197 case 'main_css':
198 header( 'Content-type: text/css' );
199 mailchimp_sf_main_css();
200 exit;
201 }
202 }
203 }
204
205 add_action( 'init', 'mailchimp_sf_early_request_handler', 0 );
206
207 /**
208 * Outputs the front-end CSS. This checks several options, so it
209 * was best to put it in a Request-handled script, as opposed to
210 * a static file.
211 */
212 function mailchimp_sf_main_css() {
213 require_once MCSF_DIR . '/views/css/frontend.php';
214 }
215
216
217 /**
218 * Add our settings page to the admin menu
219 *
220 * @return void
221 */
222 function mailchimp_sf_add_pages() {
223 // Add settings page for users who can edit plugins
224 add_menu_page(
225 esc_html__( 'Mailchimp Setup', 'mailchimp' ),
226 esc_html__( 'Mailchimp', 'mailchimp' ),
227 MCSF_CAP_THRESHOLD,
228 'mailchimp_sf_options',
229 'mailchimp_sf_setup_page',
230 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1Mi4wMyA1NSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5Bc3NldCAxPC90aXRsZT48ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj48ZyBpZD0iTGF5ZXJfMS0yIiBkYXRhLW5hbWU9IkxheWVyIDEiPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTExLjY0LDI4LjU0YTQuNzUsNC43NSwwLDAsMC0xLjE3LjA4Yy0yLjc5LjU2LTQuMzYsMi45NC00LjA1LDZhNi4yNCw2LjI0LDAsMCwwLDUuNzIsNS4yMSw0LjE3LDQuMTcsMCwwLDAsLjgtLjA2YzIuODMtLjQ4LDMuNTctMy41NSwzLjEtNi41N0MxNS41MSwyOS44MywxMy4yMSwyOC42MywxMS42NCwyOC41NFptMi43Nyw4LjA3YTEuMTcsMS4xNywwLDAsMS0xLjEuNTUsMS41MywxLjUzLDAsMCwxLTEuMzctMS41OEE0LDQsMCwwLDEsMTIuMjMsMzRhMS40NCwxLjQ0LDAsMCwwLS41NS0xLjc0LDEuNDgsMS40OCwwLDAsMC0xLjEyLS4yMSwxLjQ0LDEuNDQsMCwwLDAtLjkyLjY0LDMuMzksMy4zOSwwLDAsMC0uMzQuNzlsMCwuMTFjLS4xMy4zNC0uMzMuNDUtLjQ3LjQzcy0uMTYtLjA1LS4yMS0uMjFhMywzLDAsMCwxLC43OC0yLjU1LDIuNDYsMi40NiwwLDAsMSwyLjExLS43NiwyLjUsMi41LDAsMCwxLDEuOTEsMS4zOSwzLjE5LDMuMTksMCwwLDEtLjIzLDIuODJsLS4wOS4yQTEuMTYsMS4xNiwwLDAsMCwxMywzNmEuNzQuNzQsMCwwLDAsLjYzLjMyLDEuMzgsMS4zOCwwLDAsMCwuMzQsMGMuMTUsMCwuMy0uMDcuMzksMEEuMjQuMjQsMCwwLDEsMTQuNDEsMzYuNjFaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNTEsMzMuODhhMy44NCwzLjg0LDAsMCwwLTEuMTUtMWwtLjExLS4zNy0uMTQtLjQyYTUuNTcsNS41NywwLDAsMCwuNS0zLjMyLDUuNDMsNS40MywwLDAsMC0xLjU0LTMsMTAuMDksMTAuMDksMCwwLDAtNC4yNC0yLjI2YzAtLjY3LDAtMS40My0uMDYtMS45YTEyLjgzLDEyLjgzLDAsMCwwLS40OS0zLjI1LDEwLjQ2LDEwLjQ2LDAsMCwwLTEuMy0yLjkyYzIuMTQtMi41NiwzLjI5LTUuMjEsMy4yOS03LjU3LDAtMy44My0zLTYuMy03LjU5LTYuM2ExOS4zLDE5LjMsMCwwLDAtNy4yMiwxLjZsLS4zNC4xNEwyOC43LDEuNTJBNi4zMSw2LjMxLDAsMCwwLDI0LjQzLDAsMTQuMDcsMTQuMDcsMCwwLDAsMTcuNiwyLjJhMzYuOTMsMzYuOTMsMCwwLDAtNi43OCw1LjIxYy00LjYsNC4zOC04LjMsOS42My05LjkxLDE0QTEyLjUxLDEyLjUxLDAsMCwwLDAsMjYuNTRhNi4xNiw2LjE2LDAsMCwwLDIuMTMsNC40bC43OC42NkExMC40NCwxMC40NCwwLDAsMCwyLjc0LDM1YTkuMzYsOS4zNiwwLDAsMCwzLjIxLDYsMTAsMTAsMCwwLDAsNS4xMywyLjQzLDIwLjE5LDIwLjE5LDAsMCwwLDcuMzEsOEEyMy4zMywyMy4zMywwLDAsMCwzMC4xNyw1NUgzMWEyMy4yNywyMy4yNywwLDAsMCwxMi0zLjE2LDE5LjEsMTkuMSwwLDAsMCw3LjgyLTkuMDZsMCwwQTE2Ljg5LDE2Ljg5LDAsMCwwLDUyLDM3LjIzLDUuMTcsNS4xNywwLDAsMCw1MSwzMy44OFptLTEuNzgsOC4yMWMtMyw3LjI5LTEwLjMsMTEuMzUtMTksMTEuMDktOC4wNi0uMjQtMTQuOTQtNC41LTE4LTExLjQzYTcuOTQsNy45NCwwLDAsMS01LjEyLTIuMDYsNy41Niw3LjU2LDAsMCwxLTIuNjEtNC44NUE4LjMxLDguMzEsMCwwLDEsNSwzMUwzLjMyLDI5LjU2Qy00LjQyLDIzLDE5Ljc3LTMuODYsMjcuNTEsMi44OWwyLjY0LDIuNTgsMS40NC0uNjFjNi43OS0yLjgxLDEyLjMtMS40NSwxMi4zLDMsMCwyLjMzLTEuNDgsNS4wNS0zLjg2LDcuNTJhNy41NCw3LjU0LDAsMCwxLDIsMy40OCwxMSwxMSwwLDAsMSwuNDIsMi44MmMwLDEsLjA5LDMuMTYuMDksMy4ybDEsLjI3QTguNjQsOC42NCwwLDAsMSw0Ny4yLDI3YTMuNjYsMy42NiwwLDAsMSwxLjA2LDIuMDZBNCw0LDAsMCwxLDQ3LjU1LDMyLDEwLjE1LDEwLjE1LDAsMCwxLDQ4LDMzLjA4Yy4yLjY0LjM1LDEuMTguMzcsMS4yNS43NCwwLDEuODkuODUsMS44OSwyLjg5QTE1LjI5LDE1LjI5LDAsMCwxLDQ5LjE4LDQyLjA5WiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTQ4LDM2YTEuMzYsMS4zNiwwLDAsMC0uODYtLjE2LDExLjc2LDExLjc2LDAsMCwwLS44Mi0yLjc4QTE3Ljg5LDE3Ljg5LDAsMCwxLDQwLjQ1LDM2YTIzLjY0LDIzLjY0LDAsMCwxLTcuODEuODRjLTEuNjktLjE0LTIuODEtLjYzLTMuMjMuNzRhMTguMywxOC4zLDAsMCwwLDgsLjgxLjE0LjE0LDAsMCwxLC4xNi4xMy4xNS4xNSwwLDAsMS0uMDkuMTVzLTMuMTQsMS40Ni04LjE0LS4wOGEyLjU4LDIuNTgsMCwwLDAsMS44MywxLjkxLDguMjQsOC4yNCwwLDAsMCwxLjQ0LjM5YzYuMTksMS4wNiwxMi0yLjQ3LDEzLjI3LTMuMzYuMS0uMDcuMTYsMCwuMDguMTJsLS4xMy4xOGMtMS41OSwyLjA2LTUuODgsNC40NC0xMS40NSw0LjQ0LTIuNDMsMC00Ljg2LS44Ni01Ljc1LTIuMTctMS4zOC0yLS4wNy01LDIuMjQtNC43MWwxLC4xMWEyMS4xMywyMS4xMywwLDAsMCwxMC41LTEuNjhjMy4xNS0xLjQ2LDQuMzQtMy4wNyw0LjE2LTQuMzdBMS44NywxLjg3LDAsMCwwLDQ2LDI4LjM0YTYuOCw2LjgsMCwwLDAtMy0xLjQxYy0uNS0uMTQtLjg0LS4yMy0xLjItLjM1LS42NS0uMjEtMS0uMzktMS0xLjYxLDAtLjUzLS4xMi0yLjQtLjE2LTMuMTYtLjA2LTEuMzUtLjIyLTMuMTktMS4zNi00YTEuOTIsMS45MiwwLDAsMC0xLS4zMSwxLjg2LDEuODYsMCwwLDAtLjU4LjA2LDMuMDcsMy4wNywwLDAsMC0xLjUyLjg2LDUuMjQsNS4yNCwwLDAsMS00LDEuMzJjLS44LDAtMS42NS0uMTYtMi42Mi0uMjJsLS41NywwYTUuMjIsNS4yMiwwLDAsMC01LDQuNTdjLS41NiwzLjgzLDIuMjIsNS44MSwzLDdhMSwxLDAsMCwxLC4yMi41Mi44My44MywwLDAsMS0uMjguNTVoMGE5LjgsOS44LDAsMCwwLTIuMTYsOS4yLDcuNTksNy41OSwwLDAsMCwuNDEsMS4xMmMyLDQuNzMsOC4zLDYuOTMsMTQuNDMsNC45M2ExNS4wNiwxNS4wNiwwLDAsMCwyLjMzLTEsMTIuMjMsMTIuMjMsMCwwLDAsMy41Ny0yLjY3LDEwLjYxLDEwLjYxLDAsMCwwLDMtNS44MkM0OC42LDM2LjcsNDguMzMsMzYuMjMsNDgsMzZabS04LjI1LTcuODJjMCwuNS0uMzEuOTEtLjY4LjlzLS42Ni0uNDItLjY1LS45Mi4zMS0uOTEuNjgtLjlTMzkuNzIsMjcuNjgsMzkuNzEsMjguMThabS0xLjY4LTZjLjcxLS4xMiwxLjA2LjYyLDEuMzIsMS44NWEzLjY0LDMuNjQsMCwwLDEtLjA1LDIsNC4xNCw0LjE0LDAsMCwwLTEuMDYsMCw0LjEzLDQuMTMsMCwwLDEtLjY4LTEuNjRDMzcuMjksMjMuMjMsMzcuMzEsMjIuMzQsMzgsMjIuMjNabS0yLjQsNi41N2EuODIuODIsMCwwLDEsMS4xMS0uMTljLjQ1LjIyLjY5LjY3LjUzLDFhLjgyLjgyLDAsMCwxLTEuMTEuMTlDMzUuNywyOS41OCwzNS40NywyOS4xMywzNS42MywyOC44Wm0tMi44LS4zN2MtLjA3LjExLS4yMy4wOS0uNTcuMDZhNC4yNCw0LjI0LDAsMCwwLTIuMTQuMjIsMiwyLDAsMCwxLS40OS4xNC4xNi4xNiwwLDAsMS0uMTEsMCwuMTUuMTUsMCwwLDEtLjA1LS4xMi44MS44MSwwLDAsMSwuMzItLjUxLDIuNDEsMi40MSwwLDAsMSwxLjI3LS41MywxLjk0LDEuOTQsMCwwLDEsMS43NS41N0EuMTkuMTksMCwwLDEsMzIuODMsMjguNDNabS01LjExLTEuMjZjLS4xMiwwLS4xNy0uMDctLjE5LS4xNHMuMjgtLjU2LjYyLS44MWEzLjYsMy42LDAsMCwxLDMuNTEtLjQyQTMsMywwLDAsMSwzMywyNi44N2MuMTIuMi4xNS4zNS4wNy40NHMtLjQ0LDAtLjk1LS4yNGE0LjE4LDQuMTgsMCwwLDAtMi0uNDNBMjEuODUsMjEuODUsMCwwLDAsMjcuNzEsMjcuMTdaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMzUuNSwxMy4yOWMuMSwwLC4xNi0uMTUuMDctLjJhMTEsMTEsMCwwLDAtNC42OS0xLjIzLjA5LjA5LDAsMCwxLS4wNy0uMTQsNC43OCw0Ljc4LDAsMCwxLC44OC0uODkuMDkuMDksMCwwLDAtLjA2LS4xNiwxMi40NiwxMi40NiwwLDAsMC01LjYxLDIsLjA5LjA5LDAsMCwxLS4xMy0uMDksNi4xNiw2LjE2LDAsMCwxLC41OS0xLjQ1LjA4LjA4LDAsMCwwLS4xMS0uMTFBMjIuNzksMjIuNzksMCwwLDAsMjAsMTYuMjRhLjA5LjA5LDAsMCwwLC4xMi4xM0ExOS41MywxOS41MywwLDAsMSwyNywxMy4zMiwxOS4xLDE5LjEsMCwwLDEsMzUuNSwxMy4yOVoiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0yOC4zNCw2LjQyUzI2LjIzLDQsMjUuNiwzLjhDMjEuNjksMi43NCwxMy4yNCw4LjU3LDcuODQsMTYuMjcsNS42NiwxOS4zOSwyLjUzLDI0LjksNCwyNy43NGExMS40MywxMS40MywwLDAsMCwxLjc5LDEuNzJBNi42NSw2LjY1LDAsMCwxLDEwLDI2Ljc4LDM0LjIxLDM0LjIxLDAsMCwxLDIwLjgsMTEuNjIsNTUuMDksNTUuMDksMCwwLDEsMjguMzQsNi40MloiLz48L2c+PC9nPjwvc3ZnPg=='
231 );
232 }
233 add_action( 'admin_menu', 'mailchimp_sf_add_pages' );
234
235 /**
236 * Request handler
237 *
238 * @return void
239 */
240 function mailchimp_sf_request_handler() {
241 if ( isset( $_POST['mcsf_action'] ) ) {
242 switch ( $_POST['mcsf_action'] ) {
243 case 'logout':
244 // Check capability & Verify nonce
245 if (
246 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
247 ! isset( $_POST['_mcsf_nonce_action'] ) ||
248 ! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'mc_logout' )
249 ) {
250 wp_die( 'Cheatin&rsquo; huh?' );
251 }
252
253 // erase auth information
254 $options = array( 'mc_api_key', 'mailchimp_sf_access_token', 'mc_datacenter', 'mailchimp_sf_auth_error', 'mailchimp_sf_waiting_for_login' );
255 mailchimp_sf_delete_options( $options );
256 break;
257 case 'change_form_settings':
258 if (
259 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
260 ! isset( $_POST['_mcsf_nonce_action'] ) ||
261 ! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'update_general_form_settings' )
262 ) {
263 wp_die( 'Cheatin&rsquo; huh?' );
264 }
265
266 // Update the form settings
267 mailchimp_sf_save_general_form_settings();
268 break;
269 case 'mc_submit_signup_form':
270 // Validate nonce
271 if (
272 ! isset( $_POST['_mc_submit_signup_form_nonce'] ) ||
273 ! wp_verify_nonce( sanitize_key( $_POST['_mc_submit_signup_form_nonce'] ), 'mc_submit_signup_form' )
274 ) {
275 wp_die( 'Cheatin&rsquo; huh?' );
276 }
277
278 // Check if request from latest block.
279 if ( isset( $_POST['mailchimp_sf_list_id'] ) ) {
280 $block_form_submission = new Mailchimp_Block_Form_Submission();
281 $block_form_submission->handle_form_submission();
282 } else {
283 // Attempt the signup
284 mailchimp_sf_signup_submit();
285 }
286
287 // Do a different action for html vs. js
288 switch ( isset( $_POST['mc_submit_type'] ) ? $_POST['mc_submit_type'] : '' ) {
289 case 'html':
290 /* This gets set elsewhere! */
291 break;
292 case 'js':
293 if ( ! headers_sent() ) { // just in case...
294 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT', true, 200 );
295 }
296 echo wp_kses_post( mailchimp_sf_frontend_msg() );
297 exit;
298 }
299 }
300 }
301 }
302 add_action( 'init', 'mailchimp_sf_request_handler' );
303
304 /**
305 * Update merge fields
306 *
307 * @return void
308 */
309 function mailchimp_sf_update_merge_fields() {
310 mailchimp_sf_get_merge_vars( get_option( 'mc_list_id' ), true );
311 mailchimp_sf_get_interest_categories( get_option( 'mc_list_id' ), true );
312 update_option( 'mc_merge_field_migrate', true );
313 }
314
315 /**
316 * Get auth key
317 *
318 * @param mixed $salt Salt
319 * @return string
320 */
321 function mailchimp_sf_auth_nonce_key( $salt = null ) {
322 if ( is_null( $salt ) ) {
323 $salt = mailchimp_sf_auth_nonce_salt();
324 }
325 return 'social_authentication' . md5( AUTH_KEY . $salt );
326 }
327
328 /**
329 * Return auth nonce salt
330 *
331 * @return string
332 */
333 function mailchimp_sf_auth_nonce_salt() {
334 return md5( microtime() . isset( $_SERVER['SERVER_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_ADDR'] ) ) : '' );
335 }
336
337 /**
338 * Creates new Mailchimp API v3 object
339 *
340 * @return MailChimp_API | false
341 */
342 function mailchimp_sf_get_api() {
343 // Check for the access token first.
344 $access_token = mailchimp_sf_get_access_token();
345 $data_center = get_option( 'mc_datacenter' );
346 if ( ! empty( $access_token ) && ! empty( $data_center ) ) {
347 return new MailChimp_API( $access_token, $data_center );
348 }
349
350 // Check for the API key if the access token is not available.
351 $key = get_option( 'mc_api_key' );
352 if ( $key ) {
353 return new MailChimp_API( $key );
354 }
355
356 return false;
357 }
358
359 /**
360 * Checks to see if we're storing a password, if so, we need
361 * to upgrade to the API key
362 *
363 * @return bool
364 **/
365 function mailchimp_sf_needs_upgrade() {
366 $igs = get_option( 'mc_interest_groups' );
367
368 if ( false !== $igs // we have an option
369 && (
370 empty( $igs ) || // it can be an empty array (no interest groups)
371 ( is_array( $igs ) && isset( $igs[0]['id'] ) ) // OR it should be a populated array that's well-formed
372 )
373 ) {
374 return false; // no need to upgrade
375 } else {
376 return true; // yeah, let's do it
377 }
378 }
379
380 /**
381 * Deletes all Mailchimp options
382 *
383 * TODO: The options names should be moved to a config file
384 * or to a class dedicated to options
385 **/
386 function mailchimp_sf_delete_setup() {
387 $options = array(
388 'mc_user_id',
389 'mc_use_unsub_link',
390 'mc_list_id',
391 'mc_list_name',
392 'mc_interest_groups',
393 'mc_merge_vars',
394 );
395
396 $igs = get_option( 'mc_interest_groups' );
397 if ( is_array( $igs ) ) {
398 foreach ( $igs as $ig ) {
399 $opt = 'mc_show_interest_groups_' . $ig['id'];
400 $options[] = $opt;
401 }
402 }
403
404 $mv = get_option( 'mc_merge_vars' );
405 if ( is_array( $mv ) ) {
406 foreach ( $mv as $mv_var ) {
407 $opt = 'mc_mv_' . $mv_var['tag'];
408 $options[] = $opt;
409 }
410 }
411
412 mailchimp_sf_delete_options( $options );
413 }
414
415 /**
416 * Gets or sets a frontend message based on parameter passed to it
417 *
418 * Used to convey error messages to the user outside of the WP Admin
419 *
420 * On the plugin settings page, WP admin notices are used exclusively
421 * instead of the frontend message.
422 *
423 * @param mixed $msg Message
424 * @return string/bool depending on get/set
425 */
426 function mailchimp_sf_frontend_msg( $msg = null ) {
427 global $mcsf_msgs;
428
429 // Make sure we're formed properly
430 if ( ! is_array( $mcsf_msgs ) ) {
431 $mcsf_msgs = array();
432 }
433
434 // See if we're getting
435 if ( is_null( $msg ) ) {
436 return implode( '', $mcsf_msgs );
437 }
438
439 // Must be setting
440 $mcsf_msgs[] = $msg;
441 return true;
442 }
443
444 /**
445 * Gets or sets a frontend message based on parameter passed to it
446 *
447 * TODO: Deprecate this function in favor of mailchimp_sf_frontend_msg()
448 *
449 * @param mixed $msg Message
450 * @return string/bool depending on get/set
451 */
452 function mailchimp_sf_global_msg( $msg = null ) {
453 return mailchimp_sf_frontend_msg( $msg );
454 }
455
456 /**
457 * Sets the default options for the option form
458 *
459 * @param string $list_name The Mailchimp list name.
460 * @return void
461 */
462 function mailchimp_sf_set_form_defaults( $list_name = '' ) {
463 update_option( 'mc_header_content', esc_html__( 'Sign up for', 'mailchimp' ) . ' ' . $list_name );
464 update_option( 'mc_submit_text', esc_html__( 'Subscribe', 'mailchimp' ) );
465
466 update_option( 'mc_custom_style', 'off' );
467 update_option( 'mc_double_optin', true );
468 update_option( 'mc_use_unsub_link', 'off' );
469
470 update_option( 'mc_form_border_width', '1' );
471 update_option( 'mc_form_border_color', 'E0E0E0' );
472 update_option( 'mc_form_background', 'FFFFFF' );
473 update_option( 'mc_form_text_color', '3F3F3f' );
474 }
475
476 /**
477 * Saves the General Form settings on the options page
478 *
479 * @return void
480 **/
481 function mailchimp_sf_save_general_form_settings() {
482
483 /*Enable double optin toggle*/
484 if ( isset( $_POST['mc_double_optin'] ) ) {
485 update_option( 'mc_double_optin', true );
486 $msg = esc_html__( 'Double opt-in turned On!', 'mailchimp' );
487 admin_notice_success( $msg );
488 } elseif ( get_option( 'mc_double_optin' ) !== false ) {
489 update_option( 'mc_double_optin', false );
490 $msg = esc_html__( 'Double opt-in turned Off!', 'mailchimp' );
491 admin_notice_success( $msg );
492 }
493
494 /* NUKE the CSS! */
495 if ( isset( $_POST['mc_nuke_all_styles'] ) ) {
496 update_option( 'mc_nuke_all_styles', true );
497 $msg = esc_html__( 'Mailchimp CSS turned Off!', 'mailchimp' );
498 admin_notice_success( $msg );
499 } elseif ( get_option( 'mc_nuke_all_styles' ) !== false ) {
500 update_option( 'mc_nuke_all_styles', false );
501 $msg = esc_html__( 'Mailchimp CSS turned On!', 'mailchimp' );
502 admin_notice_success( $msg );
503 }
504
505 /* Update existing */
506 if ( isset( $_POST['mc_update_existing'] ) ) {
507 update_option( 'mc_update_existing', true );
508 $msg = esc_html__( 'Update existing subscribers turned On!' );
509 admin_notice_success( $msg );
510 } elseif ( get_option( 'mc_update_existing' ) !== false ) {
511 update_option( 'mc_update_existing', false );
512 $msg = esc_html__( 'Update existing subscribers turned Off!' );
513 admin_notice_success( $msg );
514 }
515
516 if ( isset( $_POST['mc_use_unsub_link'] ) ) {
517 update_option( 'mc_use_unsub_link', 'on' );
518 $msg = esc_html__( 'Unsubscribe link turned On!', 'mailchimp' );
519 admin_notice_success( $msg );
520 } elseif ( get_option( 'mc_use_unsub_link' ) !== 'off' ) {
521 update_option( 'mc_use_unsub_link', 'off' );
522 $msg = esc_html__( 'Unsubscribe link turned Off!', 'mailchimp' );
523 admin_notice_success( $msg );
524 }
525
526 $content = isset( $_POST['mc_header_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_header_content'] ) ) : '';
527 $content = str_replace( "\r\n", '<br/>', $content );
528 update_option( 'mc_header_content', $content );
529
530 $content = isset( $_POST['mc_subheader_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_subheader_content'] ) ) : '';
531 $content = str_replace( "\r\n", '<br/>', $content );
532 update_option( 'mc_subheader_content', $content );
533
534 $submit_text = isset( $_POST['mc_submit_text'] ) ? sanitize_text_field( wp_unslash( $_POST['mc_submit_text'] ) ) : '';
535 $submit_text = str_replace( "\r\n", '', $submit_text );
536 update_option( 'mc_submit_text', $submit_text );
537
538 // Set Custom Style option
539 update_option( 'mc_custom_style', isset( $_POST['mc_custom_style'] ) ? 'on' : 'off' );
540
541 // we told them not to put these things we are replacing in, but let's just make sure they are listening...
542 if ( isset( $_POST['mc_form_border_width'] ) ) {
543 update_option( 'mc_form_border_width', str_replace( 'px', '', absint( $_POST['mc_form_border_width'] ) ) );
544 }
545 if ( isset( $_POST['mc_form_border_color'] ) ) {
546 update_option( 'mc_form_border_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_border_color'] ) ) ) );
547 }
548 if ( isset( $_POST['mc_form_background'] ) ) {
549 update_option( 'mc_form_background', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_background'] ) ) ) );
550 }
551 if ( isset( $_POST['mc_form_text_color'] ) ) {
552 update_option( 'mc_form_text_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_text_color'] ) ) ) );
553 }
554
555 // IF NOT DEV MODE
556 $igs = get_option( 'mc_interest_groups' );
557 if ( is_array( $igs ) ) {
558 foreach ( $igs as $mv_var ) {
559 $opt = 'mc_show_interest_groups_' . $mv_var['id'];
560 if ( isset( $_POST[ $opt ] ) ) {
561 update_option( $opt, 'on' );
562 } else {
563 update_option( $opt, 'off' );
564 }
565 }
566 }
567
568 $mv = get_option( 'mc_merge_vars' );
569 if ( is_array( $mv ) ) {
570 foreach ( $mv as $mv_var ) {
571 $opt = 'mc_mv_' . $mv_var['tag'];
572 if ( isset( $_POST[ $opt ] ) || 'Y' === $mv_var['required'] ) {
573 update_option( $opt, 'on' );
574 } else {
575 update_option( $opt, 'off' );
576 }
577 }
578 }
579
580 $msg = esc_html__( 'Successfully Updated your List Subscribe Form Settings!', 'mailchimp' );
581 admin_notice_success( $msg );
582 }
583
584 /**
585 * Sees if the user changed the list, and updates options accordingly
586 **/
587 function mailchimp_sf_change_list_if_necessary() {
588 if ( ! isset( $_POST['mc_list_id'] ) ) {
589 return;
590 }
591
592 if (
593 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
594 ! isset( $_POST['update_mc_list_id_nonce'] ) ||
595 ! wp_verify_nonce( sanitize_key( $_POST['update_mc_list_id_nonce'] ), 'update_mc_list_id_action' )
596 ) {
597 wp_die( 'Security check failed.' );
598 }
599
600 if ( empty( $_POST['mc_list_id'] ) ) {
601 $msg = esc_html__( 'Please choose a valid list', 'mailchimp' );
602 admin_notice_error( $msg );
603 return;
604 }
605
606 $api = mailchimp_sf_get_api();
607 if ( ! $api ) { return; }
608
609 // we *could* support paging, but few users have that many lists (and shouldn't)
610 $lists = $api->get( 'lists', 100, array( 'fields' => 'lists.id,lists.name,lists.email_type_option' ) );
611
612 if ( ! isset( $lists['lists'] ) || is_wp_error( $lists['lists'] ) ) {
613 return;
614 }
615
616 $lists = $lists['lists'];
617
618 if ( is_array( $lists ) && ! empty( $lists ) ) {
619
620 /**
621 * If our incoming list ID (the one chosen in the select dropdown)
622 * is in our array of lists, the set it to be the active list
623 */
624 foreach ( $lists as $key => $list ) {
625 if ( isset( $_POST['mc_list_id'] ) && $list['id'] === $_POST['mc_list_id'] ) {
626 $list_id = sanitize_text_field( wp_unslash( $_POST['mc_list_id'] ) );
627 $list_name = $list['name'];
628 $list_key = $key;
629 }
630
631 $merge_fields = mailchimp_sf_get_merge_vars( $list['id'], false, false );
632 $interests = mailchimp_sf_get_interest_categories( $list['id'], false, false );
633 if ( ! empty( $merge_fields ) ) {
634 update_option( 'mailchimp_sf_merge_fields_' . $list['id'], $merge_fields );
635 }
636 if ( ! empty( $interests ) ) {
637 update_option( 'mailchimp_sf_interest_groups_' . $list['id'], $interests );
638 }
639 }
640
641 $orig_list = get_option( 'mc_list_id' );
642 if ( '' !== $list_id ) {
643 update_option( 'mc_list_id', $list_id );
644 update_option( 'mc_list_name', $list_name );
645 update_option( 'mc_email_type_option', $lists[ $list_key ]['email_type_option'] );
646
647 // See if the user changed the list
648 $new_list = false;
649 if ( $orig_list !== $list_id ) {
650 // The user changed the list, Reset the Form Defaults
651 mailchimp_sf_set_form_defaults( $list_name );
652
653 $new_list = true;
654 }
655
656 // Grab the merge vars and interest groups
657 $mv = mailchimp_sf_get_merge_vars( $list_id, $new_list );
658 $igs = mailchimp_sf_get_interest_categories( $list_id, $new_list );
659
660 $igs_text = ' ';
661 if ( is_array( $igs ) ) {
662 /* translators: %s: count (number) */
663 $igs_text .= sprintf( esc_html__( 'and %s Sets of Interest Groups', 'mailchimp' ), count( $igs ) );
664 }
665
666 $msg = sprintf(
667 /* translators: %s: count (number) */
668 __( '<b>Success!</b> Loaded and saved the info for %d Merge Variables', 'mailchimp' ) . $igs_text,
669 count( $mv )
670 ) . ' ' .
671 esc_html__( 'from your list' ) . ' "' . $list_name . '"<br/><br/>' .
672 esc_html__( 'Now you should either Turn On the Mailchimp Widget or change your options below, then turn it on.', 'mailchimp' );
673
674 admin_notice_success( $msg );
675 }
676
677 // Update the lists option.
678 update_option( 'mailchimp_sf_lists', $lists );
679 }
680 }
681
682 /**
683 * Get merge vars
684 *
685 * @param string $list_id List ID
686 * @param bool $new_list Whether this is a new list
687 * @param bool $update_option Whether to update the option
688 * @return array
689 */
690 function mailchimp_sf_get_merge_vars( $list_id, $new_list, $update_option = true ) {
691 $api = mailchimp_sf_get_api();
692 $mv = $api->get( 'lists/' . $list_id . '/merge-fields', 80 );
693
694 // if we get an error back from the api, exit this process.
695 if ( is_wp_error( $mv ) ) {
696 return;
697 }
698
699 $mv['merge_fields'] = mailchimp_sf_add_email_field( $mv['merge_fields'] );
700 if ( $update_option ) {
701 update_option( 'mc_merge_vars', $mv['merge_fields'] );
702 }
703
704 foreach ( $mv['merge_fields'] as $mv_var ) {
705 $opt = 'mc_mv_' . $mv_var['tag'];
706 if ( $new_list ) {
707 $public = $mv_var['public'] ?? false;
708 if ( ! $public ) {
709 // This is a hidden field, so we don't want to include it.
710 update_option( $opt, 'off' );
711 } else {
712 // We need to set the option to 'on' so that it shows up in the form.
713 update_option( $opt, 'on' );
714 }
715 }
716 }
717 return $mv['merge_fields'];
718 }
719
720 /**
721 * Add email field
722 *
723 * @param array $merge Merge
724 * @return array
725 */
726 function mailchimp_sf_add_email_field( $merge ) {
727 $email = array(
728 'tag' => 'EMAIL',
729 'name' => esc_html__( 'Email Address', 'mailchimp' ),
730 'type' => 'email',
731 'required' => true,
732 'public' => true,
733 'display_order' => 1,
734 'default_value' => null,
735 );
736 array_unshift( $merge, $email );
737 return $merge;
738 }
739
740 /**
741 * Get interest categories
742 *
743 * @param string $list_id List ID
744 * @param bool $new_list Whether this is a new list
745 * @param bool $update_option Whether to update the option
746 * @return array
747 */
748 function mailchimp_sf_get_interest_categories( $list_id, $new_list, $update_option = true ) {
749 $api = mailchimp_sf_get_api();
750 $igs = $api->get( 'lists/' . $list_id . '/interest-categories', 60 );
751
752 // if we get an error back from the api, exis
753 if ( is_wp_error( $igs ) ) {
754 return;
755 }
756
757 if ( is_array( $igs ) ) {
758 $key = 0;
759 foreach ( $igs['categories'] as $ig ) {
760 $groups = $api->get( 'lists/' . $list_id . '/interest-categories/' . $ig['id'] . '/interests', 60 );
761 $igs['categories'][ $key ]['groups'] = $groups['interests'];
762 $opt = 'mc_show_interest_groups_' . $ig['id'];
763
764 // turn them all on by default
765 if ( $new_list ) {
766 update_option( $opt, 'on' );
767 }
768 ++$key;
769 }
770 }
771
772 if ( $update_option ) {
773 update_option( 'mc_interest_groups', $igs['categories'] );
774 }
775
776 return $igs['categories'];
777 }
778
779
780 /**
781 * Outputs the Settings/Options page
782 */
783 function mailchimp_sf_setup_page() {
784 $path = plugin_dir_path( __FILE__ );
785 require_once $path . '/includes/admin/templates/settings.php';
786 }
787
788 /**
789 * Register the widget.
790 *
791 * @return void
792 */
793 function mailchimp_sf_register_widgets() {
794 if ( mailchimp_sf_get_api() ) {
795 register_widget( 'Mailchimp_SF_Widget' );
796 }
797 }
798 add_action( 'widgets_init', 'mailchimp_sf_register_widgets' );
799
800 /**
801 * Add shortcode
802 *
803 * @return string
804 */
805 function mailchimp_sf_shortcode() {
806 ob_start();
807 mailchimp_sf_signup_form();
808 return ob_get_clean();
809 }
810 add_shortcode( 'mailchimpsf_form', 'mailchimp_sf_shortcode' );
811
812 /**
813 * Attempts to signup a user, per the $_POST args.
814 *
815 * This sets a global message, that is then used in the widget
816 * output to retrieve and display that message.
817 *
818 * @return bool
819 */
820 function mailchimp_sf_signup_submit() {
821 $mv = get_option( 'mc_merge_vars', array() );
822 $mv_tag_keys = array();
823
824 $igs = get_option( 'mc_interest_groups', array() );
825
826 $list_id = get_option( 'mc_list_id' );
827 $email = isset( $_POST['mc_mv_EMAIL'] ) ? wp_strip_all_tags( wp_unslash( $_POST['mc_mv_EMAIL'] ) ) : '';
828
829 $merge = mailchimp_sf_merge_submit( $mv );
830
831 // Catch errors and fail early.
832 if ( is_wp_error( $merge ) ) {
833 $msg = '<strong class="mc_error_msg">' . $merge->get_error_message() . '</strong>';
834 mailchimp_sf_frontend_msg( $msg );
835
836 return false;
837 }
838
839 // Head back to the beginning of the merge vars array
840 reset( $mv );
841 // Ensure we have an array
842 $igs = ! is_array( $igs ) ? array() : $igs;
843 $igs = mailchimp_sf_groups_submit( $igs );
844
845 // Clear out empty merge vars
846 $merge = mailchimp_sf_merge_remove_empty( $merge );
847 if ( isset( $_POST['email_type'] ) && in_array( $_POST['email_type'], array( 'text', 'html', 'mobile' ), true ) ) {
848 $email_type = sanitize_text_field( wp_unslash( $_POST['email_type'] ) );
849 } else {
850 $email_type = 'html';
851 }
852
853 $api = mailchimp_sf_get_api();
854 if ( ! $api ) {
855 $url = mailchimp_sf_signup_form_url();
856 $error = sprintf(
857 '<strong class="mc_error_msg">%s</strong>',
858 wp_kses(
859 sprintf(
860 /* translators: 1: email address 2: url */
861 __(
862 'We encountered a problem adding %1$s to the list. Please <a href="%2$s">sign up here.</a>',
863 'mailchimp'
864 ),
865 esc_html( $email ),
866 esc_url( $url )
867 ),
868 [
869 'a' => [
870 'href' => [],
871 ],
872 ]
873 )
874 );
875 mailchimp_sf_frontend_msg( $error );
876 return false;
877 }
878
879 $url = 'lists/' . $list_id . '/members/' . md5( strtolower( $email ) );
880 $status = mailchimp_sf_check_status( $url );
881
882 // If update existing is turned off and the subscriber is not new, error out.
883 $is_new_subscriber = false === $status;
884 if ( ! get_option( 'mc_update_existing' ) && ! $is_new_subscriber ) {
885 $msg = esc_html__( 'This email address has already been subscribed to this list.', 'mailchimp' );
886 $error = new WP_Error( 'mailchimp-update-existing', $msg );
887 mailchimp_sf_frontend_msg( '<strong class="mc_error_msg">' . $msg . '</strong>' );
888 return false;
889 }
890
891 $body = mailchimp_sf_subscribe_body( $merge, $igs, $email_type, $email, $status, get_option( 'mc_double_optin' ) );
892 $retval = $api->post( $url, $body, 'PUT' );
893
894 // If we have errors, then show them
895 if ( is_wp_error( $retval ) ) {
896 $msg = '<strong class="mc_error_msg">' . $retval->get_error_message() . '</strong>';
897 mailchimp_sf_frontend_msg( $msg );
898 return false;
899 }
900
901 if ( 'subscribed' === $retval['status'] ) {
902 $esc = esc_html__( 'Success, you\'ve been signed up.', 'mailchimp' );
903 $msg = "<strong class='mc_success_msg'>{$esc}</strong>";
904 } else {
905 $esc = esc_html__( 'Success, you\'ve been signed up! Please look for our confirmation email.', 'mailchimp' );
906 $msg = "<strong class='mc_success_msg'>{$esc}</strong>";
907 }
908
909 // Set our front end success message
910 mailchimp_sf_frontend_msg( $msg );
911
912 return true;
913 }
914
915 /**
916 * Cleans up merge fields and interests to make them
917 * API 3.0-friendly.
918 *
919 * @param [type] $merge Merge fields
920 * @param [type] $igs Interest groups
921 * @param string $email_type Email type
922 * @param string $email Email
923 * @param string|false $status Status The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if an error occurred.
924 * @param string $double_optin Whether double opt-in is enabled. "1" for enabled and "" for disabled.
925 * @return stdClass
926 */
927 function mailchimp_sf_subscribe_body( $merge, $igs, $email_type, $email, $status, $double_optin ) {
928 $body = new stdClass();
929 $body->email_address = $email;
930 $body->email_type = $email_type;
931 $body->merge_fields = $merge;
932
933 if ( ! empty( $igs ) ) {
934 $body->interests = $igs;
935 }
936
937 // Early return for already subscribed users
938 if ( 'subscribed' === $status ) {
939 return $body;
940 }
941
942 // Subscribe the email immediately unless double opt-in is enabled
943 // "unsubscribed" and "subscribed" existing emails have been excluded at this stage
944 // "pending" emails should follow double opt-in rules
945 $body->status = $double_optin ? 'pending' : 'subscribed';
946
947 return $body;
948 }
949
950 /**
951 * Check the status of a subscriber in the list.
952 *
953 * @param string $endpoint API endpoint to check the status.
954 * @return string|false The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if the API returned 404 or an error occurred.
955 */
956 function mailchimp_sf_check_status( $endpoint ) {
957 $endpoint .= '?fields=status';
958 $api = mailchimp_sf_get_api();
959 $subscriber = $api->get( $endpoint, null );
960 if ( is_wp_error( $subscriber ) ) {
961 return false;
962 }
963 return $subscriber['status'];
964 }
965
966 /**
967 * Merge submit
968 *
969 * @param array $mv Merge Vars
970 * @return mixed
971 */
972 function mailchimp_sf_merge_submit( $mv ) {
973 // Loop through our Merge Vars, and if they're empty, but required, then print an error, and mark as failed
974 $merge = new stdClass();
975 foreach ( $mv as $mv_var ) {
976 // We also want to create an array where the keys are the tags for easier validation later
977 $tag = $mv_var['tag'];
978 $mv_tag_keys[ $tag ] = $mv_var;
979
980 $opt = 'mc_mv_' . $tag;
981
982 $opt_val = isset( $_POST[ $opt ] ) ? map_deep( stripslashes_deep( $_POST[ $opt ] ), 'sanitize_text_field' ) : '';
983
984 switch ( $mv_var['type'] ) {
985 /**
986 * US Phone validation
987 *
988 * - Merge field is phone
989 * - Merge field is "included" in the Mailchimp admin options
990 * - Phone format is set in Mailchimp account
991 * - Phone format is US in Mailchimp account
992 */
993 case 'phone':
994 if (
995 ( 'on' === get_option( $opt ) || $mv_var['required'] )
996 && isset( $mv_var['options']['phone_format'] )
997 && 'US' === $mv_var['options']['phone_format']
998 ) {
999 $opt_val = mailchimp_sf_merge_validate_phone( $opt_val, $mv_var );
1000 if ( is_wp_error( $opt_val ) ) {
1001 return $opt_val;
1002 }
1003 }
1004 break;
1005
1006 /**
1007 * Address validation
1008 *
1009 * - Merge field is address
1010 * - Merge field is "included" in the Mailchimp admin options
1011 * - Merge field is an array (address contains multiple <input> elements)
1012 */
1013 case 'address':
1014 if ( ( 'on' === get_option( $opt ) || $mv_var['required'] ) && is_array( $opt_val ) ) {
1015 $validate = mailchimp_sf_merge_validate_address( $opt_val, $mv_var );
1016 if ( is_wp_error( $validate ) ) {
1017 return $validate;
1018 }
1019
1020 if ( $validate ) {
1021 $merge->$tag = $validate;
1022 }
1023 }
1024 break;
1025
1026 /**
1027 * Handle generic array values
1028 *
1029 * Not sure what this does or is for
1030 *
1031 * - Merge field is an array, not specifically phone or address
1032 */
1033 default:
1034 if ( is_array( $opt_val ) ) {
1035 $keys = array_keys( $opt_val );
1036 $val = new stdClass();
1037 foreach ( $keys as $key ) {
1038 $val->$key = $opt_val[ $key ];
1039 }
1040 $opt_val = $val;
1041 }
1042 break;
1043 }
1044
1045 /**
1046 * Required fields
1047 *
1048 * If the field is required and empty, return an error
1049 */
1050 if ( 'Y' === $mv_var['required'] && trim( $opt_val ) === '' ) {
1051 /* translators: %s: field name */
1052 $message = sprintf( esc_html__( 'You must fill in %s.', 'mailchimp' ), esc_html( $mv_var['name'] ) );
1053 $error = new WP_Error( 'missing_required_field', $message );
1054 return $error;
1055 } elseif ( 'EMAIL' !== $tag ) {
1056 $merge->$tag = $opt_val;
1057 }
1058 }
1059 return $merge;
1060 }
1061
1062 /**
1063 * Validate phone
1064 *
1065 * @param array $opt_val Option value
1066 * @param array $data Data
1067 * @return void
1068 */
1069 function mailchimp_sf_merge_validate_phone( $opt_val, $data ) {
1070 // This filters out all 'falsey' elements
1071 $opt_val = array_filter( $opt_val );
1072 // If they weren't all empty
1073 if ( ! $opt_val ) {
1074 return;
1075 }
1076
1077 $opt_val = implode( '-', $opt_val );
1078 if ( strlen( $opt_val ) < 12 ) {
1079 $opt_val = '';
1080 }
1081
1082 if ( ! preg_match( '/[0-9]{0,3}-[0-9]{0,3}-[0-9]{0,4}/A', $opt_val ) ) {
1083 /* translators: %s: field name */
1084 $message = sprintf( esc_html__( '%s must consist of only numbers', 'mailchimp' ), esc_html( $data['name'] ) );
1085 $error = new WP_Error( 'mc_phone_validation', $message );
1086 return $error;
1087 }
1088
1089 return $opt_val;
1090 }
1091
1092 /**
1093 * Validate address
1094 *
1095 * @param array $opt_val Option value
1096 * @param array $data Data
1097 * @return mixed
1098 */
1099 function mailchimp_sf_merge_validate_address( $opt_val, $data ) {
1100 if ( 'Y' === $data['required'] ) {
1101 if ( empty( $opt_val['addr1'] ) || empty( $opt_val['city'] ) ) {
1102 /* translators: %s: field name */
1103 $message = sprintf( esc_html__( 'You must fill in %s.', 'mailchimp' ), esc_html( $data['name'] ) );
1104 $error = new WP_Error( 'invalid_address_merge', $message );
1105 return $error;
1106 }
1107 } elseif ( empty( $opt_val['addr1'] ) || empty( $opt_val['city'] ) ) {
1108 return false;
1109 }
1110
1111 $merge = new stdClass();
1112 $merge->addr1 = $opt_val['addr1'];
1113 $merge->addr2 = $opt_val['addr2'];
1114 $merge->city = $opt_val['city'];
1115 $merge->state = $opt_val['state'];
1116 $merge->zip = $opt_val['zip'];
1117 $merge->country = $opt_val['country'];
1118 return $merge;
1119 }
1120
1121 /**
1122 * Merge remove empty
1123 *
1124 * @param stdObj $merge Merge
1125 * @return stdObj
1126 */
1127 function mailchimp_sf_merge_remove_empty( $merge ) {
1128 foreach ( $merge as $k => $v ) {
1129 if ( is_object( $v ) && empty( $v ) ) {
1130 unset( $merge->$k );
1131 } elseif ( ( is_string( $v ) && trim( $v ) === '' ) || is_null( $v ) ) {
1132 unset( $merge->$k );
1133 }
1134 }
1135
1136 return $merge;
1137 }
1138
1139 /**
1140 * Groups submit
1141 *
1142 * @param array $igs Interest groups
1143 * @return stdClass
1144 */
1145 function mailchimp_sf_groups_submit( $igs ) {
1146 $groups = mailchimp_sf_set_all_groups_to_false();
1147
1148 if ( empty( $igs ) ) {
1149 return new StdClass();
1150 }
1151
1152 // get groups and ids
1153 // set all to false
1154
1155 foreach ( $igs as $ig ) {
1156 $ig_id = $ig['id'];
1157 if ( get_option( 'mc_show_interest_groups_' . $ig_id ) === 'on' && 'hidden' !== $ig['type'] ) {
1158 switch ( $ig['type'] ) {
1159 case 'dropdown':
1160 case 'radio':
1161 // there can only be one value submitted for radio/dropdowns, so use that at the group id.
1162 if ( isset( $_POST['group'][ $ig_id ] ) && ! empty( $_POST['group'][ $ig_id ] ) ) {
1163 $value = sanitize_text_field( wp_unslash( $_POST['group'][ $ig_id ] ) );
1164 $groups->$value = true;
1165 }
1166 break;
1167 case 'checkboxes':
1168 if ( isset( $_POST['group'][ $ig_id ] ) ) {
1169 $ig_ids = array_map(
1170 'sanitize_text_field',
1171 array_keys(
1172 stripslashes_deep( $_POST['group'][ $ig_id ] ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- ignoring becuase this is sanitized through array_map above
1173 )
1174 );
1175 foreach ( $ig_ids as $id ) {
1176 $groups->$id = true;
1177 }
1178 }
1179 break;
1180 default:
1181 // Nothing
1182 break;
1183 }
1184 }
1185 }
1186 return $groups;
1187 }
1188
1189 /**
1190 * Set all groups to false
1191 *
1192 * @return StdClass
1193 */
1194 function mailchimp_sf_set_all_groups_to_false() {
1195 $toreturn = new StdClass();
1196
1197 foreach ( get_option( 'mc_interest_groups' ) as $grouping ) {
1198 if ( 'hidden' !== $grouping['type'] ) {
1199 foreach ( $grouping['groups'] as $group ) {
1200 $id = $group['id'];
1201 $toreturn->$id = false;
1202 }
1203 }
1204 }
1205
1206 return $toreturn;
1207 }
1208
1209 /**
1210 * Verify key
1211 *
1212 * @param MailChimp_API $api API instance
1213 * @return mixed
1214 */
1215 function mailchimp_sf_verify_key( $api ) {
1216 $user = $api->get( '' );
1217 if ( is_wp_error( $user ) ) {
1218 return $user;
1219 }
1220
1221 // Might as well set this data if we have it already.
1222 $valid_roles = array( 'owner', 'admin', 'manager' );
1223 if ( isset( $user['role'] ) && in_array( $user['role'], $valid_roles, true ) ) {
1224 update_option( 'mc_api_key', $api->key );
1225 update_option( 'mc_user', $user );
1226 update_option( 'mc_datacenter', $api->datacenter );
1227
1228 } else {
1229 $msg = esc_html__( 'API Key must belong to "Owner", "Admin", or "Manager."', 'mailchimp' );
1230 return new WP_Error( 'mc-invalid-role', $msg );
1231 }
1232 }
1233
1234 /**
1235 * Update profile URL.
1236 *
1237 * @param string $email Email
1238 * @return string
1239 */
1240 function mailchimp_sf_update_profile_url( $email ) {
1241 $dc = get_option( 'mc_datacenter' );
1242 // This is the expected encoding for emails.
1243 $eid = base64_encode( $email ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- ignoring because this is the expected data for the endpoint
1244 $user = get_option( 'mc_user' );
1245 $list_id = get_option( 'mc_list_id' );
1246 $url = 'http://' . $dc . '.list-manage.com/subscribe/send-email?u=' . $user['account_id'] . '&id=' . $list_id . '&e=' . $eid;
1247 return $url;
1248 }
1249
1250 /**
1251 * Get signup form URL.
1252 *
1253 * @param string $list_id List ID
1254 * @return string
1255 */
1256 function mailchimp_sf_signup_form_url( $list_id = '' ) {
1257 $dc = get_option( 'mc_datacenter' );
1258 $user = get_option( 'mc_user' );
1259 if ( empty( $list_id ) ) {
1260 $list_id = get_option( 'mc_list_id' );
1261 }
1262 $url = 'http://' . $dc . '.list-manage.com/subscribe?u=' . $user['account_id'] . '&id=' . $list_id;
1263 return $url;
1264 }
1265
1266 /**
1267 * Delete options
1268 *
1269 * @param array $options Options
1270 * @return void
1271 */
1272 function mailchimp_sf_delete_options( $options = array() ) {
1273 foreach ( $options as $option ) {
1274 delete_option( $option );
1275 }
1276 }
1277
1278 /**********************
1279 * Utility Functions *
1280 **********************/
1281 /**
1282 * Utility function to allow placement of plugin in plugins, mu-plugins, child or parent theme's plugins folders
1283 *
1284 * This function must be ran _very early_ in the load process, as it sets up important constants for the rest of the plugin
1285 */
1286 function mailchimp_sf_where_am_i() {
1287 $locations = array(
1288 'plugins' => array(
1289 'dir' => plugin_dir_path( __FILE__ ),
1290 'url' => plugins_url(),
1291 ),
1292 'mu_plugins' => array(
1293 'dir' => plugin_dir_path( __FILE__ ),
1294 'url' => plugins_url(),
1295 ),
1296 'template' => array(
1297 'dir' => trailingslashit( get_template_directory() ) . 'plugins/',
1298 'url' => trailingslashit( get_template_directory_uri() ) . 'plugins/',
1299 ),
1300 'stylesheet' => array(
1301 'dir' => trailingslashit( get_stylesheet_directory() ) . 'plugins/',
1302 'url' => trailingslashit( get_stylesheet_directory_uri() ) . 'plugins/',
1303 ),
1304 );
1305
1306 // Set defaults
1307 $mscf_dirbase = trailingslashit( basename( __DIR__ ) ); // Typically wp-mailchimp/ or mailchimp/
1308 $mscf_dir = trailingslashit( plugin_dir_path( __FILE__ ) );
1309 $mscf_url = trailingslashit( plugins_url( '', __FILE__ ) );
1310
1311 // Try our hands at finding the real location
1312 foreach ( $locations as $key => $loc ) {
1313 $dir = trailingslashit( $loc['dir'] ) . $mscf_dirbase;
1314 $url = trailingslashit( $loc['url'] ) . $mscf_dirbase;
1315 if ( is_file( $dir . basename( __FILE__ ) ) ) {
1316 $mscf_dir = $dir;
1317 $mscf_url = $url;
1318 break;
1319 }
1320 }
1321
1322 // Define our complete filesystem path
1323 define( 'MCSF_DIR', $mscf_dir );
1324
1325 // Define our complete URL to the plugin folder
1326 define( 'MCSF_URL', $mscf_url );
1327 }
1328
1329
1330 /**
1331 * MODIFIED VERSION of wp_verify_nonce from WP Core. Core was not overridden to prevent problems when replacing
1332 * something universally.
1333 *
1334 * Verify that correct nonce was used with time limit.
1335 *
1336 * The user is given an amount of time to use the token, so therefore, since the
1337 * UID and $action remain the same, the independent variable is the time.
1338 *
1339 * @param string $nonce Nonce that was used in the form to verify
1340 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
1341 * @return bool Whether the nonce check passed or failed.
1342 */
1343 function mailchimp_sf_verify_nonce( $nonce, $action = -1 ) {
1344 $user = wp_get_current_user();
1345 $uid = (int) $user->ID;
1346 if ( ! $uid ) {
1347 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
1348 }
1349
1350 if ( empty( $nonce ) ) {
1351 return false;
1352 }
1353
1354 $token = 'MAILCHIMP';
1355 $i = wp_nonce_tick();
1356
1357 // Nonce generated 0-12 hours ago
1358 $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
1359 if ( hash_equals( $expected, $nonce ) ) {
1360 return 1;
1361 }
1362
1363 // Nonce generated 12-24 hours ago
1364 $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
1365 if ( hash_equals( $expected, $nonce ) ) {
1366 return 2;
1367 }
1368
1369 // Invalid nonce
1370 return false;
1371 }
1372
1373
1374 /**
1375 * MODIFIED VERSION of wp_create_nonce from WP Core. Core was not overridden to prevent problems when replacing
1376 * something universally.
1377 *
1378 * Creates a cryptographic token tied to a specific action, user, and window of time.
1379 *
1380 * @param string $action Scalar value to add context to the nonce.
1381 * @return string The token.
1382 */
1383 function mailchimp_sf_create_nonce( $action = -1 ) {
1384 $user = wp_get_current_user();
1385 $uid = (int) $user->ID;
1386 if ( ! $uid ) {
1387 /** This filter is documented in wp-includes/pluggable.php */
1388 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
1389 }
1390
1391 $token = 'MAILCHIMP';
1392 $i = wp_nonce_tick();
1393
1394 return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
1395 }
1396
1397 /**
1398 * Get Mailchimp Access Token.
1399 *
1400 * @since 1.6.0
1401 * @return string|bool
1402 */
1403 function mailchimp_sf_get_access_token() {
1404 $access_token = get_option( 'mailchimp_sf_access_token' );
1405 if ( empty( $access_token ) ) {
1406 return false;
1407 }
1408
1409 $data_encryption = new Mailchimp_Data_Encryption();
1410 $access_token = $data_encryption->decrypt( $access_token );
1411
1412 // If decryption fails, display notice to user to re-authenticate.
1413 if ( false === $access_token ) {
1414 update_option( 'mailchimp_sf_auth_error', true );
1415 }
1416
1417 return $access_token;
1418 }
1419
1420 /**
1421 * Should display Mailchimp Signup form.
1422 *
1423 * @since 1.6.0
1424 * @return bool
1425 */
1426 function mailchimp_sf_should_display_form() {
1427 return mailchimp_sf_get_api() && ! get_option( 'mailchimp_sf_auth_error' ) && get_option( 'mc_list_id' );
1428 }
1429