<?php

	/**
	 * @link              https://wsform.com/knowledgebase/constant-contact/
	 * @since             2.0.0
	 * @package           WS_Form_Constant_Contact
	 *
	 * @wordpress-plugin
	 * Plugin Name:       WS Form PRO - Constant Contact
	 * Plugin URI:        https://wsform.com/knowledgebase/constant-contact/
	 * Description:       Constant Contact add-on for WS Form PRO
	 * Version:           2.0.5
	 * Requires at least: 5.4
	 * Requires PHP:      5.6
	 * License:           GPLv3 or later
	 * License URI:       https://www.gnu.org/licenses/gpl-3.0.html
	 * Author:            WS Form
	 * Author URI:        https://wsform.com/
	 * Text Domain:       ws-form-constant-contact-v3
 */

	Class WS_Form_Add_On_Constant_Contact_V3 {

		const WS_FORM_PRO_ID 			= 'ws-form-pro/ws-form.php';
		const WS_FORM_PRO_VERSION_MIN 	= '1.9.140';

		function __construct() {

			// Load plugin.php
			if(!function_exists('is_plugin_active')) {

				include_once(ABSPATH . 'wp-admin/includes/plugin.php');
			}

			// Admin init
			add_action('plugins_loaded', array($this, 'plugins_loaded'), 20);
		}

		function plugins_loaded() {

			if(self::is_dependency_ok()) {

				new WS_Form_Action_Constant_Contact_V3();

			} else {

				self::dependency_error();

				if(isset($_GET['activate'])) { unset($_GET['activate']); }
			}
		}

		function activate() {

			if (!self::is_dependency_ok()) {

				self::dependency_error();
			}
		}

		// Check dependencies
		function is_dependency_ok() {

			if(!defined('WS_FORM_VERSION')) { return false; }

			return(

				is_plugin_active(self::WS_FORM_PRO_ID) &&
				(version_compare(WS_FORM_VERSION, self::WS_FORM_PRO_VERSION_MIN) >= 0)
			);
		}

		// Add error notice action - Pro
		function dependency_error() {

			// Show error notification
			add_action('after_plugin_row_' . plugin_basename(__FILE__), array($this, 'dependency_error_notification'), 10, 2);
		}

		// Dependency error - Notification
		function dependency_error_notification($file, $plugin) {

			// Checks
			if(!current_user_can('update_plugins')) { return; }
			if($file != plugin_basename(__FILE__)) { return; }

			// Build notice
			$dependency_notice = sprintf('<tr class="plugin-update-tr"><td colspan="3" class="plugin-update colspanchange"><div class="update-message notice inline notice-error notice-alt"><p>%s</p></div></td></tr>', sprintf(__('This add-on requires %s (version %s or later) to be installed and activated.', 'ws-form-constant-contact-v3'), '<a href="https://wsform.com?utm_source=ws_form_pro&utm_medium=plugins" target="_blank">WS Form PRO</a>', self::WS_FORM_PRO_VERSION_MIN));

			// Show notice
			echo $dependency_notice;
		}
	}

	$wsf_add_on_constant_contact = new WS_Form_Add_On_Constant_Contact_V3();	

	register_activation_hook(__FILE__, array($wsf_add_on_constant_contact, 'activate'));

	// This gets fired by WS Form when it is ready to register add-ons
	add_action('wsf_plugins_loaded', function() {

		class WS_Form_Action_Constant_Contact_V3 extends WS_Form_Action {

			public $id = 'constantcontactv3';
			public $pro_required = true;
			public $label;
			public $label_action;
			public $events;
			public $multiple = true;
			public $configured = false;
			public $priority = 50;
			public $can_repost = true;
			public $form_add = false;
			public $record_label = 'Contacts';

			// Licensing
			private $licensing;

			// Config
			private $api_endpoint;
			private $api_access_token;
			private $api_access_token_expiry;
			private $api_refresh_token;
			public $list_id = false;
			public $list_memberships = false;
			public $opt_in_field;
			public $field_mapping;
			public $custom_mapping;
			public $tag_mapping;
			public $duplicates;
			public $duplicates_merge_lists;
			public $duplicates_merge_tags;

			// Constants
			const EMAIL_MERGE_FIELD = 'email_addresses|email_address';
			const WS_FORM_LICENSE_ITEM_ID = 203466;
			const WS_FORM_LICENSE_NAME = 'Constant Contact add-on for WS Form PRO';
			const WS_FORM_LICENSE_VERSION = '2.0.5';
			const WS_FORM_LICENSE_AUTHOR = 'WS Form';
			const ACCESS_TOKEN_EXPIRY_WINDOW = 600;
			const API_LIMIT = 100;

			public function __construct() {

				// Events
				$this->events = array('submit');

				// Register config filters
				add_filter('wsf_config_options', array($this, 'config_options'), 10, 1);
				add_filter('wsf_config_meta_keys', array($this, 'config_meta_keys'), 10, 2);
				add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugin_action_links'), 10, 1);
				add_action('wsf_settings', array($this, 'wsf_settings_access_token'), 10, 0);
				add_action('rest_api_init', array($this, 'rest_api_init'), 10, 0);

				// Add nag action
				add_action('wsf_nag', array($this, 'nag'));

				// Licensing
				$this->licensing = new WS_Form_Licensing(

					self::WS_FORM_LICENSE_ITEM_ID,
					$this->id,
					self::WS_FORM_LICENSE_NAME,
					self::WS_FORM_LICENSE_VERSION,
					self::WS_FORM_LICENSE_AUTHOR,
					__FILE__
				);
				$this->licensing->transient_check();
				add_action('admin_init', array($this->licensing, 'updater'));
				add_filter('wsf_settings_static', array($this, 'settings_static'), 10, 2);
				add_filter('wsf_settings_button', array($this, 'settings_button'), 10, 3);
				add_filter('wsf_settings_update_fields', array($this, 'settings_update_fields'), 10, 2);

				// Load plugin level configuration
				self::load_config_plugin();

				// Register init action
				add_action('init', array($this, 'init'));
			}

			// Init
			public function init() {

				// Set label
				$this->label = __('Constant Contact', 'ws-form-constant-contact-v3');

				// Set label for actions pull down
				$this->label_action = __('Add to Constant Contact', 'ws-form-constant-contact-v3');

				// Register action
				parent::register($this);
			}

			// Get license item ID
			public function get_license_item_id() {

				return self::WS_FORM_LICENSE_ITEM_ID;
			}

			// Settings page rendered, check for access token
			public function wsf_settings_access_token() {

				$this->api_access_token = WS_Form_Common::get_query_var($this->id . '_access_token', false);
				if($this->api_access_token === false) { return; }

				WS_Form_Common::option_set('action_' . $this->id . '_api_access_token', $this->api_access_token);

				// Save refresh token
				$this->api_refresh_token = WS_Form_Common::get_query_var($this->id . '_refresh_token', false);
				WS_Form_Common::option_set('action_' . $this->id . '_api_refresh_token', $this->api_refresh_token);

				// Save access token expiry
				$this->api_access_token_expiry = time() + intval(WS_Form_Common::get_query_var($this->id . '_expires_in', false)) - self::ACCESS_TOKEN_EXPIRY_WINDOW;
				WS_Form_Common::option_set('action_' . $this->id . '_api_access_token_expiry', $this->api_access_token_expiry);

				// Refresh
				$redirect_url = WS_Form_Common::get_admin_url('ws-form-settings', false, 'tab=action_' . $this->id);
				header('Location: ' . $redirect_url);
				exit;
			}

			// Check access token to see if it has expired
			public function access_token_check() {

				// Check if token has expired
				if($this->api_access_token_expiry > time()) { return true; }

				// Get new access token
				$path = 'constant-contact-v3/?refresh_token=' . rawurlencode($this->api_refresh_token);
				$api_response = parent::api_call('https://wsform.com/', $path, 'GET');

				// Check status code
				if($api_response['http_code'] == 200) { 

					// Process new access token
					$api_response_decoded = json_decode($api_response['response']);
					if(is_null($api_response_decoded)) { return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3')); }

					// Save access token
					$this->api_access_token = parent::get_object_value($api_response_decoded, $this->id . '_access_token');
					WS_Form_Common::option_set('action_' . $this->id . '_api_access_token', $this->api_access_token);

					// Save refresh token
					$this->api_refresh_token = parent::get_object_value($api_response_decoded, $this->id . '_refresh_token');
					WS_Form_Common::option_set('action_' . $this->id . '_api_refresh_token', $this->api_refresh_token);

					// Save access token expiry
					$this->api_access_token_expiry = time() + intval(parent::get_object_value($api_response_decoded, $this->id . '_expires_in')) - self::ACCESS_TOKEN_EXPIRY_WINDOW;
					WS_Form_Common::option_set('action_' . $this->id . '_api_access_token_expiry', $this->api_access_token_expiry);
				}
			}

			// Plugin action link
			public function plugin_action_links($links) {

				// Settings
				array_unshift($links, sprintf('<a href="%s">%s</a>', WS_Form_Common::get_admin_url('ws-form-settings', false, 'tab=action_' . $this->id), __('Settings', 'ws-form')));

				return $links;
			}

			// Settings - Static
			public function settings_static($value, $field) {

				switch ($field) {

					case 'action_' . $this->id . '_license_version' :

						$value = self::WS_FORM_LICENSE_VERSION;
						break;

					case 'action_' . $this->id . '_license_status' :

						$value = $this->licensing->license_status();
						break;

					case 'action_' . $this->id . '_status' :

						$value = $this->configured ? 'Connected' : 'Disconnected';
						break;
				}

				return $value;
			}

			// Settings - Button
			public function settings_button($value, $field, $button) {

				switch($button) {

					case 'license_action_' . $this->id :

						$license_activated = WS_Form_Common::option_get('action_' . $this->id . '_license_activated', false);
						if($license_activated) {

							$value = '<input class="wsf-button" type="button" data-action="wsf-mode-submit" data-mode="deactivate" value="' . __('Deactivate', 'ws-form-constant-contact-v3') . '" />';

						} else {

							$value = '<input class="wsf-button" type="button" data-action="wsf-mode-submit" data-mode="activate" value="' . __('Activate', 'ws-form-constant-contact-v3') . '" />';
						}

						break;

					case 'oauth_action_' . $this->id :

						if($this->api_access_token) {

							$value = '<input class="wsf-button wsf-button-inline" type="button" data-action="wsf-mode-submit" data-mode="access_token_delete" value="' . __('Disconnect', 'ws-form-constant-contact-v3') . '" />';

						} else {

							// Build query string
							$button_url = 'https://wsform.com/constant-contact-v3';
							$button_query_string = 'referrer=' . urlencode(WS_Form_Common::get_current_url());
							$button_url = WS_Form_Common::add_query_string($button_url, $button_query_string);

							$value = sprintf('<a href="%s" class="wsf-button wsf-button-inline">%s</a>', $button_url,  __('Connect', 'ws-form-constant-contact-v3'));
						}

						break;
				}
				
				return $value;
			}

			// Settings - Update fields
			public function settings_update_fields($field, $value) {

				switch ($field) {

					case 'action_' . $this->id . '_license_key' :

						$mode = WS_Form_Common::get_query_var('action_mode');

						switch($mode) {

							case 'activate' :

								$this->licensing->activate($value);
								break;

							case 'deactivate' :

								$this->licensing->deactivate($value);
								break;

							case 'access_token_delete' :

								self::reset_api();
								break;
						}

						break;
				}
			}

			// Reset API
			public function reset_api() {

				WS_Form_Common::option_set('action_' . $this->id . '_api_access_token', '');
				WS_Form_Common::option_set('action_' . $this->id . '_api_access_token_expiry', '');
				WS_Form_Common::option_set('action_' . $this->id . '_api_refresh_token', '');
			}

			// Nag
			public function nag() {

				// Load plugin level configuration
				self::load_config_plugin();

				if(!$this->configured) {

					WS_Form_Common::admin_message_push(sprintf(__('To complete the %s setup for %s, please connect your Constant Contact account <a href="%s">here</a>.', 'ws-form-constant-contact-v3'), $this->label, WS_FORM_NAME_PRESENTABLE, WS_Form_Common::get_admin_url('ws-form-settings', -1, 'tab=action_' . $this->id)), 'notice-warning', false);
				}
			}

			// Post to API
			public function post($form, &$submit, $config) {

				// Check action is configured properly
				if(!self::check_configured()) { return false; }

				// Load configuration
				self::load_config($config);

				// Check list ID is configured properly
				if(!self::check_list_id()) { return false; }

				// Opt-In Check
				$opted_in = false;

				// Get opt in value (False if field not submitted)
				$opt_in_field_value = parent::get_submit_value($submit, WS_FORM_FIELD_PREFIX . $this->opt_in_field, false);

				if(($this->opt_in_field !== false) && ($this->opt_in_field !== '') && ($opt_in_field_value !== false)) {

					// End user did not opt in, exit gracefully
					if(empty($opt_in_field_value)) {

						self::success(__('User did not opt in, no data pushed to action', 'ws-form-constant-contact-v3'));
						return true;

					} else {

						$opted_in = true;
					}
				}

				// Build API fields
				$api_fields = array();

				// Get list fields
				$list_fields = self::get_list_fields();
				$list_fields_type_lookup = array();
				foreach($list_fields as $list_field) {

					$list_fields_type_lookup[$list_field['id']] = $list_field['action_type'];
				}

				// Get fields
				$fields = WS_Form_Common::get_fields_from_form($form);

				// Process field mapping
				foreach($this->field_mapping as $field_map) {

					// Get field ID
					$field_id = $field_map['ws_form_field'];

					// Get list field
					$list_field = $field_map['action_' . $this->id . '_list_fields'];

					// Get submit value
					$submit_value = parent::get_submit_value($submit, WS_FORM_FIELD_PREFIX . $field_id, false);
					if($submit_value === false) { continue; }

					// Process according to Zendesk type
					if(!isset($list_fields_type_lookup[$list_field])) { continue; }
					$action_type = $list_fields_type_lookup[$list_field];

					// Get field
					$field_object = isset($fields[$field_id]) ? $fields[$field_id] : false;

					// Process value
					self::process_mapping($api_fields, $list_field, $submit_value, $opted_in, $action_type, $field_object);
				}

				// Process custom mapping
				foreach($this->custom_mapping as $custom_map) {

					// Get field value
					$field_value = $custom_map['action_' . $this->id . '_custom_mapping_value'];
					$field_value = WS_Form_Common::parse_variables_process($field_value, $form, $submit, 'text/plain');

					// Get list field
					$list_field = $custom_map['action_' . $this->id . '_list_fields'];

					// Process according to Zendesk type
					if(!isset($list_fields_type_lookup[$list_field])) { continue; }
					$action_type = $list_fields_type_lookup[$list_field];

					// Process value
					self::process_mapping($api_fields, $list_field, $field_value, $opted_in, $action_type);
				}

				// Process tags
				$tags = array();
				foreach($this->tag_mapping as $field_map) {

					// Get field ID
					$field_id = $field_map['ws_form_field'];
					if($field_id == '') { continue; }

					// Read submit meta
					$tags_array = parent::get_submit_value($submit, WS_FORM_FIELD_PREFIX . $field_id, false);
					if($tags_array === false) { continue; }

					// Turn into array if it isn't already
					if(!is_array($tags_array)) { $tags_array = array($tags_array); }

					// Process each tag ready for API format
					foreach($tags_array as $tag) {

						if($tag == '') { continue; }

						$tags[] = $tag;
					}
				}

				// Check user email address is valid
				if(
					!isset($api_fields['email_address']) ||
					!isset($api_fields['email_address']['address']) ||
					!filter_var($api_fields['email_address']['address'], FILTER_VALIDATE_EMAIL)) {

					self::error(sprintf(__('Invalid user email address: %s', 'ws-form-constant-contact-v3'), $api_fields['email_address']['address']));
					return 'halt';
				}

				// Create source
				$api_fields['create_source'] = 'Contact';

				// Add to list
				$api_fields['list_memberships'] = array($this->list_id);

				// Additional list memberships
				foreach($this->list_memberships as $list_membership) {

					// Get list ID
					$list_id = $list_membership['action_' . $this->id . '_list_id'];
					if(!empty($list_id) && !in_array($list_id, $api_fields['list_memberships'])) {

						$api_fields['list_memberships'][] = $list_id;
					}
				}

				// Tags
				$api_fields['taggings'] = $tags;

				// Access token check
				self::access_token_check();

				// Add contact
				$api_response = parent::api_call($this->api_endpoint, '/v3/contacts', 'POST', json_encode($api_fields), self::get_api_headers());

				// Process response
				switch($api_response['http_code']) {

					// Contact created
					case 201 :

						// Process response
						$api_response_decoded = json_decode($api_response['response']);
						if(is_null($api_response_decoded)) { return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3')); }

						self::success(sprintf(__('Successfully added to list: %s', 'ws-form-constant-contact-v3'), $this->list_id));
						return true;

					// Duplicate contact
					case 409 :

						// Determine how to process duplicates
						switch($this->duplicates) {

							case 'update' :

								// Process response
								$api_response_decoded = json_decode($api_response['response']);
								if(is_null($api_response_decoded)) { return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3')); }

								if(
									!is_array($api_response_decoded) ||
									!isset($api_response_decoded[0]) ||
									!isset($api_response_decoded[0]->error_key) ||
									$api_response_decoded[0]->error_key != 'contacts.api.conflict' ||
									!isset($api_response_decoded[0]->error_message)
								) {
									// Invalid request
									self::error(self::get_api_error_detail($api_response['response']));
									return 'halt';
								}

								// Get contact ID. Not ideal but we'll get it from the error message
								$error_message = $api_response_decoded[0]->error_message;
								$error_message_array = explode(' ', $error_message);
								$contact_id = end($error_message_array);

								// Merge lists
								if(
									$this->duplicates_merge_lists ||
									$this->duplicates_merge_tags
								) {

									// Get existing lists
									$api_response = parent::api_call($this->api_endpoint, sprintf('/v3/contacts/%s', $contact_id), 'GET', array('include' => 'list_memberships,taggings'), self::get_api_headers());

									// Process response
									switch($api_response['http_code']) {

										// Contact updated
										case 200 :

											// Process response
											$api_response_decoded = json_decode($api_response['response']);
											if(
												is_null($api_response_decoded) ||
												!is_object($api_response_decoded)
											) {
												return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3'));
											}

											if(
												$this->duplicates_merge_lists &&
												property_exists($api_response_decoded, 'list_memberships') &&
												is_array($api_response_decoded->list_memberships) &&
												(count($api_response_decoded->list_memberships) > 0)
											) {
												$list_memberships_add = $api_fields['list_memberships'];
												$api_fields['list_memberships'] = $api_response_decoded->list_memberships;

												foreach($list_memberships_add as $list_id) {

													if(!in_array($list_id, $api_fields['list_memberships'])) {

														$api_fields['list_memberships'][] = $list_id;
													}
												}
											}

											if(
												$this->duplicates_merge_tags &&
												property_exists($api_response_decoded, 'taggings') &&
												is_array($api_response_decoded->taggings) &&
												(count($api_response_decoded->taggings) > 0)
											) {
												$taggings_add = $api_fields['taggings'];
												$api_fields['taggings'] = $api_response_decoded->taggings;

												foreach($taggings_add as $tag) {

													if(!in_array($tag, $api_fields['taggings'])) {

														$api_fields['taggings'][] = $tag;
													}
												}
											}

											break;
									}
								}

								// Set contact ID
								$api_fields['contact_id'] = $contact_id;

								// Update source
								$api_fields['update_source'] = 'Contact';

								// Update contact
								$api_response = parent::api_call($this->api_endpoint, sprintf('/v3/contacts/%s', $contact_id), 'PUT', json_encode($api_fields), self::get_api_headers());

								// Process response
								switch($api_response['http_code']) {

									// Contact updated
									case 200 :

										// Process response
										$api_response_decoded = json_decode($api_response['response']);
										if(is_null($api_response_decoded)) { return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3')); }

										self::success(sprintf(__('Successfully updated contact: %s', 'ws-form-constant-contact-v3'), $contact_id));
										return true;

									// Error
									default :

										// Invalid request
										self::error(self::get_api_error_detail($api_response['response']));
										return 'halt';
								}

							case 'halt' :

								self::error(__('Duplicate email address found', 'ws-form-constant-contact-v3'));
								return 'halt';
						}

						break;

					// Error
					default :

						// Invalid request
						self::error(self::get_api_error_detail($api_response['response']));
						return 'halt';
				}
			}

			// Process mapping
			public function process_mapping(&$api_fields, $list_field, $meta_value, $opted_in, $action_type, $field_object = false) {

				// Split list field
				$list_field_array = explode('|', $list_field);
				if(count($list_field_array) != 3) { return false; }

				$sub = $list_field_array[0];
				$kind = $list_field_array[1];
				$meta_key = $list_field_array[2];

				// Check for arrays
				if(is_array($meta_value)) { $meta_value = implode(',', $meta_value); }

				// Process meta value
				$meta_value = self::meta_value_process($meta_value, $action_type, $field_object);

				// Process by sub
				switch($sub) {

					case 'base' :

						$api_fields[$meta_key] = $meta_value;

						break;

					case 'email' :

						$api_fields['email_address'] = array(

							'address' => $meta_value,
							'permission_to_send' => ($opted_in ? 'explicit' : 'implicit')
						);

						break;

					case 'phone' :

						// Check there are no more than 3 components
						if(
							isset($api_fields['phone_numbers']) &&
							(count($api_fields['phone_numbers']) > 2)
						) {
							break;
						}

						$api_fields['phone_numbers'][] = array(

							$meta_key => $meta_value,
							'kind' => $kind
						);

						break;

					case 'address' :

						$street_address_kind_found = false;

						if(isset($api_fields['street_addresses'])) {

							foreach($api_fields['street_addresses'] as $index => $street_address) {

								if($street_address['kind'] == $kind) {

									$api_fields['street_addresses'][$index][$meta_key] = $meta_value;

									$street_address_kind_found = true;
								}
							}
						}

						if(!$street_address_kind_found) {

							// Check there are no more than 3 components
							if(
								isset($api_fields['street_addresses']) &&
								(count($api_fields['street_addresses']) > 2)
							) {
								break;
							}

							$api_fields['street_addresses'][] = array(

								$meta_key => $meta_value,
								'kind' => $kind
							);
						}

						break;

					case 'custom' :

						$api_fields['custom_fields'][] = array(

							'custom_field_id' => $meta_key,
							'value' => $meta_value
						);

						break;
				}
			}

			// Process meta value
			public function meta_value_process($meta_value, $action_type, $field_object = false) {

				if(is_array($meta_value)) { $meta_value = implode(';', $meta_value); }

				switch($action_type) {

					case 'date' :

						if($field_object !== false) {

							$meta_value = WS_Form_Common::get_date_by_type($meta_value, $field_object, 'Y-m-d');

						} else {

							$meta_value = date('Y-m-d', strtotime($meta_value));
						}

						break;
				}

				return $meta_value;
			}

			// Get from API
			public function get($form, $user) {

				// Check action is configured properly
				if(!self::check_configured()) { return false; }

				// Check list ID is set
				if(!self::check_list_id()) { return false; }

				// Get user email address
				$user_email = $user->user_email;

				// Check user email address is valid
				if(!filter_var($user_email, FILTER_VALIDATE_EMAIL)) { return self::error(__('Invalid user email address', 'ws-form-constant-contact-v3')); }

				// Parameters
				$params = array(

					'email'		=>	$user_email,
					'include'	=>	'custom_fields,phone_numbers,street_addresses,taggings'
				);

				// Access token check
				self::access_token_check();

				// Get contact
				$api_response = parent::api_call($this->api_endpoint, '/v3/contacts/', 'GET', $params, self::get_api_headers());

				// Check status code
				if($api_response['http_code'] != 200) { return false; }

				// Process list data
				$api_response_decoded = json_decode($api_response['response']);
				if(is_null($api_response_decoded)) { return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3')); }

				if(!isset($api_response_decoded->contacts)) { return false; }
				if(!isset($api_response_decoded->contacts[0])) { return false; }

				$api_response_decoded = $api_response_decoded->contacts[0];

				// Process fields and tags
				$fields_return = array();
				$tags_return = array();

				$contact_fields = get_object_vars($api_response_decoded);

				foreach($contact_fields as $id => $value) {

					if(is_null($value)) { continue; }

					switch($id) {

						// Email address
						case 'email_address' :

							if(is_object($value) && isset($value->address)) {

								$fields_return['email||email_address'] = $value->address;
							}

							break;

						// Base
						case 'first_name' :
						case 'last_name' :
						case 'job_title' :
						case 'company_name' :
						case 'birthday_month' :
						case 'birthday_day' :
						case 'anniversary' :

							$fields_return[sprintf('base||%s', $id)] = $value;

							break;

						// Process custom fields
						case 'custom_fields' :

							if(!is_array($value)) { break; }

							foreach($value as $custom_field) {

								if(
									!is_object($custom_field) ||
									!isset($custom_field->custom_field_id) ||
									!isset($custom_field->value)
								) {
									break;
								}

								$fields_return[sprintf('custom||%s', $custom_field->custom_field_id)] = $custom_field->value;
							}

							break;

						// Process phone numbers
						case 'phone_numbers' :

							if(!is_array($value)) { break; }

							foreach($value as $phone_number) {

								if(
									!is_object($phone_number) ||
									!isset($phone_number->phone_number) ||
									!isset($phone_number->kind)
								) {
									break;
								}

								$fields_return[sprintf('phone|%s|phone_number', $phone_number->kind)] = $phone_number->phone_number;
							}

							break;

						// Process street addresses
						case 'street_addresses' :

							if(!is_array($value)) { break; }

							foreach($value as $street_address) {

								if(
									!is_object($street_address) ||
									!isset($street_address->kind)
								) {
									break;
								}

								$fields_return[sprintf('address|%s|street', $street_address->kind)] = $street_address->street;
								$fields_return[sprintf('address|%s|city', $street_address->kind)] = $street_address->city;
								$fields_return[sprintf('address|%s|state', $street_address->kind)] = $street_address->state;
								$fields_return[sprintf('address|%s|postal_code', $street_address->kind)] = $street_address->postal_code;
								$fields_return[sprintf('address|%s|country', $street_address->kind)] = $street_address->country;
							}

							break;

						// Process tags
						case 'taggings' :

							if(!is_array($value)) { break; }

							foreach($value as $tag) {

								$tags_return[$tag] = true;
							}

							break;
					}
				}

				$return_array = array('fields' => $fields_return, 'tags' => $tags_return);

				return $return_array;
			}

			// Get lists
			public function get_lists($fetch = false) {

				// Check action is configured properly
				if(!self::check_configured()) { return false; }

				// Check to see if lists are cached
				$lists = WS_Form_Common::option_get('action_' . $this->id . '_lists');

				// Retried if fetch is requested or lists are not cached
				if($fetch || ($lists === false)) {

					$lists = array();

					// Parameters
					$params = array(

						'limit' => self::API_LIMIT,
						'include_count' =>	true
					);

					// Access token check
					self::access_token_check();

					// Path
					$path = '/v3/contact_lists';

					// Loop
					$has_next = true;

					do {

						// Get lists
						$api_response = parent::api_call($this->api_endpoint, $path, 'GET', $params, self::get_api_headers());

						// Check status code
						if($api_response['http_code'] != 200) { return self::error(self::get_api_error_detail($api_response['response'])); }

						// Decode response
						$api_response_decoded = json_decode($api_response['response']);
						if(is_null($api_response_decoded)) { return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3')); }

						if(
							!isset($api_response_decoded->lists) ||
							!is_array($api_response_decoded->lists)
						) {
							return self::error(__('Lists not found in API response data', 'ws-form-constant-contact-v3'));
						}

						foreach($api_response_decoded->lists as $list) {

							$lists[] = array(

								'id' => 			parent::get_object_value($list, 'list_id'), 
								'label' => 			parent::get_object_value($list, 'name'), 
								'field_count' => 	false,
								'record_count' => 	parent::get_object_value($list, 'membership_count', 0)
							);
						}

						// Check for pagination
						if(
							isset($api_response_decoded->_links) &&
							isset($api_response_decoded->_links->next) &&
							isset($api_response_decoded->_links->next->href)
						) {

							$path = $api_response_decoded->_links->next->href;

						} else {

							$has_next = false;
						}

					} while($has_next);

					// Store to options
					WS_Form_Common::option_set('action_' . $this->id . '_lists', $lists);
				}

				return $lists;
			}

			// Get list
			public function get_list($fetch = false) {

				// Check action is configured properly
				if(!self::check_configured()) { return false; }

				// Check list ID is set
				if(!self::check_list_id()) { return false; }

				$list = WS_Form_Common::option_get('action_' . $this->id . '_list_' . $this->list_id);

				if($fetch || ($list === false)) {

					// Parameters
					$params = array();

					// Access token check
					self::access_token_check();

					// Get list
					$api_response = parent::api_call($this->api_endpoint, '/v3/contact_lists/' . $this->list_id, 'GET', $params, self::get_api_headers());

					// Check status code
					if($api_response['http_code'] != 200) { return self::error(self::get_api_error_detail($api_response['response'])); }

					// Process list data
					$api_response_list = json_decode($api_response['response']);
					if(is_null($api_response_list)) { return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3')); }

					// Build list
					$list = array(

						'label' => parent::get_object_value($api_response_list, 'name')
					);

					// Store to options
					WS_Form_Common::option_set('action_' . $this->id . '_list_' . $this->list_id, $list);
				}

				return $list;
			}

			// Get list fields
			public function get_list_fields($fetch = false) {

				// Check action is configured properly
				if(!self::check_configured()) { return false; }

				$list_fields = WS_Form_Common::option_get('action_' . $this->id . '_list_field');

				if($fetch || ($list_fields === false)) {

					$list_fields = array();

					$sort_index = 0;

					// We cannot get these fields from Constant Contact API V3, only custom fields, so we'll simulate it
					$fields = array(

						(object) array('id' => 'email||email_address', 'label' => 'Email', 'action_type' => 'email', 'type' => 'email', 'required' => true),
						(object) array('id' => 'base||first_name', 'label' => 'First Name', 'action_type' => 'text', 'type' => 'text', 'required' => false),
						(object) array('id' => 'base||last_name', 'label' => 'Last Name', 'action_type' => 'text', 'type' => 'text', 'required' => false),
						(object) array('id' => 'base||job_title', 'label' => 'Job Title', 'action_type' => 'text', 'type' => 'text', 'required' => false),
						(object) array('id' => 'base||company_name', 'label' => 'Company Name', 'action_type' => 'text', 'type' => 'text', 'required' => false),

						(object) array('id' => 'base||birthday_month', 'label' => 'Birthday Month', 'action_type' => 'select', 'type' => 'select', 'required' => false, 'meta' => self::get_meta_number_select(12)),
						(object) array('id' => 'base||birthday_day', 'label' => 'Birthday Day', 'action_type' => 'select', 'type' => 'select', 'required' => false, 'meta' => self::get_meta_number_select(31)),
						(object) array('id' => 'base||anniversary', 'label' => 'Anniversary', 'action_type' => 'date', 'type' => 'datetime', 'required' => false, 'meta' => array('max_date' => sprintf('%u-01-01', date('Y', strtotime('+11 years'))))),

						(object) array('id' => 'phone|home|phone_number', 'label' => 'Phone (Home)', 'action_type' => 'tel', 'type' => 'tel', 'required' => false),
						(object) array('id' => 'phone|work|phone_number', 'label' => 'Phone (Work)', 'action_type' => 'tel', 'type' => 'tel', 'required' => false),
						(object) array('id' => 'phone|mobile|phone_number', 'label' => 'Phone (Mobile)', 'action_type' => 'tel', 'type' => 'tel', 'required' => false),
						(object) array('id' => 'phone|fax|phone_number', 'label' => 'Phone (Fax)', 'action_type' => 'tel', 'type' => 'tel', 'required' => false, 'no_add' => true),
						(object) array('id' => 'phone|other|phone_number', 'label' => 'Phone (Other)', 'action_type' => 'tel', 'type' => 'tel', 'required' => false, 'no_add' => true),

						(object) array('id' => 'address|home|street', 'label' => 'Address Home - Street', 'action_type' => 'text', 'type' => 'text', 'required' => false),
						(object) array('id' => 'address|home|city', 'label' => 'Address Home - City', 'action_type' => 'text', 'type' => 'text', 'required' => false),
						(object) array('id' => 'address|home|state', 'label' => 'Address Home - State', 'action_type' => 'text', 'type' => 'text', 'required' => false),
						(object) array('id' => 'address|home|postal_code', 'label' => 'Address Home - Postal Code', 'action_type' => 'text', 'type' => 'text', 'required' => false),
						(object) array('id' => 'address|home|country', 'label' => 'Address Home - Country', 'action_type' => 'select', 'type' => 'select', 'required' => false, 'meta' => self::get_meta_country()),

						(object) array('id' => 'address|work|street', 'label' => 'Address Work - Street', 'action_type' => 'text', 'type' => 'text', 'required' => false, 'no_add' => true),
						(object) array('id' => 'address|work|city', 'label' => 'Address Work - City', 'action_type' => 'text', 'type' => 'text', 'required' => false, 'no_add' => true),
						(object) array('id' => 'address|work|state', 'label' => 'Address Work - State', 'action_type' => 'text', 'type' => 'text', 'required' => false, 'no_add' => true),
						(object) array('id' => 'address|work|postal_code', 'label' => 'Address Work - Postal Code', 'action_type' => 'text', 'type' => 'text', 'required' => false, 'no_add' => true),
						(object) array('id' => 'address|work|country', 'label' => 'Address Work - Country', 'action_type' => 'select', 'type' => 'select', 'required' => false, 'meta' => self::get_meta_country(), 'no_add' => true),

						(object) array('id' => 'address|other|street', 'label' => 'Address Other - Street', 'action_type' => 'text', 'type' => 'text', 'required' => false, 'no_add' => true),
						(object) array('id' => 'address|other|city', 'label' => 'Address Other - City', 'action_type' => 'text', 'type' => 'text', 'required' => false, 'no_add' => true),
						(object) array('id' => 'address|other|state', 'label' => 'Address Other - State', 'action_type' => 'text', 'type' => 'text', 'required' => false, 'no_add' => true),
						(object) array('id' => 'address|other|postal_code', 'label' => 'Address Other - Postal Code', 'action_type' => 'text', 'type' => 'text', 'required' => false, 'no_add' => true),
						(object) array('id' => 'address|other|country', 'label' => 'Address Other - Country', 'action_type' => 'select', 'type' => 'select', 'required' => false, 'meta' => self::get_meta_country(), 'no_add' => true),
					);

					// Process fields
					$sort_index_offset = 0;
					foreach($fields as $field) {

						$type = parent::get_object_value($field, 'type');
						$action_type = parent::get_object_value($field, 'action_type');

						$list_fields[] = array(

							'id' => 			parent::get_object_value($field, 'id'),
							'label' => 			parent::get_object_value($field, 'label'), 
							'label_field' => 	parent::get_object_value($field, 'label'), 
							'type' => 			$type,
							'action_type' =>	$action_type,
							'required' => 		parent::get_object_value($field, 'required'), 
							'default_value' => 	'',
							'pattern' => 		'',
							'placeholder' => 	'',
							'help' => 			'', 
							'sort_index' => 	$sort_index++,
							'visible' =>		true,
							'meta' => 			parent::get_object_value($field, 'meta'),
							'no_add' =>			parent::get_object_value($field, 'no_add')
						);
					}

					// Get custom fields

					// Parameters
					$params = array(

						'limit' => self::API_LIMIT
					);

					// Access token check
					self::access_token_check();

					// Path
					$path = '/v3/contact_custom_fields';

					// Loop
					$has_next = true;

					do {

						// Get lists
						$api_response = parent::api_call($this->api_endpoint, $path, 'GET', $params, self::get_api_headers());

						// Check status code
						if($api_response['http_code'] != 200) { return self::error(self::get_api_error_detail($api_response['response'])); }

						// Decode response
						$api_response_decoded = json_decode($api_response['response']);
						if(is_null($api_response_decoded)) { return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3')); }

						if(
							!isset($api_response_decoded->custom_fields) ||
							!is_array($api_response_decoded->custom_fields)
						) {
							return self::error(__('Custom fields not found in API response data', 'ws-form-constant-contact-v3'));
						}

						foreach($api_response_decoded->custom_fields as $field) {

							// Get type
							$type = self::action_field_type_to_ws_form_field_type($field);
							$action_type = parent::get_object_value($field, 'type');

							$list_fields[] = array(

								'id' => 			sprintf('custom||%s', parent::get_object_value($field, 'custom_field_id')),
								'label' => 			parent::get_object_value($field, 'label'), 
								'label_field' => 	parent::get_object_value($field, 'label'), 
								'type' => 			$type,
								'action_type' =>	$action_type,
								'required' => 		false, 
								'default_value' => 	'',
								'pattern' => 		'',
								'placeholder' => 	'',
								'help' => 			'', 
								'sort_index' => 	$sort_index++,
								'visible' =>		true,
								'meta' => 			false
							);
						}

						// Check for pagination
						if(
							isset($api_response_decoded->_links) &&
							isset($api_response_decoded->_links->next) &&
							isset($api_response_decoded->_links->next->href)
						) {

							$path = $api_response_decoded->_links->next->href;

						} else {

							$has_next = false;
						}

					} while($has_next);

					// Store to options
					WS_Form_Common::option_set('action_' . $this->id . '_list_fields', $list_fields);
				}

				return $list_fields;
			}

			// Get form fields
			public function get_fields() {

				$form_fields = array(

					'opt_in_field' => array(

						'type'	=>	'checkbox',
						'label'	=>	__('Opt-In', 'ws-form'),
						'meta'	=>	array(

							'data_grid_checkbox' => WS_Form_Common::build_data_grid_meta('data_grid_checkbox', false, false, array(

								array(

									'id'		=> 1,
									'default'	=> '',
									'required'	=> '',
									'disabled'	=> '',
									'hidden'	=> '',
									'data'		=> array(__('I would like to opt-in and receive emails from #blog_name', 'ws-form'))
								)
							))
						)
					),

					'submit' => array(

						'type'			=>	'submit',
						'width_factor'	=>	0.5
					),

					'reset' => array(

						'type'			=>	'reset',
						'width_factor'	=>	0.5
					),
				);

				return $form_fields;
			}

			// Get form actions
			public function get_actions() {

				$form_actions = array(

					$this->id => array(

						'meta'	=> array(

							'action_' . $this->id . '_list_id'			=>	$this->list_id,
							'action_' . $this->id . '_opt_in_field'		=>	'opt_in_field',
							'action_' . $this->id . '_field_mapping'	=>	'field_mapping',
							'action_' . $this->id . '_tag_mapping'		=>	'tag_mapping'
						)
					),

					'message',

					'database'
				);

				return $form_actions;
			}

			// Get form meta
			public function get_meta() {

				return array('submit_reload' => '');
			}

			// Get tag categories
			public function get_tag_categories($fetch = false) {

				$tag_categories = array(

					array(

						'id' => 		1,
						'label' => 		__('Tags', 'ws-form-constant-contact-v3'), 
						'sort_index' =>	1,
						'type' => 		'checkbox'
					)
				);

				return $tag_categories;
			}

			// Get tags
			public function get_tags($tag_category_id = false, $fetch = false) {

				// Check action is configured properly
				if(!self::check_configured()) { return false; }

				// Check list ID is set
				if(!self::check_list_id()) { return false; }

				// Check tag category ID is set
				if($tag_category_id === false) { return self::error(__('Tag category ID is not set', 'ws-form-constant-contact-v3')); }

				$tags = WS_Form_Common::option_get('action_' . $this->id . '_tag_categories_' . $this->list_id . '_tags_' . $tag_category_id);

				if($fetch || ($tags === false)) {

					$tags = array();

					// Parameters
					$params = array(

						'limit' => self::API_LIMIT,
						'include_counts' => true
					);

					// Access token check
					self::access_token_check();

					// Path
					$path = '/v3/contact_tags';

					// Loop
					$has_next = true;
					$sort_index = 0;

					do {

						// Get lists
						$api_response = parent::api_call($this->api_endpoint, $path, 'GET', $params, self::get_api_headers());

						// Check status code
						if($api_response['http_code'] != 200) { return self::error(self::get_api_error_detail($api_response['response'])); }

						// Decode response
						$api_response_decoded = json_decode($api_response['response']);
						if(is_null($api_response_decoded)) { return self::error(__('Invalid API response data', 'ws-form-constant-contact-v3')); }

						if(
							!isset($api_response_decoded->tags) ||
							!is_array($api_response_decoded->tags)
						) {
							return self::error(__('Tags not found in API response data', 'ws-form-constant-contact-v3'));
						}

						foreach($api_response_decoded->tags as $contact_tag) {

							$tags[] = array(

								'id' => 			parent::get_object_value($contact_tag, 'tag_id'),
								'label' => 			parent::get_object_value($contact_tag, 'name'),
								'record_count' =>	parent::get_object_value($contact_tag, 'contacts_count'),
								'sort_index' =>		$sort_index++
							);
						}

						// Check for pagination
						if(
							isset($api_response_decoded->_links) &&
							isset($api_response_decoded->_links->next) &&
							isset($api_response_decoded->_links->next->href)
						) {

							$path = $api_response_decoded->_links->next->href;

						} else {

							$has_next = false;
						}

					} while($has_next);

					// Store to options
					WS_Form_Common::option_set('action_' . $this->id . '_tag_categories_' . $this->list_id . '_tags_' . $tag_category_id, $tags);
				}

				return $tags;
			}

			// Get API error title
			public function get_api_error_title($api_response) {

				$api_response_decoded = json_decode($api_response);
				if(is_null($api_response_decoded)) { return __('Unknown API error for action: ' . $this->label, 'ws-form-constant-contact-v3'); }

				// Get error title from JSON response
				$error_title = $api_response_decoded->detail;

				return $error_title;
			}

			// Get API error detail
			public function get_api_error_detail($api_response) {

				$api_response_decoded = json_decode($api_response);
				if(is_null($api_response_decoded)) { return __('Unknown API error for action: ' . $this->label, 'ws-form-constant-contact-v3'); }

				if(!is_array($api_response_decoded)) { return __('Unknown API error for action: ' . $this->label, 'ws-form-constant-contact-v3'); }

				$error_detail_array = array();
				foreach($api_response_decoded as $error) {

					$error_detail_array[] = $error->error_message;
				}

				return $error_detail_array;
			}

			// Get settings
			public function get_action_settings() {

				$settings = array(

					'meta_keys'		=> array(

						'action_' . $this->id . '_list_id',
						'action_' . $this->id . '_list_memberships',
						'action_' . $this->id . '_opt_in_field',
						'action_' . $this->id . '_field_mapping',
						'action_' . $this->id . '_custom_mapping',
						'action_' . $this->id . '_tag_mapping',
						'action_' . $this->id . '_duplicates',
						'action_' . $this->id . '_duplicates_merge_lists',
						'action_' . $this->id . '_duplicates_merge_tags'
					)
				);

				// Wrap settings so they will work with sidebar_html function in admin.js
				$settings = parent::get_settings_wrapper($settings);

				// Add labels
				$settings->label = $this->label;
				$settings->label_action = $this->label_action;

				// Add multiple
				$settings->multiple = $this->multiple;

				// Add events
				$settings->events = $this->events;

				// Add can_repost
				$settings->can_repost = $this->can_repost;

				// Apply filter
				$settings = apply_filters('wsf_action_' . $this->id . '_settings', $settings);

				return $settings;
			}

			// Check action is configured properly
			public function check_configured() {

				if(!$this->configured) { return self::error(__('Action not configured', 'ws-form-constant-contact-v3') . ' (' . $this->label . ''); }

				return $this->configured;
			}

			// Check list ID is set
			public function check_list_id() {

				if($this->list_id === false) { return self::error(__('List ID is not set', 'ws-form-constant-contact-v3')); }

				return ($this->list_id !== false);
			}

			// Meta keys for this action
			public function config_meta_keys($meta_keys = array(), $form_id = 0) {

				// Build config_meta_keys
				$config_meta_keys = array(

					// List ID
					'action_' . $this->id . '_list_id'	=> array(

						'label'						=>	__('Constant Contact List', 'ws-form-constant-contact-v3'),
						'type'						=>	'select',
						'help'						=>	__('Select the Constant Contact list to assign the contact to.', 'ws-form-constant-contact-v3'),
						'options'						=>	'action_api_populate',
						'options_blank'					=>	__('Select...', 'ws-form-constant-contact-v3'),
						'options_action_id_meta_key'	=>	'action_id',
						'options_action_api_populate'	=>	'lists',
						'reload'					=>	array(

							'action_id'			=>	$this->id,
							'method'			=>	'lists_fetch'
						),
					),

					// List memberships
					'action_' . $this->id . '_list_memberships'	=> array(

						'label'						=>	__('Additional List Memberships', 'ws-form-constant-contact-v3'),
						'type'						=>	'repeater',
						'help'						=>	__('Select additional Constant Contact lists to assign the contact to.', 'ws-form-constant-contact-v3'),
						'meta_keys'					=>	array(

							'action_' . $this->id . '_list_id_additional'
						),
						'meta_keys_unique'			=>	array(

							'action_' . $this->id . '_list_id_additional'
						),
						'reload'					=>	array(

							'action_id'			=>	$this->id,
							'method'			=>	'list_fields_fetch',
							'list_id_meta_key'	=>	'action_' . $this->id . '_list_id'
						),
						'condition'					=>	array(

							array(

								'logic'			=>	'!=',
								'meta_key'		=>	'action_' . $this->id . '_list_id',
								'meta_value'	=>	''
							)
						)
					),

					// List ID
					'action_' . $this->id . '_list_id_additional'	=> array(

						'label'						=>	__('Constant Contact List', 'ws-form-constant-contact-v3'),
						'type'						=>	'select',
						'help'						=>	__('Select the Constant Contact list to associate this form with.', 'ws-form-constant-contact-v3'),
						'options'						=>	'action_api_populate',
						'options_blank'					=>	__('Select...', 'ws-form-constant-contact-v3'),
						'options_action_id_meta_key'	=>	'action_id',
						'options_action_api_populate'	=>	'lists',
						'key'							=>	'action_' . $this->id . '_list_id'
					),

					// Opt-In field
					'action_' . $this->id . '_opt_in_field'	=> array(

						'label'							=>	__('Opt-In Field', 'ws-form-constant-contact-v3'),
						'type'							=>	'select',
						'options'						=>	'fields',
						'options_blank'					=>	__('Select...', 'ws-form-constant-contact-v3'),
						'fields_filter_type'			=>	array('select', 'checkbox', 'radio'),
						'help'							=>	__('Checkbox recommended.', 'ws-form-constant-contact-v3'),
						'condition'					=>	array(

							array(

								'logic'			=>	'!=',
								'meta_key'		=>	'action_' . $this->id . '_list_id',
								'meta_value'	=>	''
							)
						)
					),

					// Field mapping
					'action_' . $this->id . '_field_mapping'	=> array(

						'label'						=>	__('Field Mapping', 'ws-form-constant-contact-v3'),
						'type'						=>	'repeater',
						'help'						=>	__('Map WS Form fields to Constant Contact fields.', 'ws-form-constant-contact-v3'),
						'meta_keys'					=>	array(

							'ws_form_field',
							'action_' . $this->id . '_list_fields'
						),
						'meta_keys_unique'			=>	array(

							'action_' . $this->id . '_list_fields'
						),
						'reload'					=>	array(

							'action_id'			=>	$this->id,
							'method'			=>	'list_fields_fetch',
							'list_id_meta_key'	=>	'action_' . $this->id . '_list_id'
						),
						'auto_map'					=>	true,
						'condition'					=>	array(

							array(

								'logic'			=>	'!=',
								'meta_key'		=>	'action_' . $this->id . '_list_id',
								'meta_value'	=>	''
							)
						)
					),

					// Custom mapping
					'action_' . $this->id . '_custom_mapping'	=> array(

						'label'						=>	__('Custom Mapping', 'ws-form-constant-contact-v3'),
						'type'						=>	'repeater',
						'help'						=>	__('Map values or variables to Zendesk standard and custom ticket fields.', 'ws-form-constant-contact-v3'),
						'meta_keys'					=>	array(

							'action_' . $this->id . '_custom_mapping_value',
							'action_' . $this->id . '_list_fields'
						),
						'meta_keys_unique'			=>	array(

							'action_' . $this->id . '_list_fields'
						),
						'reload'					=>	array(

							'action_id'			=>	$this->id,
							'method'			=>	'list_fields_fetch',
							'list_id_meta_key'	=>	'action_' . $this->id . '_list_id'
						),
						'condition'					=>	array(

							array(

								'logic'			=>	'!=',
								'meta_key'		=>	'action_' . $this->id . '_list_id',
								'meta_value'	=>	''
							)
						)
					),

					// Tag mapping
					'action_' . $this->id . '_tag_mapping'	=> array(

						'label'						=>	__('Tag Mapping', 'ws-form-constant-contact-v3'),
						'type'						=>	'repeater',
						'help'						=>	__('Map WS Form fields to AWeber tags.', 'ws-form-constant-contact-v3'),
						'meta_keys'					=>	array(

							'ws_form_field_choice'
						),
						'meta_keys_unique'			=>	array(

							'ws_form_field_choice'
						),
						'reload'					=>	array(

							'action_id'			=>	$this->id,
							'method'			=>	'list_fields_fetch',
							'list_id_meta_key'	=>	'action_' . $this->id . '_list_id'
						),
						'condition'					=>	array(

							array(

								'logic'			=>	'!=',
								'meta_key'		=>	'action_' . $this->id . '_list_id',
								'meta_value'	=>	''
							)
						)
					),

					// List fields
					'action_' . $this->id . '_list_fields'	=> array(

						'label'							=>	__('Constant Contact Field', 'ws-form-constant-contact-v3'),
						'type'							=>	'select',
						'options'						=>	'action_api_populate',
						'options_blank'					=>	__('Select...', 'ws-form-constant-contact-v3'),
						'options_action_id'				=>	$this->id,
						'options_list_id_meta_key'		=>	'action_' . $this->id . '_list_id',
						'options_action_api_populate'	=>	'list_fields'
					),

					// Custom mapping value
					'action_' . $this->id . '_custom_mapping_value'	=> array(

						'label'							=>	__('Value', 'ws-form-constant-contact-v3'),
						'type'							=>	'text'
					),

					// Duplicates
					'action_' . $this->id . '_duplicates'	=> array(

						'label'							=>	__('If Duplicate Found', 'ws-form-constant-contact-v3'),
						'type'							=>	'select',
						'options'						=>	array(

							array('value' => '', 'text' => __('Do nothing', 'ws-form')),
							array('value' => 'update', 'text' => __('Update', 'ws-form')),
							array('value' => 'halt', 'text' => __('Halt', 'ws-form')),
						),
						'default'						=>	'',
						'help'							=>	__('Choose what to do if a duplicate email address is found in your contacts.', 'ws-form-constant-contact-v3'),
						'condition'					=>	array(

							array(

								'logic'			=>	'!=',
								'meta_key'		=>	'action_' . $this->id . '_list_id',
								'meta_value'	=>	''
							)
						)
					),

					// Duplicates - Merge lists
					'action_' . $this->id . '_duplicates_merge_lists'	=> array(

						'label'							=>	__('Merge Lists', 'ws-form-constant-contact-v3'),
						'type'							=>	'checkbox',
						'default'						=>	'',
						'help'							=>	__('If a contact is updated, merge the lists they are assigned to.', 'ws-form-constant-contact-v3'),
						'condition'					=>	array(

							array(

								'logic'			=>	'!=',
								'meta_key'		=>	'action_' . $this->id . '_list_id',
								'meta_value'	=>	''
							),

							array(

								'logic'			=>	'==',
								'meta_key'		=>	'action_' . $this->id . '_duplicates',
								'meta_value'	=>	'update'
							)
						)
					),

					// Duplicates - Merge tags
					'action_' . $this->id . '_duplicates_merge_tags'	=> array(

						'label'							=>	__('Merge Tags', 'ws-form-constant-contact-v3'),
						'type'							=>	'checkbox',
						'default'						=>	'',
						'help'							=>	__('If a contact is updated, merge the tags they are assigned to.', 'ws-form-constant-contact-v3'),
						'condition'					=>	array(

							array(

								'logic'			=>	'!=',
								'meta_key'		=>	'action_' . $this->id . '_list_id',
								'meta_value'	=>	''
							),

							array(

								'logic'			=>	'==',
								'meta_key'		=>	'action_' . $this->id . '_duplicates',
								'meta_value'	=>	'update'
							)
						)
					)
				);

				// Merge
				$meta_keys = array_merge($meta_keys, $config_meta_keys);

				return $meta_keys;
			}

			// Plug-in options for this action
			public function config_options($options) {

				$options['action_' . $this->id] = array(

					'label'		=>	$this->label,
					'fields'	=>	array(

						'action_' . $this->id . '_license_version'	=>	array(

							'label'		=>	__('Add-on Version', 'ws-form-constant-contact-v3'),
							'type'		=>	'static'
						),

						'action_' . $this->id . '_license_key'	=>	array(

							'label'		=>	__('Add-on License Key', 'ws-form-constant-contact-v3'),
							'type'		=>	'text',
							'help'		=>	__('Enter your Constant Contact add-on for WS Form PRO license key here.', 'ws-form-constant-contact-v3'),
							'button'	=>	'license_action_' . $this->id,
							'action'	=>	$this->id
						),

						'action_' . $this->id . '_license_status'	=>	array(

							'label'		=>	__('Add-on License Status', 'ws-form-constant-contact-v3'),
							'type'		=>	'static'
						),

						'action_' . $this->id . '_status'	=>	array(

							'label'			=>	__('Constant Contact Status', 'ws-form-constant-contact-v3'),
							'type'			=>	'static',
							'help'			=>	$this->configured ? '' : __("Click the 'Connect' button to connect WS Form to your Constant Contact account, or <a href=\"https://www.constantcontact.com/signup.jsp?pn=westguard\" target=\"blank\">create a new account</a>.", 'ws-form-constant-contact-v3'),
							'button'		=>	'oauth_action_' . $this->id,
						)
					)
				);

				return $options;
			}

			// Convert action field type to WS Form pattern
			public function action_field_to_ws_form_pattern($type, $options = false) {

				switch($type) {

					case 'zip' : return '(\d{5}([\-]\d{4})?)';

					default :

						return false;
				}
			}

			// Convert action field type to WS Form pattern
			public function action_field_to_ws_form_placeholder($type, $options = false) {

				switch($type) {

					case 'zip' : return '#####';

					default :

						return false;
				}
			}

			// Convert action field type to WS Form field type
			public function action_field_type_to_ws_form_field_type($field) {

				// Set type by type
				$type = parent::get_object_value($field, 'type');
				switch($type) {

					case 'string' :		return 'text';
					case 'date' :		return 'datetime';

					default :

						return 'text';
				}
			}

			// Load config for this action
			public function load_config($config = array()) {

				$this->list_id = parent::get_config($config, 'action_' . $this->id . '_list_id');
				$this->list_memberships = parent::get_config($config, 'action_' . $this->id . '_list_memberships', array());
				if(!is_array($this->list_memberships)) { $this->list_memberships = array(); }
				$this->opt_in_field = parent::get_config($config, 'action_' . $this->id . '_opt_in_field');
				$this->field_mapping = parent::get_config($config, 'action_' . $this->id . '_field_mapping', array());
				if(!is_array($this->field_mapping)) { $this->field_mapping = array(); }
				$this->custom_mapping = parent::get_config($config, 'action_' . $this->id . '_custom_mapping', array());
				if(!is_array($this->custom_mapping)) { $this->custom_mapping = array(); }
				$this->tag_mapping = parent::get_config($config, 'action_' . $this->id . '_tag_mapping', array());
				if(!is_array($this->tag_mapping)) { $this->tag_mapping = array(); }
				$this->duplicates = parent::get_config($config, 'action_' . $this->id . '_duplicates');
				$this->duplicates_merge_lists = (parent::get_config($config, 'action_' . $this->id . '_duplicates_merge_lists') == 'on');
				$this->duplicates_merge_tags = (parent::get_config($config, 'action_' . $this->id . '_duplicates_merge_tags') == 'on');
			}

			// Load config at plugin level
			public function load_config_plugin() {

				$this->configured = true;

				$this->api_access_token  		= WS_Form_Common::option_get('action_' . $this->id . '_api_access_token', '');
				$this->api_access_token_expiry  = WS_Form_Common::option_get('action_' . $this->id . '_api_access_token_expiry', '');
				$this->api_refresh_token  		= WS_Form_Common::option_get('action_' . $this->id . '_api_refresh_token', '');

				// Build endpoint using API key
				if(!empty($this->api_access_token )) {

					// Set endpoint URL
					$this->api_endpoint = 'https://api.cc.email';

				} else {

					$this->api_endpoint = false;
					$this->configured = false;
				}

				return $this->configured;
			}

			// Build REST API endpoints
			public function rest_api_init() {

				// API routes - get_* (Use cache)
				register_rest_route(WS_FORM_RESTFUL_NAMESPACE, '/action/' . $this->id . '/lists/', array('methods' => 'GET', 'callback' => array($this, 'api_get_lists'), 'permission_callback' => function () { return WS_Form_Common::can_user('create_form'); }));
				register_rest_route(WS_FORM_RESTFUL_NAMESPACE, '/action/' . $this->id . '/list/(?P<list_id>[a-zA-Z0-9\-]+)/', array('methods' => 'GET', 'callback' => array($this, 'api_get_list'), 'permission_callback' => function () { return WS_Form_Common::can_user('create_form'); }));
				register_rest_route(WS_FORM_RESTFUL_NAMESPACE, '/action/' . $this->id . '/list/(?P<list_id>[a-zA-Z0-9\-]+)/fields/', array('methods' => 'GET', 'callback' => array($this, 'api_get_list_fields'), 'permission_callback' => function () { return WS_Form_Common::can_user('create_form'); }));

				// API routes - fetch_* (Pull from API and update cache)
				register_rest_route(WS_FORM_RESTFUL_NAMESPACE, '/action/' . $this->id . '/lists/fetch/', array('methods' => 'GET', 'callback' => array($this, 'api_fetch_lists'), 'permission_callback' => function () { return WS_Form_Common::can_user('create_form'); }));
				register_rest_route(WS_FORM_RESTFUL_NAMESPACE, '/action/' . $this->id . '/list/(?P<list_id>[a-zA-Z0-9\-]+)/fetch/', array('methods' => 'GET', 'callback' => array($this, 'api_fetch_list'), 'permission_callback' => function () { return WS_Form_Common::can_user('create_form'); }));
				register_rest_route(WS_FORM_RESTFUL_NAMESPACE, '/action/' . $this->id . '/list/(?P<list_id>[a-zA-Z0-9\-]+)/fields/fetch/', array('methods' => 'GET', 'callback' => array($this, 'api_fetch_list_fields'), 'permission_callback' => function () { return WS_Form_Common::can_user('create_form'); }));
			}

			// API endpoint - Lists
			public function api_get_lists() {

				// Get lists
				$lists = self::get_lists();

				// Process response
				self::api_response($lists);
			}

			// API endpoint - List
			public function api_get_list($parameters) {

				// Get lists
				$this->list_id = WS_Form_Common::get_query_var('list_id', false, $parameters);
				$list = self::get_list();

				// Process response
				self::api_response($list);
			}

			// API endpoint - List fields
			public function api_get_list_fields($parameters) {

				// Get lists
				$this->list_id = WS_Form_Common::get_query_var('list_id', false, $parameters);
				$list_fields = self::get_list_fields();

				// Process response
				self::api_response($list_fields);
			}

			// API endpoint - Lists with fetch
			public function api_fetch_lists() {

				// Get lists
				$lists = self::get_lists(true);

				// Process response
				self::api_response($lists);
			}

			// API endpoint - List with fetch
			public function api_fetch_list($parameters) {

				// Get lists
				$this->list_id = WS_Form_Common::get_query_var('list_id', false, $parameters);
				$list = self::get_list(true);

				// Process response
				self::api_response($list);
			}

			// API endpoint - List fields with fetch
			public function api_fetch_list_fields($parameters) {

				// Get lists
				$this->list_id = WS_Form_Common::get_query_var('list_id', false, $parameters);
				$list_fields = self::get_list_fields(true);

				// Process response
				self::api_response($list_fields);
			}
		
			// Get API headers
			public function get_api_headers() {

				$headers = array('Authorization' => 'Bearer ' . $this->api_access_token);

				return $headers;
			}

			// SVG Logo - Color (Used for the 'Add Form' page)
			public function get_svg_logo_color($list_id = false) {

				$svg_logo = '<g id="logo" transform="translate(40.000000, 61.000000)"><path fill="#1856ED" d="M30.1 64C12.5 64 .5 49.6.5 34.4.5 18.8 12.7 5.5 29 4.8c.6 0 1.2.5 1.2 1.1v4.8c0 .6-.5 1.1-1.1 1.1-12 .6-21.6 10.4-21.6 22.6C7.5 46.3 16.9 57 30.1 57 42.9 57 52 46.8 52.6 35.4c0-.6.5-1.1 1.1-1.1h4.8c.6 0 1.1.5 1.1 1.2C59.1 50.6 46.9 64 30.1 64z"/><path fill="#1856ED" d="M30.1 48.3c-7.9 0-13.9-6.2-13.9-13.9 0-7.2 5.5-13.2 12.7-13.8.7 0 1.2.5 1.2 1.1v4.8c0 .5-.4 1.1-1 1.1-3.5.5-6 3.4-6 6.8 0 3.8 2.9 6.9 6.9 6.9 3.3 0 6.3-2.5 6.8-6 .1-.5.6-1 1.1-1h4.8c.7 0 1.2.5 1.1 1.2-.5 7.1-6.4 12.8-13.7 12.8z"/><path fill="#FF9E1A" d="M57.5 28.5C57 17 47.8 7.5 36 7c-.6 0-1.1-.5-1.1-1.1V1.1c0-.6.5-1.1 1.1-1.1 15.4.6 27.9 12.9 28.5 28.5 0 .6-.5 1.2-1.1 1.2h-4.8c-.5-.1-1.1-.5-1.1-1.2zM36.2 22.8c-.7-.1-1.2-.7-1.2-1.3v-4.6c0-.7.5-1.2 1.2-1.1 6.8.6 12.1 6 12.7 12.6 0 .7-.5 1.2-1.1 1.2h-5c-.5 0-.8-.3-.9-.8-.4-2.8-2.6-5.4-5.7-6z"/></g>';

				return $svg_logo;
			}

			// Number meta
			public function get_meta_number_select($count) {

				// Build birthday meta data
				$meta_key = 'data_grid_select';

				// Get base meta
				$meta_keys = WS_Form_Config::get_meta_keys();
				if(!isset($meta_keys[$meta_key])) { return false; }
				if(!isset($meta_keys[$meta_key]['default'])) { return false; }
				$meta_value = $meta_keys[$meta_key]['default'];

				// Months
				$rows = array();

				for($index = 1; $index <= $count; $index++) {

					$rows[] = array(

						'id'		=> $index,
						'data'		=> array($index)
					);
				}

				$meta_value['groups'][0]['rows'] = $rows;

				return array($meta_key => $meta_value);
			}

			// Country meta
			public function get_meta_country() {

				$countries = json_decode('[{"n":"Afghanistan","c":"af"},{"n":"\u00c5land Islands","c":"ax"},{"n":"Albania","c":"al"},{"n":"Algeria","c":"dz"},{"n":"American Samoa","c":"as"},{"n":"Andorra","c":"ad"},{"n":"Angola","c":"ao"},{"n":"Anguilla","c":"ai"},{"n":"Antarctica","c":"aq"},{"n":"Antigua and Barbuda","c":"ag"},{"n":"Argentina","c":"ar"},{"n":"Armenia","c":"am"},{"n":"Aruba","c":"aw"},{"n":"Australia","c":"au"},{"n":"Austria","c":"at"},{"n":"Azerbaijan","c":"az"},{"n":"Bahamas","c":"bs"},{"n":"Bahrain","c":"bh"},{"n":"Bangladesh","c":"bd"},{"n":"Barbados","c":"bb"},{"n":"Belarus","c":"by"},{"n":"Belgium","c":"be"},{"n":"Belize","c":"bz"},{"n":"Benin","c":"bj"},{"n":"Bermuda","c":"bm"},{"n":"Bhutan","c":"bt"},{"n":"Bolivia (Plurinational State of)","c":"bo"},{"n":"Bonaire, Sint Eustatius and Saba","c":"bq"},{"n":"Bosnia and Herzegovina","c":"ba"},{"n":"Botswana","c":"bw"},{"n":"Bouvet Island","c":"bv"},{"n":"Brazil","c":"br"},{"n":"British Indian Ocean Territory","c":"io"},{"n":"Brunei Darussalam","c":"bn"},{"n":"Bulgaria","c":"bg"},{"n":"Burkina Faso","c":"bf"},{"n":"Burundi","c":"bi"},{"n":"Cabo Verde","c":"cv"},{"n":"Cambodia","c":"kh"},{"n":"Cameroon","c":"cm"},{"n":"Canada","c":"ca"},{"n":"Cayman Islands","c":"ky"},{"n":"Central African Republic","c":"cf"},{"n":"Chad","c":"td"},{"n":"Chile","c":"cl"},{"n":"China","c":"cn"},{"n":"Christmas Island","c":"cx"},{"n":"Cocos (Keeling) Islands","c":"cc"},{"n":"Colombia","c":"co"},{"n":"Comoros","c":"km"},{"n":"Congo","c":"cg"},{"n":"Congo (Democratic Republic of the)","c":"cd"},{"n":"Cook Islands","c":"ck"},{"n":"Costa Rica","c":"cr"},{"n":"C\u00f4te d\'Ivoire","c":"ci"},{"n":"Croatia","c":"hr"},{"n":"Cuba","c":"cu"},{"n":"Cura\u00e7ao","c":"cw"},{"n":"Cyprus","c":"cy"},{"n":"Czechia","c":"cz"},{"n":"Denmark","c":"dk"},{"n":"Djibouti","c":"dj"},{"n":"Dominica","c":"dm"},{"n":"Dominican Republic","c":"do"},{"n":"Ecuador","c":"ec"},{"n":"Egypt","c":"eg"},{"n":"El Salvador","c":"sv"},{"n":"Equatorial Guinea","c":"gq"},{"n":"Eritrea","c":"er"},{"n":"Estonia","c":"ee"},{"n":"Ethiopia","c":"et"},{"n":"Falkland Islands (Malvinas)","c":"fk"},{"n":"Faroe Islands","c":"fo"},{"n":"Fiji","c":"fj"},{"n":"Finland","c":"fi"},{"n":"France","c":"fr"},{"n":"French Guiana","c":"gf"},{"n":"French Polynesia","c":"pf"},{"n":"French Southern Territories","c":"tf"},{"n":"Gabon","c":"ga"},{"n":"Gambia","c":"gm"},{"n":"Georgia","c":"ge"},{"n":"Germany","c":"de"},{"n":"Ghana","c":"gh"},{"n":"Gibraltar","c":"gi"},{"n":"Greece","c":"gr"},{"n":"Greenland","c":"gl"},{"n":"Grenada","c":"gd"},{"n":"Guadeloupe","c":"gp"},{"n":"Guam","c":"gu"},{"n":"Guatemala","c":"gt"},{"n":"Guernsey","c":"gg"},{"n":"Guinea","c":"gn"},{"n":"Guinea-Bissau","c":"gw"},{"n":"Guyana","c":"gy"},{"n":"Haiti","c":"ht"},{"n":"Heard Island and McDonald Islands","c":"hm"},{"n":"Holy See","c":"va"},{"n":"Honduras","c":"hn"},{"n":"Hong Kong","c":"hk"},{"n":"Hungary","c":"hu"},{"n":"Iceland","c":"is"},{"n":"India","c":"in"},{"n":"Indonesia","c":"id"},{"n":"Iran (Islamic Republic of)","c":"ir"},{"n":"Iraq","c":"iq"},{"n":"Ireland","c":"ie"},{"n":"Isle of Man","c":"im"},{"n":"Israel","c":"il"},{"n":"Italy","c":"it"},{"n":"Jamaica","c":"jm"},{"n":"Japan","c":"jp"},{"n":"Jersey","c":"je"},{"n":"Jordan","c":"jo"},{"n":"Kazakhstan","c":"kz"},{"n":"Kenya","c":"ke"},{"n":"Kiribati","c":"ki"},{"n":"Korea (Democratic People\'s Republic of)","c":"kp"},{"n":"Korea (Republic of)","c":"kr"},{"n":"Kuwait","c":"kw"},{"n":"Kyrgyzstan","c":"kg"},{"n":"Lao People\'s Democratic Republic","c":"la"},{"n":"Latvia","c":"lv"},{"n":"Lebanon","c":"lb"},{"n":"Lesotho","c":"ls"},{"n":"Liberia","c":"lr"},{"n":"Libya","c":"ly"},{"n":"Liechtenstein","c":"li"},{"n":"Lithuania","c":"lt"},{"n":"Luxembourg","c":"lu"},{"n":"Macao","c":"mo"},{"n":"Macedonia (the former Yugoslav Republic of)","c":"mk"},{"n":"Madagascar","c":"mg"},{"n":"Malawi","c":"mw"},{"n":"Malaysia","c":"my"},{"n":"Maldives","c":"mv"},{"n":"Mali","c":"ml"},{"n":"Malta","c":"mt"},{"n":"Marshall Islands","c":"mh"},{"n":"Martinique","c":"mq"},{"n":"Mauritania","c":"mr"},{"n":"Mauritius","c":"mu"},{"n":"Mayotte","c":"yt"},{"n":"Mexico","c":"mx"},{"n":"Micronesia (Federated States of)","c":"fm"},{"n":"Moldova (Republic of)","c":"md"},{"n":"Monaco","c":"mc"},{"n":"Mongolia","c":"mn"},{"n":"Montenegro","c":"me"},{"n":"Montserrat","c":"ms"},{"n":"Morocco","c":"ma"},{"n":"Mozambique","c":"mz"},{"n":"Myanmar","c":"mm"},{"n":"Namibia","c":"na"},{"n":"Nauru","c":"nr"},{"n":"Nepal","c":"np"},{"n":"Netherlands","c":"nl"},{"n":"New Caledonia","c":"nc"},{"n":"New Zealand","c":"nz"},{"n":"Nicaragua","c":"ni"},{"n":"Niger","c":"ne"},{"n":"Nigeria","c":"ng"},{"n":"Niue","c":"nu"},{"n":"Norfolk Island","c":"nf"},{"n":"Northern Mariana Islands","c":"mp"},{"n":"Norway","c":"no"},{"n":"Oman","c":"om"},{"n":"Pakistan","c":"pk"},{"n":"Palau","c":"pw"},{"n":"Palestine, State of","c":"ps"},{"n":"Panama","c":"pa"},{"n":"Papua New Guinea","c":"pg"},{"n":"Paraguay","c":"py"},{"n":"Peru","c":"pe"},{"n":"Philippines","c":"ph"},{"n":"Pitcairn","c":"pn"},{"n":"Poland","c":"pl"},{"n":"Portugal","c":"pt"},{"n":"Puerto Rico","c":"pr"},{"n":"Qatar","c":"qa"},{"n":"R\u00e9union","c":"re"},{"n":"Romania","c":"ro"},{"n":"Russian Federation","c":"ru"},{"n":"Rwanda","c":"rw"},{"n":"Saint Barth\u00e9lemy","c":"bl"},{"n":"Saint Helena, Ascension and Tristan da Cunha","c":"sh"},{"n":"Saint Kitts and Nevis","c":"kn"},{"n":"Saint Lucia","c":"lc"},{"n":"Saint Martin (French part)","c":"mf"},{"n":"Saint Pierre and Miquelon","c":"pm"},{"n":"Saint Vincent and the Grenadines","c":"vc"},{"n":"Samoa","c":"ws"},{"n":"San Marino","c":"sm"},{"n":"Sao Tome and Principe","c":"st"},{"n":"Saudi Arabia","c":"sa"},{"n":"Senegal","c":"sn"},{"n":"Serbia","c":"rs"},{"n":"Seychelles","c":"sc"},{"n":"Sierra Leone","c":"sl"},{"n":"Singapore","c":"sg"},{"n":"Sint Maarten (Dutch part)","c":"sx"},{"n":"Slovakia","c":"sk"},{"n":"Slovenia","c":"si"},{"n":"Solomon Islands","c":"sb"},{"n":"Somalia","c":"so"},{"n":"South Africa","c":"za"},{"n":"South Georgia and the South Sandwich Islands","c":"gs"},{"n":"South Sudan","c":"ss"},{"n":"Spain","c":"es"},{"n":"Sri Lanka","c":"lk"},{"n":"Sudan","c":"sd"},{"n":"Suriname","c":"sr"},{"n":"Svalbard and Jan Mayen","c":"sj"},{"n":"Swaziland","c":"sz"},{"n":"Sweden","c":"se"},{"n":"Switzerland","c":"ch"},{"n":"Syrian Arab Republic","c":"sy"},{"n":"Taiwan, Province of China","c":"tw"},{"n":"Tajikistan","c":"tj"},{"n":"Tanzania, United Republic of","c":"tz"},{"n":"Thailand","c":"th"},{"n":"Timor-Leste","c":"tl"},{"n":"Togo","c":"tg"},{"n":"Tokelau","c":"tk"},{"n":"Tonga","c":"to"},{"n":"Trinidad and Tobago","c":"tt"},{"n":"Tunisia","c":"tn"},{"n":"Turkey","c":"tr"},{"n":"Turkmenistan","c":"tm"},{"n":"Turks and Caicos Islands","c":"tc"},{"n":"Tuvalu","c":"tv"},{"n":"Uganda","c":"ug"},{"n":"Ukraine","c":"ua"},{"n":"United Arab Emirates","c":"ae"},{"n":"United Kingdom of Great Britain and Northern Ireland","c":"gb"},{"n":"United States of America","c":"us"},{"n":"United States Minor Outlying Islands","c":"um"},{"n":"Uruguay","c":"uy"},{"n":"Uzbekistan","c":"uz"},{"n":"Vanuatu","c":"vu"},{"n":"Venezuela (Bolivarian Republic of)","c":"ve"},{"n":"Viet Nam","c":"vn"},{"n":"Virgin Islands (British)","c":"vg"},{"n":"Virgin Islands (U.S.)","c":"vi"},{"n":"Wallis and Futuna","c":"wf"},{"n":"Western Sahara","c":"eh"},{"n":"Yemen","c":"ye"},{"n":"Zambia","c":"zm"},{"n":"Zimbabwe","c":"zw"}]');

				$group_name = __('Country', 'ws-form');

				$columns = array(

					array('id' => 0, 'label' => __('Label', 'ws-form')),
				);

				$rows = array();
				$row_index = 1;
				foreach($countries as $country) {

					$rows[] = array(

						'id'		=> $row_index++,
						'default'	=> (($country->c == 'us') ? 'on' : ''),
						'data'		=> array($country->n)
					);
				}

				return array('data_grid_select' => WS_Form_Common::build_data_grid_meta('data_grid_select', $group_name, $columns, $rows));
			}
		}
	});