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

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

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