'ajax_create_link',
'useclick_get_links' => 'ajax_get_links',
'useclick_update_link' => 'ajax_update_link',
'useclick_delete_link' => 'ajax_delete_link',
'useclick_get_domains' => 'ajax_get_domains',
'useclick_get_account' => 'ajax_get_account',
'useclick_verify_api_key' => 'ajax_verify_api_key',
);
foreach ( $ajax as $action => $method ) {
add_action( 'wp_ajax_' . $action, array( __CLASS__, $method ) );
}
}
/**
* Capability required for the plugin's screens and link management.
*
* @return string
*/
public static function capability() {
/**
* Filters the capability needed to use the UseClick.io admin screens.
*
* @param string $capability Default 'manage_options'.
*/
return (string) apply_filters( 'useclick_capability', 'manage_options' );
}
/**
* Whether an API key is saved.
*
* @return bool
*/
public static function has_api_key() {
return '' !== useclick_get_api_key();
}
/**
* Admin menu.
*/
public static function add_menu() {
$cap = self::capability();
self::$page_hooks[] = add_menu_page(
__( 'UseClick.io', 'useclick' ),
__( 'UseClick.io', 'useclick' ),
$cap,
'useclick',
array( __CLASS__, 'render_dashboard_page' ),
self::menu_icon(),
30
);
self::$page_hooks[] = add_submenu_page( 'useclick', __( 'Create short link', 'useclick' ), __( 'Create link', 'useclick' ), $cap, 'useclick', array( __CLASS__, 'render_dashboard_page' ) );
self::$page_hooks[] = add_submenu_page( 'useclick', __( 'Short links', 'useclick' ), __( 'Short links', 'useclick' ), $cap, 'useclick-links', array( __CLASS__, 'render_links_page' ) );
self::$page_hooks[] = add_submenu_page( 'useclick', __( 'Website analytics', 'useclick' ), __( 'Website analytics', 'useclick' ), $cap, 'useclick-analytics', array( 'UseClick_Analytics', 'render_admin_page' ) );
self::$page_hooks[] = add_submenu_page( 'useclick', __( 'UseClick.io settings', 'useclick' ), __( 'Settings', 'useclick' ), $cap, 'useclick-settings', array( 'UseClick_Settings', 'render_page' ) );
foreach ( self::$page_hooks as $hook ) {
add_action( 'load-' . $hook, array( __CLASS__, 'add_help_tabs' ) );
}
}
/**
* The "Help" tab (top right of every UseClick.io screen): how to get an API key, with links to the guide.
*/
public static function add_help_tabs() {
$screen = get_current_screen();
if ( ! $screen ) {
return;
}
$new_tab = ' ' . esc_html__( '(opens in a new tab)', 'useclick' ) . ' ';
$screen->add_help_tab(
array(
'id' => 'useclick-api-key',
'title' => __( 'Connect your account', 'useclick' ),
'content' =>
'
' . esc_html__( 'Easiest: on UseClick.io > Settings, click Connect with useClick.io, sign in (or create a free account) and confirm. The plugin gets its own API key; nothing to copy.', 'useclick' ) . '
' .
'' . esc_html__( 'To create and paste a key yourself instead:', 'useclick' ) . '
' .
'' .
'' . sprintf(
/* translators: 1: sign-in link, 2: sign-up link */
esc_html__( '%1$s at useclick.io, or %2$s.', 'useclick' ),
'' . esc_html__( 'Sign in', 'useclick' ) . $new_tab . ' ',
'' . esc_html__( 'create a free account', 'useclick' ) . $new_tab . ' '
) . ' ' .
'' . esc_html__( 'Open Account > API keys in the dashboard and click "+ Create API key".', 'useclick' ) . ' ' .
'' . esc_html__( 'Name the key after this site (for example "WordPress: example.com") and click "Create key".', 'useclick' ) . ' ' .
'' . esc_html__( 'Copy the key (it starts with uc_live_). It is shown only once.', 'useclick' ) . ' ' .
'' . esc_html__( 'In WordPress, open UseClick.io > Settings > Or paste an API key, paste it and click Connect.', 'useclick' ) . ' ' .
' ',
)
);
$screen->add_help_tab(
array(
'id' => 'useclick-troubleshooting',
'title' => __( 'Troubleshooting', 'useclick' ),
'content' =>
'' . esc_html__( 'The key is not accepted', 'useclick' ) . ' ' . esc_html__( 'Copy it again without spaces, and check under Account > API keys in UseClick.io that it is still Active. A lost key cannot be shown again: create a new one and revoke the old one.', 'useclick' ) . '
' .
'' . esc_html__( 'Could not reach UseClick.io', 'useclick' ) . ' ' . esc_html__( 'A firewall or security plugin is blocking outgoing requests. Allow HTTPS requests to useclick.io, or ask your host.', 'useclick' ) . '
' .
'' . esc_html__( 'Rate limit reached', 'useclick' ) . ' ' . esc_html__( 'Wait a minute. After upgrading your plan, create a new key to get the higher limit.', 'useclick' ) . '
',
)
);
$screen->set_help_sidebar(
'' . esc_html__( 'More help', 'useclick' ) . '
' .
'' . esc_html__( 'WordPress plugin guide', 'useclick' ) . $new_tab . '
' .
'' . esc_html__( 'API keys guide', 'useclick' ) . $new_tab . '
' .
'' . esc_html__( 'Plans and limits', 'useclick' ) . $new_tab . '
'
);
}
/**
* Monochrome SVG menu icon (WordPress recolours it to match the admin scheme).
*
* @return string
*/
private static function menu_icon() {
$svg = '' . self::logo_paths( 'black' ) . ' ';
// base64 is required by the WordPress menu icon API for data URIs.
return 'data:image/svg+xml;base64,' . base64_encode( $svg ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
}
/**
* Paths of the UseClick.io mark (viewBox 0 0 151 150), all filled so WordPress can recolour the menu icon.
*
* @param string $color Fill colour.
* @return string
*/
public static function logo_paths( $color ) {
$fill = esc_attr( $color );
return ' '
. ' '
. ' '
. ' ';
}
/**
* "Settings" link on the Plugins screen.
*
* @param array $links Existing links.
* @return array
*/
public static function action_links( $links ) {
$url = admin_url( 'admin.php?page=useclick-settings' );
array_unshift( $links, '' . esc_html__( 'Settings', 'useclick' ) . ' ' );
return $links;
}
/**
* Load CSS/JS on the plugin's own screens.
*
* @param string $hook Current admin page hook suffix.
*/
public static function enqueue_assets( $hook ) {
if ( ! in_array( $hook, self::$page_hooks, true ) ) {
return;
}
self::register_assets();
wp_enqueue_style( 'useclick-admin' );
wp_enqueue_script( 'useclick-admin' );
}
/**
* Register the shared stylesheet and script (also used on post screens).
*/
public static function register_assets() {
if ( wp_script_is( 'useclick-admin', 'registered' ) ) {
return;
}
wp_register_style( 'useclick-admin', USECLICK_PLUGIN_URL . 'assets/css/admin.css', array(), USECLICK_VERSION );
wp_register_script( 'useclick-admin', USECLICK_PLUGIN_URL . 'assets/js/admin.js', array( 'jquery', 'wp-a11y' ), USECLICK_VERSION, true );
$config = array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'useclick_nonce' ),
'appUrl' => USECLICK_APP_URL,
'pricingUrl' => USECLICK_APP_URL . '/pricing',
'dashboardUrl' => USECLICK_APP_URL . '/dashboard',
/** This filter is documented in includes/class-useclick-admin.php (short_domain()). */
'shortDomain' => self::short_domain(),
'planLabels' => array(
'free' => self::plan_label( 'free' ),
'starter' => self::plan_label( 'starter' ),
'growth' => self::plan_label( 'growth' ),
'pro' => self::plan_label( 'pro' ),
'business' => self::plan_label( 'business' ),
),
'i18n' => self::js_strings(),
);
wp_add_inline_script( 'useclick-admin', 'window.useclickData = ' . wp_json_encode( $config ) . ';', 'before' );
}
/**
* The built-in short domain, shown when the domain list cannot be loaded.
*
* @return string
*/
public static function short_domain() {
/**
* Filters the built-in short domain shown as a fallback in the domain picker.
*
* @param string $domain Default uclk.me.
*/
return (string) apply_filters( 'useclick_short_domain', 'uclk.me' );
}
/**
* Translated strings for admin.js.
*
* @return array
*/
private static function js_strings() {
return array(
'creating' => __( 'Creating...', 'useclick' ),
'createLink' => __( 'Create short link', 'useclick' ),
'linkCreated' => __( 'Short link created', 'useclick' ),
'copy' => __( 'Copy', 'useclick' ),
'copied' => __( 'Copied', 'useclick' ),
'viewAnalytics' => __( 'View analytics', 'useclick' ),
'analytics' => __( 'Analytics', 'useclick' ),
'invalidUrl' => __( 'Enter a full destination URL that starts with http:// or https://.', 'useclick' ),
'unknownError' => __( 'Something went wrong. Please try again.', 'useclick' ),
'networkError' => __( 'The request to WordPress failed. Check your connection and try again.', 'useclick' ),
'loadingLinks' => __( 'Loading links...', 'useclick' ),
'noLinks' => __( 'No links yet. Create your first short link on the Create link screen.', 'useclick' ),
'noMatches' => __( 'No links match your search.', 'useclick' ),
'colShortLink' => __( 'Short link', 'useclick' ),
'colTargetUrl' => __( 'Destination', 'useclick' ),
'colCampaign' => __( 'Campaign', 'useclick' ),
'colClicks' => __( 'Clicks', 'useclick' ),
'colCreated' => __( 'Created', 'useclick' ),
'colActions' => __( 'Actions', 'useclick' ),
'geoTargeted' => __( 'Geo-targeted', 'useclick' ),
'editLink' => __( 'Edit', 'useclick' ),
'deleteLink' => __( 'Delete', 'useclick' ),
/* translators: %s: short link slug */
'deleteConfirm' => __( 'Delete the short link "%s"? Visitors who open it will get an error page. This cannot be undone.', 'useclick' ),
'linkUpdated' => __( 'Link updated.', 'useclick' ),
'nothingChanged' => __( 'Nothing to update.', 'useclick' ),
'updating' => __( 'Saving...', 'useclick' ),
'updateLink' => __( 'Save changes', 'useclick' ),
'linkDeleted' => __( 'Link deleted.', 'useclick' ),
'copiedToClipboard' => __( 'Short link copied to the clipboard.', 'useclick' ),
'duplicateCountries' => __( 'Each country can only be used once. Remove the duplicate country.', 'useclick' ),
'verifying' => __( 'Checking...', 'useclick' ),
'verifyKey' => __( 'Test key', 'useclick' ),
'enterKey' => __( 'Paste an API key first.', 'useclick' ),
/* translators: %s: plan name, e.g. Starter */
'keyValid' => __( 'Key accepted. Plan: %s.', 'useclick' ),
/* translators: 1: current page, 2: total pages */
'pageOf' => __( 'Page %1$s of %2$s', 'useclick' ),
/* translators: %s: number of links */
'items' => __( '%s links', 'useclick' ),
/* translators: 1: links used, 2: plan limit */
'linksUsage' => __( '%1$s of %2$s', 'useclick' ),
/* translators: %s: plan name, e.g. Starter */
'upgradeForFeature' => __( 'Available on the %s plan and above.', 'useclick' ),
'upgradeCta' => __( 'See plans', 'useclick' ),
'freeCantEditDest' => __( 'Changing the destination needs the Starter plan or above.', 'useclick' ),
'previous' => __( 'Previous page', 'useclick' ),
'next' => __( 'Next page', 'useclick' ),
/* translators: %s: plan name, e.g. Starter. Badge on features that need that plan or above. */
'planPlus' => __( '%s+', 'useclick' ),
);
}
/**
* Remember a status message for the current user's next view of one of our screens (after a
* form handler redirects), instead of passing it in the URL.
*
* @param string $page Screen slug, e.g. useclick-analytics.
* @param string $status Status key.
*/
public static function set_flash( $page, $status ) {
set_transient( 'useclick_flash_' . get_current_user_id(), $page . '|' . $status, 5 * MINUTE_IN_SECONDS );
}
/**
* The status message waiting for this screen (once), or ''.
*
* @param string $page Screen slug.
* @return string
*/
public static function take_flash( $page ) {
$key = 'useclick_flash_' . get_current_user_id();
$value = (string) get_transient( $key );
if ( 0 !== strpos( $value, $page . '|' ) ) {
return '';
}
delete_transient( $key );
return sanitize_key( substr( $value, strlen( $page ) + 1 ) );
}
/**
* Common guard for every AJAX endpoint: nonce and capability.
*
* @param string $capability Capability to require. Defaults to the plugin capability.
*/
public static function verify_ajax( $capability = '' ) {
check_ajax_referer( 'useclick_nonce', 'nonce' );
if ( ! current_user_can( '' !== $capability ? $capability : self::capability() ) ) {
wp_send_json_error( array( 'message' => __( 'You are not allowed to do this.', 'useclick' ) ), 403 );
}
}
/**
* Send a WP_Error to the browser with the details admin.js needs (upgrade link, required plan).
*
* @param WP_Error $error Error.
*/
public static function send_error( WP_Error $error ) {
$data = $error->get_error_data();
$payload = array(
'message' => $error->get_error_message(),
'code' => $error->get_error_code(),
);
if ( is_array( $data ) ) {
foreach ( array( 'required_plan', 'upgrade_url', 'feature', 'retry_after', 'status' ) as $key ) {
if ( isset( $data[ $key ] ) ) {
$payload[ $key ] = $data[ $key ];
}
}
}
wp_send_json_error( $payload );
}
/**
* Read a string from the POST body (unslashed, not yet sanitized).
*
* @param string $key Field name.
* @return string|null Null when absent.
*/
private static function post_value( $key ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- nonce checked in verify_ajax(); sanitized by the caller.
return isset( $_POST[ $key ] ) ? (string) wp_unslash( $_POST[ $key ] ) : null;
}
/**
* Sanitize link fields sent by the admin screens.
*
* Only fields present in $raw are returned, so an update sends only what changed.
* Empty optional values are dropped on create.
*
* @param array $raw Unslashed input.
* @param bool $is_create Creating (true) or updating (false).
* @return array|WP_Error
*/
public static function sanitize_link_fields( array $raw, $is_create ) {
$fields = array();
if ( isset( $raw['target_url'] ) ) {
$url = esc_url_raw( trim( $raw['target_url'] ), array( 'http', 'https' ) );
if ( '' === $url ) {
return new WP_Error( 'invalid_url', __( 'Enter a full destination URL that starts with http:// or https://.', 'useclick' ) );
}
$fields['target_url'] = $url;
} elseif ( $is_create ) {
return new WP_Error( 'invalid_url', __( 'Enter a destination URL.', 'useclick' ) );
}
if ( isset( $raw['slug'] ) && '' !== trim( $raw['slug'] ) ) {
$slug = trim( $raw['slug'] );
if ( ! preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $slug ) ) {
return new WP_Error( 'invalid_slug', __( 'The custom slug can only contain letters, numbers, hyphens and underscores (up to 64 characters).', 'useclick' ) );
}
$fields['slug'] = $slug;
}
if ( isset( $raw['branded_domain_id'] ) ) {
$domain_id = sanitize_text_field( $raw['branded_domain_id'] );
if ( '' !== $domain_id && 'default' !== $domain_id && preg_match( '/^[A-Za-z0-9-]{1,64}$/', $domain_id ) ) {
$fields['branded_domain_id'] = $domain_id;
}
}
$text_fields = array( 'campaign', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content' );
foreach ( $text_fields as $key ) {
if ( ! isset( $raw[ $key ] ) ) {
continue;
}
$value = sanitize_text_field( $raw[ $key ] );
if ( $is_create && '' === $value ) {
continue;
}
$fields[ $key ] = $value;
}
if ( isset( $raw['geo_targets'] ) && '' !== $raw['geo_targets'] ) {
$decoded = json_decode( $raw['geo_targets'], true );
if ( ! is_array( $decoded ) ) {
return new WP_Error( 'invalid_geo', __( 'The country targets could not be read. Remove them and add them again.', 'useclick' ) );
}
$countries = self::get_countries();
$targets = array();
foreach ( $decoded as $target ) {
$country = isset( $target['country'] ) ? strtoupper( sanitize_text_field( (string) $target['country'] ) ) : '';
$url = isset( $target['target_url'] ) ? esc_url_raw( trim( (string) $target['target_url'] ), array( 'http', 'https' ) ) : '';
if ( ! isset( $countries[ $country ] ) || '' === $url ) {
continue;
}
if ( isset( $targets[ $country ] ) ) {
return new WP_Error( 'duplicate_country', __( 'Each country can only be used once. Remove the duplicate country.', 'useclick' ) );
}
$targets[ $country ] = array(
'country' => $country,
'target_url' => $url,
);
}
if ( $targets ) {
$fields['geo_targets'] = array_values( $targets );
}
}
return $fields;
}
/**
* AJAX: create a link from the Create link screen.
*/
public static function ajax_create_link() {
self::verify_ajax();
$raw = array();
foreach ( array( 'target_url', 'slug', 'branded_domain_id', 'campaign', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'geo_targets' ) as $key ) {
$value = self::post_value( $key );
if ( null !== $value ) {
$raw[ $key ] = $value;
}
}
$fields = self::sanitize_link_fields( $raw, true );
if ( is_wp_error( $fields ) ) {
self::send_error( $fields );
}
$api = new UseClick_API();
$result = $api->create_link( $fields );
if ( is_wp_error( $result ) ) {
self::send_error( $result );
}
UseClick_API::flush_cache();
wp_send_json_success( $result );
}
/**
* AJAX: list links.
*/
public static function ajax_get_links() {
self::verify_ajax();
$page = max( 1, (int) self::post_value( 'paged' ) );
$per_page = self::post_value( 'per_page' );
$per_page = null === $per_page ? 25 : (int) $per_page;
$campaign = sanitize_text_field( (string) self::post_value( 'campaign' ) );
$api = new UseClick_API();
$result = $api->get_links( $page, $per_page, $campaign );
if ( is_wp_error( $result ) ) {
self::send_error( $result );
}
wp_send_json_success( $result );
}
/**
* AJAX: update a link (only the changed fields are sent).
*/
public static function ajax_update_link() {
self::verify_ajax();
$slug = (string) self::post_value( 'slug' );
if ( ! preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $slug ) ) {
self::send_error( new WP_Error( 'invalid_slug', __( 'Unknown link.', 'useclick' ) ) );
}
$raw = array();
foreach ( array( 'target_url', 'campaign', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content' ) as $key ) {
$value = self::post_value( $key );
if ( null !== $value ) {
$raw[ $key ] = $value;
}
}
$fields = self::sanitize_link_fields( $raw, false );
if ( is_wp_error( $fields ) ) {
self::send_error( $fields );
}
if ( empty( $fields ) ) {
wp_send_json_success( array( 'unchanged' => true ) );
}
$api = new UseClick_API();
$result = $api->update_link( $slug, $fields );
if ( is_wp_error( $result ) ) {
self::send_error( $result );
}
UseClick_API::flush_cache();
wp_send_json_success( $result );
}
/**
* AJAX: delete a link.
*/
public static function ajax_delete_link() {
self::verify_ajax();
$slug = (string) self::post_value( 'slug' );
if ( ! preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $slug ) ) {
self::send_error( new WP_Error( 'invalid_slug', __( 'Unknown link.', 'useclick' ) ) );
}
$api = new UseClick_API();
$result = $api->delete_link( $slug );
if ( is_wp_error( $result ) ) {
self::send_error( $result );
}
UseClick_Posts::forget_slug( $slug );
UseClick_API::flush_cache();
wp_send_json_success();
}
/**
* AJAX: short domains for the domain picker.
*/
public static function ajax_get_domains() {
self::verify_ajax();
$api = new UseClick_API();
$result = $api->get_domains();
if ( is_wp_error( $result ) ) {
self::send_error( $result );
}
wp_send_json_success( array( 'domains' => $result ) );
}
/**
* AJAX: account (plan, limits, feature flags) for the saved key.
*/
public static function ajax_get_account() {
self::verify_ajax();
$api = new UseClick_API();
$result = $api->get_account();
if ( is_wp_error( $result ) ) {
self::send_error( $result );
}
wp_send_json_success( $result );
}
/**
* AJAX: test a key typed on the Settings screen before saving it.
*/
public static function ajax_verify_api_key() {
self::verify_ajax();
$key = trim( sanitize_text_field( (string) self::post_value( 'api_key' ) ) );
if ( '' === $key ) {
self::send_error( new WP_Error( 'no_api_key', __( 'Paste an API key first.', 'useclick' ) ) );
}
$api = new UseClick_API( $key );
$result = $api->verify_key();
if ( is_wp_error( $result ) ) {
self::send_error( $result );
}
$result['plan_label'] = self::plan_label( isset( $result['plan']['plan_name'] ) ? $result['plan']['plan_name'] : 'free' );
wp_send_json_success( $result );
}
/**
* Human label for a plan id.
*
* @param string $plan Plan id (free, starter, growth, pro, business).
* @return string
*/
public static function plan_label( $plan ) {
$labels = array(
'free' => __( 'Free', 'useclick' ),
'starter' => __( 'Starter', 'useclick' ),
'growth' => __( 'Growth', 'useclick' ),
'pro' => __( 'Pro', 'useclick' ),
'business' => __( 'Business', 'useclick' ),
);
$plan = sanitize_key( (string) $plan );
return isset( $labels[ $plan ] ) ? $labels[ $plan ] : ucfirst( $plan );
}
/**
* Print the branded page header.
*
* @param string $title Page title.
*/
public static function render_header( $title ) {
$useclick_title = $title;
$useclick_account = null;
if ( self::has_api_key() ) {
$api = new UseClick_API();
$useclick_account = $api->get_account();
}
include USECLICK_PLUGIN_DIR . 'templates/partials/header.php';
}
/**
* Create link screen.
*/
public static function render_dashboard_page() {
include USECLICK_PLUGIN_DIR . 'templates/dashboard.php';
}
/**
* Links list screen.
*/
public static function render_links_page() {
include USECLICK_PLUGIN_DIR . 'templates/links.php';
}
/**
* Countries offered for geo-targeting (ISO 3166-1 alpha-2).
*
* @return array
*/
public static function get_countries() {
return array(
'US' => __( 'United States', 'useclick' ),
'GB' => __( 'United Kingdom', 'useclick' ),
'CA' => __( 'Canada', 'useclick' ),
'AU' => __( 'Australia', 'useclick' ),
'DE' => __( 'Germany', 'useclick' ),
'FR' => __( 'France', 'useclick' ),
'IT' => __( 'Italy', 'useclick' ),
'ES' => __( 'Spain', 'useclick' ),
'NL' => __( 'Netherlands', 'useclick' ),
'BE' => __( 'Belgium', 'useclick' ),
'CH' => __( 'Switzerland', 'useclick' ),
'AT' => __( 'Austria', 'useclick' ),
'SE' => __( 'Sweden', 'useclick' ),
'NO' => __( 'Norway', 'useclick' ),
'DK' => __( 'Denmark', 'useclick' ),
'FI' => __( 'Finland', 'useclick' ),
'PL' => __( 'Poland', 'useclick' ),
'CZ' => __( 'Czech Republic', 'useclick' ),
'HU' => __( 'Hungary', 'useclick' ),
'RO' => __( 'Romania', 'useclick' ),
'IE' => __( 'Ireland', 'useclick' ),
'PT' => __( 'Portugal', 'useclick' ),
'GR' => __( 'Greece', 'useclick' ),
'UA' => __( 'Ukraine', 'useclick' ),
'RU' => __( 'Russia', 'useclick' ),
'JP' => __( 'Japan', 'useclick' ),
'CN' => __( 'China', 'useclick' ),
'IN' => __( 'India', 'useclick' ),
'BR' => __( 'Brazil', 'useclick' ),
'MX' => __( 'Mexico', 'useclick' ),
'AR' => __( 'Argentina', 'useclick' ),
'CL' => __( 'Chile', 'useclick' ),
'CO' => __( 'Colombia', 'useclick' ),
'ZA' => __( 'South Africa', 'useclick' ),
'NG' => __( 'Nigeria', 'useclick' ),
'NZ' => __( 'New Zealand', 'useclick' ),
'SG' => __( 'Singapore', 'useclick' ),
'MY' => __( 'Malaysia', 'useclick' ),
'TH' => __( 'Thailand', 'useclick' ),
'ID' => __( 'Indonesia', 'useclick' ),
'PH' => __( 'Philippines', 'useclick' ),
'VN' => __( 'Vietnam', 'useclick' ),
'KR' => __( 'South Korea', 'useclick' ),
'TR' => __( 'Turkey', 'useclick' ),
'SA' => __( 'Saudi Arabia', 'useclick' ),
'AE' => __( 'United Arab Emirates', 'useclick' ),
'IL' => __( 'Israel', 'useclick' ),
'EG' => __( 'Egypt', 'useclick' ),
);
}
}