Quellcode durchsuchen

Remove legacy WC-session + per-upload cookie transports — v1.5.2 -> v1.5.3

Per the 1.5.0 rollout plan, the studiou_fpp_batch cookie is now the sole
transport for the visitor's upload batch. Delete the redundant writes
and their hangers-on:

- Upload::handle_chunk_upload no longer writes the
  studiou_fpp_attachment_id / _file_record_id / _file_name / _thumb_url
  keys to WC session, and no longer writes the studiou_fpp_att_id /
  studiou_fpp_rec_id cookie pair via set_upload_cookies().
- Delete set_upload_cookies() / clear_upload_cookies() and the
  COOKIE_ATTACHMENT / COOKIE_RECORD constants from the Upload class.
- Drop the ajax_set_upload_session / ajax_clear_upload_session
  handlers + their wp_ajax_* registrations from Single_Product —
  nothing calls them since the JS rewrite in 1.5.0.
- Rewrite Single_Product::resolve_uploaded_file() as a thin wrapper
  that returns the oldest unbound file from resolve_batch_files().
  The shortcode fallback path and the validate_add_to_cart fallback
  branch now both read exclusively through the batch transport.
- readme.md: remove the two retired AJAX rows from the actions table.

No functional change on the product-detail page — that path never used
the legacy transports after 1.5.0. This is strictly a cleanup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dalibor Votruba vor 2 Monaten
Ursprung
Commit
f4ce70ef52

+ 2 - 2
studiou-wc-free-photo-product/CLAUDE.md

@@ -2,7 +2,7 @@
 
 ## Project Overview
 
-WordPress/WooCommerce plugin called **studiou-wc-free-photo-product** (QDR - Studiou WC Free Photo Product) v1.5.2.
+WordPress/WooCommerce plugin called **studiou-wc-free-photo-product** (QDR - Studiou WC Free Photo Product) v1.5.3.
 Allows publishing special variable products where customers upload custom raw images to be printed for a price.
 
 ## Initial Requirements
@@ -38,7 +38,7 @@ Allows publishing special variable products where customers upload custom raw im
 
 - `Studiou_WC_FPP_DB` - Custom table CRUD, media category taxonomy. Batch-aware queries: `get_batch_files($token, $product_id)`, `count_batch_files($token, $product_id)`.
 - `Studiou_WC_FPP_Product` - WC product data tab, product meta (including `_studiou_fpp_max_uploads` — per-product cap on simultaneous uploads, 0 = unlimited).
