Plugin Directory

Changeset 3383399


Ignore:
Timestamp:
10/23/2025 12:49:28 PM (5 months ago)
Author:
printess
Message:

1.6.66

  • Fixed bug that did not clear the exchange id cache when using panel Ui
  • Fixed PHP warnings with referenced printess plugin files that could not be found by replacing include_once calls with require_once.
  • Fixed broken add to cart when using specific product types or specific plugins that might chaneg the data type of product meta data
  • PanelUi is now the standard for newly created products
Location:
printess-editor/trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • printess-editor/trunk/includes/js/printessEditor.js

    r3362973 r3383399  
    599599    }
    600600    static async getOrGenerateBasketId(context) {
    601         let ret = typeof context.getBasketId === "function" ? context.getBasketId() : "";
    602         if (!ret && typeof context.getBasketIdAsync === "function") {
     601        let ret = typeof context?.getBasketId === "function" ? context.getBasketId() : "";
     602        if (!ret && typeof context?.getBasketIdAsync === "function") {
    603603            ret = await context.getBasketIdAsync() || null;
    604604        }
     
    649649    }
    650650    static closeAllHtmlDialogs() {
    651         document.querySelectorAll(":not(printess-owned) dialog").forEach((x) => {
    652             if (typeof x.close === "function") {
     651        document.querySelectorAll(":not(.printess-owned) dialog,dialog:not(.printess-owned)").forEach((x) => {
     652            if (x && !x.classList.contains("printess-owned") && typeof x.close === "function") {
    653653                try {
    654654                    x.close();
     
    707707        if (printessComponent && printessComponent.editor) {
    708708            printessComponent.style.display = "block";
    709             await printessComponent.editor.api.loadTemplateAndFormFields(context.templateNameOrSaveToken, mergeTemplates, formFields, null);
     709            await printessComponent.editor.api.loadTemplateAndFormFields(context.templateNameOrSaveToken, mergeTemplates, formFields, null, null, true);
    710710            if (!isSaveToken && pageCount !== null && pageCount > 0) {
    711711                await printessComponent.editor.api.setBookInsidePages(pageCount);
     
    773773                snippetPriceCategoryLabels: priceInfo && priceInfo.snippetPrices ? priceInfo.snippetPrices : null,
    774774                theme: theme,
     775                skipExchangeStateApplication: true,
    775776                addToBasketCallback: (token, thumbnailUrl) => {
    776777                    const addToBasket = (saveToken, thumbnailUrl) => {
     
    997998                        formFields: formFields,
    998999                        snippetPriceCategoryLabels: priceInfo && priceInfo.snippetPrices ? priceInfo.snippetPrices : null,
    999                         mergeTemplates: mergeTemplates
     1000                        mergeTemplates: mergeTemplates,
     1001                        skipExchangeStateApplication: true
    10001002                    };
    10011003                    if (typeof context.showSplitterGridSizeButton !== "undefined" && context.showSplitterGridSizeButton !== null) {
  • printess-editor/trunk/includes/js/printessWoocommerce.js

    r3365959 r3383399  
    1 const printessFocusListeners = [];
    2 const printessTrapFocus = function (root) {
     1class PrintessSharedTools {
     2    static async createSaveToken(shopToken, params) {
     3        const response = await fetch('https://api.printess.com/production/savetoken/create', {
     4            method: 'POST',
     5            redirect: "follow",
     6            headers: {
     7                "Content-Type": 'application/json',
     8                "Authorization": `Bearer ${shopToken}`,
     9            },
     10            body: JSON.stringify(params)
     11        });
     12        if (!response.ok) {
     13            console.error("Unable to create save token: [" + response.status + "] " + response.statusText);
     14            return null;
     15        }
     16        return (await response.json());
     17    }
     18    static decodeHTML(encoded) {
     19        const textarea = document.createElement('textarea');
     20        textarea.innerHTML = encoded;
     21        return textarea.value;
     22    }
     23    static parseMergeTemplate(template) {
     24        let ret = [];
     25        if (template) {
     26            if (Array.isArray(template)) {
     27                template.forEach((x) => {
     28                    ret = [
     29                        ...ret,
     30                        ...PrintessSharedTools.parseMergeTemplate(x)
     31                    ];
     32                });
     33            }
     34            else {
     35                if (typeof template !== "string") {
     36                    ret.push(template);
     37                }
     38                else {
     39                    try {
     40                        let deserialized = JSON.parse(template);
     41                        ret = [
     42                            ...ret,
     43                            ...PrintessSharedTools.parseMergeTemplate(deserialized)
     44                        ];
     45                    }
     46                    catch {
     47                        try {
     48                            let deserialized = JSON.parse(PrintessSharedTools.decodeHTML(template));
     49                            ret = [
     50                                ...ret,
     51                                ...PrintessSharedTools.parseMergeTemplate(deserialized)
     52                            ];
     53                        }
     54                        catch {
     55                            ret.push({
     56                                templateName: template
     57                            });
     58                        }
     59                    }
     60                }
     61            }
     62        }
     63        return ret;
     64    }
     65    static sleepAsync(timeoutMs) {
     66        return new Promise((resolve) => setTimeout(resolve, timeoutMs));
     67    }
     68    static queryItem(itemQuery, callback, failedCallback = null, timeout = 200, maxRetires = 20, retries = 0) {
     69        if (retries >= maxRetires) {
     70            if (typeof failedCallback === "function") {
     71                failedCallback();
     72            }
     73            return;
     74        }
     75        const element = itemQuery();
     76        if (element) {
     77            callback(element);
     78            return;
     79        }
     80        setTimeout(function () {
     81            const element = itemQuery();
     82            if (element) {
     83                callback(element);
     84            }
     85            else {
     86                PrintessSharedTools.queryItem(itemQuery, callback, failedCallback, timeout, maxRetires, retries + 1);
     87            }
     88        }, timeout);
     89    }
     90    static async queryItemAsync(itemQuery, timeout = 50, maxRetires = 20) {
     91        return new Promise((resolve, reject) => PrintessSharedTools.queryItem(itemQuery, resolve, reject, timeout, maxRetires));
     92    }
     93    static forEach(items, callback) {
     94        if (!items || typeof callback !== "function") {
     95            return;
     96        }
     97        if (Array.isArray(items)) {
     98            items.forEach(callback);
     99        }
     100        else {
     101            let index = 0;
     102            for (const prop in items) {
     103                if (items.hasOwnProperty(prop)) {
     104                    callback(items[prop], index, items, prop);
     105                    ++index;
     106                }
     107            }
     108        }
     109    }
     110    static map(items, callback) {
     111        if (!items || typeof callback !== "function") {
     112            return [];
     113        }
     114        if (Array.isArray(items)) {
     115            return items.map(callback);
     116        }
     117        else {
     118            const ret = [];
     119            let index = 0;
     120            for (const prop in items) {
     121                if (items.hasOwnProperty(prop)) {
     122                    ret.push(callback(items[prop], index, items, prop));
     123                    ++index;
     124                }
     125            }
     126            return ret;
     127        }
     128    }
     129    static filter(items, callback) {
     130        const ret = {};
     131        PrintessSharedTools.forEach(items, (value, index, arr, key) => {
     132            if (callback(value, items, key, index) === true) {
     133                ret[key] = value;
     134            }
     135        });
     136        return ret;
     137    }
     138    static filterAsArray(items, callback) {
     139        const ret = [];
     140        PrintessSharedTools.forEach(items, (value, index, arr, key) => {
     141            if (callback(value, items, key, index) === true) {
     142                ret.push({
     143                    key: key,
     144                    value: value
     145                });
     146            }
     147        });
     148        return ret;
     149    }
     150    static ensureHttpProtocol(domain) {
     151        let ret = ((typeof domain !== "string" ? domain.apiDomain : domain) || "").trim();
     152        if (ret.indexOf("http://") !== 0 && ret.indexOf("https://")) {
     153            if (ret.length > 1 && ret[0] === "/" && ret[1] === "/") {
     154                ret = ret.substring(2);
     155            }
     156            else if (ret.length > 0 && ret[0] === "/") {
     157                ret = ret.substring(1);
     158            }
     159            ret = "https://" + ret;
     160        }
     161        return ret;
     162    }
     163    static urlConcat(url, ...parts) {
     164        let ret = url || "";
     165        const filteredParts = (parts || []).filter(x => x ? true : false);
     166        filteredParts.forEach((part) => {
     167            if (part.length > 1) {
     168                if (ret && ret[ret.length - 1] !== "/") {
     169                    ret += "/";
     170                }
     171                ret += part[0] !== "/" ? part : part.substring(1);
     172            }
     173            else if (part[0] !== "/") {
     174                if (ret && ret[ret.length - 1] !== "/") {
     175                    ret += "/";
     176                }
     177                ret += part;
     178            }
     179        });
     180        return ret;
     181    }
     182    static sanitizeHost(host) {
     183        if (host) {
     184            host = host.trim();
     185            if (host.endsWith("/")) {
     186                return host;
     187            }
     188            return host + "/";
     189        }
     190        return host || "";
     191    }
     192    static async postJsonAsync(url, params, token, additionalHeaders) {
     193        const headers = {
     194            "Content-Type": 'application/json'
     195        };
     196        if (token) {
     197            headers["Authorization"] = "Bearer " + token;
     198        }
     199        if (additionalHeaders) {
     200            PrintessSharedTools.forEach(additionalHeaders, (value, index, headers, key) => {
     201                if (key && value) {
     202                    headers[key] = value;
     203                }
     204            });
     205        }
     206        const response = await fetch('https://api.printess.com/shop/savetoken/split', {
     207            method: 'POST',
     208            redirect: "follow",
     209            headers: headers,
     210            body: JSON.stringify(params)
     211        });
     212        if (!response.ok) {
     213            throw `Unable to post json to ${url}: [${response.status}]: ${response.statusText}`;
     214        }
     215        return await response.json();
     216    }
     217    loadTagsAsync(settings) {
     218        return PrintessSharedTools.postJsonAsync(PrintessSharedTools.urlConcat(PrintessSharedTools.ensureHttpProtocol(settings), "snippets/tags/load"), { includeGlobal: settings.includeGlobalTags === true }, settings.serviceToken);
     219    }
     220    static generateUUID() {
     221        var d = new Date().getTime(); //Timestamp
     222        var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0; //Time in microseconds since page-load or 0 if unsupported
     223        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
     224            var r = Math.random() * 16; //random number between 0 and 16
     225            if (d > 0) { //Use timestamp until depleted
     226                r = (d + r) % 16 | 0;
     227                d = Math.floor(d / 16);
     228            }
     229            else { //Use microseconds since page-load if supported
     230                r = (d2 + r) % 16 | 0;
     231                d2 = Math.floor(d2 / 16);
     232            }
     233            return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
     234        });
     235    }
     236    static getFileNameFromUrl(fileName) {
     237        return (fileName || "").split('#')[0].split('?')[0].split('/').pop();
     238    }
     239    static async downloadImages(images) {
     240        const ret = [];
     241        if (Array.isArray(images)) {
     242            for (let i = 0; i < images.length; ++i) {
     243                const response = await fetch(images[i].value);
     244                if (response.ok) {
     245                    const blob = await response.blob();
     246                    ret.push({
     247                        name: images[i].name,
     248                        data: new File([blob], this.getFileNameFromUrl(images[i].value), { type: blob.type })
     249                    });
     250                }
     251                else {
     252                    console.error("Unable to download image " + images[i].value + "; [" + response.status.toString() + "] " + response.statusText + ": " + await response.text());
     253                }
     254            }
     255        }
     256        else {
     257            for (const fileName in images) {
     258                if (images.hasOwnProperty(fileName)) {
     259                    const fileUrl = images[fileName];
     260                    const response = await fetch(fileUrl);
     261                    if (response.ok) {
     262                        const blob = await response.blob();
     263                        ret.push({
     264                            name: fileName,
     265                            data: new File([blob], this.getFileNameFromUrl(fileUrl), { type: blob.type })
     266                        });
     267                    }
     268                    else {
     269                        console.error("Unable to download image " + images[fileName] + "; [" + response.status.toString() + "] " + response.statusText + ": " + await response.text());
     270                    }
     271                }
     272            }
     273        }
     274        return ret;
     275    }
     276}
     277
     278const printessFocusListeners = [];
     279let printessSaveReminderActive = false;
     280function printessTrapFocus(root) {
    3281    const keyboardFocusableElements = root?.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]');
    4282    if (keyboardFocusableElements && keyboardFocusableElements.length > 0) {
     
    22300        firstFocusableElement?.focus();
    23301    }
    24 };
     302}
    25303const printessFreeFocus = function () {
    26304    if (printessFocusListeners.length > 0) {
     
    462740        return name;
    463741    };
    464     const decodeHTMLEntities = (encoded) => {
    465         const textarea = document.createElement('textarea');
    466         textarea.innerHTML = encoded;
    467         return textarea.value;
    468     };
    469     const parseMergeTemplate = (template) => {
    470         let ret = [];
    471         if (template) {
    472             if (Array.isArray(template)) {
    473                 template.forEach((x) => {
    474                     ret = [
    475                         ...ret,
    476                         ...parseMergeTemplate(x)
    477                     ];
    478                 });
    479             }
    480             else {
    481                 if (typeof template !== "string") {
    482                     ret.push(template);
    483                 }
    484                 else {
    485                     try {
    486                         let deserialized = JSON.parse(template);
    487                         ret = [
    488                             ...ret,
    489                             ...parseMergeTemplate(deserialized)
    490                         ];
    491                     }
    492                     catch {
    493                         try {
    494                             let deserialized = JSON.parse(decodeHTMLEntities(template));
    495                             ret = [
    496                                 ...ret,
    497                                 ...parseMergeTemplate(deserialized)
    498                             ];
    499                         }
    500                         catch {
    501                             ret.push({
    502                                 templateName: template
    503                             });
    504                         }
    505                     }
    506                 }
    507             }
    508         }
    509         return ret;
    510     };
    511742    const createShopContext = function (settings) {
    512743        const context = {
     
    531762                    ret = [
    532763                        ...ret,
    533                         ...parseMergeTemplate(settings.mergeTemplates)
     764                        ...PrintessSharedTools.parseMergeTemplate(settings.mergeTemplates)
    534765                    ];
    535766                }
     
    537768                    ret = [
    538769                        ...ret,
    539                         ...parseMergeTemplate(settings.product.mergeTemplates)
     770                        ...PrintessSharedTools.parseMergeTemplate(settings.product.mergeTemplates)
    540771                    ];
    541772                }
     
    545776                    ret = [
    546777                        ...ret,
    547                         ...parseMergeTemplate(selectedVariant.templateName)
     778                        ...PrintessSharedTools.parseMergeTemplate(selectedVariant.templateName)
    548779                    ];
    549780                }
     
    8991130            onSaveTimer: () => {
    9001131                if (typeof settings.showSaveWarningAfter !== "undefined" && settings.showSaveWarningAfter > 0 && typeof context.save === "function") {
    901                     this.showDialog("printess_save_reminder", "", (okClicked, value) => {
    902                         value = (value || "").trim();
    903                         if (okClicked) {
    904                             context.save();
    905                         }
    906                     });
     1132                    if (!printessSaveReminderActive) {
     1133                        this.showDialog("printess_save_reminder", "", (okClicked, value) => {
     1134                            value = (value || "").trim();
     1135                            if (okClicked) {
     1136                                context.save();
     1137                            }
     1138                        });
     1139                        printessSaveReminderActive = true;
     1140                    }
    9071141                }
    9081142            },
     
    13031537        if (previouslyFocused && previouslyFocused instanceof HTMLElement) {
    13041538            previouslyFocused.focus();
     1539        }
     1540        if (printessSaveReminderActive) {
     1541            printessSaveReminderActive = false;
    13051542        }
    13061543    };
  • printess-editor/trunk/includes/printess-admin-settings.php

    r3341026 r3383399  
    323323     */
    324324    static function register_settings() {
    325         include_once("printess-api.php");
     325        require_once(plugin_dir_path(__DIR__) . "includes/printess-api.php");
    326326
    327327        add_settings_section(
     
    973973            __( 'Overwrite product drop shipper in case of template mode', 'printess-editor' ),
    974974            function() {
    975                 include_once("printess-api.php");
     975                require_once(plugin_dir_path(__DIR__) . "includes/printess-api.php");
    976976
    977977                $dropshipping = array();
  • printess-editor/trunk/includes/printess-api.php

    r3339767 r3383399  
    1414     */
    1515    static function send_post_request( $url, $token, $data ) {
    16         include_once("printess-admin-settings.php");
     16        require_once(plugin_dir_path(__DIR__) . "includes/printess-admin-settings.php");
    1717
    1818        $ssl_verify = ! PrintessAdminSettings::get_debug();
     
    4848     */
    4949    static function send_get_request($url) {
    50         include_once("printess-admin-settings.php");
     50        require_once(plugin_dir_path(__DIR__) . "includes/includes/printess-admin-settings.php");
    5151
    5252        $ssl_verify = ! PrintessAdminSettings::get_debug();
     
    7676     */
    7777    static function get_dropshipping_info() {
    78         include_once("printess-admin-settings.php");
     78        require_once(plugin_dir_path(__DIR__) . "includes/printess-admin-settings.php");
    7979
    8080        $printess_host = PrintessAdminSettings::get_host();
     
    141141
    142142    static function get_save_token_info($save_token) {
    143         include_once("printess-admin-settings.php");
     143        require_once(plugin_dir_path(__DIR__) . "includes/printess-admin-settings.php");
    144144
    145145        $printess_host = PrintessAdminSettings::get_host();
  • printess-editor/trunk/includes/printess-product-helpers.php

    r3325412 r3383399  
    4646    }
    4747
    48     private function get_attribute_definition($attribute_name, array $attrbutes = null) {
     48    private function get_attribute_definition($attribute_name, ?array $attrbutes = null) {
    4949        if(null === $attrbutes) {
    5050            $attributes = $this->get_attributes();
     
    130130    }
    131131
     132    /**
     133     * Test if the given object is an array, check if the array has a key, produce a warning if not
     134     */
     135    function transform_meta_value_to_template_name(mixed $item, string $key): string {
     136        if(isset($item)) {
     137            if(is_array($item)) {
     138                if(array_key_exists($key, $item)) {
     139                    $name = $item[$key];
     140
     141                    if(isset($name) && is_string($name) && !empty($name)) {
     142                        return $name;
     143                    }
     144                }
     145            } else if(is_string($item) && !empty($item)) {
     146                return $item;
     147            }
     148        }
     149
     150        if(isset($item) && !is_string($item)) {
     151            try {
     152                $logger = wc_get_logger();
     153
     154            } catch(\Exception $e) {
     155                //Intentionally left blank, nothing to do here
     156                $logger->error( 'Printess Editor Integration: Unexpected data type in printess template name meta data: ' . json_encode( $item ), array( 'source' => 'my-custom-plugin' ) );
     157            }
     158        }
     159
     160        return "";
     161    }
     162
    132163    public function get_template_name($check_base_product = false): string {
    133       $template_name = $this->_product->get_meta( 'printess_template', true );
     164      $template_name = $this->transform_meta_value_to_template_name($this->_product->get_meta( 'printess_template', true ), "printess_template");
    134165
    135166      $template_name = $template_name === null || empty($template_name) ? "" : $template_name;
     
    150181                        }
    151182                    } else {
    152                         $variation_template_name = $variation->get_meta( 'printess_template_name', true );
     183                        $variation_template_name = $this->transform_meta_value_to_template_name($variation->get_meta( 'printess_template_name', true ), "printess_template_name");
    153184                    }
    154185
     
    160191
    161192                if(!$variant_has_template_name) {
    162                     $template_name = $this->_base_product->get_meta( 'printess_template', true );
     193                    $template_name = $this->transform_meta_value_to_template_name($this->_base_product->get_meta( 'printess_template', true ), "printess_template");
    163194                    $template_name = $template_name === null || empty($template_name) ? "" : $template_name;
    164195                }
     
    191222     */
    192223    public static function render_product_option_pane() {
    193         include_once("includes/printess-api.php");
    194         include_once("includes/printess-html-helpers.php");
    195         include_once("includes/printess-admin-settings.php");
     224        require_once(plugin_dir_path(__DIR__) . "includes/printess-api.php");
     225        require_once(plugin_dir_path(__DIR__) . "includes/printess-html-helpers.php");
     226        require_once(plugin_dir_path(__DIR__) . "includes/printess-admin-settings.php");
    196227
    197228        $product_helpers = new PrintessProductHelpers(get_the_ID());
     
    400431                'value'       => get_post_meta( get_the_ID(), 'printess_ui_version', true ),
    401432                'label'       => __( 'Buyer side user interface', 'printess-editor' ),
    402                 'description' => __( 'Classical or the new PanelUi (Beta!).', 'printess-editor' ),
     433                'description' => __( 'PanelUi or the old deprecated iframe intgeration', 'printess-editor' ),
    403434                'options'     => array(
    404                     'classical'    => 'Classical',
    405                     'bcui' => 'PanelUi (Beta)',
     435                    'bcui' => 'PanelUi',
     436                    'classical'    => 'Deprecated iframe integration',
    406437                ),
    407438            )
  • printess-editor/trunk/includes/printess-saved-design-repository.php

    r3073802 r3383399  
    141141  }
    142142
    143   function add_design(int $customer_id, string $save_token, string $thumbnail_url, int $product_id, string $product_name, string $display_name, array $product_options = null): int {
     143  function add_design(int $customer_id, string $save_token, string $thumbnail_url, int $product_id, string $product_name, string $display_name, ?array $product_options = null): int {
    144144    global $wpdb;
    145145    $table_name = $wpdb->prefix . "printess_saved_designs";
     
    255255  }
    256256
    257   function update_design(int $customer_id, int $design_id, string $save_token, string $thumbnail_url, array $options = null): bool {
     257  function update_design(int $customer_id, int $design_id, string $save_token, string $thumbnail_url, ?array $options = null): bool {
    258258    global $wpdb;
    259259    $table_name = $wpdb->prefix . "printess_saved_designs";
  • printess-editor/trunk/includes/printess-table.php

    r3029359 r3383399  
    3333  }
    3434
    35   function add_row(array $values = null) {
     35  function add_row(?array $values = null) {
    3636    $new_row = array();
    3737
  • printess-editor/trunk/printess.php

    r3365959 r3383399  
    55 * Plugin URI: https://printess.com/kb/integrations/woo-commerce/index.html
    66 * Developer: Bastian Kröger (support@printess.com); Alexander Oser (support@printess.com)
    7  * Version: 1.6.65
     7 * Version: 1.6.66
    88 * Author: Printess
    99 * Author URI: https://printess.com
     
    1212 * Requires at least: 5.9
    1313 * Requires PHP: 8.1
    14  * Tested up to: 6.8
    15  *
    16  * Woo: 10000:924023dfsfhsf8429842386wdff234sfd
     14 * Tested up to: 6.8.3
     15 *
     16 * Woo: 10000:924024dfsfhsf8429842386wdff234sfd
    1717 * WC requires at least: 5.8
    18  * WC tested up to: 9.8.2
     18 * WC tested up to: 10.3.0
    1919 */
    2020
     
    37503750    }
    37513751
    3752     $variation_id = $_REQUEST['variation_id'];
     3752    $variation_id = null;
     3753
     3754    if(array_key_exists('variation_id', $_REQUEST)) {
     3755        $variation_id = $_REQUEST['variation_id'];
     3756    }
     3757
    37533758    $helper = new PrintessProductHelpers(null === $variation_id || empty($variation_id) ? $product_id : intval($variation_id) );
    37543759    $product_template_name = $helper->get_template_name(true);
     
    39373942}
    39383943
    3939 if ( in_array( $printess_global_plugin_path, wp_get_active_and_valid_plugins(), true ) || in_array( $printess_global_plugin_path, wp_get_active_network_plugins(), true ) ) {
     3944
     3945if ( in_array( $printess_global_plugin_path, wp_get_active_and_valid_plugins(), true ) || (function_exists("wp_get_active_network_plugins") && in_array( $printess_global_plugin_path, wp_get_active_network_plugins(), true )) ) {
    39403946    add_action( 'woocommerce_init', 'printess_register_hooks' );
    39413947
  • printess-editor/trunk/readme.txt

    r3365959 r3383399  
    33Tags: personalization, mug, calendar, t-shirt, photo products, customization, web2print, photo books, canvas, avatar, photo tiles, personalized children book, greeting cards, graphic design, configurator
    44Requires at least: 5.6
    5 Tested up to: 6.8
    6 WC Tested up to: 9.8.2
    7 Stable tag: 1.6.40
     5Tested up to: 6.8.3
     6WC Tested up to: 10.3.0
     7Stable tag: 1.6.66
    88Requires PHP: 8.1
    99License: GPLv2 or later
     
    341341= 1.6.65 =
    342342- Fixed: Fixed bug that saved a wrong variant in case the current user is not logged in and clicks on save. Before, the default variant was used instead of the current selected variant.
     343
     344= 1.6.66 =
     345- Fixed bug that did not clear the exchange id cache when using panel Ui
     346- Fixed PHP warnings with referenced printess plugin files that could not be found by replacing include_once calls with require_once.
     347- Fixed broken add to cart when using specific product types or specific plugins that might chaneg the data type of product meta data
     348- PanelUi is now the standard for newly created products
Note: See TracChangeset for help on using the changeset viewer.