box.
18 | */
19 | $css_options = apply_filters('mc4wp_admin_form_css_options', $css_options);
20 |
21 | ?>
22 |
23 |
24 |
25 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/integrations/wp-registration-form/class-registration-form.php:
--------------------------------------------------------------------------------
1 | options['implicit']) {
33 | add_action('login_head', [ $this, 'print_css_reset' ]);
34 | add_action('um_after_register_fields', [ $this, 'maybe_output_checkbox' ], 20);
35 | add_action('register_form', [ $this, 'maybe_output_checkbox' ], 20);
36 | add_action('woocommerce_register_form', [ $this, 'maybe_output_checkbox' ], 20);
37 | }
38 |
39 | add_action('um_user_register', [ $this, 'subscribe_from_registration' ], 90, 1);
40 | add_action('user_register', [ $this, 'subscribe_from_registration' ], 90, 1);
41 |
42 | if (defined('um_plugin') && class_exists('UM')) {
43 | $this->name = 'UltimateMember';
44 | $this->description = 'Subscribes people from your UltimateMember registration form.';
45 | }
46 | }
47 |
48 | /**
49 | * Output checkbox, once.
50 | */
51 | public function maybe_output_checkbox()
52 | {
53 | if (! $this->shown) {
54 | $this->output_checkbox();
55 | $this->shown = true;
56 | }
57 | }
58 |
59 | /**
60 | * Subscribes from WP Registration Form
61 | *
62 | * @param int $user_id
63 | *
64 | * @return bool|string
65 | */
66 | public function subscribe_from_registration($user_id)
67 | {
68 |
69 | // was sign-up checkbox checked?
70 | if (! $this->triggered()) {
71 | return false;
72 | }
73 |
74 | // gather emailadress from user who WordPress registered
75 | $user = get_userdata($user_id);
76 |
77 | // was a user found with the given ID?
78 | if (! $user instanceof WP_User) {
79 | return false;
80 | }
81 |
82 | $data = $this->user_merge_vars($user);
83 |
84 | return $this->subscribe($data, $user_id);
85 | }
86 | /* End registration form functions */
87 |
88 |
89 | /**
90 | * @return bool
91 | */
92 | public function is_installed()
93 | {
94 | return true;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/assets/src/js/admin/notices.js:
--------------------------------------------------------------------------------
1 | const editor = require('./form-editor/form-editor.js')
2 | const fields = require('./form-editor/fields.js')
3 | const settings = require('./settings')
4 | const notices = {}
5 |
6 | function show (id, text) {
7 | notices[id] = text
8 | render()
9 | }
10 |
11 | function hide (id) {
12 | delete notices[id]
13 | render()
14 | }
15 |
16 | function render () {
17 | const html = Object.values(notices).map(text => '').join()
18 | let container = document.querySelector('.mc4wp-notices')
19 | if (!container) {
20 | container = document.createElement('div')
21 | container.className = 'mc4wp-notices'
22 | const heading = document.querySelector('h1, h2')
23 | heading.parentNode.insertBefore(container, heading.nextSibling)
24 | }
25 |
26 | container.innerHTML = html
27 | }
28 |
29 | const groupingsNotice = function () {
30 | const text = 'Your form contains deprecated GROUPINGS fields. Please remove these fields from your form and then re-add them through the available field buttons to make sure your data is getting through to Mailchimp correctly.'
31 | const hasGroupingsField = editor.getValue().toLowerCase().indexOf('name="groupings') > -1
32 | hasGroupingsField ? show('deprecated_groupings', text) : hide('deprecated_groupings')
33 | }
34 |
35 | const requiredFieldsNotice = function () {
36 | const missingFields = fields.getAll().filter(f => f.forceRequired === true && !editor.containsField(f.name.toUpperCase()))
37 |
38 | let text = 'Heads up! Your form is missing fields that are required in Mailchimp. Either add these fields to your form or mark them as optional in Mailchimp.'
39 | text += '' + missingFields.map(function (f) { return f.title }).join(' ') + ' ';
40 |
41 | (missingFields.length > 0) ? show('required_fields_missing', text) : hide('required_fields_missing')
42 | }
43 |
44 | const mailchimpListsNotice = function () {
45 | const text = 'Heads up! You have not yet selected a Mailchimp audience to subscribe people to. Please select at least one audience from the settings tab .'
46 |
47 | if (settings.getSelectedLists().length > 0) {
48 | hide('no_lists_selected')
49 | } else {
50 | show('no_lists_selected', text)
51 | }
52 | }
53 |
54 | // old groupings
55 | groupingsNotice()
56 | editor.on('focus', groupingsNotice)
57 | editor.on('blur', groupingsNotice)
58 |
59 | // missing required fields
60 | requiredFieldsNotice()
61 | editor.on('blur', requiredFieldsNotice)
62 | editor.on('focus', requiredFieldsNotice)
63 |
64 | document.body.addEventListener('change', mailchimpListsNotice)
65 |
--------------------------------------------------------------------------------
/includes/forms/class-form-amp.php:
--------------------------------------------------------------------------------
1 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | get_message('subscribed'),
43 | [
44 | 'a' => [],
45 | 'strong' => [],
46 | 'em' => [],
47 | ]
48 | );
49 | ?>
50 |
51 |
52 |
53 |
54 | {{message}}
55 |
56 |
57 | element
46 | function createFromElement (formElement, id) {
47 | id = id || parseInt(formElement.getAttribute('data-id')) || 0
48 | const form = new Form(id, formElement)
49 | forms.push(form)
50 | return form
51 | }
52 |
53 | /**
54 | * Triggers two events. One namespaced to the specific form ID and one global (ie fires for all forms)
55 | *
56 | * @param {string} eventName The name of the event to trigger
57 | * @param {array} eventArgs Arguments to pass to registered event listeners. The first argument should be a Form object.
58 | * @public
59 | */
60 | function trigger (eventName, eventArgs) {
61 | if (eventName === 'submit' || eventName.indexOf('.submit') > 0) {
62 | // don't spin up new thread for submit event as we want to preventDefault()...
63 | events.emit(eventArgs[0].id + '.' + eventName, eventArgs)
64 | events.emit(eventName, eventArgs)
65 | } else {
66 | // process in separate thread to prevent errors from breaking core functionality
67 | window.setTimeout(function () {
68 | events.emit(eventArgs[0].id + '.' + eventName, eventArgs)
69 | events.emit(eventName, eventArgs)
70 | }, 10)
71 | }
72 | }
73 |
74 | /**
75 | * Add event listener.
76 | * For a list of valid event names, please see https://www.mc4wp.com/kb/javascript-form-events/.
77 | * @param {string} eventName
78 | * @param {function} callback
79 | */
80 | function on (eventName, callback) {
81 | events.on(eventName, callback)
82 | }
83 |
84 | module.exports = { get, getByElement, on, trigger }
85 |
--------------------------------------------------------------------------------
/includes/views/parts/admin-sidebar.php:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
18 |
support forums on WordPress.org.', 'mailchimp-for-wp'), [ 'a' => [ 'href' => [] ] ]), 'https://wordpress.org/support/plugin/mailchimp-for-wp'); ?>
19 |
open an issue on GitHub.', 'mailchimp-for-wp'), [ 'a' => [ 'href' => [] ] ]), 'https://github.com/ibericode/mailchimp-for-wordpress/issues'); ?>
20 |
21 | ';
30 | echo '', esc_html__('Other plugins by ibericode', 'mailchimp-for-wp'), ' ';
31 | echo '';
32 |
33 | // Koko Analytics
34 | echo '';
35 | echo 'Koko Analytics ';
36 | echo esc_html__('Plug and play, privacy-friendly and GDPR/CCPA compliant statistics for WordPress.', 'mailchimp-for-wp');
37 | echo ' ';
38 |
39 | // Boxzilla
40 | echo '';
41 | echo 'Boxzilla Pop-ups ';
42 | 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');
43 | echo ' ';
44 |
45 | echo ' ';
46 | echo '';
47 | }
48 |
49 | add_action('mc4wp_admin_sidebar', '_mc4wp_admin_sidebar_other_plugins', 40);
50 | add_action('mc4wp_admin_sidebar', '_mc4wp_admin_sidebar_support_notice', 50);
51 |
52 | /**
53 | * Runs when the sidebar is outputted on Mailchimp for WordPress settings pages.
54 | *
55 | * Please note that not all pages have a sidebar.
56 | *
57 | * @since 3.0
58 | */
59 | do_action('mc4wp_admin_sidebar');
60 |
--------------------------------------------------------------------------------
/includes/forms/views/parts/add-fields-help.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/includes/forms/views/tabs/form-fields.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 |
17 |
18 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ID)) . '" size="' . ( strlen($form->ID) + 15 ) . '">'); ?>
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/includes/views/parts/lists-overview.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
15 |
18 |
.
19 | ', sprintf(esc_html__('A total of %d audiences were found in your Mailchimp account.', 'mailchimp-for-wp'), count($lists)), '';
22 | echo '
';
23 |
24 | $headings = [
25 | esc_html__('Audience name', 'mailchimp-for-wp'),
26 | esc_html__('Audience ID', 'mailchimp-for-wp'),
27 | esc_html__('# of contacts', 'mailchimp-for-wp'),
28 | ];
29 |
30 | echo '';
31 | echo '';
32 | foreach ($headings as $heading) {
33 | echo '', $heading, ' ';
34 | }
35 | echo ' ';
36 | echo ' ';
37 | echo '';
38 |
39 | foreach ($lists as $list) {
40 | $attr_data_list_id = esc_attr($list->id);
41 | $list_name = esc_html($list->name);
42 | echo '';
43 | echo '', $list_name, ' ';
44 | echo '', esc_html($list->id), ' ';
45 | echo '', esc_html($list->stats->member_count), ' ';
46 | echo ' ';
47 |
48 | echo '';
49 | echo '';
50 | echo ' ', esc_html__('Edit this audience in Mailchimp', 'mailchimp-for-wp'), '
';
51 | echo '', esc_html__('Loading... Please wait.', 'mailchimp-for-wp'), '
';
52 | echo ' ';
53 | echo ' ';
54 | ?>
55 | ';
58 | echo '
';
59 | } // end if empty
60 | ?>
61 |
62 |
--------------------------------------------------------------------------------
/assets/src/js/admin/form-editor/form-watcher.js:
--------------------------------------------------------------------------------
1 | const m = require('mithril')
2 | const editor = require('./form-editor.js')
3 | const fields = require('./fields.js')
4 |
5 | const REGEX_ARRAY_BRACKETS_WITH_KEY = /\[(\w+)\]/g
6 | const REGEX_ARRAY_BRACKETS_EMPTY = /\[\]$/
7 | const requiredFieldsInput = document.getElementById('required-fields')
8 |
9 | function updateFields () {
10 | fields.getAll().forEach(function (field) {
11 | // don't run for empty field names
12 | if (field.name.length <= 0) return
13 |
14 | let fieldName = field.name
15 | if (field.type === 'checkbox') {
16 | fieldName += '[]'
17 | }
18 |
19 | field.inFormContent = editor.containsField(fieldName)
20 |
21 | // if form contains 1 address field of group, mark all fields in this group as "required"
22 | if (field.mailchimpType === 'address') {
23 | if (field.originalRequiredValue === undefined) {
24 | field.originalRequiredValue = field.forceRequired
25 | }
26 |
27 | // query other fields for this address group
28 | const nameGroup = field.name.replace(REGEX_ARRAY_BRACKETS_WITH_KEY, '')
29 | if (editor.query('[name^="' + nameGroup + '"]').length > 0) {
30 | field.forceRequired = true
31 | } else {
32 | field.forceRequired = field.originalRequiredValue
33 | }
34 | }
35 | })
36 |
37 | findRequiredFields()
38 | m.redraw()
39 | }
40 |
41 | function findRequiredFields () {
42 | // query fields required by Mailchimp
43 | const requiredFields = fields.getAll().filter(f => f.forceRequired === true)
44 | .map(f => f.name.toUpperCase().replace(REGEX_ARRAY_BRACKETS_WITH_KEY, '.$1'))
45 |
46 | // query fields in form with [required] attribute
47 | const requiredFieldElements = editor.query('[required]');
48 | [].forEach.call(requiredFieldElements, function (el) {
49 | let name = el.name
50 |
51 | // bail if name attr empty or starts with underscore
52 | if (!name || name.length < 0 || name[0] === '_') {
53 | return
54 | }
55 |
56 | // replace array brackets with dot style notation
57 | name = name.replace(REGEX_ARRAY_BRACKETS_WITH_KEY, '.$1')
58 |
59 | // replace array-style fields
60 | name = name.replace(REGEX_ARRAY_BRACKETS_EMPTY, '')
61 |
62 | // uppercase everything before the .
63 | let pos = name.indexOf('.')
64 | pos = pos > 0 ? pos : name.length
65 | name = name.substr(0, pos).toUpperCase() + name.substr(pos)
66 |
67 | // only add field if it's not already in it
68 | if (requiredFields.indexOf(name) === -1) {
69 | requiredFields.push(name)
70 | }
71 | })
72 |
73 | // update meta
74 | requiredFieldsInput.value = requiredFields.join(',')
75 | }
76 | /**
77 | * @param {function} callback
78 | * @param {int} delay in ms
79 | */
80 | function debounce (callback, delay) {
81 | let timeout
82 | return () => {
83 | if (timeout) clearTimeout(timeout)
84 | timeout = window.setTimeout(callback, delay)
85 | }
86 | }
87 |
88 | // events
89 | editor.on('change', debounce(updateFields, 500))
90 | fields.on('change', debounce(updateFields, 100))
91 |
--------------------------------------------------------------------------------
/assets/src/js/admin/form-editor/fields.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('../../events.js')
2 | const events = new EventEmitter()
3 | const fields = {}
4 |
5 | function Field (data) {
6 | return {
7 | name: data.name,
8 | title: data.title || data.name,
9 | type: data.type,
10 | mailchimpType: data.mailchimpType || null,
11 | label: data.label || data.title || '',
12 | showLabel: typeof (data.showLabel) === 'boolean' ? data.showLabel : true,
13 | value: data.value || '',
14 | placeholder: data.placeholder || '',
15 | required: typeof (data.required) === 'boolean' ? data.required : false,
16 | forceRequired: typeof (data.forceRequired) === 'boolean' ? data.forceRequired : false,
17 | wrap: typeof (data.wrap) === 'boolean' ? data.wrap : true,
18 | min: data.min,
19 | max: data.max,
20 | help: data.help || '',
21 | choices: data.choices || [],
22 | inFormContent: null,
23 | acceptsMultipleValues: data.acceptsMultipleValues,
24 | link: data.link || '',
25 | description: data.description || ''
26 | }
27 | }
28 |
29 | function FieldChoice (data) {
30 | return {
31 | title: data.title || data.label,
32 | selected: data.selected || false,
33 | value: data.value || data.label,
34 | label: data.label
35 | }
36 | }
37 |
38 | function createChoices (data) {
39 | // Here we create FieldChoice object for each choice item
40 | // If we got an associate array / dictionary / object then we use keys as values
41 | // Otherwise, we leave it to the rendering phase to set the value attribute
42 | return Object.keys(data).map((key) => new FieldChoice({ label: data[key], value: Array.isArray(data) ? null : key }))
43 | }
44 |
45 | function register (category, data) {
46 | // if a field with the exact same name already exists,
47 | // update its forceRequired property
48 | const existingField = fields[data.name]
49 | if (existingField) {
50 | if (!existingField.forceRequired && data.forceRequired) {
51 | existingField.forceRequired = true
52 | }
53 |
54 | return existingField
55 | }
56 |
57 | // array of choices given? convert to FieldChoice objects
58 | if (data.choices) {
59 | data.choices = createChoices(data.choices)
60 |
61 | if (data.value) {
62 | data.choices = data.choices.map(function (choice) {
63 | if (choice.value === data.value) {
64 | choice.selected = true
65 | }
66 | return choice
67 | })
68 | }
69 | }
70 |
71 | // create Field object
72 | const field = new Field(data)
73 | field.category = category
74 |
75 | // add to array
76 | fields[data.name] = field
77 |
78 | // trigger event
79 | events.emit('change', [])
80 | return field
81 | }
82 |
83 | function deregister (field) {
84 | delete fields[field.name]
85 | }
86 |
87 | function get (name) {
88 | return fields[name]
89 | }
90 |
91 | function getAll () {
92 | return Object.values(fields)
93 | }
94 |
95 | module.exports = {
96 | get,
97 | getAll,
98 | deregister,
99 | register,
100 | on: events.on.bind(events)
101 | }
102 |
--------------------------------------------------------------------------------
/includes/admin/migrations/4.0.0-groupings-to-interests.php:
--------------------------------------------------------------------------------
1 | name === $interest_category->title) {
16 | return $grouping;
17 | }
18 | }
19 |
20 | return null;
21 | }
22 |
23 | /**
24 | * @ignore
25 | * @return object
26 | */
27 | function _mc4wp_400_find_group_for_interest($groups, $interest)
28 | {
29 | foreach ($groups as $group_id => $group_name) {
30 | if ($group_name === $interest->name) {
31 | return (object) [
32 | 'name' => $group_name,
33 | 'id' => $group_id,
34 | ];
35 | }
36 | }
37 |
38 | return null;
39 | }
40 |
41 | // in case the migration is _very_ late to the party
42 | if (! class_exists('MC4WP_API_V3')) {
43 | return;
44 | }
45 |
46 | $options = get_option('mc4wp', []);
47 | if (empty($options['api_key'])) {
48 | return;
49 | }
50 |
51 | // get current state from transient
52 | $lists = get_transient('mc4wp_mailchimp_lists_fallback');
53 | if (empty($lists)) {
54 | return;
55 | }
56 |
57 | @set_time_limit(600);
58 | $api_v3 = new MC4WP_API_V3($options['api_key']);
59 | $map = [];
60 |
61 | foreach ($lists as $list) {
62 | // cast to stdClass because of missing classes
63 | $list = (object) (array) $list;
64 |
65 | // no groupings? easy!
66 | if (empty($list->groupings)) {
67 | continue;
68 | }
69 |
70 | // fetch (new) interest categories for this list
71 | try {
72 | $interest_categories = $api_v3->get_list_interest_categories($list->id);
73 | } catch (MC4WP_API_Exception $e) {
74 | continue;
75 | }
76 |
77 |
78 | foreach ($interest_categories as $interest_category) {
79 | // compare interest title with grouping name, if it matches, get new id.
80 | $grouping = _mc4wp_400_find_grouping_for_interest_category($list->groupings, $interest_category);
81 | if (! $grouping) {
82 | continue;
83 | }
84 |
85 | $groups = [];
86 |
87 | try {
88 | $interests = $api_v3->get_list_interest_category_interests($list->id, $interest_category->id);
89 | } catch (MC4WP_API_Exception $e) {
90 | continue;
91 | }
92 |
93 | foreach ($interests as $interest) {
94 | $group = _mc4wp_400_find_group_for_interest($grouping->groups, $interest);
95 |
96 | if ($group) {
97 | $groups[ $group->id ] = $interest->id;
98 | $groups[ $group->name ] = $interest->id;
99 | }
100 | }
101 |
102 | $map[ (string) $grouping->id ] = [
103 | 'id' => $interest_category->id,
104 | 'groups' => $groups,
105 | ];
106 | }
107 | }
108 |
109 |
110 | if (! empty($map)) {
111 | update_option('mc4wp_groupings_map', $map);
112 | }
113 |
--------------------------------------------------------------------------------
/includes/admin/class-admin-texts.php:
--------------------------------------------------------------------------------
1 | plugin_file = $plugin_file;
22 | }
23 |
24 | /**
25 | * Add hooks
26 | */
27 | public function add_hooks()
28 | {
29 | global $pagenow;
30 |
31 | add_filter('admin_footer_text', [ $this, 'footer_text' ]);
32 |
33 | // Hooks for Plugins overview page
34 | if ($pagenow === 'plugins.php') {
35 | add_filter('plugin_action_links_' . $this->plugin_file, [ $this, 'add_plugin_settings_link' ], 10, 2);
36 | add_filter('plugin_row_meta', [ $this, 'add_plugin_meta_links' ], 10, 2);
37 | }
38 | }
39 |
40 | /**
41 | * Ask for a plugin review in the WP Admin footer, if this is one of the plugin pages.
42 | *
43 | * @param string $text
44 | *
45 | * @return string
46 | */
47 | public function footer_text($text)
48 | {
49 | if (! empty($_GET['page']) && strpos($_GET['page'], 'mailchimp-for-wp') === 0) {
50 | $text = sprintf('If you enjoy using Mailchimp for WordPress , please leave us a ★★★★★ plugin review on WordPress.org .', 'https://wordpress.org/support/plugin/mailchimp-for-wp/reviews/#new-post');
51 | }
52 |
53 | return $text;
54 | }
55 |
56 | /**
57 | * Add the settings link to the Plugins overview
58 | *
59 | * @param array $links
60 | * @param $file
61 | *
62 | * @return array
63 | */
64 | public function add_plugin_settings_link($links, $file)
65 | {
66 | if ($file !== $this->plugin_file) {
67 | return $links;
68 | }
69 |
70 | $settings_link = sprintf('%s ', admin_url('admin.php?page=mailchimp-for-wp'), esc_html__('Settings', 'mailchimp-for-wp'));
71 | array_unshift($links, $settings_link);
72 | return $links;
73 | }
74 |
75 | /**
76 | * Adds meta links to the plugin in the WP Admin > Plugins screen
77 | *
78 | * @param array $links
79 | * @param string $file
80 | *
81 | * @return array
82 | */
83 | public function add_plugin_meta_links($links, $file)
84 | {
85 | if ($file !== $this->plugin_file) {
86 | return $links;
87 | }
88 |
89 | $links[] = '' . esc_html__('Documentation', 'mailchimp-for-wp') . ' ';
90 |
91 | /**
92 | * Filters meta links shown on the Plugins overview page
93 | *
94 | * This takes an array of strings
95 | *
96 | * @since 3.0
97 | * @param array $links
98 | * @ignore
99 | */
100 | $links = apply_filters('mc4wp_admin_plugin_meta_links', $links);
101 |
102 | return $links;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/assets/src/js/forms-block.js:
--------------------------------------------------------------------------------
1 | const __ = window.wp.i18n.__
2 | const { registerBlockType } = window.wp.blocks
3 | const { SelectControl } = window.wp.components // eslint-disable-line no-unused-vars
4 | const forms = window.mc4wp_forms
5 |
6 | registerBlockType('mailchimp-for-wp/form', {
7 | apiVersion: 3,
8 | title: __('Mailchimp for WordPress Form'),
9 | description: __('Block showing a Mailchimp for WordPress sign-up form'),
10 | category: 'widgets',
11 | attributes: {
12 | id: {
13 | type: 'int'
14 | }
15 | },
16 | icon: ( ),
17 | supports: {
18 | html: false
19 | },
20 |
21 | edit: function (props) {
22 | const options = forms.map(f => {
23 | return {
24 | label: f.name,
25 | value: f.id
26 | }
27 | })
28 |
29 | if (props.attributes.id === undefined && forms.length > 0) {
30 | props.setAttributes({ id: forms[0].id })
31 | }
32 |
33 | return (
34 |
35 | {
40 | props.setAttributes({ id: value })
41 | }}
42 | />
43 |
44 | )
45 | },
46 |
47 | // Render nothing in the saved content, because we render in PHP
48 | save: function (props) {
49 | return null
50 | // return `[mc4wp_form id="${props.attributes.id}"]`;
51 | }
52 | })
53 |
--------------------------------------------------------------------------------
/assets/src/js/forms/conditional-elements.js:
--------------------------------------------------------------------------------
1 | function getFieldValues (form, fieldName) {
2 | const values = []
3 | const inputs = form.querySelectorAll('input[name="' + fieldName + '"],select[name="' + fieldName + '"],textarea[name="' + fieldName + '"]')
4 |
5 | for (let i = 0; i < inputs.length; i++) {
6 | if ((inputs[i].type === 'radio' || inputs[i].type === 'checkbox') && !inputs[i].checked) {
7 | continue
8 | }
9 |
10 | values.push(inputs[i].value)
11 | }
12 |
13 | return values
14 | }
15 |
16 | function findForm (element) {
17 | let bubbleElement = element
18 |
19 | while (bubbleElement.parentElement) {
20 | bubbleElement = bubbleElement.parentElement
21 |
22 | if (bubbleElement.tagName === 'FORM') {
23 | return bubbleElement
24 | }
25 | }
26 |
27 | return null
28 | }
29 |
30 | function toggleElement (el) {
31 | const show = !!el.getAttribute('data-show-if')
32 | const conditions = show ? el.getAttribute('data-show-if').split(':') : el.getAttribute('data-hide-if').split(':')
33 | const fieldName = conditions[0]
34 | const expectedValues = ((conditions.length > 1 ? conditions[1] : '*').split('|'))
35 | const form = findForm(el)
36 | const values = getFieldValues(form, fieldName)
37 |
38 | // determine whether condition is met
39 | let conditionMet = false
40 | for (let i = 0; i < values.length && !conditionMet; i++) {
41 | // condition is met when value is in array of expected values OR expected values contains a wildcard and value is not empty
42 | conditionMet = expectedValues.indexOf(values[i]) > -1 || (expectedValues.indexOf('*') > -1 && values[i].length > 0)
43 | }
44 |
45 | // toggle element display
46 | if (show) {
47 | el.style.display = conditionMet ? '' : 'none'
48 | } else {
49 | el.style.display = conditionMet ? 'none' : ''
50 | }
51 |
52 | // find all inputs inside this element and toggle [required] attr (to prevent HTML5 validation on hidden elements)
53 | const inputs = el.querySelectorAll('input,select,textarea')
54 | for (let i = 0; i < inputs.length; i++) {
55 | if ((conditionMet || show) && inputs[i].getAttribute('data-was-required')) {
56 | inputs[i].required = true
57 | inputs[i].removeAttribute('data-was-required')
58 | }
59 |
60 | if ((!conditionMet || !show) && inputs[i].required) {
61 | inputs[i].setAttribute('data-was-required', 'true')
62 | inputs[i].required = false
63 | }
64 | }
65 | }
66 |
67 | // evaluate conditional elements globally
68 | function evaluate () {
69 | const elements = document.querySelectorAll('.mc4wp-form [data-show-if],.mc4wp-form [data-hide-if]')
70 | for (let i = 0; i < elements.length; i++) {
71 | toggleElement(elements[i])
72 | }
73 | }
74 |
75 | // re-evaluate conditional elements for change events on forms
76 | function handleInputEvent (evt) {
77 | if (!evt.target || !evt.target.form || evt.target.form.className.indexOf('mc4wp-form') < 0) {
78 | return
79 | }
80 |
81 | const elements = evt.target.form.querySelectorAll('[data-show-if],[data-hide-if]')
82 | for (let i = 0; i < elements.length; i++) {
83 | toggleElement(elements[i])
84 | }
85 | }
86 |
87 | document.addEventListener('keyup', handleInputEvent, true)
88 | document.addEventListener('change', handleInputEvent, true)
89 | document.addEventListener('mc4wp-refresh', evaluate, true)
90 | window.addEventListener('load', evaluate)
91 | evaluate()
92 |
--------------------------------------------------------------------------------
/includes/views/parts/lists-overview-details.php:
--------------------------------------------------------------------------------
1 |
8 | Merge fields
9 |
10 |
11 |
12 | Name
13 | Tag
14 | Type
15 |
16 |
17 |
18 |
19 |
20 | name); ?> required) {
22 | ?>
23 | *
24 |
25 | tag); ?>
26 |
27 | type); ?>
28 | options->date_format)) {
30 | echo esc_html('(' . $f->options->date_format . ')');
31 | }
32 | ?>
33 | options->choices)) {
35 | echo esc_html('(' . join(', ', $f->options->choices) . ')');
36 | }
37 | ?>
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Interest Categories
46 |
47 |
48 |
49 | Name
50 | Type
51 | Interests
52 |
53 |
54 |
55 |
56 |
57 |
58 | title); ?>
59 |
60 |
61 | ID: id); ?>
62 |
63 | type); ?>
64 |
65 |
66 |
67 | Name ID
68 |
69 |
70 | interests as $id => $name) { ?>
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | Marketing Permissions
88 |
89 |
90 |
91 | ID
92 | Name
93 |
94 |
95 |
96 |
97 |
98 | marketing_permission_id); ?>
99 | text); ?>
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/integrations/easy-digital-downloads/class-easy-digital-downloads.php:
--------------------------------------------------------------------------------
1 | options['implicit']) {
28 | // TODO: Allow more positions
29 | add_action('edd_purchase_form_user_info_fields', [ $this, 'output_checkbox' ], 1);
30 | add_action('edd_payment_meta', [ $this, 'save_checkbox_value' ]);
31 | }
32 |
33 | add_action('edd_complete_purchase', [ $this, 'subscribe_from_edd' ], 50);
34 | }
35 |
36 | /**
37 | * @param array $meta
38 | *
39 | * @return array
40 | */
41 | public function save_checkbox_value($meta)
42 | {
43 |
44 | // don't save anything if the checkbox was not checked
45 | if (! $this->checkbox_was_checked()) {
46 | return $meta;
47 | }
48 |
49 | $meta['_mc4wp_optin'] = 1;
50 | return $meta;
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | *
56 | * @param $object_id
57 | *
58 | * @return bool
59 | */
60 | public function triggered($object_id = null)
61 | {
62 | if ($this->options['implicit']) {
63 | return true;
64 | }
65 |
66 | if (! $object_id) {
67 | return false;
68 | }
69 |
70 | $meta = edd_get_payment_meta($object_id);
71 | if (is_array($meta) && isset($meta['_mc4wp_optin']) && $meta['_mc4wp_optin']) {
72 | return true;
73 | }
74 |
75 | return false;
76 | }
77 |
78 | /**
79 | * @param int $payment_id The ID of the payment
80 | *
81 | * @return bool|string
82 | */
83 | public function subscribe_from_edd($payment_id)
84 | {
85 | if (! $this->triggered($payment_id)) {
86 | return false;
87 | }
88 |
89 | $email = (string) edd_get_payment_user_email($payment_id);
90 | $data = [
91 | 'EMAIL' => $email,
92 | ];
93 |
94 | // add first and last name to merge vars, if given
95 | $user_info = (array) edd_get_payment_meta_user_info($payment_id);
96 |
97 | if (! empty($user_info['first_name']) && ! empty($user_info['last_name'])) {
98 | $data['NAME'] = $user_info['first_name'] . ' ' . $user_info['last_name'];
99 | }
100 |
101 | if (! empty($user_info['first_name'])) {
102 | $data['FNAME'] = $user_info['first_name'];
103 | }
104 |
105 | if (! empty($user_info['last_name'])) {
106 | $data['LNAME'] = $user_info['last_name'];
107 | }
108 |
109 | return $this->subscribe($data, $payment_id);
110 | }
111 |
112 | /**
113 | * @return bool
114 | */
115 | public function is_installed()
116 | {
117 | return class_exists('Easy_Digital_Downloads');
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/includes/views/extensions.php:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
12 |
13 |
Mailchimp for WordPress: Add-on plugins
14 |
15 |
16 |
Mailchimp for WordPress Premium , take your email marketing to the next level!
17 |
You're currently on the free version of the MC4WP: Mailchimp for WordPress plugin.
18 |
Did you know that there is a premium version too? It comes with the following additional features:
19 |
20 | Multiple and improved forms — allowing an unlimited amount of sign-up forms that submit without requiring a full page reload.
21 | E-Commerce integration — tightly integrate your WooCommerce store with Mailchimp.
22 | User Sync — keep your WordPress user database in sync with a Mailchimp list.
23 | Logging - every form submission is stored locally, allowing charted data and exporting to CSV or JSON
24 | Form designer — make your forms look pretty without having to know or write a single line of CSS.
25 | Append form to posts — an easy setting to automatically append a form to all posts (in a certain category).
26 | Priority support — gain access to our 24/7 support team.
27 |
28 |
29 | Buy Mailchimp for WordPress Premium
30 | More information
31 |
32 |
33 |
Comes with our 30-day no questions asked money back guarantee .
34 |
35 |
36 |
37 |
38 |
39 |
The following (free) add-on plugins are available for Mailchimp for WordPress.
40 |
41 |
42 |
43 |
Adds a sign-up bar to the top or bottom of your site. A sure fire way to grow your lists.
44 |
45 |
46 |
47 |
48 |
Improved Mailchimp integration for multilingual sites using WPML.
49 |
50 |
51 |
52 |
53 |
Pop-ups for your sign-up forms.
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/includes/admin/class-review-notice.php:
--------------------------------------------------------------------------------
1 | tools = $tools;
28 | }
29 |
30 | /**
31 | * Add action & filter hooks.
32 | */
33 | public function add_hooks()
34 | {
35 | add_action('admin_notices', [ $this, 'show' ]);
36 | add_action('mc4wp_admin_dismiss_review_notice', [ $this, 'dismiss' ]);
37 | }
38 |
39 | /**
40 | * Set flag in user meta so notice won't be shown.
41 | */
42 | public function dismiss()
43 | {
44 | $user = wp_get_current_user();
45 | update_user_meta($user->ID, $this->meta_key_dismissed, 1);
46 | }
47 |
48 | /**
49 | * @return bool
50 | */
51 | public function show()
52 | {
53 | // only show on Mailchimp for WordPress' pages.
54 | if (! $this->tools->on_plugin_page()) {
55 | return false;
56 | }
57 |
58 | // only show if 2 weeks have passed since first use.
59 | $two_weeks_in_seconds = ( 60 * 60 * 24 * 14 );
60 | if ($this->time_since_first_use() <= $two_weeks_in_seconds) {
61 | return false;
62 | }
63 |
64 | // only show if user did not dismiss before
65 | $user = wp_get_current_user();
66 | if (get_user_meta($user->ID, $this->meta_key_dismissed, true)) {
67 | return false;
68 | }
69 |
70 | echo '';
71 | echo '
';
72 | echo esc_html__('You\'ve been using Mailchimp for WordPress for some time now; we hope you love it!', 'mailchimp-for-wp'), ' ';
73 | echo sprintf(wp_kses(__('If you do, please leave us a 5★ rating on WordPress.org . It would be of great help to us.', 'mailchimp-for-wp'), [ 'a' => [ 'href' => [] ] ]), 'https://wordpress.org/support/view/plugin-reviews/mailchimp-for-wp?rate=5#new-post');
74 | echo '
';
75 | echo '
';
76 | echo '
';
77 | return true;
78 | }
79 |
80 | /**
81 | * @return int
82 | */
83 | private function time_since_first_use()
84 | {
85 | $options = get_option('mc4wp', []);
86 | if (! is_array($options)) {
87 | $options = [];
88 | }
89 |
90 | // option was never added before, do it now.
91 | if (empty($options['first_activated_on'])) {
92 | $options['first_activated_on'] = time();
93 | update_option('mc4wp', $options);
94 | }
95 |
96 | return time() - $options['first_activated_on'];
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/includes/forms/views/add-form.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
58 |
59 |
60 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/integrations/ninja-forms/class-field.php:
--------------------------------------------------------------------------------
1 | _settings['label_pos']['value'] = 'right';
31 |
32 | add_filter('ninja_forms_custom_columns', [ $this, 'custom_columns' ], 10, 2);
33 | add_action('init', [$this, 'translate_nicename']);
34 | }
35 |
36 | public function translate_nicename()
37 | {
38 | $this->_nicename = __('Mailchimp opt-in', 'mailchimp-for-wp');
39 | }
40 |
41 | /**
42 | * Admin Form Element
43 | * Display the checkbox on the edit submissions area.
44 | * @since 3.0
45 | *
46 | * @param $id Field ID.
47 | * @param $value Field value.
48 | * @return string HTML used for display of checkbox.
49 | */
50 | public function admin_form_element($id, $value)
51 | {
52 | // If the checkboxes value is one...
53 | if (1 === (int) $value) {
54 | // ...this variable to checked.
55 | $checked = 'checked';
56 | } else {
57 | // ...else leave the variable empty.
58 | $checked = '';
59 | }
60 |
61 | // Return HTML to be output to the submission edit page.
62 | return " ";
63 | }
64 |
65 | /**
66 | * Custom Columns
67 | * Creates what is displayed in the columns on the submissions page.
68 | * @since 3.0
69 | *
70 | * @param string $value checkbox value
71 | * @param MC4WP_Ninja_Forms_Field $field field model.
72 | * @return $value string|void
73 | */
74 | public function custom_columns($value, $field)
75 | {
76 | // If the field type is equal to checkbox...
77 | if ('mc4wp_optin' === $field->get_setting('type')) {
78 | // Backwards compatibility check for the new checked value setting.
79 | if (null === $field->get_setting('checked_value') && 1 === (int) $value) {
80 | return __('Checked', 'ninja-forms');
81 | } elseif (null === $field->get_setting('unchecked_value') && 0 === (int) $value) {
82 | return __('Unchecked', 'ninja-forms');
83 | }
84 |
85 | // If the field value is set to 1....
86 | if (1 === (int) $value) {
87 | // Set the value to the checked value setting.
88 | $value = $field->get_setting('checked_value');
89 | } else {
90 | // Else set the value to the unchecked value setting.
91 | $value = $field->get_setting('unchecked_value');
92 | }
93 | }
94 | return $value;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/integrations/wp-comment-form/class-comment-form.php:
--------------------------------------------------------------------------------
1 | options['implicit']) {
33 | // hooks for outputting the checkbox
34 | add_filter('comment_form_submit_field', [ $this, 'add_checkbox_before_submit_button' ], 90);
35 |
36 | add_action('thesis_hook_after_comment_box', [ $this, 'maybe_output_checkbox' ], 90);
37 | add_action('comment_form', [ $this, 'maybe_output_checkbox' ], 90);
38 | }
39 |
40 | // hooks for checking if we should subscribe the commenter
41 | add_action('comment_post', [ $this, 'subscribe_from_comment' ], 40, 2);
42 | }
43 |
44 | /**
45 | * This adds the checkbox just before the submit button and sets a flag to prevent it from outputting twice
46 | *
47 | * @param $submit_button_html
48 | *
49 | * @return string
50 | */
51 | public function add_checkbox_before_submit_button($submit_button_html)
52 | {
53 | $this->added_through_filter = true;
54 | return $this->get_checkbox_html() . $submit_button_html;
55 | }
56 |
57 | /**
58 | * Output fallback
59 | * Will output the checkbox if comment_form() function does not use `comment_form_submit_field` filter yet.
60 | */
61 | public function maybe_output_checkbox()
62 | {
63 | if (! $this->added_through_filter) {
64 | $this->output_checkbox();
65 | }
66 | }
67 |
68 | /**
69 | * Grabs data from WP Comment Form
70 | *
71 | * @param int $comment_id
72 | * @param string $comment_approved
73 | *
74 | * @return bool|string
75 | */
76 | public function subscribe_from_comment($comment_id, $comment_approved = '')
77 | {
78 |
79 | // was sign-up checkbox checked?
80 | if (! $this->triggered()) {
81 | return false;
82 | }
83 |
84 | // is this a spam comment?
85 | if ($comment_approved === 'spam') {
86 | return false;
87 | }
88 |
89 | $comment = get_comment($comment_id);
90 |
91 | $data = [
92 | 'EMAIL' => $comment->comment_author_email,
93 | 'NAME' => $comment->comment_author,
94 | 'OPTIN_IP' => $comment->comment_author_IP,
95 | ];
96 |
97 | return $this->subscribe($data, $comment_id);
98 | }
99 |
100 | /**
101 | * @return bool
102 | */
103 | public function is_installed()
104 | {
105 | return true;
106 | }
107 |
108 | /**
109 | * {@inheritdoc }
110 | */
111 | public function get_object_link($object_id)
112 | {
113 | $comment = get_comment($object_id);
114 |
115 | if (! $comment) {
116 | return '';
117 | }
118 |
119 | return sprintf('Comment #%d ', get_edit_comment_link($object_id), $object_id);
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/includes/class-personal-data-exporter.php:
--------------------------------------------------------------------------------
1 | __('Mailchimp Subscriptions'),
18 | 'callback' => [self::class, 'get_mailchimp_subscription_data']
19 | ];
20 |
21 | return $exporters;
22 | }
23 |
24 | /**
25 | * Retrieves the Mailchimp subscription data for a given email address.
26 | *
27 | * This method uses the Mailchimp for WordPress (MC4WP) API to search for members based on the provided
28 | * email address and returns a list of Mailchimp lists the user is subscribed to, if any.
29 | *
30 | * @param string $email_address The email address of the user to search for.
31 | *
32 | * @return array An array containing the user's Mailchimp subscription data:
33 | * - 'data' (array): The subscription information, including:
34 | * - 'group_id' (string): The group identifier for Mailchimp.
35 | * - 'group_label' (string): The label for the group ('Mailchimp Subscriptions').
36 | * - 'item_id' (string): The item identifier ('mailchimp-subscriptions').
37 | * - 'data' (array): The subscription details, with:
38 | * - 'name' (string): The label ('Mailchimp List').
39 | * - 'value' (string): A comma-separated list of Mailchimp lists the user is subscribed to.
40 | * - 'done' (bool): Indicates the completion of the process (always true).
41 | */
42 | public static function get_mailchimp_subscription_data($email_address)
43 | {
44 | $api = mc4wp_get_api_v3();
45 | $client = $api->get_client();
46 | $data = $client->get('search-members?query=' . urlencode($email_address));
47 |
48 | // Parse the API response to get the lists the user is subscribed to.
49 | $subscribed_lists = [];
50 | $data_to_export = [];
51 |
52 | if (!empty($data->exact_matches->members)) {
53 | $lists = $api->get_lists();
54 | foreach ($data->exact_matches->members as $member) {
55 | // Fetch the user's subscribed lists.
56 | if (isset($member->list_id)) {
57 | foreach ($lists as $list) {
58 | if ($list->id == $member->list_id) {
59 | $subscribed_lists[] = $list->name;
60 | continue;
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
67 | if ($subscribed_lists) {
68 | $data_to_export[] = [
69 | 'group_id' => 'mailchimp',
70 | 'group_label' => __('Mailchimp Subscriptions', 'mailchimp-for-wp'),
71 | 'item_id' => 'mailchimp-subscriptions',
72 | 'data' => [
73 | [
74 | 'name' => __('Mailchimp Lists', 'mailchimp-for-wp'),
75 | 'value' => implode(', ', $subscribed_lists),
76 | ]
77 | ]
78 | ];
79 | }
80 |
81 | return [
82 | 'data' => $data_to_export,
83 | 'done' => true,
84 | ];
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/includes/class-container.php:
--------------------------------------------------------------------------------
1 | services[ $name ]);
28 | }
29 |
30 | /**
31 | * @param string $name
32 | *
33 | * @return mixed
34 | * @throws Exception
35 | */
36 | public function get($name)
37 | {
38 | if (! $this->has($name)) {
39 | throw new Exception(sprintf('No service named %s was registered.', $name));
40 | }
41 |
42 | $service = $this->services[ $name ];
43 |
44 | // is this a resolvable service?
45 | if (is_callable($service)) {
46 | // resolve service if it's not resolved yet
47 | if (! isset($this->resolved_services[ $name ])) {
48 | $this->resolved_services[ $name ] = call_user_func($service);
49 | }
50 |
51 | return $this->resolved_services[ $name ];
52 | }
53 |
54 | return $this->services[ $name ];
55 | }
56 |
57 | /**
58 | * (PHP 5 >= 5.0.0)
59 | * Whether a offset exists
60 | * @link http://php.net/manual/en/arrayaccess.offsetexists.php
61 | *
62 | * @param mixed $offset
63 | * An offset to check for.
64 | *
65 | *
66 | * @return boolean true on success or false on failure.
67 | *
68 | *
69 | * The return value will be casted to boolean if non-boolean was returned.
70 | */
71 | #[\ReturnTypeWillChange]
72 | public function offsetExists($offset)
73 | {
74 | return $this->has($offset);
75 | }
76 |
77 | /**
78 | * (PHP 5 >= 5.0.0)
79 | * Offset to retrieve
80 | * @link http://php.net/manual/en/arrayaccess.offsetget.php
81 | *
82 | * @param mixed $offset
83 | * The offset to retrieve.
84 | *
85 | *
86 | * @return mixed Can return all value types.
87 | */
88 | #[\ReturnTypeWillChange]
89 | public function offsetGet($offset)
90 | {
91 | return $this->get($offset);
92 | }
93 |
94 | /**
95 | * (PHP 5 >= 5.0.0)
96 | * Offset to set
97 | * @link http://php.net/manual/en/arrayaccess.offsetset.php
98 | *
99 | * @param mixed $offset
100 | * The offset to assign the value to.
101 | *
102 | * @param mixed $value
103 | * The value to set.
104 | *
105 | *
106 | * @return void
107 | */
108 | #[\ReturnTypeWillChange]
109 | public function offsetSet($offset, $value)
110 | {
111 | $this->services[ $offset ] = $value;
112 | }
113 |
114 | /**
115 | * (PHP 5 >= 5.0.0)
116 | * Offset to unset
117 | * @link http://php.net/manual/en/arrayaccess.offsetunset.php
118 | *
119 | * @param mixed $offset
120 | * The offset to unset.
121 | *
122 | *
123 | * @return void
124 | */
125 | #[\ReturnTypeWillChange]
126 | public function offsetUnset($offset)
127 | {
128 | unset($this->services[ $offset ]);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/includes/views/general-settings.php:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 | Mailchimp for WordPress
9 |
10 |
11 |
12 |
13 |
14 |
15 | Mailchimp for WordPress:
16 |
17 |
18 |
19 | messages->show();
22 | ?>
23 |
24 |
69 |
70 | ';
75 | include __DIR__ . '/parts/lists-overview.php';
76 | }
77 |
78 | require __DIR__ . '/parts/admin-footer.php';
79 |
80 | ?>
81 |
82 |
83 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/integrations/custom/class-custom.php:
--------------------------------------------------------------------------------
1 | get_data();
42 | $value = isset($data[ $this->checkbox_name ]) ? $data[ $this->checkbox_name ] : '';
43 | $truthy_values = [ 1, '1', 'yes', true, 'true', 'y' ];
44 | return in_array($value, $truthy_values, true);
45 | }
46 |
47 | /**
48 | * Maybe fire a general subscription request
49 | *
50 | * @return bool|string
51 | */
52 | public function listen()
53 | {
54 | if (! $this->checkbox_was_checked()) {
55 | return false;
56 | }
57 |
58 | // ignore requests from bots, crawlers and link previews
59 | if (empty($_SERVER['HTTP_USER_AGENT']) || preg_match('/bot|crawl|spider|seo|lighthouse|facebookexternalhit|preview/i', $_SERVER['HTTP_USER_AGENT'])) {
60 | return false;
61 | }
62 |
63 | // ignore requests without an HTTP referrer
64 | if (empty($_SERVER['HTTP_REFERER'])) {
65 | return false;
66 | }
67 |
68 | // ignore requests where HTTP Referer does not contain hostname from home_url
69 | $site_hostname = parse_url(get_home_url(), PHP_URL_HOST);
70 | if (strpos($_SERVER['HTTP_REFERER'], $site_hostname) === false) {
71 | return false;
72 | }
73 |
74 | $data = $this->get_data();
75 |
76 | // don't run for CF7 or Events Manager requests
77 | // (since they use the same "mc4wp-subscribe" trigger)
78 | $disable_triggers = [
79 | '_wpcf7' => '',
80 | 'action' => 'booking_add',
81 | ];
82 |
83 | foreach ($disable_triggers as $trigger => $trigger_value) {
84 | if (isset($data[ $trigger ])) {
85 | $value = $data[ $trigger ];
86 |
87 | // do nothing if trigger value is optional
88 | // or if trigger value matches
89 | if (empty($trigger_value) || $value === $trigger_value) {
90 | return false;
91 | }
92 | }
93 | }
94 |
95 | // run!
96 | return $this->process();
97 | }
98 |
99 | /**
100 | * Process custom form
101 | *
102 | * @return bool|string
103 | */
104 | public function process()
105 | {
106 | $parser = new MC4WP_Field_Guesser($this->get_data());
107 | $data = $parser->combine([ 'guessed', 'namespaced' ]);
108 |
109 | // do nothing if no email was found
110 | if (empty($data['EMAIL'])) {
111 | $this->get_log()->warning(sprintf('%s > Unable to find EMAIL field.', $this->name));
112 | return false;
113 | }
114 |
115 | return $this->subscribe($data);
116 | }
117 |
118 | /**
119 | * @return bool
120 | */
121 | public function is_installed()
122 | {
123 | return true;
124 | }
125 |
126 | /**
127 | * @return array
128 | */
129 | public function get_ui_elements()
130 | {
131 | return [ 'lists', 'double_optin', 'update_existing', 'replace_interests' ];
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/assets/src/js/admin/form-editor/field-helper.js:
--------------------------------------------------------------------------------
1 | const m = require('mithril')
2 | const editor = require('./form-editor.js')
3 | const fields = require('./fields.js')
4 | const i18n = window.mc4wp_forms_i18n
5 | const generate = require('./field-generator.js')
6 | const Overlay = require('../overlay.js')
7 | const forms = require('./field-forms.js')
8 | let fieldConfig
9 |
10 | editor.on('blur', () => m.redraw())
11 |
12 | /**
13 | * Choose a field to open the helper form for
14 | */
15 | function setActiveField (name) {
16 | fieldConfig = name !== null ? fields.get(name) : null
17 |
18 | // if this hidden field has choices (hidden groups), glue them together by their label.
19 | if (fieldConfig && fieldConfig.type === 'hidden' && fieldConfig.choices.length > 0) {
20 | fieldConfig.value = fieldConfig.choices.map(function (c) {
21 | return c.label
22 | }).join('|')
23 | }
24 |
25 | m.redraw()
26 | }
27 |
28 | /**
29 | * Create HTML based on current config object
30 | */
31 | function createFieldHTMLAndAddToForm () {
32 | // generate html
33 | const html = generate(fieldConfig)
34 |
35 | // add to editor
36 | editor.insert(html)
37 |
38 | // reset field form
39 | setActiveField(null)
40 | }
41 |
42 | /**
43 | * View
44 | * @returns {*}
45 | */
46 | function view () {
47 | // build DOM for fields choice
48 | const availableFields = fields.getAll()
49 |
50 | const fieldsChoice = m('div#mc4wp-available-fields.mc4wp-margin-s', [
51 | m('h4', { style: { marginTop: 0 } }, i18n.chooseField),
52 |
53 | [i18n.listFields, i18n.interestCategories, i18n.formFields].map(function (category) {
54 | const categoryFields = availableFields.filter(function (f) {
55 | return f.category === category
56 | })
57 |
58 | if (categoryFields.length === 0) {
59 | return ''
60 | }
61 |
62 | return m('div.mc4wp-margin-s', [
63 | m('h4', category),
64 |
65 | // render fields
66 | categoryFields.map(function (field) {
67 | let className = 'button'
68 | if (field.forceRequired) {
69 | className += ' is-required'
70 | }
71 |
72 | const inForm = field.inFormContent
73 | if (inForm !== null) {
74 | className += ' ' + (inForm ? 'in-form' : 'not-in-form')
75 | }
76 |
77 | return m('button', {
78 | className,
79 | type: 'button',
80 | onclick: (evt) => setActiveField(evt.target.value),
81 | value: field.name
82 | }, field.title)
83 | })
84 | ])
85 | })
86 | ])
87 |
88 | // build DOM for overlay
89 | let form = null
90 | if (fieldConfig) {
91 | form = m(Overlay, { onClose: () => setActiveField(null) }, // field wizard
92 | m('div#mc4wp-add-form-field', [
93 | // heading
94 | m('h3', [
95 | fieldConfig.title,
96 | fieldConfig.forceRequired ? m('span.mc4wp-red', '*') : '',
97 | fieldConfig.name.length ? m('code', fieldConfig.name) : ''
98 | ]),
99 |
100 | // help text
101 | (fieldConfig.help.length) ? m('p', m.trust(fieldConfig.help)) : '',
102 |
103 | // actual form
104 | forms.render(fieldConfig),
105 |
106 | // add to form button
107 | m('p', [
108 | m('button', {
109 | class: 'button-primary',
110 | type: 'button',
111 | onkeydown: function (evt) {
112 | if (evt.keyCode === 13) {
113 | createFieldHTMLAndAddToForm()
114 | }
115 | },
116 | onclick: createFieldHTMLAndAddToForm
117 | }, i18n.addToForm)
118 | ])
119 | ]))
120 | }
121 |
122 | return [
123 | fieldsChoice,
124 | form
125 | ]
126 | }
127 |
128 | const fieldHelperRootElement = document.getElementById('mc4wp-field-wizard')
129 | if (fieldHelperRootElement) {
130 | m.mount(fieldHelperRootElement, { view })
131 | }
132 |
--------------------------------------------------------------------------------
/includes/forms/class-form-tags.php:
--------------------------------------------------------------------------------
1 | tags['response'] = [
36 | 'description' => __('Replaced with the form response (error or success messages).', 'mailchimp-for-wp'),
37 | 'callback' => [ $this, 'get_form_response' ],
38 | 'raw_html' => true,
39 | ];
40 |
41 | $this->tags['data'] = [
42 | 'description' => sprintf(__('Data from the URL or a submitted form.', 'mailchimp-for-wp')),
43 | 'callback' => [ $this, 'get_data' ],
44 | 'example' => "data key='UTM_SOURCE' default='Default Source'",
45 | ];
46 |
47 | $this->tags['subscriber_count'] = [
48 | 'description' => __('Replaced with the number of subscribers on the selected list(s)', 'mailchimp-for-wp'),
49 | 'callback' => [ $this, 'get_subscriber_count' ],
50 | ];
51 | }
52 |
53 |
54 | public function replace_in_form_content($string, MC4WP_Form $form, MC4WP_Form_Element $element)
55 | {
56 | $this->form = $form;
57 | $this->form_element = $element;
58 |
59 | $string = $this->replace_in_html($string);
60 | return $string;
61 | }
62 |
63 | public function replace_in_form_response($string, MC4WP_Form $form)
64 | {
65 | $this->form = $form;
66 |
67 | $string = $this->replace_in_html($string);
68 | return $string;
69 | }
70 |
71 | public function replace_in_form_redirect_url($string, MC4WP_Form $form)
72 | {
73 | $this->form = $form;
74 | $string = $this->replace_in_url($string);
75 | return $string;
76 | }
77 |
78 | /**
79 | * Returns the number of subscribers on the selected lists (for the form context)
80 | *
81 | * @return int
82 | */
83 | public function get_subscriber_count()
84 | {
85 | $mailchimp = new MC4WP_MailChimp();
86 | $count = $mailchimp->get_subscriber_count($this->form->get_lists());
87 | return number_format($count);
88 | }
89 |
90 | /**
91 | * Returns the form response
92 | *
93 | * @return string
94 | */
95 | public function get_form_response()
96 | {
97 | if ($this->form_element instanceof MC4WP_Form_Element) {
98 | return $this->form_element->get_response_html();
99 | }
100 |
101 | return '';
102 | }
103 |
104 | /**
105 | * Gets data value from GET or POST variables.
106 | *
107 | * @param array $args
108 | * @return string
109 | */
110 | public function get_data(array $args = [])
111 | {
112 | if (empty($args['key'])) {
113 | return '';
114 | }
115 |
116 | $default = isset($args['default']) ? $args['default'] : '';
117 | $key = $args['key'];
118 |
119 | $data = array_merge($_GET, $_POST);
120 | $value = isset($data[ $key ]) ? $data[ $key ] : $default;
121 |
122 | // turn array into readable value
123 | if (is_array($value)) {
124 | $value = array_filter($value);
125 | $value = join(', ', $value);
126 | }
127 |
128 | return $value;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/assets/src/js/admin/form-editor/form-editor.js:
--------------------------------------------------------------------------------
1 | // load CodeMirror & plugins
2 | /**
3 | * @type {CodeMirror}
4 | */
5 | const CodeMirror = require('codemirror')
6 | require('codemirror/mode/htmlmixed/htmlmixed')
7 | require('codemirror/addon/edit/matchtags.js')
8 |
9 | /* variables */
10 | const FormEditor = {}
11 | const _dom = document.createElement('form')
12 | let domDirty = false
13 | let editor
14 | const element = document.getElementById('mc4wp-form-content')
15 | const previewFrame = document.getElementById('mc4wp-form-preview')
16 | let previewDom
17 | const templateRegex = /\{[^{}]+\}/g
18 |
19 | /* functions */
20 | function setPreviewDom () {
21 | const frameContent = previewFrame.contentDocument || previewFrame.contentWindow.document
22 | previewDom = frameContent.querySelector('.mc4wp-form-fields')
23 |
24 | if (previewDom) {
25 | updatePreview()
26 | }
27 | }
28 |
29 | function updatePreview () {
30 | if (!previewDom) {
31 | return setPreviewDom()
32 | }
33 |
34 | let markup = FormEditor.getValue()
35 |
36 | // replace template tags (twice, to allow for nested tags)
37 | markup = markup.replace(templateRegex, '').replace(templateRegex, '')
38 |
39 | // update dom
40 | previewDom.innerHTML = markup
41 | previewDom.dispatchEvent(new Event('mc4wp-refresh'))
42 | }
43 |
44 | /**
45 | * @returns {HTMLFormElement}
46 | */
47 | function dom () {
48 | if (domDirty) {
49 | _dom.innerHTML = FormEditor.getValue().toLowerCase()
50 | domDirty = false
51 | }
52 |
53 | return _dom
54 | }
55 |
56 | /**
57 | * @returns {string}
58 | */
59 | FormEditor.getValue = function () {
60 | return editor ? editor.getValue() : element.value
61 | }
62 |
63 | /**
64 | * @param {string} query
65 | * @returns {NodeListOf}
66 | */
67 | FormEditor.query = function (query) {
68 | return dom().querySelectorAll(query.toLowerCase())
69 | }
70 |
71 | /**
72 | * @param {string} fieldName
73 | * @returns {boolean}
74 | */
75 | FormEditor.containsField = function (fieldName) {
76 | return dom().elements.namedItem(fieldName.toLowerCase()) !== null
77 | }
78 |
79 | /**
80 | * @param {string} html
81 | */
82 | FormEditor.insert = function (html) {
83 | if (editor) {
84 | editor.replaceSelection(html)
85 | editor.focus()
86 | } else {
87 | element.value += html
88 | }
89 | }
90 |
91 | /**
92 | *
93 | * @param {string} event
94 | * @param {function} callback
95 | * @returns {*}
96 | */
97 | FormEditor.on = function (event, callback) {
98 | if (editor) {
99 | // translate "input" event for CodeMirror
100 | event = (event === 'input') ? 'changes' : event
101 | return editor.on(event, callback)
102 | }
103 |
104 | return element.addEventListener(event, callback)
105 | }
106 |
107 | FormEditor.refresh = function () {
108 | if (editor) editor.refresh()
109 | }
110 |
111 | /* bootstrap */
112 | if (element) {
113 | _dom.innerHTML = element.value.toLowerCase()
114 |
115 | // turn