first commit

This commit is contained in:
2024-07-31 13:12:38 +07:00
commit b4e8cbe182
10213 changed files with 3125839 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
<?php
class MC4WP_Admin_Ajax
{
/**
* @var MC4WP_Admin_Tools
*/
protected $tools;
/**
* MC4WP_Admin_Ajax constructor.
*
* @param MC4WP_Admin_Tools $tools
*/
public function __construct(MC4WP_Admin_Tools $tools)
{
$this->tools = $tools;
}
/**
* Hook AJAX actions
*/
public function add_hooks()
{
add_action('wp_ajax_mc4wp_renew_mailchimp_lists', array( $this, 'refresh_mailchimp_lists' ));
add_action('wp_ajax_mc4wp_get_list_details', array( $this, 'get_list_details' ));
}
/**
* Empty lists cache & fetch lists again.
*/
public function refresh_mailchimp_lists()
{
if (! $this->tools->is_user_authorized()) {
wp_send_json_error();
return;
}
check_ajax_referer('mc4wp-ajax');
$mailchimp = new MC4WP_MailChimp();
$success = $mailchimp->refresh_lists();
wp_send_json($success);
}
/**
* Retrieve details (merge fields and interest categories) for one or multiple lists in Mailchimp
* @throws MC4WP_API_Exception
*/
public function get_list_details()
{
if (! $this->tools->is_user_authorized()) {
wp_send_json_error();
return;
}
$list_ids = (array) explode(',', $_GET['ids']);
$data = array();
$mailchimp = new MC4WP_MailChimp();
foreach ($list_ids as $list_id) {
$data[] = (object) array(
'id' => $list_id,
'merge_fields' => $mailchimp->get_list_merge_fields($list_id),
'interest_categories' => $mailchimp->get_list_interest_categories($list_id),
'marketing_permissions' => $mailchimp->get_list_marketing_permissions($list_id),
);
}
if (isset($_GET['format']) && $_GET['format'] === 'html') {
$merge_fields = $data[0]->merge_fields;
$interest_categories = $data[0]->interest_categories;
$marketing_permissions = $data[0]->marketing_permissions;
require MC4WP_PLUGIN_DIR . '/includes/views/parts/lists-overview-details.php';
} else {
wp_send_json($data);
}
exit;
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* Class MC4WP_Admin_Messages
*
* @ignore
* @since 3.0
*/
class MC4WP_Admin_Messages
{
/**
* @var array
*/
protected $bag;
/**
* @var bool
*/
protected $dirty = false;
/**
* Add hooks
*/
public function add_hooks()
{
add_action('admin_notices', array( $this, 'show' ));
register_shutdown_function(array( $this, 'save' ));
}
private function load()
{
if (is_null($this->bag)) {
$this->bag = get_option('mc4wp_flash_messages', array());
}
}
// empty flash bag
private function reset()
{
$this->bag = array();
$this->dirty = true;
}
/**
* Flash a message (shows on next pageload)
*
* @param $message
* @param string $type
*/
public function flash($message, $type = 'success')
{
$this->load();
$this->bag[] = array(
'text' => $message,
'type' => $type,
);
$this->dirty = true;
}
/**
* Show queued flash messages
*/
public function show()
{
$this->load();
foreach ($this->bag as $message) {
echo sprintf('<div class="notice notice-%s is-dismissible"><p>%s</p></div>', $message['type'], $message['text']);
}
$this->reset();
}
/**
* Save queued messages
*
* @hooked `shutdown`
*/
public function save()
{
if ($this->dirty) {
update_option('mc4wp_flash_messages', $this->bag, false);
}
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* Class MC4WP_Admin_Texts
*
* @ignore
* @since 3.0
*/
class MC4WP_Admin_Texts
{
/**
* @var string
*/
protected $plugin_file;
/**
* @param string $plugin_file
*/
public function __construct($plugin_file)
{
$this->plugin_file = $plugin_file;
}
/**
* Add hooks
*/
public function add_hooks()
{
global $pagenow;
add_filter('admin_footer_text', array( $this, 'footer_text' ));
// Hooks for Plugins overview page
if ($pagenow === 'plugins.php') {
add_filter('plugin_action_links_' . $this->plugin_file, array( $this, 'add_plugin_settings_link' ), 10, 2);
add_filter('plugin_row_meta', array( $this, 'add_plugin_meta_links' ), 10, 2);
}
}
/**
* Ask for a plugin review in the WP Admin footer, if this is one of the plugin pages.
*
* @param string $text
*
* @return string
*/
public function footer_text($text)
{
if (! empty($_GET['page']) && strpos($_GET['page'], 'mailchimp-for-wp') === 0) {
$text = sprintf('If you enjoy using <strong>Mailchimp for WordPress</strong>, please <a href="%s" target="_blank">leave us a ★★★★★ plugin review on WordPress.org</a>.', 'https://wordpress.org/support/plugin/mailchimp-for-wp/reviews/#new-post');
}
return $text;
}
/**
* Add the settings link to the Plugins overview
*
* @param array $links
* @param $file
*
* @return array
*/
public function add_plugin_settings_link($links, $file)
{
if ($file !== $this->plugin_file) {
return $links;
}
$settings_link = sprintf('<a href="%s">%s</a>', admin_url('admin.php?page=mailchimp-for-wp'), esc_html__('Settings', 'mailchimp-for-wp'));
array_unshift($links, $settings_link);
return $links;
}
/**
* Adds meta links to the plugin in the WP Admin > Plugins screen
*
* @param array $links
* @param string $file
*
* @return array
*/
public function add_plugin_meta_links($links, $file)
{
if ($file !== $this->plugin_file) {
return $links;
}
$links[] = '<a href="https://www.mc4wp.com/kb/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=plugins-page">' . esc_html__('Documentation', 'mailchimp-for-wp') . '</a>';
/**
* Filters meta links shown on the Plugins overview page
*
* This takes an array of strings
*
* @since 3.0
* @param array $links
* @ignore
*/
$links = apply_filters('mc4wp_admin_plugin_meta_links', $links);
return $links;
}
}

View File

@@ -0,0 +1,67 @@
<?php
class MC4WP_Admin_Tools
{
/**
* @return string
*/
public function get_plugin_page()
{
if (empty($_GET['page'])) {
return '';
}
$prefix = 'mailchimp-for-wp';
$page = ltrim(substr($_GET['page'], strlen($prefix)), '-');
return $page;
}
/**
* @param string $page
*
* @return bool
*/
public function on_plugin_page($page = null)
{
// any settings page
if (is_null($page)) {
return isset($_GET['page']) && strpos($_GET['page'], 'mailchimp-for-wp') === 0;
}
// specific page
return $this->get_plugin_page() === $page;
}
/**
* Does the logged-in user have the required capability?
*
* @return bool
*/
public function is_user_authorized()
{
return current_user_can($this->get_required_capability());
}
/**
* Get required capability to access settings page and view dashboard widgets.
*
* @return string
*/
public function get_required_capability()
{
$capability = 'manage_options';
/**
* Filters the required user capability to access the Mailchimp for WordPress' settings pages, view the dashboard widgets.
*
* Defaults to `manage_options`
*
* @since 3.0
* @param string $capability
* @see https://codex.wordpress.org/Roles_and_Capabilities
*/
$capability = (string) apply_filters('mc4wp_admin_required_capability', $capability);
return $capability;
}
}

View File

@@ -0,0 +1,530 @@
<?php
/**
* Class MC4WP_Admin
*
* @ignore
* @access private
*/
class MC4WP_Admin
{
/**
* @var string The relative path to the main plugin file from the plugins dir
*/
protected $plugin_file;
/**
* @var MC4WP_Admin_Messages
*/
protected $messages;
/**
* @var MC4WP_Admin_Ads
*/
protected $ads;
/**
* @var MC4WP_Admin_Tools
*/
protected $tools;
/**
* @var MC4WP_Admin_Review_Notice
*/
protected $review_notice;
/**
* Constructor
*
* @param MC4WP_Admin_Tools $tools
* @param MC4WP_Admin_Messages $messages
*/
public function __construct(MC4WP_Admin_Tools $tools, MC4WP_Admin_Messages $messages)
{
$this->tools = $tools;
$this->messages = $messages;
$this->plugin_file = plugin_basename(MC4WP_PLUGIN_FILE);
$this->ads = new MC4WP_Admin_Ads();
$this->review_notice = new MC4WP_Admin_Review_Notice($tools);
}
/**
* Registers all hooks
*/
public function add_hooks()
{
// Actions used globally throughout WP Admin
add_action('admin_menu', array( $this, 'build_menu' ));
add_action('admin_init', array( $this, 'initialize' ));
add_action('current_screen', array( $this, 'customize_admin_texts' ));
add_action('wp_dashboard_setup', array( $this, 'register_dashboard_widgets' ));
add_action('mc4wp_admin_empty_lists_cache', array( $this, 'renew_lists_cache' ));
add_action('mc4wp_admin_empty_debug_log', array( $this, 'empty_debug_log' ));
add_action('admin_notices', array( $this, 'show_api_key_notice' ));
add_action('mc4wp_admin_dismiss_api_key_notice', array( $this, 'dismiss_api_key_notice' ));
add_action('admin_enqueue_scripts', array( $this, 'enqueue_assets' ));
$this->ads->add_hooks();
$this->messages->add_hooks();
$this->review_notice->add_hooks();
}
/**
* Initializes various stuff used in WP Admin
*
* - Registers settings
*/
public function initialize()
{
// register settings
register_setting('mc4wp_settings', 'mc4wp', array( $this, 'save_general_settings' ));
// Load upgrader
$this->init_upgrade_routines();
// listen for custom actions
$this->listen_for_actions();
}
/**
* Listen for `_mc4wp_action` requests
*/
public function listen_for_actions()
{
// do nothing if _mc4wp_action was not in the request parameters
if (! isset($_REQUEST['_mc4wp_action'])) {
return;
}
// check if user is authorized
if (! $this->tools->is_user_authorized()) {
return;
}
// verify nonce
if (! isset($_REQUEST['_wpnonce']) || false === wp_verify_nonce($_REQUEST['_wpnonce'], '_mc4wp_action')) {
wp_nonce_ays('_mc4wp_action');
exit;
}
$action = (string) $_REQUEST['_mc4wp_action'];
/**
* Allows you to hook into requests containing `_mc4wp_action` => action name.
*
* The dynamic portion of the hook name, `$action`, refers to the action name.
*
* By the time this hook is fired, the user is already authorized. After processing all the registered hooks,
* the request is redirected back to the referring URL.
*
* @since 3.0
*/
do_action('mc4wp_admin_' . $action);
// redirect back to where we came from (to prevent double submit)
if (isset($_POST['_redirect_to'])) {
$redirect_url = $_POST['_redirect_to'];
} elseif (isset($_GET['_redirect_to'])) {
$redirect_url = $_GET['_redirect_to'];
} else {
$redirect_url = remove_query_arg('_mc4wp_action');
}
wp_safe_redirect($redirect_url);
exit;
}
/**
* Register dashboard widgets
*/
public function register_dashboard_widgets()
{
if (! $this->tools->is_user_authorized()) {
return;
}
/**
* Setup dashboard widget, users are authorized by now.
*
* Use this hook to register your own dashboard widgets for users with the required capability.
*
* @since 3.0
* @ignore
*/
do_action('mc4wp_dashboard_setup');
}
/**
* Upgrade routine
*/
private function init_upgrade_routines()
{
// upgrade routine for upgrade routine....
$previous_version = get_option('mc4wp_lite_version', 0);
if ($previous_version) {
delete_option('mc4wp_lite_version');
update_option('mc4wp_version', $previous_version);
}
$previous_version = get_option('mc4wp_version', 0);
// Ran upgrade routines before?
if (empty($previous_version)) {
update_option('mc4wp_version', MC4WP_VERSION);
// if we have at least one form, we're going to run upgrade routine for v3 => v4 anyway.
$posts = get_posts(
array(
'post_type' => 'mc4wp-form',
'posts_per_page' => 1,
)
);
if (empty($posts)) {
return;
}
$previous_version = '3.9';
}
// This means we're good!
if (version_compare($previous_version, MC4WP_VERSION, '>=')) {
return;
}
define('MC4WP_DOING_UPGRADE', true);
$upgrade_routines = new MC4WP_Upgrade_Routines($previous_version, MC4WP_VERSION, __DIR__ . '/migrations');
$upgrade_routines->run();
update_option('mc4wp_version', MC4WP_VERSION);
}
/**
* Renew Mailchimp lists cache
*/
public function renew_lists_cache()
{
// try getting new lists to fill cache again
$mailchimp = new MC4WP_MailChimp();
$lists = $mailchimp->refresh_lists();
if (! empty($lists)) {
$this->messages->flash(esc_html__('Success! The cached configuration for your Mailchimp lists has been renewed.', 'mailchimp-for-wp'));
}
}
/**
* Customize texts throughout WP Admin
*/
public function customize_admin_texts()
{
$texts = new MC4WP_Admin_Texts($this->plugin_file);
$texts->add_hooks();
}
/**
* Validates the General settings
* @param array $settings
* @return array
*/
public function save_general_settings(array $settings)
{
$current = mc4wp_get_options();
// merge with current settings to allow passing partial arrays to this method
$settings = array_merge($current, $settings);
// Make sure not to use obfuscated key
if (strpos($settings['api_key'], '*') !== false) {
$settings['api_key'] = $current['api_key'];
}
// Sanitize API key
$settings['api_key'] = sanitize_text_field($settings['api_key']);
// if API key changed, empty Mailchimp cache
if ($settings['api_key'] !== $current['api_key']) {
delete_transient('mc4wp_mailchimp_lists');
}
/**
* Runs right before general settings are saved.
*
* @param array $settings The updated settings array
* @param array $current The old settings array
*/
do_action('mc4wp_save_settings', $settings, $current);
return $settings;
}
/**
* Load scripts and stylesheet on Mailchimp for WP Admin pages
*/
public function enqueue_assets()
{
if (! $this->tools->on_plugin_page()) {
return;
}
$opts = mc4wp_get_options();
$page = $this->tools->get_plugin_page();
$mailchimp = new MC4WP_MailChimp();
// css
wp_register_style('mc4wp-admin', mc4wp_plugin_url('assets/css/admin.css'), array(), MC4WP_VERSION);
wp_enqueue_style('mc4wp-admin');
// js
wp_register_script('mc4wp-admin', mc4wp_plugin_url('assets/js/admin.js'), array(), MC4WP_VERSION, true);
wp_enqueue_script('mc4wp-admin');
$connected = ! empty($opts['api_key']);
$mailchimp_lists = $connected ? $mailchimp->get_lists() : array();
wp_localize_script(
'mc4wp-admin',
'mc4wp_vars',
array(
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('mc4wp-ajax'),
'mailchimp' => array(
'api_connected' => $connected,
'lists' => $mailchimp_lists,
),
'countries' => MC4WP_Tools::get_countries(),
'i18n' => array(
'invalid_api_key' => __('The given value does not look like a valid Mailchimp API key.', 'mailchimp-for-wp'),
'pro_only' => __('This is a premium feature. Please upgrade to Mailchimp for WordPress Premium to be able to use it.', 'mailchimp-for-wp'),
'renew_mailchimp_lists' => __('Renew Mailchimp lists', 'mailchimp-for-wp'),
'fetching_mailchimp_lists' => __('Fetching Mailchimp lists', 'mailchimp-for-wp'),
'fetching_mailchimp_lists_done' => __('Done! Mailchimp lists renewed.', 'mailchimp-for-wp'),
'fetching_mailchimp_lists_error' => __('Failed to renew your lists. An error occured.', 'mailchimp-for-wp'),
),
)
);
/**
* Hook to enqueue your own custom assets on the Mailchimp for WordPress setting pages.
*
* @since 3.0
*
* @param string $suffix
* @param string $page
*/
do_action('mc4wp_admin_enqueue_assets', '', $page);
}
/**
* Register the setting pages and their menu items
*/
public function build_menu()
{
$required_cap = $this->tools->get_required_capability();
$menu_items = array(
array(
'title' => esc_html__('Mailchimp API Settings', 'mailchimp-for-wp'),
'text' => 'Mailchimp',
'slug' => '',
'callback' => array( $this, 'show_generals_setting_page' ),
'position' => 0,
),
array(
'title' => esc_html__('Other Settings', 'mailchimp-for-wp'),
'text' => esc_html__('Other', 'mailchimp-for-wp'),
'slug' => 'other',
'callback' => array( $this, 'show_other_setting_page' ),
'position' => 90,
),
);
/**
* Filters the menu items to appear under the main menu item.
*
* To add your own item, add an associative array in the following format.
*
* $menu_items[] = array(
* 'title' => 'Page title',
* 'text' => 'Menu text',
* 'slug' => 'Page slug',
* 'callback' => 'my_page_function',
* 'position' => 50
* );
*
* @param array $menu_items
* @since 3.0
*/
$menu_items = (array) apply_filters('mc4wp_admin_menu_items', $menu_items);
// add top menu item
$icon = file_get_contents(MC4WP_PLUGIN_DIR . '/assets/img/icon.svg');
add_menu_page('Mailchimp for WP', 'MC4WP', $required_cap, 'mailchimp-for-wp', array( $this, 'show_generals_setting_page' ), 'data:image/svg+xml;base64,' . base64_encode($icon), '99.68491');
// sort submenu items by 'position'
usort($menu_items, array( $this, 'sort_menu_items_by_position' ));
// add sub-menu items
foreach ($menu_items as $item) {
$this->add_menu_item($item);
}
}
/**
* @param array $item
*/
public function add_menu_item(array $item)
{
// generate menu slug
$slug = 'mailchimp-for-wp';
if (! empty($item['slug'])) {
$slug .= '-' . $item['slug'];
}
// provide some defaults
$parent_slug = ! empty($item['parent_slug']) ? $item['parent_slug'] : 'mailchimp-for-wp';
$capability = ! empty($item['capability']) ? $item['capability'] : $this->tools->get_required_capability();
// register page
$hook = add_submenu_page($parent_slug, $item['title'] . ' - Mailchimp for WordPress', $item['text'], $capability, $slug, $item['callback']);
// register callback for loading this page, if given
if (array_key_exists('load_callback', $item)) {
add_action('load-' . $hook, $item['load_callback']);
}
}
/**
* Show the API Settings page
*/
public function show_generals_setting_page()
{
$opts = mc4wp_get_options();
$api_key = mc4wp_get_api_key();
$lists = array();
$connected = ! empty($api_key);
if ($connected) {
try {
$connected = $this->get_api()->is_connected();
$mailchimp = new MC4WP_MailChimp();
$lists = $mailchimp->get_lists();
} catch (MC4WP_API_Connection_Exception $e) {
$message = sprintf('<strong>%s</strong> %s %s ', esc_html__('Error connecting to Mailchimp:', 'mailchimp-for-wp'), $e->getCode(), $e->getMessage());
if (is_object($e->response_data) && ! empty($e->response_data->ref_no)) {
$message .= '<br />' . sprintf(esc_html__('Looks like your server is blocked by Mailchimp\'s firewall. Please contact Mailchimp support and include the following reference number: %s', 'mailchimp-for-wp'), $e->response_data->ref_no);
}
$message .= '<br /><br />' . sprintf('<a href="%s">' . esc_html__('Here\'s some info on solving common connectivity issues.', 'mailchimp-for-wp') . '</a>', 'https://www.mc4wp.com/kb/solving-connectivity-issues/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=settings-notice');
$this->messages->flash($message, 'error');
$connected = false;
} catch (MC4WP_API_Exception $e) {
$message = sprintf('<strong>%s</strong><br /> %s', esc_html__('Mailchimp returned the following error:', 'mailchimp-for-wp'), nl2br((string) $e));
$this->messages->flash($message, 'error');
$connected = false;
}
}
$obfuscated_api_key = mc4wp_obfuscate_string($api_key);
require MC4WP_PLUGIN_DIR . '/includes/views/general-settings.php';
}
/**
* Show the Other Settings page
*/
public function show_other_setting_page()
{
$opts = mc4wp_get_options();
$log = $this->get_log();
$log_reader = new MC4WP_Debug_Log_Reader($log->file);
require MC4WP_PLUGIN_DIR . '/includes/views/other-settings.php';
}
/**
* @param $a
* @param $b
*
* @return int
*/
public function sort_menu_items_by_position($a, $b)
{
$pos_a = isset($a['position']) ? $a['position'] : 80;
$pos_b = isset($b['position']) ? $b['position'] : 90;
return $pos_a < $pos_b ? -1 : 1;
}
/**
* Empties the log file
*/
public function empty_debug_log()
{
$log = $this->get_log();
file_put_contents($log->file, '');
$this->messages->flash(esc_html__('Log successfully emptied.', 'mailchimp-for-wp'));
}
/**
* Shows a notice when API key is not set.
*/
public function show_api_key_notice()
{
// don't show if on settings page already
if ($this->tools->on_plugin_page('')) {
return;
}
// only show to user with proper permissions
if (! $this->tools->is_user_authorized()) {
return;
}
// don't show if dismissed
if (get_transient('mc4wp_api_key_notice_dismissed')) {
return;
}
// don't show if api key is set already
$api_key = mc4wp_get_api_key();
if (! empty($api_key)) {
return;
}
echo '<div class="notice notice-warning mc4wp-is-dismissible">';
echo '<p>', sprintf(wp_kses(__('To get started with Mailchimp for WordPress, please <a href="%s">enter your Mailchimp API key on the settings page of the plugin</a>.', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), admin_url('admin.php?page=mailchimp-for-wp')), '</p>';
echo '<form method="post"><input type="hidden" name="_mc4wp_action" value="dismiss_api_key_notice" /><button type="submit" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button></form>';
echo '</div>';
}
/**
* Dismisses the API key notice for 1 week
*/
public function dismiss_api_key_notice()
{
set_transient('mc4wp_api_key_notice_dismissed', 1, 3600 * 24 * 7);
}
/**
* @return MC4WP_Debug_Log
*/
protected function get_log()
{
return mc4wp('log');
}
/**
* @return MC4WP_API_V3
*/
protected function get_api()
{
return mc4wp('api');
}
}

View File

@@ -0,0 +1,163 @@
<?php
/**
* Class MC4WP_Admin_Ads
*
* @ignore
* @access private
*/
class MC4WP_Admin_Ads
{
/**
* @return bool Adds hooks
*/
public function add_hooks()
{
// don't hook if Premium is activated
if (defined('MC4WP_PREMIUM_VERSION')) {
return false;
}
add_filter('mc4wp_admin_plugin_meta_links', array( $this, 'plugin_meta_links' ));
add_action('mc4wp_admin_form_after_behaviour_settings_rows', array( $this, 'after_form_settings_rows' ));
add_action('mc4wp_admin_form_after_appearance_settings_rows', array( $this, 'after_form_appearance_settings_rows' ));
add_action('mc4wp_admin_sidebar', array( $this, 'admin_sidebar' ));
add_action('mc4wp_admin_footer', array( $this, 'admin_footer' ));
add_action('mc4wp_admin_other_settings', array( $this, 'ecommerce' ), 90);
add_filter('mc4wp_admin_menu_items', array( $this, 'add_menu_item' ));
add_action('mc4wp_admin_after_woocommerce_integration_settings', array( $this, 'ecommerce' ));
return true;
}
public function add_menu_item($items)
{
$items['extensions'] = array(
'title' => __('Add-ons', 'mailchimp-for-wp'),
'text' => __('Add-ons', 'mailchimp-for-wp'),
'slug' => 'extensions',
'callback' => array( $this, 'show_extensions_page' ),
'position' => 100,
);
return $items;
}
/**
* Add text row to "Form > Appearance" tab.
*/
public function after_form_appearance_settings_rows()
{
echo '<tr>';
echo '<td colspan="2">';
echo '<p class="description">';
echo sprintf(__('Want to customize the style of your form? <a href="%s">Try our Styles Builder</a> & edit the look of your forms with just a few clicks.', 'mailchimp-for-wp'), 'https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=form-settings-link');
echo '</p>';
echo '</td>';
echo '</tr>';
}
/**
* Add text row to "Form > Settings" tab.
*/
public function after_form_settings_rows()
{
echo '<tr>';
echo '<td colspan="2">';
echo '<p class="description">';
if (rand(1, 2) === 1) {
echo sprintf(__('Be notified whenever someone subscribes? <a href="%s">Mailchimp for WordPress Premium</a> allows you to set up email notifications for your forms.', 'mailchimp-for-wp'), 'https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=footer-link');
} else {
echo sprintf(__('Increased conversions? <a href="%s">Mailchimp for WordPress Premium</a> submits forms without reloading the entire page, resulting in a much better experience for your visitors.', 'mailchimp-for-wp'), 'https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=form-settings-link');
}
echo '</p>';
echo '</td>';
echo '</tr>';
}
/**
* @param array $links
*
* @return array
*/
public function plugin_meta_links($links)
{
$links[] = '<a href="https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=plugins-upgrade-link">' . __('Upgrade to Premium', 'mailchimp-for-wp') . '</a>';
return $links;
}
/**
* Add several texts to admin footer.
*/
public function admin_footer()
{
if (isset($_GET['view']) && $_GET['view'] === 'edit-form') {
// WPML & Polylang specific message
if (defined('ICL_LANGUAGE_CODE')) {
echo '<p class="description">' . sprintf(__('Do you want translated forms for all of your languages? <a href="%s">Try Mailchimp for WordPress Premium</a>, which does just that plus more.', 'mailchimp-for-wp'), 'https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=footer-link') . '</p>';
return;
}
// General "edit form" message
echo '<p class="description">' . sprintf(__('Do you want to create more than one form? Our Premium add-on does just that! <a href="%s">Have a look at all Premium benefits</a>.', 'mailchimp-for-wp'), 'https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=footer-link') . '</p>';
return;
}
// General message
echo '<p class="description">' . sprintf(__('Are you enjoying this plugin? The Premium add-on unlocks several powerful features. <a href="%s">Find out about all benefits now</a>.', 'mailchimp-for-wp'), 'https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=footer-link') . '</p>';
}
/**
* Add email opt-in form to sidebar
*/
public function admin_sidebar()
{
echo '<style>.mc4wp-premium-box {
background: #fff8c5;
border: 1px solid #d4a72c66;
padding: 1em;
}</style>';
echo '<div class="mc4wp-box">';
echo '<div class="mc4wp-premium-box">';
echo '<h3>Mailchimp for WordPress Premium</h3>';
echo '<p>';
echo 'You are currently using the free version of Mailchimp for WordPress. ';
echo '</p>';
echo '<p>';
echo 'There is a Premium version of this plugin that adds several powerful features. Like multiple and improved sign-up forms, an easier way to visually enhance those forms, advanced e-commerce integration and keeping track of all sign-up attempts in your local WordPress database.';
echo '</p>';
echo '<p>You can have all those benefits for a small yearly fee. <a href="https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=upgrade-box">Take a look at Mailchimp for WordPress Premium here</a>.</p>';
echo '</div>';
echo '</div>';
}
/**
* Show notice about E-Commerce integration in Premium.
*/
public function ecommerce()
{
// detect whether WooCommerce is installed & activated.
if (! class_exists('WooCommerce')) {
return;
}
echo '<div class="mc4wp-margin-m">';
echo '<h3>Advanced WooCommerce integration for Mailchimp</h3>';
echo '<p>';
echo __('Do you want to track all WooCommerce orders in Mailchimp so you can send emails based on the purchase activity of your subscribers?', 'mailchimp-for-wp');
echo '</p>';
echo '<p>';
echo sprintf(__('<a href="%1$s">Upgrade to Mailchimp for WordPress Premium</a> or <a href="%2$s">read more about Mailchimp\'s E-Commerce features</a>.', 'mailchimp-for-wp') . '</p>', 'https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=other-settings-link', 'https://www.mc4wp.com/kb/what-is-ecommerce360/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=other-settings-link');
echo '</p>';
echo '</div>';
}
public function show_extensions_page()
{
require MC4WP_PLUGIN_DIR . '/includes/views/extensions.php';
}
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* Class MC4WP_Admin_Review_Notice
*
* @ignore
*/
class MC4WP_Admin_Review_Notice
{
/**
* @var MC4WP_Admin_Tools
*/
protected $tools;
/**
* @var string
*/
protected $meta_key_dismissed = '_mc4wp_review_notice_dismissed';
/**
* MC4WP_Admin_Review_Notice constructor.
*
* @param MC4WP_Admin_Tools $tools
*/
public function __construct(MC4WP_Admin_Tools $tools)
{
$this->tools = $tools;
}
/**
* Add action & filter hooks.
*/
public function add_hooks()
{
add_action('admin_notices', array( $this, 'show' ));
add_action('mc4wp_admin_dismiss_review_notice', array( $this, 'dismiss' ));
}
/**
* Set flag in user meta so notice won't be shown.
*/
public function dismiss()
{
$user = wp_get_current_user();
update_user_meta($user->ID, $this->meta_key_dismissed, 1);
}
/**
* @return bool
*/
public function show()
{
// only show on Mailchimp for WordPress' pages.
if (! $this->tools->on_plugin_page()) {
return false;
}
// only show if 2 weeks have passed since first use.
$two_weeks_in_seconds = ( 60 * 60 * 24 * 14 );
if ($this->time_since_first_use() <= $two_weeks_in_seconds) {
return false;
}
// only show if user did not dismiss before
$user = wp_get_current_user();
if (get_user_meta($user->ID, $this->meta_key_dismissed, true)) {
return false;
}
echo '<div class="notice notice-info mc4wp-is-dismissible" id="mc4wp-review-notice">';
echo '<p>';
echo esc_html__('You\'ve been using Mailchimp for WordPress for some time now; we hope you love it!', 'mailchimp-for-wp'), ' <br />';
echo sprintf(wp_kses(__('If you do, please <a href="%s">leave us a 5★ rating on WordPress.org</a>. It would be of great help to us.', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), 'https://wordpress.org/support/view/plugin-reviews/mailchimp-for-wp?rate=5#new-post');
echo '</p>';
echo '<form method="POST" id="mc4wp-dismiss-review-form"><button type="submit" class="notice-dismiss"><span class="screen-reader-text">', esc_html__('Dismiss this notice.', 'mailchimp-for-wp'), '</span></button><input type="hidden" name="_mc4wp_action" value="dismiss_review_notice" />', wp_nonce_field('_mc4wp_action', '_wpnonce', true, false), '</form>';
echo '</div>';
return true;
}
/**
* @return int
*/
private function time_since_first_use()
{
$options = get_option('mc4wp', array());
if (! is_array($options)) {
$options = array();
}
// option was never added before, do it now.
if (empty($options['first_activated_on'])) {
$options['first_activated_on'] = time();
update_option('mc4wp', $options);
}
return time() - $options['first_activated_on'];
}
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* Class MC4WP_DB_Upgrader
*
* This class takes care of loading migration files from the specified migrations directory.
* Migration files should only use default WP functions and NOT use code which might not be there in the future.
*
* @ignore
*/
class MC4WP_Upgrade_Routines
{
/**
* @var float
*/
protected $version_from = 0;
/**
* @var float
*/
protected $version_to = 0;
/**
* @var string
*/
protected $migrations_dir = '';
/**
* @param float $from
* @param float $to
*/
public function __construct($from, $to, $migrations_dir)
{
$this->version_from = $from;
$this->version_to = $to;
$this->migrations_dir = $migrations_dir;
}
/**
* Run the various upgrade routines, all the way up to the latest version
*/
public function run()
{
$migrations = $this->find_migrations();
// run in sub-function for scope
array_map(array( $this, 'run_migration' ), $migrations);
}
/**
* @return array
*/
public function find_migrations()
{
$files = glob(rtrim($this->migrations_dir, '/') . '/*.php');
$migrations = array();
// return empty array when glob returns non-array value.
if (! is_array($files)) {
return $migrations;
}
foreach ($files as $file) {
$migration = basename($file);
$parts = explode('-', $migration);
$version = $parts[0];
if (version_compare($this->version_from, $version, '<')) {
$migrations[] = $file;
}
}
return $migrations;
}
/**
* Include a migration file and runs it.
*
* @param string $file
*/
protected function run_migration($file)
{
include $file;
}
}

View File

@@ -0,0 +1,88 @@
<?php
defined('ABSPATH') or exit;
// get options
$form_options = get_option('mc4wp_lite_form', array());
// bail if there are no previous options
if (empty($form_options)) {
return;
}
// bail if there are Pro forms already
$has_forms = get_posts(
array(
'post_type' => 'mc4wp-form',
'post_status' => 'publish',
'numberposts' => 1,
)
);
// There are forms already, don't continue.
if (! empty($has_forms)) {
// delete option as it apparently exists.
delete_option('mc4wp_lite_form');
return;
}
// create post type for form
$id = wp_insert_post(
array(
'post_type' => 'mc4wp-form',
'post_status' => 'publish',
'post_title' => __('Default sign-up form', 'mailchimp-for-wp'),
'post_content' => ( empty($form_options['markup']) ) ? '' : $form_options['markup'],
)
);
// set default_form_id
update_option('mc4wp_default_form_id', $id);
// set form settings
$setting_keys = array(
'css',
'custom_theme_color',
'double_optin',
'update_existing',
'replace_interests',
'send_welcome',
'redirect',
'hide_after_success',
);
$settings = array();
foreach ($setting_keys as $setting_key) {
// use isset to account for "0" settings
if (isset($form_options[ $setting_key ])) {
$settings[ $setting_key ] = $form_options[ $setting_key ];
}
}
// get only keys of lists setting
if (isset($form_options['lists'])) {
$settings['lists'] = array_keys($form_options['lists']);
}
update_post_meta($id, '_mc4wp_settings', $settings);
// set form message texts
$message_keys = array(
'text_subscribed',
'text_error',
'text_invalid_email',
'text_already_subscribed',
'text_required_field_missing',
'text_unsubscribed',
'text_not_subscribed',
);
foreach ($message_keys as $message_key) {
if (! empty($form_options[ $message_key ])) {
update_post_meta($id, $message_key, $form_options[ $message_key ]);
}
}
// delete old option
delete_option('mc4wp_lite_form');

View File

@@ -0,0 +1,65 @@
<?php
defined('ABSPATH') or exit;
$global_options = (array) get_option('mc4wp_form', array());
// find all form posts
$posts = get_posts(
array(
'post_type' => 'mc4wp-form',
'post_status' => 'publish',
'numberposts' => -1,
)
);
$css_map = array(
'default' => 'basic',
'custom' => 'styles-builder',
'light' => 'theme-light',
'dark' => 'theme-dark',
'red' => 'theme-red',
'green' => 'theme-green',
'blue' => 'theme-blue',
'custom-color' => 'theme-custom-color',
);
$stylesheets = array();
foreach ($posts as $post) {
// get form options from post meta directly
$options = (array) get_post_meta($post->ID, '_mc4wp_settings', true);
// store all global options in scoped form settings
// do this BEFORE changing css key, so we take that as well.
foreach ($global_options as $key => $value) {
if (strlen($value) > 0 && ( ! isset($options[ $key ]) || strlen($options[ $key ]) == 0 )) {
$options[ $key ] = $value;
}
}
// update "css" option value
if (isset($options['css']) && isset($css_map[ $options['css'] ])) {
$options['css'] = $css_map[ $options['css'] ];
}
// create stylesheets option
if (! empty($options['css'])) {
$stylesheet = $options['css'];
if (strpos($stylesheet, 'theme-') === 0) {
$stylesheet = 'themes';
}
if (! in_array($stylesheet, $stylesheets)) {
$stylesheets[] = $stylesheet;
}
}
update_post_meta($post->ID, '_mc4wp_settings', $options);
}
// update stylesheets option
update_option('mc4wp_form_stylesheets', $stylesheets);
// delete old options
delete_option('mc4wp_form');

View File

@@ -0,0 +1,42 @@
<?php
defined('ABSPATH') or exit;
// find all form posts
$posts = get_posts(
array(
'post_type' => 'mc4wp-form',
'post_status' => 'publish',
'numberposts' => -1,
)
);
// set form message texts
$message_keys = array(
'text_subscribed',
'text_error',
'text_invalid_email',
'text_already_subscribed',
'text_required_field_missing',
'text_unsubscribed',
'text_not_subscribed',
);
foreach ($posts as $post) {
$settings = get_post_meta($post->ID, '_mc4wp_settings', true);
foreach ($message_keys as $key) {
if (empty($settings[ $key ])) {
continue;
}
$message = $settings[ $key ];
// move message setting over to post meta
update_post_meta($post->ID, $key, $message);
unset($settings[ $key ]);
}
// update post meta with unset message keys
update_post_meta($post->ID, '_mc4wp_settings', $settings);
}

View File

@@ -0,0 +1,16 @@
<?php
defined('ABSPATH') or exit;
// transfer option
$options = (array) get_option('mc4wp_lite', array());
// merge options, with Pro options taking precedence
$pro_options = (array) get_option('mc4wp', array());
$options = array_merge($options, $pro_options);
// update options
update_option('mc4wp', $options);
// delete old option
delete_option('mc4wp_lite');

View File

@@ -0,0 +1,64 @@
<?php
defined('ABSPATH') or exit;
$old_options = get_option('mc4wp_lite_checkbox', array());
$pro_options = get_option('mc4wp_checkbox', array());
if (! empty($pro_options)) {
$old_options = array_merge($old_options, $pro_options);
}
// do we have to do something?
if (empty($old_options)) {
return;
}
// find activated integrations (show_at_xxx options)
$new_options = array();
$map = array(
'comment_form' => 'wp-comment-form',
'registration_form' => 'wp-registration-form',
'buddypress_form' => 'buddypress',
'bbpres_forms' => 'bbpress',
'woocommerce_checkout' => 'woocommerce',
'edd_checkout' => 'easy-digital-downloads',
);
$option_keys = array(
'label',
'precheck',
'css',
'lists',
'double_optin',
'update_existing',
'replace_interests',
'send_welcome',
);
foreach ($map as $old_integration_slug => $new_integration_slug) {
// check if integration is enabled using its old slug
$show_key = sprintf('show_at_%s', $old_integration_slug);
if (empty($old_options[ $show_key ])) {
continue;
}
$options = array(
'enabled' => 1,
);
foreach ($option_keys as $option_key) {
if (isset($old_options[ $option_key ])) {
$options[ $option_key ] = $old_options[ $option_key ];
}
}
// add to new options
$new_options[ $new_integration_slug ] = $options;
}
// save new settings
update_option('mc4wp_integrations', $new_options);
// delete old options
delete_option('mc4wp_lite_checkbox');
delete_option('mc4wp_checkbox');

View File

@@ -0,0 +1,41 @@
<?php
defined('ABSPATH') or exit;
// move stylebuilders file to bundle
$file = (string) get_option('mc4wp_custom_css_file', '');
if (empty($file)) {
return;
}
$uploads = wp_upload_dir();
// figure out absolute file path
$prefix = str_replace('http:', '', $uploads['baseurl']);
$relative_path = str_replace($prefix, '', $file);
// get part before ?
if (strpos($relative_path, '?') !== false) {
$parts = explode('?', $relative_path);
$relative_path = array_shift($parts);
}
// This is the absolute path to the file, he he..
$file = $uploads['basedir'] . $relative_path;
if (file_exists($file)) {
// create directory, if necessary
$dir = $uploads['basedir'] . '/mc4wp-stylesheets';
if (! file_exists($dir)) {
@mkdir($dir, 0755);
}
@chmod($dir, 0755);
// Move file to new location
$new_file = $dir . '/bundle.css';
$success = rename($file, $new_file);
}
// remove old option
delete_option('mc4wp_custom_css_file');

View File

@@ -0,0 +1,39 @@
<?php
defined('ABSPATH') or exit;
$section_widgets = get_option('sidebars_widgets', array());
$replaced = false;
foreach ($section_widgets as $section => $widgets) {
// WP has an "array_version" key that is not an array...
if (! is_array($widgets)) {
continue;
}
// loop through widget ID's
foreach ($widgets as $key => $widget_id) {
// does this widget ID start with "mc4wp_widget"?
if (strpos($widget_id, 'mc4wp_widget') === 0) {
// replace "mc4wp_widget" with "mc4wp_form_widget"
$new_widget_id = str_replace('mc4wp_widget', 'mc4wp_form_widget', $widget_id);
$section_widgets[ $section ][ $key ] = $new_widget_id;
$replaced = true;
}
}
}
// update option if we made changes
if ($replaced) {
update_option('sidebars_widgets', $section_widgets);
}
// update widget options
$options = get_option('widget_mc4wp_widget', false);
if ($options) {
update_option('widget_mc4wp_form_widget', $options);
// delete old option
delete_option('widget_mc4wp_widget');
}

View File

@@ -0,0 +1,11 @@
<?php
defined('ABSPATH') or exit;
$options = get_option('mc4wp_integrations', array());
if (! empty($options['woocommerce']) && ! empty($options['woocommerce']['position'])) {
$options['woocommerce']['position'] = sprintf('checkout_%s', $options['woocommerce']['position']);
}
update_option('mc4wp_integrations', $options);

View File

@@ -0,0 +1,112 @@
<?php
defined('ABSPATH') or exit;
/**
* @ignore
* @return object
*/
function _mc4wp_400_find_grouping_for_interest_category($groupings, $interest_category)
{
foreach ($groupings as $grouping) {
// cast to stdClass because of missing class
$grouping = (object) (array) $grouping;
if ($grouping->name === $interest_category->title) {
return $grouping;
}
}
return null;
}
/**
* @ignore
* @return object
*/
function _mc4wp_400_find_group_for_interest($groups, $interest)
{
foreach ($groups as $group_id => $group_name) {
if ($group_name === $interest->name) {
return (object) array(
'name' => $group_name,
'id' => $group_id,
);
}
}
return null;
}
// in case the migration is _very_ late to the party
if (! class_exists('MC4WP_API_V3')) {
return;
}
$options = get_option('mc4wp', array());
if (empty($options['api_key'])) {
return;
}
// get current state from transient
$lists = get_transient('mc4wp_mailchimp_lists_fallback');
if (empty($lists)) {
return;
}
@set_time_limit(600);
$api_v3 = new MC4WP_API_V3($options['api_key']);
$map = array();
foreach ($lists as $list) {
// cast to stdClass because of missing classes
$list = (object) (array) $list;
// no groupings? easy!
if (empty($list->groupings)) {
continue;
}
// fetch (new) interest categories for this list
try {
$interest_categories = $api_v3->get_list_interest_categories($list->id);
} catch (MC4WP_API_Exception $e) {
continue;
}
foreach ($interest_categories as $interest_category) {
// compare interest title with grouping name, if it matches, get new id.
$grouping = _mc4wp_400_find_grouping_for_interest_category($list->groupings, $interest_category);
if (! $grouping) {
continue;
}
$groups = array();
try {
$interests = $api_v3->get_list_interest_category_interests($list->id, $interest_category->id);
} catch (MC4WP_API_Exception $e) {
continue;
}
foreach ($interests as $interest) {
$group = _mc4wp_400_find_group_for_interest($grouping->groups, $interest);
if ($group) {
$groups[ $group->id ] = $interest->id;
$groups[ $group->name ] = $interest->id;
}
}
$map[ (string) $grouping->id ] = array(
'id' => $interest_category->id,
'groups' => $groups,
);
}
}
if (! empty($map)) {
update_option('mc4wp_groupings_map', $map);
}

View File

@@ -0,0 +1,31 @@
<?php
defined('ABSPATH') or exit;
/** @ignore */
function _mc4wp_400_replace_comma_with_pipe($matches)
{
$old = $matches[1];
$new = str_replace(',', '|', $old);
return str_replace($old, $new, $matches[0]);
}
// get all forms
$posts = get_posts(
array(
'post_type' => 'mc4wp-form',
'numberposts' => -1,
)
);
foreach ($posts as $post) {
// find hidden field values in form and pass through replace function
$old = $post->post_content;
$new = preg_replace_callback('/type="hidden" .* value="(.*)"/i', '_mc4wp_400_replace_comma_with_pipe', $old);
// update post if we replaced something
if ($new != $old) {
$post->post_content = $new;
wp_update_post($post);
}
}

View File

@@ -0,0 +1,33 @@
<?php
defined('ABSPATH') or exit;
// get old log filename
$upload_dir = wp_upload_dir(null, false);
$old_filename = trailingslashit($upload_dir['basedir']) . 'mc4wp-debug.log';
$new_filename = trailingslashit($upload_dir['basedir']) . 'mc4wp-debug-log.php';
// check if old default log file exists
if (! file_exists($old_filename)) {
return;
}
// rename to new file.
@rename($old_filename, $new_filename);
// if success, insert php exit tag as first line
if (file_exists($new_filename)) {
$handle = fopen($new_filename, 'r+');
if (is_resource($handle)) {
// make sure first line of log file is a PHP tag + exit statement (to prevent direct file access)
$line = fgets($handle);
$php_exit_string = '<?php exit; ?>';
if (strpos($line, $php_exit_string) !== 0) {
rewind($handle);
fwrite($handle, $php_exit_string . PHP_EOL . $line);
}
fclose($handle);
}
}

View File

@@ -0,0 +1,12 @@
<?php
defined('ABSPATH') or exit;
if (function_exists('mc4wp_refresh_mailchimp_lists')) {
mc4wp_refresh_mailchimp_lists();
}
delete_transient('mc4wp_mailchimp_lists_v3');
delete_option('mc4wp_mailchimp_lists_v3_fallback');
wp_schedule_event(strtotime('tomorrow 3 am'), 'daily', 'mc4wp_refresh_mailchimp_lists');

View File

@@ -0,0 +1,8 @@
<?php
defined('ABSPATH') or exit;
wp_clear_scheduled_hook('mc4wp_refresh_mailchimp_lists');
$time_string = sprintf('tomorrow %d:%d%d am', rand(1, 6), rand(0, 5), rand(0, 9));
wp_schedule_event(strtotime($time_string), 'daily', 'mc4wp_refresh_mailchimp_lists');

View File

@@ -0,0 +1,4 @@
<?php
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'mc4wp_mailchimp_list_%'");

View File

@@ -0,0 +1,18 @@
<?php
defined('ABSPATH') or exit;
// get old filename
$upload_dir = wp_upload_dir(null, false);
$old_filename = trailingslashit($upload_dir['basedir']) . 'mc4wp-debug-log.php';
// if old file exists, move it to new location
if (is_file($old_filename)) {
$new_filename = $upload_dir['basedir'] . '/mailchimp-for-wp/debug-log.php';
$dir = dirname($new_filename);
if (! is_dir($dir)) {
mkdir($dir, 0755, true);
}
rename($old_filename, $new_filename);
}

View File

@@ -0,0 +1,253 @@
<?php
class MC4WP_API_V3_Client
{
/**
* @var string
*/
private $api_key;
/**
* @var string
*/
private $api_url = 'https://api.mailchimp.com/3.0/';
/**
* @var array
*/
private $last_response;
/**
* @var array
*/
private $last_request;
/**
* Constructor
*
* @param string $api_key
*/
public function __construct($api_key)
{
$this->api_key = $api_key;
$dash_position = strpos($api_key, '-');
if ($dash_position !== false) {
$this->api_url = str_replace('//api.', '//' . substr($api_key, $dash_position + 1) . '.api.', $this->api_url);
}
}
/**
* @param string $resource
* @param array $args
*
* @return mixed
* @throws MC4WP_API_Exception
*/
public function get($resource, array $args = array())
{
return $this->request('GET', $resource, $args);
}
/**
* @param string $resource
* @param array $data
*
* @return mixed
* @throws MC4WP_API_Exception
*/
public function post($resource, array $data)
{
return $this->request('POST', $resource, $data);
}
/**
* @param string $resource
* @param array $data
* @return mixed
* @throws MC4WP_API_Exception
*/
public function put($resource, array $data)
{
return $this->request('PUT', $resource, $data);
}
/**
* @param string $resource
* @param array $data
* @return mixed
* @throws MC4WP_API_Exception
*/
public function patch($resource, array $data)
{
return $this->request('PATCH', $resource, $data);
}
/**
* @param string $resource
* @return mixed
* @throws MC4WP_API_Exception
*/
public function delete($resource)
{
return $this->request('DELETE', $resource);
}
/**
* @param string $method
* @param string $resource
* @param array $data
*
* @return mixed
*
* @throws MC4WP_API_Exception
*/
private function request($method, $resource, array $data = array())
{
$this->reset();
// don't bother if no API key was given.
if (empty($this->api_key)) {
throw new MC4WP_API_Exception('Missing API key', 001);
}
$method = strtoupper(trim($method));
$url = $this->api_url . ltrim($resource, '/');
$args = array(
'method' => $method,
'headers' => $this->get_headers(),
'timeout' => 20,
'sslverify' => apply_filters('mc4wp_use_sslverify', true),
);
if (! empty($data)) {
if (in_array($method, array( 'GET', 'DELETE' ), true)) {
$url = add_query_arg($data, $url);
} else {
$args['headers']['Content-Type'] = 'application/json';
$args['body'] = json_encode($data);
}
}
/**
* Filter the request arguments for all requests generated by this class
*
* @param array $args
*/
$args = apply_filters('mc4wp_http_request_args', $args, $url);
// perform request
$response = wp_remote_request($url, $args);
// store request & response
$args['url'] = $url;
$this->last_request = $args;
$this->last_response = $response;
// parse response
$data = $this->parse_response($response);
return $data;
}
/**
* @return array
*/
private function get_headers()
{
global $wp_version;
$headers = array(
'Authorization' => sprintf('Basic %s', base64_encode('mc4wp:' . $this->api_key)),
'User-Agent' => sprintf('mc4wp/%s; WordPress/%s; %s', MC4WP_VERSION, $wp_version, home_url()),
);
// Copy Accept-Language from browser headers
if (! empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$headers['Accept-Language'] = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
}
return $headers;
}
/**
* @param array|WP_Error $response
*
* @return mixed
*
* @throws MC4WP_API_Connection_Exception|MC4WP_API_Resource_Not_Found_Exception|MC4WP_API_Exception
*/
private function parse_response($response)
{
if ($response instanceof WP_Error) {
throw new MC4WP_API_Connection_Exception($response->get_error_message(), (int) $response->get_error_code(), $this->last_request);
}
// decode response body
$code = (int) wp_remote_retrieve_response_code($response);
$message = wp_remote_retrieve_response_message($response);
$body = wp_remote_retrieve_body($response);
// set body to "true" in case Mailchimp returned No Content
if ($code < 300 && empty($body)) {
$body = 'true';
}
$data = json_decode($body);
if ($code >= 400) {
// check for akamai errors
// {"type":"akamai_error_message","title":"akamai_503","status":503,"ref_no":"Reference Number: 00.950e16c3.1498559813.1450dbe2"}
if (is_object($data) && isset($data->type) && $data->type === 'akamai_error_message') {
throw new MC4WP_API_Connection_Exception($message, $code, $this->last_request, $this->last_response, $data);
}
if ($code === 404) {
throw new MC4WP_API_Resource_Not_Found_Exception($message, $code, $this->last_request, $this->last_response, $data);
}
// mailchimp returned an error..
throw new MC4WP_API_Exception($message, $code, $this->last_request, $this->last_response, $data);
}
// throw exception if unable to decode response
if ($data === null) {
throw new MC4WP_API_Exception($message, $code, $this->last_request, $this->last_response);
}
return $data;
}
/**
* Empties all data from previous response
*/
private function reset()
{
$this->last_response = null;
$this->last_request = null;
}
/**
* @return string
*/
public function get_last_response_body()
{
return wp_remote_retrieve_body($this->last_response);
}
/**
* @return array
*/
public function get_last_response_headers()
{
return wp_remote_retrieve_headers($this->last_response);
}
/**
* @return array|WP_Error
*/
public function get_last_response()
{
return $this->last_response;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
<?php
class MC4WP_API_Connection_Exception extends MC4WP_API_Exception
{
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* Class MC4WP_API_Exception
*
* @property string $title
* @property string $detail
* @property array $errors
*/
class MC4WP_API_Exception extends Exception
{
/**
* @var object
*/
public $response = array();
/**
* @var object
*/
public $request = array();
/**
* @var array
*/
public $response_data = array();
/**
* MC4WP_API_Exception constructor.
*
* @param string $message
* @param int $code
* @param array $request
* @param array $response
* @param object $data
*/
public function __construct($message, $code, $request = null, $response = null, $data = null)
{
parent::__construct($message, $code);
$this->request = $request;
$this->response = $response;
$this->response_data = $data;
}
/**
* Backwards compatibility for direct property access.
* @param string $property
* @return mixed
*/
public function __get($property)
{
if (in_array($property, array( 'title', 'detail', 'errors' ), true)) {
if (! empty($this->response_data) && isset($this->response_data->{$property})) {
return $this->response_data->{$property};
}
return '';
}
}
/**
* @return string
*/
public function __toString()
{
$string = $this->message . '.';
// add errors from response data returned by Mailchimp
if (! empty($this->response_data)) {
if (! empty($this->response_data->title) && $this->response_data->title !== $this->getMessage()) {
$string .= ' ' . $this->response_data->title . '.';
}
// add detail message
if (! empty($this->response_data->detail)) {
$string .= ' ' . $this->response_data->detail;
}
// add field specific errors
if (! empty($this->response_data->errors) && isset($this->response_data->errors[0]->field)) {
// strip off obsolete msg
$string = str_replace('For field-specific details, see the \'errors\' array.', '', $string);
// generate list of field errors
$field_errors = array();
foreach ($this->response_data->errors as $error) {
if (! empty($error->field)) {
$field_errors[] = sprintf('- %s : %s', $error->field, $error->message);
} else {
$field_errors[] = sprintf('- %s', $error->message);
}
}
$string .= " \n" . join("\n", $field_errors);
}
}
// Add request data
if (! empty($this->request) && is_array($this->request)) {
$string .= "\n\n" . sprintf("Request: \n%s %s\n", $this->request['method'], $this->request['url']);
// foreach ( $this->request['headers'] as $key => $value ) {
// $string .= sprintf( "%s: %s\n", $key, $value );
// }
if (! empty($this->request['body'])) {
$string .= "\n" . $this->request['body'];
}
}
// Add response data
if (! empty($this->response) && is_array($this->response)) {
$response_code = wp_remote_retrieve_response_code($this->response);
$response_message = wp_remote_retrieve_response_message($this->response);
$response_body = wp_remote_retrieve_body($this->response);
$string .= "\n\n" . sprintf("Response: \n%d %s\n%s", $response_code, $response_message, $response_body);
}
return $string;
}
}

View File

@@ -0,0 +1,6 @@
<?php
class MC4WP_API_Resource_Not_Found_Exception extends MC4WP_API_Exception
{
// Thrown when a requested resource does not exist in Mailchimp
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* Class MC4WP_Service_Container
*
* @access private
* @ignore
*/
class MC4WP_Container implements ArrayAccess
{
/**
* @var array
*/
protected $services = array();
/**
* @var array
*/
protected $resolved_services = array();
/**
* @param string $name
* @return boolean
*/
public function has($name)
{
return isset($this->services[ $name ]);
}
/**
* @param string $name
*
* @return mixed
* @throws Exception
*/
public function get($name)
{
if (! $this->has($name)) {
throw new Exception(sprintf('No service named %s was registered.', $name));
}
$service = $this->services[ $name ];
// is this a resolvable service?
if (is_callable($service)) {
// resolve service if it's not resolved yet
if (! isset($this->resolved_services[ $name ])) {
$this->resolved_services[ $name ] = call_user_func($service);
}
return $this->resolved_services[ $name ];
}
return $this->services[ $name ];
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Whether a offset exists
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset <p>
* An offset to check for.
* </p>
*
* @return boolean true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
*/
#[\ReturnTypeWillChange]
public function offsetExists($offset)
{
return $this->has($offset);
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Offset to retrieve
* @link http://php.net/manual/en/arrayaccess.offsetget.php
*
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
*
* @return mixed Can return all value types.
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Offset to set
* @link http://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
*
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->services[ $offset ] = $value;
}
/**
* (PHP 5 &gt;= 5.0.0)<br/>
* Offset to unset
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset <p>
* The offset to unset.
* </p>
*
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->services[ $offset ]);
}
}

View File

@@ -0,0 +1,160 @@
<?php
/**
* Class MC4WP_Debug_Log_Reader
*/
class MC4WP_Debug_Log_Reader
{
/**
* @var resource|null
*/
private $handle;
/**
* @var string
*/
private static $regex = '/^(\[[\d \-\:]+\]) (\w+\:) (.*)$/S';
/**
* @var string
*/
private static $html_template = '<span class="time">$1</span> <span class="level">$2</span> <span class="message">$3</span>';
/**
* @var string The log file location.
*/
private $file;
/**
* MC4WP_Debug_Log_Reader constructor.
*
* @param $file
*/
public function __construct($file)
{
$this->file = $file;
}
/**
* @return string
*/
public function all()
{
return file_get_contents($this->file);
}
/**
* Sets file pointer to $n of lines from the end of file.
*
* @param int $n
*/
private function seek_line_from_end($n)
{
$line_count = 0;
// get line count
while (! feof($this->handle)) {
fgets($this->handle);
++$line_count;
}
// rewind to beginning
rewind($this->handle);
// calculate target
$target = $line_count - $n;
$target = $target > 1 ? $target : 1; // always skip first line because oh PHP header
$current = 0;
// keep reading until we're at target
while ($current < $target) {
fgets($this->handle);
++$current;
}
}
/**
* @return string|null
*/
public function read()
{
// open file if not yet opened
if (! is_resource($this->handle)) {
// doesn't exist?
if (! file_exists($this->file)) {
return null;
}
$this->handle = @fopen($this->file, 'r');
// unable to read?
if (! is_resource($this->handle)) {
return null;
}
// set pointer to 1000 files from EOF
$this->seek_line_from_end(1000);
}
// stop reading once we're at the end
if (feof($this->handle)) {
fclose($this->handle);
$this->handle = null;
return null;
}
// read line, up to 8kb
$text = fgets($this->handle);
// strip tags & trim
$text = strip_tags($text);
$text = trim($text);
return $text;
}
/**
* @return string
*/
public function read_as_html()
{
$line = $this->read();
// null means end of file
if (is_null($line)) {
return null;
}
// empty string means empty line, but not yet eof
if (empty($line)) {
return '';
}
$line = preg_replace(self::$regex, self::$html_template, $line);
return $line;
}
/**
* Reads X number of lines.
*
* If $start is negative, reads from end of log file.
*
* @param int $start
* @param int $number
* @return string
*/
public function lines($start, $number)
{
$handle = fopen($start, 'r');
$lines = '';
$current_line = 0;
while ($current_line < $number) {
$lines .= fgets($handle);
}
fclose($handle);
return $lines;
}
}

View File

@@ -0,0 +1,233 @@
<?php
/**
* Class MC4WP_Debug_Log
*
* Simple logging class which writes to a file, loosely based on PSR-3.
*/
class MC4WP_Debug_Log
{
/**
* Detailed debug information
*/
const DEBUG = 100;
/**
* Interesting events
*
* Examples: Visitor subscribed
*/
const INFO = 200;
/**
* Exceptional occurrences that are not errors
*
* Examples: User already subscribed
*/
const WARNING = 300;
/**
* Runtime errors
*/
const ERROR = 400;
/**
* Logging levels from syslog protocol defined in RFC 5424
*
* @var array $levels Logging levels
*/
protected static $levels = array(
self::DEBUG => 'DEBUG',
self::INFO => 'INFO',
self::WARNING => 'WARNING',
self::ERROR => 'ERROR',
);
/**
* @var string The file to which messages should be written.
*/
public $file;
/**
* @var int Only write messages with this level or higher
*/
public $level;
/**
* @var resource
*/
protected $stream;
/**
* MC4WP_Debug_Log constructor.
*
* @param string $file
* @param mixed $level;
*/
public function __construct($file, $level = self::DEBUG)
{
$this->file = $file;
$this->level = self::to_level($level);
}
/**
* @param mixed $level
* @param string $message
* @return boolean
*/
public function log($level, $message)
{
$level = self::to_level($level);
// only log if message level is higher than log level
if ($level < $this->level) {
return false;
}
// obfuscate email addresses in log message since log might be public.
$message = mc4wp_obfuscate_email_addresses((string) $message);
// first, get rid of everything between "invisible" tags
$message = preg_replace('/<(?:style|script|head)>.+?<\/(?:style|script|head)>/is', '', $message);
// then, strip tags (while retaining content of these tags)
$message = strip_tags($message);
$message = trim($message);
/**
* Modifies the message that is written to the debug log.
* Return an empty string to skip logging this message altogether.
*
* @param string $message
*/
$message = apply_filters('mc4wp_debug_log_message', $message);
if (empty($message)) {
return false;
}
// generate line
$level_name = self::get_level_name($level);
$datetime = gmdate('Y-m-d H:i:s', time() + ( get_option('gmt_offset', 0) * HOUR_IN_SECONDS ));
$message = sprintf('[%s] %s: %s', $datetime, $level_name, $message) . PHP_EOL;
// did we open stream yet?
if (! is_resource($this->stream)) {
// attempt to open stream
$this->stream = @fopen($this->file, 'c+');
if (! is_resource($this->stream)) {
return false;
}
// make sure first line of log file is a PHP tag + exit statement (to prevent direct file access)
$line = fgets($this->stream);
$php_exit_string = '<?php exit; ?>';
if (strpos($line, $php_exit_string) !== 0) {
rewind($this->stream);
fwrite($this->stream, $php_exit_string . PHP_EOL . $line);
}
// place pointer at end of file
fseek($this->stream, 0, SEEK_END);
}
// lock file while we write, ignore errors (not much we can do)
flock($this->stream, LOCK_EX);
// write the message to the file
fwrite($this->stream, $message);
// unlock file again, but don't close it for remainder of this request
flock($this->stream, LOCK_UN);
return true;
}
/**
* @param string $message
* @return boolean
*/
public function warning($message)
{
return $this->log(self::WARNING, $message);
}
/**
* @param string $message
* @return boolean
*/
public function info($message)
{
return $this->log(self::INFO, $message);
}
/**
* @param string $message
* @return boolean
*/
public function error($message)
{
return $this->log(self::ERROR, $message);
}
/**
* @param string $message
* @return boolean
*/
public function debug($message)
{
return $this->log(self::DEBUG, $message);
}
/**
* Converts PSR-3 levels to local ones if necessary
*
* @param string|int Level number or name (PSR-3)
* @return int
*/
public static function to_level($level)
{
if (is_string($level)) {
$level = strtoupper($level);
if (defined(__CLASS__ . '::' . $level)) {
return constant(__CLASS__ . '::' . $level);
}
throw new InvalidArgumentException('Level "' . $level . '" is not defined, use one of: ' . implode(', ', array_keys(self::$levels)));
}
return $level;
}
/**
* Gets the name of the logging level.
*
* @param int $level
* @return string
*/
public static function get_level_name($level)
{
if (! isset(self::$levels[ $level ])) {
throw new InvalidArgumentException('Level "' . $level . '" is not defined, use one of: ' . implode(', ', array_keys(self::$levels)));
}
return self::$levels[ $level ];
}
/**
* Tests if the log file is writable
*
* @return bool
*/
public function test()
{
$handle = @fopen($this->file, 'a');
$writable = false;
if (is_resource($handle)) {
$writable = true;
fclose($handle);
}
return $writable;
}
}

View File

@@ -0,0 +1,260 @@
<?php
/**
* Class MC4WP_Dynamic_Content_Tags
*
* @access private
* @ignore
*/
abstract class MC4WP_Dynamic_Content_Tags
{
/**
* @var string The escape function for replacement values.
*/
protected $escape_function = null;
/**
* @var array Array of registered dynamic content tags
*/
protected $tags = array();
/**
* Register template tags
*/
protected function register()
{
// Global tags can go here
$this->tags['cookie'] = array(
'description' => sprintf(__('Data from a cookie.', 'mailchimp-for-wp')),
'callback' => array( $this, 'get_cookie' ),
'example' => "cookie name='my_cookie' default='Default Value'",
);
$this->tags['email'] = array(
'description' => __('The email address of the current visitor (if known).', 'mailchimp-for-wp'),
'callback' => array( $this, 'get_email' ),
);
$this->tags['current_url'] = array(
'description' => __('The URL of the page.', 'mailchimp-for-wp'),
'callback' => 'mc4wp_get_request_url',
);
$this->tags['current_path'] = array(
'description' => __('The path of the page.', 'mailchimp-for-wp'),
'callback' => 'mc4wp_get_request_path',
);
$this->tags['date'] = array(
'description' => sprintf(__('The current date. Example: %s.', 'mailchimp-for-wp'), '<strong>' . gmdate('Y/m/d', time() + ( get_option('gmt_offset') * HOUR_IN_SECONDS )) . '</strong>'),
'replacement' => gmdate('Y/m/d', time() + ( get_option('gmt_offset') * HOUR_IN_SECONDS )),
);
$this->tags['time'] = array(
'description' => sprintf(__('The current time. Example: %s.', 'mailchimp-for-wp'), '<strong>' . gmdate('H:i:s', time() + ( get_option('gmt_offset') * HOUR_IN_SECONDS )) . '</strong>'),
'replacement' => gmdate('H:i:s', time() + ( get_option('gmt_offset') * HOUR_IN_SECONDS )),
);
$this->tags['language'] = array(
'description' => sprintf(__('The site\'s language. Example: %s.', 'mailchimp-for-wp'), '<strong>' . get_locale() . '</strong>'),
'callback' => 'get_locale',
);
$this->tags['ip'] = array(
'description' => sprintf(__('The visitor\'s IP address. Example: %s.', 'mailchimp-for-wp'), '<strong>' . mc4wp_get_request_ip_address() . '</strong>'),
'callback' => 'mc4wp_get_request_ip_address',
);
$this->tags['user'] = array(
'description' => sprintf(__('The property of the currently logged-in user.', 'mailchimp-for-wp')),
'callback' => array( $this, 'get_user_property' ),
'example' => "user property='user_email'",
);
$this->tags['post'] = array(
'description' => sprintf(__('Property of the current page or post.', 'mailchimp-for-wp')),
'callback' => array( $this, 'get_post_property' ),
'example' => "post property='ID'",
);
}
/**
* @return array
*/
public function all()
{
if ($this->tags === array()) {
$this->register();
}
return $this->tags;
}
/**
* @param array $matches
*
* @return string
*/
protected function replace_tag(array $matches)
{
$tags = $this->all();
$tag = $matches[1];
if (isset($tags[ $tag ])) {
$config = $tags[ $tag ];
$replacement = '';
if (isset($config['replacement'])) {
$replacement = $config['replacement'];
} elseif (isset($config['callback'])) {
// parse attributes
$attributes = array();
if (isset($matches[2])) {
$attribute_string = $matches[2];
$attributes = shortcode_parse_atts($attribute_string);
}
// call function
$replacement = call_user_func($config['callback'], $attributes);
}
if (is_callable($this->escape_function)) {
$replacement = call_user_func($this->escape_function, $replacement);
}
return $replacement;
}
// default to not replacing it
return $matches[0];
}
/**
* @param string $string The string containing dynamic content tags.
* @param string $escape_function Escape mode for the replacement value. Leave empty for no escaping.
* @return string
*/
protected function replace($string, $escape_function = '')
{
$this->escape_function = $escape_function;
// replace strings like this: {tagname attr="value"}
$string = preg_replace_callback('/\{(\w+)(\ +(?:(?!\{)[^}\n])+)*\}/', array( $this, 'replace_tag' ), $string);
// call again to take care of nested variables
$string = preg_replace_callback('/\{(\w+)(\ +(?:(?!\{)[^}\n])+)*\}/', array( $this, 'replace_tag' ), $string);
return $string;
}
/**
* @param string $string
*
* @return string
*/
protected function replace_in_html($string)
{
return $this->replace($string, 'esc_html');
}
/**
* @param string $string
*
* @return string
*/
protected function replace_in_attributes($string)
{
return $this->replace($string, 'esc_attr');
}
/**
* @param string $string
*
* @return string
*/
protected function replace_in_url($string)
{
return $this->replace($string, 'urlencode');
}
/**
* Gets data variable from cookie.
*
* @param array $args
*
* @return string
*/
protected function get_cookie($args = array())
{
if (empty($args['name'])) {
return '';
}
$name = $args['name'];
$default = isset($args['default']) ? $args['default'] : '';
if (isset($_COOKIE[ $name ])) {
return esc_html(stripslashes($_COOKIE[ $name ]));
}
return $default;
}
/*
* Get property of currently logged-in user
*
* @param array $args
*
* @return string
*/
protected function get_user_property($args = array())
{
$property = empty($args['property']) ? 'user_email' : $args['property'];
$default = isset($args['default']) ? $args['default'] : '';
$user = wp_get_current_user();
if ($user instanceof WP_User && isset($user->{$property})) {
return esc_html($user->{$property});
}
return $default;
}
/*
* Get property of viewed post
*
* @param array $args
*
* @return string
*/
protected function get_post_property($args = array())
{
global $post;
$property = empty($args['property']) ? 'ID' : $args['property'];
$default = isset($args['default']) ? $args['default'] : '';
if ($post instanceof WP_Post && isset($post->{$property})) {
return $post->{$property};
}
return $default;
}
/**
* @return string
*/
protected function get_email()
{
if (! empty($_REQUEST['EMAIL'])) {
return $_REQUEST['EMAIL'];
}
// then , try logged-in user
if (is_user_logged_in()) {
$user = wp_get_current_user();
return $user->user_email;
}
// TODO: Read from cookie? Or add $_COOKIE support to {data} tag?
return '';
}
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* Class MC4WP_Field_Formatter
*
* Formats values based on what the Mailchimp API expects or accepts for the given field types.
*/
class MC4WP_Field_Formatter
{
/**
* @param mixed $value
* @param object $options
* @return array
*/
public function address($value, $options = null)
{
// auto-format if this is a string
if (is_string($value)) {
// addr1, addr2, city, state, zip, country
$address_pieces = explode(',', $value);
$address_pieces = array_filter($address_pieces);
$address_pieces = array_values($address_pieces);
// try to fill it.... this is a long shot
$value = array(
'addr1' => $address_pieces[0],
'city' => isset($address_pieces[1]) ? $address_pieces[1] : '',
'state' => isset($address_pieces[2]) ? $address_pieces[2] : '',
'zip' => isset($address_pieces[3]) ? $address_pieces[3] : '',
);
if (! empty($address_pieces[4])) {
$value['country'] = $address_pieces[4];
}
} elseif (is_array($value)) {
// merge with array of empty defaults to allow skipping certain fields
$default = array_fill_keys(array( 'addr1', 'city', 'state', 'zip' ), '');
$value = array_merge($default, $value);
}
return $value;
}
/**
* @param mixed $value
* @param object $options
* @return string
*/
public function birthday($value, $options = null)
{
$format = is_object($options) && isset($options->date_format) ? $options->date_format : 'MM/DD';
if (is_array($value)) {
// allow for "day" and "month" fields
if (isset($value['month']) && isset($value['day'])) {
$value = $value['month'] . '/' . $value['day'];
} else {
// if other array, just join together
$value = join('/', $value);
}
}
$value = trim($value);
if (empty($value)) {
return $value;
}
// always use slashes as delimiter, so next part works
$value = str_replace(array( '.', '-' ), '/', $value);
// if format = DD/MM OR if first part is definitely a day value (>12), then flip order
// this allows `strtotime` to understand `dd/mm` values
$values = explode('/', $value);
if ($format === 'DD/MM' || ( $values[0] > 12 && $values[0] <= 31 && isset($values[1]) && $values[1] <= 12 )) {
$values = array_reverse($values);
$value = join('/', $values);
}
// Mailchimp expects a MM/DD format, regardless of their display preference
$value = (string) gmdate('m/d', strtotime($value));
return $value;
}
/**
* @param mixed $value
* @param object $options
* @return string
*/
public function date($value, $options = null)
{
if (is_array($value)) {
// allow for "year", "month" and "day" keys
if (isset($value['year']) && isset($value['month']) && isset($value['day'])) {
$value = $value['year'] . '/' . $value['month'] . '/' . $value['day'];
} else {
// if other array, just join together
$value = join('/', $value);
}
}
$value = trim($value);
if (empty($value)) {
return $value;
}
// Mailchimp expects a Y-m-d format no matter the display preference
return (string) gmdate('Y-m-d', strtotime($value));
}
/**
* @param string $value
* @param object $options
* @return string
*/
public function language($value, $options = null)
{
$value = trim($value);
$exceptions = array(
'pt_PT',
'es_ES',
'fr_CA',
);
if (! in_array($value, $exceptions, true)) {
$value = substr($value, 0, 2);
}
return $value;
}
/**
* @param mixed $value
* @param object $options
* @return bool
*/
public function boolean($value, $options = null)
{
$falsey = array( 'false', '0' );
if (in_array($value, $falsey, true)) {
return false;
}
// otherwise, just cast.
return (bool) $value;
}
}

View File

@@ -0,0 +1,132 @@
<?php
/**
* Class MC4WP_Field_Guesser
*
* @access private
* @ignore
*/
class MC4WP_Field_Guesser
{
/**
* @var array
*/
protected $fields;
/**
* @param array $fields
*/
public function __construct(array $fields)
{
$fields = array_change_key_case($fields, CASE_UPPER);
$this->fields = $fields;
}
/**
* Get all data which is namespaced with a given namespace
*
* @param string $namespace
*
* @return array
*/
public function namespaced($namespace = 'mc4wp-')
{
$prefix = strtoupper($namespace);
$return = array();
$length = strlen($prefix);
foreach ($this->fields as $key => $value) {
if (strpos($key, $prefix) === 0) {
$new_key = substr($key, $length);
$return[ $new_key ] = $value;
}
}
return $return;
}
/**
* Guess values for the following fields
* - EMAIL
* - NAME
* - FNAME
* - LNAME
*
* @return array
*/
public function guessed()
{
$guessed = array();
foreach ($this->fields as $field => $value) {
// transform value into array to support 1-level arrays
$sub_fields = is_array($value) ? $value : array( $value );
foreach ($sub_fields as $sub_field_value) {
// poor man's urldecode, to get Enfold theme's contact element to work.
$sub_field_value = str_replace('%40', '@', $sub_field_value);
// is this an email value? if so, assume it's the EMAIL field
if (empty($guessed['EMAIL']) && is_string($sub_field_value) && is_email($sub_field_value)) {
$guessed['EMAIL'] = $sub_field_value;
continue 2;
}
// remove special characters from field name
$simple_key = str_replace(array( '-', '_', ' ' ), '', $field);
if (empty($guessed['FNAME']) && $this->string_contains($simple_key, array( 'FIRSTNAME', 'FNAME', 'GIVENNAME', 'FORENAME' ))) {
// find first name field
$guessed['FNAME'] = $sub_field_value;
} elseif (empty($guessed['LNAME']) && $this->string_contains($simple_key, array( 'LASTNAME', 'LNAME', 'SURNAME', 'FAMILYNAME' ))) {
// find last name field
$guessed['LNAME'] = $sub_field_value;
} elseif (empty($guessed['NAME']) && $this->string_contains($simple_key, 'NAME')) {
// find name field
$guessed['NAME'] = $sub_field_value;
}
}
}
return $guessed;
}
/**
* @param $methods
*
* @return array
*/
public function combine(array $methods)
{
$combined = array();
foreach ($methods as $method) {
if (method_exists($this, $method)) {
$combined = array_merge($combined, call_user_func(array( $this, $method )));
}
}
return $combined;
}
/**
* @param string $haystack
* @param string|array $needles
*
* @return bool
*/
private function string_contains($haystack, $needles)
{
if (! is_array($needles)) {
$needles = array( $needles );
}
foreach ($needles as $needle) {
if (strpos($haystack, $needle) !== false) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,180 @@
<?php
/**
* Class MC4WP_Field_Map
*
* @access private
* @since 4.0
* @ignore
*/
class MC4WP_List_Data_Mapper
{
/**
* @var array
*/
private $data = array();
/**
* @var array
*/
private $list_ids = array();
/**
* @var MC4WP_Field_Formatter
*/
private $formatter;
/**
* @var MC4WP_MailChimp
*/
private $mailchimp;
/**
* @param array $data
* @param array $list_ids
*/
public function __construct(array $data, array $list_ids)
{
$this->data = array_change_key_case($data, CASE_UPPER);
if (! isset($this->data['EMAIL'])) {
throw new InvalidArgumentException('Data needs at least an EMAIL key.');
}
$this->list_ids = $list_ids;
$this->formatter = new MC4WP_Field_Formatter();
$this->mailchimp = new MC4WP_MailChimp();
}
/**
* @return MC4WP_MailChimp_Subscriber[]
*/
public function map()
{
$map = array();
foreach ($this->list_ids as $list_id) {
$map[ "$list_id" ] = $this->map_list($list_id);
}
return $map;
}
/**
* @param string $list_id
* @return MC4WP_MailChimp_Subscriber
* @throws Exception
*/
protected function map_list($list_id)
{
$subscriber = new MC4WP_MailChimp_Subscriber();
$subscriber->email_address = $this->data['EMAIL'];
// find merge fields
$merge_fields = $this->mailchimp->get_list_merge_fields($list_id);
foreach ($merge_fields as $merge_field) {
// skip EMAIL field as that is handled separately (see above)
if ($merge_field->tag === 'EMAIL') {
continue;
}
// use empty() here to skip empty field values
if (empty($this->data[ $merge_field->tag ])) {
continue;
}
// format field value
$value = $this->data[ $merge_field->tag ];
$value = $this->format_merge_field_value($merge_field, $value);
// add to map
$subscriber->merge_fields[ $merge_field->tag ] = $value;
}
// find interest categories
if (! empty($this->data['INTERESTS'])) {
$interest_categories = $this->mailchimp->get_list_interest_categories($list_id);
foreach ($interest_categories as $interest_category) {
foreach ($interest_category->interests as $interest_id => $interest_name) {
// straight lookup by ID as key with value copy.
if (isset($this->data['INTERESTS'][ $interest_id ])) {
$subscriber->interests[ $interest_id ] = $this->formatter->boolean($this->data['INTERESTS'][ $interest_id ]);
}
// straight lookup by ID as top-level value
if (in_array($interest_id, $this->data['INTERESTS'], false)) {
$subscriber->interests[ $interest_id ] = true;
}
// look in array with category ID as key.
if (isset($this->data['INTERESTS'][ $interest_category->id ])) {
$value = $this->data['INTERESTS'][ $interest_category->id ];
$values = is_array($value) ? $value : array_map('trim', explode('|', $value));
// find by category ID + interest ID
if (in_array($interest_id, $values, false)) {
$subscriber->interests[ $interest_id ] = true;
}
// find by category ID + interest name
if (in_array($interest_name, $values, true)) {
$subscriber->interests[ $interest_id ] = true;
}
}
}
}
}
// add GDPR marketing permissions
if (! empty($this->data['MARKETING_PERMISSIONS'])) {
$values = $this->data['MARKETING_PERMISSIONS'];
$values = is_array($values) ? $values : explode(',', $values);
$values = array_map('trim', $values);
$marketing_permissions = $this->mailchimp->get_list_marketing_permissions($list_id);
foreach ($marketing_permissions as $mp) {
if (in_array($mp->marketing_permission_id, $values, true) || in_array($mp->text, $values, true)) {
$subscriber->marketing_permissions[] = (object) array(
'marketing_permission_id' => $mp->marketing_permission_id,
'enabled' => true,
);
}
}
}
// find language
/* @see http://kb.mailchimp.com/lists/managing-subscribers/view-and-edit-subscriber-languages?utm_source=mc-api&utm_medium=docs&utm_campaign=apidocs&_ga=1.211519638.2083589671.1469697070 */
if (! empty($this->data['MC_LANGUAGE'])) {
$subscriber->language = $this->formatter->language($this->data['MC_LANGUAGE']);
}
return $subscriber;
}
/**
* @param object $merge_field
* @param string $value
*
* @return mixed
*/
private function format_merge_field_value($merge_field, $value)
{
$field_type = strtolower($merge_field->type);
if (method_exists($this->formatter, $field_type)) {
$value = call_user_func(array( $this->formatter, $field_type ), $value, $merge_field->options);
}
/**
* Filters the value of a field after it is formatted.
*
* Use this to format a field value according to the field type (in Mailchimp).
*
* @since 3.0
* @param string $value The value
* @param string $field_type The type of the field (in Mailchimp)
*/
$value = apply_filters('mc4wp_format_field_value', $value, $field_type);
return $value;
}
}

View File

@@ -0,0 +1,82 @@
<?php
class MC4WP_MailChimp_Subscriber
{
/**
* @var string Email address for this subscriber.
*/
public $email_address = '';
/**
* @var array The key of this objects properties is the ID of the interest in question.
*/
public $interests = array();
/**
* @var array An individual merge var and value for a member.
*/
public $merge_fields = array();
/**
* @var string Subscribers status.
*/
public $status = 'pending';
/**
* @var string Type of email this member asked to get (html or text).
*/
public $email_type = 'html';
/**
* @var string IP address the subscriber signed up from.
*/
public $ip_signup;
/**
* @var string The subscriber's language
*/
public $language;
/**
* @var boolean VIP status for subscriber.
*/
public $vip;
/**
* @var array The tags applied to this member.
*/
public $tags = array();
/**
* @var array The marketing permissions for the subscriber.
*/
public $marketing_permissions = array();
/**
* Retrieves member data as an array, without null values.
*
* @return array
*/
public function to_array()
{
$all = get_object_vars($this);
$array = array();
foreach ($all as $key => $value) {
// skip null values
if ($value === null) {
continue;
}
// skip empty marketing_permissions property
if ($key === 'marketing_permissions' && empty($value)) {
continue;
}
// otherwise, add to final array
$array[ $key ] = $value;
}
return $array;
}
}

View File

@@ -0,0 +1,579 @@
<?php
/**
* Helper class for dealing with common API requests.
*/
class MC4WP_MailChimp
{
/**
* @var string
*/
public $error_code = '';
/**
* @var string
*/
public $error_message = '';
/**
*
* Sends a subscription request to the Mailchimp API
*
* @param string $list_id The list id to subscribe to
* @param string $email_address The email address to subscribe
* @param array $args
* @param bool $update_existing Update information if this email is already on list?
* @param bool $replace_interests Replace interest groupings, only if update_existing is true.
*
* @return object
* @throws Exception
*/
public function list_subscribe($list_id, $email_address, array $args = array(), $update_existing = false, $replace_interests = true)
{
$this->reset_error();
$default_args = array(
'status' => 'pending',
'email_address' => $email_address,
);
$existing_member_data = null;
// setup default args
$args = array_merge($default_args, $args);
$api = $this->get_api();
// first, check if subscriber is already on the given list
try {
$existing_member_data = $api->get_list_member($list_id, $email_address);
if ($existing_member_data->status === 'subscribed') {
// if we're not supposed to update, bail.
if (! $update_existing) {
$this->error_code = 214;
$this->error_message = 'That subscriber already exists.';
return null;
}
$args['status'] = 'subscribed';
// this key only exists if list actually has interests
if (isset($existing_member_data->interests)) {
$existing_interests = (array) $existing_member_data->interests;
// if replace, assume all existing interests disabled
if ($replace_interests) {
$existing_interests = array_fill_keys(array_keys($existing_interests), false);
}
$args['interests'] = array_replace($existing_interests, $args['interests']);
}
} elseif ($args['status'] === 'pending' && $existing_member_data->status === 'pending') {
// this ensures that a new double opt-in email is send out
$api->update_list_member(
$list_id,
$email_address,
array(
'status' => 'unsubscribed',
)
);
}
} catch (MC4WP_API_Resource_Not_Found_Exception $e) {
// subscriber does not exist (not an issue in this case)
} catch (MC4WP_API_Exception $e) {
// other errors.
$this->error_code = $e->getCode();
$this->error_message = $e;
return null;
}
try {
if ($existing_member_data) {
$data = $api->update_list_member($list_id, $email_address, $args);
$data->was_already_on_list = $existing_member_data->status === 'subscribed';
if (isset($args['tags']) && is_array($args['tags'])) {
$this->list_add_tags_to_subscriber($list_id, $data, $args['tags']);
}
} else {
$data = $api->add_new_list_member($list_id, $args);
$data->was_already_on_list = false;
}
} catch (MC4WP_API_Exception $e) {
$this->error_code = $e->getCode();
$this->error_message = $e;
return null;
}
return $data;
}
/**
* Format tags to send to Mailchimp.
*
* @param $mailchimp_tags array existent user tags
* @param $new_tags array new tags to add
*
* @return array
* @since 4.7.9
*/
private function merge_and_format_member_tags($mailchimp_tags, $new_tags)
{
$mailchimp_tags = array_map(
function ($tag) {
return $tag->name;
},
$mailchimp_tags
);
$tags = array_unique(array_merge($mailchimp_tags, $new_tags), SORT_REGULAR);
return array_map(
function ($tag) {
return array(
'name' => $tag,
'status' => 'active',
);
},
$tags
);
}
/**
* Post the tags on a list member.
*
* @param $mailchimp_list_id string The list id to subscribe to
* @param $mailchimp_member stdClass mailchimp user informations
* @param $new_tags array tags to add to the user
*
* @return bool
* @throws Exception
* @since 4.7.9
*/
private function list_add_tags_to_subscriber($mailchimp_list_id, $mailchimp_member, array $new_tags)
{
// do nothing if no tags given
if (count($new_tags) === 0) {
return true;
}
$api = $this->get_api();
$data = array(
'tags' => $this->merge_and_format_member_tags($mailchimp_member->tags, $new_tags),
);
try {
$api->update_list_member_tags($mailchimp_list_id, $mailchimp_member->email_address, $data);
} catch (MC4WP_API_Exception $ex) {
// fail silently
return false;
}
return true;
}
/**
* Changes the subscriber status to "unsubscribed"
*
* @param string $list_id
* @param string $email_address
*
* @return boolean
*/
public function list_unsubscribe($list_id, $email_address)
{
$this->reset_error();
try {
$this->get_api()->update_list_member($list_id, $email_address, array( 'status' => 'unsubscribed' ));
} catch (MC4WP_API_Resource_Not_Found_Exception $e) {
// if email wasn't even on the list: great.
return true;
} catch (MC4WP_API_Exception $e) {
$this->error_code = $e->getCode();
$this->error_message = $e;
return false;
}
return true;
}
/**
* Checks if an email address is on a given list with status "subscribed"
*
* @param string $list_id
* @param string $email_address
*
* @return boolean
* @throws Exception
*/
public function list_has_subscriber($list_id, $email_address)
{
try {
$data = $this->get_api()->get_list_member($list_id, $email_address);
} catch (MC4WP_API_Resource_Not_Found_Exception $e) {
return false;
}
return ! empty($data->id) && $data->status === 'subscribed';
}
/**
* @param string $list_id
*
* @return array
* @throws Exception
*/
public function get_list_merge_fields($list_id)
{
$transient_key = sprintf('mc4wp_list_%s_mf', $list_id);
$cached = get_transient($transient_key);
if (is_array($cached)) {
return $cached;
}
$api = $this->get_api();
try {
// fetch list merge fields
$merge_fields = $api->get_list_merge_fields(
$list_id,
array(
'count' => 100,
'fields' => 'merge_fields.name,merge_fields.tag,merge_fields.type,merge_fields.required,merge_fields.default_value,merge_fields.options,merge_fields.public',
)
);
} catch (MC4WP_API_Exception $e) {
return array();
}
// add EMAIL field
array_unshift(
$merge_fields,
(object) array(
'tag' => 'EMAIL',
'name' => __('Email address', 'mailchimp-for-wp'),
'required' => true,
'type' => 'email',
'options' => array(),
'public' => true,
)
);
set_transient($transient_key, $merge_fields, HOUR_IN_SECONDS * 24);
return $merge_fields;
}
/**
* @param string $list_id
*
* @return array
* @throws Exception
*/
public function get_list_interest_categories($list_id)
{
$transient_key = sprintf('mc4wp_list_%s_ic', $list_id);
$cached = get_transient($transient_key);
if (is_array($cached)) {
return $cached;
}
$api = $this->get_api();
try {
// fetch list interest categories
$interest_categories = $api->get_list_interest_categories(
$list_id,
array(
'count' => 100,
'fields' => 'categories.id,categories.title,categories.type',
)
);
} catch (MC4WP_API_Exception $e) {
return array();
}
foreach ($interest_categories as $interest_category) {
$interest_category->interests = array();
try {
// fetch groups for this interest
$interests_data = $api->get_list_interest_category_interests(
$list_id,
$interest_category->id,
array(
'count' => 100,
'fields' => 'interests.id,interests.name',
)
);
foreach ($interests_data as $interest_data) {
$interest_category->interests[ (string) $interest_data->id ] = $interest_data->name;
}
} catch (MC4WP_API_Exception $e) {
// ignore
}
}
set_transient($transient_key, $interest_categories, HOUR_IN_SECONDS * 24);
return $interest_categories;
}
/**
* Gets marketing permissions from a Mailchimp list.
* The list needs to have at least 1 member for this to work.
*
* @param string $list_id
*
* @return array
* @throws Exception
*/
public function get_list_marketing_permissions($list_id)
{
$transient_key = sprintf('mc4wp_list_%s_mp', $list_id);
$cached = get_transient($transient_key);
if (is_array($cached)) {
return $cached;
}
try {
$api = $this->get_api();
$data = $api->get_list_members(
$list_id,
array(
'fields' => array( 'members.marketing_permissions' ),
'count' => 1,
)
);
$marketing_permissions = array();
if (count($data->members) > 0 && $data->members[0]->marketing_permissions) {
foreach ($data->members[0]->marketing_permissions as $mp) {
$marketing_permissions[] = (object) array(
'marketing_permission_id' => $mp->marketing_permission_id,
'text' => $mp->text,
);
}
}
} catch (MC4WP_API_Exception $e) {
return array();
}
set_transient($transient_key, $marketing_permissions, HOUR_IN_SECONDS * 24);
return $marketing_permissions;
}
/**
* Get Mailchimp lists, from cache or remote API.
*
* @param boolean $skip_cache Whether to force a result by hitting Mailchimp API
*
* @return array
*/
public function get_lists($skip_cache = false)
{
$cache_key = 'mc4wp_mailchimp_lists';
$cached = get_transient($cache_key);
if (is_array($cached) && ! $skip_cache) {
return $cached;
}
$lists = $this->fetch_lists();
/**
* Filters the cache time for Mailchimp lists configuration, in seconds. Defaults to 24 hours.
*/
$cache_ttl = (int) apply_filters('mc4wp_lists_count_cache_time', HOUR_IN_SECONDS * 24);
// make sure cache ttl is not lower than 60 seconds
$cache_ttl = max(60, $cache_ttl);
set_transient($cache_key, $lists, $cache_ttl);
return $lists;
}
private function fetch_lists()
{
$client = $this->get_api()->get_client();
$lists_data = array();
$offset = 0;
$count = 10;
$exceptions_skipped = 0;
// increase total time limit to 3 minutes
@set_time_limit(180);
// increase HTTP timeout to 30s as MailChimp is super slow to calculate dynamic fields
add_filter(
'mc4wp_http_request_args',
function ($args) {
$args['timeout'] = 30;
return $args;
}
);
// collect all lists in separate HTTP requests
do {
try {
$data = $client->get(
'/lists',
array(
'count' => $count,
'offset' => $offset,
'fields' => 'total_items,lists.id,lists.name,lists.web_id,lists.stats.member_count,lists.marketing_permissions',
)
);
$lists_data = array_merge($lists_data, $data->lists);
$offset += $count;
} catch (MC4WP_API_Connection_Exception $e) {
// ignore timeout errors as this is likely due to mailchimp being slow to calculate the lists.stats.member_count property
// keep going so we can at least pull-in all other lists
$offset += $count;
++$exceptions_skipped;
// failsafe against infinite loop
// bail after 5 skipped exceptions
if ($exceptions_skipped >= 5) {
break;
}
continue;
} catch (MC4WP_API_Exception $e) {
// break on other errors, like "API key missing"etc.
break;
}
} while ($data->total_items >= $offset);
// key by list ID
$lists = array();
foreach ($lists_data as $list_data) {
$lists["$list_data->id"] = $list_data;
}
return $lists;
}
/**
* @param string $list_id
*
* @return object|null
*/
public function get_list($list_id)
{
$lists = $this->get_lists();
return isset($lists["$list_id"]) ? $lists["$list_id"] : null;
}
/**
* Fetch lists data from Mailchimp.
*/
public function refresh_lists()
{
$lists = $this->get_lists(true);
foreach ($lists as $list_id => $list) {
// delete cached merge fields
$transient_key = sprintf('mc4wp_list_%s_mf', $list_id);
delete_transient($transient_key);
// delete cached interest categories
$transient_key = sprintf('mc4wp_list_%s_ic', $list_id);
delete_transient($transient_key);
// delete cached marketing permissions
$transient_key = sprintf('mc4wp_list_%s_mp', $list_id);
delete_transient($transient_key);
}
return ! empty($lists);
}
/**
* Returns number of subscribers on given lists.
*
* @param array|string $list_ids Array of list ID's, or single string.
*
* @return int Total # subscribers for given lists.
*/
public function get_subscriber_count($list_ids)
{
// make sure we're getting an array
if (! is_array($list_ids)) {
$list_ids = array( $list_ids );
}
// if we got an empty array, return 0
if (empty($list_ids)) {
return 0;
}
$lists = $this->get_lists();
// start calculating subscribers count for all given list ID's combined
$count = 0;
foreach ($list_ids as $list_id) {
if (! isset($lists["$list_id"])) {
continue;
}
$list = $lists["$list_id"];
$count += $list->stats->member_count;
}
/**
* Filters the total subscriber_count for the given List ID's.
*
* @param string $count
* @param array $list_ids
*
* @since 2.0
*/
return apply_filters('mc4wp_subscriber_count', $count, $list_ids);
}
/**
* Resets error properties.
*/
public function reset_error()
{
$this->error_message = '';
$this->error_code = '';
}
/**
* @return bool
*/
public function has_error()
{
return ! empty($this->error_code);
}
/**
* @return string
*/
public function get_error_message()
{
return $this->error_message;
}
/**
* @return string
*/
public function get_error_code()
{
return $this->error_code;
}
/**
* @return MC4WP_API_V3
* @throws Exception
*/
private function get_api()
{
return mc4wp('api');
}
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* Class MC4WP_Plugin
*
* Helper class for easy access to information like the plugin file or plugin directory.
* Used in MC4WP Premium.
*
* @access public
* @ignore
*/
class MC4WP_Plugin
{
/**
* @var string The plugin version.
*/
protected $version;
/**
* @var string The main plugin file.
*/
protected $file;
/**
* @param string $file The plugin version.
* @param string $version The main plugin file.
*/
public function __construct($file, $version)
{
$this->file = $file;
$this->version = $version;
}
/**
* Get the main plugin file.
*
* @return string
*/
public function file()
{
return $this->file;
}
/**
* Get the plugin version.
*
* @return string
*/
public function version()
{
return $this->version;
}
/**
* Gets the directory the plugin lives in.
*
* @param string $path
*
* @return string
*/
public function dir($path = '')
{
// ensure path has leading slash
if ('' !== $path) {
$path = '/' . ltrim($path, '/');
}
return dirname($this->file) . $path;
}
/**
* Gets the URL to the plugin files.
*
* @param string $path
*
* @return string
*/
public function url($path = '')
{
return plugins_url($path, $this->file);
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* Class MC4WP_Queue_Job
*
* @ignore
*/
class MC4WP_Queue_Job
{
/**
* @var string
*/
public $id;
/**
* @var mixed
*/
public $data;
/**
* @var int
*/
public $max_attempts = 1;
/**
* @var int
*/
public $attempts = 0;
/**
* MC4WP_Queue_Job constructor.
*
* @param mixed $data
*/
public function __construct($data)
{
$this->id = (string) microtime(true) . rand(1, 10000);
$this->data = $data;
}
}

View File

@@ -0,0 +1,198 @@
<?php
/**
* Class MC4WP_Queue
*
* @ignore
*/
class MC4WP_Queue
{
/**
* @var MC4WP_Queue_Job[]
*/
protected $jobs;
/**
* @var string
*/
protected $option_name;
/**
* @var bool
*/
protected $dirty = false;
/**
* @var int
*/
const MAX_JOB_COUNT = 1000;
/**
* MC4WP_Ecommerce_Queue constructor.
*
* @param string $option_name
*/
public function __construct($option_name)
{
$this->option_name = $option_name;
register_shutdown_function(array( $this, 'save' ));
}
/**
* Load jobs from option
*/
protected function load()
{
if (! is_null($this->jobs)) {
return;
}
$jobs = get_option($this->option_name, array());
if (! is_array($jobs)) {
$jobs = array();
} else {
$valid_jobs = array();
foreach ($jobs as $i => $obj) {
// filter invalid data from array
if (! is_object($obj) || empty($obj->data)) {
continue;
}
// make sure each job is instance of MC4WP_Queue_Job
if ($obj instanceof MC4WP_Queue_Job) {
$job = $obj;
} else {
$job = new MC4WP_Queue_Job($obj->data);
$job->id = $obj->id;
}
$valid_jobs[] = $job;
}
$jobs = $valid_jobs;
}
$this->jobs = $jobs;
}
/**
* Get all jobs in the queue
*
* @return MC4WP_Queue_Job[] Array of jobs
*/
public function all()
{
$this->load();
return $this->jobs;
}
/**
* Add job to queue
*
* @param mixed $data
* @return boolean
*/
public function put($data)
{
$this->load();
// check if we already have a job with same data
foreach ($this->jobs as $job) {
if ($job->data === $data) {
return false;
}
}
// if we have more than MAX_JOB_COUNT jobs, remove first job item.
// this protects against an ever-growing job list, but also potentially loses jobs if the queue is not processed soon enough.
if (count($this->jobs) > self::MAX_JOB_COUNT) {
array_shift($this->jobs);
}
// add job to end of jobs array
$job = new MC4WP_Queue_Job($data);
$this->jobs[] = $job;
$this->dirty = true;
return true;
}
/**
* Get all jobs in the queue
*
* @return MC4WP_Queue_Job|false
*/
public function get()
{
$this->load();
// do we have jobs?
if (count($this->jobs) === 0) {
return false;
}
// return first element
return reset($this->jobs);
}
/**
* @param MC4WP_Queue_Job $job
*/
public function delete(MC4WP_Queue_Job $job)
{
$this->load();
$index = array_search($job, $this->jobs, true);
// check for "false" here, as 0 is a valid index.
if ($index !== false) {
unset($this->jobs[ $index ]);
$this->jobs = array_values($this->jobs);
$this->dirty = true;
}
}
/**
* @param MC4WP_Queue_Job $job
*/
public function reschedule(MC4WP_Queue_Job $job)
{
$this->load();
// delete job from start of queue
$this->delete($job);
// add job to end of queue
$this->jobs[] = $job;
$this->dirty = true;
}
/**
* Reset queue
*/
public function reset()
{
$this->jobs = array();
$this->dirty = true;
}
/**
* Save the queue
*/
public function save()
{
if (! $this->dirty || is_null($this->jobs)) {
return false;
}
$success = update_option($this->option_name, $this->jobs, false);
if ($success) {
$this->dirty = false;
}
return $success;
}
}

View File

@@ -0,0 +1,269 @@
<?php
/**
* Class MC4WP_Tools
*
* @access private
* @ignore
*/
class MC4WP_Tools
{
/**
* @return array
*/
public static function get_countries()
{
return array(
'AF' => 'Afghanistan',
'AX' => 'Aland Islands',
'AL' => 'Albania',
'DZ' => 'Algeria',
'AS' => 'American Samoa',
'AD' => 'Andorra',
'AO' => 'Angola',
'AI' => 'Anguilla',
'AQ' => 'Antarctica',
'AG' => 'Antigua and Barbuda',
'AR' => 'Argentina',
'AM' => 'Armenia',
'AW' => 'Aruba',
'AU' => 'Australia',
'AT' => 'Austria',
'AZ' => 'Azerbaijan',
'BS' => 'Bahamas',
'BH' => 'Bahrain',
'BD' => 'Bangladesh',
'BB' => 'Barbados',
'BY' => 'Belarus',
'BE' => 'Belgium',
'BZ' => 'Belize',
'BJ' => 'Benin',
'BM' => 'Bermuda',
'BT' => 'Bhutan',
'BO' => 'Bolivia',
'BQ' => 'Bonaire, Saint Eustatius and Saba',
'BA' => 'Bosnia and Herzegovina',
'BW' => 'Botswana',
'BV' => 'Bouvet Island',
'BR' => 'Brazil',
'IO' => 'British Indian Ocean Territory',
'VG' => 'British Virgin Islands',
'BN' => 'Brunei',
'BG' => 'Bulgaria',
'BF' => 'Burkina Faso',
'BI' => 'Burundi',
'KH' => 'Cambodia',
'CM' => 'Cameroon',
'CA' => 'Canada',
'CV' => 'Cape Verde',
'KY' => 'Cayman Islands',
'CF' => 'Central African Republic',
'TD' => 'Chad',
'CL' => 'Chile',
'CN' => 'China',
'CX' => 'Christmas Island',
'CC' => 'Cocos Islands',
'CO' => 'Colombia',
'KM' => 'Comoros',
'CK' => 'Cook Islands',
'CR' => 'Costa Rica',
'HR' => 'Croatia',
'CU' => 'Cuba',
'CW' => 'Curacao',
'CY' => 'Cyprus',
'CZ' => 'Czech Republic',
'CD' => 'Democratic Republic of the Congo',
'DK' => 'Denmark',
'DJ' => 'Djibouti',
'DM' => 'Dominica',
'DO' => 'Dominican Republic',
'TL' => 'East Timor',
'EC' => 'Ecuador',
'EG' => 'Egypt',
'SV' => 'El Salvador',
'GQ' => 'Equatorial Guinea',
'ER' => 'Eritrea',
'EE' => 'Estonia',
'ET' => 'Ethiopia',
'FK' => 'Falkland Islands',
'FO' => 'Faroe Islands',
'FJ' => 'Fiji',
'FI' => 'Finland',
'FR' => 'France',
'GF' => 'French Guiana',
'PF' => 'French Polynesia',
'TF' => 'French Southern Territories',
'GA' => 'Gabon',
'GM' => 'Gambia',
'GE' => 'Georgia',
'DE' => 'Germany',
'GH' => 'Ghana',
'GI' => 'Gibraltar',
'GR' => 'Greece',
'GL' => 'Greenland',
'GD' => 'Grenada',
'GP' => 'Guadeloupe',
'GU' => 'Guam',
'GT' => 'Guatemala',
'GG' => 'Guernsey',
'GN' => 'Guinea',
'GW' => 'Guinea-Bissau',
'GY' => 'Guyana',
'HT' => 'Haiti',
'HM' => 'Heard Island and McDonald Islands',
'HN' => 'Honduras',
'HK' => 'Hong Kong',
'HU' => 'Hungary',
'IS' => 'Iceland',
'IN' => 'India',
'ID' => 'Indonesia',
'IR' => 'Iran',
'IQ' => 'Iraq',
'IE' => 'Ireland',
'IM' => 'Isle of Man',
'IL' => 'Israel',
'IT' => 'Italy',
'CI' => 'Ivory Coast',
'JM' => 'Jamaica',
'JP' => 'Japan',
'JE' => 'Jersey',
'JO' => 'Jordan',
'KZ' => 'Kazakhstan',
'KE' => 'Kenya',
'KI' => 'Kiribati',
'XK' => 'Kosovo',
'KW' => 'Kuwait',
'KG' => 'Kyrgyzstan',
'LA' => 'Laos',
'LV' => 'Latvia',
'LB' => 'Lebanon',
'LS' => 'Lesotho',
'LR' => 'Liberia',
'LY' => 'Libya',
'LI' => 'Liechtenstein',
'LT' => 'Lithuania',
'LU' => 'Luxembourg',
'MO' => 'Macao',
'MK' => 'Macedonia',
'MG' => 'Madagascar',
'MW' => 'Malawi',
'MY' => 'Malaysia',
'MV' => 'Maldives',
'ML' => 'Mali',
'MT' => 'Malta',
'MH' => 'Marshall Islands',
'MQ' => 'Martinique',
'MR' => 'Mauritania',
'MU' => 'Mauritius',
'YT' => 'Mayotte',
'MX' => 'Mexico',
'FM' => 'Micronesia',
'MD' => 'Moldova',
'MC' => 'Monaco',
'MN' => 'Mongolia',
'ME' => 'Montenegro',
'MS' => 'Montserrat',
'MA' => 'Morocco',
'MZ' => 'Mozambique',
'MM' => 'Myanmar',
'NA' => 'Namibia',
'NR' => 'Nauru',
'NP' => 'Nepal',
'NL' => 'Netherlands',
'NC' => 'New Caledonia',
'NZ' => 'New Zealand',
'NI' => 'Nicaragua',
'NE' => 'Niger',
'NG' => 'Nigeria',
'NU' => 'Niue',
'NF' => 'Norfolk Island',
'KP' => 'North Korea',
'MP' => 'Northern Mariana Islands',
'NO' => 'Norway',
'OM' => 'Oman',
'PK' => 'Pakistan',
'PW' => 'Palau',
'PS' => 'Palestinian Territory',
'PA' => 'Panama',
'PG' => 'Papua New Guinea',
'PY' => 'Paraguay',
'PE' => 'Peru',
'PH' => 'Philippines',
'PN' => 'Pitcairn',
'PL' => 'Poland',
'PT' => 'Portugal',
'PR' => 'Puerto Rico',
'QA' => 'Qatar',
'CG' => 'Republic of the Congo',
'RE' => 'Reunion',
'RO' => 'Romania',
'RU' => 'Russia',
'RW' => 'Rwanda',
'BL' => 'Saint Barthelemy',
'SH' => 'Saint Helena',
'KN' => 'Saint Kitts and Nevis',
'LC' => 'Saint Lucia',
'MF' => 'Saint Martin',
'PM' => 'Saint Pierre and Miquelon',
'VC' => 'Saint Vincent and the Grenadines',
'WS' => 'Samoa',
'SM' => 'San Marino',
'ST' => 'Sao Tome and Principe',
'SA' => 'Saudi Arabia',
'SN' => 'Senegal',
'RS' => 'Serbia',
'SC' => 'Seychelles',
'SL' => 'Sierra Leone',
'SG' => 'Singapore',
'SX' => 'Sint Maarten',
'SK' => 'Slovakia',
'SI' => 'Slovenia',
'SB' => 'Solomon Islands',
'SO' => 'Somalia',
'ZA' => 'South Africa',
'GS' => 'South Georgia and the South Sandwich Islands',
'KR' => 'South Korea',
'SS' => 'South Sudan',
'ES' => 'Spain',
'LK' => 'Sri Lanka',
'SD' => 'Sudan',
'SR' => 'Suriname',
'SJ' => 'Svalbard and Jan Mayen',
'SZ' => 'Swaziland',
'SE' => 'Sweden',
'CH' => 'Switzerland',
'SY' => 'Syria',
'TW' => 'Taiwan',
'TJ' => 'Tajikistan',
'TZ' => 'Tanzania',
'TH' => 'Thailand',
'TG' => 'Togo',
'TK' => 'Tokelau',
'TO' => 'Tonga',
'TT' => 'Trinidad and Tobago',
'TN' => 'Tunisia',
'TR' => 'Turkey',
'TM' => 'Turkmenistan',
'TC' => 'Turks and Caicos Islands',
'TV' => 'Tuvalu',
'VI' => 'U.S. Virgin Islands',
'UG' => 'Uganda',
'UA' => 'Ukraine',
'AE' => 'United Arab Emirates',
'GB' => 'United Kingdom',
'US' => 'United States',
'UM' => 'United States Minor Outlying Islands',
'UY' => 'Uruguay',
'UZ' => 'Uzbekistan',
'VU' => 'Vanuatu',
'VA' => 'Vatican',
'VE' => 'Venezuela',
'VN' => 'Vietnam',
'WF' => 'Wallis and Futuna',
'EH' => 'Western Sahara',
'YE' => 'Yemen',
'ZM' => 'Zambia',
'ZW' => 'Zimbabwe',
);
}
}

View File

@@ -0,0 +1,5 @@
<?php
defined('ABSPATH') or exit;
add_action('mc4wp_refresh_mailchimp_lists', 'mc4wp_refresh_mailchimp_lists');

View File

@@ -0,0 +1,16 @@
<?php
defined('ABSPATH') or exit;
add_filter('mc4wp_form_data', 'mc4wp_add_name_data', 60);
add_filter('mc4wp_integration_data', 'mc4wp_add_name_data', 60);
add_filter('mctb_data', '_mc4wp_update_groupings_data', PHP_INT_MAX);
add_filter('mc4wp_form_data', '_mc4wp_update_groupings_data', PHP_INT_MAX);
add_filter('mc4wp_integration_data', '_mc4wp_update_groupings_data', PHP_INT_MAX);
add_filter('mailchimp_sync_user_data', '_mc4wp_update_groupings_data', PHP_INT_MAX);
add_filter('mc4wp_use_sslverify', '_mc4wp_use_sslverify', 1);
mc4wp_apply_deprecated_filters('mc4wp_merge_vars', 'mc4wp_form_data');
mc4wp_apply_deprecated_filters('mc4wp_form_merge_vars', 'mc4wp_form_data');
mc4wp_apply_deprecated_filters('mc4wp_integration_merge_vars', 'mc4wp_integration_data');

View File

@@ -0,0 +1 @@
<?php

View File

@@ -0,0 +1,31 @@
<?php
/**
* Gets the absolute url to edit a form
*
* @param int $form_id ID of the form
* @param string $tab Tab identifier to open
*
* @return string
*/
function mc4wp_get_edit_form_url($form_id, $tab = '')
{
$url = admin_url(sprintf('admin.php?page=mailchimp-for-wp-forms&view=edit-form&form_id=%d', $form_id));
if (! empty($tab)) {
$url .= sprintf('&tab=%s', $tab);
}
return $url;
}
/**
* Get absolute URL to create a new form
*
* @return string
*/
function mc4wp_get_add_form_url()
{
$url = admin_url('admin.php?page=mailchimp-for-wp-forms&view=add-form');
return $url;
}

View File

@@ -0,0 +1,491 @@
<?php
/**
* Class MC4WP_Forms_Admin
*
* @ignore
* @access private
*/
class MC4WP_Forms_Admin
{
/**
* @var MC4WP_Admin_Messages
*/
protected $messages;
/**
* @param MC4WP_Admin_Messages $messages
*/
public function __construct(MC4WP_Admin_Messages $messages)
{
$this->messages = $messages;
}
/**
* Add hooks
*/
public function add_hooks()
{
add_action('register_shortcode_ui', array( $this, 'register_shortcake_ui' ));
add_action('mc4wp_save_form', array( $this, 'update_form_stylesheets' ));
add_action('mc4wp_admin_edit_form', array( $this, 'process_save_form' ));
add_action('mc4wp_admin_add_form', array( $this, 'process_add_form' ));
add_filter('mc4wp_admin_menu_items', array( $this, 'add_menu_item' ), 5);
add_action('mc4wp_admin_show_forms_page-edit-form', array( $this, 'show_edit_page' ));
add_action('mc4wp_admin_show_forms_page-add-form', array( $this, 'show_add_page' ));
add_action('mc4wp_admin_enqueue_assets', array( $this, 'enqueue_assets' ), 10, 2);
add_action('enqueue_block_editor_assets', array( $this, 'enqueue_gutenberg_assets' ));
}
public function enqueue_gutenberg_assets()
{
wp_enqueue_script('mc4wp-form-block', mc4wp_plugin_url('assets/js/forms-block.js'), array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-components' ));
$forms = mc4wp_get_forms();
$data = array();
foreach ($forms as $form) {
$data[] = array(
'name' => $form->name,
'id' => $form->ID,
);
}
wp_localize_script('mc4wp-form-block', 'mc4wp_forms', $data);
}
/**
* @param string $suffix
* @param string $page
*/
public function enqueue_assets($suffix, $page = '')
{
if ($page !== 'forms' || empty($_GET['view']) || $_GET['view'] !== 'edit-form') {
return;
}
wp_register_script('mc4wp-forms-admin', mc4wp_plugin_url('assets/js/forms-admin.js'), array( 'mc4wp-admin' ), MC4WP_VERSION, true);
wp_enqueue_script('mc4wp-forms-admin');
wp_localize_script(
'mc4wp-forms-admin',
'mc4wp_forms_i18n',
array(
'addToForm' => __('Add to form', 'mailchimp-for-wp'),
'agreeToTerms' => __('I have read and agree to the terms & conditions', 'mailchimp-for-wp'),
'agreeToTermsShort' => __('Agree to terms', 'mailchimp-for-wp'),
'agreeToTermsLink' => __('Link to your terms & conditions page', 'mailchimp-for-wp'),
'city' => __('City', 'mailchimp-for-wp'),
'checkboxes' => __('Checkboxes', 'mailchimp-for-wp'),
'choices' => __('Choices', 'mailchimp-for-wp'),
'choiceType' => __('Choice type', 'mailchimp-for-wp'),
'chooseField' => __('Choose a field to add to the form', 'mailchimp-for-wp'),
'close' => __('Close', 'mailchimp-for-wp'),
'country' => __('Country', 'mailchimp-for-wp'),
'dropdown' => __('Dropdown', 'mailchimp-for-wp'),
'emailAddress' => __('Email address', 'mailchimp-for-wp'),
'fieldType' => __('Field type', 'mailchimp-for-wp'),
'fieldLabel' => __('Field label', 'mailchimp-for-wp'),
'formAction' => __('Form action', 'mailchimp-for-wp'),
'formActionDescription' => __('This field will allow your visitors to choose whether they would like to subscribe or unsubscribe', 'mailchimp-for-wp'),
'formFields' => __('Form fields', 'mailchimp-for-wp'),
'forceRequired' => __('This field is marked as required in Mailchimp.', 'mailchimp-for-wp'),
'initialValue' => __('Initial value', 'mailchimp-for-wp'),
'interestCategories' => __('Interest categories', 'mailchimp-for-wp'),
'isFieldRequired' => __('Is this field required?', 'mailchimp-for-wp'),
'listChoice' => __('List choice', 'mailchimp-for-wp'),
'listChoiceDescription' => __('This field will allow your visitors to choose a list to subscribe to.', 'mailchimp-for-wp'),
'listFields' => __('List fields', 'mailchimp-for-wp'),
'min' => __('Min', 'mailchimp-for-wp'),
'max' => __('Max', 'mailchimp-for-wp'),
'noAvailableFields' => __('No available fields. Did you select a Mailchimp list in the form settings?', 'mailchimp-for-wp'),
'optional' => __('Optional', 'mailchimp-for-wp'),
'placeholder' => __('Placeholder', 'mailchimp-for-wp'),
'placeholderHelp' => __('Text to show when field has no value.', 'mailchimp-for-wp'),
'preselect' => __('Preselect', 'mailchimp-for-wp'),
'remove' => __('Remove', 'mailchimp-for-wp'),
'radioButtons' => __('Radio buttons', 'mailchimp-for-wp'),
'streetAddress' => __('Street Address', 'mailchimp-for-wp'),
'state' => __('State', 'mailchimp-for-wp'),
'subscribe' => __('Subscribe', 'mailchimp-for-wp'),
'submitButton' => __('Submit button', 'mailchimp-for-wp'),
'wrapInParagraphTags' => __('Wrap in paragraph tags?', 'mailchimp-for-wp'),
'value' => __('Value', 'mailchimp-for-wp'),
'valueHelp' => __('Text to prefill this field with.', 'mailchimp-for-wp'),
'zip' => __('ZIP', 'mailchimp-for-wp'),
)
);
}
/**
* @param $items
*
* @return mixed
*/
public function add_menu_item($items)
{
$items['forms'] = array(
'title' => esc_html__('Forms', 'mailchimp-for-wp'),
'text' => esc_html__('Form', 'mailchimp-for-wp'),
'slug' => 'forms',
'callback' => array( $this, 'show_forms_page' ),
'load_callback' => array( $this, 'redirect_to_form_action' ),
'position' => 10,
);
return $items;
}
/**
* Act on the "add form" form
*/
public function process_add_form()
{
$form_data = $_POST['mc4wp_form'];
$form_content = include MC4WP_PLUGIN_DIR . '/config/default-form-content.php';
// Fix for MultiSite stripping KSES for roles other than administrator
remove_all_filters('content_save_pre');
$form_id = wp_insert_post(
array(
'post_type' => 'mc4wp-form',
'post_status' => 'publish',
'post_title' => $form_data['name'],
'post_content' => $form_content,
)
);
// if settings were passed, save those too.
if (isset($form_data['settings'])) {
update_post_meta($form_id, '_mc4wp_settings', $form_data['settings']);
}
// set default form ID
$this->set_default_form_id($form_id);
$this->messages->flash(esc_html__('Form saved.', 'mailchimp-for-wp'));
$edit_form_url = mc4wp_get_edit_form_url($form_id);
wp_redirect($edit_form_url);
exit;
}
/**
* Saves a form to the database
* @param int $form_id
* @param array $data
* @return int
*/
private function save_form($form_id, array $data)
{
$keys = array(
'settings' => array(),
'messages' => array(),
'name' => '',
'content' => '',
);
$data = array_merge($keys, $data);
$data = $this->sanitize_form_data($data);
$post_data = array(
'ID' => $form_id,
'post_type' => 'mc4wp-form',
'post_status' => ! empty($data['status']) ? $data['status'] : 'publish',
'post_title' => $data['name'],
'post_content' => $data['content'],
);
// Fix for MultiSite stripping KSES for roles other than administrator
remove_all_filters('content_save_pre');
wp_insert_post($post_data);
// merge new settings with current settings to allow passing partial data
$current_settings = get_post_meta($form_id, '_mc4wp_settings', true);
if (is_array($current_settings)) {
$data['settings'] = array_merge($current_settings, $data['settings']);
}
update_post_meta($form_id, '_mc4wp_settings', $data['settings']);
// save form messages in individual meta keys
foreach ($data['messages'] as $key => $message) {
update_post_meta($form_id, 'text_' . $key, $message);
}
/**
* Runs right after a form is updated.
*
* @since 3.0
*
* @param int $form_id
*/
do_action('mc4wp_save_form', $form_id);
return $form_id;
}
/**
* @param array $data
* @return array
*/
public function sanitize_form_data(array $data)
{
$raw_data = $data;
// strip <form> tags from content
$data['content'] = preg_replace('/<\/?form(.|\s)*?>/i', '', $data['content']);
// replace lowercased name="name" to prevent 404
$data['content'] = str_ireplace(' name=\"name\"', ' name=\"NAME\"', $data['content']);
// sanitize text fields
$data['settings']['redirect'] = sanitize_text_field($data['settings']['redirect']);
// strip tags from messages
foreach ($data['messages'] as $key => $message) {
$data['messages'][ $key ] = strip_tags($message, '<strong><b><br><a><script><u><em><i><span><img>');
}
// make sure lists is an array
if (! isset($data['settings']['lists'])) {
$data['settings']['lists'] = array();
}
$data['settings']['lists'] = array_filter((array) $data['settings']['lists']);
// if current user can not post unfiltered HTML, run HTML through whitelist using wp_kses
if (! current_user_can('unfiltered_html')) {
$data['content'] = mc4wp_kses($data['content']);
foreach ($data['messages'] as $key => $message) {
$data['messages'][ $key ] = mc4wp_kses($data['messages'][ $key ]);
}
}
/**
* Filters the form data just before it is saved.
*
* @param array $data Sanitized array of form data.
* @param array $raw_data Raw array of form data.
*
* @since 3.0.8
* @ignore
*/
$data = (array) apply_filters('mc4wp_form_sanitized_data', $data, $raw_data);
return $data;
}
/**
* Saves a form
*/
public function process_save_form()
{
// save global settings (if submitted)
if (isset($_POST['mc4wp']) && is_array($_POST['mc4wp'])) {
$options = get_option('mc4wp', array());
$posted = $_POST['mc4wp'];
foreach ($posted as $key => $value) {
$options[ $key ] = trim($value);
}
update_option('mc4wp', $options);
}
// update form, settings and messages
$form_id = (int) $_POST['mc4wp_form_id'];
$form_data = $_POST['mc4wp_form'];
$this->save_form($form_id, $form_data);
$this->set_default_form_id($form_id);
$this->messages->flash(esc_html__('Form saved.', 'mailchimp-for-wp'));
}
/**
* @param int $form_id
*/
private function set_default_form_id($form_id)
{
$default_form_id = get_option('mc4wp_default_form_id', 0);
if (empty($default_form_id)) {
update_option('mc4wp_default_form_id', $form_id);
}
}
/**
* Goes through each form and aggregates array of stylesheet slugs to load.
*
* @hooked `mc4wp_save_form`
*/
public function update_form_stylesheets()
{
$stylesheets = array();
$forms = mc4wp_get_forms();
foreach ($forms as $form) {
$stylesheet = $form->get_stylesheet();
if (! empty($stylesheet) && ! in_array($stylesheet, $stylesheets, true)) {
$stylesheets[] = $stylesheet;
}
}
update_option('mc4wp_form_stylesheets', $stylesheets);
}
/**
* Redirect to correct form action
*
* @ignore
*/
public function redirect_to_form_action()
{
if (! empty($_GET['view'])) {
return;
}
try {
// try default form first
$default_form = mc4wp_get_form();
$redirect_url = mc4wp_get_edit_form_url($default_form->ID);
} catch (Exception $e) {
// no default form, query first available form and go there
$forms = mc4wp_get_forms(
array(
'posts_per_page' => 1,
'orderby' => 'ID',
'order' => 'ASC',
)
);
if (count($forms) > 0) {
// take first form and use it to go to the "edit form" screen
$form = array_shift($forms);
$redirect_url = mc4wp_get_edit_form_url($form->ID);
} else {
// we don't have a form yet, go to "add new" screen
$redirect_url = mc4wp_get_add_form_url();
}
}
if (headers_sent()) {
echo sprintf('<meta http-equiv="refresh" content="0;url=%s" />', $redirect_url);
} else {
wp_redirect($redirect_url);
}
exit;
}
/**
* Show the Forms Settings page
*
* @internal
*/
public function show_forms_page()
{
$view = ! empty($_GET['view']) ? $_GET['view'] : '';
/**
* @ignore
*/
do_action('mc4wp_admin_show_forms_page', $view);
/**
* @ignore
*/
do_action('mc4wp_admin_show_forms_page-' . $view);
}
/**
* Show the "Edit Form" page
*
* @internal
*/
public function show_edit_page()
{
$form_id = ( ! empty($_GET['form_id']) ) ? (int) $_GET['form_id'] : 0;
$mailchimp = new MC4WP_MailChimp();
$lists = $mailchimp->get_lists();
try {
$form = mc4wp_get_form($form_id);
} catch (Exception $e) {
echo '<h2>', esc_html__('Form not found.', 'mailchimp-for-wp'), '</h2>';
echo '<p>', $e->getMessage(), '</p>';
echo '<p><a href="javascript:history.go(-1);"> &lsaquo; ', esc_html__('Go back', 'mailchimp-for-wp'), '</a></p>';
return;
}
$opts = $form->settings;
$active_tab = ( isset($_GET['tab']) ) ? $_GET['tab'] : 'fields';
$form_preview_url = add_query_arg(
array(
'mc4wp_preview_form' => $form_id,
),
site_url('/', 'admin')
);
require __DIR__ . '/views/edit-form.php';
}
/**
* Shows the "Add Form" page
*
* @internal
*/
public function show_add_page()
{
$mailchimp = new MC4WP_MailChimp();
$lists = $mailchimp->get_lists();
$number_of_lists = count($lists);
require __DIR__ . '/views/add-form.php';
}
/**
* Get URL for a tab on the current page.
*
* @since 3.0
* @internal
* @param $tab
* @return string
*/
public function tab_url($tab)
{
return add_query_arg(array( 'tab' => $tab ), remove_query_arg('tab'));
}
/**
* Registers UI for when shortcake is activated
*/
public function register_shortcake_ui()
{
$assets = new MC4WP_Form_Asset_Manager();
$assets->load_stylesheets();
$forms = mc4wp_get_forms();
$options = array();
foreach ($forms as $form) {
$options[ $form->ID ] = $form->name;
}
/**
* Register UI for your shortcode
*
* @param string $shortcode_tag
* @param array $ui_args
*/
shortcode_ui_register_for_shortcode(
'mc4wp_form',
array(
'label' => esc_html__('Mailchimp Sign-Up Form', 'mailchimp-for-wp'),
'listItemImage' => 'dashicons-feedback',
'attrs' => array(
array(
'label' => esc_html__('Select the form to show', 'mailchimp-for-wp'),
'attr' => 'id',
'type' => 'select',
'options' => $options,
),
),
)
);
}
}

View File

@@ -0,0 +1,222 @@
<?php
/**
* This class takes care of all form assets related functionality
*
* @access private
* @ignore
*/
class MC4WP_Form_Asset_Manager
{
/**
* @var bool Flag to determine whether scripts should be enqueued.
*/
private $load_scripts = false;
/**
* Add hooks
*/
public function add_hooks()
{
add_action('init', array( $this, 'register_scripts' ));
add_action('wp_enqueue_scripts', array( $this, 'load_stylesheets' ));
add_action('wp_footer', array( $this, 'load_scripts' ));
add_action('mc4wp_output_form', array( $this, 'before_output_form' ));
add_action('script_loader_tag', array( $this, 'add_defer_attribute' ), 10, 2);
}
/**
* Register scripts to be enqueued later.
*/
public function register_scripts()
{
wp_register_script('mc4wp-forms-api', mc4wp_plugin_url('assets/js/forms.js'), array(), MC4WP_VERSION, true);
}
/**
* @param string $stylesheet
*
* @return bool
*/
public function is_registered_stylesheet($stylesheet)
{
$stylesheets = $this->get_registered_stylesheets();
return in_array($stylesheet, $stylesheets, true);
}
/**
* @return array
*/
public function get_registered_stylesheets()
{
return array(
'basic',
'themes',
);
}
/**
* @param string $stylesheet
*
* @return string
*/
public function get_stylesheet_url($stylesheet)
{
return mc4wp_plugin_url('assets/css/form-' . $stylesheet . '.css');
}
/**
* Get array of stylesheet handles which should be enqueued.
*
* @return array
*/
public function get_active_stylesheets()
{
$stylesheets = (array) get_option('mc4wp_form_stylesheets', array());
/**
* Filters the stylesheets to be loaded
*
* Should be an array of stylesheet handles previously registered using `wp_register_style`.
* Each value is prefixed with `mc4wp-form-` to get the handle.
*
* Return an empty array if you want to disable the loading of all stylesheets.
*
* @since 3.0
* @param array $stylesheets Array of valid stylesheet handles
*/
$stylesheets = (array) apply_filters('mc4wp_form_stylesheets', $stylesheets);
return $stylesheets;
}
/**
* Load the various stylesheets
*/
public function load_stylesheets()
{
$stylesheets = $this->get_active_stylesheets();
foreach ($stylesheets as $stylesheet) {
if (! $this->is_registered_stylesheet($stylesheet)) {
continue;
}
$handle = 'mc4wp-form-' . $stylesheet;
$url = $this->get_stylesheet_url($stylesheet);
wp_enqueue_style($handle, $url, array(), MC4WP_VERSION);
add_editor_style($url);
}
/**
* @ignore
*/
do_action('mc4wp_load_form_stylesheets', $stylesheets);
}
/**
* Get data object for client-side use for after a form is submitted over HTTP POST (not AJAX).
*
* @return array
*/
public function get_submitted_form_data()
{
$submitted_form = mc4wp_get_submitted_form();
if (! $submitted_form instanceof MC4WP_Form) {
return null;
}
$data = array(
'id' => $submitted_form->ID,
'event' => $submitted_form->last_event,
'data' => $submitted_form->get_data(),
'element_id' => $submitted_form->config['element_id'],
'auto_scroll' => true,
);
if ($submitted_form->has_errors()) {
$data['errors'] = $submitted_form->errors;
}
/**
* Filters the `auto_scroll` setting for when a form is submitted.
* Set to false to disable scrolling to form.
*
* @param boolean $auto_scroll
* @since 3.0
*/
$data['auto_scroll'] = apply_filters('mc4wp_form_auto_scroll', $data['auto_scroll']);
return $data;
}
/**
* Load JavaScript files
*/
public function before_output_form()
{
$load_scripts = apply_filters('mc4wp_load_form_scripts', true);
if (! $load_scripts) {
return;
}
$this->print_dummy_javascript();
$this->load_scripts = true;
}
/**
* Prints dummy JavaScript which allows people to call `mc4wp.forms.on()` before the JS is loaded.
*/
public function print_dummy_javascript()
{
echo '<script>';
include __DIR__ . '/views/js/dummy-api.js';
echo '</script>';
}
/**
* Outputs the inline JavaScript that is used to enhance forms
*/
public function load_scripts()
{
$load_scripts = apply_filters('mc4wp_load_form_scripts', $this->load_scripts);
if (! $load_scripts) {
return;
}
// load general client-side form API
wp_enqueue_script('mc4wp-forms-api');
// maybe load JS file for when a form was submitted over HTTP POST
$submitted_form_data = $this->get_submitted_form_data();
if ($submitted_form_data !== null) {
wp_enqueue_script('mc4wp-forms-submitted', mc4wp_plugin_url('assets/js/forms-submitted.js'), array( 'mc4wp-forms-api' ), MC4WP_VERSION, true);
wp_localize_script('mc4wp-forms-submitted', 'mc4wp_submitted_form', $submitted_form_data);
}
// print inline scripts
echo '<script>';
echo '(function() {';
include __DIR__ . '/views/js/url-fields.js';
echo '})();';
echo '</script>';
/** @ignore */
do_action('mc4wp_load_form_scripts');
}
/**
* Adds `defer` attribute to all form-related `<script>` elements so they do not block page rendering.
*
* @param string $tag
* @param string $handle
* @return string
*/
public function add_defer_attribute($tag, $handle)
{
if (! in_array($handle, array( 'mc4wp-forms-api', 'mc4wp-forms-submitted' ), true) || stripos($tag, ' defer') !== false) {
return $tag;
}
return str_replace(' src=', ' defer src=', $tag);
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Class MC4WP_Form_AMP
*/
class MC4WP_Form_AMP
{
/**
* Hook!
*/
public function add_hooks()
{
add_filter('mc4wp_form_content', array( $this, 'add_response_templates' ), 10, 2);
add_filter('mc4wp_form_element_attributes', array( $this, 'add_amp_request' ));
add_filter('mc4wp_load_form_scripts', array( $this, 'suppress_scripts' ));
}
/**
* Add AMP templates for submit/success/error.
*
* @param string $content The form content.
* @param MC4WP_Form $form The form object.
* @return string Modified $content.
*/
public function add_response_templates($content, $form)
{
if (! function_exists('amp_is_request') || ! amp_is_request()) {
return $content;
}
ob_start();
?>
<div submitting>
<template type="amp-mustache">
<?php echo esc_html__('Submitting...', 'mailchimp-for-wp'); ?>
</template>
</div>
<div submit-success>
<template type="amp-mustache">
<?php
echo wp_kses(
$form->get_message('subscribed'),
array(
'a' => array(),
'strong' => array(),
'em' => array(),
)
);
?>
</template>
</div>
<div submit-error>
<template type="amp-mustache">
{{message}}
</template>
</div>
<?php
$content .= ob_get_clean();
return $content;
}
/**
* Add 'action-xhr' to AMP forms.
*
* @param array $attributes Key-Value pairs of attributes output on form.
* @return array Modified $attributes.
*/
public function add_amp_request($attributes)
{
if (function_exists('amp_is_request') && amp_is_request()) {
$attributes['action-xhr'] = get_rest_url(null, 'mc4wp/v1/form');
}
return $attributes;
}
/**
* Suppress form scripts on AMP pages.
*
* @param bool $load_scripts Whether scripts should be loaded.
* @return bool Modified $load_scripts.
*/
public function suppress_scripts($load_scripts)
{
if (function_exists('amp_is_request') && amp_is_request()) {
return false;
}
return $load_scripts;
}
}

View File

@@ -0,0 +1,376 @@
<?php
/**
* Class MC4WP_Form_Element
*
* @since 3.0
* @ignore
* @access private
*/
class MC4WP_Form_Element
{
/**
* @var string
*/
public $ID;
/**
* @var MC4WP_Form
*/
public $form;
/**
* @var array
*
* Can be used to set element-specific config settings. Accepts the following keys.
*
* - lists: Customized number of Mailchimp list ID's to subscribe to.
* - email_type: The email type
*/
public $config = array();
/**
* @var bool
*/
public $is_submitted = false;
/**
* @param MC4WP_Form $form
* @param string $id
* @param array $config
*/
public function __construct(MC4WP_Form $form, $id, array $config = array())
{
$this->form = $form;
$this->ID = $id;
$this->config = $config;
$this->is_submitted = $this->form->is_submitted
&& $this->form->config['element_id'] === $this->ID;
}
/**
* @return string
*/
protected function get_visible_fields()
{
$content = $this->form->content;
$form = $this->form;
$element = $this;
/**
* Filters the HTML for the form fields.
*
* Use this filter to add custom HTML to a form programmatically
*
* @param string $content
* @param MC4WP_Form $form
* @param MC4WP_Form_Element $element
* @since 2.0
*/
$visible_fields = (string) apply_filters('mc4wp_form_content', $content, $form, $element);
return $visible_fields;
}
/**
* @return string
*/
protected function get_hidden_fields()
{
// hidden fields
$hidden_fields = '<label style="display: none !important;">' . __('Leave this field empty if you\'re human:', 'mailchimp-for-wp') . ' ' . '<input type="text" name="_mc4wp_honeypot" value="" tabindex="-1" autocomplete="off" /></label>';
$hidden_fields .= '<input type="hidden" name="_mc4wp_timestamp" value="' . time() . '" />';
$hidden_fields .= '<input type="hidden" name="_mc4wp_form_id" value="' . esc_attr($this->form->ID) . '" />';
$hidden_fields .= '<input type="hidden" name="_mc4wp_form_element_id" value="' . esc_attr($this->ID) . '" />';
// was "lists" parameter passed in shortcode arguments?
if (! empty($this->config['lists'])) {
$lists_string = is_array($this->config['lists']) ? join(',', $this->config['lists']) : $this->config['lists'];
$hidden_fields .= '<input type="hidden" name="_mc4wp_lists" value="' . esc_attr($lists_string) . '" />';
}
// was "lists" parameter passed in shortcode arguments?
if (! empty($this->config['email_type'])) {
$hidden_fields .= '<input type="hidden" name="_mc4wp_email_type" value="' . esc_attr($this->config['email_type']) . '" />';
}
return (string) $hidden_fields;
}
/**
* Get HTML string for a notice, including wrapper element.
*
* @param MC4WP_Form_Notice $notice
*
* @return string
*/
protected function get_notice_html(MC4WP_Form_Notice $notice)
{
if ($notice->text === '') {
return '';
}
$html = sprintf('<div class="mc4wp-alert mc4wp-%s" role="alert"><p>%s</p></div>', esc_attr($notice->type), $notice->text);
return $html;
}
/**
* Gets the form response string
*
* @param boolean $force_show
* @return string
*/
public function get_response_html($force_show = false)
{
$html = '';
$form = $this->form;
if ($this->is_submitted || $force_show) {
foreach ($this->form->notices as $notice) {
$html .= $this->get_notice_html($notice);
}
}
/**
* Filter the form response HTML
*
* Use this to add your own HTML to the form response. The form instance is passed to the callback function.
*
* @since 3.0
*
* @param string $html The complete HTML string of the response, excluding the wrapper element.
* @param MC4WP_Form $form The form object
*/
$html = (string) apply_filters('mc4wp_form_response_html', $html, $form);
// wrap entire response in div, regardless of a form was submitted
$html = '<div class="mc4wp-response">' . $html . '</div>';
return $html;
}
/**
* @return string
*/
protected function get_response_position()
{
$position = 'after';
$form = $this->form;
// check if content contains {response} tag
if (stripos($this->form->content, '{response}') !== false) {
return '';
}
/**
* Filters the position for the form response.
*
* Valid values are "before" and "after". Will have no effect if `{response}` is used in the form content.
*
* @param string $position
* @param MC4WP_Form $form
* @since 2.0
*/
$response_position = (string) apply_filters('mc4wp_form_response_position', $position, $form);
return $response_position;
}
/**
* Get HTML to be added _before_ the HTML of the form fields.
*
* @return string
*/
protected function get_html_before_fields()
{
$html = '';
$form = $this->form;
/**
* Filters the HTML before the form fields.
*
* @param string $html
* @param MC4WP_Form $form
* @ignore
*/
$html = (string) apply_filters('mc4wp_form_before_fields', $html, $form);
if ($this->get_response_position() === 'before') {
$html = $html . $this->get_response_html();
}
return $html;
}
/**
* Get HTML to be added _after_ the HTML of the form fields.
*
* @return string
*/
protected function get_html_after_fields()
{
$html = '';
$form = $this->form;
/**
* Filters the HTML after the form fields.
*
* @param string $html
* @param MC4WP_Form $form
* @ignore
*/
$html = (string) apply_filters('mc4wp_form_after_fields', $html, $form);
if ($this->get_response_position() === 'after') {
$html = $this->get_response_html() . $html;
}
return $html;
}
/**
* Get all HTMl attributes for the form element
*
* @return string
*/
protected function get_form_element_attributes()
{
$form = $this;
$form_action_attribute = null;
$attributes = array(
'id' => $this->ID,
'class' => $this->get_css_classes(),
);
/**
* Filters the `action` attribute of the `<form>` element.
*
* Defaults to `null`, which means no `action` attribute will be printed.
*
* @param string $form_action_attribute
* @param MC4WP_Form $form
*/
$form_action_attribute = apply_filters('mc4wp_form_action', $form_action_attribute, $form);
if (is_string($form_action_attribute)) {
$attributes['action'] = $form_action_attribute;
}
/**
* Filters all attributes to be added to the `<form>` element
*
* @param array $attributes Key-value pairs of attributes.
* @param MC4WP_Form $form
*/
$attributes = (array) apply_filters('mc4wp_form_element_attributes', $attributes, $form);
// hardcoded attributes, can not be changed.
$attributes['method'] = 'post';
$attributes['data-id'] = $this->form->ID;
$attributes['data-name'] = $this->form->name;
// build string of key="value" from array
$string = '';
foreach ($attributes as $name => $value) {
$string .= sprintf('%s="%s" ', $name, esc_attr($value));
}
return $string;
}
/**
* @param array|null $config Use this to override the configuration for this form element
* @return string
*/
public function generate_html(array $config = null)
{
if ($config) {
$this->config = $config;
}
// return empty string if form is in trash
if ($this->form->status !== 'publish') {
return '';
}
// Start building content string
$opening_html = '<!-- Mailchimp for WordPress v' . MC4WP_VERSION . ' - https://wordpress.org/plugins/mailchimp-for-wp/ -->';
$opening_html .= '<form ' . $this->get_form_element_attributes() . '>';
$before_fields = $this->get_html_before_fields();
$fields = '';
$after_fields = $this->get_html_after_fields();
$closing_html = '</form><!-- / Mailchimp for WordPress Plugin -->';
if (
! $this->is_submitted
|| ! $this->form->settings['hide_after_success']
|| $this->form->has_errors()
) {
// add HTML for fields + wrapper element.
$fields = '<div class="mc4wp-form-fields">' .
$this->get_visible_fields() .
'</div>' .
$this->get_hidden_fields();
}
// concatenate everything
$output = $opening_html .
$before_fields .
$fields .
$after_fields .
$closing_html;
return $output;
}
/**
* Get a space separated list of CSS classes for this form
*
* @return string
*/
protected function get_css_classes()
{
$classes = array();
$form = $this->form;
$classes[] = 'mc4wp-form';
$classes[] = 'mc4wp-form-' . $form->ID;
// Add form classes if this specific form element was submitted
if ($this->is_submitted) {
$classes[] = 'mc4wp-form-submitted';
if (! $form->has_errors()) {
$classes[] = 'mc4wp-form-success';
} else {
$classes[] = 'mc4wp-form-error';
}
}
// add class for CSS targeting in custom stylesheets
if (! empty($form->settings['css'])) {
if (strpos($form->settings['css'], 'theme-') === 0) {
$classes[] = 'mc4wp-form-theme';
}
$classes[] = 'mc4wp-form-' . $form->settings['css'];
}
// add classes from config array
if (! empty($this->config['element_class'])) {
$classes = array_merge($classes, explode(' ', $this->config['element_class']));
}
/**
* Filters `class` attributes for the `<form>` element.
*
* @param array $classes
* @param MC4WP_Form $form
*/
$classes = apply_filters('mc4wp_form_css_classes', $classes, $form);
return implode(' ', $classes);
}
}

View File

@@ -0,0 +1,339 @@
<?php
/**
* Class MC4WP_Form_Listener
*
* @since 3.0
* @access private
*/
class MC4WP_Form_Listener
{
/**
* @var MC4WP_Form The submitted form instance
*/
public $submitted_form;
public function add_hooks()
{
add_action('init', array( $this, 'listen' ));
}
/**
* Listen for submitted forms
* @return bool
*/
public function listen()
{
if (empty($_POST['_mc4wp_form_id'])) {
return false;
}
// get form instance
try {
$form_id = (int) $_POST['_mc4wp_form_id'];
$form = mc4wp_get_form($form_id);
} catch (Exception $e) {
return false;
}
// sanitize request data
$request_data = $_POST;
$request_data = mc4wp_sanitize_deep($request_data);
$request_data = stripslashes_deep($request_data);
// bind request to form & validate
$form->handle_request($request_data);
$form->validate();
// store submitted form
$this->submitted_form = $form;
// did form have errors?
if (! $form->has_errors()) {
switch ($form->get_action()) {
case 'subscribe':
$this->process_subscribe_form($form);
break;
case 'unsubscribe':
$this->process_unsubscribe_form($form);
break;
}
} else {
foreach ($form->errors as $error_code) {
$form->add_notice($form->get_message($error_code), 'error');
}
$this->get_log()->info(sprintf('Form %d > Submitted with errors: %s', $form->ID, join(', ', $form->errors)));
}
$this->respond($form);
return true;
}
/**
* Process a subscribe form.
*
* @param MC4WP_Form $form
*/
public function process_subscribe_form(MC4WP_Form $form)
{
$result = false;
$mailchimp = new MC4WP_MailChimp();
$email_type = $form->get_email_type();
$data = $form->get_data();
$ip_address = mc4wp_get_request_ip_address();
/** @var MC4WP_MailChimp_Subscriber $subscriber */
$subscriber = null;
// create a map of all lists with list-specific data
$mapper = new MC4WP_List_Data_Mapper($data, $form->get_lists());
/** @var MC4WP_MailChimp_Subscriber[] $map */
$map = $mapper->map();
// loop through lists
foreach ($map as $list_id => $subscriber) {
$subscriber->status = $form->settings['double_optin'] ? 'pending' : 'subscribed';
$subscriber->email_type = $email_type;
$subscriber->ip_signup = $ip_address;
$subscriber->tags = $form->get_subscriber_tags();
/**
* Filters subscriber data before it is sent to Mailchimp. Fires for both form & integration requests.
*
* @param MC4WP_MailChimp_Subscriber $subscriber
* @param string $list_id ID of the Mailchimp list this subscriber will be added/updated in
*/
$subscriber = apply_filters('mc4wp_subscriber_data', $subscriber, $list_id);
if (! $subscriber instanceof MC4WP_MailChimp_Subscriber) {
continue;
}
/**
* Filters subscriber data before it is sent to Mailchimp. Only fires for form requests.
*
* @param MC4WP_MailChimp_Subscriber $subscriber
* @param string $list_id ID of the Mailchimp list this subscriber will be added/updated in
*/
$subscriber = apply_filters('mc4wp_form_subscriber_data', $subscriber, $list_id);
if (! $subscriber instanceof MC4WP_MailChimp_Subscriber) {
continue;
}
// send a subscribe request to Mailchimp for each list
$result = $mailchimp->list_subscribe($list_id, $subscriber->email_address, $subscriber->to_array(), $form->settings['update_existing'], $form->settings['replace_interests']);
}
$log = $this->get_log();
// do stuff on failure
if (! is_object($result) || empty($result->id)) {
$error_code = $mailchimp->get_error_code();
$error_message = $mailchimp->get_error_message();
if ((int) $mailchimp->get_error_code() === 214) {
$form->add_error('already_subscribed');
$form->add_notice($form->messages['already_subscribed'], 'notice');
$log->warning(sprintf('Form %d > %s is already subscribed to the selected list(s)', $form->ID, $data['EMAIL']));
} else {
$form->add_error($error_code);
$form->add_notice($form->messages['error'], 'error');
$log->error(sprintf('Form %d > Mailchimp API error: %s %s', $form->ID, $error_code, $error_message));
/**
* Fire action hook so API errors can be hooked into.
*
* @param MC4WP_Form $form
* @param string $error_message
*/
do_action('mc4wp_form_api_error', $form, $error_message);
}
// bail
return;
}
// Success! Did we update or newly subscribe?
if ($result->status === 'subscribed' && $result->was_already_on_list) {
$form->last_event = 'updated_subscriber';
$form->add_notice($form->messages['updated'], 'success');
$log->info(sprintf('Form %d > Successfully updated %s', $form->ID, $data['EMAIL']));
/**
* Fires right after a form was used to update an existing subscriber.
*
* @since 3.0
*
* @param MC4WP_Form $form Instance of the submitted form
* @param string $email
* @param array $data
*/
do_action('mc4wp_form_updated_subscriber', $form, $subscriber->email_address, $data);
} else {
$form->last_event = 'subscribed';
$form->add_notice($form->messages['subscribed'], 'success');
$log->info(sprintf('Form %d > Successfully subscribed %s', $form->ID, $data['EMAIL']));
/**
* Fires right after a form was used to add a new subscriber.
*
* @since 4.8.13
*
* @param MC4WP_Form $form Instance of the submitted form
* @param string $email
* @param array $data
*/
do_action('mc4wp_form_added_subscriber', $form, $subscriber->email_address, $data);
}
/**
* Fires right after a form was used to add a new subscriber (or update an existing one).
*
* @since 3.0
*
* @param MC4WP_Form $form Instance of the submitted form
* @param string $email
* @param array $data
* @param MC4WP_MailChimp_Subscriber[] $subscriber
*/
do_action('mc4wp_form_subscribed', $form, $subscriber->email_address, $data, $map);
}
/**
* @param MC4WP_Form $form
*/
public function process_unsubscribe_form(MC4WP_Form $form)
{
$mailchimp = new MC4WP_MailChimp();
$log = $this->get_log();
$result = null;
$data = $form->get_data();
// unsubscribe from each list
foreach ($form->get_lists() as $list_id) {
$result = $mailchimp->list_unsubscribe($list_id, $data['EMAIL']);
}
if (! $result) {
$form->add_notice($form->messages['error'], 'error');
$log->error(sprintf('Form %d > Mailchimp API error: %s', $form->ID, $mailchimp->get_error_message()));
// bail
return;
}
// Success! Unsubscribed.
$form->last_event = 'unsubscribed';
$form->add_notice($form->messages['unsubscribed'], 'notice');
$log->info(sprintf('Form %d > Successfully unsubscribed %s', $form->ID, $data['EMAIL']));
/**
* Fires right after a form was used to unsubscribe.
*
* @since 3.0
*
* @param MC4WP_Form $form Instance of the submitted form.
* @param string $email
*/
do_action('mc4wp_form_unsubscribed', $form, $data['EMAIL']);
}
/**
* @param MC4WP_Form $form
*/
public function respond(MC4WP_Form $form)
{
$success = ! $form->has_errors();
if ($success) {
/**
* Fires right after a form is submitted without any errors (success).
*
* @since 3.0
*
* @param MC4WP_Form $form Instance of the submitted form
*/
do_action('mc4wp_form_success', $form);
} else {
/**
* Fires right after a form is submitted with errors.
*
* @since 3.0
*
* @param MC4WP_Form $form The submitted form instance.
*/
do_action('mc4wp_form_error', $form);
// fire a dedicated event for each error
foreach ($form->errors as $error) {
/**
* Fires right after a form was submitted with errors.
*
* The dynamic portion of the hook, `$error`, refers to the error that occurred.
*
* Default errors give us the following possible hooks:
*
* - mc4wp_form_error_error General errors
* - mc4wp_form_error_spam
* - mc4wp_form_error_invalid_email Invalid email address
* - mc4wp_form_error_already_subscribed Email is already on selected list(s)
* - mc4wp_form_error_required_field_missing One or more required fields are missing
* - mc4wp_form_error_no_lists_selected No Mailchimp lists were selected
*
* @since 3.0
*
* @param MC4WP_Form $form The form instance of the submitted form.
*/
do_action('mc4wp_form_error_' . $error, $form);
}
}
/**
* Fires right before responding to the form request.
*
* @since 3.0
*
* @param MC4WP_Form $form Instance of the submitted form.
*/
do_action('mc4wp_form_respond', $form);
// do stuff on success (if form was submitted over plain HTTP, not for AJAX or REST requests)
if ($success && ! $this->request_wants_json()) {
$redirect_url = $form->get_redirect_url();
if (! empty($redirect_url)) {
wp_redirect($redirect_url);
exit;
}
}
}
private function request_wants_json()
{
if (isset($_SERVER['HTTP_ACCEPT']) && false !== strpos($_SERVER['HTTP_ACCEPT'], 'application/json')) {
return true;
}
return false;
}
/**
* @return MC4WP_API_V3
*/
protected function get_api()
{
return mc4wp('api');
}
/**
* @return MC4WP_Debug_Log
*/
protected function get_log()
{
return mc4wp('log');
}
}

View File

@@ -0,0 +1,208 @@
<?php
/**
* This class takes care of all form related functionality
*
* Do not interact with this class directly, use `mc4wp_form` functions tagged with @access public instead.
*
* @class MC4WP_Form_Manager
* @ignore
* @access private
*/
class MC4WP_Form_Manager
{
/**
* @var MC4WP_Form_Output_Manager
*/
protected $output_manager;
/**
* @var MC4WP_Form_Listener
*/
protected $listener;
/**
* @var MC4WP_Form_Tags
*/
protected $tags;
/**
* @var MC4WP_Form_Previewer
*/
protected $previewer;
/**
* @var MC4WP_Form_Asset_Manager
*/
protected $assets;
/**
* @var MC4WP_Form_AMP
*/
protected $amp_compatibility;
/**
* Constructor
*/
public function __construct()
{
$this->output_manager = new MC4WP_Form_Output_Manager();
$this->tags = new MC4WP_Form_Tags();
$this->listener = new MC4WP_Form_Listener();
$this->previewer = new MC4WP_Form_Previewer();
$this->assets = new MC4WP_Form_Asset_Manager();
$this->amp_compatibility = new MC4WP_Form_AMP();
}
/**
* Hook!
*/
public function add_hooks()
{
add_action('init', array( $this, 'initialize' ));
add_action('widgets_init', array( $this, 'register_widget' ));
add_action('rest_api_init', array( $this, 'register_endpoint' ));
$this->listener->add_hooks();
$this->output_manager->add_hooks();
$this->assets->add_hooks();
$this->tags->add_hooks();
$this->previewer->add_hooks();
$this->amp_compatibility->add_hooks();
}
/**
* Initialize
*/
public function initialize()
{
$this->register_post_type();
$this->register_block_type();
}
private function register_block_type()
{
// Bail if register_block_type does not exist (available since WP 5.0)
if (! function_exists('register_block_type')) {
return;
}
register_block_type(
'mailchimp-for-wp/form',
array(
'render_callback' => array( $this->output_manager, 'shortcode' ),
)
);
}
/**
* Register post type "mc4wp-form"
*/
private function register_post_type()
{
// register post type
register_post_type(
'mc4wp-form',
array(
'labels' => array(
'name' => 'Mailchimp Sign-up Forms',
'singular_name' => 'Sign-up Form',
),
'public' => false,
)
);
}
/**
* Register our Form widget
*/
public function register_widget()
{
register_widget('MC4WP_Form_Widget');
}
/**
* Register an API endpoint for handling a form.
*/
public function register_endpoint()
{
register_rest_route(
'mc4wp/v1',
'/form',
array(
'methods' => 'POST',
'permission_callback' => '__return_true',
'callback' => array( $this, 'handle_endpoint' ),
)
);
}
/**
* Process requests to the form endpoint.
*
* A listener checks every request for a form submit, so we just need to fetch the listener and get its status.
*/
public function handle_endpoint()
{
$form = mc4wp_get_submitted_form();
if (! $form instanceof MC4WP_Form) {
return new WP_Error(
'not_found',
esc_html__('Resource does not exist.', 'mailchimp-for-wp'),
array(
'status' => 404,
)
);
}
if ($form->has_errors()) {
$message_key = $form->errors[0];
$message = $form->get_message($message_key);
return new WP_Error(
$message_key,
$message,
array(
'status' => 400,
)
);
}
return new WP_REST_Response(true, 200);
}
/**
* @param $form_id
* @param array $config
* @param bool $echo
*
* @return string
*/
public function output_form($form_id, $config = array(), $echo = true)
{
return $this->output_manager->output_form($form_id, $config, $echo);
}
/**
* Gets the currently submitted form
*
* @return MC4WP_Form|null
*/
public function get_submitted_form()
{
if ($this->listener->submitted_form instanceof MC4WP_Form) {
return $this->listener->submitted_form;
}
return null;
}
/**
* Return all tags
*
* @return array
*/
public function get_tags()
{
return $this->tags->all();
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* Class MC4WP_Form_Notice
*
* @ignore
* @access private
*/
class MC4WP_Form_Notice
{
/**
* @var string
*/
public $type = 'error';
/**
* @var string
*/
public $text;
/**
* @param string $text
* @param string $type
*/
public function __construct($text, $type = 'error')
{
$this->text = $text;
if (! empty($type)) {
$this->type = $type;
}
}
/**
* @return string
*/
public function __toString()
{
return $this->text;
}
}

View File

@@ -0,0 +1,39 @@
<?php
class MC4WP_Form_Previewer
{
public function add_hooks()
{
add_action('parse_request', array( $this, 'listen' ));
}
public function listen()
{
if (empty($_GET['mc4wp_preview_form'])) {
return;
}
if (! current_user_can('edit_posts')) {
return;
}
show_admin_bar(false);
add_filter('pre_handle_404', '__return_true');
remove_all_actions('template_redirect');
add_action('template_redirect', array( $this, 'load_preview' ));
}
public function load_preview()
{
// clear output, some plugin or hooked code might have thrown errors by now.
if (ob_get_level() > 0) {
ob_end_clean();
}
$form_id = (int) $_GET['mc4wp_preview_form'];
status_header(200);
require __DIR__ . '/views/preview.php';
exit;
}
}

View File

@@ -0,0 +1,129 @@
<?php
/**
* Class MC4WP_Form_Tags
*
* @access private
* @ignore
*/
class MC4WP_Form_Tags extends MC4WP_Dynamic_Content_Tags
{
/**
* @var MC4WP_Form
*/
protected $form;
/**
* @var MC4WP_Form_Element
*/
protected $form_element;
public function add_hooks()
{
add_filter('mc4wp_form_response_html', array( $this, 'replace_in_form_response' ), 10, 2);
add_filter('mc4wp_form_content', array( $this, 'replace_in_form_content' ), 10, 3);
add_filter('mc4wp_form_redirect_url', array( $this, 'replace_in_form_redirect_url' ), 10, 2);
}
/**
* Register template tags
*/
public function register()
{
parent::register();
$this->tags['response'] = array(
'description' => __('Replaced with the form response (error or success messages).', 'mailchimp-for-wp'),
'callback' => array( $this, 'get_form_response' ),
);
$this->tags['data'] = array(
'description' => sprintf(__('Data from the URL or a submitted form.', 'mailchimp-for-wp')),
'callback' => array( $this, 'get_data' ),
'example' => "data key='UTM_SOURCE' default='Default Source'",
);
$this->tags['subscriber_count'] = array(
'description' => __('Replaced with the number of subscribers on the selected list(s)', 'mailchimp-for-wp'),
'callback' => array( $this, 'get_subscriber_count' ),
);
}
public function replace_in_form_content($string, MC4WP_Form $form, MC4WP_Form_Element $element = null)
{
$this->form = $form;
$this->form_element = $element;
$string = $this->replace($string);
return $string;
}
public function replace_in_form_response($string, MC4WP_Form $form)
{
$this->form = $form;
$string = $this->replace($string);
return $string;
}
public function replace_in_form_redirect_url($string, MC4WP_Form $form)
{
$this->form = $form;
$string = $this->replace_in_url($string);
return $string;
}
/**
* Returns the number of subscribers on the selected lists (for the form context)
*
* @return int
*/
public function get_subscriber_count()
{
$mailchimp = new MC4WP_MailChimp();
$count = $mailchimp->get_subscriber_count($this->form->get_lists());
return number_format($count);
}
/**
* Returns the form response
*
* @return string
*/
public function get_form_response()
{
if ($this->form_element instanceof MC4WP_Form_Element) {
return $this->form_element->get_response_html();
}
return '';
}
/**
* Gets data value from GET or POST variables.
*
* @param array $args
* @return string
*/
public function get_data(array $args = array())
{
if (empty($args['key'])) {
return '';
}
$default = isset($args['default']) ? $args['default'] : '';
$key = $args['key'];
$data = array_merge($_GET, $_POST);
$value = isset($data[ $key ]) ? $data[ $key ] : $default;
// turn array into readable value
if (is_array($value)) {
$value = array_filter($value);
$value = join(', ', $value);
}
return esc_html($value);
}
}

View File

@@ -0,0 +1,776 @@
<?php
/**
* Class MC4WP_Form
*
* Represents a Form object.
*
* To get a form instance, use `mc4wp_get_form( $id );` where `$id` is the post ID.
*
* @access public
* @since 3.0
*/
class MC4WP_Form
{
/**
* @var array Array of instantiated form objects.
*/
public static $instances = array();
/**
* @param int $post_id
* @throws Exception
*/
public static function throw_not_found_exception($post_id)
{
$message = sprintf(__('There is no form with ID %d, perhaps it was deleted?', 'mailchimp-for-wp'), $post_id);
throw new Exception($message);
}
/**
* Get a shared form instance.
*
* @param WP_Post|int $post Post instance or post ID.
* @return MC4WP_Form
* @throws Exception
*/
public static function get_instance($post = 0)
{
if ($post instanceof WP_Post) {
$post_id = $post->ID;
} else {
$post_id = (int) $post;
if ($post_id === 0) {
$post_id = (int) get_option('mc4wp_default_form_id', 0);
}
}
if ($post_id === 0) {
self::throw_not_found_exception($post_id);
}
if (isset(self::$instances[ $post_id ])) {
return self::$instances[ $post_id ];
}
// get post object if we don't have it by now
if (! $post instanceof WP_Post) {
$post = get_post($post_id);
}
// check post object
if (! $post instanceof WP_Post || $post->post_type !== 'mc4wp-form') {
self::throw_not_found_exception($post_id);
}
// get all post meta in single call for performance
$post_meta = (array) get_post_meta($post_id);
$form = new MC4WP_Form($post_id, $post, $post_meta);
// store instance
self::$instances[ $post_id ] = $form;
return $form;
}
/**
* @var int The form ID, matches the underlying post its ID
*/
public $ID = 0;
/**
* @var string The form name
*/
public $name = 'Default Form';
/**
* @var string The form HTML content
*/
public $content = '';
/**
* @var array Array of settings
*/
public $settings = array();
/**
* @var array Array of messages
*/
public $messages = array();
/**
* @var array Array of notices to be shown when this form is rendered
*/
public $notices = array();
/**
* @var array Array of error codes
*/
public $errors = array();
/**
* @var bool Was this form submitted?
*/
public $is_submitted = false;
/**
* @var array Array of the data that was submitted, in name => value pairs.
*
* Keys in this array are uppercased and keys starting with _ are stripped.
*/
private $data = array();
/**
* @var array Array of the raw form data that was submitted.
*/
public $raw_data = array();
/**
* @var array
*/
public $config = array(
'action' => 'subscribe',
'lists' => array(),
'email_type' => '',
'element_id' => '',
);
/**
* @var string
*/
public $last_event = '';
/**
* @var string
*/
public $status;
/**
* @param int $id The post ID
* @param WP_Post $post
* @param array $post_meta
*/
public function __construct($id, WP_Post $post, array $post_meta = array())
{
$this->ID = (int) $id;
$this->name = $post->post_title;
$this->content = $post->post_content;
$this->status = $post->post_status;
$this->settings = $this->load_settings($post_meta);
$this->messages = $this->load_messages($post_meta);
// update config from settings
$this->config['lists'] = $this->settings['lists'];
}
/**
* @param string $name
*
* @return mixed
*/
public function __get($name)
{
$method_name = sprintf('get_%s', $name);
if (method_exists($this, $method_name)) {
return $this->$method_name();
}
}
/**
* Gets the form response string
*
* This does not take the submitted form element into account.
*
* @see MC4WP_Form_Element::get_response_html()
*
* @return string
*/
public function get_response_html()
{
return $this->get_element()->get_response_html(true);
}
/**
* @param string $element_id
* @param array $config
* @return MC4WP_Form_element
*/
public function get_element($element_id = 'mc4wp-form', array $config = array())
{
return new MC4WP_Form_Element($this, $element_id, $config);
}
/**
* Get HTML string for this form.
*
* If you want to output a form, use `mc4wp_show_form` instead as it.
*
* @param string $element_id
* @param array $config
*
* @return string
*/
public function get_html($element_id = 'mc4wp-form', array $config = array())
{
$element = $this->get_element($element_id, $config);
$html = $element->generate_html();
return $html;
}
/**
* @param array $post_meta
* @return array
*/
protected function load_settings(array $post_meta = array())
{
$form = $this;
$default_settings = include MC4WP_PLUGIN_DIR . '/config/default-form-settings.php';
// start with defaults
$settings = $default_settings;
// get custom settings from meta
if (! empty($post_meta['_mc4wp_settings'])) {
$meta = $post_meta['_mc4wp_settings'][0];
$meta = (array) maybe_unserialize($meta);
// ensure lists is an array
if (empty($meta['lists'])) {
$meta['lists'] = array();
}
// merge with current settings (defaults)
$settings = array_merge($settings, $meta);
}
/**
* Filters the form settings
*
* @since 3.0
*
* @param array $settings
* @param MC4WP_Form $form
*/
$settings = (array) apply_filters('mc4wp_form_settings', $settings, $form);
return $settings;
}
/**
* @param array $post_meta
* @return array
*/
protected function load_messages(array $post_meta = array())
{
$form = $this;
// get default messages
$default_messages = include MC4WP_PLUGIN_DIR . '/config/default-form-messages.php';
// start with default messages
$messages = $default_messages;
/**
* Filters the form messages
*
* @since 3.0
*
* @param array $registered_messages
* @param MC4WP_Form $form
*/
$messages = (array) apply_filters('mc4wp_form_messages', $messages, $form);
// for backwards compatiblity, grab text of each message (if is array)
foreach ($messages as $key => $message) {
if (is_array($message) && isset($message['text'])) {
$messages[ $key ] = $message['text'];
}
}
foreach ($messages as $key => $message_text) {
// overwrite default text with text in form meta.
if (isset($post_meta[ 'text_' . $key ][0])) {
$message_text = $post_meta[ 'text_' . $key ][0];
}
$messages[ $key ] = $message_text;
}
return $messages;
}
/**
* Does this form has a field of the given type?
*
* @param string $type
*
* @return bool
*/
public function has_field_type($type)
{
return in_array(strtolower($type), $this->get_field_types(), true);
}
/**
* Get an array of field types which are present in this form.
*
* @return array
*/
public function get_field_types()
{
preg_match_all('/type=\"(\w+)?\"/', strtolower($this->content), $result);
$field_types = $result[1];
return $field_types;
}
/**
* Add notice to this form when it is rendered
* @param string $text
* @param string $type
*/
public function add_notice($text, $type = 'notice')
{
$this->notices[] = new MC4WP_Form_Notice($text, $type);
}
/**
* Output this form
*
* @return string
*/
public function __toString()
{
return mc4wp_show_form($this->ID, array(), false);
}
/**
* Get "redirect to url after success" setting for this form
*
* @return string
*/
public function get_redirect_url()
{
$form = $this;
$url = trim($this->settings['redirect']);
/**
* Filters the redirect URL setting
*
* @since 3.0
*
* @param string $url
* @param MC4WP_Form $form
*/
$url = (string) apply_filters('mc4wp_form_redirect_url', $url, $form);
return $url;
}
/**
* Is this form valid?
*
* Will always return true if the form is not yet submitted. Otherwise, it will run validation and store any errors.
* This method should be called before `get_errors()`
*
* @return bool
*/
public function validate()
{
if (! $this->is_submitted) {
return true;
}
$form = $this;
$errors = array();
if (empty($this->config['lists'])) {
$errors[] = 'no_lists_selected';
}
if (! isset($this->raw_data['_mc4wp_timestamp']) || $this->raw_data['_mc4wp_timestamp'] > ( time() - 2 )) {
$errors[] = 'spam';
} elseif (! isset($this->raw_data['_mc4wp_honeypot']) || ! empty($this->raw_data['_mc4wp_honeypot'])) {
$errors[] = 'spam';
}
if (empty($errors)) {
// validate email field
if (empty($this->data['EMAIL']) || ! is_email($this->data['EMAIL'])) {
$errors[] = 'invalid_email';
}
// validate other required fields
foreach ($this->get_required_fields() as $field) {
$value = mc4wp_array_get($this->data, $field);
// check for empty string or array here instead of empty() since we want to allow for "0" values.
if ($value === '' || $value === array()) {
$errors[] = 'required_field_missing';
break;
}
}
}
/**
* Filters whether this form has errors. Runs only when a form is submitted.
* Expects an array of message keys with an error type (string).
*
* Beware: all non-string values added to this array will be filtered out.
*
* @since 3.0
*
* @param array $errors
* @param MC4WP_Form $form
*/
$errors = (array) apply_filters('mc4wp_form_errors', $errors, $form);
// filter out all non-string values
$errors = array_filter($errors, 'is_string');
// set property on self
$this->errors = $errors;
// return whether we have errors
return ! $this->has_errors();
}
/**
* Handle an incoming request. Should be called before calling validate() method.
*
* @see MC4WP_Form::validate
* @param array $data
* @return void
*/
public function handle_request(array $data)
{
$this->is_submitted = true;
$this->raw_data = $data;
$this->data = $this->parse_request_data($data);
$this->last_event = '';
// update form configuration from given data
$config = array();
$map = array(
'_mc4wp_lists' => 'lists',
'_mc4wp_action' => 'action',
'_mc4wp_form_element_id' => 'element_id',
'_mc4wp_email_type' => 'email_type',
);
// use isset here to allow empty lists (which should show a notice)
foreach ($map as $param_key => $config_key) {
if (isset($this->raw_data[ $param_key ])) {
$value = $this->raw_data[ $param_key ];
if (is_array($value)) {
$value = array_filter($value);
}
$config[ $config_key ] = $value;
}
}
if (! empty($config)) {
$this->set_config($config);
}
}
/**
* Parse a request for data which should be binded to `$data` property.
*
* This does the following on all post data.
*
* - Removes fields starting with an underscore.
* - Remove fields which are set to be ignored.
* - Uppercase all field names
*
* @param array $data
*
* @return array
*/
protected function parse_request_data(array $data)
{
$form = $this;
$filtered = array();
$ignored_field_names = array();
/**
* Filters field names which should be ignored when showing data.
*
* @since 3.0
*
* @param array $ignored_field_names Array of ignored field names
* @param MC4WP_Form $form The form instance.
*/
$ignored_field_names = apply_filters('mc4wp_form_ignored_field_names', $ignored_field_names, $form);
foreach ($data as $key => $value) {
// skip fields in ignored field names
if ($key[0] === '_' || in_array($key, $ignored_field_names, true)) {
continue;
}
// uppercase key
$key = strtoupper($key);
// filter empty array values
if (is_array($value)) {
$value = array_filter($value);
}
$filtered[ $key ] = $value;
}
return $filtered;
}
/**
* Update configuration for this form
*
* @param array $config
* @return array
*/
public function set_config(array $config)
{
$this->config = array_merge($this->config, $config);
// make sure lists is an array
if (! is_array($this->config['lists'])) {
$this->config['lists'] = array_map('trim', explode(',', $this->config['lists']));
}
// make sure action is valid
if (! in_array($this->config['action'], array( 'subscribe', 'unsubscribe' ), true)) {
$this->config['action'] = 'subscribe';
}
// email_type should be a valid value
if (! in_array($this->config['email_type'], array( 'html', 'text' ), true)) {
$this->config['email_type'] = '';
}
return $this->config;
}
/**
* Get ID's of Mailchimp lists this form subscribes to
*
* @return array
*/
public function get_lists()
{
$lists = $this->config['lists'];
$form = $this;
/**
* Filters Mailchimp lists new subscribers should be added to.
*
* @param array $lists
*/
$lists = (array) apply_filters('mc4wp_lists', $lists);
/**
* Filters Mailchimp lists new subscribers coming from this form should be added to.
*
* @param array $lists
* @param MC4WP_Form $form
*/
$lists = (array) apply_filters('mc4wp_form_lists', $lists, $form);
// filter out empty array elements
$lists = array_filter($lists);
return $lists;
}
/**
* Does this form have errors?
*
* Should always evaluate to false when form has not been submitted.
*
* @see `mc4wp_form_errors` filter.
* @return bool
*/
public function has_errors()
{
return count($this->errors) > 0;
}
/**
* Add an error to this form
*
* @param string $error_code
*/
public function add_error($error_code)
{
// only add each error once
if (! in_array($error_code, $this->errors, true)) {
$this->errors[] = $error_code;
}
}
/**
* Get the form action
*
* Valid return values are "subscribe" and "unsubscribe"
*
* @return string
*/
public function get_action()
{
return $this->config['action'];
}
/**
* @return array
*/
public function get_data()
{
$data = $this->data;
$form = $this;
/**
* Filters the form data.
*
* @param array $data
* @param MC4WP_Form $form
*/
$data = apply_filters('mc4wp_form_data', $data, $form);
return $data;
}
/**
* @return array
*/
public function get_raw_data()
{
return $this->raw_data;
}
/**
* Get array of name attributes for the required fields in this form.
*
* @return array
*/
public function get_required_fields()
{
$form = $this;
// explode required fields (generated in JS) to an array (uppercased)
$required_fields_string = strtoupper($this->settings['required_fields']);
// remove array-formatted fields
// workaround for #261 (https://github.com/ibericode/mailchimp-for-wordpress/issues/261)
$required_fields_string = preg_replace('/\[\w+\]/', '', $required_fields_string);
// turn into an array
$required_fields = explode(',', $required_fields_string);
// EMAIL is not a required field as it has its own validation rules
$required_fields = array_diff($required_fields, array( 'EMAIL' ));
// filter duplicate & empty values
$required_fields = array_unique($required_fields);
$required_fields = array_filter($required_fields);
// fix uppercased subkeys, see https://github.com/ibericode/mailchimp-for-wordpress/issues/516
foreach ($required_fields as $key => $value) {
$pos = strpos($value, '.');
if ($pos > 0) {
$required_fields[ $key ] = substr($value, 0, $pos) . strtolower(substr($value, $pos));
}
}
/**
* Filters the required fields for a form
*
* By default, this holds the following fields.
*
* - All fields which are required for the selected Mailchimp lists
* - All fields in the form with a `required` attribute.
*
* @param array $required_fields
* @param MC4WP_Form $form
*/
$required_fields = (array) apply_filters('mc4wp_form_required_fields', $required_fields, $form);
return $required_fields;
}
/**
* Get "email_type" setting for new Mailchimp subscribers added by this form.
*
* @return string
*/
public function get_email_type()
{
$email_type = $this->config['email_type'];
if (empty($email_type)) {
$email_type = mc4wp_get_email_type();
}
return $email_type;
}
/**
* Gets the filename of the stylesheet to load for this form.
*
* @return string
*/
public function get_stylesheet()
{
$stylesheet = $this->settings['css'];
if (empty($stylesheet)) {
return '';
}
// form themes live in the same stylesheet
if (strpos($stylesheet, 'theme-') !== false) {
$stylesheet = 'themes';
}
return $stylesheet;
}
/**
* @param string $key
* @return string
*/
public function get_message($key)
{
$message = isset($this->messages[ $key ]) ? $this->messages[ $key ] : $this->messages['error'];
if ($key === 'no_lists_selected' && current_user_can('manage_options')) {
$message .= sprintf(' (<a href="%s">%s</a>)', mc4wp_get_edit_form_url($this->ID, 'settings'), 'edit form settings');
}
return $message;
}
/**
* @since 4.4
* @return array
*/
public function get_subscriber_tags()
{
if (empty($this->settings['subscriber_tags'])) {
return array();
}
$tags = explode(',', $this->settings['subscriber_tags']);
$tags = array_map('trim', $tags);
// remove empty tag values
foreach ($tags as $i => $tag) {
if ($tag === '') {
unset($tags[ $i ]);
}
}
return array_values($tags);
}
}

View File

@@ -0,0 +1,127 @@
<?php
/**
* Class MC4WP_Form_Output_Manager
*
* @ignore
* @access private
*/
class MC4WP_Form_Output_Manager
{
/**
* @var int The # of forms outputted
*/
public $count = 0;
/**
* @const string
*/
const SHORTCODE = 'mc4wp_form';
/**
* Add hooks
*/
public function add_hooks()
{
// enable shortcodes in form content
add_filter('mc4wp_form_content', 'do_shortcode');
add_action('init', array( $this, 'register_shortcode' ));
}
/**
* Registers the [mc4wp_form] shortcode
*/
public function register_shortcode()
{
add_shortcode(self::SHORTCODE, array( $this, 'shortcode' ));
}
/**
* @param array $attributes
* @param string $content
* @return string
*/
public function shortcode($attributes = array(), $content = '')
{
$default_attributes = array(
'id' => '',
'lists' => '',
'email_type' => '',
'element_id' => '',
'element_class' => '',
);
$attributes = shortcode_atts(
$default_attributes,
$attributes,
self::SHORTCODE
);
$config = array(
'element_id' => $attributes['element_id'],
'lists' => $attributes['lists'],
'email_type' => $attributes['email_type'],
'element_class' => $attributes['element_class'],
);
return $this->output_form($attributes['id'], $config, false);
}
/**
* @param int $id
* @param array $config
* @param bool $echo
*
* @return string
*/
public function output_form($id = 0, $config = array(), $echo = true)
{
try {
$form = mc4wp_get_form($id);
} catch (Exception $e) {
if (current_user_can('manage_options')) {
return sprintf('<strong>Mailchimp for WordPress error:</strong> %s', $e->getMessage());
}
return '';
}
++$this->count;
// set a default element_id if none is given
if (empty($config['element_id'])) {
$config['element_id'] = 'mc4wp-form-' . $this->count;
}
$form_html = $form->get_html($config['element_id'], $config);
try {
// start new output buffer
ob_start();
/**
* Runs just before a form element is outputted.
*
* @since 3.0
*
* @param MC4WP_Form $form
*/
do_action('mc4wp_output_form', $form);
// output the form (in output buffer)
echo $form_html;
// grab all contents in current output buffer & then clean + end it.
$html = ob_get_clean();
} catch (Error $e) {
$html = $form_html;
}
// echo content if necessary
if ($echo) {
echo $html;
}
return $html;
}
}

View File

@@ -0,0 +1,131 @@
<?php
defined('ABSPATH') or exit;
/**
* Adds MC4WP_Widget widget.
*
* @ignore
*/
class MC4WP_Form_Widget extends WP_Widget
{
/**
* @var array
*/
private $default_instance_settings = array(
'title' => '',
'form_id' => '',
);
/**
* Register widget with WordPress.
*/
public function __construct()
{
// translate default widget title
$this->default_instance_settings['title'] = __('Newsletter', 'mailchimp-for-wp');
parent::__construct(
'mc4wp_form_widget', // Base ID
__('Mailchimp Sign-Up Form', 'mailchimp-for-wp'), // Name
array(
'description' => __('Displays your Mailchimp for WordPress sign-up form', 'mailchimp-for-wp'),
)
);
}
/**
* Front-end display of widget.
*
* @see WP_Widget::widget()
*
* @param array $args Widget arguments.
* @param array $instance_settings Saved values from database.
*/
public function widget($args, $instance_settings)
{
// ensure $instance_settings is an array
if (! is_array($instance_settings)) {
$instance_settings = array();
}
$instance_settings = array_merge($this->default_instance_settings, $instance_settings);
$title = apply_filters('widget_title', $instance_settings['title'], $instance_settings, $this->id_base);
echo $args['before_widget'];
if (! empty($title)) {
echo $args['before_title'] . $title . $args['after_title'];
}
mc4wp_show_form($instance_settings['form_id']);
echo $args['after_widget'];
}
/**
* Back-end widget form.
*
* @see WP_Widget::form()
*
* @param array $settings Previously saved values from database.
*
* @return string|void
*/
public function form($settings)
{
$settings = array_merge($this->default_instance_settings, (array) $settings); ?>
<p>
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:', 'mailchimp-for-wp'); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($settings['title']); ?>" />
</p>
<?php
/**
* Runs right after the widget settings form is outputted
*
* @param array $settings
* @param MC4WP_Form_Widget $this
* @ignore
*/
do_action('mc4wp_form_widget_form', $settings, $this);
?>
<p class="description">
<?php printf(__('You can edit your sign-up form in the <a href="%s">Mailchimp for WordPress form settings</a>.', 'mailchimp-for-wp'), admin_url('admin.php?page=mailchimp-for-wp-forms')); ?>
</p>
<?php
}
/**
* Validates widget form values as they are saved.
*
* @see WP_Widget::update()
*
* @param array $new_settings Values just sent to be saved.
* @param array $old_settings Previously saved values from database.
*
* @return array Updated safe values to be saved.
*/
public function update($new_settings, $old_settings)
{
if (! empty($new_settings['title'])) {
$new_settings['title'] = sanitize_text_field($new_settings['title']);
}
/**
* Filters the widget settings before they are saved.
*
* @param array $new_settings
* @param array $old_settings
* @param MC4WP_Form_Widget $widget
* @ignore
*/
$new_settings = apply_filters('mc4wp_form_widget_update_settings', $new_settings, $old_settings, $this);
return $new_settings;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* Returns a Form instance
*
* @access public
*
* @param int|WP_Post $form_id.
*
* @return MC4WP_Form
*/
function mc4wp_get_form($form_id = 0)
{
return MC4WP_Form::get_instance($form_id);
}
/**
* Get an array of Form instances
*
* @access public
*
* @param array $args Array of parameters
*
* @return MC4WP_Form[]
*/
function mc4wp_get_forms(array $args = array())
{
// parse function arguments
$default_args = array(
'post_status' => 'publish',
'posts_per_page' => -1,
'ignore_sticky_posts' => true,
'no_found_rows' => true,
);
$args = array_merge($default_args, $args);
// set post_type here so it can't be overwritten using function arguments
$args['post_type'] = 'mc4wp-form';
$q = new WP_Query();
$posts = $q->query($args);
$forms = array();
foreach ($posts as $post) {
try {
$form = mc4wp_get_form($post);
} catch (Exception $e) {
continue;
}
$forms[] = $form;
}
return $forms;
}
/**
* Echoes the given form
*
* @access public
*
* @param int $form_id
* @param array $config
* @param bool $echo
*
* @return string
*/
function mc4wp_show_form($form_id = 0, $config = array(), $echo = true)
{
/** @var MC4WP_Form_Manager $forms */
$forms = mc4wp('forms');
return $forms->output_form($form_id, $config, $echo);
}
/**
* Gets an instance of the submitted form, if any.
*
* @access public
*
* @return MC4WP_Form|null
*/
function mc4wp_get_submitted_form()
{
return mc4wp('forms')->get_submitted_form();
}

View File

@@ -0,0 +1,89 @@
<?php defined('ABSPATH') or exit; ?>
<div id="mc4wp-admin" class="wrap mc4wp-settings">
<div class="mc4wp-row">
<!-- Main Content -->
<div class="main-content mc4wp-col">
<h1 class="mc4wp-page-title">
<?php echo esc_html__('Add new form', 'mailchimp-for-wp'); ?>
</h1>
<h2 style="display: none;"></h2><?php // fake h2 for admin notices ?>
<div style="max-width: 480px;">
<!-- Wrap entire page in <form> -->
<form method="post">
<input type="hidden" name="_mc4wp_action" value="add_form" />
<?php wp_nonce_field('_mc4wp_action', '_wpnonce'); ?>
<div class="mc4wp-margin-s">
<h3>
<label>
<?php echo esc_html__('What is the name of this form?', 'mailchimp-for-wp'); ?>
</label>
</h3>
<input type="text" name="mc4wp_form[name]" class="widefat" value="" spellcheck="true" autocomplete="off" placeholder="<?php echo esc_attr__('Enter your form title..', 'mailchimp-for-wp'); ?>">
</div>
<div class="mc4wp-margin-s">
<h3>
<label>
<?php echo esc_html__('To which Mailchimp lists should this form subscribe?', 'mailchimp-for-wp'); ?>
</label>
</h3>
<?php
if (! empty($lists)) {
?>
<ul id="mc4wp-lists">
<?php
foreach ($lists as $list) {
?>
<li>
<label>
<input type="checkbox" name="mc4wp_form[settings][lists][<?php echo esc_attr($list->id); ?>]" value="<?php echo esc_attr($list->id); ?>" <?php checked($number_of_lists, 1); ?> >
<?php echo esc_html($list->name); ?>
</label>
</li>
<?php
}
?>
</ul>
<?php
} else {
?>
<p class="mc4wp-notice">
<?php echo sprintf(wp_kses(__('No lists found. Did you <a href="%s">connect with Mailchimp</a>?', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), admin_url('admin.php?page=mailchimp-for-wp')); ?>
</p>
<?php
}
?>
</div>
<?php submit_button(esc_html__('Add new form', 'mailchimp-for-wp')); ?>
</form><!-- Entire page form wrap -->
</div>
<?php require MC4WP_PLUGIN_DIR . '/includes/views/parts/admin-footer.php'; ?>
</div><!-- / Main content -->
<!-- Sidebar -->
<div class="mc4wp-sidebar mc4wp-col">
<?php require MC4WP_PLUGIN_DIR . '/includes/views/parts/admin-sidebar.php'; ?>
</div>
</div>
</div>

View File

@@ -0,0 +1,119 @@
<?php defined('ABSPATH') or exit;
$tabs = array(
'fields' => esc_html__('Fields', 'mailchimp-for-wp'),
'messages' => esc_html__('Messages', 'mailchimp-for-wp'),
'settings' => esc_html__('Settings', 'mailchimp-for-wp'),
'appearance' => esc_html__('Appearance', 'mailchimp-for-wp'),
);
/**
* Filters the setting tabs on the "edit form" screen.
*
* @param array $tabs
* @ignore
*/
$tabs = apply_filters('mc4wp_admin_edit_form_tabs', $tabs);
?>
<div id="mc4wp-admin" class="wrap mc4wp-settings">
<p class="mc4wp-breadcrumbs">
<span class="prefix"><?php echo esc_html__('You are here: ', 'mailchimp-for-wp'); ?></span>
<a href="<?php echo esc_url(admin_url('admin.php?page=mailchimp-for-wp')); ?>">Mailchimp for WordPress</a> &rsaquo;
<a href="<?php echo esc_url(admin_url('admin.php?page=mailchimp-for-wp-forms')); ?>"><?php echo esc_html__('Forms', 'mailchimp-for-wp'); ?></a>
&rsaquo;
<span class="current-crumb"><strong><?php echo esc_html__('Form', 'mailchimp-for-wp'); ?> <?php echo $form_id; ?>
| <?php echo esc_html($form->name); ?></strong></span>
</p>
<!-- Main Content -->
<div>
<h1 class="mc4wp-page-title">
<?php echo esc_html__('Edit Form', 'mailchimp-for-wp'); ?>
<!-- Form actions -->
<?php
/**
* @ignore
*/
do_action('mc4wp_admin_edit_form_after_title');
?>
</h1>
<h2 style="display: none;"></h2><?php // fake h2 for admin notices ?>
<!-- Wrap entire page in <form> -->
<form method="post">
<?php // default submit button to prevent opening preview ?>
<input type="submit" style="display: none;" />
<input type="hidden" name="_mc4wp_action" value="edit_form"/>
<?php wp_nonce_field('_mc4wp_action', '_wpnonce'); ?>
<input type="hidden" name="mc4wp_form_id" value="<?php echo esc_attr($form->ID); ?>"/>
<div id="titlediv" class="mc4wp-margin-s">
<div id="titlewrap">
<label class="screen-reader-text"
for="title"><?php echo esc_html__('Enter form title here', 'mailchimp-for-wp'); ?></label>
<input type="text" name="mc4wp_form[name]" size="30"
value="<?php echo esc_attr($form->name); ?>" id="title" spellcheck="true"
autocomplete="off"
placeholder="<?php echo esc_html__('Enter the title of your sign-up form', 'mailchimp-for-wp'); ?>"
style="line-height: initial;">
</div>
<div>
<?php echo sprintf(esc_html__('Use the shortcode %s to display this form inside a post, page or text widget.', 'mailchimp-for-wp'), '<input type="text" onfocus="this.select();" readonly="readonly" value="' . esc_attr(sprintf('[mc4wp_form id=%d]', $form->ID)) . '" size="' . ( strlen($form->ID) + 15 ) . '">'); ?>
</div>
</div>
<div>
<h2 class="nav-tab-wrapper" id="mc4wp-tabs-nav">
<?php
foreach ($tabs as $tab => $name) {
$class = ( $active_tab === $tab ) ? 'nav-tab-active' : '';
echo sprintf('<a class="nav-tab nav-tab-%s %s" data-tab="%s" href="%s">%s</a>', $tab, $class, $tab, esc_attr($this->tab_url($tab)), $name);
}
?>
</h2>
<div id="mc4wp-tabs">
<?php
foreach ($tabs as $tab => $name) :
$class = ( $active_tab === $tab ) ? 'mc4wp-tab-active' : '';
// start of .tab
echo sprintf('<div class="mc4wp-tab %s" id="mc4wp-tab-%s">', $class, $tab);
/**
* Runs when outputting a tab section on the "edit form" screen
*
* @param string $tab
* @ignore
*/
do_action('mc4wp_admin_edit_form_output_' . $tab . '_tab', $opts, $form);
$tab_file = __DIR__ . '/tabs/form-' . $tab . '.php';
if (file_exists($tab_file)) {
include $tab_file;
}
// end of .tab
echo '</div>';
endforeach; // foreach tabs
?>
</div><!-- / tabs -->
</div>
</form><!-- Entire page form wrap -->
<?php require MC4WP_PLUGIN_DIR . '/includes/views/parts/admin-footer.php'; ?>
</div>
</div>

View File

@@ -0,0 +1,15 @@
(function() {
window.mc4wp = window.mc4wp || {
listeners: [],
forms: {
on: function(evt, cb) {
window.mc4wp.listeners.push(
{
event : evt,
callback: cb
}
);
}
}
}
})();

View File

@@ -0,0 +1,11 @@
function maybePrefixUrlField () {
const value = this.value.trim()
if (value !== '' && value.indexOf('http') !== 0) {
this.value = 'http://' + value
}
}
const urlFields = document.querySelectorAll('.mc4wp-form input[type="url"]')
for (let j = 0; j < urlFields.length; j++) {
urlFields[j].addEventListener('blur', maybePrefixUrlField)
}

View File

@@ -0,0 +1,66 @@
<?php defined('ABSPATH') or exit; ?>
<div class="mc4wp-admin">
<h2><?php echo esc_html__('Add more fields', 'mailchimp-for-wp'); ?></h2>
<div>
<p>
<?php echo esc_html__('To add more fields to your form, you will need to create those fields in Mailchimp first.', 'mailchimp-for-wp'); ?>
</p>
<p><strong><?php echo esc_html__("Here's how:", 'mailchimp-for-wp'); ?></strong></p>
<ol>
<li>
<p>
<?php echo esc_html__('Log in to your Mailchimp account.', 'mailchimp-for-wp'); ?>
</p>
</li>
<li>
<p>
<?php echo esc_html__('Add list fields to any of your selected lists.', 'mailchimp-for-wp'); ?>
<?php echo esc_html__('Clicking the following links will take you to the right screen.', 'mailchimp-for-wp'); ?>
</p>
<ul class="children lists--only-selected">
<?php
foreach ($lists as $list) {
?>
<li data-list-id="<?php echo $list->id; ?>" style="display: <?php echo in_array($list->id, $opts['lists']) ? '' : 'none'; ?>">
<a href="https://admin.mailchimp.com/lists/settings/merge-tags?id=<?php echo $list->web_id; ?>">
<span class="screen-reader-text"><?php echo esc_html__('Edit list fields for', 'mailchimp-for-wp'); ?> </span>
<?php echo $list->name; ?>
</a>
</li>
<?php
}
?>
</ul>
</li>
<li>
<p>
<?php echo esc_html__('Click the following button to have Mailchimp for WordPress pick up on your changes.', 'mailchimp-for-wp'); ?>
</p>
<p>
<a class="button button-primary" href="
<?php
echo esc_attr(
add_query_arg(
array(
'_mc4wp_action' => 'empty_lists_cache',
'_wpnonce' => wp_create_nonce('_mc4wp_action'),
)
)
);
?>
">
<?php echo esc_html__('Renew Mailchimp lists', 'mailchimp-for-wp'); ?>
</a>
</p>
</li>
</ol>
</div>
</div>

View File

@@ -0,0 +1,24 @@
<?php
defined('ABSPATH') or exit;
$tags = mc4wp('forms')->get_tags();
?>
<h2><?php echo esc_html__('Add dynamic form variable', 'mailchimp-for-wp'); ?></h2>
<p>
<?php echo sprintf(wp_kses(__('The following list of variables can be used to <a href="%s">add some dynamic content to your form or success and error messages</a>.', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), 'https://www.mc4wp.com/kb/using-variables-in-your-form-or-messages/') . ' ' . __('This allows you to personalise your form or response messages.', 'mailchimp-for-wp'); ?>
</p>
<table class="widefat striped">
<?php
foreach ($tags as $tag => $config) {
$tag = ! empty($config['example']) ? $config['example'] : $tag;
?>
<tr>
<td>
<input type="text" class="widefat" value="<?php echo esc_attr(sprintf('{%s}', $tag)); ?>" readonly="readonly" onfocus="this.select();" />
<p class="description" style="margin-bottom:0;"><?php echo strip_tags($config['description'], '<strong><b><em><i><a><code>'); ?></p>
</td>
</tr>
<?php
}
?>
</table>

View File

@@ -0,0 +1,55 @@
<?php
defined('ABSPATH') or exit;
// fake post to prevent notices in wp_enqueue_scripts call
$GLOBALS['post'] = new \WP_Post((object) array( 'filter' => 'raw' ));
// render simple page with form in it.
?><!DOCTYPE html>
<html>
<head>
<title>Mailchimp for WordPress Form Preview</title>
<meta charset="utf-8" />
<meta name="robots" content="noindex" />
<link type="text/css" rel="stylesheet" href="<?php bloginfo('stylesheet_url'); ?>" />
<?php
wp_enqueue_scripts();
wp_print_styles();
wp_print_head_scripts();
if (function_exists('wp_custom_css_cb')) {
wp_custom_css_cb();
}
?>
<style>
body{
background: white;
width: 100%;
max-width: 100%;
text-align: left;
}
/* hide all other elements */
body::before,
body::after,
body > *:not(#form-preview) {
display:none !important;
}
#form-preview {
display: block !important;
width: 100%;
height: 100%;
padding: 20px;
border: 0;
margin: 0;
}
</style>
</head>
<body class="page-template-default page ">
<div id="form-preview" class="page type-page status-publish hentry post post-content">
<?php mc4wp_show_form($form_id); ?>
</div>
<?php wp_footer(); ?>
</body>
</html>

View File

@@ -0,0 +1,64 @@
<?php
$theme = wp_get_theme();
$css_options = array(
'0' => sprintf(esc_html__('Inherit from %s theme', 'mailchimp-for-wp'), $theme->Name),
'basic' => esc_html__('Basic', 'mailchimp-for-wp'),
esc_html__('Form Themes', 'mailchimp-for-wp') => array(
'theme-light' => esc_html__('Light Theme', 'mailchimp-for-wp'),
'theme-dark' => esc_html__('Dark Theme', 'mailchimp-for-wp'),
'theme-red' => esc_html__('Red Theme', 'mailchimp-for-wp'),
'theme-green' => esc_html__('Green Theme', 'mailchimp-for-wp'),
'theme-blue' => esc_html__('Blue Theme', 'mailchimp-for-wp'),
),
);
/**
* Filters the <option>'s in the "CSS Stylesheet" <select> box.
*
* @ignore
*/
$css_options = apply_filters('mc4wp_admin_form_css_options', $css_options);
?>
<h2><?php echo esc_html__('Form Appearance', 'mailchimp-for-wp'); ?></h2>
<table class="form-table">
<tr valign="top">
<th scope="row"><label for="mc4wp_load_stylesheet_select"><?php echo esc_html__('Form Style', 'mailchimp-for-wp'); ?></label></th>
<td class="nowrap valigntop">
<select name="mc4wp_form[settings][css]" id="mc4wp_load_stylesheet_select">
<?php
foreach ($css_options as $key => $option) {
if (is_array($option)) {
$label = $key;
$options = $option;
printf('<optgroup label="%s">', $label);
foreach ($options as $key => $option) {
printf('<option value="%s" %s>%s</option>', $key, selected($opts['css'], $key, false), $option);
}
print( '</optgroup>' );
} else {
printf('<option value="%s" %s>%s</option>', $key, selected($opts['css'], $key, false), $option);
}
}
?>
</select>
<p class="description">
<?php echo esc_html__('If you want to load some default CSS styles, select "basic formatting styles" or choose one of the color themes', 'mailchimp-for-wp'); ?>
</p>
</td>
</tr>
<?php
/** @ignore */
do_action('mc4wp_admin_form_after_appearance_settings_rows', $opts, $form);
?>
</table>
<?php submit_button(); ?>

View File

@@ -0,0 +1,49 @@
<?php add_thickbox(); ?>
<div class="alignright">
<a href="#TB_inline?width=0&height=550&inlineId=mc4wp-form-variables" class="thickbox button-secondary">
<span class="dashicons dashicons-info"></span>
<?php echo esc_html__('Form variables', 'mailchimp-for-wp'); ?>
</a>
<a href="#TB_inline?width=600&height=400&inlineId=mc4wp-add-field-help" class="thickbox button-secondary">
<span class="dashicons dashicons-editor-help"></span>
<?php echo esc_html__('Add more fields', 'mailchimp-for-wp'); ?>
</a>
</div>
<h2><?php echo esc_html__('Form Fields', 'mailchimp-for-wp'); ?></h2>
<!-- Placeholder for the field wizard -->
<div id="mc4wp-field-wizard"></div>
<div class="mc4wp-form-markup-wrap">
<div class="mc4wp-form-editor-wrap">
<h4 style="margin: 0"><?php echo esc_html__('Form code', 'mailchimp-for-wp'); ?> <span style="visibility: hidden;" class="dashicons dashicons-editor-help"></span></h4>
<!-- Textarea for the actual form content HTML -->
<textarea class="widefat" cols="160" rows="20" id="mc4wp-form-content" name="mc4wp_form[content]" placeholder="<?php echo esc_attr__('Enter the HTML code for your form fields..', 'mailchimp-for-wp'); ?>" autocomplete="false" autocorrect="false" autocapitalize="false" spellcheck="false"><?php echo htmlspecialchars($form->content, ENT_QUOTES, get_option('blog_charset')); ?></textarea>
</div>
<div class="mc4wp-form-preview-wrap">
<h4 style="margin: 0;">
<?php echo esc_html__('Form preview', 'mailchimp-for-wp'); ?>
<span class="dashicons dashicons-editor-help" title="<?php echo esc_attr__('The form may look slightly different than this when shown in a post, page or widget area.', 'mailchimp-for-wp'); ?>"></span>
</h4>
<iframe id="mc4wp-form-preview" src="<?php echo esc_attr($form_preview_url); ?>"></iframe>
</div>
</div>
<!-- This field is updated by JavaScript as the form content changes -->
<input type="hidden" id="required-fields" name="mc4wp_form[settings][required_fields]" value="<?php echo esc_attr($form->settings['required_fields']); ?>" />
<?php submit_button(); ?>
<p class="mc4wp-form-usage"><?php printf(esc_html__('Use the shortcode %s to display this form inside a post, page or text widget.', 'mailchimp-for-wp'), '<input type="text" onfocus="this.select();" readonly="readonly" value="' . esc_attr(sprintf('[mc4wp_form id=%d]', $form->ID)) . '" size="' . ( strlen($form->ID) + 15 ) . '">'); ?></p>
<?php // Content for Thickboxes ?>
<div id="mc4wp-form-variables" style="display: none;">
<?php require __DIR__ . '/../parts/dynamic-content-tags.php'; ?>
</div>
<div id="mc4wp-add-field-help" style="display: none;">
<?php require __DIR__ . '/../parts/add-fields-help.php'; ?>
</div>

View File

@@ -0,0 +1,102 @@
<?php defined('ABSPATH') or exit;
/** @var MC4WP_Form $form */
?>
<h2><?php echo esc_html__('Form Messages', 'mailchimp-for-wp'); ?></h2>
<table class="form-table mc4wp-form-messages">
<?php
/** @ignore */
do_action('mc4wp_admin_form_before_messages_settings_rows', $opts, $form);
?>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_subscribed"><?php echo esc_html__('Successfully subscribed', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_form_subscribed" name="mc4wp_form[messages][subscribed]" value="<?php echo esc_attr($form->messages['subscribed']); ?>" />
<p class="description"><?php echo esc_html__('The text that shows when an email address is successfully subscribed to the selected list(s).', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_invalid_email"><?php echo esc_html__('Invalid email address', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_form_invalid_email" name="mc4wp_form[messages][invalid_email]" value="<?php echo esc_attr($form->messages['invalid_email']); ?>" required />
<p class="description"><?php echo esc_html__('The text that shows when an invalid email address is given.', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_required_field_missing"><?php echo esc_html__('Required field missing', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_form_required_field_missing" name="mc4wp_form[messages][required_field_missing]" value="<?php echo esc_attr($form->messages['required_field_missing']); ?>" required />
<p class="description"><?php echo esc_html__('The text that shows when a required field for the selected list(s) is missing.', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_already_subscribed"><?php echo esc_html__('Already subscribed', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_form_already_subscribed" name="mc4wp_form[messages][already_subscribed]" value="<?php echo esc_attr($form->messages['already_subscribed']); ?>" required />
<p class="description"><?php echo esc_html__('The text that shows when the given email is already subscribed to the selected list(s).', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_error"><?php echo esc_html__('General error', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_form_error" name="mc4wp_form[messages][error]" value="<?php echo esc_attr($form->messages['error']); ?>" required />
<p class="description"><?php echo esc_html__('The text that shows when a general error occured.', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_unsubscribed"><?php echo esc_html__('Unsubscribed', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_form_unsubscribed" name="mc4wp_form[messages][unsubscribed]" value="<?php echo esc_attr($form->messages['unsubscribed']); ?>" required />
<p class="description"><?php echo esc_html__('When using the unsubscribe method, this is the text that shows when the given email address is successfully unsubscribed from the selected list(s).', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_not_subscribed"><?php echo esc_html__('Not subscribed', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_form_not_subscribed" name="mc4wp_form[messages][not_subscribed]" value="<?php echo esc_attr($form->messages['not_subscribed']); ?>" required />
<p class="description"><?php echo esc_html__('When using the unsubscribe method, this is the text that shows when the given email address is not on the selected list(s).', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_no_lists_selected"><?php echo esc_html__('No list selected', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_form_no_lists_selected" name="mc4wp_form[messages][no_lists_selected]" value="<?php echo esc_attr($form->messages['no_lists_selected']); ?>" required />
<p class="description"><?php echo esc_html__('When offering a list choice, this is the text that shows when no lists were selected.', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<?php
$config = array(
'element' => 'mc4wp_form[settings][update_existing]',
'value' => 1,
);
?>
<tr valign="top" data-showif="<?php echo esc_attr(json_encode($config)); ?>">
<th scope="row"><label for="mc4wp_form_updated"><?php echo esc_html__('Updated', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_form_updated" name="mc4wp_form[messages][updated]" value="<?php echo esc_attr($form->messages['updated']); ?>" />
<p class="description"><?php echo esc_html__('The text that shows when an existing subscriber is updated.', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<?php
/** @ignore */
do_action('mc4wp_admin_form_after_messages_settings_rows', array(), $form);
?>
<tr valign="top">
<th></th>
<td>
<p class="description"><?php echo sprintf(esc_html__('HTML tags like %s are allowed in the message fields.', 'mailchimp-for-wp'), '<code>' . esc_html('<strong><em><a>') . '</code>'); ?></p>
</td>
</tr>
</table>
<?php submit_button(); ?>

View File

@@ -0,0 +1,169 @@
<h2><?php echo esc_html__('Form Settings', 'mailchimp-for-wp'); ?></h2>
<div class="mc4wp-margin-m"></div>
<h3><?php echo esc_html__('Mailchimp specific settings', 'mailchimp-for-wp'); ?></h3>
<table class="form-table" style="table-layout: fixed;">
<?php
/** @ignore */
do_action('mc4wp_admin_form_before_mailchimp_settings_rows', $opts, $form);
?>
<tr valign="top">
<th scope="row" style="width: 250px;"><?php echo esc_html__('Lists this form subscribes to', 'mailchimp-for-wp'); ?></th>
<?php
// loop through lists
if (empty($lists)) {
?>
<td colspan="2"><?php echo sprintf(wp_kses(__('No lists found, <a href="%s">are you connected to Mailchimp</a>?', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), admin_url('admin.php?page=mailchimp-for-wp')); ?></td>
<?php
} else {
?>
<td >
<ul id="mc4wp-lists" style="margin-bottom: 20px; max-height: 300px; overflow-y: auto;">
<?php
foreach ($lists as $list) {
?>
<li>
<label>
<input class="mc4wp-list-input" type="checkbox" name="mc4wp_form[settings][lists][]" value="<?php echo esc_attr($list->id); ?>" <?php checked(in_array($list->id, $opts['lists']), true); ?>> <?php echo esc_html($list->name); ?>
</label>
</li>
<?php
}
?>
</ul>
<p class="description"><?php echo esc_html__('Select the list(s) to which people who submit this form should be subscribed.', 'mailchimp-for-wp'); ?></p>
</td>
<?php
}
?>
</tr>
<tr valign="top">
<th scope="row"><?php echo esc_html__('Use double opt-in?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label>
<input type="radio" name="mc4wp_form[settings][double_optin]" value="1" <?php checked($opts['double_optin'], 1); ?> />&rlm;
<?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?>
</label> &nbsp;
<label>
<input type="radio" name="mc4wp_form[settings][double_optin]" value="0" <?php checked($opts['double_optin'], 0); ?> onclick="return confirm('<?php echo esc_attr__('Are you sure you want to disable double opt-in?', 'mailchimp-for-wp'); ?>');" />&rlm;
<?php echo esc_html__('No', 'mailchimp-for-wp'); ?>
</label>
<p class="description"><?php echo esc_html__('We strongly suggest keeping double opt-in enabled. Disabling double opt-in may affect your GDPR compliance.', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><?php echo esc_html__('Update existing subscribers?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label>
<input type="radio" name="mc4wp_form[settings][update_existing]" value="1" <?php checked($opts['update_existing'], 1); ?> />&rlm;
<?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?>
</label> &nbsp;
<label>
<input type="radio" name="mc4wp_form[settings][update_existing]" value="0" <?php checked($opts['update_existing'], 0); ?> />&rlm;
<?php echo esc_html__('No', 'mailchimp-for-wp'); ?>
</label>
<p class="description"><?php echo esc_html__('Select "yes" if you want to update existing subscribers with the data that is sent.', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<?php
$config = array(
'element' => 'mc4wp_form[settings][update_existing]',
'value' => 1,
);
?>
<tr valign="top" data-showif="<?php echo esc_attr(json_encode($config)); ?>">
<th scope="row"><?php echo esc_html__('Replace interest groups?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label>
<input type="radio" name="mc4wp_form[settings][replace_interests]" value="1" <?php checked($opts['replace_interests'], 1); ?> />&rlm;
<?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?>
</label> &nbsp;
<label>
<input type="radio" name="mc4wp_form[settings][replace_interests]" value="0" <?php checked($opts['replace_interests'], 0); ?> />&rlm;
<?php echo esc_html__('No', 'mailchimp-for-wp'); ?>
</label>
<p class="description">
<?php echo esc_html__('Select "no" if you want to add the selected interests to any previously selected interests when updating a subscriber.', 'mailchimp-for-wp'); ?>
<?php echo sprintf(' <a href="%s" target="_blank">' . esc_html__('What does this do?', 'mailchimp-for-wp') . '</a>', 'https://www.mc4wp.com/kb/what-does-replace-groupings-mean/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=settings-page'); ?>
</p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_subscriber_tags"><?php echo esc_html__('Subscriber tags', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" name="mc4wp_form[settings][subscriber_tags]" id="mc4wp_form_subscriber_tags" placeholder="<?php echo esc_attr__('Example: My tag, another tag', 'mailchimp-for-wp'); ?>" value="<?php echo esc_attr($opts['subscriber_tags']); ?>" />
<p class="description">
<?php echo esc_html__('The listed tags will be applied to all subscribers added or updated by this form.', 'mailchimp-for-wp'); ?>
<?php echo esc_html__('Separate multiple values with a comma.', 'mailchimp-for-wp'); ?>
</p>
</td>
</tr>
<?php
/** @ignore */
do_action('mc4wp_admin_form_after_mailchimp_settings_rows', $opts, $form);
?>
</table>
<div class="mc4wp-margin-m"></div>
<h3><?php echo esc_html__('Form behaviour', 'mailchimp-for-wp'); ?></h3>
<table class="form-table" style="table-layout: fixed;">
<?php
/** @ignore */
do_action('mc4wp_admin_form_before_behaviour_settings_rows', $opts, $form);
?>
<tr valign="top">
<th scope="row"><?php echo esc_html__('Hide form after a successful sign-up?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label>
<input type="radio" name="mc4wp_form[settings][hide_after_success]" value="1" <?php checked($opts['hide_after_success'], 1); ?> />&rlm;
<?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?>
</label> &nbsp;
<label>
<input type="radio" name="mc4wp_form[settings][hide_after_success]" value="0" <?php checked($opts['hide_after_success'], 0); ?> />&rlm;
<?php echo esc_html__('No', 'mailchimp-for-wp'); ?>
</label>
<p class="description">
<?php echo esc_html__('Select "yes" to hide the form fields after a successful sign-up.', 'mailchimp-for-wp'); ?>
</p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mc4wp_form_redirect"><?php echo esc_html__('Redirect to URL after successful sign-ups', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" name="mc4wp_form[settings][redirect]" id="mc4wp_form_redirect" placeholder="<?php echo sprintf(esc_attr__('Example: %s', 'mailchimp-for-wp'), esc_attr(site_url('/thank-you/'))); ?>" value="<?php echo esc_attr($opts['redirect']); ?>" />
<p class="description">
<?php echo wp_kses(__('Leave empty or enter <code>0</code> for no redirect. Otherwise, use complete (absolute) URLs, including <code>http://</code>.', 'mailchimp-for-wp'), array( 'code' => array() )); ?>
</p>
<p class="description">
<?php echo esc_html__('Your "subscribed" message will not show when redirecting to another page, so make sure to let your visitors know they were successfully subscribed.', 'mailchimp-for-wp'); ?>
</p>
</td>
</tr>
<?php
/** @ignore */
do_action('mc4wp_admin_form_after_behaviour_settings_rows', $opts, $form);
?>
</table>
<?php submit_button(); ?>

View File

@@ -0,0 +1,626 @@
<?php
/**
* Get a service by its name
*
* _Example:_
*
* $forms = mc4wp('forms');
* $api = mc4wp('api');
*
* When no service parameter is given, the entire container will be returned.
*
* @ignore
* @access private
*
* @param null|string $service (optional)
* @return mixed
*
* @throws Exception when service is not found
*/
function mc4wp($service = null)
{
static $mc4wp = null;
if (null === $mc4wp) {
$mc4wp = new MC4WP_Container();
}
if (null !== $service) {
return $mc4wp->get($service);
}
return $mc4wp;
}
/**
* Gets the Mailchimp for WP options from the database
* Uses default values to prevent undefined index notices.
*
* @since 1.0
* @access public
* @static array $options
* @return array
*/
function mc4wp_get_options()
{
$defaults = require MC4WP_PLUGIN_DIR . '/config/default-settings.php';
$options = (array) get_option('mc4wp', array());
$options = array_merge($defaults, $options);
/**
* Filters the Mailchimp for WordPress settings (general).
*
* @param array $options
*/
return apply_filters('mc4wp_settings', $options);
}
/**
* @return array
*/
function mc4wp_get_settings()
{
return mc4wp_get_options();
}
/**
* @since 4.2.6
* @return string
*/
function mc4wp_get_api_key()
{
// try to get from constant
if (defined('MC4WP_API_KEY') && constant('MC4WP_API_KEY') !== '') {
return MC4WP_API_KEY;
}
// get from options
$opts = mc4wp_get_options();
return $opts['api_key'];
}
/**
* Gets the Mailchimp for WP API class (v3) and injects it with the API key
*
* @since 4.0
* @access public
*
* @return MC4WP_API_V3
*/
function mc4wp_get_api_v3()
{
$api_key = mc4wp_get_api_key();
return new MC4WP_API_V3($api_key);
}
/**
* Creates a new instance of the Debug Log
*
* @return MC4WP_Debug_Log
*/
function mc4wp_get_debug_log()
{
$opts = mc4wp_get_options();
// get default log file location
$upload_dir = wp_upload_dir(null, false);
$file = $upload_dir['basedir'] . '/mailchimp-for-wp/debug-log.php';
$default_file = $file;
/**
* Filters the log file to write to.
*
* @param string $file The log file location. Default: /wp-content/uploads/mailchimp-for-wp/mc4wp-debug.log
*/
$file = apply_filters('mc4wp_debug_log_file', $file);
if ($file === $default_file) {
$dir = dirname($file);
if (! is_dir($dir)) {
mkdir($dir, 0755, true);
}
if (! is_file($dir . '/.htaccess')) {
$lines = array(
'<IfModule !authz_core_module>',
'Order deny,allow',
'Deny from all',
'</IfModule>',
'<IfModule authz_core_module>',
'Require all denied',
'</IfModule>',
);
file_put_contents($dir . '/.htaccess', join(PHP_EOL, $lines));
}
if (! is_file($dir . '/index.html')) {
file_put_contents($dir . '/index.html', '');
}
}
/**
* Filters the minimum level to log messages.
*
* @see MC4WP_Debug_Log
*
* @param string|int $level The minimum level of messages which should be logged.
*/
$level = apply_filters('mc4wp_debug_log_level', $opts['debug_log_level']);
return new MC4WP_Debug_Log($file, $level);
}
/**
* Get URL to a file inside the plugin directory
*
* @since 4.8.3
* @param string $path
* @return string
*/
function mc4wp_plugin_url($path)
{
static $base = null;
if ($base === null) {
$base = plugins_url('/', MC4WP_PLUGIN_FILE);
}
return $base . $path;
}
/**
* Get current URL (full)
*
* @return string
*/
function mc4wp_get_request_url()
{
global $wp;
// get requested url from global $wp object
$site_request_uri = $wp->request;
// fix for IIS servers using index.php in the URL
if (false !== stripos($_SERVER['REQUEST_URI'], '/index.php/' . $site_request_uri)) {
$site_request_uri = 'index.php/' . $site_request_uri;
}
// concatenate request url to home url
$url = home_url($site_request_uri);
$url = trailingslashit($url);
return esc_url($url);
}
/**
* Get current URL path.
*
* @return string
*/
function mc4wp_get_request_path()
{
return $_SERVER['REQUEST_URI'];
}
/**
* Get IP address for client making current request
*
* @return string|null
*/
function mc4wp_get_request_ip_address()
{
if (isset($_SERVER['X-Forwarded-For'])) {
$ip_address = $_SERVER['X-Forwarded-For'];
} elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip_address = $_SERVER['REMOTE_ADDR'];
}
if (isset($ip_address)) {
if (! is_array($ip_address)) {
$ip_address = explode(',', $ip_address);
}
// use first IP in list
$ip_address = trim($ip_address[0]);
// if IP address is not valid, simply return null
if (! filter_var($ip_address, FILTER_VALIDATE_IP)) {
return null;
}
return $ip_address;
}
return null;
}
/**
* Strips all HTML tags from all values in a mixed variable, then trims the result.
*
* @access public
* @param mixed $value
*
* @return mixed
*/
function mc4wp_sanitize_deep($value)
{
if (is_scalar($value)) {
// strip all HTML tags & whitespace
$value = trim(strip_tags($value));
// convert &amp; back to &
$value = html_entity_decode($value, ENT_NOQUOTES);
} elseif (is_array($value)) {
$value = array_map('mc4wp_sanitize_deep', $value);
} elseif (is_object($value)) {
$vars = get_object_vars($value);
foreach ($vars as $key => $data) {
$value->{$key} = mc4wp_sanitize_deep($data);
}
}
return $value;
}
/**
*
* @since 4.0
* @ignore
*
* @param array $data
* @return array
*/
function _mc4wp_update_groupings_data($data = array())
{
// data still has old "GROUPINGS" key?
if (empty($data['GROUPINGS'])) {
return $data;
}
// prepare new key
if (! isset($data['INTERESTS'])) {
$data['INTERESTS'] = array();
}
$map = get_option('mc4wp_groupings_map', array());
foreach ($data['GROUPINGS'] as $grouping_id => $groups) {
// for compatibility with expanded grouping arrays
$grouping_key = $grouping_id;
if (is_array($groups) && isset($groups['id']) && isset($groups['groups'])) {
$grouping_id = $groups['id'];
$groups = $groups['groups'];
}
// do we have transfer data for this grouping id?
if (! isset($map[ $grouping_id ])) {
continue;
}
// if we get a string, explode on delimiter(s)
if (is_string($groups)) {
// for BC with 3.x: explode on comma's
$groups = join('|', explode(',', $groups));
// explode on current delimiter
$groups = explode('|', $groups);
}
// loop through groups and find interest ID
$migrated = 0;
foreach ($groups as $key => $group_name_or_id) {
// do we know the new interest ID?
if (empty($map[ $grouping_id ]['groups'][ $group_name_or_id ])) {
continue;
}
$interest_id = $map[ $grouping_id ]['groups'][ $group_name_or_id ];
// add to interests data
if (! in_array($interest_id, $data['INTERESTS'], false)) {
++$migrated;
$data['INTERESTS'][] = $interest_id;
}
}
// remove old grouping ID if we migrated all groups.
if ($migrated === count($groups)) {
unset($data['GROUPINGS'][ $grouping_key ]);
}
}
// if everything went well, this is now empty & moved to new INTERESTS key.
if (empty($data['GROUPINGS'])) {
unset($data['GROUPINGS']);
}
// is this empty? just unset it then.
if (empty($data['INTERESTS'])) {
unset($data['INTERESTS']);
}
return $data;
}
/**
* Guesses merge vars based on given data & current request.
*
* @since 3.0
* @access public
*
* @param array $data
*
* @return array
*/
function mc4wp_add_name_data($data)
{
// Guess first and last name
if (! empty($data['NAME']) && empty($data['FNAME']) && empty($data['LNAME'])) {
$data['NAME'] = trim($data['NAME']);
$strpos = strpos($data['NAME'], ' ');
if ($strpos !== false) {
$data['FNAME'] = trim(substr($data['NAME'], 0, $strpos));
$data['LNAME'] = trim(substr($data['NAME'], $strpos));
} else {
$data['FNAME'] = $data['NAME'];
}
}
// Set name value
if (empty($data['NAME']) && ! empty($data['FNAME']) && ! empty($data['LNAME'])) {
$data['NAME'] = sprintf('%s %s', $data['FNAME'], $data['LNAME']);
}
return $data;
}
/**
* Gets the "email type" for new subscribers.
*
* Possible return values are either "html" or "text"
*
* @access public
* @since 3.0
*
* @return string
*/
function mc4wp_get_email_type()
{
$email_type = 'html';
/**
* Filters the email type preference for this new subscriber.
*
* @param string $email_type
*/
$email_type = (string) apply_filters('mc4wp_email_type', $email_type);
return $email_type;
}
/**
*
* @ignore
* @return bool
*/
function _mc4wp_use_sslverify()
{
// Disable for all transports other than CURL
if (! function_exists('curl_version')) {
return false;
}
$curl = curl_version();
// Disable if OpenSSL is not installed
if (empty($curl['ssl_version'])) {
return false;
}
// Disable if on WP 4.4, see https://core.trac.wordpress.org/ticket/34935
if ($GLOBALS['wp_version'] === '4.4') {
return false;
}
return true;
}
/**
* This will replace the first half of a string with "*" characters.
*
* @param string $string
* @return string
*/
function mc4wp_obfuscate_string($string)
{
$length = strlen($string);
$obfuscated_length = ceil($length / 2);
$string = str_repeat('*', $obfuscated_length) . substr($string, $obfuscated_length);
return $string;
}
/**
* @internal
* @ignore
*/
function _mc4wp_obfuscate_email_addresses_callback($m)
{
$one = $m[1] . str_repeat('*', strlen($m[2]));
$two = $m[3] . str_repeat('*', strlen($m[4]));
$three = $m[5];
return sprintf('%s@%s.%s', $one, $two, $three);
}
/**
* Obfuscates email addresses in a string.
*
* @param $string String possibly containing email address
* @return string
*/
function mc4wp_obfuscate_email_addresses($string)
{
return preg_replace_callback('/([\w\.]{1,4})([\w\.]*)\@(\w{1,2})(\w*)\.(\w+)/', '_mc4wp_obfuscate_email_addresses_callback', $string);
}
/**
* Refreshes Mailchimp lists. This can take a while if the connected Mailchimp account has many lists.
*
* @return void
*/
function mc4wp_refresh_mailchimp_lists()
{
$mailchimp = new MC4WP_MailChimp();
$mailchimp->refresh_lists();
}
/**
* Get element from array, allows for dot notation eg: "foo.bar"
*
* @param array $array
* @param string $key
* @param mixed $default
* @return mixed
*/
function mc4wp_array_get($array, $key, $default = null)
{
if (is_null($key)) {
return $array;
}
if (isset($array[ $key ])) {
return $array[ $key ];
}
foreach (explode('.', $key) as $segment) {
if (! is_array($array) || ! array_key_exists($segment, $array)) {
return $default;
}
$array = $array[ $segment ];
}
return $array;
}
/**
* Filters string and strips out all HTML tags and attributes, except what's in our whitelist.
*
* @param string $string The string to apply KSES whitelist on
* @return string
* @since 4.8.8
*/
function mc4wp_kses($string)
{
$always_allowed_attr = array_fill_keys(
array(
'aria-describedby',
'aria-details',
'aria-label',
'aria-labelledby',
'aria-hidden',
'class',
'id',
'style',
'title',
'role',
'data-*',
'tabindex',
),
true
);
$input_allowed_attr = array_merge(
$always_allowed_attr,
array_fill_keys(
array(
'type',
'required',
'placeholder',
'value',
'name',
'step',
'min',
'max',
'checked',
'width',
'autocomplete',
'autofocus',
'minlength',
'maxlength',
'size',
'pattern',
'disabled',
'readonly',
),
true
)
);
$allowed = array(
'p' => $always_allowed_attr,
'label' => array_merge($always_allowed_attr, array( 'for' => true )),
'input' => $input_allowed_attr,
'button' => $input_allowed_attr,
'fieldset' => $always_allowed_attr,
'legend' => $always_allowed_attr,
'ul' => $always_allowed_attr,
'ol' => $always_allowed_attr,
'li' => $always_allowed_attr,
'select' => array_merge($input_allowed_attr, array( 'multiple' => true )),
'option' => array_merge($input_allowed_attr, array( 'selected' => true )),
'optgroup' => array(
'disabled' => true,
'label' => true,
),
'textarea' => array_merge(
$input_allowed_attr,
array(
'rows' => true,
'cols' => true,
)
),
'div' => $always_allowed_attr,
'strong' => $always_allowed_attr,
'b' => $always_allowed_attr,
'i' => $always_allowed_attr,
'br' => array(),
'em' => $always_allowed_attr,
'span' => $always_allowed_attr,
'a' => array_merge($always_allowed_attr, array( 'href' => true )),
'img' => array_merge(
$always_allowed_attr,
array(
'src' => true,
'alt' => true,
'width' => true,
'height' => true,
'srcset' => true,
'sizes' => true,
'referrerpolicy' => true,
)
),
'u' => $always_allowed_attr,
);
return wp_kses($string, $allowed);
}
/**
* Helper function for safely deprecating a changed filter hook.
*
* @param string $old_hook
* @param string $new_hook
*
* @return void
*/
function mc4wp_apply_deprecated_filters($old_hook, $new_hook)
{
add_filter($new_hook, function ($value, $a = null, $b = null, $c = null) use ($new_hook, $old_hook) {
return apply_filters_deprecated($old_hook, array( $value, $a, $b, $c ), '4.9.0', $new_hook);
}, 10, 3);
}

View File

@@ -0,0 +1,197 @@
<?php
/**
* Class MC4WP_Integration_Admin
*
* @ignore
* @access private
*/
class MC4WP_Integration_Admin
{
/**
* @var MC4WP_Integration_Manager
*/
protected $integrations;
/**
* @var MC4WP_Admin_Messages
*/
protected $messages;
/**
* @param MC4WP_Integration_Manager $integrations
* @param MC4WP_Admin_Messages $messages
*/
public function __construct(MC4WP_Integration_Manager $integrations, MC4WP_Admin_Messages $messages)
{
$this->integrations = $integrations;
$this->messages = $messages;
}
/**
* Add hooks
*/
public function add_hooks()
{
add_action('admin_init', array( $this, 'register_setting' ));
add_action('mc4wp_admin_enqueue_assets', array( $this, 'enqueue_assets' ), 10, 2);
add_filter('mc4wp_admin_menu_items', array( $this, 'add_menu_item' ));
}
/**
* Register settings
*/
public function register_setting()
{
register_setting('mc4wp_integrations_settings', 'mc4wp_integrations', array( $this, 'save_integration_settings' ));
}
/**
* Enqueue assets
*
* @param string $suffix
* @param string $page
*
* @return void
*/
public function enqueue_assets($suffix, $page)
{
// only load on integrations pages
if ($page !== 'integrations') {
return;
}
wp_register_script('mc4wp-integrations-admin', mc4wp_plugin_url('assets/js/integrations-admin.js'), array( 'mc4wp-admin' ), MC4WP_VERSION, true);
wp_enqueue_script('mc4wp-integrations-admin');
}
/**
* @param array $items
*
* @return array
*/
public function add_menu_item($items)
{
$items[] = array(
'title' => esc_html__('Integrations', 'mailchimp-for-wp'),
'text' => esc_html__('Integrations', 'mailchimp-for-wp'),
'slug' => 'integrations',
'callback' => array( $this, 'show_integrations_page' ),
'position' => 20,
);
return $items;
}
/**
* @param array $new_settings
* @return array
*/
public function save_integration_settings(array $new_settings)
{
$integrations = $this->integrations->get_all();
$current_settings = (array) get_option('mc4wp_integrations', array());
$settings = array();
foreach ($integrations as $slug => $integration) {
$settings[ $slug ] = $this->parse_integration_settings($slug, $current_settings, $new_settings);
}
return $settings;
}
/**
* @since 3.0
* @param string $slug
* @param array $current
* @param array $new
*
* @return array
*/
protected function parse_integration_settings($slug, $current, $new)
{
$settings = array();
// start with current settings
if (! empty($current[ $slug ])) {
$settings = $current[ $slug ];
}
// if no new settings were given, return current settings.
if (empty($new[ $slug ])) {
return $settings;
}
// merge new settings with currents (to allow passing partial setting arrays)
$settings = array_merge($settings, $new[ $slug ]);
// sanitize settings
$settings = $this->sanitize_integration_settings($settings);
return $settings;
}
/**
* @param array $settings
* @return array
*/
protected function sanitize_integration_settings($settings)
{
// filter null values from lists setting
if (! empty($settings['lists'])) {
$settings['lists'] = array_filter($settings['lists']);
} else {
$settings['lists'] = array();
}
$settings['label'] = strip_tags($settings['label'], '<strong><b><br><a><script><u><em><i><span><img>');
if (! current_user_can('unfiltered_html')) {
$settings['label'] = mc4wp_kses($settings['label']);
}
return $settings;
}
/**
* Show the Integration Settings page
*
* @internal
*/
public function show_integrations_page()
{
if (! empty($_GET['integration'])) {
$this->show_integration_settings_page($_GET['integration']);
return;
}
// get all installed & enabled integrations
$enabled_integrations = $this->integrations->get_enabled_integrations();
// get all integrations but remove enabled integrations from the resulting array
$integrations = $this->integrations->get_all();
require __DIR__ . '/views/integrations.php';
}
/**
* @param string $slug
*
* @internal
*/
public function show_integration_settings_page($slug)
{
try {
$integration = $this->integrations->get($slug);
} catch (Exception $e) {
echo sprintf('<h3>Integration not found.</h3><p>No integration with slug <strong>%s</strong> was found.</p>', esc_html($slug));
return;
}
$opts = $integration->options;
$mailchimp = new MC4WP_MailChimp();
$lists = $mailchimp->get_lists();
require __DIR__ . '/views/integration-settings.php';
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* Class MC4WP_Integration_Fixture
*
* @since 3.0
* @ignore
*/
class MC4WP_Integration_Fixture
{
/**
* @var string
*/
public $slug;
/**
* @var string
*/
public $class;
/**
* @var bool
*/
public $enabled;
/**
* @var bool
*/
public $enabled_by_default;
/**
* @var MC4WP_Integration
*/
public $instance;
/**
* @var array
*/
public $options;
/**
* @param string $slug
* @param string $class
* @param bool $enabled_by_default
* @param array $options
*/
public function __construct($slug, $class, $enabled_by_default, array $options)
{
$this->slug = $slug;
$this->class = $class;
$this->enabled_by_default = $enabled_by_default;
$this->enabled = $enabled_by_default;
$this->options = $options;
if (! empty($options['enabled'])) {
$this->enabled = true;
}
}
/**
* Returns the actual instance
*
* @return MC4WP_Integration
*/
public function load()
{
if (! $this->instance instanceof MC4WP_Integration) {
$this->instance = new $this->class($this->slug, $this->options);
}
return $this->instance;
}
/**
* Tunnel everything to MC4WP_Integration class
*
* @param string $name
* @param array $arguments
*
* @return MC4WP_Integration
*/
public function __call($name, $arguments)
{
return call_user_func_array(array( $this->load(), $name ), $arguments);
}
/**
* @param string $name
*
* @return string
*/
public function __get($name)
{
return $this->load()->$name;
}
/**
* @return string
*/
public function __toString()
{
return $this->slug;
}
}

View File

@@ -0,0 +1,188 @@
<?php
/**
* Class MC4WP_Integration_Manager
*
* @ignore
* @access private
*/
class MC4WP_Integration_Manager
{
/**
* @var MC4WP_Integration_Fixture[]
*/
protected $integrations = array();
/**
* @var MC4WP_Integration_Tags
*/
protected $tags;
/**
* Constructor
*/
public function __construct()
{
$this->tags = new MC4WP_Integration_Tags();
}
/**
* Add hooks
*/
public function add_hooks()
{
add_action('after_setup_theme', array( $this, 'initialize' ));
$this->tags->add_hooks();
}
/**
* Add hooks
*/
public function initialize()
{
/*** @var MC4WP_Integration_Fixture $integration */
$enabled_integrations = $this->get_enabled_integrations();
foreach ($enabled_integrations as $integration) {
$integration->load()->initialize();
}
}
/**
* Get an integration instance
*
* @return MC4WP_Integration_Fixture[]
* @throws Exception
*/
public function get_all()
{
return $this->integrations;
}
/**
* Get an integration instance
*
* @param string $slug
* @return MC4WP_Integration
* @throws Exception
*/
public function get($slug)
{
if (! isset($this->integrations[ $slug ])) {
throw new Exception(sprintf('No integration with slug %s has been registered.', $slug));
}
return $this->integrations[ $slug ]->load();
}
/**
* Register a new integration class
*
* @param string $slug
* @param string $class
* @param bool $enabled
*/
public function register_integration($slug, $class, $enabled = false)
{
$raw_options = $this->get_integration_options($slug);
$this->integrations[ $slug ] = new MC4WP_Integration_Fixture($slug, $class, $enabled, $raw_options);
}
/**
* Deregister an integration class
*
* @param string $slug
*/
public function deregister_integration($slug)
{
if (isset($this->integrations[ $slug ])) {
unset($this->integrations[ $slug ]);
}
}
/**
* Checks whether a certain integration is enabled (in the settings)
*
* This is decoupled from the integration class itself as checking an array is way "cheaper" than instantiating an object
*
* @param MC4WP_Integration_Fixture $integration
*
* @return bool
*/
public function is_enabled(MC4WP_Integration_Fixture $integration)
{
return $integration->enabled;
}
/**
* @param MC4WP_Integration $integration
* @return bool
*/
public function is_installed($integration)
{
return $integration->is_installed();
}
/**
* Get the integrations which are enabled
*
* - Some integrations are always enabled because they need manual work
* - Other integrations can be enabled in the settings page
* - Only returns installed integrations
*
* @return array
*/
public function get_enabled_integrations()
{
// get all enabled integrations
$enabled_integrations = array_filter($this->integrations, array( $this, 'is_enabled' ));
// remove duplicate values, for whatever reason..
$enabled_integrations = array_unique($enabled_integrations);
// filter out integrations which are not installed
$installed_enabled_integrations = array_filter($enabled_integrations, array( $this, 'is_installed' ));
return $installed_enabled_integrations;
}
/**
* Gets all integration options in a keyed array
*
* @return array
*/
private function load_options()
{
$options = (array) get_option('mc4wp_integrations', array());
/**
* Filters global integration options
*
* This array holds ALL integration settings
*
* @since 3.0
* @param array $options
* @ignore
*/
return (array) apply_filters('mc4wp_integration_options', $options);
}
/**
* Gets the raw options for an integration
*
* @param $slug
* @return array
*/
public function get_integration_options($slug)
{
static $options;
if ($options === null) {
$options = $this->load_options();
}
return isset($options[ $slug ]) ? $options[ $slug ] : array();
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* Class MC4WP_Integration_Tags
*
* @ignore
* @access private
*/
class MC4WP_Integration_Tags extends MC4WP_Dynamic_Content_Tags
{
/**
* @var MC4WP_Integration
*/
protected $integration;
/**
* Add hooks
*/
public function add_hooks()
{
add_filter('mc4wp_integration_checkbox_label', array( $this, 'replace_in_checkbox_label' ), 10, 2);
}
/**
* Register template tags for integrations
*/
public function register()
{
parent::register();
$this->tags['subscriber_count'] = array(
'description' => __('Replaced with the number of subscribers on the selected list(s)', 'mailchimp-for-wp'),
'callback' => array( $this, 'get_subscriber_count' ),
);
}
/**
* @hooked `mc4wp_integration_checkbox_label`
* @param string $string
* @param MC4WP_Integration $integration
* @return string
*/
public function replace_in_checkbox_label($string, MC4WP_Integration $integration)
{
$this->integration = $integration;
$string = $this->replace($string, 'esc_html');
return $string;
}
/**
* Returns the number of subscribers on the selected lists (for the form context)
*
* @return int
*/
public function get_subscriber_count()
{
$mailchimp = new MC4WP_MailChimp();
$list_ids = $this->integration->get_lists();
$count = $mailchimp->get_subscriber_count($list_ids);
return number_format($count);
}
}

View File

@@ -0,0 +1,590 @@
<?php
/**
* Class MC4WP_Integration
*
* Base class for all integrations.
*
* Extend this class and implement the `add_hooks` method to get a settings page.
*
* @access public
* @since 3.0
* @abstract
*/
abstract class MC4WP_Integration
{
/**
* @var string Name of this integration.
*/
public $name = '';
/**
* @var string Description
*/
public $description = '';
/**
* @var string Slug, used as an unique identifier for this integration.
*/
public $slug = '';
/**
* @var array Array of settings
*/
public $options = array();
/**
* @var string Name attribute for the checkbox element. Will be created from slug if empty.
*/
protected $checkbox_name = '';
/**
* @var string[]
*/
public $checkbox_classes = array();
/**
* @var string[]
*/
public $wrapper_classes = array();
/**
* Constructor
*
* @param string $slug
* @param array $options
*/
public function __construct($slug, array $options)
{
$this->slug = $slug;
$this->options = $this->parse_options($options);
// if checkbox name is not set, set a good custom value
if ($this->checkbox_name === '') {
$this->checkbox_name = '_mc4wp_subscribe_' . $this->slug;
}
}
/**
* Return array of default options
*
* @return array
*/
protected function get_default_options()
{
return array(
'css' => 0,
'double_optin' => 1,
'enabled' => 0,
'implicit' => 0,
'label' => __('Sign me up for the newsletter!', 'mailchimp-for-wp'),
'lists' => array(),
'precheck' => 0,
'replace_interests' => 0,
'update_existing' => 0,
'wrap_p' => 1,
);
}
/**
* @param array $options
*
* @return array
*/
protected function parse_options(array $options)
{
$slug = $this->slug;
$default_options = $this->get_default_options();
$options = array_merge($default_options, $options);
/**
* Filters options for a specific integration
*
* The dynamic portion of the hook, `$slug`, refers to the slug of the ingration.
*
* @param array $integration_options
*/
return (array) apply_filters('mc4wp_integration_' . $slug . '_options', $options);
}
/**
* Initialize the integration
*/
public function initialize()
{
$this->add_required_hooks();
$this->add_hooks();
}
/**
* Adds the required hooks for core functionality, like adding checkbox reset CSS.
*/
protected function add_required_hooks()
{
if ($this->options['css'] && ! $this->options['implicit']) {
add_action('wp_head', array( $this, 'print_css_reset' ));
}
}
/**
* Was integration triggered?
*
* Will always return true when integration is implicit. Otherwise, will check value of checkbox.
*
* @param int $object_id Useful when overriding method. (optional)
* @return bool
*/
public function triggered($object_id = null)
{
return $this->options['implicit'] || $this->checkbox_was_checked();
}
/**
* Adds the hooks which are specific to this integration
*/
abstract protected function add_hooks();
/**
* Print CSS reset
*
* @hooked `wp_head`
*/
public function print_css_reset()
{
$css = file_get_contents(MC4WP_PLUGIN_DIR . '/assets/css/checkbox-reset.css');
// replace selector by integration specific selector so the css affects just this checkbox
$css = str_ireplace('__INTEGRATION_SLUG__', $this->slug, $css);
printf('<style>%s</style>', $css);
}
/**
* Get the text for the label element
*
* @return string
*/
public function get_label_text()
{
$integration = $this;
$label = $this->options['label'];
if (empty($label)) {
$default_options = $this->get_default_options();
$label = $default_options['label'];
}
/**
* Filters the checkbox label
*
* @since 3.0
*
* @param string $label
* @param MC4WP_Integration $integration
* @ignore
*/
$label = (string) apply_filters('mc4wp_integration_checkbox_label', $label, $integration);
return $label;
}
/**
* Was the integration checkbox checked?
*
* @return bool
*/
public function checkbox_was_checked()
{
$data = $this->get_data();
return isset($data[ $this->checkbox_name ]) && (int) $data[ $this->checkbox_name ] === 1;
}
/**
* Get a string of attributes for the HTML element wrapping the checkbox + label
*
* @return string
*/
protected function get_wrapper_attributes()
{
$html_attrs = array(
'class' => sprintf('mc4wp-checkbox mc4wp-checkbox-%s %s', $this->slug, join(' ', $this->wrapper_classes)),
);
return $this->array_to_attr_string($html_attrs);
}
/**
* Get a string of attributes for the checkbox element.
*
* @return string
*/
protected function get_checkbox_attributes()
{
$integration = $this;
$slug = $this->slug;
$attributes = array();
if ($this->options['precheck']) {
$attributes['checked'] = 'checked';
}
if (! empty($this->checkbox_classes)) {
$attributes['class'] = join(' ', $this->checkbox_classes);
}
/**
* Filters the attributes array.
*
* @param array $attributes
* @param MC4WP_Integration $integration
* @ignore
*/
$attributes = (array) apply_filters('mc4wp_integration_checkbox_attributes', $attributes, $integration);
/**
* Filters the attributes array.
*
* The dynamic portion of the hook, `$slug`, refers to the slug for this integration.
*
* @param array $attributes
* @param MC4WP_Integration $integration
* @ignore
*/
$attributes = (array) apply_filters('mc4wp_integration_' . $slug . '_checkbox_attributes', $attributes, $integration);
return $this->array_to_attr_string($attributes);
}
/**
* Outputs a checkbox
*/
public function output_checkbox()
{
echo $this->get_checkbox_html();
}
/**
* Get HTML string for the checkbox row (incl. wrapper, label, etc.)
*
* @return string
*/
public function get_checkbox_html()
{
$show_checkbox = empty($this->options['implicit']);
$integration_slug = $this->slug;
/**
* Filters whether to show the sign-up checkbox for this integration.
*
* @param bool $show_checkbox
* @param string $integration_slug
*/
$show_checkbox = (bool) apply_filters('mc4wp_integration_show_checkbox', $show_checkbox, $integration_slug);
if (! $show_checkbox) {
return '';
}
ob_start();
echo sprintf('<!-- Mailchimp for WordPress v%s - https://www.mc4wp.com/ -->', MC4WP_VERSION);
/** @ignore */
do_action('mc4wp_integration_before_checkbox_wrapper', $this);
/** @ignore */
do_action('mc4wp_integration_' . $this->slug . '_before_checkbox_wrapper', $this);
$wrapper_tag = $this->options['wrap_p'] ? 'p' : 'span';
$wrapper_attrs = $this->get_wrapper_attributes();
// Hidden field to make sure "0" is sent to server
echo sprintf('<input type="hidden" name="%s" value="0" />', esc_attr($this->checkbox_name));
echo sprintf('<%s %s>', $wrapper_tag, $wrapper_attrs);
echo '<label>';
echo sprintf('<input type="checkbox" name="%s" value="1" %s />', esc_attr($this->checkbox_name), $this->get_checkbox_attributes());
echo sprintf('<span>%s</span>', $this->get_label_text());
echo '</label>';
echo sprintf('</%s>', $wrapper_tag);
/** @ignore */
do_action('mc4wp_integration_after_checkbox_wrapper', $this);
/** @ignore */
do_action('mc4wp_integration_' . $this->slug . '_after_checkbox_wrapper', $this);
echo '<!-- / Mailchimp for WordPress -->';
$html = ob_get_clean();
return $html;
}
/**
* Get the selected Mailchimp lists
*
* @return array Array of List ID's
*/
public function get_lists()
{
$data = $this->get_data();
$integration = $this;
$slug = $this->slug;
// get checkbox lists options
$lists = $this->options['lists'];
// get lists from request, if set.
if (! empty($data['_mc4wp_lists'])) {
$lists = $data['_mc4wp_lists'];
// ensure lists is an array
if (! is_array($lists)) {
$lists = explode(',', $lists);
$lists = array_map('trim', $lists);
}
}
/**
* Allow plugins to filter final lists value. This filter is documented elsewhere.
*
* @since 2.0
* @see MC4WP_Form::get_lists
* @ignore
*/
$lists = (array) apply_filters('mc4wp_lists', $lists);
/**
* Filters the Mailchimp lists this integration should subscribe to
*
* @since 3.0
*
* @param array $lists
* @param MC4WP_Integration $integration
*/
$lists = (array) apply_filters('mc4wp_integration_lists', $lists, $integration);
/**
* Filters the Mailchimp lists a specific integration should subscribe to
*
* The dynamic portion of the hook, `$slug`, refers to the slug of the integration.
*
* @since 3.0
*
* @param array $lists
* @param MC4WP_Integration $integration
*/
$lists = (array) apply_filters('mc4wp_integration_' . $slug . '_lists', $lists, $integration);
return $lists;
}
/**
* Makes a subscription request
*
* @param array $data
* @param int $related_object_id
*
* @return boolean
*/
protected function subscribe(array $data, $related_object_id = 0)
{
$integration = $this;
$slug = $this->slug;
$mailchimp = new MC4WP_MailChimp();
$log = $this->get_log();
$list_ids = $this->get_lists();
/** @var MC4WP_MailChimp_Subscriber $subscriber */
$subscriber = null;
$result = false;
// validate lists
if (empty($list_ids)) {
$log->warning(sprintf('%s > No Mailchimp lists were selected', $this->name));
return false;
}
/**
* Filters data for integration requests.
*
* @param array $data
*/
$data = apply_filters('mc4wp_integration_data', $data);
/**
* Filters data for a specific integration request.
*
* The dynamic portion of the hook, `$slug`, refers to the integration slug.
*
* @param array $data
* @param int $related_object_id
*/
$data = apply_filters("mc4wp_integration_{$slug}_data", $data, $related_object_id);
$email_type = mc4wp_get_email_type();
$mapper = new MC4WP_List_Data_Mapper($data, $list_ids);
/** @var MC4WP_MailChimp_Subscriber[] $map */
$map = $mapper->map();
foreach ($map as $list_id => $subscriber) {
$subscriber->status = $this->options['double_optin'] ? 'pending' : 'subscribed';
$subscriber->email_type = $email_type;
$subscriber->ip_signup = mc4wp_get_request_ip_address();
/** @ignore (documented elsewhere) */
$subscriber = apply_filters('mc4wp_subscriber_data', $subscriber);
if (! $subscriber instanceof MC4WP_MailChimp_Subscriber) {
continue;
}
/**
* Filters subscriber data before it is sent to Mailchimp. Only fires for integration requests.
*
* @param MC4WP_MailChimp_Subscriber $subscriber
*/
$subscriber = apply_filters('mc4wp_integration_subscriber_data', $subscriber);
if (! $subscriber instanceof MC4WP_MailChimp_Subscriber) {
continue;
}
/**
* Filters subscriber data before it is sent to Mailchimp. Only fires for integration requests.
*
* The dynamic portion of the hook, `$slug`, refers to the integration slug.
*
* @param MC4WP_MailChimp_Subscriber $subscriber
* @param int $related_object_id
*/
$subscriber = apply_filters("mc4wp_integration_{$slug}_subscriber_data", $subscriber, $related_object_id);
if (! $subscriber instanceof MC4WP_MailChimp_Subscriber) {
continue;
}
$result = $mailchimp->list_subscribe($list_id, $subscriber->email_address, $subscriber->to_array(), $this->options['update_existing'], $this->options['replace_interests']);
}
// if result failed, show error message
if (! $result) {
// log error
if ((int) $mailchimp->get_error_code() === 214) {
$log->warning(sprintf('%s > %s is already subscribed to the selected list(s)', $this->name, $subscriber->email_address));
} else {
$log->error(sprintf('%s > Mailchimp API Error: %s', $this->name, $mailchimp->get_error_message()));
}
// bail
return false;
}
$log->info(sprintf('%s > Successfully subscribed %s', $this->name, $subscriber->email_address));
/**
* Runs right after someone is subscribed using an integration
*
* @since 3.0
*
* @param MC4WP_Integration $integration
* @param string $email_address
* @param array $merge_vars
* @param MC4WP_MailChimp_Subscriber[] $subscriber_data
* @param int $related_object_id
*/
do_action('mc4wp_integration_subscribed', $integration, $subscriber->email_address, $subscriber->merge_fields, $map, $related_object_id);
return true;
}
/**
* Are the required dependencies for this integration installed?
*
* @return bool
*/
public function is_installed()
{
return false;
}
/**
* Which UI elements should we show on the settings page for this integration?
*
* @return array
*/
public function get_ui_elements()
{
return array_keys($this->options);
}
/**
* Does integration have the given UI element?
*
* @param string $element
* @return bool
*/
public function has_ui_element($element)
{
$elements = $this->get_ui_elements();
return in_array($element, $elements, true);
}
/**
* Return a string to the admin settings page for this object (if any)
*
* @param int $object_id
* @return string
*/
public function get_object_link($object_id)
{
return '';
}
/**
* Get the data for this integration request
*
* By default, this will return a combination of all $_GET and $_POST parameters.
* Override this method if you need data from somewhere else.
*
* This data should contain the value of the checkbox (required)
* and the lists to which should be subscribed (optional)
*
* @see MC4WP_Integration::$checkbox_name
* @see MC4WP_Integration::get_lists
* @see MC4WP_Integration::checkbox_was_checked
*
* @return array
*/
public function get_data()
{
return array_merge((array) $_GET, (array) $_POST);
}
/**
* Converts an array to an attribute string (foo="bar" bar="foo") with escaped values.
*
* @param array $attrs
* @return string
*/
protected function array_to_attr_string(array $attrs)
{
$str = '';
foreach ($attrs as $key => $value) {
$str .= sprintf('%s="%s" ', $key, esc_attr($value));
}
return $str;
}
/**
* @return MC4WP_Debug_Log
*/
protected function get_log()
{
return mc4wp('log');
}
/**
* @return MC4WP_API_V3
*/
protected function get_api()
{
return mc4wp('api');
}
}

View File

@@ -0,0 +1,58 @@
<?php
defined('ABSPATH') or exit;
/**
* Class MC4WP_User_Integration
*
* @access public
* @since 2.0
*/
abstract class MC4WP_User_Integration extends MC4WP_Integration
{
/**
* @param WP_User $user
*
* @return array
*/
protected function user_merge_vars(WP_User $user)
{
// start with user_login as name, since that's always known
$data = array(
'EMAIL' => $user->user_email,
'NAME' => $user->user_login,
);
if ('' !== $user->first_name) {
$data['NAME'] = $user->first_name;
$data['FNAME'] = $user->first_name;
}
if ('' !== $user->last_name) {
$data['LNAME'] = $user->last_name;
}
if ('' !== $user->first_name && '' !== $user->last_name) {
$data['NAME'] = sprintf('%s %s', $user->first_name, $user->last_name);
}
/**
* @use mc4wp_integration_user_data
* @since 3.0
* @deprecated 4.0
* @ignore
*/
$data = (array) apply_filters('mc4wp_user_merge_vars', $data, $user);
/**
* Filters the data for user-related integrations
* @since 4.2
* @param array $data
* @param WP_User $user
*/
$data = (array) apply_filters('mc4wp_integration_user_data', $data, $user);
return $data;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* Gets an array of all registered integrations
*
* @since 3.0
* @access public
*
* @return MC4WP_Integration[]
*/
function mc4wp_get_integrations()
{
return mc4wp('integrations')->get_all();
}
/**
* Get an instance of a registered integration class
*
* @since 3.0
* @access public
*
* @param string $slug
*
* @return MC4WP_Integration
*/
function mc4wp_get_integration($slug)
{
return mc4wp('integrations')->get($slug);
}
/**
* Register a new integration with Mailchimp for WordPress
*
* @since 3.0
* @access public
*
* @param string $slug
* @param string $class
*
* @param bool $always_enabled
*/
function mc4wp_register_integration($slug, $class, $always_enabled = false)
{
return mc4wp('integrations')->register_integration($slug, $class, $always_enabled);
}
/**
* Deregister a previously registered integration with Mailchimp for WordPress
*
* @since 3.0
* @access public
* @param string $slug
*/
function mc4wp_deregister_integration($slug)
{
mc4wp('integrations')->deregister_integration($slug);
}

View File

@@ -0,0 +1,330 @@
<?php defined('ABSPATH') or exit;
/** @var MC4WP_Integration $integration */
/** @var array $opts */
?>
<div id="mc4wp-admin" class="wrap mc4wp-settings">
<p class="mc4wp-breadcrumbs">
<span class="prefix"><?php echo esc_html__('You are here: ', 'mailchimp-for-wp'); ?></span>
<a href="<?php echo esc_url(admin_url('admin.php?page=mailchimp-for-wp')); ?>">Mailchimp for WordPress</a> &rsaquo;
<a href="<?php echo esc_url(admin_url('admin.php?page=mailchimp-for-wp-integrations')); ?>"><?php echo esc_html__('Integrations', 'mailchimp-for-wp'); ?></a> &rsaquo;
<span class="current-crumb"><strong><?php echo esc_html($integration->name); ?></strong></span>
</p>
<div class="mc4wp-row">
<!-- Main Content -->
<div class="main-content mc4wp-col">
<h1 class="mc4wp-page-title">
<?php printf(esc_html__('%s integration', 'mailchimp-for-wp'), esc_html($integration->name)); ?>
</h1>
<h2 style="display: none;"></h2>
<?php settings_errors(); ?>
<div id="notice-additional-fields" class="notice notice-warning" style="display: none;">
<p><?php echo esc_html__('The selected Mailchimp lists require non-default fields, which may prevent this integration from working.', 'mailchimp-for-wp'); ?></p>
<p><?php echo sprintf(wp_kses(__('Please ensure you <a href="%1$s">configure the plugin to send all required fields</a> or <a href="%2$s">log into your Mailchimp account</a> and make sure only the email & name fields are marked as required fields for the selected list(s).', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), 'https://www.mc4wp.com/kb/send-additional-fields-from-integrations/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=integrations-page', 'https://admin.mailchimp.com/lists/'); ?></p>
</div>
<p>
<?php echo esc_html($integration->description); ?>
</p>
<!-- Settings form -->
<form method="post" action="<?php echo admin_url('options.php'); ?>">
<?php settings_fields('mc4wp_integrations_settings'); ?>
<?php
/**
* Runs just before integration settings are outputted in admin.
*
* @since 3.0
*
* @param MC4WP_Integration $integration
* @param array $opts
* @ignore
*/
do_action('mc4wp_admin_before_integration_settings', $integration, $opts);
/**
* @ignore
*/
do_action('mc4wp_admin_before_' . $integration->slug . '_integration_settings', $integration, $opts);
?>
<table class="form-table">
<?php
if ($integration->has_ui_element('enabled')) {
?>
<tr valign="top">
<th scope="row"><?php echo esc_html__('Enabled?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap integration-toggles-wrap">
<label><input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][enabled]" value="1" <?php checked($opts['enabled'], 1); ?> /> <?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?></label> &nbsp;
<label><input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][enabled]" value="0" <?php checked($opts['enabled'], 0); ?> /> <?php echo esc_html__('No', 'mailchimp-for-wp'); ?></label>
<p class="description"><?php echo sprintf(esc_html__('Enable the %s integration? This will add a sign-up checkbox to the form.', 'mailchimp-for-wp'), $integration->name); ?></p>
</td>
</tr>
<?php
}
?>
<?php
$config = array(
'element' => 'mc4wp_integrations[' . $integration->slug . '][enabled]',
'value' => '1',
'hide' => false,
);
?>
<tbody class="integration-toggled-settings" data-showif="<?php echo esc_attr(json_encode($config)); ?>">
<?php
if ($integration->has_ui_element('implicit')) {
?>
<tr valign="top">
<th scope="row"><?php echo esc_html__('Implicit?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label><input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][implicit]" value="1" <?php checked($opts['implicit'], 1); ?> /> <?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?></label> &nbsp;
<label><input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][implicit]" value="0" <?php checked($opts['implicit'], 0); ?> /> <?php echo esc_html__('No', 'mailchimp-for-wp'); ?> <?php echo '<em>', esc_html__('(recommended)', 'mailchimp-for-wp'), '</em>'; ?>
</label>
<p class="description">
<?php
echo esc_html__('Select "yes" if you want to subscribe people without asking them explicitly.', 'mailchimp-for-wp');
echo '<br />';
echo sprintf(
wp_kses(
__('<strong>Warning: </strong> enabling this may affect your <a href="%s">GDPR compliance</a>.', 'mailchimp-for-wp'),
array(
'a' => array( 'href' => array() ),
'strong' => array(),
)
),
'https://www.mc4wp.com/kb/gdpr-compliance/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=integrations-page'
);
?>
</p>
</td>
</tr>
<?php
}
?>
<?php
if ($integration->has_ui_element('lists')) {
?>
<?php // hidden input to make sure a value is sent to the server when no checkboxes were selected ?>
<input type="hidden" name="mc4wp_integrations[<?php echo $integration->slug; ?>][lists][]" value="" />
<tr valign="top">
<th scope="row"><?php echo esc_html__('Mailchimp Lists', 'mailchimp-for-wp'); ?></th>
<?php
if (! empty($lists)) {
echo '<td>';
echo '<ul style="margin-bottom: 20px; max-height: 300px; overflow-y: auto;">';
foreach ($lists as $list) {
echo '<li><label>';
echo sprintf('<input type="checkbox" name="mc4wp_integrations[%s][lists][]" value="%s" class="mc4wp-list-input" %s> ', $integration->slug, $list->id, checked(in_array($list->id, $opts['lists'], true), true, false));
echo esc_html($list->name);
echo '</label></li>';
}
echo '</ul>';
echo '<p class="description">';
echo esc_html__('Select the list(s) to which people who check the checkbox should be subscribed.', 'mailchimp-for-wp');
echo '</p>';
echo '</td>';
} else {
echo '<td>', sprintf(wp_kses(__('No lists found, <a href="%s">are you connected to Mailchimp</a>?', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), esc_url(admin_url('admin.php?page=mailchimp-for-wp'))), '</td>';
}
?>
</tr>
<?php
} // end if UI has lists
?>
<?php
if ($integration->has_ui_element('label')) {
$config = array(
'element' => 'mc4wp_integrations[' . $integration->slug . '][implicit]',
'value' => 0,
);
?>
<tr valign="top" data-showif="<?php echo esc_attr(json_encode($config)); ?>">
<th scope="row"><label for="mc4wp_checkbox_label"><?php echo esc_html__('Checkbox label text', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" id="mc4wp_checkbox_label" name="mc4wp_integrations[<?php echo $integration->slug; ?>][label]" value="<?php echo esc_attr($opts['label']); ?>" required />
<p class="description"><?php printf(esc_html__('HTML tags like %s are allowed in the label text.', 'mailchimp-for-wp'), '<code>' . esc_html('<strong><em><a>') . '</code>'); ?></p>
</td>
</tr>
<?php
} // end if UI label
?>
<?php
if ($integration->has_ui_element('precheck')) {
$config = array(
'element' => 'mc4wp_integrations[' . $integration->slug . '][implicit]',
'value' => 0,
);
?>
<tr valign="top" data-showif="<?php echo esc_attr(json_encode($config)); ?>">
<th scope="row"><?php echo esc_html__('Pre-check the checkbox?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label><input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][precheck]" value="1" <?php checked($opts['precheck'], 1); ?> /> <?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?></label> &nbsp;
<label><input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][precheck]" value="0" <?php checked($opts['precheck'], 0); ?> /> <?php echo esc_html__('No', 'mailchimp-for-wp'); ?> <?php echo '<em>' . __('(recommended)', 'mailchimp-for-wp') . '</em>'; ?></label>
<p class="description">
<?php
echo esc_html__('Select "yes" if the checkbox should be pre-checked.', 'mailchimp-for-wp');
echo '<br />';
echo sprintf(
wp_kses(
__('<strong>Warning: </strong> enabling this may affect your <a href="%s">GDPR compliance</a>.', 'mailchimp-for-wp'),
array(
'a' => array( 'href' => array() ),
'strong' => array(),
)
),
'https://www.mc4wp.com/kb/gdpr-compliance/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=integrations-page'
);
?>
</p>
</td>
<?php
} // end if UI precheck
?>
<?php
if ($integration->has_ui_element('css')) {
$config = array(
'element' => 'mc4wp_integrations[' . $integration->slug . '][implicit]',
'value' => 0,
);
?>
<tr valign="top" data-showif="<?php echo esc_attr(json_encode($config)); ?>">
<th scope="row"><?php echo esc_html__('Load some default CSS?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label><input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][css]" value="1" <?php checked($opts['css'], 1); ?> />&rlm; <?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?></label> &nbsp;
<label><input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][css]" value="0" <?php checked($opts['css'], 0); ?> />&rlm; <?php echo esc_html__('No', 'mailchimp-for-wp'); ?></label>
<p class="description"><?php echo esc_html__('Select "yes" if the checkbox appears in a weird place.', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<?php
} // end if UI css
?>
<?php
if ($integration->has_ui_element('double_optin')) {
?>
<tr valign="top">
<th scope="row"><?php echo esc_html__('Double opt-in?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label>
<input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][double_optin]" value="1" <?php checked($opts['double_optin'], 1); ?> />&rlm;
<?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?>
</label> &nbsp;
<label>
<input type="radio" id="mc4wp_checkbox_double_optin_0" name="mc4wp_integrations[<?php echo $integration->slug; ?>][double_optin]" value="0" <?php checked($opts['double_optin'], 0); ?> />&rlm;
<?php echo esc_html__('No', 'mailchimp-for-wp'); ?>
</label>
<p class="description">
<?php echo esc_html__('Select "yes" if you want people to confirm their email address before being subscribed (recommended)', 'mailchimp-for-wp'); ?>
</p>
</td>
</tr>
<?php
} // end if UI double_optin
?>
<?php
if ($integration->has_ui_element('update_existing')) {
?>
<tr valign="top">
<th scope="row"><?php echo esc_html__('Update existing subscribers?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label>
<input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][update_existing]" value="1" <?php checked($opts['update_existing'], 1); ?> />&rlm;
<?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?>
</label> &nbsp;
<label>
<input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][update_existing]" value="0" <?php checked($opts['update_existing'], 0); ?> />&rlm;
<?php echo esc_html__('No', 'mailchimp-for-wp'); ?>
</label>
<p class="description"><?php echo esc_html__('Select "yes" if you want to update existing subscribers with the data that is sent.', 'mailchimp-for-wp'); ?></p>
</td>
</tr>
<?php
} // end if UI update_existing
?>
<?php
if ($integration->has_ui_element('replace_interests')) {
$config = array(
'element' => 'mc4wp_integrations[' . $integration->slug . '][update_existing]',
'value' => 1,
);
?>
<tr valign="top" data-showif="<?php echo esc_attr(json_encode($config)); ?>">
<th scope="row"><?php echo esc_html__('Replace interest groups?', 'mailchimp-for-wp'); ?></th>
<td class="nowrap">
<label>
<input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][replace_interests]" value="1" <?php checked($opts['replace_interests'], 1); ?> />&rlm;
<?php echo esc_html__('Yes', 'mailchimp-for-wp'); ?>
</label> &nbsp;
<label>
<input type="radio" name="mc4wp_integrations[<?php echo $integration->slug; ?>][replace_interests]" value="0" <?php checked($opts['replace_interests'], 0); ?> />&rlm;
<?php echo esc_html__('No', 'mailchimp-for-wp'); ?>
</label>
<p class="description">
<?php echo esc_html__('Select "no" if you want to add the selected interests to any previously selected interests when updating a subscriber.', 'mailchimp-for-wp'); ?>
<?php echo sprintf('<a href="%s" target="_blank">' . esc_html__('What does this do?', 'mailchimp-for-wp') . '</a>', 'https://www.mc4wp.com/kb/what-does-replace-groupings-mean/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=integrations-page'); ?>
</p>
</td>
</tr>
<?php
} // end if UI replace_interests
?>
</tbody>
</table>
<?php
/**
* Runs right after integration settings are outputted (before the submit button).
*
* @param MC4WP_Integration $integration
* @param array $opts
* @ignore
*/
do_action('mc4wp_admin_after_integration_settings', $integration, $opts);
/**
* @ignore
*/
do_action('mc4wp_admin_after_' . $integration->slug . '_integration_settings', $integration, $opts);
?>
<?php
if (count($integration->get_ui_elements()) > 0) {
submit_button();
}
?>
</form>
</div>
<!-- Sidebar -->
<div class="mc4wp-sidebar mc4wp-col">
<?php require MC4WP_PLUGIN_DIR . '/includes/views/parts/admin-sidebar.php'; ?>
</div>
</div>
</div>

View File

@@ -0,0 +1,142 @@
<?php defined('ABSPATH') or exit;
/** @var MC4WP_Integration_Fixture[] $enabled_integrations */
/** @var MC4WP_Integration_Fixture[] $available_integrations */
/** @var MC4WP_Integration_Fixture $integration */
function _mc4wp_integrations_table_row($integration)
{
?>
<tr style="
<?php
if (! $integration->is_installed()) {
echo 'opacity: 0.6;';
}
?>
">
<!-- Integration Name -->
<td>
<?php
if ($integration->is_installed()) {
echo sprintf('<strong><a href="%s" title="%s">%s</a></strong>', esc_attr(add_query_arg(array( 'integration' => $integration->slug ))), esc_html__('Configure this integration', 'mailchimp-for-wp'), $integration->name);
} else {
echo $integration->name;
}
?>
</td>
<td class="desc">
<?php
echo esc_html($integration->description);
?>
</td>
<td>
<?php
if ($integration->enabled && $integration->is_installed()) {
echo '<span class="mc4wp-status positive">', esc_html__('Active', 'mailchimp-for-wp'), '</span>';
} elseif ($integration->is_installed()) {
echo '<span class="mc4wp-status neutral">', esc_html__('Inactive', 'mailchimp-for-wp'), '</span>';
} else {
echo '<span>', esc_html__('Not installed', 'mailchimp-for-wp'), '</span>';
}
?>
</td>
</tr>
<?php
}
/**
* Render a table with integrations
*
* @param $integrations
* @ignore
*/
function _mc4wp_integrations_table($integrations)
{
?>
<table class="mc4wp-table widefat striped">
<thead>
<tr>
<th><?php echo esc_html__('Name', 'mailchimp-for-wp'); ?></th>
<th><?php echo esc_html__('Description', 'mailchimp-for-wp'); ?></th>
<th><?php echo esc_html__('Status', 'mailchimp-for-wp'); ?></th>
</tr>
</thead>
<tbody>
<?php
// active & enabled integrations first
foreach ($integrations as $integration) {
if ($integration->is_installed() && $integration->enabled) {
_mc4wp_integrations_table_row($integration);
}
}
// active & disabled integrations next
foreach ($integrations as $integration) {
if ($integration->is_installed() && ! $integration->enabled) {
_mc4wp_integrations_table_row($integration);
}
}
// rest
foreach ($integrations as $integration) {
if (! $integration->is_installed()) {
_mc4wp_integrations_table_row($integration);
}
}
?>
</tbody>
</table>
<?php
}
?>
<div id="mc4wp-admin" class="wrap mc4wp-settings">
<p class="mc4wp-breadcrumbs">
<span class="prefix"><?php echo esc_html__('You are here: ', 'mailchimp-for-wp'); ?></span>
<a href="<?php echo admin_url('admin.php?page=mailchimp-for-wp'); ?>">Mailchimp for WordPress</a> &rsaquo;
<span class="current-crumb"><strong><?php echo esc_html__('Integrations', 'mailchimp-for-wp'); ?></strong></span>
</p>
<div class="mc4wp-row">
<!-- Main Content -->
<div class="mc4wp-col mc4wp-col-4">
<h1 class="mc4wp-page-title">Mailchimp for WordPress: <?php echo esc_html__('Integrations', 'mailchimp-for-wp'); ?></h1>
<h2 style="display: none;"></h2>
<?php settings_errors(); ?>
<p>
<?php echo esc_html__('The table below shows all available integrations.', 'mailchimp-for-wp'); ?>
<?php echo esc_html__('Click on the name of an integration to edit all settings specific to that integration.', 'mailchimp-for-wp'); ?>
</p>
<form action="<?php echo admin_url('options.php'); ?>" method="post">
<?php settings_fields('mc4wp_integrations_settings'); ?>
<h3><?php echo esc_html__('Integrations', 'mailchimp-for-wp'); ?></h3>
<?php _mc4wp_integrations_table($integrations); ?>
<p><?php echo esc_html__('Greyed out integrations will become available after installing & activating the corresponding plugin.', 'mailchimp-for-wp'); ?></p>
</form>
</div>
<!-- Sidebar -->
<div class="mc4wp-sidebar mc4wp-col">
<?php require MC4WP_PLUGIN_DIR . '/includes/views/parts/admin-sidebar.php'; ?>
</div>
</div>
</div>

View File

@@ -0,0 +1,59 @@
<?php
defined('ABSPATH') or exit;
?>
<div id="mc4wp-admin" class="wrap mc4wp-settings">
<style>
#mc4wp-admin h4{ margin-bottom: 2px; }
#mc4wp-admin h4 + p { margin-top: 0; }
</style>
<h1 class="mc4wp-page-title">Mailchimp for WordPress: Add-on plugins</h1>
<div class="mc4wp-margin-m" >
<h2><span style="color: #c44;">Mailchimp for WordPress Premium</span>, take your email marketing to the next level!</h2>
<p>You're currently on the free version of the <strong>MC4WP: Mailchimp for WordPress</strong> plugin.</p>
<p>Did you know that there is a premium version too? It comes with the following additional features:</p>
<ul class="ul-square">
<li><strong>Multiple and improved forms</strong> &mdash; allowing an unlimited amount of sign-up forms that submit without requiring a full page reload.</li>
<li><strong>E-Commerce integration</strong> &mdash; tightly integrate your WooCommerce store with Mailchimp.
<li><strong>User Sync</strong> &mdash; keep your WordPress user database in sync with a Mailchimp list.</li>
<li><strong>Logging</strong> - every form submission is stored locally, allowing charted data and exporting to CSV or JSON</li>
<li><strong>Form designer</strong> &mdash; make your forms look pretty without having to know or write a single line of CSS.</li>
<li><strong>Append form to posts</strong> &mdash; an easy setting to automatically append a form to all posts (in a certain category).</li>
<li><strong>Priority support</strong> &mdash; gain access to our 24/7 support team.</li>
</ul>
<p>
<a href="https://www.mc4wp.com/pricing/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=extensions-page" class="button" style="background: #c44; border: #c44; color: white; padding: 6px 12px; height: auto; font-weight: bold;">Buy Mailchimp for WordPress Premium</a> &nbsp;
<a href="https://www.mc4wp.com/premium-features/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=extensions-page"><span style="text-decoration: none; vertical-align: middle; margin-right: 6px;" class="dashicons dashicons-info"> </span>More information</a>
</p>
<p><em>Comes with <a href="https://www.mc4wp.com/refund-policy/">our 30-day no questions asked money back guarantee</a>.</em>
</div>
<hr />
<div class="mc4wp-margin-m">
<p>The following (free) add-on plugins are available for Mailchimp for WordPress.</p>
<div>
<h4><a href="https://wordpress.org/plugins/mailchimp-top-bar/">Mailchimp Top Bar</a></h4>
<p>Adds a sign-up bar to the top or bottom of your site. A sure fire way to grow your lists.</p>
</div>
<div>
<h4><a href="https://wordpress.org/plugins/mc4wp-wpml/">WPML Integration</a></h4>
<p>Improved Mailchimp integration for multilingual sites using WPML.</p>
</div>
<div>
<h4><a href="https://wordpress.org/plugins/boxzilla/">Boxzilla Pop-ups</a></h4>
<p>Pop-ups for your sign-up forms.</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,106 @@
<?php
defined('ABSPATH') or exit;
?>
<div id="mc4wp-admin" class="wrap mc4wp-settings">
<p class="mc4wp-breadcrumbs">
<span class="prefix"><?php echo esc_html__('You are here: ', 'mailchimp-for-wp'); ?></span>
<span class="current-crumb"><strong>Mailchimp for WordPress</strong></span>
</p>
<div class="mc4wp-row">
<!-- Main Content -->
<div class="main-content mc4wp-col">
<h1 class="mc4wp-page-title">
Mailchimp for WordPress: <?php echo esc_html__('API Settings', 'mailchimp-for-wp'); ?>
</h1>
<h2 style="display: none;"></h2>
<?php
settings_errors();
$this->messages->show();
?>
<form action="<?php echo admin_url('options.php'); ?>" method="post">
<?php settings_fields('mc4wp_settings'); ?>
<table class="form-table">
<tr valign="top">
<th scope="row">
<?php echo esc_html__('Status', 'mailchimp-for-wp'); ?>
</th>
<td>
<?php
if ($connected) {
?>
<span class="mc4wp-status positive"><?php echo esc_html__('CONNECTED', 'mailchimp-for-wp'); ?></span>
<?php
} else {
?>
<span class="mc4wp-status neutral"><?php echo esc_html__('NOT CONNECTED', 'mailchimp-for-wp'); ?></span>
<?php
}
?>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="mailchimp_api_key"><?php echo esc_html__('API Key', 'mailchimp-for-wp'); ?></label></th>
<td>
<input type="text" class="widefat" placeholder="<?php echo esc_html__('Your Mailchimp API key', 'mailchimp-for-wp'); ?>" id="mailchimp_api_key" name="mc4wp[api_key]" value="<?php echo esc_attr($obfuscated_api_key); ?>" <?php echo defined('MC4WP_API_KEY') ? 'readonly="readonly"' : ''; ?> />
<p class="description">
<?php echo esc_html__('The API key for connecting with your Mailchimp account.', 'mailchimp-for-wp'); ?>
<a target="_blank" href="https://admin.mailchimp.com/account/api"><?php echo esc_html__('Get your API key here.', 'mailchimp-for-wp'); ?></a>
</p>
<?php
if (defined('MC4WP_API_KEY')) {
echo '<p class="description">', wp_kses(__('You defined your Mailchimp API key using the <code>MC4WP_API_KEY</code> constant.', 'mailchimp-for-wp'), array( 'code' => array() )), '</p>';
}
?>
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
<?php
/**
* Runs right after general settings are outputted in admin.
*
* @since 3.0
* @ignore
*/
do_action('mc4wp_admin_after_general_settings');
if (! empty($opts['api_key'])) {
echo '<hr />';
include __DIR__ . '/parts/lists-overview.php';
}
require __DIR__ . '/parts/admin-footer.php';
?>
</div>
<!-- Sidebar -->
<div class="mc4wp-sidebar mc4wp-col">
<?php require __DIR__ . '/parts/admin-sidebar.php'; ?>
</div>
</div>
</div>

View File

@@ -0,0 +1,176 @@
<?php
defined('ABSPATH') or exit;
/** @var array $opts */
/** @var MC4WP_Debug_Log $log */
/** @var MC4WP_Debug_Log_Reader $log_reader */
?>
<div id="mc4wp-admin" class="wrap mc4wp-settings">
<p class="mc4wp-breadcrumbs">
<span class="prefix"><?php echo esc_html__('You are here: ', 'mailchimp-for-wp'); ?></span>
<a href="<?php echo admin_url('admin.php?page=mailchimp-for-wp'); ?>">Mailchimp for WordPress</a> &rsaquo;
<span class="current-crumb"><strong><?php echo esc_html__('Other Settings', 'mailchimp-for-wp'); ?></strong></span>
</p>
<div class="mc4wp-row">
<!-- Main Content -->
<div class="main-content mc4wp-col">
<h1 class="mc4wp-page-title">
<?php echo esc_html__('Other Settings', 'mailchimp-for-wp'); ?>
</h1>
<h2 style="display: none;"></h2>
<?php settings_errors(); ?>
<?php
/**
* @ignore
*/
do_action('mc4wp_admin_before_other_settings', $opts);
?>
<!-- Settings -->
<form action="<?php echo admin_url('options.php'); ?>" method="post">
<?php settings_fields('mc4wp_settings'); ?>
<div class="mc4wp-margin-m" >
<h3><?php echo esc_html__('Miscellaneous settings', 'mailchimp-for-wp'); ?></h3>
<table class="form-table">
<tr>
<th><label for="mc4wp-debug-log-level"><?php echo esc_html__('Logging', 'mailchimp-for-wp'); ?></label></th>
<td>
<select id="mc4wp-debug-log-level" name="mc4wp[debug_log_level]">
<option value="warning" <?php selected('warning', $opts['debug_log_level']); ?>><?php echo esc_html__('Errors & warnings only', 'mailchimp-for-wp'); ?></option>
<option value="debug" <?php selected('debug', $opts['debug_log_level']); ?>><?php echo esc_html__('Everything', 'mailchimp-for-wp'); ?></option>
</select>
<p class="description">
<?php echo sprintf(wp_kses(__('Determines what events should be written to <a href="%s">the debug log</a> (see below).', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), 'https://www.mc4wp.com/kb/how-to-enable-log-debugging/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=settings-page'); ?>
</p>
</td>
</tr>
</table>
</div>
<?php
/**
* @ignore
*/
do_action('mc4wp_admin_other_settings', $opts);
?>
<div style="margin-top: -20px;"><?php submit_button(); ?></div>
</form>
<!-- Debug Log -->
<div class="mc4wp-margin-m">
<h3><?php echo esc_html__('Debug Log', 'mailchimp-for-wp'); ?> <input type="text" id="debug-log-filter" class="alignright regular-text" placeholder="<?php echo esc_attr__('Filter..', 'mailchimp-for-wp'); ?>" /></h3>
<?php
if (! $log->test()) {
echo '<p>';
echo esc_html__('Log file is not writable.', 'mailchimp-for-wp') . ' ';
echo sprintf(wp_kses(__('Please ensure %1$s has the proper <a href="%2$s">file permissions</a>.', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), '<code>' . $log->file . '</code>', 'https://codex.wordpress.org/Changing_File_Permissions');
echo '</p>';
// hack to hide filter input
echo '<style>#debug-log-filter { display: none; }</style>';
} else {
?>
<div id="debug-log" class="mc4wp-log widefat">
<?php
$line = $log_reader->read_as_html();
if (! empty($line)) {
while (is_string($line)) {
if (! empty($line)) {
echo '<div class="debug-log-line">' . $line . '</div>';
}
$line = $log_reader->read_as_html();
}
} else {
echo '<div class="debug-log-empty">';
echo '-- ', esc_html__('Nothing here. Which means there are no errors!', 'mailchimp-for-wp');
echo '</div>';
}
?>
</div>
<form method="post">
<input type="hidden" name="_mc4wp_action" value="empty_debug_log">
<?php wp_nonce_field('_mc4wp_action', '_wpnonce'); ?>
<p>
<input type="submit" class="button" value="<?php echo esc_attr__('Empty Log', 'mailchimp-for-wp'); ?>"/>
</p>
</form>
<?php
} // end if is writable
if ($log->level >= 300) {
echo '<p>';
echo esc_html__('Right now, the plugin is configured to only log errors and warnings.', 'mailchimp-for-wp');
echo '</p>';
}
?>
<script>
(function() {
// scroll to bottom of log
let log = document.getElementById("debug-log"),
logItems;
log.scrollTop = log.scrollHeight;
log.style.minHeight = '';
log.style.maxHeight = '';
log.style.height = log.clientHeight + "px";
// add filter
document.getElementById('debug-log-filter').addEventListener('keydown', function(evt) {
if(evt.keyCode === 13 ) {
searchLog(evt.target.value.trim());
}
});
// search log for query
function searchLog(query) {
if( ! logItems ) {
logItems = [].map.call(log.children, function(node) {
return node.cloneNode(true);
})
}
const ri = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), 'i');
const newLog = log.cloneNode(false);
logItems.forEach(function(node) {
if( ! node.textContent ) { return ; }
if( ! query.length || ri.test(node.textContent) ) {
newLog.appendChild(node);
}
});
log.parentNode.replaceChild(newLog, log);
log = newLog;
log.scrollTop = log.scrollHeight;
}
})();
</script>
</div>
<!-- / Debug Log -->
<?php require __DIR__ . '/parts/admin-footer.php'; ?>
</div>
<!-- Sidebar -->
<div class="mc4wp-sidebar mc4wp-col">
<?php require __DIR__ . '/parts/admin-sidebar.php'; ?>
</div>
</div>
</div>

View File

@@ -0,0 +1,55 @@
<?php defined('ABSPATH') or exit;
/**
* @ignore
*/
function _mc4wp_admin_translation_notice()
{
// show for every language other than the default
if (stripos(get_locale(), 'en_us') === 0) {
return;
}
/* translators: %s links to the WordPress.org translation project */
echo '<p class="description">' . sprintf(wp_kses(__('Mailchimp for WordPress is in need of translations. Is the plugin not translated in your language or do you spot errors with the current translations? Helping out is easy! Please <a href="%s">help translate the plugin using your WordPress.org account</a>.', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), 'https://translate.wordpress.org/projects/wp-plugins/mailchimp-for-wp/stable/') . '</p>';
}
/**
* @ignore
*/
function _mc4wp_admin_github_notice()
{
if (strpos($_SERVER['HTTP_HOST'], 'localhost') === false && ! WP_DEBUG) {
return;
}
echo '<p class="description">Developer? Follow <a href="https://github.com/ibericode/mailchimp-for-wordpress">Mailchimp for WordPress on GitHub</a> or have a look at our repository of <a href="https://github.com/ibericode/mailchimp-for-wordpress/tree/master/sample-code-snippets">sample code snippets</a>.</p>';
}
/**
* @ignore
*/
function _mc4wp_admin_disclaimer_notice()
{
echo '<p class="description">', esc_html__('This plugin is not developed by or affiliated with Mailchimp in any way.', 'mailchimp-for-wp'), '</p>';
}
add_action('mc4wp_admin_footer', '_mc4wp_admin_translation_notice', 20);
add_action('mc4wp_admin_footer', '_mc4wp_admin_github_notice', 50);
add_action('mc4wp_admin_footer', '_mc4wp_admin_disclaimer_notice', 80);
?>
<div class="mc4wp-margin-l">
<?php
/**
* Runs while printing the footer of every Mailchimp for WordPress settings page.
*
* @since 3.0
*/
do_action('mc4wp_admin_footer');
?>
</div>

View File

@@ -0,0 +1,65 @@
<?php
defined('ABSPATH') or exit;
/**
* @ignore
*/
function _mc4wp_admin_sidebar_support_notice()
{
?>
<div class="mc4wp-box mc4wp-margin-m">
<h4 class="mc4wp-title"><?php echo esc_html__('Looking for help?', 'mailchimp-for-wp'); ?></h4>
<p><?php echo esc_html__('We have some resources available to help you in the right direction.', 'mailchimp-for-wp'); ?></p>
<ul class="ul-square">
<li><a href="https://www.mc4wp.com/kb/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=sidebar"><?php echo esc_html__('Knowledge Base', 'mailchimp-for-wp'); ?></a></li>
<li><a href="https://wordpress.org/plugins/mailchimp-for-wp/faq/"><?php echo esc_html__('Frequently Asked Questions', 'mailchimp-for-wp'); ?></a></li>
</ul>
<p><?php echo sprintf(wp_kses(__('If your answer can not be found in the resources listed above, please use the <a href="%s">support forums on WordPress.org</a>.', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), 'https://wordpress.org/support/plugin/mailchimp-for-wp'); ?></p>
<p><?php echo sprintf(wp_kses(__('Found a bug? Please <a href="%s">open an issue on GitHub</a>.', 'mailchimp-for-wp'), array( 'a' => array( 'href' => array() ) )), 'https://github.com/ibericode/mailchimp-for-wordpress/issues'); ?></p>
</div>
<?php
}
/**
* @ignore
*/
function _mc4wp_admin_sidebar_other_plugins()
{
echo '<div class="mc4wp-box mc4wp-margin-m">';
echo '<h4 class="mc4wp-title">', esc_html__('Other plugins by ibericode', 'mailchimp-for-wp'), '</h4>';
echo '<ul style="margin-bottom: 0;">';
// Koko Analytics
echo '<li style="margin: 12px 0;">';
echo sprintf('<strong><a href="%s">Koko Analytics</a></strong><br />', 'https://wordpress.org/plugins/koko-analytics/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=sidebar');
echo esc_html__('Privacy-friendly analytics plugin that does not use any external services.', 'mailchimp-for-wp');
echo '</li>';
// Boxzilla
echo '<li style="margin: 12px 0;">';
echo sprintf('<strong><a href="%s">Boxzilla Pop-ups</a></strong><br />', 'https://wordpress.org/plugins/boxzilla/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=sidebar');
echo esc_html__('Pop-ups or boxes that slide-in with a newsletter sign-up form. A sure-fire way to grow your email lists.', 'mailchimp-for-wp');
echo '</li>';
// HTML Forms
echo '<li style="margin: 12px 0;">';
echo sprintf('<strong><a href="%s">HTML Forms</a></strong><br />', 'https://wordpress.org/plugins/html-forms/#utm_source=wp-plugin&utm_medium=mailchimp-for-wp&utm_campaign=sidebar');
echo esc_html__('Super flexible forms using native HTML. Just like Mailchimp for WordPress forms but for other purposes, like a contact form.', 'mailchimp-for-wp');
echo '</li>';
echo '</ul>';
echo '</div>';
}
add_action('mc4wp_admin_sidebar', '_mc4wp_admin_sidebar_other_plugins', 40);
add_action('mc4wp_admin_sidebar', '_mc4wp_admin_sidebar_support_notice', 50);
/**
* Runs when the sidebar is outputted on Mailchimp for WordPress settings pages.
*
* Please note that not all pages have a sidebar.
*
* @since 3.0
*/
do_action('mc4wp_admin_sidebar');

View File

@@ -0,0 +1,104 @@
<?php
/**
* @var object[] $merge_fields
* @var object[] $interest_categories
* @var object[] $marketing_permissions
*/
?>
<h3>Merge fields</h3>
<table class="widefat striped">
<thead>
<tr>
<th>Name</th>
<th>Tag</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<?php foreach ($merge_fields as $f) { ?>
<tr>
<td><?php echo $f->name; ?> <?php
if ($f->required) {
?>
<span class="mc4wp-red">*</span>
<?php } ?></td>
<td><code><?php echo $f->tag; ?></code></td>
<td>
<?php echo $f->type; ?>
<?php
if ($f->options && $f->options->date_format) {
echo '(' . $f->options->date_format . ')';
}
?>
<?php
if ($f->options && $f->options->choices) {
echo '(' . join(', ', $f->options->choices) . ')';
}
?>
</td>
</tr>
<?php } ?>
</tbody>
</table>
<?php if ($interest_categories) { ?>
<h3>Interest Categories</h3>
<table class="striped widefat">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Interests</th>
</tr>
</thead>
<tbody>
<?php foreach ($interest_categories as $f) { ?>
<tr>
<td>
<strong><?php echo $f->title; ?></strong>
<br />
<br />
ID: <code><?php echo $f->id; ?></code>
</td>
<td><?php echo $f->type; ?></td>
<td>
<table>
<thead>
<tr><th>Name</th><th>ID</th></tr>
</thead>
<tbody>
<?php foreach ($f->interests as $id => $name) { ?>
<tr>
<td><?php echo $name; ?></td>
<td><code><?php echo $id; ?></code></td>
</tr>
<?php } ?>
</tbody>
</table>
</td>
</tr>
<?php } ?>
</tbody>
</table>
<?php } // end if interest categories ?>
<?php if ($marketing_permissions) { ?>
<h3>Marketing Permissions</h3>
<table class="striped widefat">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<?php foreach ($marketing_permissions as $mp) { ?>
<tr>
<td><code><?php echo $mp->marketing_permission_id; ?></code></td>
<td><?php echo $mp->text; ?></td>
</tr>
<?php } ?>
</tbody>
</table>
<?php } // end if marketing permissions ?>

View File

@@ -0,0 +1,59 @@
<h3><?php echo esc_html__('Your Mailchimp Account', 'mailchimp-for-wp'); ?></h3>
<p><?php echo esc_html__('The table below shows your Mailchimp lists and their details. If you just applied changes to your Mailchimp lists, please use the following button to renew the cached lists configuration.', 'mailchimp-for-wp'); ?></p>
<div id="mc4wp-list-fetcher">
<form method="post" action="">
<input type="hidden" name="_mc4wp_action" value="empty_lists_cache" />
<p>
<input type="submit" value="<?php echo esc_html__('Renew Mailchimp lists', 'mailchimp-for-wp'); ?>" class="button" />
</p>
</form>
</div>
<div class="mc4wp-lists-overview">
<?php
if (empty($lists)) {
?>
<p><?php echo esc_html__('No lists were found in your Mailchimp account', 'mailchimp-for-wp'); ?>.</p>
<?php
} else {
echo sprintf('<p>' . esc_html__('A total of %d lists were found in your Mailchimp account.', 'mailchimp-for-wp') . '</p>', count($lists));
echo '<table class="widefat striped" id="mc4wp-mailchimp-lists-overview">';
$headings = array(
esc_html__('List Name', 'mailchimp-for-wp'),
esc_html__('ID', 'mailchimp-for-wp'),
esc_html__('Subscribers', 'mailchimp-for-wp'),
);
echo '<thead>';
echo '<tr>';
foreach ($headings as $heading) {
echo sprintf('<th>%s</th>', $heading);
}
echo '</tr>';
echo '</thead>';
foreach ($lists as $list) {
echo '<tr>';
echo sprintf('<td><a href="#" class="mc4wp-mailchimp-list" data-list-id="%s">%s</a><span class="row-actions alignright"></span></td>', esc_attr($list->id), esc_html($list->name));
echo sprintf('<td><code>%s</code></td>', esc_html($list->id));
echo sprintf('<td>%s</td>', esc_html($list->stats->member_count));
echo '</tr>';
echo sprintf('<tr class="list-details list-%s-details" style="display: none;">', $list->id);
echo '<td colspan="3" style="padding: 0 20px 40px;">';
echo sprintf('<p class="alignright" style="margin: 20px 0;"><a href="https://admin.mailchimp.com/lists/members/?id=%s" target="_blank"><span class="dashicons dashicons-edit"></span> ' . esc_html__('Edit this list in Mailchimp', 'mailchimp-for-wp') . '</a></p>', $list->web_id);
echo '<div><div>', esc_html__('Loading... Please wait.', 'mailchimp-for-wp'), '</div></div>';
echo '</td>';
echo '</tr>';
?>
<?php
} // end foreach $lists
echo '</table>';
} // end if empty
?>
</div>