Plugin Directory

Changeset 3787432


Ignore:
Timestamp:
11/06/2026 08:11:47 AM (24 hours ago)
Author:
pluginsshop
Message:

Release Cartdini 1.8.8: checkout integrity fixes and improved readme

  • Preserve saved shipping answers on virtual Checkout Blocks orders.
  • Guard customer cleanup identity and remove obsolete draft answers safely.
  • Report failed discovery-preference erasure as retryable.
  • Improve Free feature descriptions, examples, FAQs and relevant search tags.
  • Add related WordPress.org links to Frictions and OrderFloor.
  • Preserve full release history in changelog.txt.
  • Retain existing artwork and compatibility with Pro 1.8.7.
Location:
cartdini
Files:
75 added
8 edited

Legend:

Unmodified
Added
Removed
  • cartdini/trunk/administration-notes.txt

    r3785872 r3787432  
    1313== Installation ==
    1414
    15 Current matched release: Free 1.8.7 and Pro 1.8.7. Pro 1.8.7 requires Free 1.8.4 or newer. Existing Pro 1.8.4 remains compatible while you update Free first.
     15Current Free release: 1.8.8, compatible with the existing Pro 1.8.7 add-on. This corrective Free release does not require a new Pro ZIP. Pro 1.8.7 requires Free 1.8.4 or newer.
    1616
    17171. Install and activate WooCommerce 9.9 or newer.
    … …  
    21215. Add a test product to the cart, open checkout and complete a test order. Check answers in the order and supported emails before production.
    2222
    23 To upgrade an existing installation, back up the site, then upload this ZIP and choose Replace current with uploaded. Saved fields, settings and order answers are retained. If you use Pro, update Free first, then update Cartdini Pro to 1.8.7. Older incompatible add-ons pause safely until updated; their saved definitions remain retained during the update. Test your saved configurations and complete checkout flows on staging before updating the live store.
     23To upgrade an existing installation, back up the site, then upload this ZIP and choose Replace current with uploaded. Saved fields, settings and historical order answers are retained. If you already use Pro 1.8.7, leave it installed and update Free to 1.8.8. Older incompatible add-ons pause safely until updated; their saved definitions remain retained during the update. Test your saved configurations and complete checkout flows on staging before updating the live store.
    2424
    25 Free 1.8.0 and newer require Pro extension API 6. When upgrading an older pair, complete both plugin replacements together during a maintenance window: older Pro versions pause until updated so they cannot silently ignore new date restrictions. Pro license and update access remain available between replacements. An existing Pro 1.8.2 installation can remain in place when updating Free to 1.8.7.
     25Free 1.8.0 and newer require Pro extension API 6. When upgrading an older pair, complete both plugin replacements together during a maintenance window: older Pro versions pause until updated so they cannot silently ignore new date restrictions. Pro license and update access remain available between replacements. Free 1.8.8 retains this extension API and the Pro 1.8.7 requirements.
     26
     27CHECKOUT DATA INTEGRITY (1.8.8)
     28Virtual Checkout Blocks orders using custom address inputs preserve existing saved shipping answers instead of copying billing into shipping. Physical same-address and billing-only shipping retain their normal copies. Nonapplicable shipping is excluded from the new order and its display/email snapshot.
     29
     30When a shopper submits a reusable draft, pending or failed checkout order, removed Cartdini question metadata is reconciled against the current supported questions, including native fields. Historical completed orders and private attachment records are not purged by this checkout reconciliation. Hidden customer-answer cleanup checks both the authenticated shopper and the order identity.
     31
     32Administrator preference erasure processes a bounded batch and checks remaining keys. Busy or failed deletions return an explicit error: retry the WordPress personal-data erasure after the other writer finishes or storage recovers. A partial deletion is not reported as complete; do not repeatedly auto-retry a persistent storage failure.
    2633
    2734Configurations that use the expanded 1.3.0 format are saved separately. Cartdini preserves the previous compatible configuration and a recovery copy. An older plugin pair uses that older configuration; it cannot use the new controls. If an older version changes settings, the editor asks you to review both copies before choosing one. Back up the whole site before a downgrade, including orders and customer data.
  • cartdini/trunk/cartdini.php

    r3785872 r3787432  
    33 * Plugin Name: Cartdini – Checkout Field Editor for WooCommerce
    44 * Description: Edit WooCommerce checkout fields in Classic and Checkout Blocks, with an admin preview.
    5  * Version: 1.8.7
     5 * Version: 1.8.8
    66 * Author: pluginsshop
    77 * Author URI: https://plugins.shop/
    … …  
    1818 */
    1919defined( 'ABSPATH' ) || exit;
    20 define( 'CARTDINI_VERSION', '1.8.7' );
     20define( 'CARTDINI_VERSION', '1.8.8' );
    2121define( 'CARTDINI_FILE', __FILE__ );
    2222require_once __DIR__ . '/includes/class-activation-navigation.php';
  • cartdini/trunk/changelog.txt

    r3775804 r3787432  
    1 Cartdini Free historical changelog through 1.8.4
     1Cartdini Free full changelog through 1.8.8
     2
     3= 1.8.8 =
     4* Preserve saved shipping answers on virtual Checkout Blocks orders when address questions use the custom field bridge; retain physical same-address copying.
     5* Exclude nonapplicable shipping answers from bridge validation, current-order storage and order/email snapshots.
     6* Restrict hidden-answer customer cleanup to the current shopper and avoid saving unrelated account properties.
     7* Remove retired Cartdini question metadata from the current reusable checkout order without deleting current native fields, historical orders or private attachment records.
     8* Return a retryable privacy-erasure error when preferences are busy or cannot be deleted, instead of silently completing or paging indefinitely.
     9* Keep existing Cartdini Pro 1.8.7 compatible; no Pro update is required.
     10
     11= 1.8.7 =
     12* Add Plugins.shop attribution and an optional More Plugins collection with local artwork, clear availability and safe external links.
     13* Add an accessible Pro previews switch using the existing per-administrator/store preference; Off also suppresses optional suggestions.
     14* Keep Explore Pro available in Free and actual Pro workflows available regardless of the Free preview preference.
     15* Collapse mobile navigation behind Menu, preserve unfinished edits across collection navigation and keep the editor as the default destination.
     16* Keep custom order inputs before payment or submission when the native Order information block is missing; preserve explicitly positioned blocks.
     17* Clarify Checkout Blocks section placement and the limits of the structural preview.
     18* Match Pro 1.8.7 while preserving answer storage and licensing requirements.
     19
     20= 1.8.6 =
     21* Preserve saved shipping answers when a Classic order does not need shipping, while retaining normal billing-to-shipping copying for physical orders.
     22* Recover focused editor inputs, identify invalid Classic settings and refresh question labels and shared-field controls.
     23* Let shoppers clear optional radio answers in Classic Checkout and Checkout Blocks.
     24* Clarify first-checkout testing, draft-only deletion and failed-save recovery; improve Tools layout.
     25* Use pluginsshop author details and match Cartdini Pro 1.8.6.
     26* Add an optional, dismissible WordPress.org review invitation after established checkout use, shared with Pro.
     27* Keep review preferences local, scoped per administrator/store, and covered by personal-data export and erasure.
     28* Preserve checkout fields and saved drafts when responding to the invitation.
     29
     30= 1.8.5 =
     31* Clarify Free workflows, checkout compatibility, Pro boundaries, installation and privacy in the readme.
     32* Bundle detailed administrator notes and the full historical changelog.
     33* Open the editor after successful single manual Free activation; preserve bulk, network and background flows.
     34* Match the website logo and Pro 1.8.5. Checkout behavior and requirements are unchanged.
    235
    336= 1.8.4 =
  • cartdini/trunk/includes/class-blocks.php

    r3785872 r3787432  
    174174        foreach (Config::fields() as $field) {
    175175            foreach (Checkout::instances($field) as $instance) {
     176                if ($instance['scope']==='shipping' && !Checkout::shipping_applies()) { continue; }
    176177                if (self::uses_bridge($field)) { $values[$instance['key']]=$custom[$instance['key']] ?? ''; }
    177178                else { $group=$instance['scope']==='other' ? 'additional_fields' : $instance['scope'].'_address'; $values[$instance['key']]=$json[$group]['cartdini/'.$field['key']] ?? ''; }
    178179            }
    179180        }
    180         $copy=($extra['use_shipping_as_billing'] ?? false) === true && function_exists('WC') && WC()->cart && WC()->cart->needs_shipping_address();
    181         $no_shipping=function_exists('WC') && WC()->cart && !WC()->cart->needs_shipping_address();
    182         if ($no_shipping) { foreach (Config::fields() as $field) { if ($field['location']!=='address') { continue; } $instances=Checkout::instances($field); $values[$instances[1]['key']]=$values[$instances[0]['key']] ?? ''; } }
     181        $copy=($extra['use_shipping_as_billing'] ?? false) === true && Checkout::shipping_applies() && WC()->cart->needs_shipping_address();
     182        $billing_only=Checkout::shipping_applies() && !WC()->cart->needs_shipping_address();
     183        if ($billing_only) { foreach (Config::fields() as $field) { if ($field['location']!=='address') { continue; } $instances=Checkout::instances($field); $values[$instances[1]['key']]=$values[$instances[0]['key']] ?? ''; } }
    183184        if ($copy) { foreach (Config::fields() as $field) { if ($field['location']!=='address') { continue; } $instances=Checkout::instances($field); $values[$instances[0]['key']]=$values[$instances[1]['key']] ?? ''; } }
    184185        return $values;
    185186    }
    186187    public static function save_order($order,$request): void {
    187         if ($request->get_method() !== 'POST' || !self::fields()) { return; }
     188        if ($request->get_method() !== 'POST') { return; }
     189        if (!self::fields()) { Checkout::reconcile_blocks_order($order); return; }
    188190        $values=self::request_values($request); $plan=[];
    189191        // Validate every answer before changing any metadata. The authenticated
    … …  
    191193        foreach (self::fields() as $field) {
    192194            foreach (Checkout::instances($field) as $instance) {
     195                if ($instance['scope']==='shipping' && !Checkout::shipping_applies()) { continue; }
    193196                $visible=Checkout::visible($field,$instance,$values,$order);
    194197                $test=$field; $test['required']=Checkout::required($field,$instance,$values,$order);
    … …  
    206209        if (is_wp_error($plan)) { $detail=$plan->get_error_data();$status=is_array($detail)&&is_int($detail['status']??null)&&$detail['status']>=400&&$detail['status']<=599?$detail['status']:400;throw new \Automattic\WooCommerce\StoreApi\Exceptions\RouteException('cartdini_invalid',esc_html($plan->get_error_message()),(int)$status); }
    207210        if (!is_array($plan)) { throw new \Automattic\WooCommerce\StoreApi\Exceptions\RouteException('cartdini_invalid',esc_html__('Checkout fields could not be prepared. Please review your answers and retry.','cartdini'),400); }
     211        Checkout::reconcile_blocks_order($order);
    208212        foreach ($plan as $item) {
    209213            if ($item['value'] === null) { $order->delete_meta_data($item['key']); }
    … …  
    219223            if ($field['location']==='order') { continue; }
    220224            foreach (Checkout::instances($field) as $instance) {
     225                if ($instance['scope']==='shipping' && !Checkout::shipping_applies()) { continue; }
    221226                $key='_wc_'.$instance['scope'].'/cartdini/'.$field['key']; $value=$order->get_meta($key,true);
    222227                foreach ([$session,$persistent] as $customer) {
  • cartdini/trunk/includes/class-checkout.php

    r3776410 r3787432  
    101101    }
    102102    /** Shipping can apply even when the store only ships to the billing address. */
    103     private static function classic_shipping_applies(): bool {
     103    public static function shipping_applies(): bool {
    104104        return function_exists( 'WC' ) && WC()->cart && WC()->cart->needs_shipping();
    105105    }
    106106    private static function skip_shipping( array $i, array $values ): bool {
    107         return $i['scope'] === 'shipping' && ( ! self::classic_shipping_applies() || ! WC()->cart->needs_shipping_address() || empty( $values['ship_to_different_address'] ) );
     107        return $i['scope'] === 'shipping' && ( ! self::shipping_applies() || ! WC()->cart->needs_shipping_address() || empty( $values['ship_to_different_address'] ) );
    108108    }
    109109    /** Render malformed Free scalar input without changing the raw submission. */
    … …  
    158158                // An order without shipping must not replace or erase an
    159159                // unrelated saved shipping answer on a returning account.
    160                 if ($instance['scope']==='shipping' && !self::classic_shipping_applies()) { continue; }
     160                if ($instance['scope']==='shipping' && !self::shipping_applies()) { continue; }
    161161                $key='_wc_'.$instance['scope'].'/cartdini/'.$field['key']; $value=$order->get_meta($key,true);
    162162                foreach ([$session,$account] as $customer) {
    … …  
    194194        foreach ( Config::fields() as $f ) {
    195195            foreach ( self::instances( $f ) as $i ) {
    196                 if ( $i['scope'] === 'shipping' && ! self::classic_shipping_applies() ) { continue; }
     196                if ( $i['scope'] === 'shipping' && ! self::shipping_applies() ) { continue; }
    197197                $read_i = $i;
    198198                if ( self::skip_shipping( $i, $values ) ) { $read_i = self::instances($f)[0]; }
    … …  
    243243            if ( ! self::blocks_supported( $field ) ) { continue; }
    244244            foreach ( self::instances( $field ) as $instance ) {
     245                if ( $instance['scope'] === 'shipping' && ! self::shipping_applies() ) { continue; }
    245246                if ( ! self::visible( $field, $instance, $values, $order ) ) { continue; }
    246247                $value = Config::clean_value( $values[$instance['key']] ?? '', $field );
    … …  
    255256        self::check_answer_size( $entries, true );
    256257    }
     258    public static function reconcile_blocks_order( $order ): void {
     259        if ( ! $order instanceof \WC_Order || ! $order->has_status( ['checkout-draft','pending','failed'] ) || $order->is_paid() || $order->get_date_paid() ) { return; }
     260        $allowed = [];
     261        foreach ( Config::fields() as $field ) {
     262            if ( ! self::blocks_supported( $field ) ) { continue; }
     263            foreach ( self::instances( $field ) as $instance ) {
     264                if ( $instance['scope'] === 'shipping' && ! self::shipping_applies() ) { continue; }
     265                $allowed['_wc_'.$instance['scope'].'/cartdini/'.$field['key']] = true;
     266            }
     267        }
     268        foreach ( $order->get_meta_data() as $metadata ) {
     269            $stored = $metadata->get_data();
     270            if ( self::is_field_meta( $stored['key'] ) && ! isset( $allowed[$stored['key']] ) ) { $order->delete_meta_data( $stored['key'] ); }
     271        }
     272    }
    257273    public static function snapshot_blocks( $order ): void {
    258274        $service=\Automattic\WooCommerce\Blocks\Package::container()->get(\Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields::class);
    … …  
    261277            if(!self::blocks_supported($f)){continue;}
    262278            foreach(self::instances($f) as $i){
     279                if ( $i['scope'] === 'shipping' && ! self::shipping_applies() ) { $order->delete_meta_data('_wc_'.$i['scope'].'/cartdini/'.$f['key']); continue; }
    263280                // Recheck persisted source answers before creating the display
    264281                // order/email snapshot, even if a native service kept a hidden value.
    … …  
    278295    }
    279296    public static function clear_hidden_blocks( $object, $request ): void {
     297        if ( $object instanceof \WC_Customer && ! self::checkout_customer_matches( $object ) ) { return; }
    280298        $configured=Config::fields();$by_key=array_column($configured,null,'key');
    281299        // WC()->customer uses the session data store. Removing its metadata does
    … …  
    302320        // Only the authenticated checkout shopper may have account data removed.
    303321        // An order's customer ID is not proof of the current shopper's identity.
    304         if($id<1||$id!==(int)get_current_user_id()){return null;}
     322        if($id<1||!self::checkout_customer_matches($customer,$object)){return null;}
    305323        return new \WC_Customer($id);
     324    }
     325    private static function checkout_customer_matches( $customer, $order = null ): bool {
     326        if ( ! $customer instanceof \WC_Customer || (int)$customer->get_id() !== (int)get_current_user_id() ) { return false; }
     327        return ! $order instanceof \WC_Order || (int)$order->get_customer_id() === (int)$customer->get_id();
    306328    }
    307329    /** Shared cleanup: only the authenticated shopper is passed as $persistent. */
    308330    public static function clear_block_value( $object, array $field, array $instance, $persistent = null ): void {
     331        if ( $object instanceof \WC_Customer && ! self::checkout_customer_matches( $object ) ) { return; }
    309332        $key='_wc_'.$instance['scope'].'/cartdini/'.$field['key'];
    310         $object->delete_meta_data($key);
    311         if($persistent){$persistent->delete_meta_data($key);}
     333        if ( $object instanceof \WC_Order ) { $object->delete_meta_data($key); }
     334        if ( $instance['scope'] === 'shipping' && ! self::shipping_applies() ) { return; }
     335        if ( $object instanceof \WC_Customer && self::checkout_customer_matches($object) ) { $object->delete_meta_data($key); }
     336        if($persistent && self::checkout_customer_matches($persistent,$object)){$persistent->delete_meta_data($key);}
    312337        // Some WooCommerce releases persist hidden order fields before this action.
    313         if($object instanceof \WC_Order && WC()->customer){WC()->customer->delete_meta_data($key);WC()->customer->save();}
     338        $session=function_exists('WC') ? WC()->customer : null;
     339        if($object instanceof \WC_Order && self::checkout_customer_matches($session,$object)){
     340            $session->delete_meta_data($key);
     341            if(is_a($session->get_data_store()->get_current_class_name(),'WC_Customer_Data_Store_Session',true)){$session->save();}
     342            else{$session->save_meta_data();}
     343        }
    314344    }
    315345    public static function register_blocks(): void {
  • cartdini/trunk/includes/class-discovery-preferences.php

    r3775804 r3787432  
    371371            } finally { Lock::release( $lock, $token ); }
    372372        }
    373         $result['done'] = count( $keys ) <= 25 || ! $result['items_removed'];
     373        $remaining = self::owned_keys( $id );
     374        if ( is_wp_error( $remaining ) ) { return $remaining; }
     375        $result['done'] = ! $remaining;
     376        if ( $remaining && ( $result['items_retained'] || ! array_diff( $keys, $remaining ) ) ) {
     377            $result['items_retained'] = true;
     378            return new \WP_Error( 'cartdini_preferences_erasure_incomplete', __( 'Cartdini preferences could not be erased. Retry erasure.', 'cartdini' ), $result );
     379        }
    374380        return $result;
    375381    }
  • cartdini/trunk/languages/cartdini.pot

    r3785872 r3787432  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Cartdini – Checkout Field Editor for WooCommerce 1.8.7\n"
     5"Project-Id-Version: Cartdini – Checkout Field Editor for WooCommerce 1.8.8\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/cartdini\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
  • cartdini/trunk/readme.txt

    r3785872 r3787432  
    11=== Cartdini – Checkout Field Editor for WooCommerce ===
    22Contributors: pluginsshop
    3 Tags: woocommerce, checkout, checkout fields, checkout editor, checkout manager
     3Tags: woocommerce, checkout fields, checkout field editor, checkout manager, checkout blocks
    44Requires at least: 6.7
    55Tested up to: 7.1
    66Requires PHP: 8.0
    7 Stable tag: 1.8.7
     7Stable tag: 1.8.8
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1010
    11 Add custom WooCommerce checkout fields for delivery instructions, gift notes and order details. Supports Classic Checkout and Checkout Blocks.
     11Customize WooCommerce checkout fields: add gift notes, delivery instructions and billing details. Works with Classic Checkout and Checkout Blocks.
    1212
    1313== Description ==
    1414
    15 Cartdini is a WooCommerce checkout field editor for delivery instructions, gift notes, purchase references and other order details. Add questions without writing code and keep customers' answers with the order.
     15**Add custom WooCommerce checkout fields without code.** Collect gift messages, delivery instructions and purchase references; keep customers' answers with the order.
    1616
    17 The Free plugin includes nine custom field types in both Classic Checkout and Checkout Blocks: text, select, checkbox, textarea, email, telephone, number, radio and URL. It works without a Cartdini account, license or external service.
     17Nine Free field types work in Classic Checkout and Checkout Blocks. No Cartdini account, license or subscription.
    1818
    19 = What you can do with Free =
     19= Custom checkout fields included in Free =
    2020
    21 * Add up to 100 custom fields to order information, contact details or both billing and shipping addresses.
    22 * Write labels and help text, choose optional or required answers, and configure supported text limits, number ranges and steps.
    23 * Reorder or duplicate questions and preview their structure before saving.
    24 * Choose answer display in admin/customer orders and supported admin/customer emails.
    25 * Edit supported existing checkout fields while keeping WooCommerce's essential address validation.
    26 * Recover an unfinished local editor draft, review it and save explicitly.
    27 * Use WooCommerce High-Performance Order Storage (HPOS) or legacy order storage.
     21* **Nine types:** text, textarea, dropdown select, checkbox, radio buttons, email, telephone, number and URL.
     22* **Up to 100 questions:** Order information, Contact or shared billing/shipping addresses.
     23* **Field controls:** labels, help, supported placeholders, optional/required answers and supported text/number limits.
     24* **Editor:** reorder, duplicate, disable and structurally preview questions; save explicitly.
     25* **Saved answers:** select display on supported admin/customer order pages and emails.
     26* **Storage:** High-Performance Order Storage (HPOS) and legacy orders.
    2827
    29 Add an optional "Delivery instructions" textarea, save it, add a test product to your cart and check its answer on a test order. A gift note is Free; showing it only when another answer matches is Pro.
     28= Practical questions for your store =
    3029
    31 [Setup guides](https://cartdini.com/docs) | [Classic and Blocks compatibility](https://cartdini.com/compatibility) | [Support](https://cartdini.com/support)
     30* Add a **gift message** textarea for the packing team.
     31* Collect **delivery instructions** or an additional contact number alongside an address.
     32* Collect a **purchase order number** as text; this does not create purchase orders or change payment methods.
     33* Offer a dropdown or radio question for an order preference, without adding a charge.
    3234
    33 = Editing existing checkout fields =
     35Address questions share billing/shipping definitions. Virtual orders preserve saved shipping answers instead of replacing them with billing answers.
    3436
    35 Classic supports label, placeholder and position changes for supported billing, shipping and order fields. Essential address/email visibility and validation remain native. Six optional-field controls allow visibility/requirement changes; reset individual overrides.
     37= Manage existing checkout fields =
    3638
    37 Checkout Blocks supports shared Company, Phone and Address line 2 visibility/requirement settings, labels and order for ten shared address fields, and the default Place Order button text. Shared address changes can also affect Classic checkout; explicit Classic overrides take precedence. Native settings save separately, outside custom-field imports, templates and history. WooCommerce retains country-specific requirements, hidden fields and validation. Blocks contact email, address placeholders and section content remain native controls; payment extensions may supply their own button text.
     39Checkout manager controls differ by checkout:
    3840
    39 = When you need Pro =
     41* **Classic:** supported labels, placeholders and positions; six optional-field visibility/requirement controls and individual resets.
     42* **Blocks:** shared Company, Phone and Address line 2 visibility/requirements, supported address labels/order and default Place Order text. Shared settings can affect Classic; explicit Classic overrides win. Native settings save separately.
    4043
    41 [Cartdini Pro](https://cartdini.com/free-vs-pro) is a separate paid add-on. It adds conditional questions, fixed paid extras for eligible order choices, private file uploads, date availability rules, time and multiple-choice inputs, repeatable groups, reusable custom-field setups and order-answer CSV tools. Check workflow requirements before purchase. Pro is optional.
     44Essential address/email validation and country requirements stay with WooCommerce. Blocks contact email, address placeholders and sections remain native; gateways may override button text. Cartdini does not remove every required field.
    4245
    43 Optional Pro previews show local examples and real control screenshots. The Pro previews switch hides or restores preview navigation and optional suggestions for your administrator account on this store. Explore Pro remains available when previews are off. Examples never publish fields, upload files or add charges. Tasks require an explicit remember/pricing action; previewing does not enable measurement.
     46[Setup guides](https://cartdini.com/docs) | [Compatibility and limitations](https://cartdini.com/compatibility) | [Support](https://cartdini.com/support)
    4447
    45 The editor opens first. More Plugins is a separate, optional collection of tools from Plugins.shop, with bundled logos and descriptions. Opening the collection makes no external request and installs nothing. Product links open Plugins.shop in a new tab; check current availability there. On small screens, the Menu button expands navigation without pushing the editor down on every visit.
     48= Optional Cartdini Pro add-on =
     49
     50Separate paid [Cartdini Pro](https://cartdini.com/free-vs-pro) adds conditional fields, fixed paid extras for eligible choices, private uploads, date availability rules, time/multiple-choice inputs, repeatable groups, reusable setups and order-answer CSV tools. Uploads/groups are order-only.
     51
     52Initial activation unlocks Pro editing/tools. Disconnecting locks tools but saved checkout behavior continues; deactivation pauses Pro-only behavior. Pro is optional, not auto-installed. Previews never publish fields, upload files or charge shoppers.
     53
     54= Related WooCommerce plugins from Plugins.shop =
     55
     56Optional tools from our team for your WooCommerce store:
     57
     58* [Frictions – WooCommerce checkout session replay](https://wordpress.org/plugins/frictions/) — investigate eligible checkout visits and observed errors with protected structural replay.
     59* [OrderFloor – WooCommerce minimum order and quantity limits](https://wordpress.org/plugins/orderfloor/) — set minimum order amounts, quantity limits and pack multiples.
     60* [OrderWorth – WooCommerce cost of goods and profit reports](https://wordpress.org/plugins/orderworth/) — review product costs, expenses and estimated profit for WooCommerce orders.
     61
     62Neither is required or auto-installed. Check their requirements; links do not imply automatic integration or guaranteed compatibility.
    4663
    4764== Installation ==
    4865
    49 Requires WordPress 6.7+, PHP 8.0+ and WooCommerce 9.9+. JavaScript is required for the editor. Current matched release: Free 1.8.7 and Pro 1.8.7.
     66Requires WordPress 6.7+, PHP 8.0+, WooCommerce 9.9+ and JavaScript for the editor.
    5067
    51 1. Install and activate WooCommerce.
    52 2. In Plugins > Add New, search for Cartdini by pluginsshop and choose Install Now. Alternatively, use Upload Plugin with the Free ZIP.
    53 3. Use WordPress's Activate Plugin control. A successful single manual activation opens the Cartdini editor.
    54 4. Add your questions, review them and choose Save. For bulk/network activation, open WooCommerce > Cartdini yourself.
    55 5. Add a test product to your cart, then open checkout. Complete test checkouts, including missing or invalid answers, and check order/email display before using the configuration on a live store.
     681. Activate WooCommerce, then install Cartdini through Plugins > Add New or upload the Free ZIP.
     692. Activate Cartdini. Ordinary single activation opens the editor; otherwise open WooCommerce > Cartdini.
     703. Add a question, review its type/location and choose Save questions & fields.
     714. Test checkout, missing/invalid answers and saved order/email answers before going live.
    5672
    57 Back up your site before updating. Upload the new ZIP and choose Replace current with uploaded; do not uninstall first. With Pro, update Free first and Pro second, keeping both active. Pro 1.8.7 requires Free 1.8.4 or newer; the recommended matched pair is 1.8.7. Settings and saved order answers are retained. Update pairs older than 1.8.0 together: incompatible Pro extensions pause safely. Older formats/recovery: administration-notes.txt.
     73Fresh installs enable no sample questions. Save applies changes; reactivation resumes saved settings.
     74
     75Back up; replace without uninstalling. Keep Free active and update it before Pro. Free 1.8.8 supports Pro 1.8.7 (minimum Free 1.8.4). Settings/history are retained. Update pre-1.8.0 pairs together; incompatible Pro features pause. Recovery details: administration-notes.txt.
    5876
    5977== Frequently Asked Questions ==
    6078
    61 = Does Free work without an account or license? =
    62 Yes. Free field editing and checkout features need no Cartdini account, license key or subscription. Free makes no requests to the Cartdini license service.
     79= Can I add a gift note or delivery instructions for free? =
     80Yes: add a textarea, label it and choose its location. Conditional display requires Pro. Gift notes are separate from WooCommerce's native "Add a note to your order" option; label them distinctly.
    6381
    64 = Does activating Cartdini change my checkout immediately? =
    65 New installations enable no sample fields. Save to apply a configuration. Reactivation resumes saved settings; previews and recovered drafts do not publish them.
     82= Does it work with the block checkout? =
     83Yes: all nine Free types support both. Existing-field controls differ as described above. Test your theme/gateways; not every third-party checkout builder is supported.
    6684
    67 = Does it work with WooCommerce Checkout Blocks and HPOS? =
    68 Yes. All nine Free custom field types support Classic and Checkout Blocks, and order data uses WooCommerce's CRUD APIs for HPOS and legacy orders. Existing-field controls differ between checkouts as described above. Test your theme and payment extensions on the actual checkout. The compatibility guide lists recorded environments and limits.
     85= Why does my custom field appear after payment? =
     86Order questions follow the Order information block in your Checkout page. Move that block before Payment in the page editor if preferred. Question-reorder arrows do not move sections. The structural preview is not your exact storefront layout.
    6987
    70 = Where can I see customers' answers? =
    71 Answers stay with orders and, for supported contact/address fields, customer accounts. Choose their display on supported order pages/emails; hiding answers does not delete them. Order-answer CSV export and optional Orders-list answer columns belong to Pro.
     88= Where are checkout answers saved? =
     89With orders and, for supported contact/address fields, customer accounts. Order-only notes are not account-prefilled. Display settings control supported order/email output; hiding answers does not delete them. CSV export and optional Orders-list answer columns require Pro.
    7290
    73 = Can I make a question conditional, charge for a choice or accept a file? =
    74 Those are Pro features. Free collects ordinary notes. Pro adds conditions, fixed positive fees for eligible order choices and private JPG/PNG/PDF files. Uploads and repeatable groups use the order location only. Pro's initial license activation unlocks editing and tools; disconnecting locks those tools while saved checkout behavior continues. Deactivating Pro pauses Pro-only behavior. Review the separate add-on's licensing and compatibility details.
     91= What if I leave before saving? =
     92Drafts last up to 24 hours in the same browser tab for your administrator/store. Closing it or clearing storage may remove them. Recovery requires review and Save. Follow retry/reload guidance after conflicts; custom questions and native Blocks settings save separately.
    7593
    76 = What happens to my unfinished work? =
    77 A draft stays in this browser tab for up to 24 hours, scoped to this administrator/store. Closing the tab or clearing storage may remove it. Recovery checks newer settings and loads a draft for review; Save is still required. Custom fields and native Blocks settings have separate save actions. Follow retry/reload guidance rather than deleting recovery records.
    78 
    79 = Does deleting the plugin remove settings or order answers? =
    80 No. Deactivation and deletion retain configuration, recovery data and saved answers. Removing a field does not erase historical answers. While Free and WooCommerce are active, WordPress personal-data tools export saved account/order answers; erasure removes account answers and follows WooCommerce's order-retention settings. If an order has private Pro uploads, keep or reactivate a compatible Pro for file erasure; this needs no license. Failed or unreadable storage is reported for retry, and order identity/answers are retained until private-file erasure can be checked safely. Removing both plugins leaves retained data outside their privacy tools.
     94= What happens if I remove a field or delete Cartdini? =
     95Removing a field keeps historical answers. Deactivation/deletion retains settings, recovery data and saved answers. With Free and WooCommerce active, WordPress privacy tools export account/order answers; erasure removes account answers and follows WooCommerce order-retention settings. Keep compatible Pro active to erase private uploads; no license is needed for erasure. Failed/unreadable storage remains retryable, preserving order identity/answers until private-file erasure is checked safely. Removing both plugins leaves retained data outside their privacy tools.
    8196
    8297== Privacy and local preferences ==
    8398
    84 Free stores answers in your WordPress/WooCommerce database. It sends no answers or administrator preferences to Cartdini. Request only necessary information and explain its purpose, recipients and retention in your store's privacy policy. Suggested wording is available in WordPress Settings > Privacy.
     99Answers stay in your WordPress/WooCommerce database. Free sends no answers or administrator preferences to Cartdini. Request necessary information only and explain purpose, recipients and retention in your privacy policy. Suggested wording: Settings > Privacy.
    85100
    86 Browser drafts store field settings, not shopper answers, preview answers, files or license credentials. Full-screen and individual contextual suggestion dismissals use browser storage; WordPress navigation is visible by default. Optional remembered Pro tasks use site-scoped administrator metadata and expire after seven days. The Pro previews switch also uses administrator/store-scoped WordPress metadata, persists across browsers and updates, and controls optional suggestions as well as preview navigation. It never disables actual Pro workflows.
     101* **Browser storage:** drafts hold field settings, not shopper/preview answers, files or credentials. Full-screen/contextual dismissals use browser storage; WordPress navigation is visible by default.
     102* **Pro previews:** On/Off persists per administrator/store across browsers/updates. It controls optional previews/suggestions, not real Pro workflows; Explore Pro stays available. Remembered tasks require an explicit remember/pricing action, use local administrator metadata and expire after seven days.
     103* **Local action counts:** off by default. When enabled, preview opens, pricing clicks and matching workflow saves are counted for 90 days, not purchases/conversion rates. No shopper answers/license keys; disabling clears counts. Expired tasks/counts use WordPress cron and support privacy export/erasure. Previewing is not consent to measurement.
     104* **Review invitation:** only in Cartdini settings after 30 days and qualifying checkout use on three dates; disabled on development/local/staging sites. Eligibility stores a first-use time and at most three dates, no order IDs/customer details/answers. Local per-administrator/store review/dismissal preferences persist across updates and support export/erasure. Remind in 30 days or stop permanently; no payment verification, review-submission tracking or external analytics.
    87105
    88 Optional local action counts are off by default. If enabled, they count preview opens, pricing-link clicks and matching workflow saves for 90 days; they do not measure purchases or conversion rates. Counts contain no shopper answers or license keys, stay on the store and are cleared when disabled. Expired tasks and counts are scheduled for removal through WordPress cron while active and are included in personal-data export/erasure. Opening an example is not consent to measurement. Free makes no external service requests; following documentation, support or pricing links opens the selected website. Pro license connections are separately disclosed and require an explicit administrator action.
    89 
    90 A quiet review invitation appears only in Cartdini settings after at least 30 days from the first recorded qualifying checkout and use on three distinct days. Eligibility keeps only a local first-use time and up to three checkout creation dates, without order identifiers, customer details or answer copies. It is not payment verification or external analytics. The invitation is disabled in WordPress development, local and staging environments. Review-link and dismissal preferences are scoped to the administrator and website, persist across updates, and are included in personal-data export and erasure. Reminders can be delayed for 30 days or stopped permanently. Opening the WordPress.org review link does not tell Cartdini whether a review was submitted.
     106The optional More Plugins screen bundles artwork/descriptions; opening it sends no request and installs nothing. Product links open Plugins.shop. Free makes no external service requests; following documentation/support/pricing links opens that website. Pro license connections are separately disclosed and require explicit administrator action.
    91107
    92108== Source and licenses ==
    93109
    94 All executable PHP, JavaScript and CSS is included as readable source; no compilation or external download is needed. See assets/SOURCE.md for dependencies and translation instructions. Locally bundled fonts make no external request and include their SIL Open Font License in assets/fonts/OFL.txt. Cartdini is licensed under GPLv2 or later; see LICENSE.txt.
     110Readable PHP/JavaScript/CSS need no build. Dependencies/translations: assets/SOURCE.md. Local fonts make no requests; SIL license: assets/fonts/OFL.txt. Plugin license: GPLv2 or later, LICENSE.txt.
    95111
    96112== Screenshots ==
    … …  
    103119== Changelog ==
    104120
    105 = 1.8.7 =
    106 * Add Plugins.shop attribution and an optional More Plugins collection with local artwork, clear availability and safe external links.
    107 * Add an accessible Pro previews switch using the existing per-administrator/store preference; Off also suppresses optional suggestions.
    108 * Keep Explore Pro available in Free and actual Pro workflows available regardless of the Free preview preference.
    109 * Collapse mobile navigation behind Menu, preserve unfinished edits across collection navigation and keep the editor as the default destination.
    110 * Keep custom order inputs before payment or submission when the native Order information block is missing; preserve explicitly positioned blocks.
    111 * Clarify Checkout Blocks section placement and the limits of the structural preview.
    112 * Match Pro 1.8.7 while preserving answer storage and licensing requirements.
     121= 1.8.8 =
     122* Preserve saved shipping answers on virtual Checkout Blocks orders; retain normal physical-order copying.
     123* Exclude nonapplicable shipping answers from order details and emails.
     124* Harden customer identity checks and remove obsolete answers from reused checkout drafts, preserving history.
     125* Make busy or failed preference erasure retryable.
     126* Remain compatible with Pro 1.8.7; no Pro update required.
    113127
    114 = 1.8.6 =
    115 * Preserve saved shipping answers when a Classic order does not need shipping, while retaining normal billing-to-shipping copying for physical orders.
    116 * Recover focused editor inputs, identify invalid Classic settings and refresh question labels and shared-field controls.
    117 * Let shoppers clear optional radio answers in Classic Checkout and Checkout Blocks.
    118 * Clarify first-checkout testing, draft-only deletion and failed-save recovery; improve Tools layout.
    119 * Use pluginsshop author details and match Cartdini Pro 1.8.6.
    120 * Add an optional, dismissible WordPress.org review invitation after established checkout use, shared with Pro.
    121 * Keep review preferences local, scoped per administrator/store, and covered by personal-data export and erasure.
    122 * Preserve checkout fields and saved drafts when responding to the invitation.
    123 
    124 = 1.8.5 =
    125 * Clarify Free workflows, checkout compatibility, Pro boundaries, installation and privacy in the readme.
    126 * Bundle detailed administrator notes and the full historical changelog.
    127 * Open the editor after successful single manual Free activation; preserve bulk, network and background flows.
    128 * Match the website logo and Pro 1.8.5. Checkout behavior and requirements are unchanged.
    129 
    130 = 1.8.4 =
    131 * Preserve Pro date availability restrictions when saving and resuming a local editor draft. Unfinished values remain available for review; normal validation and an explicit Save are still required.
    132 * Use Cartdini Pro 1.8.4 for the matched release.
    133 
    134 Earlier releases: changelog.txt.
     128Full release notes and earlier versions: changelog.txt.
Note: See TracChangeset for help on using the changeset viewer.