<?php /** * Currency Ticker  Scrolling Exchange Rate Bar * * Displays after breadcrumbs on all pages. * Uses Frankfurter API v2 (free, no key required). * * @package RankwellNews * @since 2.1.0 */ namespace Rankwell\Core; use Rankwell\Admin\Settings; if (!defined('ABSPATH')) { exit; } class CurrencyTicker implements ComponentInterface { private const CACHE_DURATION = 2 * HOUR_IN_SECONDS; private const API_URL = 'https://api.frankfurter.dev/v2'; private const CURRENCIES = [ 'NGN' => ['name' => 'Nigerian Naira', 'symbol' => ' ¦', 'flag' => 'Ø<ÝóØ<Ýì'], 'USD' => ['name' => 'US Dollar', 'symbol' => '$', 'flag' => 'Ø<ÝúØ<Ýø'], 'GBP' => ['name' => 'British Pound', 'symbol' => '£', 'flag' => 'Ø<ÝìØ<Ýç'], 'EUR' => ['name' => 'Euro', 'symbol' => ' ¬', 'flag' => 'Ø<ÝêØ<Ýú'], 'CAD' => ['name' => 'Canadian Dollar', 'symbol' => 'C$', 'flag' => 'Ø<ÝèØ<Ýæ'], 'GHS' => ['name' => 'Ghanaian Cedi', 'symbol' => 'GH µ', 'flag' => 'Ø<ÝìØ<Ýí'], 'ZAR' => ['name' => 'South African Rand', 'symbol' => 'R', 'flag' => 'Ø<ÝÿØ<Ýæ'], 'KES' => ['name' => 'Kenyan Shilling', 'symbol' => 'KSh', 'flag' => 'Ø<ÝðØ<Ýê'], 'JPY' => ['name' => 'Japanese Yen', 'symbol' => '¥', 'flag' => 'Ø<ÝïØ<Ýõ'], 'CNY' => ['name' => 'Chinese Yuan', 'symbol' => '¥', 'flag' => 'Ø<ÝèØ<Ýó'], 'AED' => ['name' => 'UAE Dirham', 'symbol' => '/.%', 'flag' => 'Ø<ÝæØ<Ýê'], 'INR' => ['name' => 'Indian Rupee', 'symbol' => ' ¹', 'flag' => 'Ø<ÝîØ<Ýó'], 'SAR' => ['name' => 'Saudi Riyal', 'symbol' => 'ýü', 'flag' => 'Ø<ÝøØ<Ýæ'], 'CHF' => ['name' => 'Swiss Franc', 'symbol' => 'Fr', 'flag' => 'Ø<ÝèØ<Ýí'], 'AUD' => ['name' => 'Australian Dollar', 'symbol' => 'A$', 'flag' => 'Ø<ÝæØ<Ýú'], ]; public static function init(): void { if (!Settings::get('currency_ticker_enabled', 1)) { return; } add_action('rankwell_after_breadcrumbs', [__CLASS__, 'render_ticker']); add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_assets']); } public static function enqueue_assets(): void { wp_enqueue_style( 'rw-currency-ticker', RANKWELL_NEWS_URI . '/assets/css/components/_currency-ticker.css', [], RANKWELL_NEWS_VERSION ); } private static function get_rates(): array { $cache_key = 'rankwell_currency_rates'; $rates = get_transient($cache_key); if (false !== $rates) { return $rates; } $base = Settings::get('currency_base', 'NGN'); $targets = Settings::get('currency_targets', 'USD,GBP,EUR'); $targets = str_replace(' ', '', strtoupper($targets)); $url = add_query_arg([ 'base' => $base, 'quotes' => $targets, ], self::API_URL . '/rates'); $response = wp_remote_get($url, ['timeout' => 10]); if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) { return []; } $body = json_decode(wp_remote_retrieve_body($response), true); if (empty($body) || !is_array($body)) { return []; } // v2 returns array of {date, base, quote, rate} objects $rates = []; $date = ''; foreach ($body as $item) { if (empty($item['quote']) || !isset($item['rate'])) { continue; } // Convert: if 1 NGN = 0.00073 USD, show how many NGN per 1 USD $rates[$item['quote']] = (float) $item['rate']; if (empty($date) && !empty($item['date'])) { $date = $item['date']; } } if (empty($rates)) { return []; } $data = [ 'base' => $base, 'date' => $date, 'rates' => $rates, ]; set_transient($cache_key, $data, self::CACHE_DURATION); return $data; } public static function render_ticker(): void { $data = self::get_rates(); if (empty($data['rates'])) { return; } $base = $data['base']; $rates = $data['rates']; ?> <div class="rn-currency-ticker" data-base="<?php echo esc_attr($base); ?>"> <div class="rn-currency-ticker-inner"> <span class="rn-currency-ticker-label"> <?php echo esc_html(self::CURRENCIES[$base]['flag'] ?? ''); ?> <?php esc_html_e('Exchange Rates', 'rankwell-news'); ?> </span> <div class="rn-currency-ticker-track"> <?php // First set  visible ?> <?php foreach ($rates as $code => $rate): ?> <span class="rn-currency-ticker-item"> <?php echo esc_html(self::CURRENCIES[$code]['flag'] ?? ''); ?> <strong><?php echo esc_html($code); ?></strong> <?php echo esc_html(number_format(1 / $rate, 2)); ?> <?php echo esc_html(self::CURRENCIES[$base]['symbol'] ?? ''); ?> </span> <?php endforeach; ?> <?php // Duplicate set  for seamless loop ?> <?php foreach ($rates as $code => $rate): ?> <span class="rn-currency-ticker-item" aria-hidden="true"> <?php echo esc_html(self::CURRENCIES[$code]['flag'] ?? ''); ?> <strong><?php echo esc_html($code); ?></strong> <?php echo esc_html(number_format(1 / $rate, 2)); ?> <?php echo esc_html(self::CURRENCIES[$base]['symbol'] ?? ''); ?> </span> <?php endforeach; ?> </div> </div> </div> <?php } public static function get_currency_options(): array { $options = []; foreach (self::CURRENCIES as $code => $data) { $options[$code] = sprintf('%s %s (%s)', $data['flag'], $data['name'], $code); } return $options; } }<?php /** * Currency Ticker  Scrolling Exchange Rate Bar * * Displays after breadcrumbs on all pages. * Uses Frankfurter API v2 (free, no key required). * * @package RankwellNews * @since 2.1.0 */ namespace Rankwell\Core; use Rankwell\Admin\Settings; if (!defined('ABSPATH')) { exit; } class CurrencyTicker implements ComponentInterface { private const CACHE_DURATION = 2 * HOUR_IN_SECONDS; private const API_URL = 'https://api.frankfurter.dev/v2'; private const CURRENCIES = [ 'NGN' => ['name' => 'Nigerian Naira', 'symbol' => ' ¦', 'flag' => 'Ø<ÝóØ<Ýì'], 'USD' => ['name' => 'US Dollar', 'symbol' => '$', 'flag' => 'Ø<ÝúØ<Ýø'], 'GBP' => ['name' => 'British Pound', 'symbol' => '£', 'flag' => 'Ø<ÝìØ<Ýç'], 'EUR' => ['name' => 'Euro', 'symbol' => ' ¬', 'flag' => 'Ø<ÝêØ<Ýú'], 'CAD' => ['name' => 'Canadian Dollar', 'symbol' => 'C$', 'flag' => 'Ø<ÝèØ<Ýæ'], 'GHS' => ['name' => 'Ghanaian Cedi', 'symbol' => 'GH µ', 'flag' => 'Ø<ÝìØ<Ýí'], 'ZAR' => ['name' => 'South African Rand', 'symbol' => 'R', 'flag' => 'Ø<ÝÿØ<Ýæ'], 'KES' => ['name' => 'Kenyan Shilling', 'symbol' => 'KSh', 'flag' => 'Ø<ÝðØ<Ýê'], 'JPY' => ['name' => 'Japanese Yen', 'symbol' => '¥', 'flag' => 'Ø<ÝïØ<Ýõ'], 'CNY' => ['name' => 'Chinese Yuan', 'symbol' => '¥', 'flag' => 'Ø<ÝèØ<Ýó'], 'AED' => ['name' => 'UAE Dirham', 'symbol' => '/.%', 'flag' => 'Ø<ÝæØ<Ýê'], 'INR' => ['name' => 'Indian Rupee', 'symbol' => ' ¹', 'flag' => 'Ø<ÝîØ<Ýó'], 'SAR' => ['name' => 'Saudi Riyal', 'symbol' => 'ýü', 'flag' => 'Ø<ÝøØ<Ýæ'], 'CHF' => ['name' => 'Swiss Franc', 'symbol' => 'Fr', 'flag' => 'Ø<ÝèØ<Ýí'], 'AUD' => ['name' => 'Australian Dollar', 'symbol' => 'A$', 'flag' => 'Ø<ÝæØ<Ýú'], ]; public static function init(): void { if (!Settings::get('currency_ticker_enabled', 1)) { return; } add_action('rankwell_after_breadcrumbs', [__CLASS__, 'render_ticker']); add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_assets']); } public static function enqueue_assets(): void { wp_enqueue_style( 'rw-currency-ticker', RANKWELL_NEWS_URI . '/assets/css/components/_currency-ticker.css', [], RANKWELL_NEWS_VERSION ); } private static function get_rates(): array { $cache_key = 'rankwell_currency_rates'; $rates = get_transient($cache_key); if (false !== $rates) { return $rates; } $base = Settings::get('currency_base', 'NGN'); $targets = Settings::get('currency_targets', 'USD,GBP,EUR'); $targets = str_replace(' ', '', strtoupper($targets)); $url = add_query_arg([ 'base' => $base, 'quotes' => $targets, ], self::API_URL . '/rates'); $response = wp_remote_get($url, ['timeout' => 10]); if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) { return []; } $body = json_decode(wp_remote_retrieve_body($response), true); if (empty($body) || !is_array($body)) { return []; } // v2 returns array of {date, base, quote, rate} objects $rates = []; $date = ''; foreach ($body as $item) { if (empty($item['quote']) || !isset($item['rate'])) { continue; } // Convert: if 1 NGN = 0.00073 USD, show how many NGN per 1 USD $rates[$item['quote']] = (float) $item['rate']; if (empty($date) && !empty($item['date'])) { $date = $item['date']; } } if (empty($rates)) { return []; } $data = [ 'base' => $base, 'date' => $date, 'rates' => $rates, ]; set_transient($cache_key, $data, self::CACHE_DURATION); return $data; } public static function render_ticker(): void { $data = self::get_rates(); if (empty($data['rates'])) { return; } $base = $data['base']; $rates = $data['rates']; ?> <div class="rn-currency-ticker" data-base="<?php echo esc_attr($base); ?>"> <div class="rn-currency-ticker-inner"> <span class="rn-currency-ticker-label"> <?php echo esc_html(self::CURRENCIES[$base]['flag'] ?? ''); ?> <?php esc_html_e('Exchange Rates', 'rankwell-news'); ?> </span> <div class="rn-currency-ticker-track"> <?php // First set  visible ?> <?php foreach ($rates as $code => $rate): ?> <span class="rn-currency-ticker-item"> <?php echo esc_html(self::CURRENCIES[$code]['flag'] ?? ''); ?> <strong><?php echo esc_html($code); ?></strong> <?php echo esc_html(number_format(1 / $rate, 2)); ?> <?php echo esc_html(self::CURRENCIES[$base]['symbol'] ?? ''); ?> </span> <?php endforeach; ?> <?php // Duplicate set  for seamless loop ?> <?php foreach ($rates as $code => $rate): ?> <span class="rn-currency-ticker-item" aria-hidden="true"> <?php echo esc_html(self::CURRENCIES[$code]['flag'] ?? ''); ?> <strong><?php echo esc_html($code); ?></strong> <?php echo esc_html(number_format(1 / $rate, 2)); ?> <?php echo esc_html(self::CURRENCIES[$base]['symbol'] ?? ''); ?> </span> <?php endforeach; ?> </div> </div> </div> <?php } public static function get_currency_options(): array { $options = []; foreach (self::CURRENCIES as $code => $data) { $options[$code] = sprintf('%s %s (%s)', $data['flag'], $data['name'], $code); } return $options; } }