Changeset 3761105
- Timestamp:
- 10/23/2026 07:33:33 AM (45 hours ago)
- Location:
- quotanto
- Files:
-
- 31 added
- 6 edited
-
tags/1.0.2 (added)
-
tags/1.0.2/assets (added)
-
tags/1.0.2/assets/quotanto.css (added)
-
tags/1.0.2/assets/quote-cart.js (added)
-
tags/1.0.2/blocks (added)
-
tags/1.0.2/blocks/request-button (added)
-
tags/1.0.2/blocks/request-button/block.json (added)
-
tags/1.0.2/includes (added)
-
tags/1.0.2/includes/class-quotanto-abilities.php (added)
-
tags/1.0.2/includes/class-quotanto-account.php (added)
-
tags/1.0.2/includes/class-quotanto-captcha.php (added)
-
tags/1.0.2/includes/class-quotanto-catalog.php (added)
-
tags/1.0.2/includes/class-quotanto-emails.php (added)
-
tags/1.0.2/includes/class-quotanto-lifecycle.php (added)
-
tags/1.0.2/includes/class-quotanto-plugin.php (added)
-
tags/1.0.2/includes/class-quotanto-quote.php (added)
-
tags/1.0.2/includes/class-quotanto-request-form.php (added)
-
tags/1.0.2/includes/class-quotanto-settings-page.php (added)
-
tags/1.0.2/includes/class-quotanto-settings.php (added)
-
tags/1.0.2/includes/class-quotanto-statuses.php (added)
-
tags/1.0.2/includes/emails (added)
-
tags/1.0.2/includes/emails/class-quotanto-email-new-request.php (added)
-
tags/1.0.2/includes/emails/class-quotanto-email-quote.php (added)
-
tags/1.0.2/includes/emails/class-quotanto-email-request-received.php (added)
-
tags/1.0.2/includes/emails/class-quotanto-email.php (added)
-
tags/1.0.2/languages (added)
-
tags/1.0.2/languages/quotanto.pot (added)
-
tags/1.0.2/quotanto.php (added)
-
tags/1.0.2/readme.txt (added)
-
tags/1.0.2/uninstall.php (added)
-
tags/1.0.2/wpml-config.xml (added)
-
trunk/assets/quotanto.css (modified) (1 diff)
-
trunk/blocks/request-button/block.json (modified) (1 diff)
-
trunk/includes/class-quotanto-request-form.php (modified) (9 diffs)
-
trunk/languages/quotanto.pot (modified) (6 diffs)
-
trunk/quotanto.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
quotanto/trunk/assets/quotanto.css
r3751965 r3761105 74 74 } 75 75 76 .quotanto__notice--info { 77 border-color: #2f6fb3; 78 } 79 76 80 .quotanto__notice--error { 77 81 border-color: #b42318; -
quotanto/trunk/blocks/request-button/block.json
r3751965 r3761105 3 3 "apiVersion": 3, 4 4 "name": "quotanto/request-button", 5 "version": "1.0. 1",5 "version": "1.0.2", 6 6 "title": "Request a quote", 7 7 "category": "woocommerce", -
quotanto/trunk/includes/class-quotanto-request-form.php
r3751965 r3761105 92 92 93 93 /** 94 * Whether a classic product hook firing now is WooCommerce replaying it in a block template. 95 * 96 * Block product and shop templates replay classic hooks in places that do not fit the form. Elsewhere on a block 97 * theme, such as a page with the [product_page] or [products] shortcode, the hooks come from the classic template 98 * and are used as on a classic theme. The main query decides: [product_page] swaps in a single product query of its own. 99 */ 100 private static function block_template_replay(): bool { 101 if ( ! wp_is_block_theme() || self::$classic_template ) { 102 return false; 103 } 104 $main = $GLOBALS['wp_the_query'] ?? null; 105 if ( ! $main instanceof WP_Query ) { 106 return true; 107 } 108 return $main->is_singular( 'product' ) 109 || $main->is_post_type_archive( 'product' ) 110 || $main->is_page( wc_get_page_id( 'shop' ) ) 111 || $main->is_tax( get_object_taxonomies( 'product' ) ); 112 } 113 114 /** 94 115 * On a block theme the classic product template only runs inside WooCommerce's Classic Template block. 95 116 * … … 112 133 // Block templates replay this classic hook next to the price for compatibility, which is the 113 134 // wrong place. There the form is appended to the Add to Cart block instead. 114 if ( wp_is_block_theme() && ! self::$classic_template) {135 if ( self::block_template_replay() ) { 115 136 return; 116 137 } … … 168 189 global $product; 169 190 // Block templates replay this classic hook; there the link is appended to the Product Button block instead. 170 if ( wp_is_block_theme() && ! self::$classic_template) {191 if ( self::block_template_replay() ) { 171 192 return; 172 193 } … … 215 236 216 237 /** 217 * Appends the cart form to the Cart block .238 * Appends the cart form to the Cart block, as wide as the block: classic themes make a wide block wider than the content. 218 239 * 219 240 * @param string $content Block HTML. 220 241 */ 221 242 public static function append_to_cart_block( string $content ): string { 222 return $content . self::render_cart(); 243 $form = self::render_cart(); 244 if ( '' === $form || ! preg_match( '/^\s*<div\b[^>]*\bclass="[^"]*\b(alignwide|alignfull)\b/', $content, $match ) ) { 245 return $content . $form; 246 } 247 return $content . '<div class="' . esc_attr( $match[1] ) . '">' . $form . '</div>'; 223 248 } 224 249 … … 283 308 private static function state(): string { 284 309 $state = sanitize_key( Quotanto_Plugin::request_var( 'quotanto' ) ); 285 return in_array( $state, array( 'sent', 'open', ' invalid', 'captcha', 'limit', 'failed' ), true ) ? $state : '';310 return in_array( $state, array( 'sent', 'open', 'expired', 'invalid', 'captcha', 'limit', 'failed' ), true ) ? $state : ''; 286 311 } 287 312 … … 303 328 if ( 'sent' === $state ) { 304 329 echo '<p class="quotanto__notice quotanto__notice--ok" role="status">' . esc_html__( 'Thanks. We have your request and sent you a confirmation email. We will reply with a quote.', 'quotanto' ) . '</p>'; 330 } elseif ( 'expired' === $state ) { 331 // A page left open too long is not the customer's mistake: no error style. 332 echo '<p class="quotanto__notice quotanto__notice--info" role="status">' . esc_html__( 'This page has expired. Reload it and send again.', 'quotanto' ) . '</p>'; 305 333 } elseif ( self::has_notice( $state ) ) { 306 334 echo '<p class="quotanto__notice quotanto__notice--error" role="alert">' . esc_html( self::error_message( $state ) ) . '</p>'; … … 522 550 'captcha' => __( 'Please complete the captcha and try again.', 'quotanto' ), 523 551 'limit' => __( 'Too many requests from this connection. Please try again in an hour.', 'quotanto' ), 524 'invalid' => __( 'Please check the form and try again. If the page has been open for a long time, reload it first.', 'quotanto' ),552 'invalid' => __( 'Please check the form and try again.', 'quotanto' ), 525 553 default => __( 'We could not save your request. Please try again in a moment.', 'quotanto' ), 526 554 }; … … 646 674 } 647 675 676 // A nonce fails when the page was open longer than the nonce lives, or the visitor logged in or out since. 648 677 if ( ! $verified ) { 649 self::finish( $back, ' invalid', $anchor );678 self::finish( $back, 'expired', $anchor ); 650 679 } 651 680 … … 659 688 */ 660 689 $minimum = max( 0, (int) apply_filters( 'quotanto_min_seconds', self::MIN_SECONDS ) ); 661 if ( ! $timestamp || ! hash_equals( self::sign( $timestamp ), $signature ) || $age < $minimum || $age > WEEK_IN_SECONDS) {690 if ( ! $timestamp || ! hash_equals( self::sign( $timestamp ), $signature ) || $age < $minimum ) { 662 691 self::finish( $back, 'invalid', $anchor ); 692 } 693 if ( $age > WEEK_IN_SECONDS ) { 694 self::finish( $back, 'expired', $anchor ); 663 695 } 664 696 -
quotanto/trunk/languages/quotanto.pot
r3751965 r3761105 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Quotanto 1.0.1\n"5 "Project-Id-Version: Quotanto – Request a Quote for WooCommerce 1.0.2\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/quotanto\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- 17T15:17:36+02:00\n"12 "POT-Creation-Date: 2026-10-23T09:21:41+02:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 16 16 17 17 #. Plugin Name of the plugin 18 #. Author of the plugin19 18 #: quotanto.php:3 20 #: quotanto.php:12 21 msgid "Quotanto" 19 msgid "Quotanto – Request a Quote for WooCommerce" 22 20 msgstr "" 23 21 … … 30 28 #: quotanto.php:5 31 29 msgid "Let customers request a quote, price it in WooCommerce, and let them pay at your normal checkout. Works with block cart and checkout." 30 msgstr "" 31 32 #. Author of the plugin 33 #: quotanto.php:12 34 msgid "Quotanto" 32 35 msgstr "" 33 36 … … 77 80 78 81 #: includes/class-quotanto-captcha.php:183 79 #: includes/class-quotanto-request-form.php:5 2282 #: includes/class-quotanto-request-form.php:550 80 83 msgid "Please complete the captcha and try again." 81 84 msgstr "" … … 174 177 175 178 #. translators: 1: button text, for example "Request a quote". 2: product name. 176 #: includes/class-quotanto-request-form.php:1 59179 #: includes/class-quotanto-request-form.php:180 177 180 #, php-format 178 181 msgid "%1$s: %2$s" 179 182 msgstr "" 180 183 181 #: includes/class-quotanto-request-form.php:3 04184 #: includes/class-quotanto-request-form.php:329 182 185 msgid "Thanks. We have your request and sent you a confirmation email. We will reply with a quote." 183 186 msgstr "" 184 187 185 #: includes/class-quotanto-request-form.php:316 188 #: includes/class-quotanto-request-form.php:332 189 msgid "This page has expired. Reload it and send again." 190 msgstr "" 191 192 #: includes/class-quotanto-request-form.php:344 186 193 msgid "Log in to request a quote" 187 194 msgstr "" 188 195 189 #: includes/class-quotanto-request-form.php: 394196 #: includes/class-quotanto-request-form.php:422 190 197 msgid "Remove the products that cannot be quoted to send this cart as a request." 191 198 msgstr "" 192 199 193 #: includes/class-quotanto-request-form.php:4 19200 #: includes/class-quotanto-request-form.php:447 194 201 msgid "Choose an option" 195 202 msgstr "" 196 203 197 #: includes/class-quotanto-request-form.php:4 55204 #: includes/class-quotanto-request-form.php:483 198 205 msgid "These products are sold by quote. Send the cart as a request and we will reply with prices." 199 206 msgstr "" 200 207 201 #: includes/class-quotanto-request-form.php:4 70208 #: includes/class-quotanto-request-form.php:498 202 209 msgid "Quantity" 203 210 msgstr "" 204 211 205 #: includes/class-quotanto-request-form.php: 475212 #: includes/class-quotanto-request-form.php:503 206 213 msgid "Your name" 207 214 msgstr "" 208 215 209 #: includes/class-quotanto-request-form.php: 479216 #: includes/class-quotanto-request-form.php:507 210 217 msgid "Email address" 211 218 msgstr "" 212 219 213 #: includes/class-quotanto-request-form.php: 484220 #: includes/class-quotanto-request-form.php:512 214 221 #: includes/class-quotanto-settings-page.php:160 215 222 msgid "Phone" 216 223 msgstr "" 217 224 218 #: includes/class-quotanto-request-form.php: 484225 #: includes/class-quotanto-request-form.php:512 219 226 msgid "Phone (optional)" 220 227 msgstr "" 221 228 222 #: includes/class-quotanto-request-form.php: 485229 #: includes/class-quotanto-request-form.php:513 223 230 msgid "Digits, spaces and + ( ) - . only" 224 231 msgstr "" 225 232 226 #: includes/class-quotanto-request-form.php: 490233 #: includes/class-quotanto-request-form.php:518 227 234 msgid "Anything we should know?" 228 235 msgstr "" 229 236 230 #: includes/class-quotanto-request-form.php: 490237 #: includes/class-quotanto-request-form.php:518 231 238 msgid "Anything we should know? (optional)" 232 239 msgstr "" 233 240 234 #: includes/class-quotanto-request-form.php:5 08241 #: includes/class-quotanto-request-form.php:536 235 242 msgid "Send request" 236 243 msgstr "" 237 244 238 #: includes/class-quotanto-request-form.php:5 23245 #: includes/class-quotanto-request-form.php:551 239 246 msgid "Too many requests from this connection. Please try again in an hour." 240 247 msgstr "" 241 248 242 #: includes/class-quotanto-request-form.php:5 24243 msgid "Please check the form and try again. If the page has been open for a long time, reload it first."244 msgstr "" 245 246 #: includes/class-quotanto-request-form.php:5 25249 #: includes/class-quotanto-request-form.php:552 250 msgid "Please check the form and try again." 251 msgstr "" 252 253 #: includes/class-quotanto-request-form.php:553 247 254 msgid "We could not save your request. Please try again in a moment." 248 255 msgstr "" 249 256 250 #: includes/class-quotanto-request-form.php:7 44257 #: includes/class-quotanto-request-form.php:776 251 258 msgid "Quote requested from the cart." 252 259 msgstr "" 253 260 254 #: includes/class-quotanto-request-form.php:7 44261 #: includes/class-quotanto-request-form.php:776 255 262 msgid "Quote requested from the product page." 256 263 msgstr "" -
quotanto/trunk/quotanto.php
r3751965 r3761105 1 1 <?php 2 2 /** 3 * Plugin Name: Quotanto 3 * Plugin Name: Quotanto – Request a Quote for WooCommerce 4 4 * Plugin URI: https://quotanto.com 5 5 * Description: Let customers request a quote, price it in WooCommerce, and let them pay at your normal checkout. Works with block cart and checkout. 6 * Version: 1.0. 16 * Version: 1.0.2 7 7 * Requires at least: 6.9 8 8 * Requires PHP: 8.1 … … 21 21 defined( 'ABSPATH' ) || exit; 22 22 23 define( 'QUOTANTO_VERSION', '1.0. 1' );23 define( 'QUOTANTO_VERSION', '1.0.2' ); 24 24 define( 'QUOTANTO_FILE', __FILE__ ); 25 25 define( 'QUOTANTO_DIR', plugin_dir_path( __FILE__ ) ); -
quotanto/trunk/readme.txt
r3751965 r3761105 1 === Quotanto ===1 === Quotanto – Request a Quote for WooCommerce === 2 2 Contributors: quotanto 3 Tags: woocommerce, quote, request a quote, rfq, b2b3 Tags: request a quote, hide price, catalog mode, rfq, woocommerce 4 4 Requires at least: 6.9 5 5 Tested up to: 7.1 6 6 Requires PHP: 8.1 7 7 Requires Plugins: woocommerce 8 Stable tag: 1.0. 18 Stable tag: 1.0.2 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html 11 11 12 Quote requests for WooCommerce that end at your own checkout. No second cart, no extra payment page, no cookies for browsing visitors.12 Adds a request a quote button to WooCommerce, with hidden prices and a catalog mode. Customers pay the quote at your own checkout. 13 13 14 14 == Description == 15 15 16 Quotanto adds a quote flow to WooCommerce and stays out of the way.16 Add a request a quote button to WooCommerce, hide prices or run a catalog mode. Quotes end at your own checkout. 17 17 18 18 1. A customer asks for a quote on a product page, or for their whole cart. … … 121 121 Other captcha plugins can hook into `quotanto_form_fields` and `quotanto_validate_request`. 122 122 123 = Does it work with my theme or Elementor? = 124 125 Yes, with block and classic themes and Elementor pages. In Elementor Pro product templates, add the `[quotanto_request]` shortcode. 126 123 127 = Does it send data anywhere? = 124 128 … … 137 141 == Changelog == 138 142 143 = 1.0.2 = 144 * New: Clear message when a form page has expired. 145 * Fix: Quote form and shop link in product shortcodes on block themes. 146 * Fix: Cart quote form lines up with the cart on classic themes. 147 * Tested with more themes and page builders. 148 139 149 = 1.0.1 = 140 150 * Fix: Request limit counts each visitor, also behind a proxy.
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)