Changeset 3491060
- Timestamp:
- 03/25/2026 04:00:57 PM (45 hours ago)
- Location:
- woo-mailerlite
- Files:
-
- 10 added
- 30 edited
- 1 copied
-
tags/3.1.13 (copied) (copied from woo-mailerlite/trunk)
-
tags/3.1.13/README.txt (modified) (2 diffs)
-
tags/3.1.13/admin/WooMailerLiteAdmin.php (modified) (4 diffs)
-
tags/3.1.13/admin/assets/css/lib (added)
-
tags/3.1.13/admin/assets/css/lib/select2.min.css (added)
-
tags/3.1.13/admin/assets/js/components/Forms/CustomSelect.js (modified) (1 diff)
-
tags/3.1.13/admin/assets/js/components/Forms/SyncFields.js (modified) (1 diff)
-
tags/3.1.13/admin/assets/js/components/Modals/Modals.js (modified) (2 diffs)
-
tags/3.1.13/admin/assets/js/components/Views/Settings.js (modified) (8 diffs)
-
tags/3.1.13/admin/assets/js/components/Views/Wizard.js (modified) (6 diffs)
-
tags/3.1.13/admin/assets/js/lib (added)
-
tags/3.1.13/admin/assets/js/lib/select2.min.js (added)
-
tags/3.1.13/admin/assets/js/ml-quick-edit.js (added)
-
tags/3.1.13/admin/controllers/WooMailerLiteAdminSettingsController.php (modified) (4 diffs)
-
tags/3.1.13/includes/WooMailerLite.php (modified) (1 diff)
-
tags/3.1.13/includes/api/WooMailerLiteRewriteApi.php (modified) (1 diff)
-
tags/3.1.13/includes/common/WooMailerLiteOptions.php (modified) (3 diffs)
-
tags/3.1.13/includes/controllers/WooMailerLiteController.php (modified) (1 diff)
-
tags/3.1.13/includes/jobs/WooMailerLiteAbstractJob.php (modified) (1 diff)
-
tags/3.1.13/public/css/mailerlite-select2.css (modified) (10 diffs)
-
tags/3.1.13/woo-mailerlite.php (modified) (2 diffs)
-
trunk/README.txt (modified) (2 diffs)
-
trunk/admin/WooMailerLiteAdmin.php (modified) (4 diffs)
-
trunk/admin/assets/css/lib (added)
-
trunk/admin/assets/css/lib/select2.min.css (added)
-
trunk/admin/assets/js/components/Forms/CustomSelect.js (modified) (1 diff)
-
trunk/admin/assets/js/components/Forms/SyncFields.js (modified) (1 diff)
-
trunk/admin/assets/js/components/Modals/Modals.js (modified) (2 diffs)
-
trunk/admin/assets/js/components/Views/Settings.js (modified) (8 diffs)
-
trunk/admin/assets/js/components/Views/Wizard.js (modified) (6 diffs)
-
trunk/admin/assets/js/lib (added)
-
trunk/admin/assets/js/lib/select2.min.js (added)
-
trunk/admin/assets/js/ml-quick-edit.js (added)
-
trunk/admin/controllers/WooMailerLiteAdminSettingsController.php (modified) (4 diffs)
-
trunk/includes/WooMailerLite.php (modified) (1 diff)
-
trunk/includes/api/WooMailerLiteRewriteApi.php (modified) (1 diff)
-
trunk/includes/common/WooMailerLiteOptions.php (modified) (3 diffs)
-
trunk/includes/controllers/WooMailerLiteController.php (modified) (1 diff)
-
trunk/includes/jobs/WooMailerLiteAbstractJob.php (modified) (1 diff)
-
trunk/public/css/mailerlite-select2.css (modified) (10 diffs)
-
trunk/woo-mailerlite.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
woo-mailerlite/tags/3.1.13/README.txt
r3484689 r3491060 6 6 Tested up to: 6.8.2 7 7 Requires PHP: 7.2.5 8 Stable tag: 3.1.1 28 Stable tag: 3.1.13 9 9 License: GPLv3 or later 10 10 License URI: http://www.gnu.org/licenses/gpl-3.0.html … … 84 84 85 85 == Changelog == 86 = 3.1.13 (25th March 2026) = 87 * Bug fixes and performance improvements 88 86 89 = 3.1.12 (17th March 2026) = 87 90 * Bug fixes -
woo-mailerlite/tags/3.1.13/admin/WooMailerLiteAdmin.php
r3415073 r3491060 23 23 public function enqueueScripts($hook) 24 24 { 25 if ($hook === 'edit.php' && isset($_GET['post_type']) && $_GET['post_type'] === 'product') { 26 wp_enqueue_script('woo-mailerlite-quick-edit', plugin_dir_url(__FILE__) . '../admin/assets/js/ml-quick-edit.js', ['jquery', 'inline-edit-post'], null, true); 27 return; 28 } 29 25 30 if ($hook !== 'woocommerce_page_mailerlite') { 26 31 return; 27 32 } 33 34 wp_dequeue_script('select2'); 35 wp_deregister_script('select2'); 36 28 37 wp_enqueue_script('woo-mailerlite-vue-cdn', 'https://cdn.jsdelivr.net/npm/vue@3.5.13/dist/vue.global.prod.js', [], null, true); 29 38 wp_localize_script('woo-mailerlite-admin', 'woo_mailerlite_admin_data', array( … … 32 41 )); 33 42 34 wp_enqueue_script(' style2-script', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js');35 wp_enqueue_script('woo-mailerlite-admin', plugin_dir_url(__FILE__) . '../admin/assets/js/ml-app.js', ['jquery', 'woo-mailerlite-vue-cdn' ], null, true);43 wp_enqueue_script('woo-mailerlite-select2', plugin_dir_url(__FILE__) . 'assets/js/lib/select2.min.js', ['jquery'], WOO_MAILERLITE_VERSION, true); 44 wp_enqueue_script('woo-mailerlite-admin', plugin_dir_url(__FILE__) . '../admin/assets/js/ml-app.js', ['jquery', 'woo-mailerlite-vue-cdn', 'woo-mailerlite-select2'], null, true); 36 45 37 46 } … … 41 50 return; 42 51 } 52 53 wp_dequeue_style('select2'); 54 wp_deregister_style('select2'); 55 43 56 wp_enqueue_style('woo-mailerlite-admin-css', plugin_dir_url( __FILE__ ) . '../admin/assets/css/admin.css', false, WOO_MAILERLITE_VERSION); 44 wp_enqueue_style('style2-style', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css'); 45 wp_enqueue_style('style2-mailerlite-style', plugin_dir_url( __FILE__ ) . '../public/css/mailerlite-select2.css',false, WOO_MAILERLITE_VERSION); 57 wp_enqueue_style('woo-mailerlite-select2-css', plugin_dir_url( __FILE__ ) . 'assets/css/lib/select2.min.css', false, WOO_MAILERLITE_VERSION); 58 wp_enqueue_style('woo-mailerlite-select2-theme', plugin_dir_url( __FILE__ ) . '../public/css/mailerlite-select2.css', ['woo-mailerlite-select2-css'], WOO_MAILERLITE_VERSION); 59 } 60 61 public function removeConflictingSelect2($hook) 62 { 63 if ($hook !== 'woocommerce_page_mailerlite') { 64 return; 65 } 66 67 global $wp_scripts, $wp_styles; 68 69 $scriptPatterns = ['select2', 'selectWoo', 'wc-enhanced-select']; 70 71 foreach ($wp_scripts->registered as $handle => $script) { 72 if ($handle === 'woo-mailerlite-select2') { 73 continue; 74 } 75 foreach ($scriptPatterns as $pattern) { 76 if (strpos($script->src, $pattern) !== false) { 77 wp_dequeue_script($handle); 78 wp_deregister_script($handle); 79 break; 80 } 81 } 82 } 83 84 $stylePatterns = ['select2', 'selectWoo']; 85 86 foreach ($wp_styles->registered as $handle => $style) { 87 if ($handle === 'woo-mailerlite-select2-css' || $handle === 'woo-mailerlite-select2-theme') { 88 continue; 89 } 90 foreach ($stylePatterns as $pattern) { 91 if (strpos($style->src, $pattern) !== false) { 92 wp_dequeue_style($handle); 93 wp_deregister_style($handle); 94 break; 95 } 96 } 97 } 46 98 } 47 99 … … 115 167 public function populateIgnoreProductBlock($column, $post_id) 116 168 { 117 $ignoredProducts = WooMailerLiteOptions::get('ignored_products', []);118 169 119 170 switch ($column) { 120 171 case 'name' : 172 $ignoredProducts = WooMailerLiteOptions::get('ignored_products', []); 121 173 ?> 122 174 <div class="hidden ml_ignore_product_inline" id="ml_ignore_product_inline_<?=intval($post_id) ?>"> 123 <div id="_ml_ignore_product"><?php echo array_key_exists($post_id, $ignoredProducts) ? 'yes' : 'no' ?></div>175 <div class="_ml_ignore_product"><?php echo array_key_exists($post_id, $ignoredProducts) ? 'yes' : 'no' ?></div> 124 176 </div> 125 177 <?php -
woo-mailerlite/tags/3.1.13/admin/assets/js/components/Forms/CustomSelect.js
r3338878 r3491060 2 2 import eventBus from "../eventBus.js"; 3 3 const template = ` 4 <select ref="wooMlSubGroup" class="w c-enhanced-select">4 <select ref="wooMlSubGroup" class="woo-ml-group-select"> 5 5 <option value="" disabled >{{ placeholder }}</option> 6 6 </select> -
woo-mailerlite/tags/3.1.13/admin/assets/js/components/Forms/SyncFields.js
r3338878 r3491060 1 1 const template = ` 2 <select v-model="setSyncFields" />2 <select v-model="setSyncFields" data-testid="sync-fields-select"/> 3 3 `; 4 4 -
woo-mailerlite/tags/3.1.13/admin/assets/js/components/Modals/Modals.js
r3338878 r3491060 5 5 const template = ` 6 6 <!-- Create Group Modal --> 7 <div v-if="openCreateGroupModal" class="woo-ml-wizard-modal" id="wooMlWizardCreateGroupModal" role="dialog" >7 <div v-if="openCreateGroupModal" class="woo-ml-wizard-modal" id="wooMlWizardCreateGroupModal" role="dialog" data-testid="create-group-modal"> 8 8 <div class="woo-ml-wizard-modal-parent"> 9 9 <div class="woo-ml-wizard-modal-container"> … … 15 15 <div class="woo-ml-wizard-modal-body"> 16 16 <div class="create-group-input"> 17 <input ref="wooMlCreateGroup" type="text" name="createGroup" placeholder="Enter group name" v-model="createGroupName" class="" >17 <input ref="wooMlCreateGroup" type="text" name="createGroup" placeholder="Enter group name" v-model="createGroupName" class="" data-testid="create-group-name-input"> 18 18 </div> 19 19 <div class="modal-button-ml"> 20 20 <button @click="openCreateGroupModal = false" type="button" class="btn-secondary-ml woo-ml-close" style="margin-right: 12px;">Close</button> 21 <button @click="createGroup" ref="createGroup" type="button" class="btn-primary-ml" ><span class="woo-ml-button-text">Create group</span></button>21 <button @click="createGroup" ref="createGroup" type="button" class="btn-primary-ml" data-testid="create-group-submit-btn"><span class="woo-ml-button-text">Create group</span></button> 22 22 </div> 23 23 </div> -
woo-mailerlite/tags/3.1.13/admin/assets/js/components/Views/Settings.js
r3377455 r3491060 6 6 7 7 const template = ` 8 <div v-if="syncInProgress || asyncSyncInProgress" class="woo-ml-sync-loading-container" >8 <div v-if="syncInProgress || asyncSyncInProgress" class="woo-ml-sync-loading-container" data-testid="sync-in-progress"> 9 9 <strong>Sync in progress...</strong> 10 10 <p>This will continue as long as you stay on this page. … … 20 20 <label for="wooMlSubGroup" class="settings-label mb-3-ml">Subscriber group</label> 21 21 <label class="input-mailerlite mb-2-ml" style="display: flex;"> 22 <custom-select 23 id="wooMlSubGroup" 24 class="wc-enhanced-select" 25 name="subscriber-group" 22 <custom-select 23 id="wooMlSubGroup" 24 name="subscriber-group" 26 25 :options="groups" 27 26 v-model="selectedGroup" … … 43 42 </label> 44 43 <label v-if="ignoredProducts.length !== 0" class="input-mailerlite" style="cursor: default;"> 45 <div multiple class="wc-enhanced-select"name="ignore_product_list"44 <div multiple name="ignore_product_list" 46 45 style="min-height: 26px; padding-left: 8px; display: flex; flex-direction: row; overflow: hidden; flex-wrap: wrap; padding: 4px; padding-top: 0; border: 1px solid #d1d5db; border-radius: 0.25rem;"> 47 < option v-for="(item, index) in ignoredProducts" :key="index" style="background-color: #e5e7eb; padding: 2px 5px; border-radius: 2px; font-size: 13px; margin-right: 4px; margin-top: 4px;">{{ item }}</option>46 <span v-for="(item, index) in ignoredProducts" :key="index" style="background-color: #e5e7eb; padding: 2px 5px; border-radius: 2px; font-size: 13px; margin-right: 4px; margin-top: 4px;">{{ item }}</span> 48 47 </div> 49 48 </label> … … 76 75 </div> 77 76 </label> 78 <label class="input-mailerlite"> 79 <sync-fields 80 id="sync_fields" 81 multiple="multiple" 82 data-placeholder="Click to select fields you want to sync" 83 class="wc-enhanced-select" 77 <label class="input-mailerlite" data-testid="settings-sync-fields"> 78 <sync-fields 79 id="sync_fields" 80 multiple="multiple" 81 data-placeholder="Click to select fields you want to sync" 84 82 style="width: 100%;" 85 83 :options="syncFields" … … 102 100 </button> 103 101 <button @click.prevent="startSync" v-if="totalUntrackedResources && !syncInProgress" type="button" class="btn btn-secondary-ml flex-start-ml" 104 data-woo-ml-reset-resources-sync="true" ref="startSync" ><span class="woo-ml-button-text">Synchronize {{ totalUntrackedResources }} untracked resources</span>102 data-woo-ml-reset-resources-sync="true" ref="startSync" data-testid="sync-untracked-resources-btn"><span class="woo-ml-button-text">Synchronize {{ totalUntrackedResources }} untracked resources</span> 105 103 </button> 106 104 </div> … … 119 117 value="yes" 120 118 id="subscribe_checkout_checkbox" 119 data-testid="subscribe-checkout-checkbox" 121 120 /> 122 121 <label for="subscribe_checkout_checkbox" class="settings-label-medium">Enable list subscription via checkout page.</label> … … 140 139 <label for="wooMlSubGroup" class="settings-label mb-3-ml">Subscribe checkbox position</label> 141 140 <label class="input-mailerlite"> 142 <select 143 class="w c-enhanced-select"144 name="checkout_position" 141 <select 142 class="woo-ml-native-select" 143 name="checkout_position" 145 144 :disabled="!settings.subscribeOnCheckout" 146 145 v-model="settings.selectedCheckoutPosition" … … 269 268 270 269 <div style="display: flex; justify-content: flex-end; margin-top: 2rem;"> 271 <button @click.prevent="updateSettings" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" type="submit" class="btn-primary-ml" style="margin-top: 2rem;" id="updateSettingsBtn" ><span class="woo-ml-button-text">Save changes</span></button>270 <button @click.prevent="updateSettings" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" type="submit" class="btn-primary-ml" style="margin-top: 2rem;" id="updateSettingsBtn" data-testid="update-settings-btn"><span class="woo-ml-button-text">Save changes</span></button> 272 271 </div> 273 272 <div class="settings-block"> -
woo-mailerlite/tags/3.1.13/admin/assets/js/components/Views/Wizard.js
r3338878 r3491060 28 28 <label for="wooMlApiKey" class="settings-label mb-3-ml">API key</label> 29 29 <div class="api-key-input"> 30 <input ref="wooMlApiKey" type="text" name="api-key" placeholder="Enter your MailerLite API key" v-model="wooMlApiKey" :disabled="isLoading" >31 <button @click="connectAccount" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" type="button" id="wooMlWizardApiKeyBtn" class="btn-primary-ml" ><span class="woo-ml-button-text">Connect account</span></button>30 <input ref="wooMlApiKey" type="text" name="api-key" placeholder="Enter your MailerLite API key" v-model="wooMlApiKey" :disabled="isLoading" data-testid="wizard-api-key"> 31 <button @click="connectAccount" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" type="button" id="wooMlWizardApiKeyBtn" class="btn-primary-ml" data-testid="wizard-connect-account-btn"><span class="woo-ml-button-text">Connect account</span></button> 32 32 </div> 33 33 <div class="signup-link-ml"> … … 61 61 <label for="wooMlSubGroup" class="settings-label mb-3-ml">Group</label> 62 62 <label class="input-mailerlite mb-2-ml" style="display: flex;"> 63 <custom-select 64 ref="wooMlSubGroupComponent" 65 class="wc-enhanced-select" 66 name="subscriber-group" 63 <custom-select 64 ref="wooMlSubGroupComponent" 65 name="subscriber-group" 67 66 style="width: 100%;" 68 67 :options="groups" … … 70 69 placeholder="Select group" 71 70 ></custom-select> 72 <button @click="createGroup" id="createGroupModal" type="button" class="btn-secondary-ml" style="margin-left: 0.5rem; white-space: nowrap;" >Create group</button>71 <button @click="createGroup" id="createGroupModal" type="button" class="btn-secondary-ml" style="margin-left: 0.5rem; white-space: nowrap;" data-testid="wizard-create-group-btn">Create group</button> 73 72 </label> 74 73 … … 109 108 </div> 110 109 </label> 111 <label class="input-mailerlite" >110 <label class="input-mailerlite" data-testid="wizard-sync-fields"> 112 111 <sync-fields 113 112 id="sync_fields" … … 115 114 v-model="selectedSyncFields" 116 115 placeholder="Click to select fields you want to sync" 117 class="wc-enhanced-select"style="width: 100%;"116 style="width: 100%;" 118 117 :options="syncFields" 119 118 :model-value="selectedSyncFields" … … 123 122 124 123 <div class="settings-block" style="display: flex; justify-content: space-between; padding-top: 2rem;"> 125 <button @click="startImport" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" id="startImport" type="button" class="btn-primary-ml" ><span class="woo-ml-button-text">Next</span></button>124 <button @click="startImport" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" id="startImport" type="button" class="btn-primary-ml" data-testid="wizard-start-import-btn"><span class="woo-ml-button-text">Next</span></button> 126 125 </div> 127 126 </div> -
woo-mailerlite/tags/3.1.13/admin/controllers/WooMailerLiteAdminSettingsController.php
r3470352 r3491060 18 18 19 19 $ignoredProducts = WooMailerLiteOptions::get('ignored_products', []); 20 21 $wasIgnored = $product->ignored; 22 20 23 if ($this->request('ml_ignore_product')) { 21 24 $product->ignored = true; … … 34 37 35 38 if ($this->apiClient()->isClassic()) { 36 $this->apiClient()->setConsumerData([ 37 'store' => home_url(), 38 'currency' => get_option('woocommerce_currency'), 39 'ignore_list' => array_map('strval', array_keys(WooMailerLiteOptions::get('ignored_products', []))), 40 'consumer_key' => WooMailerLiteOptions::get('consumerKey', null), 41 'consumer_secret' => WooMailerLiteOptions::get('consumerSecret', null), 42 'group_id' => WooMailerLiteOptions::get('group.id'), 43 'resubscribe' => WooMailerLiteOptions::get('settings.resubscribe'), 44 'create_segments' => false 45 ]); 39 if ((bool)$wasIgnored !== (bool)$product->ignored) { 40 $this->apiClient()->setConsumerData([ 41 'store' => home_url(), 42 'currency' => get_option('woocommerce_currency'), 43 'ignore_list' => array_map('strval', array_keys(WooMailerLiteOptions::get('ignored_products', []))), 44 'consumer_key' => WooMailerLiteOptions::get('consumerKey', null), 45 'consumer_secret' => WooMailerLiteOptions::get('consumerSecret', null), 46 'group_id' => WooMailerLiteOptions::get('group.id'), 47 'resubscribe' => WooMailerLiteOptions::get('settings.resubscribe'), 48 'create_segments' => false 49 ]); 50 } 46 51 } else { 47 52 $product->exclude_from_automations = $product->ignored; … … 97 102 98 103 $ignoredProducts = WooMailerLiteOptions::get('ignored_products', []); 104 105 $wasIgnored = $product->ignored; 106 99 107 if ($this->request('ml_ignore_product')) { 100 108 $product->ignored = true; … … 109 117 WooMailerLiteOptions::update('ignored_products', $ignoredProducts); 110 118 if ($this->apiClient()->isClassic()) { 111 $this->apiClient()->setConsumerData([ 112 'store' => home_url(), 113 'currency' => get_option('woocommerce_currency'), 114 'ignore_list' => array_map('strval', array_keys(WooMailerLiteOptions::get('ignored_products', []))), 115 'consumer_key' => WooMailerLiteOptions::get('consumerKey', null), 116 'consumer_secret' => WooMailerLiteOptions::get('consumerSecret', null), 117 'group_id' => WooMailerLiteOptions::get('group.id'), 118 'resubscribe' => WooMailerLiteOptions::get('settings.resubscribe'), 119 'create_segments' => false 120 ]); 119 if ((bool)$wasIgnored !== (bool)$product->ignored) { 120 $this->apiClient()->setConsumerData([ 121 'store' => home_url(), 122 'currency' => get_option('woocommerce_currency'), 123 'ignore_list' => array_map('strval', array_keys(WooMailerLiteOptions::get('ignored_products', []))), 124 'consumer_key' => WooMailerLiteOptions::get('consumerKey', null), 125 'consumer_secret' => WooMailerLiteOptions::get('consumerSecret', null), 126 'group_id' => WooMailerLiteOptions::get('group.id'), 127 'resubscribe' => WooMailerLiteOptions::get('settings.resubscribe'), 128 'create_segments' => false 129 ]); 130 } 121 131 } else { 122 132 $product->exclude_from_automations = $product->ignored; -
woo-mailerlite/tags/3.1.13/includes/WooMailerLite.php
r3462363 r3491060 115 115 $this->loader->add_action('woocommerce_product_bulk_and_quick_edit', WooMailerLiteAdminSettingsController::instance(), 'updateIgnoreProductsBulkAndQuickEdit', 10, 2); 116 116 $this->loader->add_filter('script_loader_tag', $pluginAdmin, 'addModuleTypeScript', 10, 3); 117 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'enqueueScripts'); 118 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'enqueueStyles'); 117 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'enqueueScripts', 20); 118 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'enqueueStyles', 20); 119 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'removeConflictingSelect2', 9999); 119 120 $this->loader->add_action('admin_menu', $pluginAdmin, 'addPluginAdminMenu', 71); 120 121 $this->loader->add_action('wp_ajax_woo_mailerlite_handle_connect_account', WooMailerLiteAdminWizardController::instance(), 'handleConnectAccount'); -
woo-mailerlite/tags/3.1.13/includes/api/WooMailerLiteRewriteApi.php
r3484689 r3491060 3 3 class WooMailerLiteRewriteApi extends WooMailerLiteApi 4 4 { 5 const BASE_URL = 'https://connect.mailerlite.com/api';5 const DEFAULT_BASE_URL = 'https://connect.mailerlite.com/api'; 6 6 7 7 public function __construct() 8 8 { 9 parent::__construct(self::BASE_URL); 9 $baseUrl = $this->getBaseUrl(); 10 parent::__construct($baseUrl); 11 } 12 13 /** 14 * Base URL for the Rewrite API. Override for tests via MAILERLITE_API_URL env var 15 * or by defining MAILERLITE_API_URL in wp-config (e.g. wp-env config in .wp-env.json). 16 * 17 * @return string 18 */ 19 private function getBaseUrl() 20 { 21 if (defined('MAILERLITE_API_URL')) { 22 return MAILERLITE_API_URL; 23 } 24 return self::DEFAULT_BASE_URL; 10 25 } 11 26 -
woo-mailerlite/tags/3.1.13/includes/common/WooMailerLiteOptions.php
r3484689 r3491060 59 59 60 60 if ($key === self::$apiKey && !empty($options[$key])) { 61 $decrypted = WooMailerLiteEncryption::instance()->decrypt($options[$key]); 62 if ($decrypted !== false) { 63 return $decrypted; 64 } else { 65 self::update($key, WooMailerLiteEncryption::instance()->encrypt($options[$key])); 66 } 61 return self::handleApiKeyRetrieval($options[$key]); 67 62 } 68 63 return $options[$key]; … … 76 71 public static function update($key, $value) 77 72 { 73 if ($key === self::$apiKey && !empty($value) && !self::isEncryptedData($value)) { 74 $encrypted = WooMailerLiteEncryption::instance()->encrypt($value); 75 if ($encrypted !== false) { 76 $value = $encrypted; 77 } else { 78 WooMailerLiteLog()->error('WooMailerLite: Encryption failed, storing as plain text temporarily.'); 79 } 80 } 81 78 82 $options = get_option(self::$key, []); 79 83 $options[$key] = $value; … … 125 129 return in_array($status, self::PENDING_ORDER_STATUSES); 126 130 } 131 132 private static function isEncryptedData($value) { 133 return is_string($value) 134 && strlen($value) > 50 135 && preg_match('/^[A-Za-z0-9+\/]+=*$/', $value) 136 && base64_decode($value, true) !== false; 137 } 138 139 private static function handleApiKeyRetrieval($value) 140 { 141 if (self::isEncryptedData($value)) { 142 $decrypted = WooMailerLiteEncryption::instance()->decrypt($value); 143 if ($decrypted !== false) { 144 return $decrypted; 145 } else { 146 WooMailerLiteLog()->error('WooMailerLite: API key decryption failed. Re-enter API key.'); 147 return null; 148 } 149 } else { 150 self::update(self::$apiKey, $value); 151 return $value; 152 } 153 } 127 154 } -
woo-mailerlite/tags/3.1.13/includes/controllers/WooMailerLiteController.php
r3421835 r3491060 86 86 } 87 87 88 if (in_array('int', $validation) && !ctype_digit(strval($this->request[$key]))) {89 throw new Exception("The $key field must be a ninteger.");88 if (in_array('int', $validation) && (!ctype_digit(strval($this->request[$key])) || (int)$this->request[$key] <= 0)) { 89 throw new Exception("The $key field must be a positive integer."); 90 90 } 91 91 -
woo-mailerlite/tags/3.1.13/includes/jobs/WooMailerLiteAbstractJob.php
r3421835 r3491060 24 24 public static function dispatch(array $data = []): void 25 25 { 26 $data['attempts'] = $data['attempts'] ?? 0; 27 28 // In local/test (e.g. wp-env), run sync immediately so jobs don't stay pending. 29 if (!isset($data['selfMechanism']['sync']) && defined('WOO_MAILERLITE_SYNC_IMMEDIATE') && WOO_MAILERLITE_SYNC_IMMEDIATE) { 30 $data['selfMechanism']['sync'] = true; 31 } 32 26 33 $jobClass = static::class; 27 34 $objectId = 0; 28 29 $data['attempts'] = $data['attempts'] ?? 0;30 35 31 36 if ((!isset($data['selfMechanism']['sync']) || !$data['selfMechanism']['sync']) && function_exists('as_enqueue_async_action')) { -
woo-mailerlite/tags/3.1.13/public/css/mailerlite-select2.css
r3338878 r3491060 5 5 padding-left: 0; 6 6 border-radius: 0.25rem;} 7 .select2- dropdown {7 .select2-container--mailerlite .select2-dropdown { 8 8 border-color: #d1d5db; 9 9 } … … 52 52 background-color: #eee; 53 53 cursor: default; } 54 .select2-container--mailerlite.select2-container--disabled .select2-selection--single .select2-selection__clear , .select2-container--default .select2-selection--multiple .select2-selection__clear{54 .select2-container--mailerlite.select2-container--disabled .select2-selection--single .select2-selection__clear { 55 55 display: none; } 56 56 … … 59 59 border-width: 0 4px 5px 4px; } 60 60 61 .select2-container--mailerlite .select2-selection--multiple ,.select2-container--default .select2-selection--multiple{61 .select2-container--mailerlite .select2-selection--multiple { 62 62 background-color: white; 63 63 border: 1px solid #d1d5db; … … 67 67 padding-right: 5px; 68 68 position: relative; } 69 .select2-container-- default.select2-container--focus .select2-selection--multiple {69 .select2-container--mailerlite.select2-container--focus .select2-selection--multiple { 70 70 border-color: #d1d5db; 71 71 } … … 78 78 } 79 79 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice { 80 background-color: #e4e4e4; 81 border: 1px solid #d1d5db; 82 border-radius: 4px; 80 display: inline-flex; 81 align-items: center; 82 background-color: #e5e7eb; 83 border: 0; 84 border-radius: 2px; 83 85 box-sizing: border-box; 84 display: inline-block;86 font-size: 13px; 85 87 margin-left: 5px; 86 88 margin-top: 5px; 87 padding: 0;88 padding-left: 20px;89 position: relative;90 89 max-width: 100%; 91 90 overflow: hidden; 91 padding: 2px 5px; 92 position: relative; 93 vertical-align: bottom; 94 white-space: nowrap; 95 } 96 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__display { 97 color: rgba(0,0,0,0.75); 98 cursor: default; 99 min-width: 0; 100 order: 0; 101 overflow: hidden; 102 padding-right: 4px; 92 103 text-overflow: ellipsis; 93 vertical-align: bottom;94 white-space: nowrap; }95 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__display {96 cursor: default;97 color: rgba(0,0,0,0.75);98 padding-left: 2px;99 padding-right: 5px;100 104 } 101 105 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove { 102 106 background-color: transparent; 103 107 border: none; 104 border-right: 1px solid #aaa;105 border-top-left-radius: 4px;106 border-bottom-left-radius: 4px;107 108 color: #999; 108 109 cursor: pointer; 109 font-size: 1em; 110 font-weight: bold; 111 padding: 0 4px; 112 position: absolute; 113 left: 0; 114 top: 0; } 115 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:hover, .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:focus { 116 background-color: #f1f1f1; 117 color: #333; 118 outline: none; } 110 flex-shrink: 0; 111 font-size: 0.875rem; 112 font-weight: normal; 113 line-height: 1; 114 order: 1; 115 padding: 0 2px; 116 position: static; 117 } 118 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:hover, 119 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:focus { 120 color: #555; 121 background-color: transparent; 122 outline: none; 123 } 119 124 120 125 .select2-container--mailerlite[dir="rtl"] .select2-selection--multiple .select2-selection__choice { … … 150 155 display: none; } 151 156 152 .select2-container--mailerlite.select2-container--open.select2-container--above .select2-selection--single, .select2-container--mailerlite.select2-container--open.select2-container--above .select2-selection--multiple { 157 .select2-container--mailerlite.select2-container--open.select2-container--above .select2-selection--single, 158 .select2-container--mailerlite.select2-container--open.select2-container--above .select2-selection--multiple { 153 159 border-top-left-radius: 0; 154 160 border-top-right-radius: 0; } 155 161 156 .select2-container--mailerlite.select2-container--open.select2-container--below .select2-selection--single, .select2-container--mailerlite.select2-container--open.select2-container--below .select2-selection--multiple { 162 .select2-container--mailerlite.select2-container--open.select2-container--below .select2-selection--single, 163 .select2-container--mailerlite.select2-container--open.select2-container--below .select2-selection--multiple { 157 164 border-bottom-left-radius: 0; 158 165 border-bottom-right-radius: 0; } … … 212 219 padding: 6px; } 213 220 214 .select2- results__option {221 .select2-container--mailerlite .select2-results__option { 215 222 padding-right: 20px; 216 223 vertical-align: middle; 217 224 } 218 225 219 .select2- results__option:before {226 .select2-container--mailerlite .select2-results__option:before { 220 227 content: ""; 221 228 display: inline-block; … … 251 258 border-width: 2px; 252 259 } 253 .select2-container-- open .select2-dropdown--below {260 .select2-container--mailerlite.select2-container--open .select2-dropdown--below { 254 261 border-color: transparent; 255 262 border-radius: 6px; … … 258 265 } 259 266 260 .select2-selection .select2-selection--multiple:after { 261 content: 'hhghgh'; 262 } 263 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice { 264 background-color: #e5e7eb; 265 border: 0; 266 border-radius: 2px; 267 font-size: 13px; 268 margin-bottom: 0; 269 padding-top: 2px; 270 padding-bottom: 2px; 271 padding-left: 5px; 272 padding-right: 14px; 273 } 274 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove { 275 border-right: 0; 276 color: rgba(0,0,0,0.75); 277 font-weight: normal; 278 font-size: 1rem; 279 padding-right: 5px; 280 right: 0; 281 left: auto; 282 } 283 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:hover { 284 color: #9b9b9b; 285 background-color: #e5e7eb; 286 } 287 .select2-container--mailerlite .select2-selection--multiple, .select2-container--default.select2-container--focus .select2-selection--multiple { 267 .select2-container--mailerlite .select2-selection--multiple, 268 .select2-container--mailerlite.select2-container--focus .select2-selection--multiple { 288 269 border-width: 1px !important; 289 270 border-color: #d1d5db !important; 290 271 } 291 .select2- results__option.select2-results__option--selected:before {272 .select2-container--mailerlite .select2-results__option.select2-results__option--selected:before { 292 273 color: #fff; 293 274 background-color: #09c269; 294 275 border: 0; 295 276 display: inline-block; 296 content: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C /svg%3E");277 content: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C%2Fsvg%3E"); 297 278 padding-left: 0; 298 279 width: 14px; 299 280 height: 14px; 300 281 } 301 .select2- results__option.select2-results__message:before {282 .select2-container--mailerlite .select2-results__option.select2-results__message:before { 302 283 content: none; 303 284 } … … 312 293 position: absolute; 313 294 } 314 .wc-wp-version-gte-53 .select2-container.select2-container--open .select2-selection--multiple { 295 .woo-ml-native-select { 296 border: 1px solid #d1d5db; 297 border-radius: 0.25rem; 298 height: 38px; 299 width: 100%; 300 padding: 0 12px; 301 font-size: 14px; 302 color: rgba(0,0,0,0.75); 303 background-color: #fff; 304 appearance: none; 305 background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E"); 306 background-repeat: no-repeat; 307 background-position: right 12px center; 308 background-size: 14px 14px; 309 cursor: pointer; 310 } 311 .woo-ml-native-select:focus { 312 outline: none; 313 border-color: #000; 314 } 315 .woo-ml-native-select:disabled { 316 background-color: #eee; 317 cursor: default; 318 } 319 320 .wc-wp-version-gte-53 .select2-container--mailerlite.select2-container--open .select2-selection--multiple { 315 321 border-width: 1px; 316 322 border-color: #d1d5db; -
woo-mailerlite/tags/3.1.13/woo-mailerlite.php
r3484689 r3491060 16 16 * Plugin URI: https://mailerlite.com 17 17 * Description: Official MailerLite integration for WooCommerce. Track sales and campaign ROI, import products details, automate emails based on purchases and seamlessly add your customers to your email marketing lists via WooCommerce's checkout process. 18 * Version: 3.1.1 218 * Version: 3.1.13 19 19 * Author: MailerLite 20 20 * Author URI: https://mailerlite.com … … 40 40 * Update when you release new versions. 41 41 */ 42 define( 'WOO_MAILERLITE_VERSION', '3.1.1 2' );42 define( 'WOO_MAILERLITE_VERSION', '3.1.13' ); 43 43 44 44 define('WOO_MAILERLITE_ASYNC_JOBS', false); -
woo-mailerlite/trunk/README.txt
r3484689 r3491060 6 6 Tested up to: 6.8.2 7 7 Requires PHP: 7.2.5 8 Stable tag: 3.1.1 28 Stable tag: 3.1.13 9 9 License: GPLv3 or later 10 10 License URI: http://www.gnu.org/licenses/gpl-3.0.html … … 84 84 85 85 == Changelog == 86 = 3.1.13 (25th March 2026) = 87 * Bug fixes and performance improvements 88 86 89 = 3.1.12 (17th March 2026) = 87 90 * Bug fixes -
woo-mailerlite/trunk/admin/WooMailerLiteAdmin.php
r3415073 r3491060 23 23 public function enqueueScripts($hook) 24 24 { 25 if ($hook === 'edit.php' && isset($_GET['post_type']) && $_GET['post_type'] === 'product') { 26 wp_enqueue_script('woo-mailerlite-quick-edit', plugin_dir_url(__FILE__) . '../admin/assets/js/ml-quick-edit.js', ['jquery', 'inline-edit-post'], null, true); 27 return; 28 } 29 25 30 if ($hook !== 'woocommerce_page_mailerlite') { 26 31 return; 27 32 } 33 34 wp_dequeue_script('select2'); 35 wp_deregister_script('select2'); 36 28 37 wp_enqueue_script('woo-mailerlite-vue-cdn', 'https://cdn.jsdelivr.net/npm/vue@3.5.13/dist/vue.global.prod.js', [], null, true); 29 38 wp_localize_script('woo-mailerlite-admin', 'woo_mailerlite_admin_data', array( … … 32 41 )); 33 42 34 wp_enqueue_script(' style2-script', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js');35 wp_enqueue_script('woo-mailerlite-admin', plugin_dir_url(__FILE__) . '../admin/assets/js/ml-app.js', ['jquery', 'woo-mailerlite-vue-cdn' ], null, true);43 wp_enqueue_script('woo-mailerlite-select2', plugin_dir_url(__FILE__) . 'assets/js/lib/select2.min.js', ['jquery'], WOO_MAILERLITE_VERSION, true); 44 wp_enqueue_script('woo-mailerlite-admin', plugin_dir_url(__FILE__) . '../admin/assets/js/ml-app.js', ['jquery', 'woo-mailerlite-vue-cdn', 'woo-mailerlite-select2'], null, true); 36 45 37 46 } … … 41 50 return; 42 51 } 52 53 wp_dequeue_style('select2'); 54 wp_deregister_style('select2'); 55 43 56 wp_enqueue_style('woo-mailerlite-admin-css', plugin_dir_url( __FILE__ ) . '../admin/assets/css/admin.css', false, WOO_MAILERLITE_VERSION); 44 wp_enqueue_style('style2-style', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css'); 45 wp_enqueue_style('style2-mailerlite-style', plugin_dir_url( __FILE__ ) . '../public/css/mailerlite-select2.css',false, WOO_MAILERLITE_VERSION); 57 wp_enqueue_style('woo-mailerlite-select2-css', plugin_dir_url( __FILE__ ) . 'assets/css/lib/select2.min.css', false, WOO_MAILERLITE_VERSION); 58 wp_enqueue_style('woo-mailerlite-select2-theme', plugin_dir_url( __FILE__ ) . '../public/css/mailerlite-select2.css', ['woo-mailerlite-select2-css'], WOO_MAILERLITE_VERSION); 59 } 60 61 public function removeConflictingSelect2($hook) 62 { 63 if ($hook !== 'woocommerce_page_mailerlite') { 64 return; 65 } 66 67 global $wp_scripts, $wp_styles; 68 69 $scriptPatterns = ['select2', 'selectWoo', 'wc-enhanced-select']; 70 71 foreach ($wp_scripts->registered as $handle => $script) { 72 if ($handle === 'woo-mailerlite-select2') { 73 continue; 74 } 75 foreach ($scriptPatterns as $pattern) { 76 if (strpos($script->src, $pattern) !== false) { 77 wp_dequeue_script($handle); 78 wp_deregister_script($handle); 79 break; 80 } 81 } 82 } 83 84 $stylePatterns = ['select2', 'selectWoo']; 85 86 foreach ($wp_styles->registered as $handle => $style) { 87 if ($handle === 'woo-mailerlite-select2-css' || $handle === 'woo-mailerlite-select2-theme') { 88 continue; 89 } 90 foreach ($stylePatterns as $pattern) { 91 if (strpos($style->src, $pattern) !== false) { 92 wp_dequeue_style($handle); 93 wp_deregister_style($handle); 94 break; 95 } 96 } 97 } 46 98 } 47 99 … … 115 167 public function populateIgnoreProductBlock($column, $post_id) 116 168 { 117 $ignoredProducts = WooMailerLiteOptions::get('ignored_products', []);118 169 119 170 switch ($column) { 120 171 case 'name' : 172 $ignoredProducts = WooMailerLiteOptions::get('ignored_products', []); 121 173 ?> 122 174 <div class="hidden ml_ignore_product_inline" id="ml_ignore_product_inline_<?=intval($post_id) ?>"> 123 <div id="_ml_ignore_product"><?php echo array_key_exists($post_id, $ignoredProducts) ? 'yes' : 'no' ?></div>175 <div class="_ml_ignore_product"><?php echo array_key_exists($post_id, $ignoredProducts) ? 'yes' : 'no' ?></div> 124 176 </div> 125 177 <?php -
woo-mailerlite/trunk/admin/assets/js/components/Forms/CustomSelect.js
r3338878 r3491060 2 2 import eventBus from "../eventBus.js"; 3 3 const template = ` 4 <select ref="wooMlSubGroup" class="w c-enhanced-select">4 <select ref="wooMlSubGroup" class="woo-ml-group-select"> 5 5 <option value="" disabled >{{ placeholder }}</option> 6 6 </select> -
woo-mailerlite/trunk/admin/assets/js/components/Forms/SyncFields.js
r3338878 r3491060 1 1 const template = ` 2 <select v-model="setSyncFields" />2 <select v-model="setSyncFields" data-testid="sync-fields-select"/> 3 3 `; 4 4 -
woo-mailerlite/trunk/admin/assets/js/components/Modals/Modals.js
r3338878 r3491060 5 5 const template = ` 6 6 <!-- Create Group Modal --> 7 <div v-if="openCreateGroupModal" class="woo-ml-wizard-modal" id="wooMlWizardCreateGroupModal" role="dialog" >7 <div v-if="openCreateGroupModal" class="woo-ml-wizard-modal" id="wooMlWizardCreateGroupModal" role="dialog" data-testid="create-group-modal"> 8 8 <div class="woo-ml-wizard-modal-parent"> 9 9 <div class="woo-ml-wizard-modal-container"> … … 15 15 <div class="woo-ml-wizard-modal-body"> 16 16 <div class="create-group-input"> 17 <input ref="wooMlCreateGroup" type="text" name="createGroup" placeholder="Enter group name" v-model="createGroupName" class="" >17 <input ref="wooMlCreateGroup" type="text" name="createGroup" placeholder="Enter group name" v-model="createGroupName" class="" data-testid="create-group-name-input"> 18 18 </div> 19 19 <div class="modal-button-ml"> 20 20 <button @click="openCreateGroupModal = false" type="button" class="btn-secondary-ml woo-ml-close" style="margin-right: 12px;">Close</button> 21 <button @click="createGroup" ref="createGroup" type="button" class="btn-primary-ml" ><span class="woo-ml-button-text">Create group</span></button>21 <button @click="createGroup" ref="createGroup" type="button" class="btn-primary-ml" data-testid="create-group-submit-btn"><span class="woo-ml-button-text">Create group</span></button> 22 22 </div> 23 23 </div> -
woo-mailerlite/trunk/admin/assets/js/components/Views/Settings.js
r3377455 r3491060 6 6 7 7 const template = ` 8 <div v-if="syncInProgress || asyncSyncInProgress" class="woo-ml-sync-loading-container" >8 <div v-if="syncInProgress || asyncSyncInProgress" class="woo-ml-sync-loading-container" data-testid="sync-in-progress"> 9 9 <strong>Sync in progress...</strong> 10 10 <p>This will continue as long as you stay on this page. … … 20 20 <label for="wooMlSubGroup" class="settings-label mb-3-ml">Subscriber group</label> 21 21 <label class="input-mailerlite mb-2-ml" style="display: flex;"> 22 <custom-select 23 id="wooMlSubGroup" 24 class="wc-enhanced-select" 25 name="subscriber-group" 22 <custom-select 23 id="wooMlSubGroup" 24 name="subscriber-group" 26 25 :options="groups" 27 26 v-model="selectedGroup" … … 43 42 </label> 44 43 <label v-if="ignoredProducts.length !== 0" class="input-mailerlite" style="cursor: default;"> 45 <div multiple class="wc-enhanced-select"name="ignore_product_list"44 <div multiple name="ignore_product_list" 46 45 style="min-height: 26px; padding-left: 8px; display: flex; flex-direction: row; overflow: hidden; flex-wrap: wrap; padding: 4px; padding-top: 0; border: 1px solid #d1d5db; border-radius: 0.25rem;"> 47 < option v-for="(item, index) in ignoredProducts" :key="index" style="background-color: #e5e7eb; padding: 2px 5px; border-radius: 2px; font-size: 13px; margin-right: 4px; margin-top: 4px;">{{ item }}</option>46 <span v-for="(item, index) in ignoredProducts" :key="index" style="background-color: #e5e7eb; padding: 2px 5px; border-radius: 2px; font-size: 13px; margin-right: 4px; margin-top: 4px;">{{ item }}</span> 48 47 </div> 49 48 </label> … … 76 75 </div> 77 76 </label> 78 <label class="input-mailerlite"> 79 <sync-fields 80 id="sync_fields" 81 multiple="multiple" 82 data-placeholder="Click to select fields you want to sync" 83 class="wc-enhanced-select" 77 <label class="input-mailerlite" data-testid="settings-sync-fields"> 78 <sync-fields 79 id="sync_fields" 80 multiple="multiple" 81 data-placeholder="Click to select fields you want to sync" 84 82 style="width: 100%;" 85 83 :options="syncFields" … … 102 100 </button> 103 101 <button @click.prevent="startSync" v-if="totalUntrackedResources && !syncInProgress" type="button" class="btn btn-secondary-ml flex-start-ml" 104 data-woo-ml-reset-resources-sync="true" ref="startSync" ><span class="woo-ml-button-text">Synchronize {{ totalUntrackedResources }} untracked resources</span>102 data-woo-ml-reset-resources-sync="true" ref="startSync" data-testid="sync-untracked-resources-btn"><span class="woo-ml-button-text">Synchronize {{ totalUntrackedResources }} untracked resources</span> 105 103 </button> 106 104 </div> … … 119 117 value="yes" 120 118 id="subscribe_checkout_checkbox" 119 data-testid="subscribe-checkout-checkbox" 121 120 /> 122 121 <label for="subscribe_checkout_checkbox" class="settings-label-medium">Enable list subscription via checkout page.</label> … … 140 139 <label for="wooMlSubGroup" class="settings-label mb-3-ml">Subscribe checkbox position</label> 141 140 <label class="input-mailerlite"> 142 <select 143 class="w c-enhanced-select"144 name="checkout_position" 141 <select 142 class="woo-ml-native-select" 143 name="checkout_position" 145 144 :disabled="!settings.subscribeOnCheckout" 146 145 v-model="settings.selectedCheckoutPosition" … … 269 268 270 269 <div style="display: flex; justify-content: flex-end; margin-top: 2rem;"> 271 <button @click.prevent="updateSettings" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" type="submit" class="btn-primary-ml" style="margin-top: 2rem;" id="updateSettingsBtn" ><span class="woo-ml-button-text">Save changes</span></button>270 <button @click.prevent="updateSettings" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" type="submit" class="btn-primary-ml" style="margin-top: 2rem;" id="updateSettingsBtn" data-testid="update-settings-btn"><span class="woo-ml-button-text">Save changes</span></button> 272 271 </div> 273 272 <div class="settings-block"> -
woo-mailerlite/trunk/admin/assets/js/components/Views/Wizard.js
r3338878 r3491060 28 28 <label for="wooMlApiKey" class="settings-label mb-3-ml">API key</label> 29 29 <div class="api-key-input"> 30 <input ref="wooMlApiKey" type="text" name="api-key" placeholder="Enter your MailerLite API key" v-model="wooMlApiKey" :disabled="isLoading" >31 <button @click="connectAccount" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" type="button" id="wooMlWizardApiKeyBtn" class="btn-primary-ml" ><span class="woo-ml-button-text">Connect account</span></button>30 <input ref="wooMlApiKey" type="text" name="api-key" placeholder="Enter your MailerLite API key" v-model="wooMlApiKey" :disabled="isLoading" data-testid="wizard-api-key"> 31 <button @click="connectAccount" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" type="button" id="wooMlWizardApiKeyBtn" class="btn-primary-ml" data-testid="wizard-connect-account-btn"><span class="woo-ml-button-text">Connect account</span></button> 32 32 </div> 33 33 <div class="signup-link-ml"> … … 61 61 <label for="wooMlSubGroup" class="settings-label mb-3-ml">Group</label> 62 62 <label class="input-mailerlite mb-2-ml" style="display: flex;"> 63 <custom-select 64 ref="wooMlSubGroupComponent" 65 class="wc-enhanced-select" 66 name="subscriber-group" 63 <custom-select 64 ref="wooMlSubGroupComponent" 65 name="subscriber-group" 67 66 style="width: 100%;" 68 67 :options="groups" … … 70 69 placeholder="Select group" 71 70 ></custom-select> 72 <button @click="createGroup" id="createGroupModal" type="button" class="btn-secondary-ml" style="margin-left: 0.5rem; white-space: nowrap;" >Create group</button>71 <button @click="createGroup" id="createGroupModal" type="button" class="btn-secondary-ml" style="margin-left: 0.5rem; white-space: nowrap;" data-testid="wizard-create-group-btn">Create group</button> 73 72 </label> 74 73 … … 109 108 </div> 110 109 </label> 111 <label class="input-mailerlite" >110 <label class="input-mailerlite" data-testid="wizard-sync-fields"> 112 111 <sync-fields 113 112 id="sync_fields" … … 115 114 v-model="selectedSyncFields" 116 115 placeholder="Click to select fields you want to sync" 117 class="wc-enhanced-select"style="width: 100%;"116 style="width: 100%;" 118 117 :options="syncFields" 119 118 :model-value="selectedSyncFields" … … 123 122 124 123 <div class="settings-block" style="display: flex; justify-content: space-between; padding-top: 2rem;"> 125 <button @click="startImport" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" id="startImport" type="button" class="btn-primary-ml" ><span class="woo-ml-button-text">Next</span></button>124 <button @click="startImport" :class="{ 'woo-ml-button-loading': isLoading }" :disabled="isLoading" id="startImport" type="button" class="btn-primary-ml" data-testid="wizard-start-import-btn"><span class="woo-ml-button-text">Next</span></button> 126 125 </div> 127 126 </div> -
woo-mailerlite/trunk/admin/controllers/WooMailerLiteAdminSettingsController.php
r3470352 r3491060 18 18 19 19 $ignoredProducts = WooMailerLiteOptions::get('ignored_products', []); 20 21 $wasIgnored = $product->ignored; 22 20 23 if ($this->request('ml_ignore_product')) { 21 24 $product->ignored = true; … … 34 37 35 38 if ($this->apiClient()->isClassic()) { 36 $this->apiClient()->setConsumerData([ 37 'store' => home_url(), 38 'currency' => get_option('woocommerce_currency'), 39 'ignore_list' => array_map('strval', array_keys(WooMailerLiteOptions::get('ignored_products', []))), 40 'consumer_key' => WooMailerLiteOptions::get('consumerKey', null), 41 'consumer_secret' => WooMailerLiteOptions::get('consumerSecret', null), 42 'group_id' => WooMailerLiteOptions::get('group.id'), 43 'resubscribe' => WooMailerLiteOptions::get('settings.resubscribe'), 44 'create_segments' => false 45 ]); 39 if ((bool)$wasIgnored !== (bool)$product->ignored) { 40 $this->apiClient()->setConsumerData([ 41 'store' => home_url(), 42 'currency' => get_option('woocommerce_currency'), 43 'ignore_list' => array_map('strval', array_keys(WooMailerLiteOptions::get('ignored_products', []))), 44 'consumer_key' => WooMailerLiteOptions::get('consumerKey', null), 45 'consumer_secret' => WooMailerLiteOptions::get('consumerSecret', null), 46 'group_id' => WooMailerLiteOptions::get('group.id'), 47 'resubscribe' => WooMailerLiteOptions::get('settings.resubscribe'), 48 'create_segments' => false 49 ]); 50 } 46 51 } else { 47 52 $product->exclude_from_automations = $product->ignored; … … 97 102 98 103 $ignoredProducts = WooMailerLiteOptions::get('ignored_products', []); 104 105 $wasIgnored = $product->ignored; 106 99 107 if ($this->request('ml_ignore_product')) { 100 108 $product->ignored = true; … … 109 117 WooMailerLiteOptions::update('ignored_products', $ignoredProducts); 110 118 if ($this->apiClient()->isClassic()) { 111 $this->apiClient()->setConsumerData([ 112 'store' => home_url(), 113 'currency' => get_option('woocommerce_currency'), 114 'ignore_list' => array_map('strval', array_keys(WooMailerLiteOptions::get('ignored_products', []))), 115 'consumer_key' => WooMailerLiteOptions::get('consumerKey', null), 116 'consumer_secret' => WooMailerLiteOptions::get('consumerSecret', null), 117 'group_id' => WooMailerLiteOptions::get('group.id'), 118 'resubscribe' => WooMailerLiteOptions::get('settings.resubscribe'), 119 'create_segments' => false 120 ]); 119 if ((bool)$wasIgnored !== (bool)$product->ignored) { 120 $this->apiClient()->setConsumerData([ 121 'store' => home_url(), 122 'currency' => get_option('woocommerce_currency'), 123 'ignore_list' => array_map('strval', array_keys(WooMailerLiteOptions::get('ignored_products', []))), 124 'consumer_key' => WooMailerLiteOptions::get('consumerKey', null), 125 'consumer_secret' => WooMailerLiteOptions::get('consumerSecret', null), 126 'group_id' => WooMailerLiteOptions::get('group.id'), 127 'resubscribe' => WooMailerLiteOptions::get('settings.resubscribe'), 128 'create_segments' => false 129 ]); 130 } 121 131 } else { 122 132 $product->exclude_from_automations = $product->ignored; -
woo-mailerlite/trunk/includes/WooMailerLite.php
r3462363 r3491060 115 115 $this->loader->add_action('woocommerce_product_bulk_and_quick_edit', WooMailerLiteAdminSettingsController::instance(), 'updateIgnoreProductsBulkAndQuickEdit', 10, 2); 116 116 $this->loader->add_filter('script_loader_tag', $pluginAdmin, 'addModuleTypeScript', 10, 3); 117 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'enqueueScripts'); 118 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'enqueueStyles'); 117 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'enqueueScripts', 20); 118 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'enqueueStyles', 20); 119 $this->loader->add_action('admin_enqueue_scripts', $pluginAdmin, 'removeConflictingSelect2', 9999); 119 120 $this->loader->add_action('admin_menu', $pluginAdmin, 'addPluginAdminMenu', 71); 120 121 $this->loader->add_action('wp_ajax_woo_mailerlite_handle_connect_account', WooMailerLiteAdminWizardController::instance(), 'handleConnectAccount'); -
woo-mailerlite/trunk/includes/api/WooMailerLiteRewriteApi.php
r3484689 r3491060 3 3 class WooMailerLiteRewriteApi extends WooMailerLiteApi 4 4 { 5 const BASE_URL = 'https://connect.mailerlite.com/api';5 const DEFAULT_BASE_URL = 'https://connect.mailerlite.com/api'; 6 6 7 7 public function __construct() 8 8 { 9 parent::__construct(self::BASE_URL); 9 $baseUrl = $this->getBaseUrl(); 10 parent::__construct($baseUrl); 11 } 12 13 /** 14 * Base URL for the Rewrite API. Override for tests via MAILERLITE_API_URL env var 15 * or by defining MAILERLITE_API_URL in wp-config (e.g. wp-env config in .wp-env.json). 16 * 17 * @return string 18 */ 19 private function getBaseUrl() 20 { 21 if (defined('MAILERLITE_API_URL')) { 22 return MAILERLITE_API_URL; 23 } 24 return self::DEFAULT_BASE_URL; 10 25 } 11 26 -
woo-mailerlite/trunk/includes/common/WooMailerLiteOptions.php
r3484689 r3491060 59 59 60 60 if ($key === self::$apiKey && !empty($options[$key])) { 61 $decrypted = WooMailerLiteEncryption::instance()->decrypt($options[$key]); 62 if ($decrypted !== false) { 63 return $decrypted; 64 } else { 65 self::update($key, WooMailerLiteEncryption::instance()->encrypt($options[$key])); 66 } 61 return self::handleApiKeyRetrieval($options[$key]); 67 62 } 68 63 return $options[$key]; … … 76 71 public static function update($key, $value) 77 72 { 73 if ($key === self::$apiKey && !empty($value) && !self::isEncryptedData($value)) { 74 $encrypted = WooMailerLiteEncryption::instance()->encrypt($value); 75 if ($encrypted !== false) { 76 $value = $encrypted; 77 } else { 78 WooMailerLiteLog()->error('WooMailerLite: Encryption failed, storing as plain text temporarily.'); 79 } 80 } 81 78 82 $options = get_option(self::$key, []); 79 83 $options[$key] = $value; … … 125 129 return in_array($status, self::PENDING_ORDER_STATUSES); 126 130 } 131 132 private static function isEncryptedData($value) { 133 return is_string($value) 134 && strlen($value) > 50 135 && preg_match('/^[A-Za-z0-9+\/]+=*$/', $value) 136 && base64_decode($value, true) !== false; 137 } 138 139 private static function handleApiKeyRetrieval($value) 140 { 141 if (self::isEncryptedData($value)) { 142 $decrypted = WooMailerLiteEncryption::instance()->decrypt($value); 143 if ($decrypted !== false) { 144 return $decrypted; 145 } else { 146 WooMailerLiteLog()->error('WooMailerLite: API key decryption failed. Re-enter API key.'); 147 return null; 148 } 149 } else { 150 self::update(self::$apiKey, $value); 151 return $value; 152 } 153 } 127 154 } -
woo-mailerlite/trunk/includes/controllers/WooMailerLiteController.php
r3421835 r3491060 86 86 } 87 87 88 if (in_array('int', $validation) && !ctype_digit(strval($this->request[$key]))) {89 throw new Exception("The $key field must be a ninteger.");88 if (in_array('int', $validation) && (!ctype_digit(strval($this->request[$key])) || (int)$this->request[$key] <= 0)) { 89 throw new Exception("The $key field must be a positive integer."); 90 90 } 91 91 -
woo-mailerlite/trunk/includes/jobs/WooMailerLiteAbstractJob.php
r3421835 r3491060 24 24 public static function dispatch(array $data = []): void 25 25 { 26 $data['attempts'] = $data['attempts'] ?? 0; 27 28 // In local/test (e.g. wp-env), run sync immediately so jobs don't stay pending. 29 if (!isset($data['selfMechanism']['sync']) && defined('WOO_MAILERLITE_SYNC_IMMEDIATE') && WOO_MAILERLITE_SYNC_IMMEDIATE) { 30 $data['selfMechanism']['sync'] = true; 31 } 32 26 33 $jobClass = static::class; 27 34 $objectId = 0; 28 29 $data['attempts'] = $data['attempts'] ?? 0;30 35 31 36 if ((!isset($data['selfMechanism']['sync']) || !$data['selfMechanism']['sync']) && function_exists('as_enqueue_async_action')) { -
woo-mailerlite/trunk/public/css/mailerlite-select2.css
r3338878 r3491060 5 5 padding-left: 0; 6 6 border-radius: 0.25rem;} 7 .select2- dropdown {7 .select2-container--mailerlite .select2-dropdown { 8 8 border-color: #d1d5db; 9 9 } … … 52 52 background-color: #eee; 53 53 cursor: default; } 54 .select2-container--mailerlite.select2-container--disabled .select2-selection--single .select2-selection__clear , .select2-container--default .select2-selection--multiple .select2-selection__clear{54 .select2-container--mailerlite.select2-container--disabled .select2-selection--single .select2-selection__clear { 55 55 display: none; } 56 56 … … 59 59 border-width: 0 4px 5px 4px; } 60 60 61 .select2-container--mailerlite .select2-selection--multiple ,.select2-container--default .select2-selection--multiple{61 .select2-container--mailerlite .select2-selection--multiple { 62 62 background-color: white; 63 63 border: 1px solid #d1d5db; … … 67 67 padding-right: 5px; 68 68 position: relative; } 69 .select2-container-- default.select2-container--focus .select2-selection--multiple {69 .select2-container--mailerlite.select2-container--focus .select2-selection--multiple { 70 70 border-color: #d1d5db; 71 71 } … … 78 78 } 79 79 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice { 80 background-color: #e4e4e4; 81 border: 1px solid #d1d5db; 82 border-radius: 4px; 80 display: inline-flex; 81 align-items: center; 82 background-color: #e5e7eb; 83 border: 0; 84 border-radius: 2px; 83 85 box-sizing: border-box; 84 display: inline-block;86 font-size: 13px; 85 87 margin-left: 5px; 86 88 margin-top: 5px; 87 padding: 0;88 padding-left: 20px;89 position: relative;90 89 max-width: 100%; 91 90 overflow: hidden; 91 padding: 2px 5px; 92 position: relative; 93 vertical-align: bottom; 94 white-space: nowrap; 95 } 96 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__display { 97 color: rgba(0,0,0,0.75); 98 cursor: default; 99 min-width: 0; 100 order: 0; 101 overflow: hidden; 102 padding-right: 4px; 92 103 text-overflow: ellipsis; 93 vertical-align: bottom;94 white-space: nowrap; }95 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__display {96 cursor: default;97 color: rgba(0,0,0,0.75);98 padding-left: 2px;99 padding-right: 5px;100 104 } 101 105 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove { 102 106 background-color: transparent; 103 107 border: none; 104 border-right: 1px solid #aaa;105 border-top-left-radius: 4px;106 border-bottom-left-radius: 4px;107 108 color: #999; 108 109 cursor: pointer; 109 font-size: 1em; 110 font-weight: bold; 111 padding: 0 4px; 112 position: absolute; 113 left: 0; 114 top: 0; } 115 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:hover, .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:focus { 116 background-color: #f1f1f1; 117 color: #333; 118 outline: none; } 110 flex-shrink: 0; 111 font-size: 0.875rem; 112 font-weight: normal; 113 line-height: 1; 114 order: 1; 115 padding: 0 2px; 116 position: static; 117 } 118 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:hover, 119 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:focus { 120 color: #555; 121 background-color: transparent; 122 outline: none; 123 } 119 124 120 125 .select2-container--mailerlite[dir="rtl"] .select2-selection--multiple .select2-selection__choice { … … 150 155 display: none; } 151 156 152 .select2-container--mailerlite.select2-container--open.select2-container--above .select2-selection--single, .select2-container--mailerlite.select2-container--open.select2-container--above .select2-selection--multiple { 157 .select2-container--mailerlite.select2-container--open.select2-container--above .select2-selection--single, 158 .select2-container--mailerlite.select2-container--open.select2-container--above .select2-selection--multiple { 153 159 border-top-left-radius: 0; 154 160 border-top-right-radius: 0; } 155 161 156 .select2-container--mailerlite.select2-container--open.select2-container--below .select2-selection--single, .select2-container--mailerlite.select2-container--open.select2-container--below .select2-selection--multiple { 162 .select2-container--mailerlite.select2-container--open.select2-container--below .select2-selection--single, 163 .select2-container--mailerlite.select2-container--open.select2-container--below .select2-selection--multiple { 157 164 border-bottom-left-radius: 0; 158 165 border-bottom-right-radius: 0; } … … 212 219 padding: 6px; } 213 220 214 .select2- results__option {221 .select2-container--mailerlite .select2-results__option { 215 222 padding-right: 20px; 216 223 vertical-align: middle; 217 224 } 218 225 219 .select2- results__option:before {226 .select2-container--mailerlite .select2-results__option:before { 220 227 content: ""; 221 228 display: inline-block; … … 251 258 border-width: 2px; 252 259 } 253 .select2-container-- open .select2-dropdown--below {260 .select2-container--mailerlite.select2-container--open .select2-dropdown--below { 254 261 border-color: transparent; 255 262 border-radius: 6px; … … 258 265 } 259 266 260 .select2-selection .select2-selection--multiple:after { 261 content: 'hhghgh'; 262 } 263 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice { 264 background-color: #e5e7eb; 265 border: 0; 266 border-radius: 2px; 267 font-size: 13px; 268 margin-bottom: 0; 269 padding-top: 2px; 270 padding-bottom: 2px; 271 padding-left: 5px; 272 padding-right: 14px; 273 } 274 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove { 275 border-right: 0; 276 color: rgba(0,0,0,0.75); 277 font-weight: normal; 278 font-size: 1rem; 279 padding-right: 5px; 280 right: 0; 281 left: auto; 282 } 283 .select2-container--mailerlite .select2-selection--multiple .select2-selection__choice__remove:hover { 284 color: #9b9b9b; 285 background-color: #e5e7eb; 286 } 287 .select2-container--mailerlite .select2-selection--multiple, .select2-container--default.select2-container--focus .select2-selection--multiple { 267 .select2-container--mailerlite .select2-selection--multiple, 268 .select2-container--mailerlite.select2-container--focus .select2-selection--multiple { 288 269 border-width: 1px !important; 289 270 border-color: #d1d5db !important; 290 271 } 291 .select2- results__option.select2-results__option--selected:before {272 .select2-container--mailerlite .select2-results__option.select2-results__option--selected:before { 292 273 color: #fff; 293 274 background-color: #09c269; 294 275 border: 0; 295 276 display: inline-block; 296 content: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C /svg%3E");277 content: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C%2Fsvg%3E"); 297 278 padding-left: 0; 298 279 width: 14px; 299 280 height: 14px; 300 281 } 301 .select2- results__option.select2-results__message:before {282 .select2-container--mailerlite .select2-results__option.select2-results__message:before { 302 283 content: none; 303 284 } … … 312 293 position: absolute; 313 294 } 314 .wc-wp-version-gte-53 .select2-container.select2-container--open .select2-selection--multiple { 295 .woo-ml-native-select { 296 border: 1px solid #d1d5db; 297 border-radius: 0.25rem; 298 height: 38px; 299 width: 100%; 300 padding: 0 12px; 301 font-size: 14px; 302 color: rgba(0,0,0,0.75); 303 background-color: #fff; 304 appearance: none; 305 background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E"); 306 background-repeat: no-repeat; 307 background-position: right 12px center; 308 background-size: 14px 14px; 309 cursor: pointer; 310 } 311 .woo-ml-native-select:focus { 312 outline: none; 313 border-color: #000; 314 } 315 .woo-ml-native-select:disabled { 316 background-color: #eee; 317 cursor: default; 318 } 319 320 .wc-wp-version-gte-53 .select2-container--mailerlite.select2-container--open .select2-selection--multiple { 315 321 border-width: 1px; 316 322 border-color: #d1d5db; -
woo-mailerlite/trunk/woo-mailerlite.php
r3484689 r3491060 16 16 * Plugin URI: https://mailerlite.com 17 17 * Description: Official MailerLite integration for WooCommerce. Track sales and campaign ROI, import products details, automate emails based on purchases and seamlessly add your customers to your email marketing lists via WooCommerce's checkout process. 18 * Version: 3.1.1 218 * Version: 3.1.13 19 19 * Author: MailerLite 20 20 * Author URI: https://mailerlite.com … … 40 40 * Update when you release new versions. 41 41 */ 42 define( 'WOO_MAILERLITE_VERSION', '3.1.1 2' );42 define( 'WOO_MAILERLITE_VERSION', '3.1.13' ); 43 43 44 44 define('WOO_MAILERLITE_ASYNC_JOBS', false);
Note: See TracChangeset
for help on using the changeset viewer.