AeliaWCEU_VAT_AssistantSettings

Handles the settings for the Blacklister plugin and provides convenience methods to read and write them.

Defined (1)

The class is defined in the following location(s).

/src/lib/classes/settings/settings.php  
  1. class Settings extends \Aelia\WC\Settings { 
  2. /*** Settings Key ***/ 
  3. // @var string The key to identify plugin settings amongst WP options. 
  4. const SETTINGS_KEY = 'wc_aelia_eu_vat_assistant'; 
  5.  
  6. /*** Settings fields ***/ 
  7. // Checkout 
  8. const FIELD_EU_VAT_NUMBER_FIELD_REQUIRED = 'eu_vat_number_field_required'; 
  9. const FIELD_EU_VAT_FIELD_TITLE = 'eu_vat_field_title'; 
  10. const FIELD_EU_VAT_FIELD_DESCRIPTION = 'eu_vat_field_description'; 
  11. const FIELD_SHOW_EU_VAT_FIELD_IF_CUSTOMER_IN_BASE_COUNTRY = 'show_eu_vat_field_number_when_customer_in_base_country'; 
  12. const FIELD_REMOVE_VAT_IF_CUSTOMER_IN_BASE_COUNTRY = 'remove_vat_when_customer_in_base_country'; 
  13. const FIELD_STORE_INVALID_VAT_NUMBERS = 'store_invalid_vat_numbers'; 
  14. const FIELD_ACCEPT_VAT_NUMBER_WHEN_VALIDATION_SERVER_BUSY = 'accept_vat_number_when_validation_server_busy'; 
  15.  
  16. // Self-certification 
  17. const FIELD_SHOW_SELF_CERTIFICATION_FIELD = 'show_self_certification_field'; 
  18. const FIELD_HIDE_SELF_CERTIFICATION_FIELD_VALID_VAT_NUMBER = 'hide_self_certification_field_valid_vat_number'; 
  19. const FIELD_SELF_CERTIFICATION_FIELD_REQUIRED_WHEN_CONFLICT = 'self_certification_field_required_when_conflict'; 
  20. const FIELD_USE_SHIPPING_ADDRESS_AS_EVIDENCE = 'use_shipping_address_as_evidence'; 
  21. const FIELD_SELF_CERTIFICATION_FIELD_TITLE = 'self_certification_field_title'; 
  22.  
  23. // Currency 
  24. const FIELD_VAT_CURRENCY = 'vat_currency'; 
  25. const FIELD_EXCHANGE_RATES = 'exchange_rates'; 
  26.  
  27. // Exchange rates 
  28. const FIELD_EXCHANGE_RATES_UPDATE_ENABLE = 'exchange_rates_update_enable'; 
  29. const FIELD_EXCHANGE_RATES_UPDATE_SCHEDULE = 'exchange_rates_update_schedule'; 
  30. const FIELD_EXCHANGE_RATES_LAST_UPDATE = 'exchange_rates_last_update'; 
  31. const FIELD_EXCHANGE_RATES_PROVIDER = 'exchange_rate_provider'; 
  32.  
  33. // Sales 
  34. const FIELD_SALE_DISALLOWED_COUNTRIES = 'sale_disallowed_countries'; 
  35.  
  36. // Reports 
  37. const FIELD_TAX_CLASSES_EXCLUDED_FROM_MOSS = 'tax_classes_excluded_from_moss'; 
  38.  
  39. // Options 
  40. const FIELD_VAT_ROUNDING_DECIMALS = 'vat_rounding_decimals'; 
  41. const FIELD_DEBUG_MODE = 'debug_mode'; 
  42. const FIELD_COLLECT_VAT_DATA_FOR_MANUAL_ORDERS = 'collect_vat_data_for_manual_orders'; 
  43.  
  44. // Defaults 
  45. // @var string The default Exchange Rates Model class to use when the configured one is not valid. 
  46. const DEFAULT_EXCHANGE_RATES_PROVIDER = 'Exchange_Rates_BitPay_Model'; 
  47. const DEFAULT_VAT_ROUNDING_DECIMALS = 4; 
  48. const DEFAULT_SHOW_SELF_CERTIFICATION_FIELD = self::OPTION_SELF_CERTIFICATION_FIELD_NO; 
  49. const DEFAULT_FIELD_EU_VAT_NUMBER_FIELD_REQUIRED = self::OPTION_EU_VAT_NUMBER_FIELD_OPTIONAL; 
  50.  
  51. // Field values 
  52. const OPTION_SELF_CERTIFICATION_FIELD_YES = 'yes'; 
  53. const OPTION_SELF_CERTIFICATION_FIELD_NO = 'no'; 
  54. const OPTION_SELF_CERTIFICATION_FIELD_CONFLICT_ONLY = 'conflict-only'; 
  55.  
  56. const OPTION_EU_VAT_NUMBER_FIELD_OPTIONAL = 'optional'; 
  57. const OPTION_EU_VAT_NUMBER_FIELD_REQUIRED = 'required'; 
  58. const OPTION_EU_VAT_NUMBER_FIELD_REQUIRED_EU_ONLY = 'required_for_eu_only'; 
  59. const OPTION_EU_VAT_NUMBER_FIELD_REQUIRED_IF_COMPANY_FILLED = 'required_if_company_field_filled'; 
  60. const OPTION_EU_VAT_NUMBER_FIELD_REQUIRED_IF_COMPANY_FILLED_EU_ONLY = 'required_if_company_field_filled_eu_only'; 
  61. const OPTION_EU_VAT_NUMBER_FIELD_HIDDEN = 'hidden'; 
  62.  
  63. // @var array The definition of the hook that will be called to update the Exchange Rates on a scheduled basis. 
  64. protected $_exchange_rates_update_hook = 'aelia_wc_eu_vat_assistant_exchange_rates_update_hook'; 
  65. // @var array A list of the available exchange rates models. 
  66. protected $_exchange_rates_models; 
  67.  
  68. // @var array A list of validation errors. 
  69. protected $validation_errors; 
  70.  
  71. /** 
  72. * Getter for private "_exchange_rates_update_hook" property. 
  73. * @return string Value of "_exchange_rates_update_hook" property. 
  74. */ 
  75. public function exchange_rates_update_hook() { 
  76. return $this->_exchange_rates_update_hook; 
  77.  
  78. /** 
  79. * Returns the currency settings to apply when a currency is selected for 
  80. * the first time and has no settings. 
  81. * @return array An array of settings. 
  82. */ 
  83. public function default_currency_settings() { 
  84. return array( 
  85. 'rate' => '',  
  86. //'rate_markup' => '',  
  87. 'set_manually' => 0,  
  88. ); 
  89.  
  90. /** 
  91. * Returns the default settings for the plugin. Used mainly at first 
  92. * installation. 
  93. * @param string key If specified, method will return only the setting identified 
  94. * by the key. 
  95. * @param mixed default The default value to return if the setting requested 
  96. * via the "key" argument is not found. 
  97. * @return array|mixed The default settings, or the value of the specified 
  98. * setting. 
  99. * @see WC_Aelia_Settings:default_settings(). 
  100. */ 
  101. public function default_settings($key = null, $default = null) { 
  102. $default_options = array( 
  103. // Checkout 
  104. self::FIELD_EU_VAT_NUMBER_FIELD_REQUIRED => self::DEFAULT_FIELD_EU_VAT_NUMBER_FIELD_REQUIRED,  
  105. self::FIELD_EU_VAT_FIELD_TITLE => __('EU VAT Number', $this->textdomain),  
  106. self::FIELD_EU_VAT_FIELD_DESCRIPTION => __('Enter your EU VAT Number (if any). Country prefix is not required.', $this->textdomain),  
  107. self::FIELD_SHOW_EU_VAT_FIELD_IF_CUSTOMER_IN_BASE_COUNTRY => true,  
  108. self::FIELD_REMOVE_VAT_IF_CUSTOMER_IN_BASE_COUNTRY => false,  
  109. self::FIELD_STORE_INVALID_VAT_NUMBERS => true,  
  110. self::FIELD_ACCEPT_VAT_NUMBER_WHEN_VALIDATION_SERVER_BUSY => true,  
  111.  
  112. // Self-certification 
  113. self::FIELD_SHOW_SELF_CERTIFICATION_FIELD => self::DEFAULT_SHOW_SELF_CERTIFICATION_FIELD,  
  114. self::FIELD_HIDE_SELF_CERTIFICATION_FIELD_VALID_VAT_NUMBER => true,  
  115. self::FIELD_SELF_CERTIFICATION_FIELD_REQUIRED_WHEN_CONFLICT => false,  
  116. self::FIELD_USE_SHIPPING_ADDRESS_AS_EVIDENCE => false,  
  117. self::FIELD_SELF_CERTIFICATION_FIELD_TITLE => __('I am established, have my permanent address, or usually reside within <strong>{billing_country}</strong>.', $this->textdomain),  
  118.  
  119. // Currency 
  120. self::FIELD_VAT_CURRENCY => get_option('woocommerce_currency'),  
  121.  
  122. // Exchange rates 
  123. self::FIELD_EXCHANGE_RATES_UPDATE_ENABLE => false,  
  124. self::FIELD_EXCHANGE_RATES_UPDATE_SCHEDULE => 'weekly',  
  125. self::FIELD_EXCHANGE_RATES_LAST_UPDATE => null,  
  126. self::FIELD_EXCHANGE_RATES_PROVIDER => self::DEFAULT_EXCHANGE_RATES_PROVIDER,  
  127.  
  128. // Reports 
  129. self::FIELD_TAX_CLASSES_EXCLUDED_FROM_MOSS => array(),  
  130.  
  131. // Options 
  132. self::FIELD_VAT_ROUNDING_DECIMALS => self::DEFAULT_VAT_ROUNDING_DECIMALS,  
  133. self::FIELD_DEBUG_MODE => false,  
  134. self::FIELD_COLLECT_VAT_DATA_FOR_MANUAL_ORDERS => false,  
  135. ); 
  136.  
  137. if(empty($key)) { 
  138. return $default_options; 
  139. else { 
  140. return get_value($key, $default_options, $default); 
  141.  
  142. /** 
  143. * Returns the currency used for VAT reports. 
  144. * @return string 
  145. */ 
  146. public function vat_currency() { 
  147. return $this->get(self::FIELD_VAT_CURRENCY, $this->default_settings(self::FIELD_VAT_CURRENCY)); 
  148.  
  149. /** 
  150. * Returns a list of the currencies enabled in the shop. 
  151. * @return array 
  152. */ 
  153. public function enabled_currencies() { 
  154. return array_unique(apply_filters('wc_aelia_cs_enabled_currencies',  
  155. array(get_option('woocommerce_currency'), $this->vat_currency()))); 
  156.  
  157. /** 
  158. * Returns the exchange rates configured for the plugin. All the rates are 
  159. * relative to the VAT currency. 
  160. * @return array An array of currency => exchange rate value. 
  161. * @see Aelia\WC\EU_VAT_Assistant\Settings::vat_currency() 
  162. */ 
  163. public function get_exchange_rates() { 
  164. $result = array(); 
  165. $exchange_rates_settings = $this->current_settings(self::FIELD_EXCHANGE_RATES); 
  166.  
  167. if(!is_array($exchange_rates_settings)) { 
  168. $exchange_rates_settings = array(); 
  169.  
  170. $vat_currency = $this->vat_currency(); 
  171. // Return all exchange rates, excluding the invalid ones 
  172. foreach($exchange_rates_settings as $currency => $settings) { 
  173. // Skip VAT currency, its exchange rate will be forced to 1 anmyway 
  174. if($currency == $vat_currency) { 
  175. continue; 
  176.  
  177. if(is_numeric(get_value('rate', $settings))) { 
  178. $exchange_rate = (float)$settings['rate']; 
  179. else { 
  180. $exchange_rate = 0; 
  181.  
  182. //// Add the markup to the exchange rate, if one was specified 
  183. //if($include_markup && is_numeric(get_value('rate_markup', $settings))) { 
  184. // $exchange_rate += (float)$settings['rate_markup']; 
  185. //} 
  186.  
  187. $result[$currency] = $exchange_rate; 
  188. // Exchange rate for VAT currency base currency is always 1 
  189. $result[$vat_currency] = 1; 
  190.  
  191. return $result; 
  192.  
  193. /** 
  194. * Returns a list of schedule options, retrieved from WordPress list. 
  195. * @return array An array of Schedule ID => Schedule Name pairs. 
  196. */ 
  197. public function get_schedule_options() { 
  198. $wp_schedules = wp_get_schedules(); 
  199. uasort($wp_schedules, array($this, 'sort_schedules')); 
  200.  
  201. $result = array(); 
  202. foreach($wp_schedules as $schedule_id => $settings) { 
  203. $result[$schedule_id] = $settings['display']; 
  204. return $result; 
  205.  
  206. /** 
  207. * Callback method, used with uasort() function. 
  208. * Sorts WordPress scheduling options by interval (ascending). In case of two 
  209. * identical intervals, it sorts them by label (comparison is case-insensitive). 
  210. * @param array a First schedule Option. 
  211. * @param array b Second schedule Option. 
  212. * @return int Zero if (a == b), -1 if (a < b), 1 if (a > b). 
  213. * @see uasort(). 
  214. */ 
  215. protected function sort_schedules($a, $b) { 
  216. if($a['interval'] == $b['interval']) { 
  217. return strcasecmp($a['display'], $b['display']); 
  218. return ($a['interval'] < $b['interval']) ? -1 : 1; 
  219.  
  220. /** 
  221. * Returns information about the schedule of the automatic updates of exchange 
  222. * rates. 
  223. * @return array An array with the next and last update of the exchange rates. 
  224. */ 
  225. public function get_exchange_rates_schedule_info() { 
  226. $schedule_options_field_id = self::FIELD_EXCHANGE_RATES_UPDATE_SCHEDULE; 
  227. $current_schedule = $this->current_settings($schedule_options_field_id); 
  228.  
  229. // Retrieve the timestamp of next scheduled exchange rates update 
  230. if(wp_get_schedule($this->_exchange_rates_update_hook) === false) { 
  231. $next_update_schedule = __('Not Scheduled', $this->textdomain); 
  232. else { 
  233. $next_update_schedule = date_i18n(get_datetime_format(), wp_next_scheduled($this->_exchange_rates_update_hook)); 
  234.  
  235. // Retrieve the timestamp of last update 
  236. if(($last_update_timestamp = $this->current_settings(self::FIELD_EXCHANGE_RATES_LAST_UPDATE)) != null) { 
  237. $last_update_timestamp_fmt = date_i18n(get_datetime_format(), $last_update_timestamp); 
  238. else { 
  239. $last_update_timestamp_fmt = __('Never updated', $this->textdomain); 
  240.  
  241. return array( 
  242. 'next_update' => $next_update_schedule,  
  243. 'last_update' => $last_update_timestamp_fmt,  
  244. ); 
  245.  
  246. /** 
  247. * Updates the plugin settings received as an argument with the latest exchange 
  248. * rates, adding a settings error if the operation fails. 
  249. * @param array settings Current plugin settings. 
  250. * @return bool 
  251. */ 
  252. public function update_exchange_rates(array &$settings, &$errors = array()) { 
  253. // Keep track of the VAT currency, it will be used as a reference for conversion 
  254. $vat_currency = get_value(self::FIELD_VAT_CURRENCY, $settings, $this->vat_currency()); 
  255. // Get the latest exchange rates from the provider 
  256. $exchange_rates_model = $this->get_exchange_rates_model(get_value(self::FIELD_EXCHANGE_RATES_PROVIDER, $settings, null)); 
  257. $latest_exchange_rates = $exchange_rates_model->get_exchange_rates($vat_currency,  
  258. $this->enabled_currencies()); 
  259. // Debug 
  260. //var_dump($exchange_rates, $exchange_rates_to_update, $latest_exchange_rates);die(); 
  261.  
  262. $exchange_rates_model_errors = $exchange_rates_model->get_errors(); 
  263. if(($latest_exchange_rates === null) || 
  264. !empty($exchange_rates_model_errors)) { 
  265. $result = empty($exchange_rates_model_errors); 
  266.  
  267. foreach($exchange_rates_model_errors as $code => $message) { 
  268. $errors['exchange-rates-error-' . $code] = $message; 
  269. return false; 
  270.  
  271. $exchange_rates = get_value(self::FIELD_EXCHANGE_RATES, $settings, array()); 
  272. // Update the exchange rates and add them to the settings to be saved 
  273. $settings[self::FIELD_EXCHANGE_RATES] = $this->merge_exchange_rates($exchange_rates,  
  274. $latest_exchange_rates,  
  275. $vat_currency); 
  276.  
  277. return true; 
  278.  
  279. /** 
  280. * Updates a list of exchange rates settings by replacing the rates with new 
  281. * ones passed as a parameter. 
  282. * @param array exchange_rates The list of exchange rate settings to be updated. 
  283. * @param array new_exchange_rates The new exchange rates as a simple list of 
  284. * currency => rate pairs. 
  285. * @param string vat_currency The currency used for VAT reports. It will have 
  286. * an exchange rate of "1". 
  287. * @return array The updated exchange rate settings. 
  288. */ 
  289. protected function merge_exchange_rates($exchange_rates, array $new_exchange_rates, $vat_currency) { 
  290. $exchange_rates = empty($exchange_rates) ? array() : $exchange_rates; 
  291.  
  292. foreach($new_exchange_rates as $currency => $rate) { 
  293. // Base VAT currency has a fixed exchange rate of 1 (it doesn't need to be 
  294. // converted) 
  295. if($currency == $vat_currency) { 
  296. $exchange_rates[$currency]['rate'] = 1; 
  297. continue; 
  298.  
  299. $currency_settings = get_value($currency, $exchange_rates, $this->default_currency_settings()); 
  300.  
  301. // Update the exchange rate unless the currency is set to "set manually" 
  302. // to prevent automatic updates 
  303. if(get_value('set_manually', $currency_settings, 0) == 0) { 
  304. $currency_settings['rate'] = $rate; 
  305. $exchange_rates[$currency] = $currency_settings; 
  306. return $exchange_rates; 
  307.  
  308. /** 
  309. * Get the instance of the exchange rate model to use to retrieve the rates. 
  310. * @param string key The key identifying the exchange rate model class. 
  311. * @param array An array of settings that can be used to override the ones 
  312. * currently saved in the configuration. 
  313. * @param string default_class The exchange rates model class to use as a default. 
  314. * @return \Aelia\WC\ExchangeRatesModel. 
  315. */ 
  316. protected function get_exchange_rates_model_instance($key,  
  317. array $settings = null,  
  318. $default_class = self::DEFAULT_EXCHANGE_RATES_PROVIDER) { 
  319. $model_info = get_value($key, $this->_exchange_rates_models); 
  320. $model_class = get_value('class_name', $model_info, $default_class); 
  321. return new $model_class($settings); 
  322.  
  323. /** 
  324. * Returns the label of the provider used to retrieve current exchange rates 
  325. * for VAT currency. 
  326. * @return string 
  327. */ 
  328. public function get_current_exchange_rates_provider_label() { 
  329. $model_info = get_value($this->get(self::FIELD_EXCHANGE_RATES_PROVIDER), $this->_exchange_rates_models); 
  330. return get_value('label', $model_info, __('Not available', $this->textdomain)); 
  331.  
  332. /** 
  333. * Returns the instance of the exchange rate model. 
  334. * @param string exchange_rates_model_key The key to retrieve the exchange 
  335. * rates model class. 
  336. * @param array settings The settings to pass to the exchange rates model instance. 
  337. * @return \Aelia\WC\ExchangeRatesModel. 
  338. */ 
  339. protected function get_exchange_rates_model($exchange_rates_model_key, $settings = null) { 
  340. if(empty($this->_exchange_rates_model)) { 
  341. $this->_exchange_rates_model = $this->get_exchange_rates_model_instance($exchange_rates_model_key,  
  342. $settings); 
  343. return $this->_exchange_rates_model; 
  344.  
  345. /** 
  346. * Validates the settings specified via the Options page. 
  347. * @param array settings An array of settings. 
  348. */ 
  349. public function validate_settings($settings) { 
  350. // Debug 
  351. //var_dump($settings);die(); 
  352. $this->validation_errors = array(); 
  353. $processed_settings = $this->current_settings(); 
  354.  
  355. // Save the schedule for automatic update of exchange rates 
  356. $this->set_exchange_rates_update_schedule($processed_settings, $settings); 
  357.  
  358. // Validate exchange rates 
  359. $exchange_rates = get_value(self::FIELD_EXCHANGE_RATES, $settings, array()); 
  360. $exchange_rates_to_update = array(); 
  361. if($this->validate_exchange_rates($exchange_rates, $exchange_rates_to_update) === true) { 
  362. $settings[self::FIELD_EXCHANGE_RATES] = $exchange_rates; 
  363.  
  364. // Debug 
  365. //var_dump($exchange_rates, $exchange_rates_to_update);die(); 
  366.  
  367. // We can update exchange rates only if an exchange rates provider has been 
  368. // configured correctly 
  369. if($this->validate_exchange_rates_provider_settings($settings) === true) { 
  370. // Update exchange rates in three cases: 
  371. // - If none is present 
  372. // - If the list of exchange rates to update is not empty 
  373. // - If button "Save and update Exchange Rates" has been clicked 
  374. if(empty($settings[self::FIELD_EXCHANGE_RATES]) || !empty($exchange_rates_to_update) || 
  375. (isset($_POST[self::SETTINGS_KEY]) && get_value('update_exchange_rates_button', $_POST[self::SETTINGS_KEY]))) { 
  376.  
  377. // Fetch the latest exchange rates and merge them with the one entered manually 
  378. if($this->update_exchange_rates($settings, $errors) === false) { 
  379. $this->add_multiple_settings_errors($errors); 
  380. else { 
  381. $settings[self::FIELD_EXCHANGE_RATES_LAST_UPDATE] = current_time('timestamp'); 
  382. // This is not an "error", but a confirmation message. Unfortunately,  
  383. // WordPress only has "add_settings_error" to add messages of any type 
  384. add_settings_error(self::SETTINGS_KEY,  
  385. 'exchange-rates-updated',  
  386. __('Settings saved. Exchange rates have been updated.', $this->textdomain),  
  387. 'updated'); 
  388.  
  389. // Save settings if they passed validation 
  390. if(empty($this->validation_errors)) { 
  391. $processed_settings = array_merge($processed_settings, $settings); 
  392.  
  393. /** Values of multi-select options have to be copied manually because, if 
  394. * the user empties those fields, they are not passed with the $_POST data. 
  395. * Due to that, a multi-select option that was previously populated would 
  396. * not be emptied. 
  397. */ 
  398. $processed_settings[self::FIELD_SALE_DISALLOWED_COUNTRIES] = get_value(self::FIELD_SALE_DISALLOWED_COUNTRIES, $settings, array()); 
  399. $processed_settings[self::FIELD_TAX_CLASSES_EXCLUDED_FROM_MOSS] = get_value(self::FIELD_TAX_CLASSES_EXCLUDED_FROM_MOSS, $settings, array()); 
  400. else { 
  401. $this->show_validation_errors(); 
  402.  
  403. // Return the array processing any additional functions filtered by this action. 
  404. return apply_filters('wc_aelia_eu_vat_assistant_settings', $processed_settings, $settings); 
  405.  
  406. /** 
  407. * Class constructor. 
  408. */ 
  409. public function __construct($settings_key = self::SETTINGS_KEY,  
  410. $textdomain = '',  
  411. \Aelia\WC\Settings_Renderer $renderer = null) { 
  412. if(empty($renderer)) { 
  413. // Instantiate the render to be used to generate the settings page 
  414. $renderer = new \Aelia\WC\Settings_Renderer(); 
  415. parent::__construct($settings_key, $textdomain, $renderer); 
  416.  
  417. // Register available exchange rates models 
  418. $this->register_exchange_rates_models(); 
  419.  
  420. add_action('admin_init', array($this, 'init_settings')); 
  421.  
  422. // If no settings are registered, save the default ones 
  423. if($this->load() === null) { 
  424. $this->save(); 
  425.  
  426. /** 
  427. * Factory method. 
  428. * @param string settings_key The key used to store and retrieve the plugin settings. 
  429. * @param string textdomain The text domain used for localisation. 
  430. * @return \Aelia\WC\Settings 
  431. */ 
  432. public static function factory($settings_key = self::SETTINGS_KEY,  
  433. $textdomain = '') { 
  434. $class = get_called_class(); 
  435. $settings_manager = new $class($settings_key, $textdomain); 
  436.  
  437. return $settings_manager; 
  438.  
  439. /** 
  440. * Registers a model used to retrieve Exchange Rates. 
  441. */ 
  442. // TODO Refactor logic to share the exchange rates models with the ones provided by the Currency Switcher, when installed 
  443. protected function register_exchange_rates_model($class_name, $label) { 
  444. if(!class_exists($class_name) || 
  445. !in_array('Aelia\WC\IExchangeRatesModel', class_implements($class_name))) { 
  446. throw new Exception(sprintf(__('Attempted to register class "%s" as an Exchange Rates ' . 
  447. 'model, but the class does not exist, or does not implement '. 
  448. 'Aelia\WC\IExchangeRatesModel interface.', $this->textdomain),  
  449. $class_name)); 
  450.  
  451. $model_id = md5($class_name); 
  452. $model_info = new stdClass(); 
  453. $model_info->class_name = $class_name; 
  454. $model_info->label = $label; 
  455. $this->_exchange_rates_models[$model_id] = $model_info; 
  456.  
  457. /** 
  458. * Registers all the available models to retrieve Exchange Rates. 
  459. */ 
  460. // TODO Refactor logic to share the exchange rates models with the ones provided by the Currency Switcher, when installed 
  461. protected function register_exchange_rates_models() { 
  462. $namespace_prefix = '\\' . __NAMESPACE__ . '\\'; 
  463. // Allow 3rd parties to add their own models 
  464. $exchange_rates_models = apply_filters('aelia_wc_exchange_rates_models', array( 
  465. $namespace_prefix . 'Exchange_Rates_BitPay_Model' => __('BitPay', $this->textdomain),  
  466. $namespace_prefix . 'Exchange_Rates_ECB_Model' => __('ECB', $this->textdomain),  
  467. $namespace_prefix . 'Exchange_Rates_HMRC_Model' => __('HMRC (UK)', $this->textdomain),  
  468. $namespace_prefix . 'Exchange_Rates_IrishRevenueHTML_Model' => __('Irish Revenue (HTML) - WARNING: experimental, may not always work!', $this->textdomain),  
  469. $namespace_prefix . 'Exchange_Rates_DNB_Model' => __('Danish National Bank', $this->textdomain),  
  470. // The Exchange_Rates_ECB_Historical_Model is used by reports. It's added to this list, but 
  471. // commented out, so that it can enabled and used for testing as needed 
  472. //$namespace_prefix . 'Exchange_Rates_ECB_Historical_Model' => __('ECB - Historical', $this->textdomain),  
  473. )); 
  474. asort($exchange_rates_models); 
  475.  
  476. foreach($exchange_rates_models as $model_class => $model_lanel) { 
  477. $this->register_exchange_rates_model($model_class, $model_lanel); 
  478.  
  479. /** 
  480. * Returns a list of the available exchange rates models. 
  481. * @return array 
  482. */ 
  483. public function exchange_rates_providers_options() { 
  484. $result = array(); 
  485. foreach($this->_exchange_rates_models as $key => $properties) { 
  486. $result[$key] = __(get_value('label', $properties), $this->textdomain); 
  487. return $result; 
  488.  
  489. /** 
  490. * Configures the schedule to automatically update the exchange rates. 
  491. * @param array current_settings An array containing current plugin settings. 
  492. * @param array new_settings An array containing new plugin settings. 
  493. */ 
  494. protected function set_exchange_rates_update_schedule(array $current_settings, array $new_settings) { 
  495. // Clear exchange rates update schedule, if it was disabled 
  496. $exchange_rates_schedule_enabled = get_value(self::FIELD_EXCHANGE_RATES_UPDATE_ENABLE, $new_settings, 0); 
  497. if($exchange_rates_schedule_enabled != self::ENABLED_YES) { 
  498. wp_clear_scheduled_hook($this->_exchange_rates_update_hook); 
  499. else { 
  500. // If exchange rates update is still scheduled, check if its schedule changed. 
  501. // If it changed, remove old schedule and set a new one. 
  502. $new_exchange_rates_update_schedule = get_value(self::FIELD_EXCHANGE_RATES_UPDATE_SCHEDULE, $new_settings); 
  503. if((get_value(self::FIELD_EXCHANGE_RATES_UPDATE_SCHEDULE, $current_settings) != $new_exchange_rates_update_schedule) || 
  504. (get_value(self::FIELD_EXCHANGE_RATES_UPDATE_ENABLE, $current_settings) != $exchange_rates_schedule_enabled)) { 
  505. wp_clear_scheduled_hook($this->_exchange_rates_update_hook); 
  506. //var_dump($new_exchange_rates_update_schedule);die(); 
  507. wp_schedule_event(current_time('timestamp'), $new_exchange_rates_update_schedule, $this->_exchange_rates_update_hook); 
  508.  
  509. /** 
  510. * Displays the validation errors (if any). 
  511. */ 
  512. protected function show_validation_errors() { 
  513. foreach($this->validation_errors as $error_key => $error_message) { 
  514. add_settings_error(self::SETTINGS_KEY, $error_key, $error_message); 
  515.  
  516. /** 
  517. * Updates the exchange rates. Triggered by a scheduled task. 
  518. */ 
  519. public function scheduled_update_exchange_rates() { 
  520. $settings = $this->current_settings(); 
  521. if($this->update_exchange_rates($settings, $errors) === true) { 
  522. // Save the timestamp of last update 
  523. $settings[self::FIELD_EXCHANGE_RATES_LAST_UPDATE] = current_time('timestamp'); 
  524. $this->save($settings); 
  525.  
  526. /*** Validation methods ***/ 
  527. /** 
  528. * Validates a list of exchange rates. 
  529. * @param array A list of exchange rates. 
  530. * @return bool True, if the validation succeeds, false otherwise. 
  531. */ 
  532. protected function validate_exchange_rates($exchange_rates, &$exchange_rates_to_update = array()) { 
  533. $currency_with_invalid_rates = array(); 
  534. foreach($exchange_rates as $currency => $settings) { 
  535. $exchange_rate = get_value('rate', $settings); 
  536. if(!is_numeric($exchange_rate) || empty($exchange_rate)) { 
  537. if((get_value('set_manually', $settings, 0) === 1)) { 
  538. // Exchange rate is invalid and it was set manually. Add it to the error list 
  539. $currency_with_invalid_rates[] = $currency; 
  540. else { 
  541. // Exchange rate is invalid and it was set to update automatically. 
  542. // Add it to the update list 
  543. $exchange_rates_to_update[] = $currency; 
  544. if(!empty($currency_with_invalid_rates)) { 
  545. $this->validation_errors['invalid-rate'] = sprintf(__('Some exchange rates entered manually are invalid. ' . 
  546. 'Please review the rates for the following ' . 
  547. 'currencies: %s.',  
  548. $this->textdomain),  
  549. implode(', ', $currency_with_invalid_rates)); 
  550. return empty($currency_with_invalid_rates); 
  551.  
  552. /** 
  553. * Validates settings for the selected exchange rates provider. 
  554. * @param array settings An array of settings. 
  555. * @return bool 
  556. */ 
  557. protected function validate_exchange_rates_provider_settings($settings) { 
  558. // TODO Implement validation as needed 
  559. return true;