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

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