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

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