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

1,181 lines 38.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: MailChimp
4 Plugin URI: http://www.mailchimp.com/plugins/mailchimp-wordpress-plugin/
5 Description: The MailChimp plugin allows you to quickly and easily add a signup form for your MailChimp list.
6 Version: 1.5.2
7 Author: MailChimp
8 Author URI: https://mailchimp.com/
9 */
10 /* Copyright 2008-2012 MailChimp.com (email : api@mailchimp.com)
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 */
26
27 // Version constant for easy CSS refreshes
28 define('MCSF_VER', '1.5.2');
29
30 // What's our permission (capability) threshold
31 define('MCSF_CAP_THRESHOLD', 'manage_options');
32
33 // Define our location constants, both MCSF_DIR and MCSF_URL
34 mailchimpSF_where_am_i();
35
36 // Get our MailChimp API class in scope
37 if (!class_exists('MailChimp_API')) {
38 $path = plugin_dir_path(__FILE__);
39 require_once($path . 'lib/mailchimp/mailchimp.php');
40 }
41
42 // includes the widget code so it can be easily called either normally or via ajax
43 include_once('mailchimp_widget.php');
44
45 // includes the backwards compatibility functions
46 include_once('mailchimp_compat.php');
47
48 /**
49 * Do the following plugin setup steps here
50 *
51 * Internationalization
52 * Resource (JS & CSS) enqueuing
53 *
54 * @return void
55 */
56 function mailchimpSF_plugin_init() {
57 // Internationalize the plugin
58 $textdomain = 'mailchimp_i18n';
59 $locale = apply_filters( 'plugin_locale', get_locale(), $textdomain);
60 load_textdomain('mailchimp_i18n', MCSF_LANG_DIR.$textdomain.'-'.$locale.'.mo');
61
62 // Check for mc_api_key or sopresto key and continue if neither
63 mailchimpSF_migrate_sopresto();
64
65 if (get_option('mc_list_id') && get_option('mc_merge_field_migrate') != true && mailchimpSF_get_api() !== false) {
66 mailchimpSF_update_merge_fields(get_option('mc_list_id'));
67 }
68
69 // Bring in our appropriate JS and CSS resources
70 mailchimpSF_load_resources();
71 }
72
73 add_action( 'init', 'mailchimpSF_plugin_init' );
74
75
76 /**
77 * Add the settings link to the MailChimp plugin row
78 *
79 * @param array $links - Links for the plugin
80 * @return array - Links
81 */
82 function mailchimpSD_plugin_action_links($links) {
83 $settings_page = add_query_arg(array('page' => 'mailchimpSF_options'), admin_url('options-general.php'));
84 $settings_link = '<a href="'.esc_url($settings_page).'">'.__('Settings', 'mailchimp_i18n' ).'</a>';
85 array_unshift($links, $settings_link);
86 return $links;
87 }
88 add_filter('plugin_action_links_'.plugin_basename(__FILE__), 'mailchimpSD_plugin_action_links', 10, 1);
89
90 /**
91 * Loads the appropriate JS and CSS resources depending on
92 * settings and context (admin or not)
93 *
94 * @return void
95 */
96 function mailchimpSF_load_resources() {
97 // JS
98 if (get_option('mc_use_javascript') == 'on') {
99 if (!is_admin()) {
100 wp_enqueue_script('jquery_scrollto', MCSF_URL.'js/scrollTo.js', array('jquery'), MCSF_VER);
101 wp_enqueue_script('mailchimpSF_main_js', MCSF_URL.'js/mailchimp.js', array('jquery', 'jquery-form'), MCSF_VER);
102 // some javascript to get ajax version submitting to the proper location
103 global $wp_scripts;
104 $wp_scripts->localize('mailchimpSF_main_js', 'mailchimpSF', array(
105 'ajax_url' => trailingslashit(home_url()),
106 ));
107 }
108 }
109
110 if (get_option('mc_use_datepicker') == 'on' && !is_admin()) {
111 // Datepicker theme
112 wp_enqueue_style('flick', MCSF_URL.'/css/flick/flick.css');
113 // Datepicker JS
114 wp_enqueue_script('datepicker', MCSF_URL.'/js/datepicker.js', array('jquery','jquery-ui-core'));
115 }
116
117 if(get_option('mc_nuke_all_styles') !== true) {
118 wp_enqueue_style('mailchimpSF_main_css', home_url('?mcsf_action=main_css&ver='.MCSF_VER, 'relative'));
119 wp_enqueue_style('mailchimpSF_ie_css', MCSF_URL.'css/ie.css');
120 global $wp_styles;
121 $wp_styles->add_data( 'mailchimpSF_ie_css', 'conditional', 'IE' );
122 }
123 }
124
125
126 /**
127 * Loads resources for the MailChimp admin page
128 *
129 * @return void
130 */
131 function mc_admin_page_load_resources() {
132 wp_enqueue_style('mailchimpSF_admin_css', MCSF_URL.'css/admin.css');
133 }
134 add_action('load-settings_page_mailchimpSF_options', 'mc_admin_page_load_resources');
135
136
137 /**
138 * Loads jQuery Datepicker for the date-pick class
139 **/
140 function mc_datepicker_load() {
141 require_once(MCSF_DIR . '/views/datepicker.php');
142 }
143 if (get_option('mc_use_datepicker') == 'on' && !is_admin()) {
144 add_action('wp_head', 'mc_datepicker_load');
145 }
146
147 /**
148 * Handles requests that as light-weight a load as possible.
149 * typically, JS or CSS
150 **/
151 function mailchimpSF_early_request_handler() {
152 if (isset($_GET['mcsf_action'])) {
153 switch ($_GET['mcsf_action']) {
154 case 'main_css':
155 header("Content-type: text/css");
156 mailchimpSF_main_css();
157 exit;
158 }
159 }
160 }
161 add_action('init', 'mailchimpSF_early_request_handler', 0);
162
163 /**
164 * Outputs the front-end CSS. This checks several options, so it
165 * was best to put it in a Request-handled script, as opposed to
166 * a static file.
167 */
168 function mailchimpSF_main_css() {
169 require_once(MCSF_DIR . '/views/css/frontend.php');
170 }
171
172
173 /**
174 * Add our settings page to the admin menu
175 *
176 * @return void
177 */
178 function mailchimpSF_add_pages(){
179 // Add settings page for users who can edit plugins
180 add_options_page( __( 'MailChimp Setup', 'mailchimp_i18n' ), __( 'MailChimp Setup', 'mailchimp_i18n' ), MCSF_CAP_THRESHOLD, 'mailchimpSF_options', 'mailchimpSF_setup_page');
181 }
182 add_action('admin_menu', 'mailchimpSF_add_pages');
183
184 function mailchimpSF_request_handler() {
185 if (isset($_POST['mcsf_action'])) {
186 switch ($_POST['mcsf_action']) {
187 case 'login':
188 $key = trim($_POST['mailchimpSF_api_key']);
189
190 try {
191 $api = new MailChimp_API($key);
192 } catch (Exception $e) {
193 $msg = "<strong class='mc_error_msg'>" . $e->getMessage() . "</strong>";
194 mailchimpSF_global_msg($msg);
195 break;
196 }
197
198 $key = mailchimpSF_verify_key($api);
199 if(is_wp_error($key)) {
200 $msg = "<strong class='mc_error_msg'>" . $key->get_error_message() . "</strong>";
201 mailchimpSF_global_msg($msg);
202 }
203
204 break;
205 case 'logout':
206 // Check capability & Verify nonce
207 if (!current_user_can(MCSF_CAP_THRESHOLD) || !wp_verify_nonce($_POST['_mcsf_nonce_action'], 'mc_logout')) {
208 wp_die('Cheatin&rsquo; huh?');
209 }
210
211 // erase auth information
212 $options = array('mc_api_key', 'mc_sopresto_user', 'mc_sopresto_public_key', 'mc_sopresto_secret_key');
213 mailchimpSF_delete_options($options);
214 break;
215 case 'change_form_settings':
216 if (!current_user_can(MCSF_CAP_THRESHOLD) || !wp_verify_nonce($_POST['_mcsf_nonce_action'], 'update_general_form_settings')) {
217 wp_die('Cheatin&rsquo; huh?');
218 }
219
220 // Update the form settings
221 mailchimpSF_save_general_form_settings();
222 break;
223 case 'mc_submit_signup_form':
224 // Validate nonce
225 if (!wp_verify_nonce($_POST['_mc_submit_signup_form_nonce'], 'mc_submit_signup_form')) {
226 wp_die('Cheatin&rsquo; huh?');
227 }
228
229 // Attempt the signup
230 mailchimpSF_signup_submit();
231
232 // Do a different action for html vs. js
233 switch ($_POST['mc_submit_type']) {
234 case 'html':
235 /* Allow to fall through. The widget will pick up the
236 * global message left over from the signup_submit function */
237 break;
238 case 'js':
239 if (!headers_sent()){ //just in case...
240 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT', true, 200);
241 }
242 echo mailchimpSF_global_msg(); // Don't esc_html this, b/c we've already escaped it
243 exit;
244 }
245 }
246 }
247 }
248 add_action('init', 'mailchimpSF_request_handler');
249
250 function mailchimpSF_migrate_sopresto() {
251 $sopresto = get_option('mc_sopresto_secret_key');
252 if(!$sopresto) {
253 return;
254 }
255
256 // Talk to Sopresto, make exchange, delete old sopresto things.
257 $body = array(
258 'public_key' => get_option('mc_sopresto_public_key'),
259 'hash' => sha1(get_option('mc_sopresto_public_key').get_option('mc_sopresto_secret_key'))
260 );
261
262 $url = 'https://sopresto.socialize-this.com/mailchimp/exchange';
263 $args = array(
264 'method' => 'POST',
265 'timeout' => 500,
266 'redirection' => 5,
267 'httpversion' => '1.0',
268 'user-agent' => 'MailChimp WordPress Plugin/' . get_bloginfo( 'url' ),
269 'body' => $body
270 );
271
272 //post to sopresto
273 $key = wp_remote_post($url, $args);
274 if(!is_wp_error($key) && $key['response']['code'] == 200) {
275 $key = json_decode($key['body']);
276 try {
277 $api = new MailChimp_API($key->response);
278 } catch (Exception $e) {
279 $msg = "<strong class='mc_error_msg'>" . $e->getMessage() . "</strong>";
280 mailchimpSF_global_msg($msg);
281 return;
282 }
283
284 $verify = mailchimpSF_verify_key($api);
285
286 //something went wrong with the key that we had
287 if(is_wp_error($verify)) {
288 return;
289 }
290
291 delete_option('mc_sopresto_public_key');
292 delete_option('mc_sopresto_secret_key');
293 delete_option('mc_sopresto_user');
294
295 return;
296 }
297
298 // Nothing to do here.
299 return;
300 }
301
302 function mailchimpSF_update_merge_fields($list_id)
303 {
304 mailchimpSF_get_merge_vars(get_option('mc_list_id'), true);
305 mailchimpSF_get_interest_categories(get_option('mc_list_id'), true);
306 update_option('mc_merge_field_migrate', true);
307 }
308
309 function mailchimpSF_auth_nonce_key($salt = null) {
310 if (is_null($salt)) {
311 $salt = mailchimpSF_auth_nonce_salt();
312 }
313 return 'social_authentication' . md5( AUTH_KEY . $salt );
314 }
315
316 function mailchimpSF_auth_nonce_salt() {
317 return md5(microtime().$_SERVER['SERVER_ADDR']);
318 }
319
320 /**
321 * Creates new MailChimp API v3 object
322 *
323 * @return MailChimp_API | false
324 */
325
326 function mailchimpSF_get_api($force = false) {
327 $key = get_option('mc_api_key');
328 if($key) {
329 return new MailChimp_API($key);
330 }
331
332 return false;
333 }
334
335
336
337 /**
338 * Checks to see if we're storing a password, if so, we need
339 * to upgrade to the API key
340 *
341 * @return bool
342 **/
343 function mailchimpSF_needs_upgrade() {
344 $igs = get_option('mc_interest_groups');
345
346 if ($igs !== false // we have an option
347 && (
348 empty($igs) || // it can be an empty array (no interest groups)
349 (is_array($igs) && isset($igs[0]['id'])) // OR it should be a populated array that's well-formed
350 )) {
351 return false; // no need to upgrade
352 }
353 else {
354 return true; // yeah, let's do it
355 }
356 }
357
358 /**
359 * Deletes all mailchimp options
360 **/
361 function mailchimpSF_delete_setup() {
362 $options = array('mc_user_id', 'mc_sopresto_user', 'mc_sopresto_public_key', 'mc_sopresto_secret_key', 'mc_rewards', 'mc_use_javascript', 'mc_use_datepicker', 'mc_use_unsub_link', 'mc_list_id', 'mc_list_name', 'mc_interest_groups', 'mc_merge_vars');
363
364 $igs = get_option('mc_interest_groups');
365 if (is_array($igs)) {
366 foreach ($igs as $ig) {
367 $opt = 'mc_show_interest_groups_'.$ig['id'];
368 $options[] = $opt;
369 }
370 }
371
372 $mv = get_option('mc_merge_vars');
373 if (is_array($mv)){
374 foreach($mv as $var){
375 $opt = 'mc_mv_'.$var['tag'];
376 $options[] = $opt;
377 }
378 }
379
380 mailchimpSF_delete_options($options);
381 }
382
383 /**
384 * Gets or sets a global message based on parameter passed to it
385 *
386 * @return string/bool depending on get/set
387 **/
388 function mailchimpSF_global_msg($msg = null) {
389 global $mcsf_msgs;
390
391 // Make sure we're formed properly
392 if (!is_array($mcsf_msgs)) {
393 $mcsf_msgs = array();
394 }
395
396 // See if we're getting
397 if (is_null($msg)) {
398 return implode('', $mcsf_msgs);
399 }
400
401 // Must be setting
402 $mcsf_msgs[] = $msg;
403 return true;
404 }
405
406 /**
407 * Sets the default options for the option form
408 **/
409 function mailchimpSF_set_form_defaults($list_name = '') {
410 update_option('mc_header_content',__( 'Sign up for', 'mailchimp_i18n' ).' '.$list_name);
411 update_option('mc_submit_text',__( 'Subscribe', 'mailchimp_i18n' ));
412
413 update_option('mc_use_datepicker', 'on');
414 update_option('mc_custom_style','off');
415 update_option('mc_use_javascript','on');
416 update_option('mc_double_optin', true);
417 update_option('mc_use_unsub_link','off');
418 update_option('mc_header_border_width','1');
419 update_option('mc_header_border_color','E3E3E3');
420 update_option('mc_header_background','FFFFFF');
421 update_option('mc_header_text_color','CC6600');
422
423 update_option('mc_form_border_width','1');
424 update_option('mc_form_border_color','E0E0E0');
425 update_option('mc_form_background','FFFFFF');
426 update_option('mc_form_text_color','3F3F3f');
427 }
428
429 /**
430 * Saves the General Form settings on the options page
431 *
432 * @return void
433 **/
434 function mailchimpSF_save_general_form_settings() {
435
436 // IF NOT DEV MODE
437 if (isset($_POST['mc_rewards'])){
438 update_option('mc_rewards', 'on');
439 $msg = '<p class="success_msg">'.__('Monkey Rewards turned On!', 'mailchimp_i18n').'</p>';
440 mailchimpSF_global_msg($msg);
441 } else if (get_option('mc_rewards')!='off') {
442 update_option('mc_rewards', 'off');
443 $msg = '<p class="success_msg">'.__('Monkey Rewards turned Off!', 'mailchimp_i18n').'</p>';
444 mailchimpSF_global_msg($msg);
445 }
446 if (isset($_POST['mc_use_javascript'])){
447 update_option('mc_use_javascript', 'on');
448 $msg = '<p class="success_msg">'.__('Fancy Javascript submission turned On!', 'mailchimp_i18n').'</p>';
449 mailchimpSF_global_msg($msg);
450 } else if (get_option('mc_use_javascript')!='off') {
451 update_option('mc_use_javascript', 'off');
452 $msg = '<p class="success_msg">'.__('Fancy Javascript submission turned Off!', 'mailchimp_i18n').'</p>';
453 mailchimpSF_global_msg($msg);
454 }
455
456 if (isset($_POST['mc_use_datepicker'])){
457 update_option('mc_use_datepicker', 'on');
458 $msg = '<p class="success_msg">'.__('Datepicker turned On!', 'mailchimp_i18n').'</p>';
459 mailchimpSF_global_msg($msg);
460 } else if (get_option('mc_use_datepicker')!='off') {
461 update_option('mc_use_datepicker', 'off');
462 $msg = '<p class="success_msg">'.__('Datepicker turned Off!', 'mailchimp_i18n').'</p>';
463 mailchimpSF_global_msg($msg);
464 }
465
466 /*Enable double optin toggle*/
467
468 if(isset($_POST['mc_double_optin'])) {
469 update_option('mc_double_optin', true);
470 $msg = '<p class="success_msg">'.__('Double opt-in turned On!', 'mailchimp_i18n').'</p>';
471 mailchimpSF_global_msg($msg);
472 } else if (get_option('mc_double_optin') != false) {
473 update_option('mc_double_optin', false);
474 $msg = '<p class="success_msg">'.__('Double opt-in turned Off!', 'mailchimp_i18n').'</p>';
475 mailchimpSF_global_msg($msg);
476 }
477
478 /* NUKE the CSS! */
479 if(isset($_POST['mc_nuke_all_styles'])) {
480 update_option('mc_nuke_all_styles', true);
481 $msg = '<p class="success_msg">'.__('MailChimp CSS turned Off!', 'mailchimp_i18n').'</p>';
482 mailchimpSF_global_msg($msg);
483 }elseif (get_option('mc_nuke_all_styles') !== false) {
484 update_option('mc_nuke_all_styles', false);
485 $msg = '<p class="success_msg">'.__('MailChimp CSS turned On!', 'mailchimp_i18n').'</p>';
486 mailchimpSF_global_msg($msg);
487 }
488
489 /* Update existing */
490 if (isset($_POST['mc_update_existing'])) {
491 update_option('mc_update_existing', true);
492 $msg = '<p class="success_msg">' . __('Update existing subscribers turned On!') . '</p>';
493 mailchimpSF_global_msg($msg);
494 } elseif (get_option('mc_update_existing') ==! false) {
495 update_option('mc_update_existing', false);
496 $msg = '<p class="success_msg">' . __('Update existing subscribers turned Off!') . '</p>';
497 mailchimpSF_global_msg($msg);
498 }
499
500 if (isset($_POST['mc_use_unsub_link'])){
501 update_option('mc_use_unsub_link', 'on');
502 $msg = '<p class="success_msg">'.__('Unsubscribe link turned On!', 'mailchimp_i18n').'</p>';
503 mailchimpSF_global_msg($msg);
504 }
505
506 elseif (get_option('mc_use_unsub_link')!='off') {
507 update_option('mc_use_unsub_link', 'off');
508 $msg = '<p class="success_msg">'.__('Unsubscribe link turned Off!', 'mailchimp_i18n').'</p>';
509 mailchimpSF_global_msg($msg);
510 }
511
512 $content = stripslashes($_POST['mc_header_content']);
513 $content = str_replace("\r\n","<br/>", $content);
514 update_option('mc_header_content', $content );
515
516 $content = stripslashes($_POST['mc_subheader_content']);
517 $content = str_replace("\r\n","<br/>", $content);
518 update_option('mc_subheader_content', $content );
519
520
521 $submit_text = stripslashes($_POST['mc_submit_text']);
522 $submit_text = str_replace("\r\n","", $submit_text);
523 update_option('mc_submit_text', $submit_text);
524
525 // Set Custom Style option
526 update_option('mc_custom_style', isset($_POST['mc_custom_style']) ? 'on' : 'off');
527
528 //we told them not to put these things we are replacing in, but let's just make sure they are listening...
529 if(isset($_POST['mc_form_border_width'])) {
530 update_option('mc_form_border_width',str_replace('px', '', $_POST['mc_form_border_width']) );
531 }
532 if(isset($_POST['mc_form_border_color'])) {
533 update_option('mc_form_border_color', str_replace('#', '', $_POST['mc_form_border_color']));
534 }
535 if(isset($_POST['mc_form_background'])){
536 update_option('mc_form_background',str_replace('#', '', $_POST['mc_form_background']));
537 }
538 if(isset($_POST['mc_form_text_color'])) {
539 update_option('mc_form_text_color', str_replace('#', '', $_POST['mc_form_text_color']));
540 }
541
542
543 // IF NOT DEV MODE
544 $igs = get_option('mc_interest_groups');
545 if (is_array($igs)) {
546 foreach($igs as $var){
547 $opt = 'mc_show_interest_groups_'.$var['id'];
548 if (isset($_POST[$opt])){
549 update_option($opt,'on');
550 } else {
551 update_option($opt,'off');
552 }
553 }
554 }
555
556 $mv = get_option('mc_merge_vars');
557 if (is_array($mv)) {
558 foreach($mv as $var){
559 $opt = 'mc_mv_'.$var['tag'];
560 if (isset($_POST[$opt]) || $var['required']=='Y'){
561 update_option($opt,'on');
562 } else {
563 update_option($opt,'off');
564 }
565 }
566 }
567
568 $msg = '<p class="success_msg">'.esc_html(__('Successfully Updated your List Subscribe Form Settings!', 'mailchimp_i18n')).'</p>';
569 mailchimpSF_global_msg($msg);
570 }
571
572 /**
573 * Sees if the user changed the list, and updates options accordingly
574 **/
575 function mailchimpSF_change_list_if_necessary() {
576 // Simple permission check before going through all this
577 if (!current_user_can(MCSF_CAP_THRESHOLD)) { return; }
578
579 $api = mailchimpSF_get_api();
580 if (!$api) { return; }
581
582 //we *could* support paging, but few users have that many lists (and shouldn't)
583 $lists = $api->get('lists',100);
584 $lists = $lists['lists'];
585
586 if (is_array($lists) && !empty($lists) && isset($_POST['mc_list_id'])) {
587
588 /* If our incoming list ID (the one chosen in the select dropdown)
589 is in our array of lists, the set it to be the active list */
590 foreach($lists as $key => $list) {
591 if ($list['id'] == $_POST['mc_list_id']) {
592 $list_id = $_POST['mc_list_id'];
593 $list_name = $list['name'];
594 $list_key = $key;
595 }
596 }
597
598 $orig_list = get_option('mc_list_id');
599 if ($list_id != '') {
600 update_option('mc_list_id', $list_id);
601 update_option('mc_list_name', $list_name);
602 update_option('mc_email_type_option', $lists[$list_key]['email_type_option']);
603
604
605 // See if the user changed the list
606 $new_list = false;
607 if ($orig_list != $list_id){
608 // The user changed the list, Reset the Form Defaults
609 mailchimpSF_set_form_defaults($list_name);
610
611 $new_list = true;
612 }
613 // email_type_option
614
615 // Grab the merge vars and interest groups
616 $mv = mailchimpSF_get_merge_vars($list_id, $new_list);
617 $igs = mailchimpSF_get_interest_categories($list_id, $new_list);
618
619 $igs_text = ' ';
620 if (is_array($igs)) {
621 $igs_text .= sprintf(__('and %s Sets of Interest Groups', 'mailchimp_i18n'), count($igs));
622 }
623
624 $msg = '<p class="success_msg">'.
625 sprintf(
626 __('<b>Success!</b> Loaded and saved the info for %d Merge Variables', 'mailchimp_i18n').$igs_text,
627 count($mv)
628 ).' '.
629 __('from your list').' "'.$list_name.'"<br/><br/>'.
630 __('Now you should either Turn On the MailChimp Widget or change your options below, then turn it on.', 'mailchimp_i18n').'</p>';
631 mailchimpSF_global_msg($msg);
632 }
633 }
634 }
635
636 function mailchimpSF_get_merge_vars($list_id, $new_list) {
637 $api = mailchimpSF_get_api();
638 $mv = $api->get('lists/' . $list_id . '/merge-fields', 80);
639
640 if(is_wp_error($mv)) {
641 return;
642 }
643
644 $mv['merge_fields'] = mailchimpSF_add_email_field($mv['merge_fields']);
645 update_option('mc_merge_vars', $mv['merge_fields']);
646 foreach($mv['merge_fields'] as $var){
647 $opt = 'mc_mv_'.$var['tag'];
648 //turn them all on by default
649 if ($new_list) {
650 update_option($opt, 'on' );
651 }
652 }
653 return $mv['merge_fields'];
654 }
655
656 function mailchimpSF_add_email_field($merge) {
657
658 $email = array(
659 'tag' => 'EMAIL',
660 'name' => __('Email Address', 'mailchimp_i18n'),
661 'type' => 'email',
662 'required' => true,
663 'public' => true,
664 'display_order' => 1,
665 'default_value' => null
666 );
667 array_unshift($merge, $email);
668 return $merge;
669 }
670
671 function mailchimpSF_get_interest_categories($list_id, $new_list) {
672 $api = mailchimpSF_get_api();
673 $igs = $api->get('lists/' . $list_id . '/interest-categories', 60);
674
675 if(is_wp_error($igs)) {
676 return;
677 }
678
679 if (is_array($igs)) {
680 $key = 0;
681 foreach($igs['categories'] as $ig) {
682 $groups = $api->get('lists/' . $list_id . '/interest-categories/' . $ig['id'] . '/interests', 60);
683 $igs['categories'][$key]['groups'] = $groups['interests'];
684 $opt = 'mc_show_interest_groups_'.$ig['id'];
685
686 //turn them all on by default
687 if ($new_list) {
688 update_option($opt, 'on' );
689 }
690 $key++;
691 }
692 }
693 update_option('mc_interest_groups', $igs['categories']);
694 return $igs['categories'];
695 }
696
697
698 /**
699 * Outputs the Settings/Options page
700 */
701 function mailchimpSF_setup_page() {
702 $path = plugin_dir_path(__FILE__);
703 wp_enqueue_script('showMe', MCSF_URL.'js/hidecss.js', array('jquery'), MCSF_VER);
704 require_once($path.'/views/setup_page.php');
705 }//mailchimpSF_setup_page()
706
707
708 function mailchimpSF_register_widgets() {
709 if (mailchimpSF_get_api()) {
710 register_widget('mailchimpSF_Widget');
711 }
712 }
713 add_action('widgets_init', 'mailchimpSF_register_widgets');
714
715 function mailchimpSF_shortcode($atts){
716 ob_start();
717 mailchimpSF_signup_form();
718 return ob_get_clean();
719 }
720 add_shortcode('mailchimpsf_form', 'mailchimpSF_shortcode');
721
722 /**
723 * Attempts to signup a user, per the $_POST args.
724 *
725 * This sets a global message, that is then used in the widget
726 * output to retrieve and display that message.
727 *
728 * @return bool
729 */
730 function mailchimpSF_signup_submit() {
731 $mv = get_option('mc_merge_vars', array());
732 $mv_tag_keys = array();
733
734 $igs = get_option('mc_interest_groups', array());
735
736 $listId = get_option('mc_list_id');
737 $email = isset($_POST['mc_mv_EMAIL']) ? strip_tags(stripslashes($_POST['mc_mv_EMAIL'])) : '';
738 $merge = $errs = $html_errs = array(); // Set up some vars
739
740 $merge = mailchimpSF_merge_submit($mv);
741
742 //Catch errors and fail early.
743 if(is_wp_error($merge)) {
744 $msg = "<strong class='mc_error_msg'>" . $merge->get_error_message() . "</strong>";
745 mailchimpSF_global_msg($msg);
746
747 return false;
748 }
749
750 // Head back to the beginning of the merge vars array
751 reset($mv);
752 // Ensure we have an array
753 $igs = !is_array($igs) ? array() : $igs;
754 $igs = mailchimpSF_groups_submit($igs);
755
756 // Clear out empty merge vars
757 $merge = mailchimpSF_merge_remove_empty($merge);
758 if (isset($_POST['email_type']) && in_array($_POST['email_type'], array('text', 'html', 'mobile'))) {
759 $email_type = $_POST['email_type'];
760 }
761 else {
762 $email_type = 'html';
763 }
764
765 $api = mailchimpSF_get_api();
766 if (!$api) {
767 $url = mailchimpSF_signup_form_url();
768 $error = '<strong class="mc_error_msg">'. __('We encountered a problem adding ' . $email . ' to the list. Please <a href="' . $url . '">sign up here.</a>') . '</strong>';
769 mailchimpSF_global_msg($error);
770 return false;
771 }
772
773 $url = 'lists/'. $listId . '/members/' . md5(strtolower($email));
774 $status = mailchimpSF_check_status($url);
775
776
777 // If update existing is turned off and the subscriber exists, error out.
778 if (get_option('mc_update_existing') == false && $status === 'subscribed') {
779 $msg = 'This email address is already subscribed to the list.';
780 $error = new WP_Error('mailchimp-update-existing', $msg);
781 mailchimpSF_global_msg('<strong class="mc_error_msg">' . $msg . '</strong>');
782 return false;
783 }
784
785 $body = mailchimpSF_subscribe_body($merge, $igs, $email_type, $email, $status, get_option('mc_double_optin'));
786 $retval = $api->post($url, $body, 'PUT');
787
788 // If we have errors, then show them
789 if(is_wp_error($retval)) {
790 $msg = "<strong class='mc_error_msg'>" . $retval->get_error_message() . "</strong>";
791 mailchimpSF_global_msg($msg);
792 return false;
793 }
794
795 if($retval['status'] == 'subscribed') {
796 $esc = __("Success, you've been signed up.", 'mailchimp_i18n');
797 $msg = "<strong class='mc_success_msg'>{$esc}</strong>";
798 } else {
799 $esc = __("Success, you've been signed up! Please look for our confirmation email.", 'mailchimp_i18n');
800 $msg = "<strong class='mc_success_msg'>{$esc}</strong>";
801 }
802
803 // Set our global message
804 mailchimpSF_global_msg($msg);
805
806 return true;
807 }
808
809 /*
810 Cleans up merge fields and interests to make them
811 API 3.0-friendly.
812 */
813
814 function mailchimpSF_subscribe_body($merge, $igs, $email_type, $email, $status, $double_optin)
815 {
816 $body = new stdClass();
817 $body->email_address = $email;
818 $body->email_type = $email_type;
819 $body->merge_fields = $merge;
820 if (!empty($igs)) {
821 $body->interests = $igs;
822 }
823
824 if($status !== 'subscribed') {
825 // single opt-in that covers new subscribers
826 if (!$status && $double_optin == false) {
827 $body->status = 'subscribed';
828 } else {
829 // anyone else
830 $body->status = 'pending';
831 }
832 }
833 return $body;
834 }
835
836 function mailchimpSF_check_status($endpoint) {
837 $endpoint .= '?fields=status';
838 $api = mailchimpSF_get_api();
839 $subscriber = $api->get($endpoint, null);
840 if(is_wp_error($subscriber)) {
841 return false;
842 }
843 return $subscriber['status'];
844 }
845
846 function mailchimpSF_merge_submit($mv) {
847 // Loop through our Merge Vars, and if they're empty, but required, then print an error, and mark as failed
848 $merge = new stdClass();
849 foreach($mv as $var) {
850 // We also want to create an array where the keys are the tags for easier validation later
851 $mv_tag_keys[$var['tag']] = $var;
852
853 $opt = 'mc_mv_'.$var['tag'];
854
855 $opt_val = isset($_POST[$opt]) ? stripslashes_deep($_POST[$opt]) : '';
856
857 // Handle phone number logic
858 if ($var['type'] === 'phone' && $var['options']['phone_format'] === 'US') {
859 $opt_val = mailchimpSF_merge_validate_phone($opt_val, $var);
860 if(is_wp_error($opt_val)) {
861 return $opt_val;
862 }
863 }
864 // Handle address logic
865 else if (is_array($opt_val) && $var['type'] == 'address') {
866 $validate = mailchimpSF_merge_validate_address($opt_val, $var);
867 if(is_wp_error($validate)) {
868 return $validate;
869 }
870
871 if($validate) {
872 $merge->$var['tag'] = $validate;
873 }
874 continue;
875
876 }
877 else if (is_array($opt_val)) {
878 $keys = array_keys($opt_val);
879 $val = new stdClass();
880 foreach($keys as $key) {
881 $val->$key = $opt_val[$key];
882 }
883 $opt_val = $val;
884 }
885
886 if ($var['required'] == 'Y' && trim($opt_val) == '') {
887 $message = sprintf(__("You must fill in %s.", 'mailchimp_i18n'), esc_html($var['name']));
888 $error = new WP_Error('missing_required_field', $message);
889 return $error;
890 }
891 else {
892 if ($var['tag'] != 'EMAIL') {
893 $merge->$var['tag'] = $opt_val;
894 }
895 }
896 }
897 return $merge;
898 }
899
900 function mailchimpSF_merge_validate_phone($opt_val, $var) {
901 // This filters out all 'falsey' elements
902 $opt_val = array_filter($opt_val);
903 // If they weren't all empty
904 if (!$opt_val) {
905 return;
906 }
907
908 $opt_val = implode('-', $opt_val);
909 if (strlen($opt_val) < 12) {
910 $opt_val = '';
911 }
912
913
914 if (!preg_match('/[0-9]{0,3}-[0-9]{0,3}-[0-9]{0,4}/A', $opt_val)) {
915 $message = sprintf(__("%s must consist of only numbers", 'mailchimp_i18n'), esc_html($var['name']));
916 $error = new WP_Error('mc_phone_validation', $message);
917 return $error;
918 }
919
920 return $opt_val;
921 }
922
923 function mailchimpSF_merge_validate_address($opt_val, $var) {
924 if ($var['required'] == 'Y') {
925 if (empty($opt_val['addr1']) || empty($opt_val['city'])) {
926 $message = sprintf(__("You must fill in %s.", 'mailchimp_i18n'), esc_html($var['name']));
927 $error = new WP_Error('invalid_address_merge', $message);
928 return $error;
929 }
930 } else {
931 if (empty($opt_val['addr1']) || empty($opt_val['city'])) {
932 return false;
933 }
934 }
935
936 $merge = new stdClass();
937 $merge->addr1 = $opt_val['addr1'];
938 $merge->addr2 = $opt_val['addr2'];
939 $merge->city = $opt_val['city'];
940 $merge->state = $opt_val['state'];
941 $merge->zip = $opt_val['zip'];
942 $merge->country = $opt_val['country'];
943 return $merge;
944
945 }
946
947 function mailchimpSF_merge_remove_empty($merge)
948 {
949 foreach ($merge as $k => $v) {
950 if (is_object($v) && empty($v)) {
951 unset($merge->$k);
952 } elseif ((is_string($v) && trim($v) === '') || is_null($v)) {
953 unset($merge->$k);
954 }
955 }
956
957 // If we have an empty $merge, then assign empty string.
958 if (count($merge) == 0 || $merge == '') {
959 $merge = '';
960 }
961
962 return $merge;
963 }
964
965 function mailchimpSF_groups_submit($igs) {
966 $groups = mailchimpSF_set_all_groups_to_false();
967
968 if(empty($igs)) {
969 return new StdClass();
970 }
971
972 //get groups and ids
973 //set all to false
974
975 foreach ($igs as $ig) {
976 if (get_option('mc_show_interest_groups_'.$ig['id']) == 'on' && $ig['type'] !== 'hidden') {
977 switch ($ig['type']) {
978 case 'dropdown':
979 case 'radio':
980 // there can only be one value submitted for radio/dropdowns, so use that at the group id.
981 if (isset($_POST['group'][$ig['id']]) && !empty($_POST['group'][$ig['id']])){
982 $value = $_POST['group'][$ig['id']];
983 $groups->$value = true;
984 }
985 break;
986 case 'checkboxes':
987 if (isset($_POST['group'][$ig['id']])) {
988 foreach ($_POST['group'][$ig['id']] as $id => $value) {
989 $groups->$id = true;
990 }
991 }
992 break;
993 default:
994 // Nothing
995 break;
996 }
997 }
998 }
999 return $groups;
1000 }
1001
1002 function mailchimpSF_set_all_groups_to_false() {
1003 $toreturn = new StdClass();
1004
1005 foreach (get_option('mc_interest_groups') as $grouping) {
1006 if($grouping['type'] !== 'hidden') {
1007 foreach ($grouping['groups'] as $group) {
1008 $toreturn->$group['id'] = false;
1009 }
1010 }
1011 }
1012
1013 return $toreturn;
1014 }
1015
1016 function mailchimpSF_verify_key($api) {
1017 $user = $api->get('');
1018 if (is_wp_error($user)) {
1019 return $user;
1020 }
1021
1022 //Might as well set this data if we have it already.
1023 $valid_roles = array('owner', 'admin', 'manager');
1024 if(in_array($user['role'], $valid_roles)) {
1025 update_option('mc_api_key', $api->key);
1026 update_option('mc_user', $user);
1027 update_option('mc_datacenter', $api->datacenter);
1028
1029 } else {
1030 $msg = __('API Key must belong to "Owner", "Admin", or "Manager."', 'mailchimp_i18n');
1031 return new WP_Error('mc-invalid-role', $msg);
1032 }
1033 return;
1034 }
1035
1036 function mailchimpSF_update_profile_url($email) {
1037 $dc = get_option('mc_datacenter');
1038 $eid = base64_encode($email);
1039 $user = get_option('mc_user');
1040 $list_id = get_option('mc_list_id');
1041 $url = 'http://' . $dc . '.list-manage.com/subscribe/send-email?u=' . $user['account_id'] . '&id=' . $list_id . '&e=' . $eid;
1042 return $url;
1043 }
1044
1045 function mailchimpSF_signup_form_url() {
1046 $dc = get_option('mc_datacenter');
1047 $user = get_option('mc_user');
1048 $list_id = get_option('mc_list_id');
1049 $url = 'http://' . $dc . '.list-manage.com/subscribe?u=' . $user['account_id'] . '&id=' . $list_id;
1050 return $url;
1051 }
1052
1053 function mailchimpSF_delete_options($options = array()) {
1054 foreach($options as $option) {
1055 delete_option($option);
1056 }
1057 }
1058
1059
1060 /**********************
1061 * Utility Functions *
1062 **********************/
1063 /**
1064 * Utility function to allow placement of plugin in plugins, mu-plugins, child or parent theme's plugins folders
1065 *
1066 * This function must be ran _very early_ in the load process, as it sets up important constants for the rest of the plugin
1067 */
1068 function mailchimpSF_where_am_i() {
1069 $locations = array(
1070 'plugins' => array(
1071 'dir' => plugin_dir_path(__FILE__),
1072 'url' => plugins_url()
1073 ),
1074 'mu_plugins' => array(
1075 'dir' => plugin_dir_path(__FILE__),
1076 'url' => plugins_url(),
1077 ),
1078 'template' => array(
1079 'dir' => trailingslashit(get_template_directory()).'plugins/',
1080 'url' => trailingslashit(get_template_directory_uri()).'plugins/',
1081 ),
1082 'stylesheet' => array(
1083 'dir' => trailingslashit(get_stylesheet_directory()).'plugins/',
1084 'url' => trailingslashit(get_stylesheet_directory_uri()).'plugins/',
1085 ),
1086 );
1087
1088 // Set defaults
1089 $mscf_dirbase = trailingslashit(basename(dirname(__FILE__))); // Typically wp-mailchimp/ or mailchimp/
1090 $mscf_dir = trailingslashit(plugin_dir_path(__FILE__));
1091 $mscf_url = trailingslashit(plugins_url(null, __FILE__));
1092
1093 // Try our hands at finding the real location
1094 foreach ($locations as $key => $loc) {
1095 $dir = trailingslashit($loc['dir']).$mscf_dirbase;
1096 $url = trailingslashit($loc['url']).$mscf_dirbase;
1097 if (is_file($dir.basename(__FILE__))) {
1098 $mscf_dir = $dir;
1099 $mscf_url = $url;
1100 break;
1101 }
1102 }
1103
1104 // Define our complete filesystem path
1105 define('MCSF_DIR', $mscf_dir);
1106
1107 /* Lang location needs to be relative *from* ABSPATH,
1108 so strip it out of our language dir location */
1109 define('MCSF_LANG_DIR', trailingslashit(MCSF_DIR).'po/');
1110
1111 // Define our complete URL to the plugin folder
1112 define('MCSF_URL', $mscf_url);
1113 }
1114
1115
1116 /**
1117 * MODIFIED VERSION of wp_verify_nonce from WP Core. Core was not overridden to prevent problems when replacing
1118 * something universally.
1119 *
1120 * Verify that correct nonce was used with time limit.
1121 *
1122 * The user is given an amount of time to use the token, so therefore, since the
1123 * UID and $action remain the same, the independent variable is the time.
1124 *
1125 * @param string $nonce Nonce that was used in the form to verify
1126 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
1127 * @return bool Whether the nonce check passed or failed.
1128 */
1129 function mailchimpSF_verify_nonce($nonce, $action = -1) {
1130 $user = wp_get_current_user();
1131 $uid = (int) $user->ID;
1132 if ( ! $uid ) {
1133 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
1134 }
1135
1136 if ( empty( $nonce ) ) {
1137 return false;
1138 }
1139
1140 $token = 'MAILCHIMP';
1141 $i = wp_nonce_tick();
1142
1143 // Nonce generated 0-12 hours ago
1144 $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10 );
1145 if ( hash_equals( $expected, $nonce ) ) {
1146 return 1;
1147 }
1148
1149 // Nonce generated 12-24 hours ago
1150 $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
1151 if ( hash_equals( $expected, $nonce ) ) {
1152 return 2;
1153 }
1154
1155 // Invalid nonce
1156 return false;
1157 }
1158
1159
1160 /**
1161 * MODIFIED VERSION of wp_create_nonce from WP Core. Core was not overridden to prevent problems when replacing
1162 * something universally.
1163 *
1164 * Creates a cryptographic token tied to a specific action, user, and window of time.
1165 *
1166 * @param string $action Scalar value to add context to the nonce.
1167 * @return string The token.
1168 */
1169 function mailchimpSF_create_nonce($action = -1) {
1170 $user = wp_get_current_user();
1171 $uid = (int) $user->ID;
1172 if ( ! $uid ) {
1173 /** This filter is documented in wp-includes/pluggable.php */
1174 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
1175 }
1176
1177 $token = 'MAILCHIMP';
1178 $i = wp_nonce_tick();
1179
1180 return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
1181 }