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

1,058 lines 31.3 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.9.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', '1.9.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 )
169 );
170
171 // Datepicker theme
172 wp_enqueue_style( 'flick', MCSF_URL . 'assets/css/flick/flick.css', array(), MCSF_VER );
173
174 // CSS
175 if ( get_option( 'mc_nuke_all_styles' ) !== '1' ) {
176 wp_enqueue_style( 'mailchimp_sf_main_css', MCSF_URL . 'assets/css/frontend.css', array(), MCSF_VER );
177
178 // Backwards compatibility. TODO: Remove this in a future version.
179 if ( get_option( 'mc_custom_style' ) === 'on' ) {
180 $custom_css = mailchimp_sf_custom_style_css();
181 wp_add_inline_style( 'mailchimp_sf_main_css', $custom_css );
182 }
183 }
184 }
185
186 /**
187 * Custom styles CSS
188 *
189 * @return string
190 */
191 function mailchimp_sf_custom_style_css() {
192 ob_start();
193 ?>
194 #mc_signup_form {
195 padding:5px;
196 border-width: <?php echo absint( get_option( 'mc_form_border_width' ) ); ?>px;
197 border-style: <?php echo ( get_option( 'mc_form_border_width' ) === 0 ) ? 'none' : 'solid'; ?>;
198 border-color: #<?php echo esc_attr( get_option( 'mc_form_border_color' ) ); ?>;
199 color: #<?php echo esc_attr( get_option( 'mc_form_text_color' ) ); ?>;
200 background-color: #<?php echo esc_attr( get_option( 'mc_form_background' ) ); ?>;
201 }
202 <?php
203 return ob_get_clean();
204 }
205
206 /**
207 * Request handler
208 *
209 * @return void
210 */
211 function mailchimp_sf_request_handler() {
212 if ( isset( $_POST['mcsf_action'] ) ) {
213 switch ( $_POST['mcsf_action'] ) {
214 case 'logout':
215 // Check capability & Verify nonce
216 if (
217 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
218 ! isset( $_POST['_mcsf_nonce_action'] ) ||
219 ! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'mc_logout' )
220 ) {
221 wp_die( 'Cheatin&rsquo; huh?' );
222 }
223
224 // erase auth information
225 $options = array( 'mc_api_key', 'mailchimp_sf_access_token', 'mc_datacenter', 'mailchimp_sf_auth_error', 'mailchimp_sf_waiting_for_login' );
226 mailchimp_sf_delete_options( $options );
227 break;
228 case 'change_form_settings':
229 if (
230 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
231 ! isset( $_POST['_mcsf_nonce_action'] ) ||
232 ! wp_verify_nonce( sanitize_key( $_POST['_mcsf_nonce_action'] ), 'update_general_form_settings' )
233 ) {
234 wp_die( 'Cheatin&rsquo; huh?' );
235 }
236
237 // Update the form settings
238 mailchimp_sf_save_general_form_settings();
239 break;
240 }
241 }
242 }
243 add_action( 'init', 'mailchimp_sf_request_handler' );
244
245 /**
246 * Update merge fields
247 *
248 * @return void
249 */
250 function mailchimp_sf_update_merge_fields() {
251 mailchimp_sf_get_merge_vars( get_option( 'mc_list_id' ), true );
252 mailchimp_sf_get_interest_categories( get_option( 'mc_list_id' ), true );
253 update_option( 'mc_merge_field_migrate', true );
254 }
255
256 /**
257 * Get auth key
258 *
259 * @param mixed $salt Salt
260 * @return string
261 */
262 function mailchimp_sf_auth_nonce_key( $salt = null ) {
263 if ( is_null( $salt ) ) {
264 $salt = mailchimp_sf_auth_nonce_salt();
265 }
266 return 'social_authentication' . md5( AUTH_KEY . $salt );
267 }
268
269 /**
270 * Return auth nonce salt
271 *
272 * @return string
273 */
274 function mailchimp_sf_auth_nonce_salt() {
275 return md5( microtime() . isset( $_SERVER['SERVER_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_ADDR'] ) ) : '' );
276 }
277
278 /**
279 * Creates new Mailchimp API v3 object
280 *
281 * @return MailChimp_API | false
282 */
283 function mailchimp_sf_get_api() {
284 // Check for the access token first.
285 $access_token = mailchimp_sf_get_access_token();
286 $data_center = get_option( 'mc_datacenter' );
287 if ( ! empty( $access_token ) && ! empty( $data_center ) ) {
288 return new MailChimp_API( $access_token, $data_center );
289 }
290
291 // Check for the API key if the access token is not available.
292 $key = get_option( 'mc_api_key' );
293 if ( $key ) {
294 return new MailChimp_API( $key );
295 }
296
297 return false;
298 }
299
300 /**
301 * Checks to see if we're storing a password, if so, we need
302 * to upgrade to the API key
303 *
304 * @return bool
305 **/
306 function mailchimp_sf_needs_upgrade() {
307 $igs = get_option( 'mc_interest_groups' );
308
309 if ( false !== $igs // we have an option
310 && (
311 empty( $igs ) || // it can be an empty array (no interest groups)
312 ( is_array( $igs ) && isset( $igs[0]['id'] ) ) // OR it should be a populated array that's well-formed
313 )
314 ) {
315 return false; // no need to upgrade
316 } else {
317 return true; // yeah, let's do it
318 }
319 }
320
321 /**
322 * Deletes all Mailchimp options
323 *
324 * TODO: The options names should be moved to a config file
325 * or to a class dedicated to options
326 **/
327 function mailchimp_sf_delete_setup() {
328 $options = array(
329 'mc_user_id',
330 'mc_use_unsub_link',
331 'mc_list_id',
332 'mc_list_name',
333 'mc_interest_groups',
334 'mc_merge_vars',
335 );
336
337 $igs = get_option( 'mc_interest_groups' );
338 if ( is_array( $igs ) ) {
339 foreach ( $igs as $ig ) {
340 $opt = 'mc_show_interest_groups_' . $ig['id'];
341 $options[] = $opt;
342 }
343 }
344
345 $mv = get_option( 'mc_merge_vars' );
346 if ( is_array( $mv ) ) {
347 foreach ( $mv as $mv_var ) {
348 $opt = 'mc_mv_' . $mv_var['tag'];
349 $options[] = $opt;
350 }
351 }
352
353 mailchimp_sf_delete_options( $options );
354 }
355
356 /**
357 * Gets or sets a frontend message based on parameter passed to it
358 *
359 * Used to convey error messages to the user outside of the WP Admin
360 *
361 * On the plugin settings page, WP admin notices are used exclusively
362 * instead of the frontend message.
363 *
364 * @param mixed $msg Message
365 * @return string/bool depending on get/set
366 */
367 function mailchimp_sf_frontend_msg( $msg = null ) {
368 global $mcsf_msgs;
369
370 // Make sure we're formed properly
371 if ( ! is_array( $mcsf_msgs ) ) {
372 $mcsf_msgs = array();
373 }
374
375 // See if we're getting
376 if ( is_null( $msg ) ) {
377 return implode( '', $mcsf_msgs );
378 }
379
380 // Must be setting
381 $mcsf_msgs[] = $msg;
382 return true;
383 }
384
385 /**
386 * Gets or sets a frontend message based on parameter passed to it
387 *
388 * TODO: Deprecate this function in favor of mailchimp_sf_frontend_msg()
389 *
390 * @param mixed $msg Message
391 * @return string/bool depending on get/set
392 */
393 function mailchimp_sf_global_msg( $msg = null ) {
394 return mailchimp_sf_frontend_msg( $msg );
395 }
396
397 /**
398 * Sets the default options for the option form
399 *
400 * @param string $list_name The Mailchimp list name.
401 * @return void
402 */
403 function mailchimp_sf_set_form_defaults( $list_name = '' ) {
404 update_option( 'mc_header_content', esc_html__( 'Sign up for', 'mailchimp' ) . ' ' . $list_name );
405 update_option( 'mc_submit_text', esc_html__( 'Subscribe', 'mailchimp' ) );
406
407 update_option( 'mc_custom_style', 'off' );
408 update_option( 'mc_double_optin', true );
409 update_option( 'mc_use_unsub_link', 'off' );
410
411 update_option( 'mc_form_border_width', '1' );
412 update_option( 'mc_form_border_color', 'E0E0E0' );
413 update_option( 'mc_form_background', 'FFFFFF' );
414 update_option( 'mc_form_text_color', '3F3F3f' );
415 }
416
417 /**
418 * Saves the General Form settings on the options page
419 *
420 * @return void
421 **/
422 function mailchimp_sf_save_general_form_settings() {
423
424 /*Enable double optin toggle*/
425 if ( isset( $_POST['mc_double_optin'] ) ) {
426 update_option( 'mc_double_optin', true );
427 $msg = esc_html__( 'Double opt-in turned On!', 'mailchimp' );
428 admin_notice_success( $msg );
429 } elseif ( get_option( 'mc_double_optin' ) !== false ) {
430 update_option( 'mc_double_optin', false );
431 $msg = esc_html__( 'Double opt-in turned Off!', 'mailchimp' );
432 admin_notice_success( $msg );
433 }
434
435 /* NUKE the CSS! */
436 if ( isset( $_POST['mc_nuke_all_styles'] ) ) {
437 update_option( 'mc_nuke_all_styles', true );
438 $msg = esc_html__( 'Mailchimp CSS turned Off!', 'mailchimp' );
439 admin_notice_success( $msg );
440 } elseif ( get_option( 'mc_nuke_all_styles' ) !== false ) {
441 update_option( 'mc_nuke_all_styles', false );
442 $msg = esc_html__( 'Mailchimp CSS turned On!', 'mailchimp' );
443 admin_notice_success( $msg );
444 }
445
446 /* Update existing */
447 if ( isset( $_POST['mc_update_existing'] ) ) {
448 update_option( 'mc_update_existing', true );
449 $msg = esc_html__( 'Update existing subscribers turned On!' );
450 admin_notice_success( $msg );
451 } elseif ( get_option( 'mc_update_existing' ) !== false ) {
452 update_option( 'mc_update_existing', false );
453 $msg = esc_html__( 'Update existing subscribers turned Off!' );
454 admin_notice_success( $msg );
455 }
456
457 if ( isset( $_POST['mc_use_unsub_link'] ) ) {
458 update_option( 'mc_use_unsub_link', 'on' );
459 $msg = esc_html__( 'Unsubscribe link turned On!', 'mailchimp' );
460 admin_notice_success( $msg );
461 } elseif ( get_option( 'mc_use_unsub_link' ) !== 'off' ) {
462 update_option( 'mc_use_unsub_link', 'off' );
463 $msg = esc_html__( 'Unsubscribe link turned Off!', 'mailchimp' );
464 admin_notice_success( $msg );
465 }
466
467 $content = isset( $_POST['mc_header_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_header_content'] ) ) : '';
468 $content = str_replace( "\r\n", '<br/>', $content );
469 update_option( 'mc_header_content', $content );
470
471 $content = isset( $_POST['mc_subheader_content'] ) ? wp_kses_post( wp_unslash( $_POST['mc_subheader_content'] ) ) : '';
472 $content = str_replace( "\r\n", '<br/>', $content );
473 update_option( 'mc_subheader_content', $content );
474
475 $submit_text = isset( $_POST['mc_submit_text'] ) ? sanitize_text_field( wp_unslash( $_POST['mc_submit_text'] ) ) : '';
476 $submit_text = str_replace( "\r\n", '', $submit_text );
477 update_option( 'mc_submit_text', $submit_text );
478
479 // Set Custom Style option
480 update_option( 'mc_custom_style', isset( $_POST['mc_custom_style'] ) ? 'on' : 'off' );
481
482 // we told them not to put these things we are replacing in, but let's just make sure they are listening...
483 if ( isset( $_POST['mc_form_border_width'] ) ) {
484 update_option( 'mc_form_border_width', str_replace( 'px', '', absint( $_POST['mc_form_border_width'] ) ) );
485 }
486 if ( isset( $_POST['mc_form_border_color'] ) ) {
487 update_option( 'mc_form_border_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_border_color'] ) ) ) );
488 }
489 if ( isset( $_POST['mc_form_background'] ) ) {
490 update_option( 'mc_form_background', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_background'] ) ) ) );
491 }
492 if ( isset( $_POST['mc_form_text_color'] ) ) {
493 update_option( 'mc_form_text_color', str_replace( '#', '', sanitize_text_field( wp_unslash( $_POST['mc_form_text_color'] ) ) ) );
494 }
495
496 // IF NOT DEV MODE
497 $igs = get_option( 'mc_interest_groups' );
498 if ( is_array( $igs ) ) {
499 foreach ( $igs as $mv_var ) {
500 $opt = 'mc_show_interest_groups_' . $mv_var['id'];
501 if ( isset( $_POST[ $opt ] ) ) {
502 update_option( $opt, 'on' );
503 } else {
504 update_option( $opt, 'off' );
505 }
506 }
507 }
508
509 $mv = get_option( 'mc_merge_vars' );
510 if ( is_array( $mv ) ) {
511 foreach ( $mv as $mv_var ) {
512 $opt = 'mc_mv_' . $mv_var['tag'];
513 if ( isset( $_POST[ $opt ] ) || 'Y' === $mv_var['required'] ) {
514 update_option( $opt, 'on' );
515 } else {
516 update_option( $opt, 'off' );
517 }
518 }
519 }
520
521 $msg = esc_html__( 'Successfully Updated your List Subscribe Form Settings!', 'mailchimp' );
522 admin_notice_success( $msg );
523 }
524
525 /**
526 * Sees if the user changed the list, and updates options accordingly
527 **/
528 function mailchimp_sf_change_list_if_necessary() {
529 if ( ! isset( $_POST['mc_list_id'] ) ) {
530 return;
531 }
532
533 if (
534 ! current_user_can( MCSF_CAP_THRESHOLD ) ||
535 ! isset( $_POST['update_mc_list_id_nonce'] ) ||
536 ! wp_verify_nonce( sanitize_key( $_POST['update_mc_list_id_nonce'] ), 'update_mc_list_id_action' )
537 ) {
538 wp_die( 'Security check failed.' );
539 }
540
541 if ( empty( $_POST['mc_list_id'] ) ) {
542 $msg = esc_html__( 'Please choose a valid list', 'mailchimp' );
543 admin_notice_error( $msg );
544 return;
545 }
546
547 $api = mailchimp_sf_get_api();
548 if ( ! $api ) { return; }
549
550 // we *could* support paging, but few users have that many lists (and shouldn't)
551 $lists = $api->get( 'lists', 100, array( 'fields' => 'lists.id,lists.name,lists.email_type_option' ) );
552
553 if ( ! isset( $lists['lists'] ) || is_wp_error( $lists['lists'] ) ) {
554 return;
555 }
556
557 $lists = $lists['lists'];
558
559 if ( is_array( $lists ) && ! empty( $lists ) ) {
560
561 /**
562 * If our incoming list ID (the one chosen in the select dropdown)
563 * is in our array of lists, the set it to be the active list
564 */
565 foreach ( $lists as $key => $list ) {
566 if ( isset( $_POST['mc_list_id'] ) && $list['id'] === $_POST['mc_list_id'] ) {
567 $list_id = sanitize_text_field( wp_unslash( $_POST['mc_list_id'] ) );
568 $list_name = $list['name'];
569 $list_key = $key;
570 }
571
572 $merge_fields = mailchimp_sf_get_merge_vars( $list['id'], false, false );
573 $interests = mailchimp_sf_get_interest_categories( $list['id'], false, false );
574 if ( ! empty( $merge_fields ) ) {
575 update_option( 'mailchimp_sf_merge_fields_' . $list['id'], $merge_fields );
576 }
577 if ( ! empty( $interests ) ) {
578 update_option( 'mailchimp_sf_interest_groups_' . $list['id'], $interests );
579 }
580 }
581
582 $orig_list = get_option( 'mc_list_id' );
583 if ( '' !== $list_id ) {
584 update_option( 'mc_list_id', $list_id );
585 update_option( 'mc_list_name', $list_name );
586 update_option( 'mc_email_type_option', $lists[ $list_key ]['email_type_option'] );
587
588 // See if the user changed the list
589 $new_list = false;
590 if ( $orig_list !== $list_id ) {
591 // The user changed the list, Reset the Form Defaults
592 mailchimp_sf_set_form_defaults( $list_name );
593
594 $new_list = true;
595 }
596
597 // Grab the merge vars and interest groups
598 $mv = mailchimp_sf_get_merge_vars( $list_id, $new_list );
599 $igs = mailchimp_sf_get_interest_categories( $list_id, $new_list );
600
601 $igs_text = ' ';
602 if ( is_array( $igs ) ) {
603 /* translators: %s: count (number) */
604 $igs_text .= sprintf( esc_html__( 'and %s Sets of Interest Groups', 'mailchimp' ), count( $igs ) );
605 }
606
607 $msg = sprintf(
608 /* translators: %s: count (number) */
609 __( '<b>Success!</b> Loaded and saved the info for %d Merge Variables', 'mailchimp' ) . $igs_text,
610 count( $mv )
611 ) . ' ' .
612 esc_html__( 'from your list' ) . ' "' . $list_name . '"<br/><br/>' .
613 esc_html__( 'Now you should either Turn On the Mailchimp Widget or change your options below, then turn it on.', 'mailchimp' );
614
615 admin_notice_success( $msg );
616 }
617
618 // Update the lists option.
619 update_option( 'mailchimp_sf_lists', $lists );
620 }
621 }
622
623 /**
624 * Get merge vars
625 *
626 * @param string $list_id List ID
627 * @param bool $new_list Whether this is a new list
628 * @param bool $update_option Whether to update the option
629 * @return array
630 */
631 function mailchimp_sf_get_merge_vars( $list_id, $new_list, $update_option = true ) {
632 $api = mailchimp_sf_get_api();
633 $mv = $api->get( 'lists/' . $list_id . '/merge-fields', 80 );
634
635 // if we get an error back from the api, exit this process.
636 if ( is_wp_error( $mv ) ) {
637 return;
638 }
639
640 $mv['merge_fields'] = mailchimp_sf_add_email_field( $mv['merge_fields'] );
641 if ( $update_option ) {
642 update_option( 'mc_merge_vars', $mv['merge_fields'] );
643 }
644
645 foreach ( $mv['merge_fields'] as $mv_var ) {
646 $opt = 'mc_mv_' . $mv_var['tag'];
647 if ( $new_list ) {
648 $public = $mv_var['public'] ?? false;
649 if ( ! $public ) {
650 // This is a hidden field, so we don't want to include it.
651 update_option( $opt, 'off' );
652 } else {
653 // We need to set the option to 'on' so that it shows up in the form.
654 update_option( $opt, 'on' );
655 }
656 }
657 }
658 return $mv['merge_fields'];
659 }
660
661 /**
662 * Add email field
663 *
664 * @param array $merge Merge
665 * @return array
666 */
667 function mailchimp_sf_add_email_field( $merge ) {
668 $email = array(
669 'tag' => 'EMAIL',
670 'name' => esc_html__( 'Email Address', 'mailchimp' ),
671 'type' => 'email',
672 'required' => true,
673 'public' => true,
674 'display_order' => 1,
675 'default_value' => null,
676 );
677 array_unshift( $merge, $email );
678 return $merge;
679 }
680
681 /**
682 * Get interest categories
683 *
684 * @param string $list_id List ID
685 * @param bool $new_list Whether this is a new list
686 * @param bool $update_option Whether to update the option
687 * @return array
688 */
689 function mailchimp_sf_get_interest_categories( $list_id, $new_list, $update_option = true ) {
690 $api = mailchimp_sf_get_api();
691 $igs = $api->get( 'lists/' . $list_id . '/interest-categories', 60 );
692
693 // if we get an error back from the api, exis
694 if ( is_wp_error( $igs ) ) {
695 return;
696 }
697
698 if ( is_array( $igs ) ) {
699 $key = 0;
700 foreach ( $igs['categories'] as $ig ) {
701 $groups = $api->get( 'lists/' . $list_id . '/interest-categories/' . $ig['id'] . '/interests', 60 );
702 $igs['categories'][ $key ]['groups'] = $groups['interests'];
703 $opt = 'mc_show_interest_groups_' . $ig['id'];
704
705 // turn them all on by default
706 if ( $new_list ) {
707 update_option( $opt, 'on' );
708 }
709 ++$key;
710 }
711 }
712
713 if ( $update_option ) {
714 update_option( 'mc_interest_groups', $igs['categories'] );
715 }
716
717 return $igs['categories'];
718 }
719
720 /**
721 * Register the widget.
722 *
723 * @return void
724 */
725 function mailchimp_sf_register_widgets() {
726 if ( mailchimp_sf_get_api() ) {
727 register_widget( 'Mailchimp_SF_Widget' );
728 }
729 }
730 add_action( 'widgets_init', 'mailchimp_sf_register_widgets' );
731
732 /**
733 * Add shortcode
734 *
735 * @return string
736 */
737 function mailchimp_sf_shortcode() {
738 ob_start();
739 mailchimp_sf_signup_form();
740 return ob_get_clean();
741 }
742 add_shortcode( 'mailchimpsf_form', 'mailchimp_sf_shortcode' );
743
744 /**
745 * Cleans up merge fields and interests to make them
746 * API 3.0-friendly.
747 *
748 * @param [type] $merge Merge fields
749 * @param [type] $igs Interest groups
750 * @param string $email_type Email type
751 * @param string $email Email
752 * @param string|false $status Status The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if an error occurred.
753 * @param string $double_optin Whether double opt-in is enabled. "1" for enabled and "" for disabled.
754 * @return stdClass
755 */
756 function mailchimp_sf_subscribe_body( $merge, $igs, $email_type, $email, $status, $double_optin ) {
757 $body = new stdClass();
758 $body->email_address = $email;
759 $body->email_type = $email_type;
760 $body->merge_fields = $merge;
761
762 if ( ! empty( $igs ) ) {
763 $body->interests = $igs;
764 }
765
766 // Early return for already subscribed users
767 if ( 'subscribed' === $status ) {
768 return $body;
769 }
770
771 // Subscribe the email immediately unless double opt-in is enabled
772 // "unsubscribed" and "subscribed" existing emails have been excluded at this stage
773 // "pending" emails should follow double opt-in rules
774 $body->status = $double_optin ? 'pending' : 'subscribed';
775
776 return $body;
777 }
778
779 /**
780 * Check the status of a subscriber in the list.
781 *
782 * @param string $endpoint API endpoint to check the status.
783 * @return string|false The subscription status ('subscribed', 'unsubscribed', 'pending', etc.) or false if the API returned 404 or an error occurred.
784 */
785 function mailchimp_sf_check_status( $endpoint ) {
786 $endpoint .= '?fields=status';
787 $api = mailchimp_sf_get_api();
788 $subscriber = $api->get( $endpoint, null );
789 if ( is_wp_error( $subscriber ) ) {
790 return false;
791 }
792 return $subscriber['status'];
793 }
794
795 /**
796 * Validate phone
797 *
798 * @param array $opt_val Option value
799 * @param array $data Data
800 * @return void
801 */
802 function mailchimp_sf_merge_validate_phone( $opt_val, $data ) {
803 // This filters out all 'falsey' elements
804 $opt_val = array_filter( $opt_val );
805 // If they weren't all empty
806 if ( ! $opt_val ) {
807 return;
808 }
809
810 $opt_val = implode( '-', $opt_val );
811 if ( strlen( $opt_val ) < 12 ) {
812 $opt_val = '';
813 }
814
815 if ( ! preg_match( '/[0-9]{0,3}-[0-9]{0,3}-[0-9]{0,4}/A', $opt_val ) ) {
816 /* translators: %s: field name */
817 $message = sprintf( esc_html__( '%s must consist of only numbers', 'mailchimp' ), esc_html( $data['name'] ) );
818 $error = new WP_Error( 'mc_phone_validation', $message );
819 return $error;
820 }
821
822 return $opt_val;
823 }
824
825 /**
826 * Validate address
827 *
828 * @param array $opt_val Option value
829 * @param array $data Data
830 * @return mixed
831 */
832 function mailchimp_sf_merge_validate_address( $opt_val, $data ) {
833 if ( 'Y' === $data['required'] ) {
834 if ( empty( $opt_val['addr1'] ) || empty( $opt_val['city'] ) ) {
835 /* translators: %s: field name */
836 $message = sprintf( esc_html__( 'You must fill in %s.', 'mailchimp' ), esc_html( $data['name'] ) );
837 $error = new WP_Error( 'invalid_address_merge', $message );
838 return $error;
839 }
840 } elseif ( empty( $opt_val['addr1'] ) || empty( $opt_val['city'] ) ) {
841 return false;
842 }
843
844 $merge = new stdClass();
845 $merge->addr1 = $opt_val['addr1'];
846 $merge->addr2 = $opt_val['addr2'];
847 $merge->city = $opt_val['city'];
848 $merge->state = $opt_val['state'];
849 $merge->zip = $opt_val['zip'];
850 $merge->country = $opt_val['country'];
851 return $merge;
852 }
853
854 /**
855 * Verify key
856 *
857 * @param MailChimp_API $api API instance
858 * @return mixed
859 */
860 function mailchimp_sf_verify_key( $api ) {
861 $user = $api->get( '' );
862 if ( is_wp_error( $user ) ) {
863 return $user;
864 }
865
866 // Might as well set this data if we have it already.
867 $valid_roles = array( 'owner', 'admin', 'manager' );
868 if ( isset( $user['role'] ) && in_array( $user['role'], $valid_roles, true ) ) {
869 update_option( 'mc_api_key', $api->key );
870 update_option( 'mc_user', $user );
871 update_option( 'mc_datacenter', $api->datacenter );
872
873 } else {
874 $msg = esc_html__( 'API Key must belong to "Owner", "Admin", or "Manager."', 'mailchimp' );
875 return new WP_Error( 'mc-invalid-role', $msg );
876 }
877 }
878
879 /**
880 * Update profile URL.
881 *
882 * @param string $email Email
883 * @return string
884 */
885 function mailchimp_sf_update_profile_url( $email ) {
886 $dc = get_option( 'mc_datacenter' );
887 // This is the expected encoding for emails.
888 $eid = base64_encode( $email ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- ignoring because this is the expected data for the endpoint
889 $user = get_option( 'mc_user' );
890 $list_id = get_option( 'mc_list_id' );
891 $url = 'http://' . $dc . '.list-manage.com/subscribe/send-email?u=' . $user['account_id'] . '&id=' . $list_id . '&e=' . $eid;
892 return $url;
893 }
894
895 /**
896 * Delete options
897 *
898 * @param array $options Options
899 * @return void
900 */
901 function mailchimp_sf_delete_options( $options = array() ) {
902 foreach ( $options as $option ) {
903 delete_option( $option );
904 }
905 }
906
907 /**********************
908 * Utility Functions *
909 **********************/
910 /**
911 * Utility function to allow placement of plugin in plugins, mu-plugins, child or parent theme's plugins folders
912 *
913 * This function must be ran _very early_ in the load process, as it sets up important constants for the rest of the plugin
914 */
915 function mailchimp_sf_where_am_i() {
916 $locations = array(
917 'plugins' => array(
918 'dir' => plugin_dir_path( __FILE__ ),
919 'url' => plugins_url(),
920 ),
921 'mu_plugins' => array(
922 'dir' => plugin_dir_path( __FILE__ ),
923 'url' => plugins_url(),
924 ),
925 'template' => array(
926 'dir' => trailingslashit( get_template_directory() ) . 'plugins/',
927 'url' => trailingslashit( get_template_directory_uri() ) . 'plugins/',
928 ),
929 'stylesheet' => array(
930 'dir' => trailingslashit( get_stylesheet_directory() ) . 'plugins/',
931 'url' => trailingslashit( get_stylesheet_directory_uri() ) . 'plugins/',
932 ),
933 );
934
935 // Set defaults
936 $mscf_dirbase = trailingslashit( basename( __DIR__ ) ); // Typically wp-mailchimp/ or mailchimp/
937 $mscf_dir = trailingslashit( plugin_dir_path( __FILE__ ) );
938 $mscf_url = trailingslashit( plugins_url( '', __FILE__ ) );
939
940 // Try our hands at finding the real location
941 foreach ( $locations as $key => $loc ) {
942 $dir = trailingslashit( $loc['dir'] ) . $mscf_dirbase;
943 $url = trailingslashit( $loc['url'] ) . $mscf_dirbase;
944 if ( is_file( $dir . basename( __FILE__ ) ) ) {
945 $mscf_dir = $dir;
946 $mscf_url = $url;
947 break;
948 }
949 }
950
951 // Define our complete filesystem path
952 define( 'MCSF_DIR', $mscf_dir );
953
954 // Define our complete URL to the plugin folder
955 define( 'MCSF_URL', $mscf_url );
956 }
957
958
959 /**
960 * MODIFIED VERSION of wp_verify_nonce from WP Core. Core was not overridden to prevent problems when replacing
961 * something universally.
962 *
963 * Verify that correct nonce was used with time limit.
964 *
965 * The user is given an amount of time to use the token, so therefore, since the
966 * UID and $action remain the same, the independent variable is the time.
967 *
968 * @param string $nonce Nonce that was used in the form to verify
969 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
970 * @return bool Whether the nonce check passed or failed.
971 */
972 function mailchimp_sf_verify_nonce( $nonce, $action = -1 ) {
973 $user = wp_get_current_user();
974 $uid = (int) $user->ID;
975 if ( ! $uid ) {
976 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
977 }
978
979 if ( empty( $nonce ) ) {
980 return false;
981 }
982
983 $token = 'MAILCHIMP';
984 $i = wp_nonce_tick();
985
986 // Nonce generated 0-12 hours ago
987 $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
988 if ( hash_equals( $expected, $nonce ) ) {
989 return 1;
990 }
991
992 // Nonce generated 12-24 hours ago
993 $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
994 if ( hash_equals( $expected, $nonce ) ) {
995 return 2;
996 }
997
998 // Invalid nonce
999 return false;
1000 }
1001
1002
1003 /**
1004 * MODIFIED VERSION of wp_create_nonce from WP Core. Core was not overridden to prevent problems when replacing
1005 * something universally.
1006 *
1007 * Creates a cryptographic token tied to a specific action, user, and window of time.
1008 *
1009 * @param string $action Scalar value to add context to the nonce.
1010 * @return string The token.
1011 */
1012 function mailchimp_sf_create_nonce( $action = -1 ) {
1013 $user = wp_get_current_user();
1014 $uid = (int) $user->ID;
1015 if ( ! $uid ) {
1016 /** This filter is documented in wp-includes/pluggable.php */
1017 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
1018 }
1019
1020 $token = 'MAILCHIMP';
1021 $i = wp_nonce_tick();
1022
1023 return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
1024 }
1025
1026 /**
1027 * Get Mailchimp Access Token.
1028 *
1029 * @since 1.6.0
1030 * @return string|bool
1031 */
1032 function mailchimp_sf_get_access_token() {
1033 $access_token = get_option( 'mailchimp_sf_access_token' );
1034 if ( empty( $access_token ) ) {
1035 return false;
1036 }
1037
1038 $data_encryption = new Mailchimp_Data_Encryption();
1039 $access_token = $data_encryption->decrypt( $access_token );
1040
1041 // If decryption fails, display notice to user to re-authenticate.
1042 if ( false === $access_token ) {
1043 update_option( 'mailchimp_sf_auth_error', true );
1044 }
1045
1046 return $access_token;
1047 }
1048
1049 /**
1050 * Should display Mailchimp Signup form.
1051 *
1052 * @since 1.6.0
1053 * @return bool
1054 */
1055 function mailchimp_sf_should_display_form() {
1056 return mailchimp_sf_get_api() && ! get_option( 'mailchimp_sf_auth_error' ) && get_option( 'mc_list_id' );
1057 }
1058