Changeset 3764726
- Timestamp:
- 10/25/2026 02:08:28 PM (3 hours ago)
- Location:
- cartnorm-omnibus-lowest-price
- Files:
-
- 14 edited
- 1 copied
-
tags/0.1.2 (copied) (copied from cartnorm-omnibus-lowest-price/trunk )
-
tags/0.1.2/cartnorm-omnibus-lowest-price.php (modified) (2 diffs)
-
tags/0.1.2/includes/class-admin.php (modified) (1 diff)
-
tags/0.1.2/includes/class-compliance-check.php (modified) (1 diff)
-
tags/0.1.2/includes/class-notices.php (modified) (8 diffs)
-
tags/0.1.2/includes/class-settings.php (modified) (2 diffs)
-
tags/0.1.2/languages/cartnorm-omnibus-lowest-price.pot (modified) (10 diffs)
-
tags/0.1.2/readme.txt (modified) (4 diffs)
-
trunk/cartnorm-omnibus-lowest-price.php (modified) (2 diffs)
-
trunk/includes/class-admin.php (modified) (1 diff)
-
trunk/includes/class-compliance-check.php (modified) (1 diff)
-
trunk/includes/class-notices.php (modified) (8 diffs)
-
trunk/includes/class-settings.php (modified) (2 diffs)
-
trunk/languages/cartnorm-omnibus-lowest-price.pot (modified) (10 diffs)
-
trunk/readme.txt (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
cartnorm-omnibus-lowest-price/tags/0.1.2/cartnorm-omnibus-lowest-price.php
r3764542 r3764726 4 4 * Plugin URI: https://cartnorm.com/ 5 5 * Description: Shows the lowest price from the 30 days before a discount, as required by the EU Omnibus Directive. Change-only price history that never bloats your database. 6 * Version: 0.1. 16 * Version: 0.1.2 7 7 * Requires at least: 6.4 8 8 * Requires PHP: 7.4 … … 21 21 defined( 'ABSPATH' ) || exit; 22 22 23 define( 'CARTNORM_VERSION', '0.1. 1' );23 define( 'CARTNORM_VERSION', '0.1.2' ); 24 24 define( 'CARTNORM_FILE', __FILE__ ); 25 25 define( 'CARTNORM_DIR', plugin_dir_path( __FILE__ ) ); -
cartnorm-omnibus-lowest-price/tags/0.1.2/includes/class-admin.php
r3764542 r3764726 138 138 echo '<table class="widefat striped" style="font-size:11px"><thead><tr><th>' . esc_html__( 'From', 'cartnorm-omnibus-lowest-price' ) . '</th><th>' . esc_html__( 'Price', 'cartnorm-omnibus-lowest-price' ) . '</th><th>' . esc_html__( 'Sale', 'cartnorm-omnibus-lowest-price' ) . '</th><th></th></tr></thead><tbody>'; 139 139 foreach ( $rows as $row ) { 140 echo '<tr><td>' . esc_html( wp_date( get_option( 'date_format' ), Clock::from_mysql( $row->valid_from ) ) ) . '</td><td>' . wp_kses_post( wc_price( $row->price ) ) . '</td><td>' . ( (int) $row->on_sale ? '✓' : '' ) . '</td><td>' . Corrections::link( $row ) . '</td></tr>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- link() escapes.140 echo '<tr><td>' . esc_html( wp_date( get_option( 'date_format' ), Clock::from_mysql( $row->valid_from ) ) ) . '</td><td>' . wp_kses_post( wc_price( $row->price ) ) . '</td><td>' . ( (int) $row->on_sale ? '✓' : '' ) . '</td><td>' . wp_kses_post( Corrections::link( $row ) ) . '</td></tr>'; 141 141 } 142 142 echo '</tbody></table>'; -
cartnorm-omnibus-lowest-price/tags/0.1.2/includes/class-compliance-check.php
r3764542 r3764726 169 169 ) 170 170 ) . '</p>'; 171 Notices::render_pro_note(); 171 172 172 173 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only filter. -
cartnorm-omnibus-lowest-price/tags/0.1.2/includes/class-notices.php
r3764542 r3764726 6 6 * - "Import price history": an inline, dismissible box inside this plugin's own 7 7 * settings section, shown only when another Omnibus plugin's history exists. 8 * - A note about the optional Pro add-on: one small inline box inside this 9 * plugin's own settings section and Omnibus check page, for users who can 10 * manage WooCommerce. It can be dismissed for good (per user, works without 11 * JavaScript) and is never shown when the Pro add-on is active. 8 12 * - The history-building status is a plain line in the settings section 9 13 * description (see Settings::history_status()), not a notice. … … 28 32 const FIELD_TYPE = 'cartnorm_notices'; 29 33 const IMPORT_INFIX = 'import_'; 34 const PRO_KEY = 'pro_info'; 35 const PRO_URL = 'https://cartnorm.com/pricing/'; 30 36 31 37 /** … … 95 101 */ 96 102 public static function keys() { 97 $keys = array( );103 $keys = array( self::PRO_KEY ); 98 104 foreach ( array_keys( Importer::source_labels() ) as $source ) { 99 105 $keys[] = self::IMPORT_INFIX . $source; … … 131 137 } 132 138 return true; 139 } 140 141 /** 142 * Whether Cartnorm Omnibus Pro is active. 143 * 144 * @return bool 145 */ 146 public static function pro_active() { 147 /** 148 * Whether Cartnorm Omnibus Pro is active. 149 * 150 * @param bool $active Active. 151 */ 152 return (bool) apply_filters( 'cartnorm_pro_active', defined( 'CARTNORM_PRO_VERSION' ) ); 153 } 154 155 /** 156 * Whether a currency switcher supported by Cartnorm Omnibus Pro is active: 157 * WPML / WooCommerce Multilingual in multi-currency mode, Aelia, CURCY or 158 * FOX (WOOCS). Only checks for loaded classes, functions and globals. 159 * 160 * @return bool 161 */ 162 public static function multi_currency_detected() { 163 $detected = self::wcml_multi_currency() 164 || class_exists( 'WC_Aelia_CurrencySwitcher' ) || has_filter( 'wc_aelia_cs_convert' ) 165 || function_exists( 'wmc_get_price' ) 166 || ( isset( $GLOBALS['WOOCS'] ) && is_object( $GLOBALS['WOOCS'] ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName 167 168 /** 169 * Whether the shop uses a currency switcher supported by Cartnorm Omnibus Pro. 170 * 171 * @param bool $detected Detected. 172 */ 173 return (bool) apply_filters( 'cartnorm_multi_currency_detected', $detected ); 174 } 175 176 /** 177 * WPML / WooCommerce Multilingual with multi-currency switched on. 178 * 179 * @return bool 180 */ 181 private static function wcml_multi_currency() { 182 if ( ! defined( 'WCML_VERSION' ) && ! class_exists( 'woocommerce_wpml' ) ) { 183 return false; 184 } 185 global $woocommerce_wpml; 186 if ( is_object( $woocommerce_wpml ) && isset( $woocommerce_wpml->settings['enable_multi_currency'] ) ) { 187 $independent = defined( 'WCML_MULTI_CURRENCIES_INDEPENDENT' ) ? WCML_MULTI_CURRENCIES_INDEPENDENT : 2; 188 return (int) $woocommerce_wpml->settings['enable_multi_currency'] === (int) $independent; 189 } 190 return true; 191 } 192 193 /** 194 * No-JavaScript dismiss link for a notice. 195 * 196 * @param string $key Notice key. 197 * @return string 198 */ 199 private static function dismiss_link( $key ) { 200 $url = wp_nonce_url( 201 add_query_arg( 202 array( 203 'action' => self::ACTION, 204 'notice' => $key, 205 ), 206 admin_url( 'admin-post.php' ) 207 ), 208 self::ACTION 209 ); 210 return ' <a class="hide-if-js" href="' . esc_url( $url ) . '">' . esc_html__( 'Dismiss', 'cartnorm-omnibus-lowest-price' ) . '</a>'; 211 } 212 213 /** 214 * The note about Cartnorm Omnibus Pro, as HTML. Empty unless the current 215 * user can manage WooCommerce, is on one of this plugin's own screens, has 216 * not dismissed it, and the Pro add-on is not active. 217 * 218 * @return string 219 */ 220 public static function pro_note_html() { 221 if ( ! current_user_can( 'manage_woocommerce' ) || ! self::is_own_screen() || self::pro_active() || self::is_dismissed( self::PRO_KEY ) ) { 222 return ''; 223 } 224 if ( self::multi_currency_detected() ) { 225 $text = __( 'Your shop shows prices in more than one currency. This plugin shows the lowest-price notice in your base currency only; Cartnorm Omnibus Pro shows the correct 30-day lowest price in each currency.', 'cartnorm-omnibus-lowest-price' ); 226 } else { 227 $text = __( 'Optional paid add-on: Cartnorm Omnibus Pro adds multi-currency support, a warning when the regular price was raised before a sale, a monthly email report, Google Shopping feed prices and an EU withdrawal button.', 'cartnorm-omnibus-lowest-price' ); 228 } 229 return '<div class="notice notice-info inline is-dismissible cartnorm-notice cartnorm-pro-note" data-cartnorm-notice="' . esc_attr( self::PRO_KEY ) . '"><p>' 230 . esc_html( $text ) 231 . ' <a href="' . esc_url( self::PRO_URL ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Learn more', 'cartnorm-omnibus-lowest-price' ) . '</a>' 232 . self::dismiss_link( self::PRO_KEY ) 233 . '</p></div>'; 234 } 235 236 /** 237 * Print the Pro note (Omnibus check page). 238 */ 239 public static function render_pro_note() { 240 echo wp_kses( self::pro_note_html(), Display::allowed_html() ); 133 241 } 134 242 … … 171 279 } 172 280 173 $dismiss_url = wp_nonce_url(174 add_query_arg(175 array(176 'action' => self::ACTION,177 'notice' => $key,178 ),179 admin_url( 'admin-post.php' )180 ),181 self::ACTION182 );183 281 $out .= '<div class="notice notice-info inline is-dismissible cartnorm-notice" data-cartnorm-notice="' . esc_attr( $key ) . '"><p>' 184 282 . esc_html( $text ) . ' ' . $button 185 . ' <a class="hide-if-js" href="' . esc_url( $dismiss_url ) . '">' . esc_html__( 'Dismiss', 'cartnorm-omnibus-lowest-price' ) . '</a>'283 . self::dismiss_link( $key ) 186 284 . '</p></div>'; 187 285 } 188 return $out ;286 return $out . self::pro_note_html(); 189 287 } 190 288 … … 200 298 return; 201 299 } 202 echo '<tr class="cartnorm-notices-row"><td colspan="2" style="padding:0 0 8px">' . $html . '</td></tr>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped in html().203 } 204 205 /** 206 * Script that stores the dismissal when the core dismiss button is clicked .207 * Loaded only on this plugin's own screens.300 echo '<tr class="cartnorm-notices-row"><td colspan="2" style="padding:0 0 8px">' . wp_kses( $html, Display::allowed_html() ) . '</td></tr>'; 301 } 302 303 /** 304 * Script that stores the dismissal when the core dismiss button is clicked, 305 * and a one-line style for the box. Loaded only on this plugin's own screens. 208 306 */ 209 307 public static function enqueue() { … … 213 311 $script = '(function(){document.addEventListener("click",function(e){var b=e.target.closest?e.target.closest(".cartnorm-notice .notice-dismiss"):null;if(!b){return;}var n=b.closest(".cartnorm-notice");var d=new FormData();d.append("action",' . wp_json_encode( self::ACTION ) . ');d.append("notice",n.getAttribute("data-cartnorm-notice"));d.append("_ajax_nonce",' . wp_json_encode( wp_create_nonce( self::ACTION ) ) . ');fetch(' . wp_json_encode( admin_url( 'admin-ajax.php' ) ) . ',{method:"POST",credentials:"same-origin",body:d});});})();'; 214 312 wp_add_inline_script( 'common', $script ); 313 // WooCommerce screens override the core padding that keeps text clear of the dismiss button. 314 wp_add_inline_style( 'common', '.wp-core-ui .cartnorm-notice.notice.is-dismissible{padding-right:48px}' ); 215 315 } 216 316 … … 248 348 } 249 349 self::dismiss( self::requested_key() ); 250 wp_safe_redirect( Settings::url() ); 350 $back = wp_get_referer(); 351 wp_safe_redirect( $back ? $back : Settings::url() ); 251 352 exit; 252 353 } -
cartnorm-omnibus-lowest-price/tags/0.1.2/includes/class-settings.php
r3764542 r3764726 80 80 add_filter( 'woocommerce_get_settings_products', array( __CLASS__, 'add_settings' ), 10, 2 ); 81 81 add_filter( 'plugin_action_links_' . plugin_basename( CARTNORM_FILE ), array( __CLASS__, 'action_links' ) ); 82 add_filter( 'plugin_row_meta', array( __CLASS__, 'row_meta' ), 10, 2 ); 82 83 } 83 84 … … 98 99 */ 99 100 public static function action_links( $links ) { 100 array_unshift( $links, '<a href="' . esc_url( self::url() ) . '">' . esc_html__( 'Settings', 'cartnorm-omnibus-lowest-price' ) . '</a>' ); 101 array_unshift( $links, Display::kses( '<a href="' . esc_url( self::url() ) . '">' . esc_html__( 'Settings', 'cartnorm-omnibus-lowest-price' ) . '</a>' ) ); 102 return $links; 103 } 104 105 /** 106 * Add a "Pro version" link to this plugin's row on the Plugins screen 107 * (only for this plugin, and not when the Pro add-on is active). 108 * 109 * @param array $links Row meta links. 110 * @param string $file Plugin file. 111 * @return array 112 */ 113 public static function row_meta( $links, $file ) { 114 if ( plugin_basename( CARTNORM_FILE ) !== $file || Notices::pro_active() ) { 115 return $links; 116 } 117 $links[] = Display::kses( '<a href="' . esc_url( Notices::PRO_URL ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Pro version', 'cartnorm-omnibus-lowest-price' ) . '</a>' ); 101 118 return $links; 102 119 } -
cartnorm-omnibus-lowest-price/tags/0.1.2/languages/cartnorm-omnibus-lowest-price.pot
r3764542 r3764726 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Cartnorm Omnibus Lowest Price 0.1. 1\n"5 "Project-Id-Version: Cartnorm Omnibus Lowest Price 0.1.2\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/cartnorm-omnibus-lowest-price\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: 2026-10-25T 08:45:23+00:00\n"12 "POT-Creation-Date: 2026-10-25T14:03:29+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" 15 15 "X-Domain: cartnorm-omnibus-lowest-price\n" 16 16 17 #: blocks/lowest-price/block.json 18 msgctxt "block title" 19 msgid "Lowest price (Omnibus)" 20 msgstr "" 21 22 #: blocks/lowest-price/block.json 23 msgctxt "block description" 24 msgid "Shows the lowest price of the product in the 30 days before the discount." 25 msgstr "" 26 27 #: blocks/lowest-price/block.json 28 msgctxt "block keyword" 29 msgid "omnibus" 30 msgstr "" 31 32 #: blocks/lowest-price/block.json 33 msgctxt "block keyword" 34 msgid "lowest price" 35 msgstr "" 36 37 #: blocks/lowest-price/block.json 38 msgctxt "block keyword" 39 msgid "30 days" 40 msgstr "" 41 42 #: blocks/lowest-price/block.json 43 msgctxt "block keyword" 44 msgid "price history" 45 msgstr "" 46 17 47 #. Plugin Name of the plugin 18 48 #: cartnorm-omnibus-lowest-price.php … … 43 73 msgstr "" 44 74 45 #: includes/class-admin.php:68 includes/class-settings.php:111 75 #: includes/class-admin.php:68 76 #: includes/class-settings.php:128 46 77 #: includes/integrations/class-elementor-widget.php:36 47 78 msgid "Lowest price (Omnibus)" … … 102 133 msgstr "" 103 134 104 #: includes/class-admin.php:161 includes/class-corrections.php:218 105 #: includes/class-importer.php:337 includes/class-notices.php:247 135 #: includes/class-admin.php:161 136 #: includes/class-corrections.php:218 137 #: includes/class-importer.php:337 138 #: includes/class-notices.php:347 106 139 msgid "You are not allowed to do this." 107 140 msgstr "" … … 151 184 msgstr "" 152 185 153 #: includes/class-compliance-check.php:19 8186 #: includes/class-compliance-check.php:199 154 187 msgid "No products are reduced at the moment." 155 188 msgstr "" 156 189 157 #: includes/class-compliance-check.php:20 3190 #: includes/class-compliance-check.php:204 158 191 #: includes/integrations/class-elementor-widget.php:72 159 192 msgid "Product" 160 193 msgstr "" 161 194 162 #: includes/class-compliance-check.php:20 3195 #: includes/class-compliance-check.php:204 163 196 msgid "Status" 164 197 msgstr "" 165 198 166 #: includes/class-compliance-check.php:20 3199 #: includes/class-compliance-check.php:204 167 200 msgid "Sale price" 168 201 msgstr "" 169 202 170 #: includes/class-compliance-check.php:20 3203 #: includes/class-compliance-check.php:204 171 204 msgid "Lowest price, 30 days before" 172 205 msgstr "" 173 206 174 #: includes/class-compliance-check.php:20 3207 #: includes/class-compliance-check.php:204 175 208 msgid "Sale started" 176 209 msgstr "" 177 210 178 #: includes/class-corrections.php:35 includes/class-corrections.php:69 211 #: includes/class-corrections.php:35 212 #: includes/class-corrections.php:69 179 213 msgid "Correct price record" 180 214 msgstr "" … … 184 218 msgstr "" 185 219 186 #: includes/class-corrections.php:78 includes/class-corrections.php:130 220 #: includes/class-corrections.php:78 221 #: includes/class-corrections.php:130 187 222 #: includes/class-corrections.php:154 188 223 msgid "This record does not exist." … … 199 234 msgstr "" 200 235 201 #: includes/class-corrections.php:97 includes/class-corrections.php:131 236 #: includes/class-corrections.php:97 237 #: includes/class-corrections.php:131 202 238 #: includes/class-corrections.php:157 203 239 msgid "The current price cannot be corrected here. Change it in the product instead." … … 236 272 msgstr "" 237 273 238 #: includes/class-corrections.php:129 includes/class-corrections.php:148 274 #: includes/class-corrections.php:129 275 #: includes/class-corrections.php:148 239 276 msgid "Please give a reason for the correction." 240 277 msgstr "" 241 278 242 #: includes/class-corrections.php:132 includes/class-corrections.php:160 279 #: includes/class-corrections.php:132 280 #: includes/class-corrections.php:160 243 281 msgid "Please enter a valid price." 244 282 msgstr "" 245 283 284 #: includes/class-country-rules.php:64 285 msgid "EU default rules" 286 msgstr "" 287 246 288 #: includes/class-country-rules.php:65 247 msgid " EU default rules"289 msgid "Austria" 248 290 msgstr "" 249 291 250 292 #: includes/class-country-rules.php:66 251 msgid " Austria"293 msgid "Belgium" 252 294 msgstr "" 253 295 254 296 #: includes/class-country-rules.php:67 255 msgid " Belgium"297 msgid "Czech Republic" 256 298 msgstr "" 257 299 258 300 #: includes/class-country-rules.php:68 259 msgid " Czech Republic"301 msgid "France" 260 302 msgstr "" 261 303 262 304 #: includes/class-country-rules.php:69 263 msgid " France"305 msgid "Germany" 264 306 msgstr "" 265 307 266 308 #: includes/class-country-rules.php:70 267 msgid " Germany"309 msgid "Ireland" 268 310 msgstr "" 269 311 270 312 #: includes/class-country-rules.php:71 271 msgid "I reland"313 msgid "Italy" 272 314 msgstr "" 273 315 274 316 #: includes/class-country-rules.php:72 275 msgid " Italy"317 msgid "Netherlands" 276 318 msgstr "" 277 319 278 320 #: includes/class-country-rules.php:73 279 msgid " Netherlands"321 msgid "Poland" 280 322 msgstr "" 281 323 282 324 #: includes/class-country-rules.php:74 283 msgid "Po land"325 msgid "Portugal" 284 326 msgstr "" 285 327 286 328 #: includes/class-country-rules.php:75 287 msgid " Portugal"329 msgid "Romania" 288 330 msgstr "" 289 331 290 332 #: includes/class-country-rules.php:76 291 msgid " Romania"333 msgid "Spain" 292 334 msgstr "" 293 335 294 336 #: includes/class-country-rules.php:77 295 msgid "Spain"296 msgstr ""297 298 #: includes/class-country-rules.php:78299 337 msgid "Sweden" 300 338 msgstr "" 301 339 302 #: includes/class-country-rules.php: 80340 #: includes/class-country-rules.php:79 303 341 msgid "Automatic (store country)" 304 342 msgstr "" 305 343 306 #: includes/class-notices.php: 88344 #: includes/class-notices.php:94 307 345 msgid "Cartnorm Omnibus Lowest Price needs WooCommerce to be installed and active." 308 346 msgstr "" 309 347 348 #: includes/class-notices.php:210 349 msgid "Dismiss" 350 msgstr "" 351 352 #: includes/class-notices.php:225 353 msgid "Your shop shows prices in more than one currency. This plugin shows the lowest-price notice in your base currency only; Cartnorm Omnibus Pro shows the correct 30-day lowest price in each currency." 354 msgstr "" 355 356 #: includes/class-notices.php:227 357 msgid "Optional paid add-on: Cartnorm Omnibus Pro adds multi-currency support, a warning when the regular price was raised before a sale, a monthly email report, Google Shopping feed prices and an EU withdrawal button." 358 msgstr "" 359 360 #: includes/class-notices.php:231 361 msgid "Learn more" 362 msgstr "" 363 310 364 #. translators: %s: name of another plugin 311 #: includes/class-notices.php: 153365 #: includes/class-notices.php:261 312 366 #, php-format 313 367 msgid "Importing price history from %s in the background." 314 368 msgstr "" 315 369 316 #: includes/class-notices.php: 160370 #: includes/class-notices.php:268 317 371 msgid "Import price history" 318 372 msgstr "" 319 373 320 374 #. translators: %s: name of another plugin 321 #: includes/class-notices.php: 163375 #: includes/class-notices.php:271 322 376 #, php-format 323 377 msgid "Price history from %s was found. Import it to keep your full 30-day history after switching." 324 378 msgstr "" 325 379 326 #: includes/class-notices.php:185 327 msgid "Dismiss" 328 msgstr "" 329 330 #: includes/class-settings.php:100 380 #: includes/class-settings.php:101 331 381 msgid "Settings" 332 382 msgstr "" 333 383 384 #: includes/class-settings.php:117 385 msgid "Pro version" 386 msgstr "" 387 334 388 #. translators: 1: number of stored price changes, 2: date tracking started 335 #: includes/class-settings.php:1 31389 #: includes/class-settings.php:148 336 390 #, php-format 337 391 msgid "Price history: %1$s stored price changes since %2$s. Rows are written only when a price changes." 338 392 msgstr "" 339 393 340 #: includes/class-settings.php:1 41394 #: includes/class-settings.php:158 341 395 msgid "Lowest price before a discount (EU Omnibus Directive)" 342 396 msgstr "" 343 397 344 #: includes/class-settings.php:1 43398 #: includes/class-settings.php:160 345 399 msgid "When a price is reduced, EU law requires showing the lowest price of the product in the 30 days before the reduction, and calculating any strikethrough price or percentage from that price." 346 400 msgstr "" 347 401 348 #: includes/class-settings.php:1 51402 #: includes/class-settings.php:168 349 403 msgid "Enable" 350 404 msgstr "" 351 405 352 #: includes/class-settings.php:1 52406 #: includes/class-settings.php:169 353 407 msgid "Show the lowest price information in the shop" 354 408 msgstr "" 355 409 356 #: includes/class-settings.php:1 58410 #: includes/class-settings.php:175 357 411 msgid "Show on" 358 412 msgstr "" 359 413 360 #: includes/class-settings.php:1 59414 #: includes/class-settings.php:176 361 415 msgid "Single product pages (including each variation)" 362 416 msgstr "" 363 417 364 #: includes/class-settings.php:1 66418 #: includes/class-settings.php:183 365 419 msgid "Shop, category and other product lists" 366 420 msgstr "" 367 421 368 #: includes/class-settings.php:1 73422 #: includes/class-settings.php:190 369 423 msgid "Position on product pages" 370 424 msgstr "" 371 425 372 #: includes/class-settings.php:1 77426 #: includes/class-settings.php:194 373 427 msgid "If your theme or page builder shows prices in an unusual way, choose another position, or \"Nowhere\" and place the block, shortcode [cartnorm_lowest_price] or Elementor widget yourself." 374 428 msgstr "" 375 429 376 #: includes/class-settings.php:1 79430 #: includes/class-settings.php:196 377 431 msgid "Right under the price (recommended)" 378 432 msgstr "" 379 433 380 #: includes/class-settings.php:1 80434 #: includes/class-settings.php:197 381 435 msgid "Under the short description (classic themes)" 382 436 msgstr "" 383 437 384 #: includes/class-settings.php:1 81438 #: includes/class-settings.php:198 385 439 msgid "Under the add-to-cart button (classic themes)" 386 440 msgstr "" 387 441 388 #: includes/class-settings.php:1 82442 #: includes/class-settings.php:199 389 443 msgid "Nowhere (I place it myself)" 390 444 msgstr "" 391 445 392 #: includes/class-settings.php: 186446 #: includes/class-settings.php:203 393 447 msgid "Show for" 394 448 msgstr "" 395 449 396 #: includes/class-settings.php: 191450 #: includes/class-settings.php:208 397 451 msgid "Reduced products only (what the law requires)" 398 452 msgstr "" 399 453 400 #: includes/class-settings.php: 192454 #: includes/class-settings.php:209 401 455 msgid "All products (lowest price in the last 30 days)" 402 456 msgstr "" 403 457 404 #: includes/class-settings.php: 196458 #: includes/class-settings.php:213 405 459 msgid "Strikethrough price" 406 460 msgstr "" 407 461 408 #: includes/class-settings.php:2 00462 #: includes/class-settings.php:217 409 463 msgid "The CJEU (case C-330/23) ruled that an announced reduction must be calculated from the lowest price of the previous 30 days." 410 464 msgstr "" 411 465 412 #: includes/class-settings.php:2 02466 #: includes/class-settings.php:219 413 467 msgid "Lowest price of the 30 days before the reduction (recommended)" 414 468 msgstr "" 415 469 416 #: includes/class-settings.php:2 03470 #: includes/class-settings.php:220 417 471 msgid "Regular price (WooCommerce default)" 418 472 msgstr "" 419 473 420 #: includes/class-settings.php:2 07474 #: includes/class-settings.php:224 421 475 msgid "Sale badge" 422 476 msgstr "" 423 477 424 #: includes/class-settings.php:2 12478 #: includes/class-settings.php:229 425 479 msgid "Theme default (\"Sale!\")" 426 480 msgstr "" 427 481 428 #: includes/class-settings.php:2 13482 #: includes/class-settings.php:230 429 483 msgid "Percentage calculated from the 30-day lowest price, rounded down (e.g. \"-20%\")" 430 484 msgstr "" 431 485 432 #: includes/class-settings.php:2 17486 #: includes/class-settings.php:234 433 487 msgid "Fake discount guard" 434 488 msgstr "" 435 489 436 #: includes/class-settings.php:2 18490 #: includes/class-settings.php:235 437 491 msgid "Hide the strikethrough price, the sale badge and the notice when the sale price is not actually below the 30-day lowest price" 438 492 msgstr "" 439 493 440 #: includes/class-settings.php:2 24494 #: includes/class-settings.php:241 441 495 msgid "Country rules" 442 496 msgstr "" 443 497 444 #: includes/class-settings.php:2 29498 #: includes/class-settings.php:246 445 499 msgid "Member states differ in details such as progressive reductions and new products. Choose the country whose law applies to your shop." 446 500 msgstr "" 447 501 448 #: includes/class-settings.php:2 32502 #: includes/class-settings.php:249 449 503 msgid "Progressive reductions" 450 504 msgstr "" 451 505 452 #: includes/class-settings.php:2 36506 #: includes/class-settings.php:253 453 507 msgid "Whether uninterrupted, increasing reductions keep the reference price of the first reduction." 454 508 msgstr "" 455 509 456 #: includes/class-settings.php:2 38510 #: includes/class-settings.php:255 457 511 msgid "Follow the country rules" 458 512 msgstr "" 459 513 460 #: includes/class-settings.php:2 39514 #: includes/class-settings.php:256 461 515 msgid "Never (each further reduction gets a new reference)" 462 516 msgstr "" 463 517 464 #: includes/class-settings.php:2 40518 #: includes/class-settings.php:257 465 519 msgid "Always (one uninterrupted campaign)" 466 520 msgstr "" 467 521 468 #: includes/class-settings.php:2 44522 #: includes/class-settings.php:261 469 523 msgid "When history is too short" 470 524 msgstr "" 471 525 472 #: includes/class-settings.php:2 48526 #: includes/class-settings.php:265 473 527 msgid "Used while the plugin is still building history, for example right after installation." 474 528 msgstr "" 475 529 476 #: includes/class-settings.php:2 50530 #: includes/class-settings.php:267 477 531 msgid "Use the regular price" 478 532 msgstr "" 479 533 480 #: includes/class-settings.php:2 51534 #: includes/class-settings.php:268 481 535 msgid "Use the lowest price recorded so far" 482 536 msgstr "" 483 537 484 #: includes/class-settings.php:2 52538 #: includes/class-settings.php:269 485 539 msgid "Show nothing" 486 540 msgstr "" 487 541 488 #: includes/class-settings.php:2 56542 #: includes/class-settings.php:273 489 543 msgid "Look-back period (days)" 490 544 msgstr "" 491 545 492 #: includes/class-settings.php:2 61546 #: includes/class-settings.php:278 493 547 msgid "At least 30 days. Some member states allow or require longer periods in specific cases." 494 548 msgstr "" 495 549 496 #: includes/class-settings.php:2 64550 #: includes/class-settings.php:281 497 551 msgid "Custom text" 498 552 msgstr "" 499 553 500 #: includes/class-settings.php:2 69554 #: includes/class-settings.php:286 501 555 msgid "Leave empty to use the default text in the shop language (built in for German, Polish, French, Italian, Spanish and Dutch). Use {price} for the price and {days} for the number of days." 502 556 msgstr "" 503 557 504 #: includes/class-settings.php:2 72558 #: includes/class-settings.php:289 505 559 msgid "Keep history for (days)" 506 560 msgstr "" 507 561 508 #: includes/class-settings.php:2 77562 #: includes/class-settings.php:294 509 563 msgid "Older price changes are removed automatically. The price that was valid at the start of the look-back window is always kept." 510 564 msgstr "" 511 565 512 566 #. translators: 1: number of days of the look-back period, 2: number of days left 513 #: includes/class-settings.php:3 02567 #: includes/class-settings.php:319 514 568 #, php-format 515 569 msgid "The full %1$d-day history will be available in %2$d day. Until then, the fallback chosen under \"When history is too short\" is used." … … 519 573 520 574 #. translators: Customer-facing notice shown next to a reduced price (EU Omnibus Directive). {price} is replaced by the price, {days} by the number of days. Keep both placeholders. 521 #: includes/class-settings.php:3 20575 #: includes/class-settings.php:337 522 576 msgid "Lowest price in the {days} days before the discount: {price}" 523 577 msgstr "" 524 578 525 579 #. translators: Customer-facing notice shown next to a price that is not reduced. {price} is replaced by the price, {days} by the number of days. Keep both placeholders. 526 #: includes/class-settings.php:3 31580 #: includes/class-settings.php:348 527 581 msgid "Lowest price in the last {days} days: {price}" 528 582 msgstr "" 529 583 530 584 #. translators: Placeholder for the lowest-price notice in the block editor and page builders. 531 #: includes/class-settings.php:342 blocks/lowest-price/editor.js:16 585 #: includes/class-settings.php:359 586 #: blocks/lowest-price/editor.js:16 532 587 msgid "Lowest price in the 30 days before the discount (shown when the product is reduced)" 533 588 msgstr "" … … 564 619 msgid "Right" 565 620 msgstr "" 566 567 #: blocks/lowest-price/block.json568 msgctxt "block title"569 msgid "Lowest price (Omnibus)"570 msgstr ""571 572 #: blocks/lowest-price/block.json573 msgctxt "block description"574 msgid "Shows the lowest price of the product in the 30 days before the discount."575 msgstr ""576 577 #: blocks/lowest-price/block.json578 msgctxt "block keyword"579 msgid "omnibus"580 msgstr ""581 582 #: blocks/lowest-price/block.json583 msgctxt "block keyword"584 msgid "lowest price"585 msgstr ""586 587 #: blocks/lowest-price/block.json588 msgctxt "block keyword"589 msgid "30 days"590 msgstr ""591 592 #: blocks/lowest-price/block.json593 msgctxt "block keyword"594 msgid "price history"595 msgstr "" -
cartnorm-omnibus-lowest-price/tags/0.1.2/readme.txt
r3764542 r3764726 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 0.1. 17 Stable tag: 0.1.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 84 84 Product pages, shop and category pages, a block for block themes, an **Elementor widget** (with color, typography and alignment), and the `[cartnorm_lowest_price]` shortcode. Choose where the notice appears on product pages: under the price, under the short description, under the add-to-cart button, or nowhere (place it yourself). On variable products, the general notice names its variation and is replaced by the selected variation's own notice. The customer-facing notice is built in for German, Polish, French, Italian, Spanish and Dutch shops, using the wording recommended by national regulators (for example "Najniższa cena z 30 dni przed obniżką" in Poland). 85 85 86 = Free, with nothing locked = 87 88 Every feature described above is included in this free plugin and works without an account or licence. An optional paid add-on, Cartnorm Omnibus Pro, adds tools for multi-currency shops and a few extras (see "Is there a Pro version?" in the FAQ). 89 86 90 = Not legal advice = 87 91 … … 122 126 The notice shown to shoppers is built in for German (Germany, Austria, Switzerland), Polish, French (France, Belgium), Italian, Spanish and Dutch (Netherlands, Belgium), so it appears in the shop language from day one. You can replace it with your own text under "Custom text" in the settings. When a translation from translate.wordpress.org is installed, it is used instead. All other texts can be translated on translate.wordpress.org. 123 127 128 = Is there a Pro version? = 129 130 Yes. Cartnorm Omnibus Pro is an optional paid add-on. This free plugin stays fully functional without it; nothing in it is locked or time-limited. Pro adds: 131 132 * Multi-currency: the lowest-price notice in the currency the customer sees (WPML / WooCommerce Multilingual, Aelia, CURCY, FOX or your own rates), with a separate 30-day history for each currency. 133 * A warning when the regular price was raised in the 30 days before a sale. 134 * A monthly Omnibus report by email. 135 * Google Shopping feed prices that match the 30-day lowest price shown in your shop. 136 * An EU withdrawal button (Directive (EU) 2023/2673). 137 138 Details and prices: [cartnorm.com/pricing](https://cartnorm.com/pricing/). The free plugin shows a "Pro version" link on the Plugins screen and one small note in its own settings, which you can dismiss for good. Both disappear when Pro is active. 139 124 140 = What happens when I uninstall the plugin? = 125 141 … … 136 152 == Changelog == 137 153 154 = 0.1.2 = 155 * New: Pro link on the Plugins screen and an optional, dismissible note in the plugin's own settings. 156 138 157 = 0.1.1 = 139 158 * Fix: in block themes (such as Twenty Twenty-Five) the sale badge in shop and category lists now follows the plugin settings too: it is hidden on discounts that are not genuine, and shows the percentage when that option is on. Older product grid blocks are covered as well. -
cartnorm-omnibus-lowest-price/trunk/cartnorm-omnibus-lowest-price.php
r3764542 r3764726 4 4 * Plugin URI: https://cartnorm.com/ 5 5 * Description: Shows the lowest price from the 30 days before a discount, as required by the EU Omnibus Directive. Change-only price history that never bloats your database. 6 * Version: 0.1. 16 * Version: 0.1.2 7 7 * Requires at least: 6.4 8 8 * Requires PHP: 7.4 … … 21 21 defined( 'ABSPATH' ) || exit; 22 22 23 define( 'CARTNORM_VERSION', '0.1. 1' );23 define( 'CARTNORM_VERSION', '0.1.2' ); 24 24 define( 'CARTNORM_FILE', __FILE__ ); 25 25 define( 'CARTNORM_DIR', plugin_dir_path( __FILE__ ) ); -
cartnorm-omnibus-lowest-price/trunk/includes/class-admin.php
r3764542 r3764726 138 138 echo '<table class="widefat striped" style="font-size:11px"><thead><tr><th>' . esc_html__( 'From', 'cartnorm-omnibus-lowest-price' ) . '</th><th>' . esc_html__( 'Price', 'cartnorm-omnibus-lowest-price' ) . '</th><th>' . esc_html__( 'Sale', 'cartnorm-omnibus-lowest-price' ) . '</th><th></th></tr></thead><tbody>'; 139 139 foreach ( $rows as $row ) { 140 echo '<tr><td>' . esc_html( wp_date( get_option( 'date_format' ), Clock::from_mysql( $row->valid_from ) ) ) . '</td><td>' . wp_kses_post( wc_price( $row->price ) ) . '</td><td>' . ( (int) $row->on_sale ? '✓' : '' ) . '</td><td>' . Corrections::link( $row ) . '</td></tr>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- link() escapes.140 echo '<tr><td>' . esc_html( wp_date( get_option( 'date_format' ), Clock::from_mysql( $row->valid_from ) ) ) . '</td><td>' . wp_kses_post( wc_price( $row->price ) ) . '</td><td>' . ( (int) $row->on_sale ? '✓' : '' ) . '</td><td>' . wp_kses_post( Corrections::link( $row ) ) . '</td></tr>'; 141 141 } 142 142 echo '</tbody></table>'; -
cartnorm-omnibus-lowest-price/trunk/includes/class-compliance-check.php
r3764542 r3764726 169 169 ) 170 170 ) . '</p>'; 171 Notices::render_pro_note(); 171 172 172 173 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only filter. -
cartnorm-omnibus-lowest-price/trunk/includes/class-notices.php
r3764542 r3764726 6 6 * - "Import price history": an inline, dismissible box inside this plugin's own 7 7 * settings section, shown only when another Omnibus plugin's history exists. 8 * - A note about the optional Pro add-on: one small inline box inside this 9 * plugin's own settings section and Omnibus check page, for users who can 10 * manage WooCommerce. It can be dismissed for good (per user, works without 11 * JavaScript) and is never shown when the Pro add-on is active. 8 12 * - The history-building status is a plain line in the settings section 9 13 * description (see Settings::history_status()), not a notice. … … 28 32 const FIELD_TYPE = 'cartnorm_notices'; 29 33 const IMPORT_INFIX = 'import_'; 34 const PRO_KEY = 'pro_info'; 35 const PRO_URL = 'https://cartnorm.com/pricing/'; 30 36 31 37 /** … … 95 101 */ 96 102 public static function keys() { 97 $keys = array( );103 $keys = array( self::PRO_KEY ); 98 104 foreach ( array_keys( Importer::source_labels() ) as $source ) { 99 105 $keys[] = self::IMPORT_INFIX . $source; … … 131 137 } 132 138 return true; 139 } 140 141 /** 142 * Whether Cartnorm Omnibus Pro is active. 143 * 144 * @return bool 145 */ 146 public static function pro_active() { 147 /** 148 * Whether Cartnorm Omnibus Pro is active. 149 * 150 * @param bool $active Active. 151 */ 152 return (bool) apply_filters( 'cartnorm_pro_active', defined( 'CARTNORM_PRO_VERSION' ) ); 153 } 154 155 /** 156 * Whether a currency switcher supported by Cartnorm Omnibus Pro is active: 157 * WPML / WooCommerce Multilingual in multi-currency mode, Aelia, CURCY or 158 * FOX (WOOCS). Only checks for loaded classes, functions and globals. 159 * 160 * @return bool 161 */ 162 public static function multi_currency_detected() { 163 $detected = self::wcml_multi_currency() 164 || class_exists( 'WC_Aelia_CurrencySwitcher' ) || has_filter( 'wc_aelia_cs_convert' ) 165 || function_exists( 'wmc_get_price' ) 166 || ( isset( $GLOBALS['WOOCS'] ) && is_object( $GLOBALS['WOOCS'] ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName 167 168 /** 169 * Whether the shop uses a currency switcher supported by Cartnorm Omnibus Pro. 170 * 171 * @param bool $detected Detected. 172 */ 173 return (bool) apply_filters( 'cartnorm_multi_currency_detected', $detected ); 174 } 175 176 /** 177 * WPML / WooCommerce Multilingual with multi-currency switched on. 178 * 179 * @return bool 180 */ 181 private static function wcml_multi_currency() { 182 if ( ! defined( 'WCML_VERSION' ) && ! class_exists( 'woocommerce_wpml' ) ) { 183 return false; 184 } 185 global $woocommerce_wpml; 186 if ( is_object( $woocommerce_wpml ) && isset( $woocommerce_wpml->settings['enable_multi_currency'] ) ) { 187 $independent = defined( 'WCML_MULTI_CURRENCIES_INDEPENDENT' ) ? WCML_MULTI_CURRENCIES_INDEPENDENT : 2; 188 return (int) $woocommerce_wpml->settings['enable_multi_currency'] === (int) $independent; 189 } 190 return true; 191 } 192 193 /** 194 * No-JavaScript dismiss link for a notice. 195 * 196 * @param string $key Notice key. 197 * @return string 198 */ 199 private static function dismiss_link( $key ) { 200 $url = wp_nonce_url( 201 add_query_arg( 202 array( 203 'action' => self::ACTION, 204 'notice' => $key, 205 ), 206 admin_url( 'admin-post.php' ) 207 ), 208 self::ACTION 209 ); 210 return ' <a class="hide-if-js" href="' . esc_url( $url ) . '">' . esc_html__( 'Dismiss', 'cartnorm-omnibus-lowest-price' ) . '</a>'; 211 } 212 213 /** 214 * The note about Cartnorm Omnibus Pro, as HTML. Empty unless the current 215 * user can manage WooCommerce, is on one of this plugin's own screens, has 216 * not dismissed it, and the Pro add-on is not active. 217 * 218 * @return string 219 */ 220 public static function pro_note_html() { 221 if ( ! current_user_can( 'manage_woocommerce' ) || ! self::is_own_screen() || self::pro_active() || self::is_dismissed( self::PRO_KEY ) ) { 222 return ''; 223 } 224 if ( self::multi_currency_detected() ) { 225 $text = __( 'Your shop shows prices in more than one currency. This plugin shows the lowest-price notice in your base currency only; Cartnorm Omnibus Pro shows the correct 30-day lowest price in each currency.', 'cartnorm-omnibus-lowest-price' ); 226 } else { 227 $text = __( 'Optional paid add-on: Cartnorm Omnibus Pro adds multi-currency support, a warning when the regular price was raised before a sale, a monthly email report, Google Shopping feed prices and an EU withdrawal button.', 'cartnorm-omnibus-lowest-price' ); 228 } 229 return '<div class="notice notice-info inline is-dismissible cartnorm-notice cartnorm-pro-note" data-cartnorm-notice="' . esc_attr( self::PRO_KEY ) . '"><p>' 230 . esc_html( $text ) 231 . ' <a href="' . esc_url( self::PRO_URL ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Learn more', 'cartnorm-omnibus-lowest-price' ) . '</a>' 232 . self::dismiss_link( self::PRO_KEY ) 233 . '</p></div>'; 234 } 235 236 /** 237 * Print the Pro note (Omnibus check page). 238 */ 239 public static function render_pro_note() { 240 echo wp_kses( self::pro_note_html(), Display::allowed_html() ); 133 241 } 134 242 … … 171 279 } 172 280 173 $dismiss_url = wp_nonce_url(174 add_query_arg(175 array(176 'action' => self::ACTION,177 'notice' => $key,178 ),179 admin_url( 'admin-post.php' )180 ),181 self::ACTION182 );183 281 $out .= '<div class="notice notice-info inline is-dismissible cartnorm-notice" data-cartnorm-notice="' . esc_attr( $key ) . '"><p>' 184 282 . esc_html( $text ) . ' ' . $button 185 . ' <a class="hide-if-js" href="' . esc_url( $dismiss_url ) . '">' . esc_html__( 'Dismiss', 'cartnorm-omnibus-lowest-price' ) . '</a>'283 . self::dismiss_link( $key ) 186 284 . '</p></div>'; 187 285 } 188 return $out ;286 return $out . self::pro_note_html(); 189 287 } 190 288 … … 200 298 return; 201 299 } 202 echo '<tr class="cartnorm-notices-row"><td colspan="2" style="padding:0 0 8px">' . $html . '</td></tr>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped in html().203 } 204 205 /** 206 * Script that stores the dismissal when the core dismiss button is clicked .207 * Loaded only on this plugin's own screens.300 echo '<tr class="cartnorm-notices-row"><td colspan="2" style="padding:0 0 8px">' . wp_kses( $html, Display::allowed_html() ) . '</td></tr>'; 301 } 302 303 /** 304 * Script that stores the dismissal when the core dismiss button is clicked, 305 * and a one-line style for the box. Loaded only on this plugin's own screens. 208 306 */ 209 307 public static function enqueue() { … … 213 311 $script = '(function(){document.addEventListener("click",function(e){var b=e.target.closest?e.target.closest(".cartnorm-notice .notice-dismiss"):null;if(!b){return;}var n=b.closest(".cartnorm-notice");var d=new FormData();d.append("action",' . wp_json_encode( self::ACTION ) . ');d.append("notice",n.getAttribute("data-cartnorm-notice"));d.append("_ajax_nonce",' . wp_json_encode( wp_create_nonce( self::ACTION ) ) . ');fetch(' . wp_json_encode( admin_url( 'admin-ajax.php' ) ) . ',{method:"POST",credentials:"same-origin",body:d});});})();'; 214 312 wp_add_inline_script( 'common', $script ); 313 // WooCommerce screens override the core padding that keeps text clear of the dismiss button. 314 wp_add_inline_style( 'common', '.wp-core-ui .cartnorm-notice.notice.is-dismissible{padding-right:48px}' ); 215 315 } 216 316 … … 248 348 } 249 349 self::dismiss( self::requested_key() ); 250 wp_safe_redirect( Settings::url() ); 350 $back = wp_get_referer(); 351 wp_safe_redirect( $back ? $back : Settings::url() ); 251 352 exit; 252 353 } -
cartnorm-omnibus-lowest-price/trunk/includes/class-settings.php
r3764542 r3764726 80 80 add_filter( 'woocommerce_get_settings_products', array( __CLASS__, 'add_settings' ), 10, 2 ); 81 81 add_filter( 'plugin_action_links_' . plugin_basename( CARTNORM_FILE ), array( __CLASS__, 'action_links' ) ); 82 add_filter( 'plugin_row_meta', array( __CLASS__, 'row_meta' ), 10, 2 ); 82 83 } 83 84 … … 98 99 */ 99 100 public static function action_links( $links ) { 100 array_unshift( $links, '<a href="' . esc_url( self::url() ) . '">' . esc_html__( 'Settings', 'cartnorm-omnibus-lowest-price' ) . '</a>' ); 101 array_unshift( $links, Display::kses( '<a href="' . esc_url( self::url() ) . '">' . esc_html__( 'Settings', 'cartnorm-omnibus-lowest-price' ) . '</a>' ) ); 102 return $links; 103 } 104 105 /** 106 * Add a "Pro version" link to this plugin's row on the Plugins screen 107 * (only for this plugin, and not when the Pro add-on is active). 108 * 109 * @param array $links Row meta links. 110 * @param string $file Plugin file. 111 * @return array 112 */ 113 public static function row_meta( $links, $file ) { 114 if ( plugin_basename( CARTNORM_FILE ) !== $file || Notices::pro_active() ) { 115 return $links; 116 } 117 $links[] = Display::kses( '<a href="' . esc_url( Notices::PRO_URL ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Pro version', 'cartnorm-omnibus-lowest-price' ) . '</a>' ); 101 118 return $links; 102 119 } -
cartnorm-omnibus-lowest-price/trunk/languages/cartnorm-omnibus-lowest-price.pot
r3764542 r3764726 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Cartnorm Omnibus Lowest Price 0.1. 1\n"5 "Project-Id-Version: Cartnorm Omnibus Lowest Price 0.1.2\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/cartnorm-omnibus-lowest-price\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: 2026-10-25T 08:45:23+00:00\n"12 "POT-Creation-Date: 2026-10-25T14:03:29+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" 15 15 "X-Domain: cartnorm-omnibus-lowest-price\n" 16 16 17 #: blocks/lowest-price/block.json 18 msgctxt "block title" 19 msgid "Lowest price (Omnibus)" 20 msgstr "" 21 22 #: blocks/lowest-price/block.json 23 msgctxt "block description" 24 msgid "Shows the lowest price of the product in the 30 days before the discount." 25 msgstr "" 26 27 #: blocks/lowest-price/block.json 28 msgctxt "block keyword" 29 msgid "omnibus" 30 msgstr "" 31 32 #: blocks/lowest-price/block.json 33 msgctxt "block keyword" 34 msgid "lowest price" 35 msgstr "" 36 37 #: blocks/lowest-price/block.json 38 msgctxt "block keyword" 39 msgid "30 days" 40 msgstr "" 41 42 #: blocks/lowest-price/block.json 43 msgctxt "block keyword" 44 msgid "price history" 45 msgstr "" 46 17 47 #. Plugin Name of the plugin 18 48 #: cartnorm-omnibus-lowest-price.php … … 43 73 msgstr "" 44 74 45 #: includes/class-admin.php:68 includes/class-settings.php:111 75 #: includes/class-admin.php:68 76 #: includes/class-settings.php:128 46 77 #: includes/integrations/class-elementor-widget.php:36 47 78 msgid "Lowest price (Omnibus)" … … 102 133 msgstr "" 103 134 104 #: includes/class-admin.php:161 includes/class-corrections.php:218 105 #: includes/class-importer.php:337 includes/class-notices.php:247 135 #: includes/class-admin.php:161 136 #: includes/class-corrections.php:218 137 #: includes/class-importer.php:337 138 #: includes/class-notices.php:347 106 139 msgid "You are not allowed to do this." 107 140 msgstr "" … … 151 184 msgstr "" 152 185 153 #: includes/class-compliance-check.php:19 8186 #: includes/class-compliance-check.php:199 154 187 msgid "No products are reduced at the moment." 155 188 msgstr "" 156 189 157 #: includes/class-compliance-check.php:20 3190 #: includes/class-compliance-check.php:204 158 191 #: includes/integrations/class-elementor-widget.php:72 159 192 msgid "Product" 160 193 msgstr "" 161 194 162 #: includes/class-compliance-check.php:20 3195 #: includes/class-compliance-check.php:204 163 196 msgid "Status" 164 197 msgstr "" 165 198 166 #: includes/class-compliance-check.php:20 3199 #: includes/class-compliance-check.php:204 167 200 msgid "Sale price" 168 201 msgstr "" 169 202 170 #: includes/class-compliance-check.php:20 3203 #: includes/class-compliance-check.php:204 171 204 msgid "Lowest price, 30 days before" 172 205 msgstr "" 173 206 174 #: includes/class-compliance-check.php:20 3207 #: includes/class-compliance-check.php:204 175 208 msgid "Sale started" 176 209 msgstr "" 177 210 178 #: includes/class-corrections.php:35 includes/class-corrections.php:69 211 #: includes/class-corrections.php:35 212 #: includes/class-corrections.php:69 179 213 msgid "Correct price record" 180 214 msgstr "" … … 184 218 msgstr "" 185 219 186 #: includes/class-corrections.php:78 includes/class-corrections.php:130 220 #: includes/class-corrections.php:78 221 #: includes/class-corrections.php:130 187 222 #: includes/class-corrections.php:154 188 223 msgid "This record does not exist." … … 199 234 msgstr "" 200 235 201 #: includes/class-corrections.php:97 includes/class-corrections.php:131 236 #: includes/class-corrections.php:97 237 #: includes/class-corrections.php:131 202 238 #: includes/class-corrections.php:157 203 239 msgid "The current price cannot be corrected here. Change it in the product instead." … … 236 272 msgstr "" 237 273 238 #: includes/class-corrections.php:129 includes/class-corrections.php:148 274 #: includes/class-corrections.php:129 275 #: includes/class-corrections.php:148 239 276 msgid "Please give a reason for the correction." 240 277 msgstr "" 241 278 242 #: includes/class-corrections.php:132 includes/class-corrections.php:160 279 #: includes/class-corrections.php:132 280 #: includes/class-corrections.php:160 243 281 msgid "Please enter a valid price." 244 282 msgstr "" 245 283 284 #: includes/class-country-rules.php:64 285 msgid "EU default rules" 286 msgstr "" 287 246 288 #: includes/class-country-rules.php:65 247 msgid " EU default rules"289 msgid "Austria" 248 290 msgstr "" 249 291 250 292 #: includes/class-country-rules.php:66 251 msgid " Austria"293 msgid "Belgium" 252 294 msgstr "" 253 295 254 296 #: includes/class-country-rules.php:67 255 msgid " Belgium"297 msgid "Czech Republic" 256 298 msgstr "" 257 299 258 300 #: includes/class-country-rules.php:68 259 msgid " Czech Republic"301 msgid "France" 260 302 msgstr "" 261 303 262 304 #: includes/class-country-rules.php:69 263 msgid " France"305 msgid "Germany" 264 306 msgstr "" 265 307 266 308 #: includes/class-country-rules.php:70 267 msgid " Germany"309 msgid "Ireland" 268 310 msgstr "" 269 311 270 312 #: includes/class-country-rules.php:71 271 msgid "I reland"313 msgid "Italy" 272 314 msgstr "" 273 315 274 316 #: includes/class-country-rules.php:72 275 msgid " Italy"317 msgid "Netherlands" 276 318 msgstr "" 277 319 278 320 #: includes/class-country-rules.php:73 279 msgid " Netherlands"321 msgid "Poland" 280 322 msgstr "" 281 323 282 324 #: includes/class-country-rules.php:74 283 msgid "Po land"325 msgid "Portugal" 284 326 msgstr "" 285 327 286 328 #: includes/class-country-rules.php:75 287 msgid " Portugal"329 msgid "Romania" 288 330 msgstr "" 289 331 290 332 #: includes/class-country-rules.php:76 291 msgid " Romania"333 msgid "Spain" 292 334 msgstr "" 293 335 294 336 #: includes/class-country-rules.php:77 295 msgid "Spain"296 msgstr ""297 298 #: includes/class-country-rules.php:78299 337 msgid "Sweden" 300 338 msgstr "" 301 339 302 #: includes/class-country-rules.php: 80340 #: includes/class-country-rules.php:79 303 341 msgid "Automatic (store country)" 304 342 msgstr "" 305 343 306 #: includes/class-notices.php: 88344 #: includes/class-notices.php:94 307 345 msgid "Cartnorm Omnibus Lowest Price needs WooCommerce to be installed and active." 308 346 msgstr "" 309 347 348 #: includes/class-notices.php:210 349 msgid "Dismiss" 350 msgstr "" 351 352 #: includes/class-notices.php:225 353 msgid "Your shop shows prices in more than one currency. This plugin shows the lowest-price notice in your base currency only; Cartnorm Omnibus Pro shows the correct 30-day lowest price in each currency." 354 msgstr "" 355 356 #: includes/class-notices.php:227 357 msgid "Optional paid add-on: Cartnorm Omnibus Pro adds multi-currency support, a warning when the regular price was raised before a sale, a monthly email report, Google Shopping feed prices and an EU withdrawal button." 358 msgstr "" 359 360 #: includes/class-notices.php:231 361 msgid "Learn more" 362 msgstr "" 363 310 364 #. translators: %s: name of another plugin 311 #: includes/class-notices.php: 153365 #: includes/class-notices.php:261 312 366 #, php-format 313 367 msgid "Importing price history from %s in the background." 314 368 msgstr "" 315 369 316 #: includes/class-notices.php: 160370 #: includes/class-notices.php:268 317 371 msgid "Import price history" 318 372 msgstr "" 319 373 320 374 #. translators: %s: name of another plugin 321 #: includes/class-notices.php: 163375 #: includes/class-notices.php:271 322 376 #, php-format 323 377 msgid "Price history from %s was found. Import it to keep your full 30-day history after switching." 324 378 msgstr "" 325 379 326 #: includes/class-notices.php:185 327 msgid "Dismiss" 328 msgstr "" 329 330 #: includes/class-settings.php:100 380 #: includes/class-settings.php:101 331 381 msgid "Settings" 332 382 msgstr "" 333 383 384 #: includes/class-settings.php:117 385 msgid "Pro version" 386 msgstr "" 387 334 388 #. translators: 1: number of stored price changes, 2: date tracking started 335 #: includes/class-settings.php:1 31389 #: includes/class-settings.php:148 336 390 #, php-format 337 391 msgid "Price history: %1$s stored price changes since %2$s. Rows are written only when a price changes." 338 392 msgstr "" 339 393 340 #: includes/class-settings.php:1 41394 #: includes/class-settings.php:158 341 395 msgid "Lowest price before a discount (EU Omnibus Directive)" 342 396 msgstr "" 343 397 344 #: includes/class-settings.php:1 43398 #: includes/class-settings.php:160 345 399 msgid "When a price is reduced, EU law requires showing the lowest price of the product in the 30 days before the reduction, and calculating any strikethrough price or percentage from that price." 346 400 msgstr "" 347 401 348 #: includes/class-settings.php:1 51402 #: includes/class-settings.php:168 349 403 msgid "Enable" 350 404 msgstr "" 351 405 352 #: includes/class-settings.php:1 52406 #: includes/class-settings.php:169 353 407 msgid "Show the lowest price information in the shop" 354 408 msgstr "" 355 409 356 #: includes/class-settings.php:1 58410 #: includes/class-settings.php:175 357 411 msgid "Show on" 358 412 msgstr "" 359 413 360 #: includes/class-settings.php:1 59414 #: includes/class-settings.php:176 361 415 msgid "Single product pages (including each variation)" 362 416 msgstr "" 363 417 364 #: includes/class-settings.php:1 66418 #: includes/class-settings.php:183 365 419 msgid "Shop, category and other product lists" 366 420 msgstr "" 367 421 368 #: includes/class-settings.php:1 73422 #: includes/class-settings.php:190 369 423 msgid "Position on product pages" 370 424 msgstr "" 371 425 372 #: includes/class-settings.php:1 77426 #: includes/class-settings.php:194 373 427 msgid "If your theme or page builder shows prices in an unusual way, choose another position, or \"Nowhere\" and place the block, shortcode [cartnorm_lowest_price] or Elementor widget yourself." 374 428 msgstr "" 375 429 376 #: includes/class-settings.php:1 79430 #: includes/class-settings.php:196 377 431 msgid "Right under the price (recommended)" 378 432 msgstr "" 379 433 380 #: includes/class-settings.php:1 80434 #: includes/class-settings.php:197 381 435 msgid "Under the short description (classic themes)" 382 436 msgstr "" 383 437 384 #: includes/class-settings.php:1 81438 #: includes/class-settings.php:198 385 439 msgid "Under the add-to-cart button (classic themes)" 386 440 msgstr "" 387 441 388 #: includes/class-settings.php:1 82442 #: includes/class-settings.php:199 389 443 msgid "Nowhere (I place it myself)" 390 444 msgstr "" 391 445 392 #: includes/class-settings.php: 186446 #: includes/class-settings.php:203 393 447 msgid "Show for" 394 448 msgstr "" 395 449 396 #: includes/class-settings.php: 191450 #: includes/class-settings.php:208 397 451 msgid "Reduced products only (what the law requires)" 398 452 msgstr "" 399 453 400 #: includes/class-settings.php: 192454 #: includes/class-settings.php:209 401 455 msgid "All products (lowest price in the last 30 days)" 402 456 msgstr "" 403 457 404 #: includes/class-settings.php: 196458 #: includes/class-settings.php:213 405 459 msgid "Strikethrough price" 406 460 msgstr "" 407 461 408 #: includes/class-settings.php:2 00462 #: includes/class-settings.php:217 409 463 msgid "The CJEU (case C-330/23) ruled that an announced reduction must be calculated from the lowest price of the previous 30 days." 410 464 msgstr "" 411 465 412 #: includes/class-settings.php:2 02466 #: includes/class-settings.php:219 413 467 msgid "Lowest price of the 30 days before the reduction (recommended)" 414 468 msgstr "" 415 469 416 #: includes/class-settings.php:2 03470 #: includes/class-settings.php:220 417 471 msgid "Regular price (WooCommerce default)" 418 472 msgstr "" 419 473 420 #: includes/class-settings.php:2 07474 #: includes/class-settings.php:224 421 475 msgid "Sale badge" 422 476 msgstr "" 423 477 424 #: includes/class-settings.php:2 12478 #: includes/class-settings.php:229 425 479 msgid "Theme default (\"Sale!\")" 426 480 msgstr "" 427 481 428 #: includes/class-settings.php:2 13482 #: includes/class-settings.php:230 429 483 msgid "Percentage calculated from the 30-day lowest price, rounded down (e.g. \"-20%\")" 430 484 msgstr "" 431 485 432 #: includes/class-settings.php:2 17486 #: includes/class-settings.php:234 433 487 msgid "Fake discount guard" 434 488 msgstr "" 435 489 436 #: includes/class-settings.php:2 18490 #: includes/class-settings.php:235 437 491 msgid "Hide the strikethrough price, the sale badge and the notice when the sale price is not actually below the 30-day lowest price" 438 492 msgstr "" 439 493 440 #: includes/class-settings.php:2 24494 #: includes/class-settings.php:241 441 495 msgid "Country rules" 442 496 msgstr "" 443 497 444 #: includes/class-settings.php:2 29498 #: includes/class-settings.php:246 445 499 msgid "Member states differ in details such as progressive reductions and new products. Choose the country whose law applies to your shop." 446 500 msgstr "" 447 501 448 #: includes/class-settings.php:2 32502 #: includes/class-settings.php:249 449 503 msgid "Progressive reductions" 450 504 msgstr "" 451 505 452 #: includes/class-settings.php:2 36506 #: includes/class-settings.php:253 453 507 msgid "Whether uninterrupted, increasing reductions keep the reference price of the first reduction." 454 508 msgstr "" 455 509 456 #: includes/class-settings.php:2 38510 #: includes/class-settings.php:255 457 511 msgid "Follow the country rules" 458 512 msgstr "" 459 513 460 #: includes/class-settings.php:2 39514 #: includes/class-settings.php:256 461 515 msgid "Never (each further reduction gets a new reference)" 462 516 msgstr "" 463 517 464 #: includes/class-settings.php:2 40518 #: includes/class-settings.php:257 465 519 msgid "Always (one uninterrupted campaign)" 466 520 msgstr "" 467 521 468 #: includes/class-settings.php:2 44522 #: includes/class-settings.php:261 469 523 msgid "When history is too short" 470 524 msgstr "" 471 525 472 #: includes/class-settings.php:2 48526 #: includes/class-settings.php:265 473 527 msgid "Used while the plugin is still building history, for example right after installation." 474 528 msgstr "" 475 529 476 #: includes/class-settings.php:2 50530 #: includes/class-settings.php:267 477 531 msgid "Use the regular price" 478 532 msgstr "" 479 533 480 #: includes/class-settings.php:2 51534 #: includes/class-settings.php:268 481 535 msgid "Use the lowest price recorded so far" 482 536 msgstr "" 483 537 484 #: includes/class-settings.php:2 52538 #: includes/class-settings.php:269 485 539 msgid "Show nothing" 486 540 msgstr "" 487 541 488 #: includes/class-settings.php:2 56542 #: includes/class-settings.php:273 489 543 msgid "Look-back period (days)" 490 544 msgstr "" 491 545 492 #: includes/class-settings.php:2 61546 #: includes/class-settings.php:278 493 547 msgid "At least 30 days. Some member states allow or require longer periods in specific cases." 494 548 msgstr "" 495 549 496 #: includes/class-settings.php:2 64550 #: includes/class-settings.php:281 497 551 msgid "Custom text" 498 552 msgstr "" 499 553 500 #: includes/class-settings.php:2 69554 #: includes/class-settings.php:286 501 555 msgid "Leave empty to use the default text in the shop language (built in for German, Polish, French, Italian, Spanish and Dutch). Use {price} for the price and {days} for the number of days." 502 556 msgstr "" 503 557 504 #: includes/class-settings.php:2 72558 #: includes/class-settings.php:289 505 559 msgid "Keep history for (days)" 506 560 msgstr "" 507 561 508 #: includes/class-settings.php:2 77562 #: includes/class-settings.php:294 509 563 msgid "Older price changes are removed automatically. The price that was valid at the start of the look-back window is always kept." 510 564 msgstr "" 511 565 512 566 #. translators: 1: number of days of the look-back period, 2: number of days left 513 #: includes/class-settings.php:3 02567 #: includes/class-settings.php:319 514 568 #, php-format 515 569 msgid "The full %1$d-day history will be available in %2$d day. Until then, the fallback chosen under \"When history is too short\" is used." … … 519 573 520 574 #. translators: Customer-facing notice shown next to a reduced price (EU Omnibus Directive). {price} is replaced by the price, {days} by the number of days. Keep both placeholders. 521 #: includes/class-settings.php:3 20575 #: includes/class-settings.php:337 522 576 msgid "Lowest price in the {days} days before the discount: {price}" 523 577 msgstr "" 524 578 525 579 #. translators: Customer-facing notice shown next to a price that is not reduced. {price} is replaced by the price, {days} by the number of days. Keep both placeholders. 526 #: includes/class-settings.php:3 31580 #: includes/class-settings.php:348 527 581 msgid "Lowest price in the last {days} days: {price}" 528 582 msgstr "" 529 583 530 584 #. translators: Placeholder for the lowest-price notice in the block editor and page builders. 531 #: includes/class-settings.php:342 blocks/lowest-price/editor.js:16 585 #: includes/class-settings.php:359 586 #: blocks/lowest-price/editor.js:16 532 587 msgid "Lowest price in the 30 days before the discount (shown when the product is reduced)" 533 588 msgstr "" … … 564 619 msgid "Right" 565 620 msgstr "" 566 567 #: blocks/lowest-price/block.json568 msgctxt "block title"569 msgid "Lowest price (Omnibus)"570 msgstr ""571 572 #: blocks/lowest-price/block.json573 msgctxt "block description"574 msgid "Shows the lowest price of the product in the 30 days before the discount."575 msgstr ""576 577 #: blocks/lowest-price/block.json578 msgctxt "block keyword"579 msgid "omnibus"580 msgstr ""581 582 #: blocks/lowest-price/block.json583 msgctxt "block keyword"584 msgid "lowest price"585 msgstr ""586 587 #: blocks/lowest-price/block.json588 msgctxt "block keyword"589 msgid "30 days"590 msgstr ""591 592 #: blocks/lowest-price/block.json593 msgctxt "block keyword"594 msgid "price history"595 msgstr "" -
cartnorm-omnibus-lowest-price/trunk/readme.txt
r3764542 r3764726 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 0.1. 17 Stable tag: 0.1.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 84 84 Product pages, shop and category pages, a block for block themes, an **Elementor widget** (with color, typography and alignment), and the `[cartnorm_lowest_price]` shortcode. Choose where the notice appears on product pages: under the price, under the short description, under the add-to-cart button, or nowhere (place it yourself). On variable products, the general notice names its variation and is replaced by the selected variation's own notice. The customer-facing notice is built in for German, Polish, French, Italian, Spanish and Dutch shops, using the wording recommended by national regulators (for example "Najniższa cena z 30 dni przed obniżką" in Poland). 85 85 86 = Free, with nothing locked = 87 88 Every feature described above is included in this free plugin and works without an account or licence. An optional paid add-on, Cartnorm Omnibus Pro, adds tools for multi-currency shops and a few extras (see "Is there a Pro version?" in the FAQ). 89 86 90 = Not legal advice = 87 91 … … 122 126 The notice shown to shoppers is built in for German (Germany, Austria, Switzerland), Polish, French (France, Belgium), Italian, Spanish and Dutch (Netherlands, Belgium), so it appears in the shop language from day one. You can replace it with your own text under "Custom text" in the settings. When a translation from translate.wordpress.org is installed, it is used instead. All other texts can be translated on translate.wordpress.org. 123 127 128 = Is there a Pro version? = 129 130 Yes. Cartnorm Omnibus Pro is an optional paid add-on. This free plugin stays fully functional without it; nothing in it is locked or time-limited. Pro adds: 131 132 * Multi-currency: the lowest-price notice in the currency the customer sees (WPML / WooCommerce Multilingual, Aelia, CURCY, FOX or your own rates), with a separate 30-day history for each currency. 133 * A warning when the regular price was raised in the 30 days before a sale. 134 * A monthly Omnibus report by email. 135 * Google Shopping feed prices that match the 30-day lowest price shown in your shop. 136 * An EU withdrawal button (Directive (EU) 2023/2673). 137 138 Details and prices: [cartnorm.com/pricing](https://cartnorm.com/pricing/). The free plugin shows a "Pro version" link on the Plugins screen and one small note in its own settings, which you can dismiss for good. Both disappear when Pro is active. 139 124 140 = What happens when I uninstall the plugin? = 125 141 … … 136 152 == Changelog == 137 153 154 = 0.1.2 = 155 * New: Pro link on the Plugins screen and an optional, dismissible note in the plugin's own settings. 156 138 157 = 0.1.1 = 139 158 * Fix: in block themes (such as Twenty Twenty-Five) the sale badge in shop and category lists now follows the plugin settings too: it is hidden on discounts that are not genuine, and shows the percentage when that option is on. Older product grid blocks are covered as well.
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)