Changeset 3371141
- Timestamp:
- 10/01/2025 01:52:59 PM (5 months ago)
- Location:
- boxo-return
- Files:
-
- 6 added
- 6 deleted
- 30 edited
- 1 copied
-
tags/0.0.61 (copied) (copied from boxo-return/trunk)
-
tags/0.0.61/admin/admin.php (modified) (3 diffs)
-
tags/0.0.61/admin/order.php (modified) (4 diffs)
-
tags/0.0.61/admin/product-picker.php (modified) (2 diffs)
-
tags/0.0.61/admin/settings.php (modified) (12 diffs)
-
tags/0.0.61/boxo-return.php (modified) (4 diffs)
-
tags/0.0.61/checkout/boxo-checkout.js (modified) (2 diffs)
-
tags/0.0.61/checkout/checkout.php (modified) (11 diffs)
-
tags/0.0.61/includes/ajax.php (modified) (4 diffs)
-
tags/0.0.61/includes/api.php (deleted)
-
tags/0.0.61/includes/available.php (added)
-
tags/0.0.61/includes/logger.php (deleted)
-
tags/0.0.61/includes/options.php (modified) (12 diffs)
-
tags/0.0.61/includes/postal-code.php (added)
-
tags/0.0.61/includes/setup.php (added)
-
tags/0.0.61/languages/boxo-return-nl.po (modified) (1 diff)
-
tags/0.0.61/languages/boxo-return-nl_BE.mo (modified) (previous)
-
tags/0.0.61/languages/boxo-return-nl_NL.mo (modified) (previous)
-
tags/0.0.61/languages/boxo-return-nl_NL_formal.mo (modified) (previous)
-
tags/0.0.61/languages/boxo-return.pot (modified) (3 diffs)
-
tags/0.0.61/readme.txt (modified) (2 diffs)
-
tags/0.0.61/utils (deleted)
-
trunk/admin/admin.php (modified) (3 diffs)
-
trunk/admin/order.php (modified) (4 diffs)
-
trunk/admin/product-picker.php (modified) (2 diffs)
-
trunk/admin/settings.php (modified) (12 diffs)
-
trunk/boxo-return.php (modified) (4 diffs)
-
trunk/checkout/boxo-checkout.js (modified) (2 diffs)
-
trunk/checkout/checkout.php (modified) (11 diffs)
-
trunk/includes/ajax.php (modified) (4 diffs)
-
trunk/includes/api.php (deleted)
-
trunk/includes/available.php (added)
-
trunk/includes/logger.php (deleted)
-
trunk/includes/options.php (modified) (12 diffs)
-
trunk/includes/postal-code.php (added)
-
trunk/includes/setup.php (added)
-
trunk/languages/boxo-return-nl.po (modified) (1 diff)
-
trunk/languages/boxo-return-nl_BE.mo (modified) (previous)
-
trunk/languages/boxo-return-nl_NL.mo (modified) (previous)
-
trunk/languages/boxo-return-nl_NL_formal.mo (modified) (previous)
-
trunk/languages/boxo-return.pot (modified) (3 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/utils (deleted)
Legend:
- Unmodified
- Added
- Removed
-
boxo-return/tags/0.0.61/admin/admin.php
r3276747 r3371141 13 13 if (!class_exists('Boxo_Admin')) { 14 14 class Boxo_Admin { 15 /** 16 * @return void 17 */ 15 18 public static function add_menu_item() { 16 19 if (!current_user_can('manage_woocommerce')) { … … 28 31 } 29 32 33 /** 34 * @return void 35 */ 30 36 public static function add_options_page() { 31 37 if (!current_user_can('manage_woocommerce')) { … … 53 59 } 54 60 61 /** 62 * @param mixed $links 63 * @return mixed 64 */ 55 65 public static function add_settings_link($links) { 66 // If filter arg type is incorrect due to external code, just get out of the way and return the arg. 67 if (!is_array($links)) { 68 return $links; 69 } 56 70 $url = admin_url('admin.php?page=boxo-return'); 57 71 $action_links = array( -
boxo-return/tags/0.0.61/admin/order.php
r3243704 r3371141 31 31 if (!class_exists('Boxo_Order')) { 32 32 class Boxo_Order { 33 /** 34 * @return void 35 */ 33 36 public static function init() { 34 37 wp_enqueue_style('boxo_order', plugins_url('order.css', __FILE__)); 35 38 } 36 39 40 /** 41 * @param mixed $columns 42 * @return mixed 43 */ 37 44 public static function order_list_add_packaging_column($columns) { 45 // If filter arg type is incorrect due to external code, just get out of the way and return the arg. 46 if (!is_array($columns)) { 47 return $columns; 48 } 38 49 $columns['boxo_packaging'] = esc_html__('Packaging', 'boxo-return'); 39 50 return $columns; 40 51 } 41 52 53 /** 54 * @param mixed $column_name 55 * @param mixed $order 56 * @return void 57 */ 42 58 public static function order_list_populate_column_hpos($column_name, $order) { 43 if ($column_name === 'boxo_packaging') { 44 self::populate_column($order); 45 } 46 } 47 59 if ($column_name === 'boxo_packaging' && $order instanceof WC_Order) { 60 $packaging = $order->get_meta('boxo_packaging', true); 61 if (!is_string($packaging)) { 62 return; 63 } 64 self::populate_column($packaging); 65 } 66 } 67 68 /** 69 * @param mixed $column_name 70 * @param mixed $order_id 71 * @return void 72 */ 48 73 public static function order_list_populate_column_cpt($column_name, $order_id) { 49 74 $order = wc_get_order($order_id); 75 if (is_bool($order)) { 76 return; 77 } 50 78 if ($column_name === 'boxo_packaging') { 51 self::populate_column($order); 52 } 53 } 54 55 public static function populate_column($order) { 56 $packaging = $order->get_meta('boxo_packaging'); 57 $cell = ""; 79 $packaging = $order->get_meta('boxo_packaging', true); 80 if (!is_string($packaging)) { 81 return; 82 } 83 self::populate_column($packaging); 84 } 85 } 86 87 /** 88 * @param string $packaging 89 * @return void 90 */ 91 public static function populate_column($packaging) { 58 92 switch ($packaging) { 59 93 case Boxo_Constants::PACKAGING_REUSABLE: 60 $cell =esc_html__('Reusable', 'boxo-return');61 break;94 echo esc_html__('Reusable', 'boxo-return'); 95 return; 62 96 case Boxo_Constants::PACKAGING_DISPOSABLE: 63 $cell =esc_html__('Disposable', 'boxo-return');64 break;97 echo esc_html__('Disposable', 'boxo-return'); 98 return; 65 99 default: 66 100 // If the order was placed when BOXO Return was not active, leave packaging field empty. 67 break; 68 } 69 echo $cell; 70 } 71 101 return; 102 } 103 } 104 105 /** 106 * @param mixed $data 107 * @param mixed $order 108 * @return mixed 109 */ 72 110 public static function order_preview_add_packaging_data($data, $order) { 73 if ($packaging = $order->get_meta('boxo_packaging')) { 74 $data['boxo_packaging'] = $packaging; 75 } 111 // If filter arg type is incorrect due to external code, just get out of the way and return the arg. 112 if (!is_array($data) || !($order instanceof WC_Order)) { 113 return $data; 114 } 115 $packaging = $order->get_meta('boxo_packaging', true); 116 if (!is_string($packaging)) { 117 return $data; 118 } 119 $data['boxo_packaging'] = $packaging; 76 120 return $data; 77 121 } 78 122 123 /** 124 * @return void 125 */ 79 126 public static function order_preview_add_packaging() { 80 127 $title_safe = esc_html__('Packaging', 'boxo-return'); … … 101 148 } 102 149 150 /** 151 * @param mixed $order 152 * @return void 153 */ 103 154 public static function order_detail_add_packaging($order) { 155 if (!($order instanceof WC_Order)) { 156 return; 157 } 104 158 $packaging = $order->get_meta('boxo_packaging', true); 105 159 $title_safe = esc_html__('Packaging', 'boxo-return'); … … 124 178 } 125 179 180 /** 181 * @return string 182 */ 126 183 private static function reusable_label() { 127 184 $heart_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/heart.svg'); … … 135 192 } 136 193 194 /** 195 * @return string 196 */ 137 197 private static function disposable_label() { 138 198 return esc_html__('Disposable packaging', 'boxo-return'); -
boxo-return/tags/0.0.61/admin/product-picker.php
r3276747 r3371141 23 23 foreach ($ids as $id) { 24 24 $product = wc_get_product($id); 25 if (! $product) {25 if (!($product instanceof WC_Product)) { 26 26 continue; 27 27 } 28 29 $image = wp_get_attachment_image_src( 30 $product->get_image_id(), 28 $image_data = wp_get_attachment_image_src( 29 intval($product->get_image_id()), 31 30 [30, 30], 32 31 ); … … 34 33 'id' => $id, 35 34 'title' => $product->get_title(), 36 'image' => $image ? $image[0] : null,35 'image' => is_array($image_data) ? $image_data[0] : null, 37 36 ]); 38 37 } -
boxo-return/tags/0.0.61/admin/settings.php
r3310810 r3371141 12 12 13 13 // When saving, make sure checkbox input is saved as boolean. 14 add_filter('pre_update_option_boxo_options', function ($val) { 15 $val['disposable_fee_enabled'] = isset($val['disposable_fee_enabled']) ? true : false; 16 $val['cart_limit_enabled'] = isset($val['cart_limit_enabled']) ? true : false; 17 return $val; 14 add_filter('pre_update_option_boxo_options', function ($vals) { 15 // If filter arg type is incorrect due to external code, just get out of the way and return the arg. 16 if (!is_array($vals)) { 17 return $vals; 18 } 19 $vals['disposable_fee_enabled'] = isset($vals['disposable_fee_enabled']) ? true : false; 20 $vals['cart_limit_enabled'] = isset($vals['cart_limit_enabled']) ? true : false; 21 return $vals; 18 22 }, 10, 1); 19 23 20 24 if (!class_exists('Boxo_Settings')) { 21 25 class Boxo_Settings { 26 /** 27 * @return void 28 */ 22 29 public static function init() { 23 30 if (!current_user_can('manage_woocommerce')) { … … 127 134 } 128 135 136 /** 137 * @return void 138 */ 129 139 public static function add_section_header() { 130 140 // Empty but required. 131 141 } 132 142 143 /** 144 * @param mixed $hook_suffix 145 * @return void 146 */ 133 147 public static function enqueue_scripts($hook_suffix) { 134 // Only load on boxo-return admin page.135 if (! str_contains($hook_suffix, 'boxo-return')) {148 // Only load on admin page for boxo-return. 149 if (!is_string($hook_suffix) || strpos($hook_suffix, 'boxo-return') === false) { 136 150 return; 137 151 } 138 wp_enqueue_script('alpine', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js', null, '3.14.5', true);152 wp_enqueue_script('alpine', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js', [], '3.14.5', true); 139 153 wp_enqueue_script('boxo_product_picker', plugins_url('product-picker.js', __FILE__)); 140 154 } 141 155 156 /** 157 * @return void 158 */ 142 159 public static function add_api_key_field() { 143 160 $value_safe = esc_attr(Boxo_Options::api_key()); … … 157 174 } 158 175 176 /** 177 * @return void 178 */ 159 179 public static function add_deposit_cents_field() { 160 $value_safe = esc_attr( Boxo_Options::deposit_cents());180 $value_safe = esc_attr(strval(Boxo_Options::deposit_cents())); 161 181 echo <<<HTML 162 182 <input id="boxo_deposit-cents-input" name="boxo_options[deposit_cents]" type="number" min="0" max="10000" value="$value_safe"> … … 164 184 } 165 185 186 /** 187 * @return void 188 */ 166 189 public static function add_disposable_fee_enabled_field() { 167 190 $enabled = Boxo_Options::disposable_fee_enabled(); … … 170 193 $label_safe = esc_html__('Enable disposable packaging fee', 'boxo-return'); 171 194 $description_safe = esc_html__('If enabled, a fee is charged when the customer does not select reusable packaging.', 'boxo-return'); 172 $fee_safe = esc_attr( Boxo_Options::disposable_fee_cents());195 $fee_safe = esc_attr(strval(Boxo_Options::disposable_fee_cents())); 173 196 $cents_label_safe = esc_html__('Fee in cents:', 'boxo-return'); 174 197 echo <<<HTML … … 203 226 } 204 227 228 /** 229 * @return void 230 */ 205 231 public static function add_selection_mode_field() { 206 232 $selection_mode = Boxo_Options::selection_mode(); … … 212 238 $disposable_safe = Boxo_Options::DEFAULT_PACKAGING_DISPOSABLE; 213 239 214 $choice_selected_attb_safe = $selection_mode == Boxo_Options::SELECTION_MODE_CHOICE ? 'selected' : '';215 $force_reusable_selected_attb_safe = $selection_mode == Boxo_Options::SELECTION_MODE_FORCE_REUSABLE ? 'selected' : '';216 $reusable_selected_attb_safe = $default_packaging == Boxo_Options::DEFAULT_PACKAGING_REUSABLE ? 'selected' : '';217 $disposable_selected_attb_safe = $default_packaging == Boxo_Options::DEFAULT_PACKAGING_DISPOSABLE ? 'selected' : '';240 $choice_selected_attb_safe = $selection_mode === Boxo_Options::SELECTION_MODE_CHOICE ? 'selected' : ''; 241 $force_reusable_selected_attb_safe = $selection_mode === Boxo_Options::SELECTION_MODE_FORCE_REUSABLE ? 'selected' : ''; 242 $reusable_selected_attb_safe = $default_packaging === Boxo_Options::DEFAULT_PACKAGING_REUSABLE ? 'selected' : ''; 243 $disposable_selected_attb_safe = $default_packaging === Boxo_Options::DEFAULT_PACKAGING_DISPOSABLE ? 'selected' : ''; 218 244 219 245 $default_packaging_label_safe = esc_html__('Default selection:', 'boxo-return'); … … 246 272 } 247 273 274 /** 275 * @return void 276 */ 248 277 public static function add_info_url_field() { 249 278 $value_safe = esc_attr(Boxo_Options::info_url()); … … 262 291 } 263 292 293 /** 294 * @return void 295 */ 264 296 public static function add_product_allow_mode_field() { 265 297 $allow_mode = Boxo_Options::product_allow_mode(); … … 269 301 $included_only_safe = Boxo_Options::PRODUCT_ALLOW_MODE_INCLUDED_ONLY; 270 302 271 $all_selected_attb_safe = $allow_mode == Boxo_Options::PRODUCT_ALLOW_MODE_ALL ? 'selected' : '';272 $all_except_excluded_selected_attb_safe = $allow_mode == Boxo_Options::PRODUCT_ALLOW_MODE_ALL_EXCEPT_EXCLUDED ? 'selected' : '';273 $included_only_selected_attb_safe = $allow_mode == Boxo_Options::PRODUCT_ALLOW_MODE_INCLUDED_ONLY ? 'selected' : '';303 $all_selected_attb_safe = $allow_mode === Boxo_Options::PRODUCT_ALLOW_MODE_ALL ? 'selected' : ''; 304 $all_except_excluded_selected_attb_safe = $allow_mode === Boxo_Options::PRODUCT_ALLOW_MODE_ALL_EXCEPT_EXCLUDED ? 'selected' : ''; 305 $included_only_selected_attb_safe = $allow_mode === Boxo_Options::PRODUCT_ALLOW_MODE_INCLUDED_ONLY ? 'selected' : ''; 274 306 275 307 $all_label_safe = esc_html__('All products', 'boxo-return'); … … 311 343 } 312 344 345 /** 346 * @return void 347 */ 313 348 public static function add_cart_limit_enabled_field() { 314 349 $enabled = Boxo_Options::cart_limit_enabled(); … … 317 352 $label_safe = esc_html__('Enable cart limit', 'boxo-return'); 318 353 $description_safe = esc_html__('If enabled, reusable packaging will not be available when the cart exceeds the set amount of items.', 'boxo-return'); 319 $limit_safe = esc_attr( Boxo_Options::cart_limit());354 $limit_safe = esc_attr(strval(Boxo_Options::cart_limit())); 320 355 $limit_label_safe = esc_html__('Max items:', 'boxo-return'); 321 356 echo <<<HTML -
boxo-return/tags/0.0.61/boxo-return.php
r3349705 r3371141 2 2 /* 3 3 * Plugin Name: BOXO Return 4 * Version: 0.0.60 4 * Version: 0.0.61 5 * Requires at least: 6.5 6 * Requires PHP: 7.1 5 7 * Description: Allows customers to select reusable packaging during checkout. 6 8 * Author: BOXO … … 20 22 } 21 23 22 const BOXO_RETURN_PLUGIN_VERSION = "0.0.6 0";24 const BOXO_RETURN_PLUGIN_VERSION = "0.0.61"; 23 25 24 26 include plugin_dir_path(__FILE__) . 'includes/constants.php'; 27 include plugin_dir_path(__FILE__) . 'includes/setup.php'; 25 28 include plugin_dir_path(__FILE__) . 'includes/options.php'; 26 include plugin_dir_path(__FILE__) . 'includes/api.php'; 29 include plugin_dir_path(__FILE__) . 'includes/postal-code.php'; 30 include plugin_dir_path(__FILE__) . 'includes/available.php'; 27 31 include plugin_dir_path(__FILE__) . 'includes/ajax.php'; 28 include plugin_dir_path(__FILE__) . 'includes/logger.php';29 32 if (is_admin()) { 30 33 include plugin_dir_path(__FILE__) . 'admin/admin.php'; … … 34 37 } 35 38 include plugin_dir_path(__FILE__) . 'checkout/checkout.php'; 39 40 register_activation_hook(__FILE__, 'Boxo_Setup::handle_activation'); 41 register_deactivation_hook(__FILE__, 'Boxo_Setup::handle_deactivation'); 36 42 37 43 // BOXO Return plugin is incompatible with Checkout Blocks. … … 44 50 add_action('init', 'boxo_load_textdomain'); 45 51 52 /** 53 * @return void 54 */ 46 55 function boxo_load_textdomain() { 47 56 load_plugin_textdomain('boxo-return', false, dirname(plugin_basename(__FILE__)) . '/languages'); -
boxo-return/tags/0.0.61/checkout/boxo-checkout.js
r3317144 r3371141 1 import {parsePostalCode} from '../utils/utils.js'2 3 const CHECK_AVAILABLE_TIMEOUT_MS = 50004 5 1 if (document.readyState !== 'loading') { 6 2 init() … … 10 6 11 7 async function init() { 12 const pluginData = getPluginData()13 console.info('[BOXO Return] ' + pluginData.boxoVersion)14 15 8 // Must use jQuery for event handlers because propagation of certain events appears to be blocked. 16 9 jQuery(function ($) { 10 // AJAX update checkout (incl. total price) on change in relevant data or packaging selection. 17 11 $(document.body).on( 18 12 'change', 19 '#billing_country, #billing_postcode, #shipping_country, #shipping_postcode, #ship-to-different-address-checkbox ',13 '#billing_country, #billing_postcode, #shipping_country, #shipping_postcode, #ship-to-different-address-checkbox, [name=boxo_packaging]', 20 14 async () => { 21 await handleChange()22 15 document.body.dispatchEvent(new Event('update_checkout')) 23 16 } 24 17 ) 25 26 // AJAX update checkout (incl. total price) on packaging change.27 $(document.body).on('change', '[name=boxo_packaging]', (e) => {28 document.body.dispatchEvent(new Event('update_checkout'))29 })30 18 }) 31 32 // Check Boxo availability on initial load, in case postal code was pre-filled.33 await handleChange()34 document.body.dispatchEvent(new Event('update_checkout'))35 19 36 20 console.info('[BOXO Return] Ready') 37 21 } 38 39 /**40 * Data added to the page by the plugin.41 * @typedef {Object} PluginData42 * @property {string} boxoVersion - The version of the Boxo Return plugin.43 * @property {string} boxoAvailableUrl - The URL of the endpoint where Boxo availability can be checked.44 * @property {string} cartProducts - A comma-separated list of IDs of the products currently in cart.45 * @property {string} cartItemCount - The amount of items currently in cart.46 * @property {string} shopCountry - The base country of the shop.47 */48 49 function getPluginData() {50 // PluginData is assumed to have been added to the window object.51 /** @type {Window & { BoxoReturn?: PluginData }} */52 const win = window53 const pluginData = win.BoxoReturn54 if (!pluginData) {55 throw new Error('[BOXO Return] Could not get plugin data')56 }57 return pluginData58 }59 60 /**61 * Check whether Boxo can be used and show or hide the packaging input accordingly.62 */63 async function handleChange() {64 const pluginData = getPluginData()65 66 // If shipping address is different from billing address, use shipping address.67 // Otherwise, use billing address.68 const shippingAddressCheckbox = document.getElementById('ship-to-different-address-checkbox')69 const hasShippingAddress =70 shippingAddressCheckbox instanceof HTMLInputElement && shippingAddressCheckbox.checked71 72 const countryInput = hasShippingAddress73 ? document.getElementById('shipping_country')74 : document.getElementById('billing_country')75 76 // Shipping/billing countries can come from a <select> (multiple countries), a hidden <input> (single country),77 // or can be removed from the page entirely, in which case the shop country is used.78 const country =79 countryInput instanceof HTMLSelectElement || countryInput instanceof HTMLInputElement80 ? countryInput.value81 : pluginData.shopCountry82 if (country !== 'NL') {83 setBoxoUnavailable()84 return85 }86 87 const postalCodeInput = hasShippingAddress88 ? document.getElementById('shipping_postcode')89 : document.getElementById('billing_postcode')90 if (!(postalCodeInput instanceof HTMLInputElement)) {91 console.error('[BOXO Return] Could not get postal code input')92 setBoxoUnavailable()93 return94 }95 96 const postalCode = parsePostalCode(postalCodeInput.value)97 if (!postalCode) {98 setBoxoUnavailable()99 return100 }101 102 if (await checkBoxoAvailable(postalCode)) {103 setBoxoAvailable()104 return105 }106 setBoxoUnavailable()107 }108 109 /**110 * Check whether Boxo is available for the current cart contents and a postal code.111 * @param {string} postalCode112 */113 async function checkBoxoAvailable(postalCode) {114 try {115 const pluginData = getPluginData()116 const url = new URL(pluginData.boxoAvailableUrl)117 url.searchParams.set('postal_code', postalCode)118 url.searchParams.set('products', pluginData.cartProducts)119 url.searchParams.set('item_count', pluginData.cartItemCount)120 121 const ac = new AbortController()122 setTimeout(() => ac.abort('Timeout'), CHECK_AVAILABLE_TIMEOUT_MS)123 const res = await fetch(url.toString(), {signal: ac.signal})124 if (!res.ok) {125 const body = await res.json()126 console.error(`[BOXO Return] ${res.status} ${body.error}`)127 return false128 }129 130 const {available, reason} = await res.json()131 if (typeof available !== 'boolean') {132 console.error('[BOXO Return] Unexpected response')133 return false134 }135 136 if (!available) {137 console.info('[BOXO Return] ' + reason)138 return false139 }140 141 console.info('[BOXO Return] Available')142 return true143 } catch (err) {144 console.error('[BOXO Return]', err)145 return false146 }147 }148 149 function setBoxoAvailable() {150 const availableInput = document.querySelector('input[name=boxo_available]')151 if (!(availableInput instanceof HTMLInputElement)) {152 throw new Error('[BOXO Return] Could not get available input')153 }154 availableInput.value = 'true'155 }156 157 function setBoxoUnavailable() {158 const availableInput = document.querySelector('input[name=boxo_available]')159 if (!(availableInput instanceof HTMLInputElement)) {160 throw new Error('[BOXO Return] Could not get available input')161 }162 availableInput.value = 'false'163 } -
boxo-return/tags/0.0.61/checkout/checkout.php
r3349705 r3371141 6 6 7 7 // Add Boxo features to checkout page after WooCommerce has loaded. 8 add_action('woocommerce_init', 'Boxo_Checkout::init' , 10);8 add_action('woocommerce_init', 'Boxo_Checkout::init'); 9 9 10 10 if (!class_exists('Boxo_Checkout')) { … … 12 12 private const DEFAULT_INFO_URL = 'https://www.boxo.nu'; 13 13 14 /** 15 * @return void 16 */ 14 17 public static function init() { 15 18 // Add css and javascript. 16 19 add_action('wp_enqueue_scripts', 'Boxo_Checkout::enqueue_scripts'); 17 20 18 // Add plugin data: add to head to prevent possible interference from checkout page customizations. 19 add_action('wp_head', 'Boxo_Checkout::add_plugin_data'); 20 21 // Add boxo available field which is updated by the client and read by the server in POST requests. 22 add_action('woocommerce_checkout_before_customer_details', 'Boxo_Checkout::add_boxo_available_field'); 21 // Log plugin version to browser console. 22 add_action('wp_head', 'Boxo_Checkout::log_plugin_version'); 23 23 24 24 // Add packaging field. … … 32 32 } 33 33 34 /** 35 * @return void 36 */ 34 37 public static function enqueue_scripts() { 35 38 if (!Boxo_Checkout::should_load()) { … … 40 43 } 41 44 42 public static function add_plugin_data() { 45 /** 46 * @return void 47 */ 48 public static function log_plugin_version() { 43 49 $boxo_version_safe = BOXO_RETURN_PLUGIN_VERSION; 44 $boxo_available_url_safe = esc_url(rest_url('boxo/available'));45 $cart_products_safe = esc_attr(implode(',', array_map(fn($item) => $item['product_id'], WC()->cart->cart_contents)));46 $cart_item_count_safe = esc_attr(WC()->cart->get_cart_contents_count());47 $shop_country_safe = esc_attr(WC()->countries->get_base_country());48 50 echo <<<HTML 49 51 <script> 50 window.BoxoReturn = { 51 boxoVersion: "$boxo_version_safe", 52 boxoAvailableUrl: "$boxo_available_url_safe", 53 cartProducts: "$cart_products_safe", 54 cartItemCount: "$cart_item_count_safe", 55 shopCountry: "$shop_country_safe", 56 } 52 console.info('[BOXO Return] $boxo_version_safe') 57 53 </script> 58 54 HTML; 59 55 } 60 56 61 public static function add_boxo_available_field() { 62 // Wrapping with div to prevent automatically added p tags. 63 echo <<<HTML 64 <div style="display: contents;"> 65 <input name="boxo_available" type="hidden" value="false"> 66 </div> 67 HTML; 68 } 69 57 /** 58 * @return void 59 */ 70 60 public static function add_packaging_field() { 71 61 if (!Boxo_Checkout::should_load()) { … … 73 63 } 74 64 75 // Only run on POST (particularly AJAX ?wc-ajax=update_order_review), 76 // because boxo_available is not yet known on page load. 77 $post_data = self::get_post_data(); 78 if ( 79 !$post_data || 80 !isset($post_data['boxo_available']) || 81 $post_data['boxo_available'] !== 'true' 82 ) { 83 return; 84 } 65 $result = self::boxo_available_for_request(); 66 if (is_string($result['message'])) { 67 $message_safe = esc_js($result['message']); 68 echo <<<HTML 69 <script>console.info('[BOXO Return] $message_safe')</script> 70 HTML; 71 } 72 if (!$result['available']) { 73 return; 74 } 75 76 $title_html_safe = esc_html__('Packaging', 'boxo-return'); 77 $title_attr_safe = esc_attr__('Packaging', 'boxo-return'); 78 79 $inputs = Boxo_Options::selection_mode() === Boxo_Options::SELECTION_MODE_FORCE_REUSABLE ? 80 self::inputs_force_reusable() 81 : self::inputs_regular(); 82 83 // Based on the HTML for standard shipping inputs in WooCommerce. 84 echo <<<HTML 85 <tr id="boxo_container" class="woocommerce-shipping-totals shipping"> 86 <th class="boxo_header">$title_html_safe</th> 87 <td data-title="$title_attr_safe"> 88 $inputs 89 </td> 90 </tr> 91 HTML; 92 } 93 94 /** 95 * @return string 96 */ 97 private static function inputs_regular() { 98 // Persist current selection or use default. 99 $selected = self::get_selected_packaging(); 100 $checked = is_string($selected) ? $selected : Boxo_Options::default_packaging(); 85 101 86 102 $reusable_attr_safe = esc_attr(Boxo_Constants::PACKAGING_REUSABLE); 87 103 $disposable_attr_safe = esc_attr(Boxo_Constants::PACKAGING_DISPOSABLE); 88 $title_html_safe = esc_html__('Packaging', 'boxo-return');89 $title_attr_safe = esc_attr__('Packaging', 'boxo-return');90 104 $reusable_label_safe = self::reusable_label_safe(); 91 $disposable_label_safe = self::disposable_label_safe(); 92 93 $force_reusable_inputs = <<<HTML 94 $reusable_label_safe 95 <input name="boxo_packaging" value="$reusable_attr_safe" hidden /> 96 HTML; 97 98 // Persist current selection or use default. 99 $selected = isset($post_data['boxo_packaging']) ? $post_data['boxo_packaging'] : Boxo_Options::default_packaging(); 100 $reusable_checked_attr_safe = $selected === Boxo_Constants::PACKAGING_REUSABLE ? 'checked' : ''; 101 $disposable_checked_attr_safe = $selected === Boxo_Constants::PACKAGING_DISPOSABLE ? 'checked' : ''; 102 103 $regular_inputs = <<<HTML 105 $disposable_safe = esc_html__('Disposable', 'boxo-return'); 106 $disposable_fee_formatted_safe = Boxo_Options::disposable_fee_enabled() 107 ? esc_html('+ €' . number_format(Boxo_Options::disposable_fee_cents() / 100, 2, ',', '.')) 108 : ''; 109 $trash_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/trash.svg'); 110 $reusable_checked_attr_safe = $checked === Boxo_Constants::PACKAGING_REUSABLE ? 'checked' : ''; 111 $disposable_checked_attr_safe = $checked === Boxo_Constants::PACKAGING_DISPOSABLE ? 'checked' : ''; 112 113 return <<<HTML 104 114 <ul id="shipping_method" class="woocommerce-shipping-methods"> 105 115 <li class="boxo_list-item-reusable"> … … 126 136 /> 127 137 <label for="boxo_packaging-disposable"> 128 $disposable_label_safe 138 <span class="boxo_option-icon">$trash_icon_safe</span> 139 $disposable_safe 140 $disposable_fee_formatted_safe 129 141 </label> 130 142 </li> 131 143 </ul> 132 144 HTML; 133 134 $inputs = Boxo_Options::selection_mode() === Boxo_Options::SELECTION_MODE_FORCE_REUSABLE ? 135 $force_reusable_inputs 136 : $regular_inputs; 137 138 // Based on the HTML for standard shipping inputs in WooCommerce. 139 echo <<<HTML 140 <tr id="boxo_container" class="woocommerce-shipping-totals shipping"> 141 <th class="boxo_header">$title_html_safe</th> 142 <td data-title="$title_attr_safe"> 143 $inputs 144 </td> 145 </tr> 146 HTML; 147 } 148 145 } 146 147 /** 148 * @return string 149 */ 150 private static function inputs_force_reusable() { 151 $reusable_label_safe = self::reusable_label_safe(); 152 $reusable_attr_safe = esc_attr(Boxo_Constants::PACKAGING_REUSABLE); 153 154 return <<<HTML 155 $reusable_label_safe 156 <input name="boxo_packaging" value="$reusable_attr_safe" hidden /> 157 HTML; 158 } 159 160 /** 161 * @return string 162 */ 149 163 private static function reusable_label_safe() { 150 164 $heart_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/heart.svg'); 151 165 $reusable_safe = esc_html__('Reusable (deposit)', 'boxo-return'); 152 $info_url_safe = esc_url(Boxo_Options::info_url() ?: self::DEFAULT_INFO_URL); 166 $info_url_option = Boxo_Options::info_url(); 167 $info_url_safe = esc_url($info_url_option !== '' ? $info_url_option : self::DEFAULT_INFO_URL); 153 168 $info_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/info.svg'); 154 169 $info_safe = sprintf(esc_html__('Nice, there is a return point near you! Return the packaging at a return point to get your deposit (€%s) back right away. Click for more information.', 'boxo-return'), number_format(Boxo_Options::deposit_cents() / 100, 2, ',', '.')); … … 164 179 } 165 180 166 private static function disposable_label_safe() { 167 $disposable_fee_formatted_safe = Boxo_Options::disposable_fee_enabled() ? esc_html('+ €' . number_format(Boxo_Options::disposable_fee_cents() / 100, 2, ',', '.')) : ''; 168 $trash_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/trash.svg'); 169 $disposable_safe = esc_html__('Disposable', 'boxo-return'); 170 171 return <<<HTML 172 <span class="boxo_option-icon">$trash_icon_safe</span> 173 $disposable_safe 174 $disposable_fee_formatted_safe 175 HTML; 176 } 177 181 /** 182 * @return void 183 */ 178 184 public static function calculate_fees() { 179 185 if (!Boxo_Checkout::should_load()) { … … 181 187 } 182 188 183 // Only run on POST (particularly AJAX ?wc-ajax=update_order_review and checkout submit),184 // because boxo_available is not yet known on page load.185 189 // If Boxo is not available for postal code, do not add any fees. 186 $post_data = self::get_post_data(); 187 if ( 188 !$post_data || 189 !isset($post_data['boxo_available']) || 190 $post_data['boxo_available'] !== 'true' 191 ) { 192 return; 193 } 194 195 $selected = isset($post_data['boxo_packaging']) 196 ? $post_data['boxo_packaging'] 190 if (!self::boxo_available_for_request()['available']) { 191 return; 192 } 193 194 $selected = self::get_selected_packaging(); 195 $packaging = is_string($selected) 196 ? $selected 197 197 : Boxo_Options::default_packaging(); 198 198 199 if ($ selected=== Boxo_Constants::PACKAGING_REUSABLE) {199 if ($packaging === Boxo_Constants::PACKAGING_REUSABLE) { 200 200 WC()->cart->add_fee( 201 201 __('Deposit', 'boxo-return'), … … 219 219 } 220 220 221 /** 222 * @param mixed $order_id 223 * @return void 224 */ 221 225 public static function update_order_meta($order_id) { 222 226 if (!Boxo_Checkout::should_load()) { … … 224 228 } 225 229 226 $post_data = self::get_post_data();227 $boxo_available = isset($post_data['boxo_available']) && $post_data['boxo_available'] === 'true';228 $packaging = $boxo_available ? sanitize_text_field($_POST['boxo_packaging']) : Boxo_Constants::PACKAGING_DISPOSABLE;229 230 230 $order = wc_get_order($order_id); 231 if (is_bool($order)) { 232 return; 233 } 234 235 $selected_packaging = self::get_selected_packaging(); 236 $packaging = self::boxo_available_for_request()['available'] && is_string($selected_packaging) 237 ? sanitize_text_field($selected_packaging) 238 : Boxo_Constants::PACKAGING_DISPOSABLE; 231 239 $order->update_meta_data('boxo_packaging', $packaging); 232 240 $order->save_meta_data(); 233 241 } 234 242 243 /** 244 * @return bool 245 */ 235 246 public static function should_load() { 236 247 // Only load on the actual checkout page, not on the order received page. … … 238 249 } 239 250 240 private static function get_post_data() { 241 // AJAX requests. 242 if (isset($_POST['post_data'])) { 251 /** 252 * @var array{available: bool, message: string|null}|null 253 */ 254 private static $boxo_available_for_request_cached = null; 255 256 /** 257 * Check the availability of BOXO for the current customer and cart. 258 * Results are cached per incoming request. 259 * @return array{available: bool, message: string|null} 260 */ 261 private static function boxo_available_for_request() { 262 if (is_array(self::$boxo_available_for_request_cached)) { 263 return self::$boxo_available_for_request_cached; 264 } 265 266 // WooCommerce handles choice between billing/shipping address internally: 267 // if only billing address is supplied, this will be used as shipping address. 268 $customer = WC()->customer; 269 $postal_code = $customer->get_shipping_postcode(); 270 $country = $customer->get_shipping_country(); 271 $cart_contents_count = WC()->cart->get_cart_contents_count(); 272 273 $cart_product_ids = []; 274 foreach (WC()->cart->get_cart_contents() as $item) { 275 if (!is_array($item) || !isset($item['product_id']) || !is_int($item['product_id'])) { 276 continue; 277 } 278 array_push($cart_product_ids, $item['product_id']); 279 } 280 281 $res = Boxo_Available::boxo_available($postal_code, $country, $cart_contents_count, $cart_product_ids); 282 self::$boxo_available_for_request_cached = $res; 283 return $res; 284 } 285 286 /** 287 * @return string|null 288 */ 289 private static function get_selected_packaging() { 290 if (isset($_POST['post_data']) && is_string($_POST['post_data'])) { 291 // AJAX requests. 243 292 parse_str($_POST['post_data'], $post_data); 244 return $post_data; 245 } 246 // Non-AJAX requests (eg. checkout submit). 247 return $_POST; 293 } else { 294 // Non-AJAX requests (eg. checkout submit). 295 $post_data = $_POST; 296 } 297 return isset($post_data['boxo_packaging']) && is_string($post_data['boxo_packaging']) 298 ? $post_data['boxo_packaging'] 299 : null; 248 300 } 249 301 } -
boxo-return/tags/0.0.61/includes/ajax.php
r3258642 r3371141 13 13 * - keyword: the keyword to search for. If not supplied, all products (within the page limit) will be returned. 14 14 * - skip: a comma-separated list of product IDs that should be skipped. 15 * 16 * @return void 15 17 */ 16 18 public static function handle_product_search() { … … 18 20 check_ajax_referer('boxo'); 19 21 20 $keyword = $_GET['keyword'];21 $skip = $_GET['skip'];22 $keyword = is_string($_GET['keyword']) ? $_GET['keyword'] : ''; 23 $skip = is_string($_GET['skip']) && $_GET['skip'] !== '' ? $_GET['skip'] : null; 22 24 23 25 global $wpdb; 24 $skip_ids = explode(',', $skip); 26 if (!($wpdb instanceof wpdb)) { 27 wp_send_json_error('Internal server error', 500); 28 } 29 30 $skip_ids = is_string($skip) ? explode(',', $skip) : []; 25 31 $skip_ids_safe = implode(",", array_map(fn($id) => esc_sql($id), $skip_ids)); 32 26 33 $query = "SELECT ID FROM {$wpdb->prefix}posts 27 34 WHERE post_type = 'product'" 28 . ($skip_ids_safe ? " AND ID NOT IN ($skip_ids_safe)" : "") .35 . ($skip_ids_safe === '' ? '' : " AND ID NOT IN ($skip_ids_safe)") . 29 36 " AND post_status != 'trash' 30 37 AND post_status != 'auto-draft' … … 33 40 LIMIT %d"; 34 41 $rows = $wpdb->get_results($wpdb->prepare( 42 // @phpstan-ignore argument.type (Since WordPress 6.2.0, this can be solved nicely with %i, but this solution is not used out of backwards compatibility.) 35 43 $query, 36 44 // If keyword is empty, the first items are returned. … … 38 46 self::MAX_ITEMS 39 47 ), ARRAY_A); 48 if (!is_array($rows)) { 49 wp_send_json_error('Internal server error', 500); 50 } 40 51 41 $products = array_map(function ($row) { 52 $products = []; 53 foreach ($rows as $row) { 42 54 $product = wc_get_product($row['ID']); 43 return [ 55 if (!($product instanceof WC_Product)) { 56 continue; 57 } 58 $image_data = wp_get_attachment_image_src( 59 intval($product->get_image_id()), 60 [30, 30], 61 ); 62 array_push($products, [ 44 63 'id' => $product->get_id(), 45 64 'title' => $product->get_title(), 46 'image' => wp_get_attachment_image_src( 47 $product->get_image_id(), 48 [30, 30], 49 )[0], 50 ]; 51 }, $rows); 65 'image' => is_array($image_data) ? $image_data[0] : null, 66 ]); 67 } 52 68 53 69 wp_send_json($products); 54 wp_die();55 70 } 56 71 } -
boxo-return/tags/0.0.61/includes/options.php
r3310810 r3371141 30 30 ]; 31 31 32 /** 33 * @param string $key 34 * @return mixed 35 */ 32 36 private static function option($key) { 33 37 $options = get_option('boxo_options', self::DEFAULT_OPTIONS); 34 if ( isset($options[$key])) {35 return $options[$key];38 if (!is_array($options) || !isset($options[$key])) { 39 return self::DEFAULT_OPTIONS[$key]; 36 40 } 37 return self::DEFAULT_OPTIONS[$key];41 return $options[$key]; 38 42 } 39 43 … … 42 46 */ 43 47 public static function api_key() { 44 return self::option("api_key"); 48 $val = self::option('api_key'); 49 if (!is_string($val)) { 50 return self::DEFAULT_OPTIONS['api_key']; 51 } 52 return $val; 45 53 } 46 54 … … 49 57 */ 50 58 public static function deposit_cents() { 51 return self::option("deposit_cents"); 59 $val = self::option('deposit_cents'); 60 if (!is_string($val) || !is_numeric($val)) { 61 return self::DEFAULT_OPTIONS['deposit_cents']; 62 } 63 return intval($val); 52 64 } 53 65 … … 56 68 */ 57 69 public static function disposable_fee_enabled() { 58 return self::option("disposable_fee_enabled"); 70 $val = self::option('disposable_fee_enabled'); 71 if (!is_bool($val)) { 72 return self::DEFAULT_OPTIONS['disposable_fee_enabled']; 73 } 74 return $val; 59 75 } 60 76 61 77 /** 62 * @return boolOptional fee for disposable packaging in cents. The fee is taxable and the amount is considered to include any taxes.78 * @return int Optional fee for disposable packaging in cents. The fee is taxable and the amount is considered to include any taxes. 63 79 */ 64 80 public static function disposable_fee_cents() { 65 return self::option("disposable_fee_cents"); 81 $val = self::option('disposable_fee_cents'); 82 if (!is_string($val) || !is_numeric($val)) { 83 return self::DEFAULT_OPTIONS['disposable_fee_cents']; 84 } 85 return intval($val); 66 86 } 67 87 … … 73 93 */ 74 94 public static function selection_mode() { 75 return self::option("selection_mode"); 95 $val = self::option('selection_mode'); 96 if (!is_string($val)) { 97 return self::DEFAULT_OPTIONS['selection_mode']; 98 } 99 return $val; 76 100 } 77 101 … … 83 107 */ 84 108 public static function default_packaging() { 85 return self::option("default_packaging"); 109 $val = self::option('default_packaging'); 110 if (!is_string($val)) { 111 return self::DEFAULT_OPTIONS['default_packaging']; 112 } 113 return $val; 86 114 } 87 115 … … 90 118 */ 91 119 public static function info_url() { 92 return self::option("info_url"); 120 $val = self::option('info_url'); 121 if (!is_string($val)) { 122 return self::DEFAULT_OPTIONS['info_url']; 123 } 124 return $val; 93 125 } 94 126 … … 101 133 */ 102 134 public static function product_allow_mode() { 103 return self::option("product_allow_mode"); 135 $val = self::option('product_allow_mode'); 136 if (!is_string($val)) { 137 return self::DEFAULT_OPTIONS['product_allow_mode']; 138 } 139 return $val; 104 140 } 105 141 … … 108 144 */ 109 145 public static function excluded_product_ids() { 110 return self::option("excluded_product_ids"); 146 $val = self::option('excluded_product_ids'); 147 if (!is_string($val)) { 148 return self::DEFAULT_OPTIONS['excluded_product_ids']; 149 } 150 return $val; 111 151 } 112 152 … … 115 155 */ 116 156 public static function included_product_ids() { 117 return self::option("included_product_ids"); 157 $val = self::option('included_product_ids'); 158 if (!is_string($val)) { 159 return self::DEFAULT_OPTIONS['included_product_ids']; 160 } 161 return $val; 118 162 } 119 163 … … 123 167 */ 124 168 public static function cart_limit_enabled() { 125 return self::option("cart_limit_enabled"); 169 $val = self::option('cart_limit_enabled'); 170 if (!is_bool($val)) { 171 return self::DEFAULT_OPTIONS['cart_limit_enabled']; 172 } 173 return $val; 126 174 } 127 175 … … 131 179 */ 132 180 public static function cart_limit() { 133 return self::option("cart_limit"); 181 $val = self::option('cart_limit'); 182 if (!is_string($val) || !is_numeric($val)) { 183 return self::DEFAULT_OPTIONS['cart_limit']; 184 } 185 return intval($val); 134 186 } 135 187 } -
boxo-return/tags/0.0.61/languages/boxo-return-nl.po
r3317144 r3371141 34 34 msgstr "https://www.boxo.nu" 35 35 36 #: admin/admin.php: 5837 #: admin/settings.php: 3636 #: admin/admin.php:72 37 #: admin/settings.php:43 38 38 msgid "Settings" 39 39 msgstr "Instellingen" 40 40 41 #: admin/settings.php: 4341 #: admin/settings.php:50 42 42 msgid "API Key" 43 43 msgstr "API-key" 44 44 45 #: admin/settings.php: 5545 #: admin/settings.php:62 46 46 msgid "Deposit amount in cents" 47 47 msgstr "Statiegeld in aantal cent" 48 48 49 #: admin/settings.php: 6749 #: admin/settings.php:74 50 50 msgid "Disposable packaging fee" 51 51 msgstr "Toeslag wegwerpverpakking" 52 52 53 #: admin/settings.php: 7953 #: admin/settings.php:86 54 54 msgid "Packaging selection" 55 55 msgstr "Verpakking selecteren" 56 56 57 #: admin/settings.php:9 157 #: admin/settings.php:98 58 58 msgid "URL to information page (optional)" 59 59 msgstr "Informatiepagina (optioneel)" 60 60 61 #: admin/settings.php:1 0361 #: admin/settings.php:110 62 62 msgid "Allowed in reusable packaging" 63 63 msgstr "Beschikbaar in herbruikbare verpakking" 64 64 65 #: admin/settings.php:1 4465 #: admin/settings.php:161 66 66 msgid "Enter the API key provided by BOXO Return." 67 67 msgstr "Voer de API-key in die je hebt ontvangen van BOXO Return." 68 68 69 #: admin/admin.php: 3669 #: admin/admin.php:42 70 70 msgid "Settings saved" 71 71 msgstr "Instellingen bijgewerkt" 72 72 73 #: admin/admin.php: 4873 #: admin/admin.php:54 74 74 msgid "Save" 75 75 msgstr "Opslaan" 76 76 77 #: admin/product-picker.php:4 877 #: admin/product-picker.php:47 78 78 msgid "Add a product:" 79 79 msgstr "Product toevoegen:" 80 80 81 #: admin/product-picker.php:5 181 #: admin/product-picker.php:50 82 82 msgid "No products found." 83 83 msgstr "Geen producten gevonden." 84 84 85 #: admin/product-picker.php:5 285 #: admin/product-picker.php:51 86 86 msgid "Remove" 87 87 msgstr "Verwijderen" 88 88 89 #: admin/settings.php:1 7089 #: admin/settings.php:193 90 90 msgid "Enable disposable packaging fee" 91 91 msgstr "Toeslag wegwerpverpakking inschakelen" 92 92 93 #: admin/settings.php:1 7193 #: admin/settings.php:194 94 94 msgid "If enabled, a fee is charged when the customer does not select reusable packaging." 95 95 msgstr "Wanneer deze optie is ingeschakeld wordt er een toeslag gerekend aan klanten die niet voor een herbruikbare verpakking kiezen." 96 96 97 #: admin/settings.php:1 7397 #: admin/settings.php:196 98 98 msgid "Fee in cents:" 99 99 msgstr "Toeslag in aantal cent:" 100 100 101 #: admin/settings.php:2 19101 #: admin/settings.php:245 102 102 msgid "Default selection:" 103 103 msgstr "Standaardselectie:" 104 104 105 #: admin/settings.php:2 20105 #: admin/settings.php:246 106 106 msgid "Customer chooses packaging" 107 107 msgstr "Klant kiest tussen herbruikbaar of wegwerp" 108 108 109 #: admin/order.php:1 28110 #: admin/settings.php:2 22109 #: admin/order.php:185 110 #: admin/settings.php:248 111 111 msgid "Reusable packaging" 112 112 msgstr "Herbruikbare verzendverpakking" 113 113 114 #: admin/order.php:1 38115 #: admin/settings.php:2 23116 #: checkout/checkout.php:2 64114 #: admin/order.php:198 115 #: admin/settings.php:249 116 #: checkout/checkout.php:214 117 117 msgid "Disposable packaging" 118 118 msgstr "Wegwerpverpakking" 119 119 120 #: admin/settings.php:2 50120 #: admin/settings.php:279 121 121 msgid "Will open in a new tab when the customer clicks the information button. Defaults to BOXO Return homepage." 122 122 msgstr "Wordt geopend in een nieuw tabblad wanneer de klant op de informatieknop klikt. Standaard wordt de BOXO Return-homepagina geopend." 123 123 124 #: admin/settings.php: 275124 #: admin/settings.php:307 125 125 msgid "All products" 126 126 msgstr "Alle producten" 127 127 128 #: admin/settings.php: 276128 #: admin/settings.php:308 129 129 msgid "All products except:" 130 130 msgstr "Alle producten behalve:" 131 131 132 #: admin/settings.php: 277132 #: admin/settings.php:309 133 133 msgid "Only these products:" 134 134 msgstr "Alleen specifieke producten:" 135 135 136 #: admin/settings.php:2 21136 #: admin/settings.php:247 137 137 msgid "Always use reusable packaging when possible" 138 138 msgstr "Gebruik altijd herbruikbaar indien mogelijk" 139 139 140 #: admin/product-picker.php:4 9140 #: admin/product-picker.php:48 141 141 msgid "Product name" 142 142 msgstr "Productnaam" 143 143 144 #: admin/order.php: 38145 #: admin/order.php: 80146 #: admin/order.php:1 05147 #: checkout/checkout.php: 195148 #: checkout/checkout.php: 196144 #: admin/order.php:49 145 #: admin/order.php:127 146 #: admin/order.php:159 147 #: checkout/checkout.php:76 148 #: checkout/checkout.php:77 149 149 msgid "Packaging" 150 150 msgstr "Verzendverpakking" 151 151 152 #: admin/order.php: 60152 #: admin/order.php:94 153 153 msgid "Reusable" 154 154 msgstr "Herbruikbaar" 155 155 156 #: admin/order.php: 63157 #: checkout/checkout.php: 220156 #: admin/order.php:97 157 #: checkout/checkout.php:105 158 158 msgid "Disposable" 159 159 msgstr "Wegwerp" 160 160 161 #: checkout/checkout.php:2 52161 #: checkout/checkout.php:201 162 162 msgid "Deposit" 163 163 msgstr "Statiegeld" 164 164 165 #: checkout/checkout.php:289 166 msgid "Select a packaging option." 167 msgstr "Selecteer een verzendverpakking." 168 169 #: admin/settings.php:115 165 #: admin/settings.php:122 170 166 msgid "Cart limit" 171 167 msgstr "Productenlimiet" 172 168 173 #: admin/settings.php:3 17169 #: admin/settings.php:352 174 170 msgid "Enable cart limit" 175 171 msgstr "Activeer productenlimiet" 176 172 177 #: admin/settings.php:3 18173 #: admin/settings.php:353 178 174 msgid "If enabled, reusable packaging will not be available when the cart exceeds the set amount of items." 179 175 msgstr "Wanneer deze optie is ingeschakeld wordt er geen herbruikbare verpakking aangeboden voor orders met meer dan dit aantal items." 180 176 181 #: admin/settings.php:3 20177 #: admin/settings.php:355 182 178 msgid "Max items:" 183 179 msgstr "Max aantal items:" 184 180 185 #: checkout/checkout.php: 202181 #: checkout/checkout.php:165 186 182 msgid "Reusable (deposit)" 187 183 msgstr "Herbruikbaar (statiegeld)" 188 184 189 #: checkout/checkout.php: 205185 #: checkout/checkout.php:169 190 186 msgid "Nice, there is a return point near you! Return the packaging at a return point to get your deposit (€%s) back right away. Click for more information." 191 187 msgstr "Yes, er is een inleverpunt in je buurt! Lever de verpakking in bij een inleverpunt en ontvang direct je statiegeld (€%s) terug. Klik voor meer informatie." -
boxo-return/tags/0.0.61/languages/boxo-return.pot
r3317144 r3371141 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: BOXO Return 0.0. 53\n"5 "Project-Id-Version: BOXO Return 0.0.61\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/boxo-return\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2025- 06-12T14:12:16+00:00\n"12 "POT-Creation-Date: 2025-10-01T13:42:31+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.11.0\n" … … 35 35 msgstr "" 36 36 37 #: admin/admin.php: 3637 #: admin/admin.php:42 38 38 msgid "Settings saved" 39 39 msgstr "" 40 40 41 #: admin/admin.php: 4841 #: admin/admin.php:54 42 42 msgid "Save" 43 43 msgstr "" 44 44 45 #: admin/admin.php: 5846 #: admin/settings.php: 3645 #: admin/admin.php:72 46 #: admin/settings.php:43 47 47 msgid "Settings" 48 48 msgstr "" 49 49 50 #: admin/order.php: 3851 #: admin/order.php: 8052 #: admin/order.php:1 0553 #: checkout/checkout.php: 19554 #: checkout/checkout.php: 19650 #: admin/order.php:49 51 #: admin/order.php:127 52 #: admin/order.php:159 53 #: checkout/checkout.php:76 54 #: checkout/checkout.php:77 55 55 msgid "Packaging" 56 56 msgstr "" 57 57 58 #: admin/order.php: 6058 #: admin/order.php:94 59 59 msgid "Reusable" 60 60 msgstr "" 61 61 62 #: admin/order.php: 6363 #: checkout/checkout.php: 22062 #: admin/order.php:97 63 #: checkout/checkout.php:105 64 64 msgid "Disposable" 65 65 msgstr "" 66 66 67 #: admin/order.php:1 2868 #: admin/settings.php:2 2267 #: admin/order.php:185 68 #: admin/settings.php:248 69 69 msgid "Reusable packaging" 70 70 msgstr "" 71 71 72 #: admin/order.php:1 3873 #: admin/settings.php:2 2374 #: checkout/checkout.php:2 6472 #: admin/order.php:198 73 #: admin/settings.php:249 74 #: checkout/checkout.php:214 75 75 msgid "Disposable packaging" 76 76 msgstr "" 77 77 78 #: admin/product-picker.php:4 878 #: admin/product-picker.php:47 79 79 msgid "Add a product:" 80 80 msgstr "" 81 81 82 #: admin/product-picker.php:4 982 #: admin/product-picker.php:48 83 83 msgid "Product name" 84 84 msgstr "" 85 85 86 #: admin/product-picker.php:5 186 #: admin/product-picker.php:50 87 87 msgid "No products found." 88 88 msgstr "" 89 89 90 #: admin/product-picker.php:5 290 #: admin/product-picker.php:51 91 91 msgid "Remove" 92 92 msgstr "" 93 93 94 #: admin/settings.php: 4394 #: admin/settings.php:50 95 95 msgid "API Key" 96 96 msgstr "" 97 97 98 #: admin/settings.php: 5598 #: admin/settings.php:62 99 99 msgid "Deposit amount in cents" 100 100 msgstr "" 101 101 102 #: admin/settings.php: 67102 #: admin/settings.php:74 103 103 msgid "Disposable packaging fee" 104 104 msgstr "" 105 105 106 #: admin/settings.php: 79106 #: admin/settings.php:86 107 107 msgid "Packaging selection" 108 108 msgstr "" 109 109 110 #: admin/settings.php:9 1110 #: admin/settings.php:98 111 111 msgid "URL to information page (optional)" 112 112 msgstr "" 113 113 114 #: admin/settings.php:1 03114 #: admin/settings.php:110 115 115 msgid "Allowed in reusable packaging" 116 116 msgstr "" 117 117 118 #: admin/settings.php:1 15118 #: admin/settings.php:122 119 119 msgid "Cart limit" 120 120 msgstr "" 121 121 122 #: admin/settings.php:1 44122 #: admin/settings.php:161 123 123 msgid "Enter the API key provided by BOXO Return." 124 124 msgstr "" 125 125 126 #: admin/settings.php:1 70126 #: admin/settings.php:193 127 127 msgid "Enable disposable packaging fee" 128 128 msgstr "" 129 129 130 #: admin/settings.php:1 71130 #: admin/settings.php:194 131 131 msgid "If enabled, a fee is charged when the customer does not select reusable packaging." 132 132 msgstr "" 133 133 134 #: admin/settings.php:1 73134 #: admin/settings.php:196 135 135 msgid "Fee in cents:" 136 136 msgstr "" 137 137 138 #: admin/settings.php:2 19138 #: admin/settings.php:245 139 139 msgid "Default selection:" 140 140 msgstr "" 141 141 142 #: admin/settings.php:2 20142 #: admin/settings.php:246 143 143 msgid "Customer chooses packaging" 144 144 msgstr "" 145 145 146 #: admin/settings.php:2 21146 #: admin/settings.php:247 147 147 msgid "Always use reusable packaging when possible" 148 148 msgstr "" 149 149 150 #: admin/settings.php:2 50150 #: admin/settings.php:279 151 151 msgid "Will open in a new tab when the customer clicks the information button. Defaults to BOXO Return homepage." 152 152 msgstr "" 153 153 154 #: admin/settings.php: 275154 #: admin/settings.php:307 155 155 msgid "All products" 156 156 msgstr "" 157 157 158 #: admin/settings.php: 276158 #: admin/settings.php:308 159 159 msgid "All products except:" 160 160 msgstr "" 161 161 162 #: admin/settings.php: 277162 #: admin/settings.php:309 163 163 msgid "Only these products:" 164 164 msgstr "" 165 165 166 #: admin/settings.php:3 17166 #: admin/settings.php:352 167 167 msgid "Enable cart limit" 168 168 msgstr "" 169 169 170 #: admin/settings.php:3 18170 #: admin/settings.php:353 171 171 msgid "If enabled, reusable packaging will not be available when the cart exceeds the set amount of items." 172 172 msgstr "" 173 173 174 #: admin/settings.php:3 20174 #: admin/settings.php:355 175 175 msgid "Max items:" 176 176 msgstr "" 177 177 178 #: checkout/checkout.php: 202178 #: checkout/checkout.php:165 179 179 msgid "Reusable (deposit)" 180 180 msgstr "" 181 181 182 #: checkout/checkout.php: 205182 #: checkout/checkout.php:169 183 183 msgid "Nice, there is a return point near you! Return the packaging at a return point to get your deposit (€%s) back right away. Click for more information." 184 184 msgstr "" 185 185 186 #: checkout/checkout.php:2 52186 #: checkout/checkout.php:201 187 187 msgid "Deposit" 188 188 msgstr "" 189 190 #: checkout/checkout.php:289191 msgid "Select a packaging option."192 msgstr "" -
boxo-return/tags/0.0.61/readme.txt
r3349705 r3371141 2 2 Contributors: boxodev 3 3 Tags: shipping, packaging 4 Requires at least: 4.74 Requires at least: 6.5 5 5 Tested up to: 6.5.3 6 Stable tag: 0.0.6 07 Requires PHP: 7. 06 Stable tag: 0.0.61 7 Requires PHP: 7.1 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 27 27 == Changelog == 28 28 29 = 0.0.61 = 30 Improve checkout performance and theme support. 31 29 32 = 0.0.60 = 30 33 Remove packaging selection requirement in checkout, which was made redundant by default selection. 34 35 = 0.0.58 = 36 Improve checkout performance. 31 37 32 38 = 0.0.57 = -
boxo-return/trunk/admin/admin.php
r3276747 r3371141 13 13 if (!class_exists('Boxo_Admin')) { 14 14 class Boxo_Admin { 15 /** 16 * @return void 17 */ 15 18 public static function add_menu_item() { 16 19 if (!current_user_can('manage_woocommerce')) { … … 28 31 } 29 32 33 /** 34 * @return void 35 */ 30 36 public static function add_options_page() { 31 37 if (!current_user_can('manage_woocommerce')) { … … 53 59 } 54 60 61 /** 62 * @param mixed $links 63 * @return mixed 64 */ 55 65 public static function add_settings_link($links) { 66 // If filter arg type is incorrect due to external code, just get out of the way and return the arg. 67 if (!is_array($links)) { 68 return $links; 69 } 56 70 $url = admin_url('admin.php?page=boxo-return'); 57 71 $action_links = array( -
boxo-return/trunk/admin/order.php
r3243704 r3371141 31 31 if (!class_exists('Boxo_Order')) { 32 32 class Boxo_Order { 33 /** 34 * @return void 35 */ 33 36 public static function init() { 34 37 wp_enqueue_style('boxo_order', plugins_url('order.css', __FILE__)); 35 38 } 36 39 40 /** 41 * @param mixed $columns 42 * @return mixed 43 */ 37 44 public static function order_list_add_packaging_column($columns) { 45 // If filter arg type is incorrect due to external code, just get out of the way and return the arg. 46 if (!is_array($columns)) { 47 return $columns; 48 } 38 49 $columns['boxo_packaging'] = esc_html__('Packaging', 'boxo-return'); 39 50 return $columns; 40 51 } 41 52 53 /** 54 * @param mixed $column_name 55 * @param mixed $order 56 * @return void 57 */ 42 58 public static function order_list_populate_column_hpos($column_name, $order) { 43 if ($column_name === 'boxo_packaging') { 44 self::populate_column($order); 45 } 46 } 47 59 if ($column_name === 'boxo_packaging' && $order instanceof WC_Order) { 60 $packaging = $order->get_meta('boxo_packaging', true); 61 if (!is_string($packaging)) { 62 return; 63 } 64 self::populate_column($packaging); 65 } 66 } 67 68 /** 69 * @param mixed $column_name 70 * @param mixed $order_id 71 * @return void 72 */ 48 73 public static function order_list_populate_column_cpt($column_name, $order_id) { 49 74 $order = wc_get_order($order_id); 75 if (is_bool($order)) { 76 return; 77 } 50 78 if ($column_name === 'boxo_packaging') { 51 self::populate_column($order); 52 } 53 } 54 55 public static function populate_column($order) { 56 $packaging = $order->get_meta('boxo_packaging'); 57 $cell = ""; 79 $packaging = $order->get_meta('boxo_packaging', true); 80 if (!is_string($packaging)) { 81 return; 82 } 83 self::populate_column($packaging); 84 } 85 } 86 87 /** 88 * @param string $packaging 89 * @return void 90 */ 91 public static function populate_column($packaging) { 58 92 switch ($packaging) { 59 93 case Boxo_Constants::PACKAGING_REUSABLE: 60 $cell =esc_html__('Reusable', 'boxo-return');61 break;94 echo esc_html__('Reusable', 'boxo-return'); 95 return; 62 96 case Boxo_Constants::PACKAGING_DISPOSABLE: 63 $cell =esc_html__('Disposable', 'boxo-return');64 break;97 echo esc_html__('Disposable', 'boxo-return'); 98 return; 65 99 default: 66 100 // If the order was placed when BOXO Return was not active, leave packaging field empty. 67 break; 68 } 69 echo $cell; 70 } 71 101 return; 102 } 103 } 104 105 /** 106 * @param mixed $data 107 * @param mixed $order 108 * @return mixed 109 */ 72 110 public static function order_preview_add_packaging_data($data, $order) { 73 if ($packaging = $order->get_meta('boxo_packaging')) { 74 $data['boxo_packaging'] = $packaging; 75 } 111 // If filter arg type is incorrect due to external code, just get out of the way and return the arg. 112 if (!is_array($data) || !($order instanceof WC_Order)) { 113 return $data; 114 } 115 $packaging = $order->get_meta('boxo_packaging', true); 116 if (!is_string($packaging)) { 117 return $data; 118 } 119 $data['boxo_packaging'] = $packaging; 76 120 return $data; 77 121 } 78 122 123 /** 124 * @return void 125 */ 79 126 public static function order_preview_add_packaging() { 80 127 $title_safe = esc_html__('Packaging', 'boxo-return'); … … 101 148 } 102 149 150 /** 151 * @param mixed $order 152 * @return void 153 */ 103 154 public static function order_detail_add_packaging($order) { 155 if (!($order instanceof WC_Order)) { 156 return; 157 } 104 158 $packaging = $order->get_meta('boxo_packaging', true); 105 159 $title_safe = esc_html__('Packaging', 'boxo-return'); … … 124 178 } 125 179 180 /** 181 * @return string 182 */ 126 183 private static function reusable_label() { 127 184 $heart_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/heart.svg'); … … 135 192 } 136 193 194 /** 195 * @return string 196 */ 137 197 private static function disposable_label() { 138 198 return esc_html__('Disposable packaging', 'boxo-return'); -
boxo-return/trunk/admin/product-picker.php
r3276747 r3371141 23 23 foreach ($ids as $id) { 24 24 $product = wc_get_product($id); 25 if (! $product) {25 if (!($product instanceof WC_Product)) { 26 26 continue; 27 27 } 28 29 $image = wp_get_attachment_image_src( 30 $product->get_image_id(), 28 $image_data = wp_get_attachment_image_src( 29 intval($product->get_image_id()), 31 30 [30, 30], 32 31 ); … … 34 33 'id' => $id, 35 34 'title' => $product->get_title(), 36 'image' => $image ? $image[0] : null,35 'image' => is_array($image_data) ? $image_data[0] : null, 37 36 ]); 38 37 } -
boxo-return/trunk/admin/settings.php
r3310810 r3371141 12 12 13 13 // When saving, make sure checkbox input is saved as boolean. 14 add_filter('pre_update_option_boxo_options', function ($val) { 15 $val['disposable_fee_enabled'] = isset($val['disposable_fee_enabled']) ? true : false; 16 $val['cart_limit_enabled'] = isset($val['cart_limit_enabled']) ? true : false; 17 return $val; 14 add_filter('pre_update_option_boxo_options', function ($vals) { 15 // If filter arg type is incorrect due to external code, just get out of the way and return the arg. 16 if (!is_array($vals)) { 17 return $vals; 18 } 19 $vals['disposable_fee_enabled'] = isset($vals['disposable_fee_enabled']) ? true : false; 20 $vals['cart_limit_enabled'] = isset($vals['cart_limit_enabled']) ? true : false; 21 return $vals; 18 22 }, 10, 1); 19 23 20 24 if (!class_exists('Boxo_Settings')) { 21 25 class Boxo_Settings { 26 /** 27 * @return void 28 */ 22 29 public static function init() { 23 30 if (!current_user_can('manage_woocommerce')) { … … 127 134 } 128 135 136 /** 137 * @return void 138 */ 129 139 public static function add_section_header() { 130 140 // Empty but required. 131 141 } 132 142 143 /** 144 * @param mixed $hook_suffix 145 * @return void 146 */ 133 147 public static function enqueue_scripts($hook_suffix) { 134 // Only load on boxo-return admin page.135 if (! str_contains($hook_suffix, 'boxo-return')) {148 // Only load on admin page for boxo-return. 149 if (!is_string($hook_suffix) || strpos($hook_suffix, 'boxo-return') === false) { 136 150 return; 137 151 } 138 wp_enqueue_script('alpine', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js', null, '3.14.5', true);152 wp_enqueue_script('alpine', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js', [], '3.14.5', true); 139 153 wp_enqueue_script('boxo_product_picker', plugins_url('product-picker.js', __FILE__)); 140 154 } 141 155 156 /** 157 * @return void 158 */ 142 159 public static function add_api_key_field() { 143 160 $value_safe = esc_attr(Boxo_Options::api_key()); … … 157 174 } 158 175 176 /** 177 * @return void 178 */ 159 179 public static function add_deposit_cents_field() { 160 $value_safe = esc_attr( Boxo_Options::deposit_cents());180 $value_safe = esc_attr(strval(Boxo_Options::deposit_cents())); 161 181 echo <<<HTML 162 182 <input id="boxo_deposit-cents-input" name="boxo_options[deposit_cents]" type="number" min="0" max="10000" value="$value_safe"> … … 164 184 } 165 185 186 /** 187 * @return void 188 */ 166 189 public static function add_disposable_fee_enabled_field() { 167 190 $enabled = Boxo_Options::disposable_fee_enabled(); … … 170 193 $label_safe = esc_html__('Enable disposable packaging fee', 'boxo-return'); 171 194 $description_safe = esc_html__('If enabled, a fee is charged when the customer does not select reusable packaging.', 'boxo-return'); 172 $fee_safe = esc_attr( Boxo_Options::disposable_fee_cents());195 $fee_safe = esc_attr(strval(Boxo_Options::disposable_fee_cents())); 173 196 $cents_label_safe = esc_html__('Fee in cents:', 'boxo-return'); 174 197 echo <<<HTML … … 203 226 } 204 227 228 /** 229 * @return void 230 */ 205 231 public static function add_selection_mode_field() { 206 232 $selection_mode = Boxo_Options::selection_mode(); … … 212 238 $disposable_safe = Boxo_Options::DEFAULT_PACKAGING_DISPOSABLE; 213 239 214 $choice_selected_attb_safe = $selection_mode == Boxo_Options::SELECTION_MODE_CHOICE ? 'selected' : '';215 $force_reusable_selected_attb_safe = $selection_mode == Boxo_Options::SELECTION_MODE_FORCE_REUSABLE ? 'selected' : '';216 $reusable_selected_attb_safe = $default_packaging == Boxo_Options::DEFAULT_PACKAGING_REUSABLE ? 'selected' : '';217 $disposable_selected_attb_safe = $default_packaging == Boxo_Options::DEFAULT_PACKAGING_DISPOSABLE ? 'selected' : '';240 $choice_selected_attb_safe = $selection_mode === Boxo_Options::SELECTION_MODE_CHOICE ? 'selected' : ''; 241 $force_reusable_selected_attb_safe = $selection_mode === Boxo_Options::SELECTION_MODE_FORCE_REUSABLE ? 'selected' : ''; 242 $reusable_selected_attb_safe = $default_packaging === Boxo_Options::DEFAULT_PACKAGING_REUSABLE ? 'selected' : ''; 243 $disposable_selected_attb_safe = $default_packaging === Boxo_Options::DEFAULT_PACKAGING_DISPOSABLE ? 'selected' : ''; 218 244 219 245 $default_packaging_label_safe = esc_html__('Default selection:', 'boxo-return'); … … 246 272 } 247 273 274 /** 275 * @return void 276 */ 248 277 public static function add_info_url_field() { 249 278 $value_safe = esc_attr(Boxo_Options::info_url()); … … 262 291 } 263 292 293 /** 294 * @return void 295 */ 264 296 public static function add_product_allow_mode_field() { 265 297 $allow_mode = Boxo_Options::product_allow_mode(); … … 269 301 $included_only_safe = Boxo_Options::PRODUCT_ALLOW_MODE_INCLUDED_ONLY; 270 302 271 $all_selected_attb_safe = $allow_mode == Boxo_Options::PRODUCT_ALLOW_MODE_ALL ? 'selected' : '';272 $all_except_excluded_selected_attb_safe = $allow_mode == Boxo_Options::PRODUCT_ALLOW_MODE_ALL_EXCEPT_EXCLUDED ? 'selected' : '';273 $included_only_selected_attb_safe = $allow_mode == Boxo_Options::PRODUCT_ALLOW_MODE_INCLUDED_ONLY ? 'selected' : '';303 $all_selected_attb_safe = $allow_mode === Boxo_Options::PRODUCT_ALLOW_MODE_ALL ? 'selected' : ''; 304 $all_except_excluded_selected_attb_safe = $allow_mode === Boxo_Options::PRODUCT_ALLOW_MODE_ALL_EXCEPT_EXCLUDED ? 'selected' : ''; 305 $included_only_selected_attb_safe = $allow_mode === Boxo_Options::PRODUCT_ALLOW_MODE_INCLUDED_ONLY ? 'selected' : ''; 274 306 275 307 $all_label_safe = esc_html__('All products', 'boxo-return'); … … 311 343 } 312 344 345 /** 346 * @return void 347 */ 313 348 public static function add_cart_limit_enabled_field() { 314 349 $enabled = Boxo_Options::cart_limit_enabled(); … … 317 352 $label_safe = esc_html__('Enable cart limit', 'boxo-return'); 318 353 $description_safe = esc_html__('If enabled, reusable packaging will not be available when the cart exceeds the set amount of items.', 'boxo-return'); 319 $limit_safe = esc_attr( Boxo_Options::cart_limit());354 $limit_safe = esc_attr(strval(Boxo_Options::cart_limit())); 320 355 $limit_label_safe = esc_html__('Max items:', 'boxo-return'); 321 356 echo <<<HTML -
boxo-return/trunk/boxo-return.php
r3349705 r3371141 2 2 /* 3 3 * Plugin Name: BOXO Return 4 * Version: 0.0.60 4 * Version: 0.0.61 5 * Requires at least: 6.5 6 * Requires PHP: 7.1 5 7 * Description: Allows customers to select reusable packaging during checkout. 6 8 * Author: BOXO … … 20 22 } 21 23 22 const BOXO_RETURN_PLUGIN_VERSION = "0.0.6 0";24 const BOXO_RETURN_PLUGIN_VERSION = "0.0.61"; 23 25 24 26 include plugin_dir_path(__FILE__) . 'includes/constants.php'; 27 include plugin_dir_path(__FILE__) . 'includes/setup.php'; 25 28 include plugin_dir_path(__FILE__) . 'includes/options.php'; 26 include plugin_dir_path(__FILE__) . 'includes/api.php'; 29 include plugin_dir_path(__FILE__) . 'includes/postal-code.php'; 30 include plugin_dir_path(__FILE__) . 'includes/available.php'; 27 31 include plugin_dir_path(__FILE__) . 'includes/ajax.php'; 28 include plugin_dir_path(__FILE__) . 'includes/logger.php';29 32 if (is_admin()) { 30 33 include plugin_dir_path(__FILE__) . 'admin/admin.php'; … … 34 37 } 35 38 include plugin_dir_path(__FILE__) . 'checkout/checkout.php'; 39 40 register_activation_hook(__FILE__, 'Boxo_Setup::handle_activation'); 41 register_deactivation_hook(__FILE__, 'Boxo_Setup::handle_deactivation'); 36 42 37 43 // BOXO Return plugin is incompatible with Checkout Blocks. … … 44 50 add_action('init', 'boxo_load_textdomain'); 45 51 52 /** 53 * @return void 54 */ 46 55 function boxo_load_textdomain() { 47 56 load_plugin_textdomain('boxo-return', false, dirname(plugin_basename(__FILE__)) . '/languages'); -
boxo-return/trunk/checkout/boxo-checkout.js
r3317144 r3371141 1 import {parsePostalCode} from '../utils/utils.js'2 3 const CHECK_AVAILABLE_TIMEOUT_MS = 50004 5 1 if (document.readyState !== 'loading') { 6 2 init() … … 10 6 11 7 async function init() { 12 const pluginData = getPluginData()13 console.info('[BOXO Return] ' + pluginData.boxoVersion)14 15 8 // Must use jQuery for event handlers because propagation of certain events appears to be blocked. 16 9 jQuery(function ($) { 10 // AJAX update checkout (incl. total price) on change in relevant data or packaging selection. 17 11 $(document.body).on( 18 12 'change', 19 '#billing_country, #billing_postcode, #shipping_country, #shipping_postcode, #ship-to-different-address-checkbox ',13 '#billing_country, #billing_postcode, #shipping_country, #shipping_postcode, #ship-to-different-address-checkbox, [name=boxo_packaging]', 20 14 async () => { 21 await handleChange()22 15 document.body.dispatchEvent(new Event('update_checkout')) 23 16 } 24 17 ) 25 26 // AJAX update checkout (incl. total price) on packaging change.27 $(document.body).on('change', '[name=boxo_packaging]', (e) => {28 document.body.dispatchEvent(new Event('update_checkout'))29 })30 18 }) 31 32 // Check Boxo availability on initial load, in case postal code was pre-filled.33 await handleChange()34 document.body.dispatchEvent(new Event('update_checkout'))35 19 36 20 console.info('[BOXO Return] Ready') 37 21 } 38 39 /**40 * Data added to the page by the plugin.41 * @typedef {Object} PluginData42 * @property {string} boxoVersion - The version of the Boxo Return plugin.43 * @property {string} boxoAvailableUrl - The URL of the endpoint where Boxo availability can be checked.44 * @property {string} cartProducts - A comma-separated list of IDs of the products currently in cart.45 * @property {string} cartItemCount - The amount of items currently in cart.46 * @property {string} shopCountry - The base country of the shop.47 */48 49 function getPluginData() {50 // PluginData is assumed to have been added to the window object.51 /** @type {Window & { BoxoReturn?: PluginData }} */52 const win = window53 const pluginData = win.BoxoReturn54 if (!pluginData) {55 throw new Error('[BOXO Return] Could not get plugin data')56 }57 return pluginData58 }59 60 /**61 * Check whether Boxo can be used and show or hide the packaging input accordingly.62 */63 async function handleChange() {64 const pluginData = getPluginData()65 66 // If shipping address is different from billing address, use shipping address.67 // Otherwise, use billing address.68 const shippingAddressCheckbox = document.getElementById('ship-to-different-address-checkbox')69 const hasShippingAddress =70 shippingAddressCheckbox instanceof HTMLInputElement && shippingAddressCheckbox.checked71 72 const countryInput = hasShippingAddress73 ? document.getElementById('shipping_country')74 : document.getElementById('billing_country')75 76 // Shipping/billing countries can come from a <select> (multiple countries), a hidden <input> (single country),77 // or can be removed from the page entirely, in which case the shop country is used.78 const country =79 countryInput instanceof HTMLSelectElement || countryInput instanceof HTMLInputElement80 ? countryInput.value81 : pluginData.shopCountry82 if (country !== 'NL') {83 setBoxoUnavailable()84 return85 }86 87 const postalCodeInput = hasShippingAddress88 ? document.getElementById('shipping_postcode')89 : document.getElementById('billing_postcode')90 if (!(postalCodeInput instanceof HTMLInputElement)) {91 console.error('[BOXO Return] Could not get postal code input')92 setBoxoUnavailable()93 return94 }95 96 const postalCode = parsePostalCode(postalCodeInput.value)97 if (!postalCode) {98 setBoxoUnavailable()99 return100 }101 102 if (await checkBoxoAvailable(postalCode)) {103 setBoxoAvailable()104 return105 }106 setBoxoUnavailable()107 }108 109 /**110 * Check whether Boxo is available for the current cart contents and a postal code.111 * @param {string} postalCode112 */113 async function checkBoxoAvailable(postalCode) {114 try {115 const pluginData = getPluginData()116 const url = new URL(pluginData.boxoAvailableUrl)117 url.searchParams.set('postal_code', postalCode)118 url.searchParams.set('products', pluginData.cartProducts)119 url.searchParams.set('item_count', pluginData.cartItemCount)120 121 const ac = new AbortController()122 setTimeout(() => ac.abort('Timeout'), CHECK_AVAILABLE_TIMEOUT_MS)123 const res = await fetch(url.toString(), {signal: ac.signal})124 if (!res.ok) {125 const body = await res.json()126 console.error(`[BOXO Return] ${res.status} ${body.error}`)127 return false128 }129 130 const {available, reason} = await res.json()131 if (typeof available !== 'boolean') {132 console.error('[BOXO Return] Unexpected response')133 return false134 }135 136 if (!available) {137 console.info('[BOXO Return] ' + reason)138 return false139 }140 141 console.info('[BOXO Return] Available')142 return true143 } catch (err) {144 console.error('[BOXO Return]', err)145 return false146 }147 }148 149 function setBoxoAvailable() {150 const availableInput = document.querySelector('input[name=boxo_available]')151 if (!(availableInput instanceof HTMLInputElement)) {152 throw new Error('[BOXO Return] Could not get available input')153 }154 availableInput.value = 'true'155 }156 157 function setBoxoUnavailable() {158 const availableInput = document.querySelector('input[name=boxo_available]')159 if (!(availableInput instanceof HTMLInputElement)) {160 throw new Error('[BOXO Return] Could not get available input')161 }162 availableInput.value = 'false'163 } -
boxo-return/trunk/checkout/checkout.php
r3349705 r3371141 6 6 7 7 // Add Boxo features to checkout page after WooCommerce has loaded. 8 add_action('woocommerce_init', 'Boxo_Checkout::init' , 10);8 add_action('woocommerce_init', 'Boxo_Checkout::init'); 9 9 10 10 if (!class_exists('Boxo_Checkout')) { … … 12 12 private const DEFAULT_INFO_URL = 'https://www.boxo.nu'; 13 13 14 /** 15 * @return void 16 */ 14 17 public static function init() { 15 18 // Add css and javascript. 16 19 add_action('wp_enqueue_scripts', 'Boxo_Checkout::enqueue_scripts'); 17 20 18 // Add plugin data: add to head to prevent possible interference from checkout page customizations. 19 add_action('wp_head', 'Boxo_Checkout::add_plugin_data'); 20 21 // Add boxo available field which is updated by the client and read by the server in POST requests. 22 add_action('woocommerce_checkout_before_customer_details', 'Boxo_Checkout::add_boxo_available_field'); 21 // Log plugin version to browser console. 22 add_action('wp_head', 'Boxo_Checkout::log_plugin_version'); 23 23 24 24 // Add packaging field. … … 32 32 } 33 33 34 /** 35 * @return void 36 */ 34 37 public static function enqueue_scripts() { 35 38 if (!Boxo_Checkout::should_load()) { … … 40 43 } 41 44 42 public static function add_plugin_data() { 45 /** 46 * @return void 47 */ 48 public static function log_plugin_version() { 43 49 $boxo_version_safe = BOXO_RETURN_PLUGIN_VERSION; 44 $boxo_available_url_safe = esc_url(rest_url('boxo/available'));45 $cart_products_safe = esc_attr(implode(',', array_map(fn($item) => $item['product_id'], WC()->cart->cart_contents)));46 $cart_item_count_safe = esc_attr(WC()->cart->get_cart_contents_count());47 $shop_country_safe = esc_attr(WC()->countries->get_base_country());48 50 echo <<<HTML 49 51 <script> 50 window.BoxoReturn = { 51 boxoVersion: "$boxo_version_safe", 52 boxoAvailableUrl: "$boxo_available_url_safe", 53 cartProducts: "$cart_products_safe", 54 cartItemCount: "$cart_item_count_safe", 55 shopCountry: "$shop_country_safe", 56 } 52 console.info('[BOXO Return] $boxo_version_safe') 57 53 </script> 58 54 HTML; 59 55 } 60 56 61 public static function add_boxo_available_field() { 62 // Wrapping with div to prevent automatically added p tags. 63 echo <<<HTML 64 <div style="display: contents;"> 65 <input name="boxo_available" type="hidden" value="false"> 66 </div> 67 HTML; 68 } 69 57 /** 58 * @return void 59 */ 70 60 public static function add_packaging_field() { 71 61 if (!Boxo_Checkout::should_load()) { … … 73 63 } 74 64 75 // Only run on POST (particularly AJAX ?wc-ajax=update_order_review), 76 // because boxo_available is not yet known on page load. 77 $post_data = self::get_post_data(); 78 if ( 79 !$post_data || 80 !isset($post_data['boxo_available']) || 81 $post_data['boxo_available'] !== 'true' 82 ) { 83 return; 84 } 65 $result = self::boxo_available_for_request(); 66 if (is_string($result['message'])) { 67 $message_safe = esc_js($result['message']); 68 echo <<<HTML 69 <script>console.info('[BOXO Return] $message_safe')</script> 70 HTML; 71 } 72 if (!$result['available']) { 73 return; 74 } 75 76 $title_html_safe = esc_html__('Packaging', 'boxo-return'); 77 $title_attr_safe = esc_attr__('Packaging', 'boxo-return'); 78 79 $inputs = Boxo_Options::selection_mode() === Boxo_Options::SELECTION_MODE_FORCE_REUSABLE ? 80 self::inputs_force_reusable() 81 : self::inputs_regular(); 82 83 // Based on the HTML for standard shipping inputs in WooCommerce. 84 echo <<<HTML 85 <tr id="boxo_container" class="woocommerce-shipping-totals shipping"> 86 <th class="boxo_header">$title_html_safe</th> 87 <td data-title="$title_attr_safe"> 88 $inputs 89 </td> 90 </tr> 91 HTML; 92 } 93 94 /** 95 * @return string 96 */ 97 private static function inputs_regular() { 98 // Persist current selection or use default. 99 $selected = self::get_selected_packaging(); 100 $checked = is_string($selected) ? $selected : Boxo_Options::default_packaging(); 85 101 86 102 $reusable_attr_safe = esc_attr(Boxo_Constants::PACKAGING_REUSABLE); 87 103 $disposable_attr_safe = esc_attr(Boxo_Constants::PACKAGING_DISPOSABLE); 88 $title_html_safe = esc_html__('Packaging', 'boxo-return');89 $title_attr_safe = esc_attr__('Packaging', 'boxo-return');90 104 $reusable_label_safe = self::reusable_label_safe(); 91 $disposable_label_safe = self::disposable_label_safe(); 92 93 $force_reusable_inputs = <<<HTML 94 $reusable_label_safe 95 <input name="boxo_packaging" value="$reusable_attr_safe" hidden /> 96 HTML; 97 98 // Persist current selection or use default. 99 $selected = isset($post_data['boxo_packaging']) ? $post_data['boxo_packaging'] : Boxo_Options::default_packaging(); 100 $reusable_checked_attr_safe = $selected === Boxo_Constants::PACKAGING_REUSABLE ? 'checked' : ''; 101 $disposable_checked_attr_safe = $selected === Boxo_Constants::PACKAGING_DISPOSABLE ? 'checked' : ''; 102 103 $regular_inputs = <<<HTML 105 $disposable_safe = esc_html__('Disposable', 'boxo-return'); 106 $disposable_fee_formatted_safe = Boxo_Options::disposable_fee_enabled() 107 ? esc_html('+ €' . number_format(Boxo_Options::disposable_fee_cents() / 100, 2, ',', '.')) 108 : ''; 109 $trash_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/trash.svg'); 110 $reusable_checked_attr_safe = $checked === Boxo_Constants::PACKAGING_REUSABLE ? 'checked' : ''; 111 $disposable_checked_attr_safe = $checked === Boxo_Constants::PACKAGING_DISPOSABLE ? 'checked' : ''; 112 113 return <<<HTML 104 114 <ul id="shipping_method" class="woocommerce-shipping-methods"> 105 115 <li class="boxo_list-item-reusable"> … … 126 136 /> 127 137 <label for="boxo_packaging-disposable"> 128 $disposable_label_safe 138 <span class="boxo_option-icon">$trash_icon_safe</span> 139 $disposable_safe 140 $disposable_fee_formatted_safe 129 141 </label> 130 142 </li> 131 143 </ul> 132 144 HTML; 133 134 $inputs = Boxo_Options::selection_mode() === Boxo_Options::SELECTION_MODE_FORCE_REUSABLE ? 135 $force_reusable_inputs 136 : $regular_inputs; 137 138 // Based on the HTML for standard shipping inputs in WooCommerce. 139 echo <<<HTML 140 <tr id="boxo_container" class="woocommerce-shipping-totals shipping"> 141 <th class="boxo_header">$title_html_safe</th> 142 <td data-title="$title_attr_safe"> 143 $inputs 144 </td> 145 </tr> 146 HTML; 147 } 148 145 } 146 147 /** 148 * @return string 149 */ 150 private static function inputs_force_reusable() { 151 $reusable_label_safe = self::reusable_label_safe(); 152 $reusable_attr_safe = esc_attr(Boxo_Constants::PACKAGING_REUSABLE); 153 154 return <<<HTML 155 $reusable_label_safe 156 <input name="boxo_packaging" value="$reusable_attr_safe" hidden /> 157 HTML; 158 } 159 160 /** 161 * @return string 162 */ 149 163 private static function reusable_label_safe() { 150 164 $heart_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/heart.svg'); 151 165 $reusable_safe = esc_html__('Reusable (deposit)', 'boxo-return'); 152 $info_url_safe = esc_url(Boxo_Options::info_url() ?: self::DEFAULT_INFO_URL); 166 $info_url_option = Boxo_Options::info_url(); 167 $info_url_safe = esc_url($info_url_option !== '' ? $info_url_option : self::DEFAULT_INFO_URL); 153 168 $info_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/info.svg'); 154 169 $info_safe = sprintf(esc_html__('Nice, there is a return point near you! Return the packaging at a return point to get your deposit (€%s) back right away. Click for more information.', 'boxo-return'), number_format(Boxo_Options::deposit_cents() / 100, 2, ',', '.')); … … 164 179 } 165 180 166 private static function disposable_label_safe() { 167 $disposable_fee_formatted_safe = Boxo_Options::disposable_fee_enabled() ? esc_html('+ €' . number_format(Boxo_Options::disposable_fee_cents() / 100, 2, ',', '.')) : ''; 168 $trash_icon_safe = file_get_contents(WP_PLUGIN_DIR . '/boxo-return/icons/trash.svg'); 169 $disposable_safe = esc_html__('Disposable', 'boxo-return'); 170 171 return <<<HTML 172 <span class="boxo_option-icon">$trash_icon_safe</span> 173 $disposable_safe 174 $disposable_fee_formatted_safe 175 HTML; 176 } 177 181 /** 182 * @return void 183 */ 178 184 public static function calculate_fees() { 179 185 if (!Boxo_Checkout::should_load()) { … … 181 187 } 182 188 183 // Only run on POST (particularly AJAX ?wc-ajax=update_order_review and checkout submit),184 // because boxo_available is not yet known on page load.185 189 // If Boxo is not available for postal code, do not add any fees. 186 $post_data = self::get_post_data(); 187 if ( 188 !$post_data || 189 !isset($post_data['boxo_available']) || 190 $post_data['boxo_available'] !== 'true' 191 ) { 192 return; 193 } 194 195 $selected = isset($post_data['boxo_packaging']) 196 ? $post_data['boxo_packaging'] 190 if (!self::boxo_available_for_request()['available']) { 191 return; 192 } 193 194 $selected = self::get_selected_packaging(); 195 $packaging = is_string($selected) 196 ? $selected 197 197 : Boxo_Options::default_packaging(); 198 198 199 if ($ selected=== Boxo_Constants::PACKAGING_REUSABLE) {199 if ($packaging === Boxo_Constants::PACKAGING_REUSABLE) { 200 200 WC()->cart->add_fee( 201 201 __('Deposit', 'boxo-return'), … … 219 219 } 220 220 221 /** 222 * @param mixed $order_id 223 * @return void 224 */ 221 225 public static function update_order_meta($order_id) { 222 226 if (!Boxo_Checkout::should_load()) { … … 224 228 } 225 229 226 $post_data = self::get_post_data();227 $boxo_available = isset($post_data['boxo_available']) && $post_data['boxo_available'] === 'true';228 $packaging = $boxo_available ? sanitize_text_field($_POST['boxo_packaging']) : Boxo_Constants::PACKAGING_DISPOSABLE;229 230 230 $order = wc_get_order($order_id); 231 if (is_bool($order)) { 232 return; 233 } 234 235 $selected_packaging = self::get_selected_packaging(); 236 $packaging = self::boxo_available_for_request()['available'] && is_string($selected_packaging) 237 ? sanitize_text_field($selected_packaging) 238 : Boxo_Constants::PACKAGING_DISPOSABLE; 231 239 $order->update_meta_data('boxo_packaging', $packaging); 232 240 $order->save_meta_data(); 233 241 } 234 242 243 /** 244 * @return bool 245 */ 235 246 public static function should_load() { 236 247 // Only load on the actual checkout page, not on the order received page. … … 238 249 } 239 250 240 private static function get_post_data() { 241 // AJAX requests. 242 if (isset($_POST['post_data'])) { 251 /** 252 * @var array{available: bool, message: string|null}|null 253 */ 254 private static $boxo_available_for_request_cached = null; 255 256 /** 257 * Check the availability of BOXO for the current customer and cart. 258 * Results are cached per incoming request. 259 * @return array{available: bool, message: string|null} 260 */ 261 private static function boxo_available_for_request() { 262 if (is_array(self::$boxo_available_for_request_cached)) { 263 return self::$boxo_available_for_request_cached; 264 } 265 266 // WooCommerce handles choice between billing/shipping address internally: 267 // if only billing address is supplied, this will be used as shipping address. 268 $customer = WC()->customer; 269 $postal_code = $customer->get_shipping_postcode(); 270 $country = $customer->get_shipping_country(); 271 $cart_contents_count = WC()->cart->get_cart_contents_count(); 272 273 $cart_product_ids = []; 274 foreach (WC()->cart->get_cart_contents() as $item) { 275 if (!is_array($item) || !isset($item['product_id']) || !is_int($item['product_id'])) { 276 continue; 277 } 278 array_push($cart_product_ids, $item['product_id']); 279 } 280 281 $res = Boxo_Available::boxo_available($postal_code, $country, $cart_contents_count, $cart_product_ids); 282 self::$boxo_available_for_request_cached = $res; 283 return $res; 284 } 285 286 /** 287 * @return string|null 288 */ 289 private static function get_selected_packaging() { 290 if (isset($_POST['post_data']) && is_string($_POST['post_data'])) { 291 // AJAX requests. 243 292 parse_str($_POST['post_data'], $post_data); 244 return $post_data; 245 } 246 // Non-AJAX requests (eg. checkout submit). 247 return $_POST; 293 } else { 294 // Non-AJAX requests (eg. checkout submit). 295 $post_data = $_POST; 296 } 297 return isset($post_data['boxo_packaging']) && is_string($post_data['boxo_packaging']) 298 ? $post_data['boxo_packaging'] 299 : null; 248 300 } 249 301 } -
boxo-return/trunk/includes/ajax.php
r3258642 r3371141 13 13 * - keyword: the keyword to search for. If not supplied, all products (within the page limit) will be returned. 14 14 * - skip: a comma-separated list of product IDs that should be skipped. 15 * 16 * @return void 15 17 */ 16 18 public static function handle_product_search() { … … 18 20 check_ajax_referer('boxo'); 19 21 20 $keyword = $_GET['keyword'];21 $skip = $_GET['skip'];22 $keyword = is_string($_GET['keyword']) ? $_GET['keyword'] : ''; 23 $skip = is_string($_GET['skip']) && $_GET['skip'] !== '' ? $_GET['skip'] : null; 22 24 23 25 global $wpdb; 24 $skip_ids = explode(',', $skip); 26 if (!($wpdb instanceof wpdb)) { 27 wp_send_json_error('Internal server error', 500); 28 } 29 30 $skip_ids = is_string($skip) ? explode(',', $skip) : []; 25 31 $skip_ids_safe = implode(",", array_map(fn($id) => esc_sql($id), $skip_ids)); 32 26 33 $query = "SELECT ID FROM {$wpdb->prefix}posts 27 34 WHERE post_type = 'product'" 28 . ($skip_ids_safe ? " AND ID NOT IN ($skip_ids_safe)" : "") .35 . ($skip_ids_safe === '' ? '' : " AND ID NOT IN ($skip_ids_safe)") . 29 36 " AND post_status != 'trash' 30 37 AND post_status != 'auto-draft' … … 33 40 LIMIT %d"; 34 41 $rows = $wpdb->get_results($wpdb->prepare( 42 // @phpstan-ignore argument.type (Since WordPress 6.2.0, this can be solved nicely with %i, but this solution is not used out of backwards compatibility.) 35 43 $query, 36 44 // If keyword is empty, the first items are returned. … … 38 46 self::MAX_ITEMS 39 47 ), ARRAY_A); 48 if (!is_array($rows)) { 49 wp_send_json_error('Internal server error', 500); 50 } 40 51 41 $products = array_map(function ($row) { 52 $products = []; 53 foreach ($rows as $row) { 42 54 $product = wc_get_product($row['ID']); 43 return [ 55 if (!($product instanceof WC_Product)) { 56 continue; 57 } 58 $image_data = wp_get_attachment_image_src( 59 intval($product->get_image_id()), 60 [30, 30], 61 ); 62 array_push($products, [ 44 63 'id' => $product->get_id(), 45 64 'title' => $product->get_title(), 46 'image' => wp_get_attachment_image_src( 47 $product->get_image_id(), 48 [30, 30], 49 )[0], 50 ]; 51 }, $rows); 65 'image' => is_array($image_data) ? $image_data[0] : null, 66 ]); 67 } 52 68 53 69 wp_send_json($products); 54 wp_die();55 70 } 56 71 } -
boxo-return/trunk/includes/options.php
r3310810 r3371141 30 30 ]; 31 31 32 /** 33 * @param string $key 34 * @return mixed 35 */ 32 36 private static function option($key) { 33 37 $options = get_option('boxo_options', self::DEFAULT_OPTIONS); 34 if ( isset($options[$key])) {35 return $options[$key];38 if (!is_array($options) || !isset($options[$key])) { 39 return self::DEFAULT_OPTIONS[$key]; 36 40 } 37 return self::DEFAULT_OPTIONS[$key];41 return $options[$key]; 38 42 } 39 43 … … 42 46 */ 43 47 public static function api_key() { 44 return self::option("api_key"); 48 $val = self::option('api_key'); 49 if (!is_string($val)) { 50 return self::DEFAULT_OPTIONS['api_key']; 51 } 52 return $val; 45 53 } 46 54 … … 49 57 */ 50 58 public static function deposit_cents() { 51 return self::option("deposit_cents"); 59 $val = self::option('deposit_cents'); 60 if (!is_string($val) || !is_numeric($val)) { 61 return self::DEFAULT_OPTIONS['deposit_cents']; 62 } 63 return intval($val); 52 64 } 53 65 … … 56 68 */ 57 69 public static function disposable_fee_enabled() { 58 return self::option("disposable_fee_enabled"); 70 $val = self::option('disposable_fee_enabled'); 71 if (!is_bool($val)) { 72 return self::DEFAULT_OPTIONS['disposable_fee_enabled']; 73 } 74 return $val; 59 75 } 60 76 61 77 /** 62 * @return boolOptional fee for disposable packaging in cents. The fee is taxable and the amount is considered to include any taxes.78 * @return int Optional fee for disposable packaging in cents. The fee is taxable and the amount is considered to include any taxes. 63 79 */ 64 80 public static function disposable_fee_cents() { 65 return self::option("disposable_fee_cents"); 81 $val = self::option('disposable_fee_cents'); 82 if (!is_string($val) || !is_numeric($val)) { 83 return self::DEFAULT_OPTIONS['disposable_fee_cents']; 84 } 85 return intval($val); 66 86 } 67 87 … … 73 93 */ 74 94 public static function selection_mode() { 75 return self::option("selection_mode"); 95 $val = self::option('selection_mode'); 96 if (!is_string($val)) { 97 return self::DEFAULT_OPTIONS['selection_mode']; 98 } 99 return $val; 76 100 } 77 101 … … 83 107 */ 84 108 public static function default_packaging() { 85 return self::option("default_packaging"); 109 $val = self::option('default_packaging'); 110 if (!is_string($val)) { 111 return self::DEFAULT_OPTIONS['default_packaging']; 112 } 113 return $val; 86 114 } 87 115 … … 90 118 */ 91 119 public static function info_url() { 92 return self::option("info_url"); 120 $val = self::option('info_url'); 121 if (!is_string($val)) { 122 return self::DEFAULT_OPTIONS['info_url']; 123 } 124 return $val; 93 125 } 94 126 … … 101 133 */ 102 134 public static function product_allow_mode() { 103 return self::option("product_allow_mode"); 135 $val = self::option('product_allow_mode'); 136 if (!is_string($val)) { 137 return self::DEFAULT_OPTIONS['product_allow_mode']; 138 } 139 return $val; 104 140 } 105 141 … … 108 144 */ 109 145 public static function excluded_product_ids() { 110 return self::option("excluded_product_ids"); 146 $val = self::option('excluded_product_ids'); 147 if (!is_string($val)) { 148 return self::DEFAULT_OPTIONS['excluded_product_ids']; 149 } 150 return $val; 111 151 } 112 152 … … 115 155 */ 116 156 public static function included_product_ids() { 117 return self::option("included_product_ids"); 157 $val = self::option('included_product_ids'); 158 if (!is_string($val)) { 159 return self::DEFAULT_OPTIONS['included_product_ids']; 160 } 161 return $val; 118 162 } 119 163 … … 123 167 */ 124 168 public static function cart_limit_enabled() { 125 return self::option("cart_limit_enabled"); 169 $val = self::option('cart_limit_enabled'); 170 if (!is_bool($val)) { 171 return self::DEFAULT_OPTIONS['cart_limit_enabled']; 172 } 173 return $val; 126 174 } 127 175 … … 131 179 */ 132 180 public static function cart_limit() { 133 return self::option("cart_limit"); 181 $val = self::option('cart_limit'); 182 if (!is_string($val) || !is_numeric($val)) { 183 return self::DEFAULT_OPTIONS['cart_limit']; 184 } 185 return intval($val); 134 186 } 135 187 } -
boxo-return/trunk/languages/boxo-return-nl.po
r3317144 r3371141 34 34 msgstr "https://www.boxo.nu" 35 35 36 #: admin/admin.php: 5837 #: admin/settings.php: 3636 #: admin/admin.php:72 37 #: admin/settings.php:43 38 38 msgid "Settings" 39 39 msgstr "Instellingen" 40 40 41 #: admin/settings.php: 4341 #: admin/settings.php:50 42 42 msgid "API Key" 43 43 msgstr "API-key" 44 44 45 #: admin/settings.php: 5545 #: admin/settings.php:62 46 46 msgid "Deposit amount in cents" 47 47 msgstr "Statiegeld in aantal cent" 48 48 49 #: admin/settings.php: 6749 #: admin/settings.php:74 50 50 msgid "Disposable packaging fee" 51 51 msgstr "Toeslag wegwerpverpakking" 52 52 53 #: admin/settings.php: 7953 #: admin/settings.php:86 54 54 msgid "Packaging selection" 55 55 msgstr "Verpakking selecteren" 56 56 57 #: admin/settings.php:9 157 #: admin/settings.php:98 58 58 msgid "URL to information page (optional)" 59 59 msgstr "Informatiepagina (optioneel)" 60 60 61 #: admin/settings.php:1 0361 #: admin/settings.php:110 62 62 msgid "Allowed in reusable packaging" 63 63 msgstr "Beschikbaar in herbruikbare verpakking" 64 64 65 #: admin/settings.php:1 4465 #: admin/settings.php:161 66 66 msgid "Enter the API key provided by BOXO Return." 67 67 msgstr "Voer de API-key in die je hebt ontvangen van BOXO Return." 68 68 69 #: admin/admin.php: 3669 #: admin/admin.php:42 70 70 msgid "Settings saved" 71 71 msgstr "Instellingen bijgewerkt" 72 72 73 #: admin/admin.php: 4873 #: admin/admin.php:54 74 74 msgid "Save" 75 75 msgstr "Opslaan" 76 76 77 #: admin/product-picker.php:4 877 #: admin/product-picker.php:47 78 78 msgid "Add a product:" 79 79 msgstr "Product toevoegen:" 80 80 81 #: admin/product-picker.php:5 181 #: admin/product-picker.php:50 82 82 msgid "No products found." 83 83 msgstr "Geen producten gevonden." 84 84 85 #: admin/product-picker.php:5 285 #: admin/product-picker.php:51 86 86 msgid "Remove" 87 87 msgstr "Verwijderen" 88 88 89 #: admin/settings.php:1 7089 #: admin/settings.php:193 90 90 msgid "Enable disposable packaging fee" 91 91 msgstr "Toeslag wegwerpverpakking inschakelen" 92 92 93 #: admin/settings.php:1 7193 #: admin/settings.php:194 94 94 msgid "If enabled, a fee is charged when the customer does not select reusable packaging." 95 95 msgstr "Wanneer deze optie is ingeschakeld wordt er een toeslag gerekend aan klanten die niet voor een herbruikbare verpakking kiezen." 96 96 97 #: admin/settings.php:1 7397 #: admin/settings.php:196 98 98 msgid "Fee in cents:" 99 99 msgstr "Toeslag in aantal cent:" 100 100 101 #: admin/settings.php:2 19101 #: admin/settings.php:245 102 102 msgid "Default selection:" 103 103 msgstr "Standaardselectie:" 104 104 105 #: admin/settings.php:2 20105 #: admin/settings.php:246 106 106 msgid "Customer chooses packaging" 107 107 msgstr "Klant kiest tussen herbruikbaar of wegwerp" 108 108 109 #: admin/order.php:1 28110 #: admin/settings.php:2 22109 #: admin/order.php:185 110 #: admin/settings.php:248 111 111 msgid "Reusable packaging" 112 112 msgstr "Herbruikbare verzendverpakking" 113 113 114 #: admin/order.php:1 38115 #: admin/settings.php:2 23116 #: checkout/checkout.php:2 64114 #: admin/order.php:198 115 #: admin/settings.php:249 116 #: checkout/checkout.php:214 117 117 msgid "Disposable packaging" 118 118 msgstr "Wegwerpverpakking" 119 119 120 #: admin/settings.php:2 50120 #: admin/settings.php:279 121 121 msgid "Will open in a new tab when the customer clicks the information button. Defaults to BOXO Return homepage." 122 122 msgstr "Wordt geopend in een nieuw tabblad wanneer de klant op de informatieknop klikt. Standaard wordt de BOXO Return-homepagina geopend." 123 123 124 #: admin/settings.php: 275124 #: admin/settings.php:307 125 125 msgid "All products" 126 126 msgstr "Alle producten" 127 127 128 #: admin/settings.php: 276128 #: admin/settings.php:308 129 129 msgid "All products except:" 130 130 msgstr "Alle producten behalve:" 131 131 132 #: admin/settings.php: 277132 #: admin/settings.php:309 133 133 msgid "Only these products:" 134 134 msgstr "Alleen specifieke producten:" 135 135 136 #: admin/settings.php:2 21136 #: admin/settings.php:247 137 137 msgid "Always use reusable packaging when possible" 138 138 msgstr "Gebruik altijd herbruikbaar indien mogelijk" 139 139 140 #: admin/product-picker.php:4 9140 #: admin/product-picker.php:48 141 141 msgid "Product name" 142 142 msgstr "Productnaam" 143 143 144 #: admin/order.php: 38145 #: admin/order.php: 80146 #: admin/order.php:1 05147 #: checkout/checkout.php: 195148 #: checkout/checkout.php: 196144 #: admin/order.php:49 145 #: admin/order.php:127 146 #: admin/order.php:159 147 #: checkout/checkout.php:76 148 #: checkout/checkout.php:77 149 149 msgid "Packaging" 150 150 msgstr "Verzendverpakking" 151 151 152 #: admin/order.php: 60152 #: admin/order.php:94 153 153 msgid "Reusable" 154 154 msgstr "Herbruikbaar" 155 155 156 #: admin/order.php: 63157 #: checkout/checkout.php: 220156 #: admin/order.php:97 157 #: checkout/checkout.php:105 158 158 msgid "Disposable" 159 159 msgstr "Wegwerp" 160 160 161 #: checkout/checkout.php:2 52161 #: checkout/checkout.php:201 162 162 msgid "Deposit" 163 163 msgstr "Statiegeld" 164 164 165 #: checkout/checkout.php:289 166 msgid "Select a packaging option." 167 msgstr "Selecteer een verzendverpakking." 168 169 #: admin/settings.php:115 165 #: admin/settings.php:122 170 166 msgid "Cart limit" 171 167 msgstr "Productenlimiet" 172 168 173 #: admin/settings.php:3 17169 #: admin/settings.php:352 174 170 msgid "Enable cart limit" 175 171 msgstr "Activeer productenlimiet" 176 172 177 #: admin/settings.php:3 18173 #: admin/settings.php:353 178 174 msgid "If enabled, reusable packaging will not be available when the cart exceeds the set amount of items." 179 175 msgstr "Wanneer deze optie is ingeschakeld wordt er geen herbruikbare verpakking aangeboden voor orders met meer dan dit aantal items." 180 176 181 #: admin/settings.php:3 20177 #: admin/settings.php:355 182 178 msgid "Max items:" 183 179 msgstr "Max aantal items:" 184 180 185 #: checkout/checkout.php: 202181 #: checkout/checkout.php:165 186 182 msgid "Reusable (deposit)" 187 183 msgstr "Herbruikbaar (statiegeld)" 188 184 189 #: checkout/checkout.php: 205185 #: checkout/checkout.php:169 190 186 msgid "Nice, there is a return point near you! Return the packaging at a return point to get your deposit (€%s) back right away. Click for more information." 191 187 msgstr "Yes, er is een inleverpunt in je buurt! Lever de verpakking in bij een inleverpunt en ontvang direct je statiegeld (€%s) terug. Klik voor meer informatie." -
boxo-return/trunk/languages/boxo-return.pot
r3317144 r3371141 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: BOXO Return 0.0. 53\n"5 "Project-Id-Version: BOXO Return 0.0.61\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/boxo-return\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2025- 06-12T14:12:16+00:00\n"12 "POT-Creation-Date: 2025-10-01T13:42:31+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.11.0\n" … … 35 35 msgstr "" 36 36 37 #: admin/admin.php: 3637 #: admin/admin.php:42 38 38 msgid "Settings saved" 39 39 msgstr "" 40 40 41 #: admin/admin.php: 4841 #: admin/admin.php:54 42 42 msgid "Save" 43 43 msgstr "" 44 44 45 #: admin/admin.php: 5846 #: admin/settings.php: 3645 #: admin/admin.php:72 46 #: admin/settings.php:43 47 47 msgid "Settings" 48 48 msgstr "" 49 49 50 #: admin/order.php: 3851 #: admin/order.php: 8052 #: admin/order.php:1 0553 #: checkout/checkout.php: 19554 #: checkout/checkout.php: 19650 #: admin/order.php:49 51 #: admin/order.php:127 52 #: admin/order.php:159 53 #: checkout/checkout.php:76 54 #: checkout/checkout.php:77 55 55 msgid "Packaging" 56 56 msgstr "" 57 57 58 #: admin/order.php: 6058 #: admin/order.php:94 59 59 msgid "Reusable" 60 60 msgstr "" 61 61 62 #: admin/order.php: 6363 #: checkout/checkout.php: 22062 #: admin/order.php:97 63 #: checkout/checkout.php:105 64 64 msgid "Disposable" 65 65 msgstr "" 66 66 67 #: admin/order.php:1 2868 #: admin/settings.php:2 2267 #: admin/order.php:185 68 #: admin/settings.php:248 69 69 msgid "Reusable packaging" 70 70 msgstr "" 71 71 72 #: admin/order.php:1 3873 #: admin/settings.php:2 2374 #: checkout/checkout.php:2 6472 #: admin/order.php:198 73 #: admin/settings.php:249 74 #: checkout/checkout.php:214 75 75 msgid "Disposable packaging" 76 76 msgstr "" 77 77 78 #: admin/product-picker.php:4 878 #: admin/product-picker.php:47 79 79 msgid "Add a product:" 80 80 msgstr "" 81 81 82 #: admin/product-picker.php:4 982 #: admin/product-picker.php:48 83 83 msgid "Product name" 84 84 msgstr "" 85 85 86 #: admin/product-picker.php:5 186 #: admin/product-picker.php:50 87 87 msgid "No products found." 88 88 msgstr "" 89 89 90 #: admin/product-picker.php:5 290 #: admin/product-picker.php:51 91 91 msgid "Remove" 92 92 msgstr "" 93 93 94 #: admin/settings.php: 4394 #: admin/settings.php:50 95 95 msgid "API Key" 96 96 msgstr "" 97 97 98 #: admin/settings.php: 5598 #: admin/settings.php:62 99 99 msgid "Deposit amount in cents" 100 100 msgstr "" 101 101 102 #: admin/settings.php: 67102 #: admin/settings.php:74 103 103 msgid "Disposable packaging fee" 104 104 msgstr "" 105 105 106 #: admin/settings.php: 79106 #: admin/settings.php:86 107 107 msgid "Packaging selection" 108 108 msgstr "" 109 109 110 #: admin/settings.php:9 1110 #: admin/settings.php:98 111 111 msgid "URL to information page (optional)" 112 112 msgstr "" 113 113 114 #: admin/settings.php:1 03114 #: admin/settings.php:110 115 115 msgid "Allowed in reusable packaging" 116 116 msgstr "" 117 117 118 #: admin/settings.php:1 15118 #: admin/settings.php:122 119 119 msgid "Cart limit" 120 120 msgstr "" 121 121 122 #: admin/settings.php:1 44122 #: admin/settings.php:161 123 123 msgid "Enter the API key provided by BOXO Return." 124 124 msgstr "" 125 125 126 #: admin/settings.php:1 70126 #: admin/settings.php:193 127 127 msgid "Enable disposable packaging fee" 128 128 msgstr "" 129 129 130 #: admin/settings.php:1 71130 #: admin/settings.php:194 131 131 msgid "If enabled, a fee is charged when the customer does not select reusable packaging." 132 132 msgstr "" 133 133 134 #: admin/settings.php:1 73134 #: admin/settings.php:196 135 135 msgid "Fee in cents:" 136 136 msgstr "" 137 137 138 #: admin/settings.php:2 19138 #: admin/settings.php:245 139 139 msgid "Default selection:" 140 140 msgstr "" 141 141 142 #: admin/settings.php:2 20142 #: admin/settings.php:246 143 143 msgid "Customer chooses packaging" 144 144 msgstr "" 145 145 146 #: admin/settings.php:2 21146 #: admin/settings.php:247 147 147 msgid "Always use reusable packaging when possible" 148 148 msgstr "" 149 149 150 #: admin/settings.php:2 50150 #: admin/settings.php:279 151 151 msgid "Will open in a new tab when the customer clicks the information button. Defaults to BOXO Return homepage." 152 152 msgstr "" 153 153 154 #: admin/settings.php: 275154 #: admin/settings.php:307 155 155 msgid "All products" 156 156 msgstr "" 157 157 158 #: admin/settings.php: 276158 #: admin/settings.php:308 159 159 msgid "All products except:" 160 160 msgstr "" 161 161 162 #: admin/settings.php: 277162 #: admin/settings.php:309 163 163 msgid "Only these products:" 164 164 msgstr "" 165 165 166 #: admin/settings.php:3 17166 #: admin/settings.php:352 167 167 msgid "Enable cart limit" 168 168 msgstr "" 169 169 170 #: admin/settings.php:3 18170 #: admin/settings.php:353 171 171 msgid "If enabled, reusable packaging will not be available when the cart exceeds the set amount of items." 172 172 msgstr "" 173 173 174 #: admin/settings.php:3 20174 #: admin/settings.php:355 175 175 msgid "Max items:" 176 176 msgstr "" 177 177 178 #: checkout/checkout.php: 202178 #: checkout/checkout.php:165 179 179 msgid "Reusable (deposit)" 180 180 msgstr "" 181 181 182 #: checkout/checkout.php: 205182 #: checkout/checkout.php:169 183 183 msgid "Nice, there is a return point near you! Return the packaging at a return point to get your deposit (€%s) back right away. Click for more information." 184 184 msgstr "" 185 185 186 #: checkout/checkout.php:2 52186 #: checkout/checkout.php:201 187 187 msgid "Deposit" 188 188 msgstr "" 189 190 #: checkout/checkout.php:289191 msgid "Select a packaging option."192 msgstr "" -
boxo-return/trunk/readme.txt
r3349705 r3371141 2 2 Contributors: boxodev 3 3 Tags: shipping, packaging 4 Requires at least: 4.74 Requires at least: 6.5 5 5 Tested up to: 6.5.3 6 Stable tag: 0.0.6 07 Requires PHP: 7. 06 Stable tag: 0.0.61 7 Requires PHP: 7.1 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 27 27 == Changelog == 28 28 29 = 0.0.61 = 30 Improve checkout performance and theme support. 31 29 32 = 0.0.60 = 30 33 Remove packaging selection requirement in checkout, which was made redundant by default selection. 34 35 = 0.0.58 = 36 Improve checkout performance. 31 37 32 38 = 0.0.57 =
Note: See TracChangeset
for help on using the changeset viewer.