Plugin Directory

Changeset 3761105


Ignore:
Timestamp:
10/23/2026 07:33:33 AM (45 hours ago)
Author:
quotanto
Message:

Release 1.0.2

Location:
quotanto
Files:
31 added
6 edited

Legend:

Unmodified
Added
Removed
  • quotanto/trunk/assets/quotanto.css

    r3751965 r3761105  
    7474}
    7575
     76.quotanto__notice--info {
     77        border-color: #2f6fb3;
     78}
     79
    7680.quotanto__notice--error {
    7781        border-color: #b42318;
  • quotanto/trunk/blocks/request-button/block.json

    r3751965 r3761105  
    33        "apiVersion": 3,
    44        "name": "quotanto/request-button",
    5         "version": "1.0.1",
     5        "version": "1.0.2",
    66        "title": "Request a quote",
    77        "category": "woocommerce",
  • quotanto/trunk/includes/class-quotanto-request-form.php

    r3751965 r3761105  
    9292
    9393        /**
     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        /**
    94115         * On a block theme the classic product template only runs inside WooCommerce's Classic Template block.
    95116         *
    … …  
    112133                // Block templates replay this classic hook next to the price for compatibility, which is the
    113134                // 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() ) {
    115136                        return;
    116137                }
    … …  
    168189                global $product;
    169190                // 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() ) {
    171192                        return;
    172193                }
    … …  
    215236
    216237        /**
    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.
    218239         *
    219240         * @param string $content Block HTML.
    220241         */
    221242        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>';
    223248        }
    224249
    … …  
    283308        private static function state(): string {
    284309                $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 : '';
    286311        }
    287312
    … …  
    303328                if ( 'sent' === $state ) {
    304329                        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>';
    305333                } elseif ( self::has_notice( $state ) ) {
    306334                        echo '<p class="quotanto__notice quotanto__notice--error" role="alert">' . esc_html( self::error_message( $state ) ) . '</p>';
    … …  
    522550                        'captcha' => __( 'Please complete the captcha and try again.', 'quotanto' ),
    523551                        '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' ),
    525553                        default   => __( 'We could not save your request. Please try again in a moment.', 'quotanto' ),
    526554                };
    … …  
    646674                }
    647675
     676                // A nonce fails when the page was open longer than the nonce lives, or the visitor logged in or out since.
    648677                if ( ! $verified ) {
    649                         self::finish( $back, 'invalid', $anchor );
     678                        self::finish( $back, 'expired', $anchor );
    650679                }
    651680
    … …  
    659688                 */
    660689                $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 ) {
    662691                        self::finish( $back, 'invalid', $anchor );
     692                }
     693                if ( $age > WEEK_IN_SECONDS ) {
     694                        self::finish( $back, 'expired', $anchor );
    663695                }
    664696
  • quotanto/trunk/languages/quotanto.pot

    r3751965 r3761105  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Quotanto 1.0.1\n"
     5"Project-Id-Version: Quotanto – Request a Quote for WooCommerce 1.0.2\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/quotanto\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
    … …  
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"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"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.12.0\n"
    … …  
    1616
    1717#. Plugin Name of the plugin
    18 #. Author of the plugin
    1918#: quotanto.php:3
    20 #: quotanto.php:12
    21 msgid "Quotanto"
     19msgid "Quotanto – Request a Quote for WooCommerce"
    2220msgstr ""
    2321
    … …  
    3028#: quotanto.php:5
    3129msgid "Let customers request a quote, price it in WooCommerce, and let them pay at your normal checkout. Works with block cart and checkout."
     30msgstr ""
     31
     32#. Author of the plugin
     33#: quotanto.php:12
     34msgid "Quotanto"
    3235msgstr ""
    3336
    … …  
    7780
    7881#: includes/class-quotanto-captcha.php:183
    79 #: includes/class-quotanto-request-form.php:522
     82#: includes/class-quotanto-request-form.php:550
    8083msgid "Please complete the captcha and try again."
    8184msgstr ""
    … …  
    174177
    175178#. translators: 1: button text, for example "Request a quote". 2: product name.
    176 #: includes/class-quotanto-request-form.php:159
     179#: includes/class-quotanto-request-form.php:180
    177180#, php-format
    178181msgid "%1$s: %2$s"
    179182msgstr ""
    180183
    181 #: includes/class-quotanto-request-form.php:304
     184#: includes/class-quotanto-request-form.php:329
    182185msgid "Thanks. We have your request and sent you a confirmation email. We will reply with a quote."
    183186msgstr ""
    184187
    185 #: includes/class-quotanto-request-form.php:316
     188#: includes/class-quotanto-request-form.php:332
     189msgid "This page has expired. Reload it and send again."
     190msgstr ""
     191
     192#: includes/class-quotanto-request-form.php:344
    186193msgid "Log in to request a quote"
    187194msgstr ""
    188195
    189 #: includes/class-quotanto-request-form.php:394
     196#: includes/class-quotanto-request-form.php:422
    190197msgid "Remove the products that cannot be quoted to send this cart as a request."
    191198msgstr ""
    192199
    193 #: includes/class-quotanto-request-form.php:419
     200#: includes/class-quotanto-request-form.php:447
    194201msgid "Choose an option"
    195202msgstr ""
    196203
    197 #: includes/class-quotanto-request-form.php:455
     204#: includes/class-quotanto-request-form.php:483
    198205msgid "These products are sold by quote. Send the cart as a request and we will reply with prices."
    199206msgstr ""
    200207
    201 #: includes/class-quotanto-request-form.php:470
     208#: includes/class-quotanto-request-form.php:498
    202209msgid "Quantity"
    203210msgstr ""
    204211
    205 #: includes/class-quotanto-request-form.php:475
     212#: includes/class-quotanto-request-form.php:503
    206213msgid "Your name"
    207214msgstr ""
    208215
    209 #: includes/class-quotanto-request-form.php:479
     216#: includes/class-quotanto-request-form.php:507
    210217msgid "Email address"
    211218msgstr ""
    212219
    213 #: includes/class-quotanto-request-form.php:484
     220#: includes/class-quotanto-request-form.php:512
    214221#: includes/class-quotanto-settings-page.php:160
    215222msgid "Phone"
    216223msgstr ""
    217224
    218 #: includes/class-quotanto-request-form.php:484
     225#: includes/class-quotanto-request-form.php:512
    219226msgid "Phone (optional)"
    220227msgstr ""
    221228
    222 #: includes/class-quotanto-request-form.php:485
     229#: includes/class-quotanto-request-form.php:513
    223230msgid "Digits, spaces and + ( ) - . only"
    224231msgstr ""
    225232
    226 #: includes/class-quotanto-request-form.php:490
     233#: includes/class-quotanto-request-form.php:518
    227234msgid "Anything we should know?"
    228235msgstr ""
    229236
    230 #: includes/class-quotanto-request-form.php:490
     237#: includes/class-quotanto-request-form.php:518
    231238msgid "Anything we should know? (optional)"
    232239msgstr ""
    233240
    234 #: includes/class-quotanto-request-form.php:508
     241#: includes/class-quotanto-request-form.php:536
    235242msgid "Send request"
    236243msgstr ""
    237244
    238 #: includes/class-quotanto-request-form.php:523
     245#: includes/class-quotanto-request-form.php:551
    239246msgid "Too many requests from this connection. Please try again in an hour."
    240247msgstr ""
    241248
    242 #: includes/class-quotanto-request-form.php:524
    243 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:525
     249#: includes/class-quotanto-request-form.php:552
     250msgid "Please check the form and try again."
     251msgstr ""
     252
     253#: includes/class-quotanto-request-form.php:553
    247254msgid "We could not save your request. Please try again in a moment."
    248255msgstr ""
    249256
    250 #: includes/class-quotanto-request-form.php:744
     257#: includes/class-quotanto-request-form.php:776
    251258msgid "Quote requested from the cart."
    252259msgstr ""
    253260
    254 #: includes/class-quotanto-request-form.php:744
     261#: includes/class-quotanto-request-form.php:776
    255262msgid "Quote requested from the product page."
    256263msgstr ""
  • quotanto/trunk/quotanto.php

    r3751965 r3761105  
    11<?php
    22/**
    3  * Plugin Name:       Quotanto
     3 * Plugin Name:       Quotanto – Request a Quote for WooCommerce
    44 * Plugin URI:        https://quotanto.com
    55 * 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.1
     6 * Version:           1.0.2
    77 * Requires at least: 6.9
    88 * Requires PHP:      8.1
    … …  
    2121defined( 'ABSPATH' ) || exit;
    2222
    23 define( 'QUOTANTO_VERSION', '1.0.1' );
     23define( 'QUOTANTO_VERSION', '1.0.2' );
    2424define( 'QUOTANTO_FILE', __FILE__ );
    2525define( 'QUOTANTO_DIR', plugin_dir_path( __FILE__ ) );
  • quotanto/trunk/readme.txt

    r3751965 r3761105  
    1 === Quotanto ===
     1=== Quotanto – Request a Quote for WooCommerce ===
    22Contributors: quotanto
    3 Tags: woocommerce, quote, request a quote, rfq, b2b
     3Tags: request a quote, hide price, catalog mode, rfq, woocommerce
    44Requires at least: 6.9
    55Tested up to: 7.1
    66Requires PHP: 8.1
    77Requires Plugins: woocommerce
    8 Stable tag: 1.0.1
     8Stable tag: 1.0.2
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1111
    12 Quote requests for WooCommerce that end at your own checkout. No second cart, no extra payment page, no cookies for browsing visitors.
     12Adds a request a quote button to WooCommerce, with hidden prices and a catalog mode. Customers pay the quote at your own checkout.
    1313
    1414== Description ==
    1515
    16 Quotanto adds a quote flow to WooCommerce and stays out of the way.
     16Add a request a quote button to WooCommerce, hide prices or run a catalog mode. Quotes end at your own checkout.
    1717
    18181. A customer asks for a quote on a product page, or for their whole cart.
    … …  
    121121Other captcha plugins can hook into `quotanto_form_fields` and `quotanto_validate_request`.
    122122
     123= Does it work with my theme or Elementor? =
     124
     125Yes, with block and classic themes and Elementor pages. In Elementor Pro product templates, add the `[quotanto_request]` shortcode.
     126
    123127= Does it send data anywhere? =
    124128
    … …  
    137141== Changelog ==
    138142
     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
    139149= 1.0.1 =
    140150* Fix: Request limit counts each visitor, also behind a proxy.
Note: See TracChangeset for help on using the changeset viewer.