-- `Studiou_WC_FPP_Upload` - Chunked upload AJAX, file assembly, attachment creation. Mints/reuses a 32-char `studiou_fpp_batch` cookie on first chunk-upload completion and persists it in the new file record's `batch_token` column. Enforces the per-product max-uploads cap before accepting chunk 0. Legacy `studiou_fpp_att_id`/`studiou_fpp_rec_id` cookies still written for one release as a read-fallback for the shortcode path.
+- `Studiou_WC_FPP_Upload` - Chunked upload AJAX, file assembly, attachment creation. Mints/reuses a 32-char `studiou_fpp_batch` cookie on first chunk-upload completion and persists it in the new file record's `batch_token` column. Enforces the per-product max-uploads cap before accepting chunk 0. The batch cookie is the **sole** transport since 1.5.3 — legacy WC-session keys and the short-lived `studiou_fpp_att_id`/`studiou_fpp_rec_id` cookie pair have been removed.
 - `Studiou_WC_FPP_Single_Product` - On `wp` priority 5, detaches pvtfw's variant table (`remove_action()` on the hook/priority read from pvtfw's own `pvtfw_variant_table_place` option) and pvtfw's available-options button on FPP products. pvtfw's singletons are reached via `PVTFW_PRINT_TABLE::instance()` / `PVTFW_AVAILABE_BTN::instance()` (the `$pvtfw_print_table` / `$pvtfw_available_btn` globals published by pvtfw end up as method-locals because pvtfw's `require_once` lives inside a method — the static singletons return the same real instances that pvtfw used to register the hooks). On `woocommerce_single_product_summary` priority 35 renders the upload dropzone + card stack + order-overview panel. Exposes `resolve_batch_files(Studiou_WC_FPP_DB $db, $product_id = 0)` (list, 1.5.0 path) and `resolve_uploaded_file()` (single, legacy shortcode path). Adds `studiou-fpp-product-page` body class. Server-side validation (`woocommerce_add_to_cart_validation` priority 1) accepts `$cart_item_data['studiou_fpp_file_record_id']` as the primary "photo present" signal, with the resolver as fallback.
 - `Studiou_WC_FPP_Shortcode` - `[studiou_free_photo_product id="X"]` renders product with WC native variation form (unchanged — still the single-file flow).
 - `Studiou_WC_FPP_Cart` - New AJAX endpoints `studiou_wcfpp_add_file_to_cart` (per-card Add-to-Cart, pre-stamps `file_record_id` on `$cart_item_data` and calls `WC()->cart->add_to_cart()`) and `studiou_wcfpp_remove_cart_line` (cart-only remove from overview panel). `add_cart_item_data()` prefers pre-stamped `file_record_id` from the new flow, falls back to the legacy visitor-level resolver. Replaces cart/order item thumbnails (classic cart via `woocommerce_cart_item_thumbnail`, block cart via `woocommerce_product_get_image_id`/`woocommerce_product_variation_get_image_id`), order item meta, order linking. `render_overview_html($product_id)` returns the panel HTML for in-place re-render after cart changes.

+ 2 - 1
studiou-wc-free-photo-product/includes/class-studiou-wc-fpp-cart.php

@@ -78,7 +78,8 @@ class Studiou_WC_FPP_Cart {
             unset($cart_item_data['studiou_fpp_file_record_id']);
         }
 
-        // 2) Legacy single-file path (shortcode) — resolve from WC session / legacy cookies.
+        // 2) Legacy single-file path (shortcode / non-card callers) — fall back to the
+        //    oldest unbound upload in the current batch.
         $resolved = Studiou_WC_FPP_Single_Product::resolve_uploaded_file($this->db);
         if (!$resolved) {
             return $cart_item_data;

+ 15 - 110
studiou-wc-free-photo-product/includes/class-studiou-wc-fpp-single-product.php

@@ -24,12 +24,6 @@ class Studiou_WC_FPP_Single_Product {
 
         // Safety net: if item was added without photo, remove it immediately
         add_action('woocommerce_add_to_cart', array($this, 'check_after_add_to_cart'), 10, 6);
-
-        // AJAX: store uploaded file reference in WC session (legacy — shortcode path)
-        add_action('wp_ajax_studiou_wcfpp_set_upload_session', array($this, 'ajax_set_upload_session'));
-        add_action('wp_ajax_nopriv_studiou_wcfpp_set_upload_session', array($this, 'ajax_set_upload_session'));
-        add_action('wp_ajax_studiou_wcfpp_clear_upload_session', array($this, 'ajax_clear_upload_session'));
-        add_action('wp_ajax_nopriv_studiou_wcfpp_clear_upload_session', array($this, 'ajax_clear_upload_session'));
     }
 
     /**
@@ -249,72 +243,28 @@ class Studiou_WC_FPP_Single_Product {
     }
 
     /**
-     * Resolve the currently "attached" upload for this visitor (legacy single-file path).
-     * Checks WC session first, then legacy cookie pair, and finally the first file in the
-     * current batch as a last-resort fallback. Used only by the shortcode flow on 1.5+ —
-     * the product-detail page uses resolve_batch_files() and pre-stamped file_record_id.
+     * Resolve the single "current" upload for callers that still think in single-file
+     * terms (shortcode fallback, the validate_add_to_cart fallback branch). Returns the
+     * oldest unbound file in the visitor's batch, or null if none.
+     *
+     * Since 1.5.3 this is just a thin wrapper on the batch transport — the legacy
+     * WC-session and legacy `studiou_fpp_att_id` / `studiou_fpp_rec_id` cookies are gone.
      *
      * @return array{attachment_id:int,file_record_id:int,file_name:string,thumb_url:string}|null
      */
     public static function resolve_uploaded_file(Studiou_WC_FPP_DB $db) {
-        $attachment_id  = 0;
-        $file_record_id = 0;
-        $file_name      = '';
-        $thumb_url      = '';
-
-        if (function_exists('WC') && WC()->session) {
-            $attachment_id  = (int) WC()->session->get('studiou_fpp_attachment_id');
-            $file_record_id = (int) WC()->session->get('studiou_fpp_file_record_id');
-            $file_name      = (string) WC()->session->get('studiou_fpp_file_name');
-            $thumb_url      = (string) WC()->session->get('studiou_fpp_thumb_url');
-        }
-
-        if ($attachment_id <= 0 || $file_record_id <= 0) {
-            $cookie_att = isset($_COOKIE[Studiou_WC_FPP_Upload::COOKIE_ATTACHMENT])
-                ? (int) $_COOKIE[Studiou_WC_FPP_Upload::COOKIE_ATTACHMENT] : 0;
-            $cookie_rec = isset($_COOKIE[Studiou_WC_FPP_Upload::COOKIE_RECORD])
-                ? (int) $_COOKIE[Studiou_WC_FPP_Upload::COOKIE_RECORD] : 0;
-            if ($cookie_att > 0 && $cookie_rec > 0) {
-                $attachment_id  = $cookie_att;
-                $file_record_id = $cookie_rec;
-                $file_name      = '';
-                $thumb_url      = '';
-            }
-        }
-
-        if ($attachment_id <= 0 || $file_record_id <= 0) {
+        $files = self::resolve_batch_files($db);
+        if (empty($files)) {
             return null;
         }
-
-        $record = $db->get_file_record($file_record_id);
-        if (!$record) {
-            return null;
-        }
-        if ((int) $record->attachment_id !== $attachment_id) {
-            return null;
-        }
-        // A record already attached to an order is no longer "in flight" for add-to-cart
-        if ((int) $record->order_id !== 0) {
-            return null;
-        }
-
-        if ($file_name === '') {
-            $file_name = (string) $record->file_name;
-        }
-        if ($thumb_url === '') {
-            $thumb = wp_get_attachment_image_src($attachment_id, 'thumbnail');
-            if ($thumb) {
-                $thumb_url = $thumb[0];
-            } else {
-                $thumb_url = (string) wp_get_attachment_url($attachment_id);
-            }
-        }
-
+        $first  = $files[0];
+        $att_id = (int) $first->attachment_id;
+        $thumb  = wp_get_attachment_image_src($att_id, 'thumbnail');
         return array(
-            'attachment_id'  => $attachment_id,
-            'file_record_id' => $file_record_id,
-            'file_name'      => $file_name,
-            'thumb_url'      => $thumb_url,
+            'attachment_id'  => $att_id,
+            'file_record_id' => (int) $first->id,
+            'file_name'      => (string) $first->file_name,
+            'thumb_url'      => $thumb ? $thumb[0] : (string) wp_get_attachment_url($att_id),
         );
     }
 
@@ -335,49 +285,4 @@ class Studiou_WC_FPP_Single_Product {
         }
     }
 
-    public function ajax_set_upload_session() {
-        check_ajax_referer('studiou-wcfpp-front-nonce', 'nonce');
-
-        $attachment_id = isset($_POST['attachment_id']) ? absint($_POST['attachment_id']) : 0;
-        $file_record_id = isset($_POST['file_record_id']) ? absint($_POST['file_record_id']) : 0;
-        $file_name = isset($_POST['file_name']) ? sanitize_text_field($_POST['file_name']) : '';
-
-        if (!$attachment_id || !$file_record_id) {
-            wp_send_json_error(array('message' => __('Invalid file data.', 'studiou-wc-free-photo-product')));
-            return;
-        }
-
-        if (WC()->session) {
-            WC()->session->set('studiou_fpp_attachment_id', $attachment_id);
-            WC()->session->set('studiou_fpp_file_record_id', $file_record_id);
-            WC()->session->set('studiou_fpp_file_name', $file_name);
-
-            // Store thumbnail URL directly so it can be used in cart without wp_get_attachment lookup
-            $thumb_url = '';
-            $thumb = wp_get_attachment_image_src($attachment_id, 'thumbnail');
-            if ($thumb) {
-                $thumb_url = $thumb[0];
-            } else {
-                $thumb_url = wp_get_attachment_url($attachment_id);
-            }
-            WC()->session->set('studiou_fpp_thumb_url', $thumb_url ?: '');
-        }
-
-        wp_send_json_success();
-    }
-
-    public function ajax_clear_upload_session() {
-        check_ajax_referer('studiou-wcfpp-front-nonce', 'nonce');
-
-        if (WC()->session) {
-            WC()->session->set('studiou_fpp_attachment_id', null);
-            WC()->session->set('studiou_fpp_file_record_id', null);
-            WC()->session->set('studiou_fpp_file_name', null);
-            WC()->session->set('studiou_fpp_thumb_url', null);
-        }
-
-        Studiou_WC_FPP_Upload::clear_upload_cookies();
-
-        wp_send_json_success();
-    }
 }

+ 4 - 41
studiou-wc-free-photo-product/includes/class-studiou-wc-fpp-upload.php

@@ -132,20 +132,9 @@ class Studiou_WC_FPP_Upload {
                 return;
             }
 
-            // Store the upload reference in the WC session immediately, in the same request.
-            // This avoids a race with a follow-up "set_upload_session" AJAX call that could
-            // still be in flight when the user clicks Add to Cart.
-            if (function_exists('WC') && WC()->session) {
-                WC()->session->set('studiou_fpp_attachment_id', $result['attachment_id']);
-                WC()->session->set('studiou_fpp_file_record_id', $result['file_record_id']);
-                WC()->session->set('studiou_fpp_file_name', $result['file_name']);
-                WC()->session->set('studiou_fpp_thumb_url', $result['thumbnail_url']);
-            }
-
-            // Also persist identifiers in a first-party cookie so the reference survives session
-            // paths that use a different identifier than the classic wp_woocommerce_session_* cookie
-            // (e.g. WC Store API / Cart-Token used by pvtfw 1.9.3+ add-to-cart).
-            self::set_upload_cookies($result['attachment_id'], $result['file_record_id']);
+            // Since 1.5.3 the batch_token cookie (set by resolve_or_mint_batch_token above
+            // and persisted in the DB row's batch_token column) is the sole transport —
+            // no more WC-session writes, no more legacy per-upload cookies.
 
             wp_send_json_success(array(
                 'complete'       => true,
@@ -421,14 +410,10 @@ class Studiou_WC_FPP_Upload {
 
         $this->db->delete_file_record($file_record_id);
 
-        self::clear_upload_cookies();
-
         wp_send_json_success(array('message' => __('File removed.', 'studiou-wc-free-photo-product')));
     }
 
-    const COOKIE_BATCH      = 'studiou_fpp_batch';
-    const COOKIE_ATTACHMENT = 'studiou_fpp_att_id';  // legacy — read-fallback only
-    const COOKIE_RECORD     = 'studiou_fpp_rec_id';  // legacy — read-fallback only
+    const COOKIE_BATCH = 'studiou_fpp_batch';
 
     /**
      * Return the current visitor's batch token, minting + persisting a new one if absent.
@@ -464,28 +449,6 @@ class Studiou_WC_FPP_Upload {
         unset($_COOKIE[self::COOKIE_BATCH]);
     }
 
-    public static function set_upload_cookies($attachment_id, $file_record_id) {
-        $attachment_id  = (int) $attachment_id;
-        $file_record_id = (int) $file_record_id;
-        if ($attachment_id <= 0 || $file_record_id <= 0) {
-            return;
-        }
-
-        $options = self::cookie_options(time() + DAY_IN_SECONDS);
-        @setcookie(self::COOKIE_ATTACHMENT, (string) $attachment_id, $options);
-        @setcookie(self::COOKIE_RECORD, (string) $file_record_id, $options);
-        // Mirror into $_COOKIE so code running later in this request sees the value too
-        $_COOKIE[self::COOKIE_ATTACHMENT] = (string) $attachment_id;
-        $_COOKIE[self::COOKIE_RECORD]     = (string) $file_record_id;
-    }
-
-    public static function clear_upload_cookies() {
-        $options = self::cookie_options(time() - DAY_IN_SECONDS);
-        @setcookie(self::COOKIE_ATTACHMENT, '', $options);
-        @setcookie(self::COOKIE_RECORD, '', $options);
-        unset($_COOKIE[self::COOKIE_ATTACHMENT], $_COOKIE[self::COOKIE_RECORD]);
-    }
-
     private static function cookie_options($expires) {
         $path = defined('COOKIEPATH') && COOKIEPATH ? COOKIEPATH : '/';
         return array(

+ 1 - 1
studiou-wc-free-photo-product/languages/studiou-wc-free-photo-product-cs_CZ.po

@@ -3,7 +3,7 @@
 # This file is distributed under the GPL v2 or later.
 msgid ""
 msgstr ""
-"Project-Id-Version: QDR - Studiou WC Free Photo Product 1.5.2\n"
+"Project-Id-Version: QDR - Studiou WC Free Photo Product 1.5.3\n"
 "Report-Msgid-Bugs-To: https://www.quadarax.com\n"
 "POT-Creation-Date: 2026-04-02 00:00+0000\n"
 "PO-Revision-Date: 2026-04-02 00:00+0000\n"

+ 1 - 1
studiou-wc-free-photo-product/languages/studiou-wc-free-photo-product.pot

@@ -2,7 +2,7 @@
 # This file is distributed under the GPL v2 or later.
 msgid ""
 msgstr ""
-"Project-Id-Version: QDR - Studiou WC Free Photo Product 1.5.2\n"
+"Project-Id-Version: QDR - Studiou WC Free Photo Product 1.5.3\n"
 "Report-Msgid-Bugs-To: https://www.quadarax.com\n"
 "POT-Creation-Date: 2026-04-02 00:00+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"

+ 0 - 2
studiou-wc-free-photo-product/readme.md

@@ -242,8 +242,6 @@ studiou-wc-free-photo-product/
 |---|---|---|
 | `studiou_wcfpp_upload_chunk` | Both | Upload a file chunk |
 | `studiou_wcfpp_remove_upload` | Both | Remove an uploaded file (pre-order) |
-| `studiou_wcfpp_set_upload_session` | Both | Store uploaded file reference in WC session |
-| `studiou_wcfpp_clear_upload_session` | Both | Clear uploaded file reference from WC session |
 | `studiou_wcfpp_add_media_category` | Admin | Create a new media category |
 | `studiou_wcfpp_update_status` | Admin | Update single file status |
 | `studiou_wcfpp_bulk_status` | Admin | Bulk update file statuses |

+ 2 - 2
studiou-wc-free-photo-product/studiou-wc-free-photo-product.php

@@ -3,7 +3,7 @@
  * Plugin Name: QDR - Studiou WC Free Photo Product
  * Plugin URI: https://www.quadarax.com/plugins/studiou-wc-free-photo-product
  * Description: Allows publishing special WooCommerce products with variants and custom raw image upload for photo printing
- * Version: 1.5.2
+ * Version: 1.5.3
  * Requires at least: 6.9.4
  * Requires PHP: 8.2
  * Requires Plugins: woocommerce, product-variant-table-for-woocommerce
@@ -22,7 +22,7 @@ if (!defined('WPINC')) {
     die;
 }
 
-if (!defined('STUDIOU_WCFPP_VERSION')) define('STUDIOU_WCFPP_VERSION', '1.5.2');
+if (!defined('STUDIOU_WCFPP_VERSION')) define('STUDIOU_WCFPP_VERSION', '1.5.3');
 if (!defined('STUDIOU_WCFPP_PLUGIN_DIR')) define('STUDIOU_WCFPP_PLUGIN_DIR', plugin_dir_path(__FILE__));
 if (!defined('STUDIOU_WCFPP_PLUGIN_URL')) define('STUDIOU_WCFPP_PLUGIN_URL', plugin_dir_url(__FILE__));
 if (!defined('STUDIOU_WCFPP_PLUGIN_BASENAME')) define('STUDIOU_WCFPP_PLUGIN_BASENAME', plugin_basename(__FILE__));