Changeset 3377455
- Timestamp:
- 10/13/2025 11:05:39 AM (6 months ago)
- Location:
- woo-mailerlite/trunk
- Files:
-
- 30 edited
-
admin/WooMailerLiteAdmin.php (modified) (2 diffs)
-
admin/assets/js/components/App.js (modified) (4 diffs)
-
admin/assets/js/components/Views/Settings.js (modified) (2 diffs)
-
admin/controllers/WooMailerLiteAdminSettingsController.php (modified) (4 diffs)
-
admin/controllers/WooMailerLiteAdminSyncController.php (modified) (3 diffs)
-
admin/controllers/WooMailerLiteAdminWizardController.php (modified) (4 diffs)
-
includes/WooMailerLite.php (modified) (2 diffs)
-
includes/WooMailerLiteService.php (modified) (2 diffs)
-
includes/WooMailerLiteSession.php (modified) (1 diff)
-
includes/api/WooMailerLiteClassicApi.php (modified) (2 diffs)
-
includes/api/WooMailerLiteRewriteApi.php (modified) (1 diff)
-
includes/common/WooMailerLiteDBConnection.php (modified) (4 diffs)
-
includes/common/WooMailerLiteQueryBuilder.php (modified) (8 diffs)
-
includes/common/traits/WooMailerLiteResources.php (modified) (6 diffs)
-
includes/controllers/WooMailerLiteController.php (modified) (1 diff)
-
includes/controllers/WooMailerLiteOrderController.php (modified) (6 diffs)
-
includes/controllers/WooMailerLitePluginController.php (modified) (1 diff)
-
includes/jobs/WooMailerLiteAbstractJob.php (modified) (5 diffs)
-
includes/jobs/WooMailerLiteCategorySyncJob.php (modified) (3 diffs)
-
includes/jobs/WooMailerLiteCustomerSyncJob.php (modified) (1 diff)
-
includes/jobs/WooMailerLiteProductSyncJob.php (modified) (3 diffs)
-
includes/models/WooMailerLiteCategory.php (modified) (2 diffs)
-
includes/models/WooMailerLiteCustomer.php (modified) (5 diffs)
-
includes/models/WooMailerLiteModel.php (modified) (1 diff)
-
includes/models/WooMailerLiteProduct.php (modified) (2 diffs)
-
includes/services/WooMailerLiteCheckoutDataService.php (modified) (2 diffs)
-
public/WooMailerLitePublic.php (modified) (1 diff)
-
public/js/woo-mailerlite-public.js (modified) (9 diffs)
-
readme.txt (modified) (2 diffs)
-
woo-mailerlite.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
woo-mailerlite/trunk/admin/WooMailerLiteAdmin.php
r3361673 r3377455 48 48 public function wooMailerLiteSettingsPageCallback() 49 49 { 50 $falseApi = false;51 if (!WooMailerLiteCache::get('valid_api')) {52 $response = WooMailerLiteApi::client()->ping();53 if ($response->status === 401) {54 $falseApi = true;55 // WooMailerLiteOptions::deleteAll();56 } else {57 WooMailerLiteCache::set('valid_api', true, 86400);58 }59 }60 $untrackedResources = WooMailerLite Category::getUntrackedCategoriesCount() + WooMailerLiteProduct::getUntrackedProductsCount() + WooMailerLiteCustomer::getAll()->count();50 $falseApi = false; 51 // if (!WooMailerLiteCache::get('valid_api')) { 52 // $response = WooMailerLiteApi::client()->ping(); 53 // if ($response->status === 401) { 54 // $falseApi = true; 55 //// WooMailerLiteOptions::deleteAll(); 56 // } else { 57 // WooMailerLiteCache::set('valid_api', true, 86400); 58 // } 59 // } 60 $untrackedResources = WooMailerLiteProduct::getUntrackedProductsCount() + WooMailerLiteCategory::getUntrackedCategoriesCount() + WooMailerLiteCustomer::getUntrackedCustomersCount(); 61 61 62 62 wp_localize_script('woo-mailerlite-vue-cdn', 'woo_mailerlite_admin_data', [ … … 81 81 'settings' => WooMailerLiteOptions::get('settings', []), 82 82 'syncFields' => WooMailerLiteOptions::get('syncFields'), 83 'asyncSync' => class_exists('ActionScheduler'), 83 // Need better solution for async sync, adding true for now and force sync (works) 84 'asyncSync' => WooMailerLiteCache::get('scheduled_jobs') && $untrackedResources, 84 85 'falseApi' => $falseApi, 85 86 'debugMode' => WooMailerLiteOptions::get('debugMode') -
woo-mailerlite/trunk/admin/assets/js/components/App.js
r3338878 r3377455 32 32 selectedFields: [], 33 33 startSync: false, 34 manualSync: false, 34 35 } 35 36 }, … … 43 44 body: new URLSearchParams({ 44 45 action: 'woo_mailerlite_sync_handler', 45 nonce: woo_mailerlite_admin_data.woo_mailerlite_admin_nonce 46 nonce: woo_mailerlite_admin_data.woo_mailerlite_admin_nonce, 47 async: !this.manualSync 46 48 }), 47 49 }); 48 50 49 if ( response.status === 200) {51 if ([200, 203].includes(response.status)) { 50 52 eventBus.emit('sync-completed', true); 51 53 clearInterval(this.interval); … … 57 59 window.location.reload() 58 60 } 59 if ( this.startSync || ((parseInt(woo_mailerlite_admin_data.currentStep) === 2) && (woo_mailerlite_admin_data.asyncSync && woo_mailerlite_admin_data.sync.syncInProgress))) {61 if (woo_mailerlite_admin_data.asyncSync || this.startSync || ((parseInt(woo_mailerlite_admin_data.currentStep) === 2) && woo_mailerlite_admin_data.sync.syncInProgress)) { 60 62 this.syncProcess(); 63 if (!woo_mailerlite_admin_data.asyncSync) { 64 this.interval = setInterval(() => { 65 woo_mailerlite_admin_data.sync.syncInProgress = true 66 this.syncProcess(); 67 }, 8000); 68 } 61 69 } 62 70 … … 66 74 }); 67 75 eventBus.on('start-sync', (data) => { 76 this.manualSync = true; 68 77 this.syncProcess(); 69 78 this.interval = setInterval(() => { -
woo-mailerlite/trunk/admin/assets/js/components/Views/Settings.js
r3338878 r3377455 6 6 7 7 const template = ` 8 <div v-if="syncInProgress " class="woo-ml-sync-loading-container">8 <div v-if="syncInProgress || asyncSyncInProgress" class="woo-ml-sync-loading-container"> 9 9 <strong>Sync in progress...</strong> 10 10 <p>This will continue as long as you stay on this page. … … 398 398 isLoading: false, 399 399 selectedSyncFields: [], 400 asyncSyncInProgress: woo_mailerlite_admin_data.asyncSync, 400 401 } 401 402 }, -
woo-mailerlite/trunk/admin/controllers/WooMailerLiteAdminSettingsController.php
r3346776 r3377455 42 42 } else { 43 43 $product->exclude_from_automations = $product->ignored; 44 $this->apiClient()->syncProduct(WooMailerLiteOptions::get('shopId'), $product->toArray() );44 $this->apiClient()->syncProduct(WooMailerLiteOptions::get('shopId'), $product->toArray(), true); 45 45 } 46 46 return true; … … 77 77 $product->exclude_from_automations = $product->ignored ? 1 : 0; 78 78 $product->categories = $product->category_ids; 79 $response = $this->apiClient()->syncProduct($shopId, $product->toArray() );79 $response = $this->apiClient()->syncProduct($shopId, $product->toArray(), true); 80 80 if ($response->success) { 81 81 $product->tracked = true; … … 107 107 } else { 108 108 $product->exclude_from_automations = $product->ignored; 109 $this->apiClient()->syncProduct(WooMailerLiteOptions::get('shopId'), $product->toArray() );109 $this->apiClient()->syncProduct(WooMailerLiteOptions::get('shopId'), $product->toArray(), true); 110 110 } 111 111 } … … 148 148 149 149 $currentStatus = $this->apiClient()->getDoubleOptin(); 150 $currentStatus->data->double_optin = $currentStatus->data->double_optin ?? $currentStatus->data->enabled ;150 $currentStatus->data->double_optin = $currentStatus->data->double_optin ?? $currentStatus->data->enabled ?? false; 151 151 152 152 if ($this->request('settings.doubleOptIn')) { -
woo-mailerlite/trunk/admin/controllers/WooMailerLiteAdminSyncController.php
r3338878 r3377455 10 10 WooMailerLiteCache::set('manual_sync', true, 18000); 11 11 } 12 $totalUntrackedResources = WooMailerLiteCategory::getUntrackedCategoriesCount() + WooMailerLiteProduct::getUntrackedProductsCount() + WooMailerLiteCustomer::getAll()->count(); 12 13 // save count in cache 14 $untrackedCategories = WooMailerLiteCategory::getUntrackedCategoriesCount(); 15 $untrackedProducts = WooMailerLiteProduct::getUntrackedProductsCount(); 16 $untrackedCustomers = WooMailerLiteCustomer::getUntrackedCustomersCount(); 17 WooMailerLiteCache::set('resource_sync_counts', [ 18 'categories' => $untrackedCategories, 19 'products' => $untrackedProducts, 20 'customers' => $untrackedCustomers, 21 ], 18000); 22 $totalUntrackedResources = $untrackedCategories + $untrackedProducts + $untrackedCustomers; 13 23 if ($totalUntrackedResources == 0) { 14 24 WooMailerLiteCache::delete('manual_sync'); … … 16 26 } 17 27 18 if ($alreadyStarted) { 19 WooMailerLiteProductSyncJob::dispatch(); 20 } else { 21 WooMailerLiteProductSyncJob::dispatchSync(); 22 } 28 WooMailerLiteCategorySyncJob::dispatch(); 23 29 24 return $this->response('sync started', 202); 30 return $this->response([ 31 'message' => 'Sync in progress', 32 'data' => [ 33 'totalUntrackedResources' => $totalUntrackedResources 34 ] 35 ], function_exists('as_enqueue_async_action') ? 203 : 202); 25 36 } 26 37 … … 31 42 WooMailerLiteProductSyncResetJob::dispatchSync(); 32 43 return $this->response(['message' => 'reset sync completed', 'data' => [ 33 'totalUntrackedResources' => WooMailerLiteCategory::getUntrackedCategoriesCount() + WooMailerLiteProduct::getUntrackedProductsCount() + WooMailerLiteCustomer::get All()->count()44 'totalUntrackedResources' => WooMailerLiteCategory::getUntrackedCategoriesCount() + WooMailerLiteProduct::getUntrackedProductsCount() + WooMailerLiteCustomer::getUntrackedCustomersCount() 34 45 ]], 202); 35 46 } -
woo-mailerlite/trunk/admin/controllers/WooMailerLiteAdminWizardController.php
r3361673 r3377455 43 43 ]; 44 44 45 if ($this->apiClient()->isRewrite()) { 46 if ($this->requestHas('page') && ($this->request['page'] !== '1')) { 47 $params['offset'] = ($this->request['page'] - 1) * $params['limit']; 48 } 45 if ($this->requestHas('page') && ($this->request['page'] !== '1')) { 46 $params['offset'] = ($this->request['page'] - 1) * $params['limit']; 49 47 } 50 48 … … 60 58 $groups = []; 61 59 if ($response->success) { 62 if (isset($response->data) ) {60 if (isset($response->data) && is_array($response->data)) { 63 61 foreach ($response->data as $group) { 64 62 $groups['data'][] = [ … … 72 70 'next' => (bool)$response->links->next 73 71 ]; 74 } elseif ($this->apiClient()->isClassic() && ( count($groups['data']) > 0)) {72 } elseif ($this->apiClient()->isClassic() && (isset($groups['data']) && count($groups['data']) > 0)) { 75 73 $groups['pagination'] = [ 76 'next' => WooMailerLiteApi::CLASSIC_API74 'next' => !empty($groups) 77 75 ]; 78 76 } … … 157 155 'syncFields' => $this->validated['syncFields'], 158 156 'enabled' => true, 159 'consumerKey' => $this->validated['consumerKey'] ,160 'consumerSecret' => $this->validated['consumerSecret'] ,157 'consumerKey' => $this->validated['consumerKey'] ?? null, 158 'consumerSecret' => $this->validated['consumerSecret'] ?? null, 161 159 ]); 162 160 if (!$response->data->group) { -
woo-mailerlite/trunk/includes/WooMailerLite.php
r3361673 r3377455 110 110 111 111 112 function on_product_category_saved($term_id, $tt_id) {113 $term = get_term($term_id, 'product_cat');114 115 // Your logic here116 error_log("Category saved: {$term->name} (ID: $term_id)");117 }118 112 $this->loader->add_filter('woocommerce_product_data_tabs', $pluginAdmin, 'woo_ml_product_data_tab'); 119 113 $this->loader->add_filter('woocommerce_product_data_store_cpt_get_products_query', $pluginAdmin, 'handleCustomProductQuery', 10, 2); … … 327 321 if (get_option('woo_ml_key', false) && (get_option('woo_ml_wizard_setup', 0) == 2)) { 328 322 $settings = get_option('woocommerce_mailerlite_settings', []); 323 if (!(get_option('woo_ml_key') == WooMailerLiteOptions::get('apiKey'))) { 324 return false; 325 } 329 326 if (!empty($settings)) { 330 327 WooMailerLiteOptions::updateMultiple([ -
woo-mailerlite/trunk/includes/WooMailerLiteService.php
r3340793 r3377455 26 26 /** 27 27 * Triggered when the cart is created/updated 28 * @return void28 * @return true 29 29 */ 30 30 public function handleCartUpdated() … … 54 54 ]); 55 55 } 56 return true; 56 57 } 57 58 -
woo-mailerlite/trunk/includes/WooMailerLiteSession.php
r3338878 r3377455 35 35 public static function getMLCartHash() 36 36 { 37 return WC()->session->get('woo_mailerlite_cart_hash'); 37 if (WC()->session) { 38 return WC()->session->get('woo_mailerlite_cart_hash'); 39 } 40 return null; 38 41 } 39 42 -
woo-mailerlite/trunk/includes/api/WooMailerLiteClassicApi.php
r3348600 r3377455 70 70 } 71 71 72 public function syncCustomers($data) 73 { 74 return $this->post('/woocommerce/sync_customer', $data); 75 } 76 77 public function importCategories() 78 { 79 return $this->successResponse(); 80 } 81 82 public function importProducts() 83 { 84 return $this->successResponse(); 85 } 86 72 87 public function createField($params) 73 88 { … … 116 131 return $this->get('/subscribers/' . $email); 117 132 } 118 119 public function syncCustomers($data)120 {121 return $this->successResponse();122 }123 124 public function importCategories()125 {126 return $this->successResponse();127 }128 129 public function importProducts()130 {131 return $this->successResponse();132 }133 133 } -
woo-mailerlite/trunk/includes/api/WooMailerLiteRewriteApi.php
r3338878 r3377455 57 57 } 58 58 59 public function syncProduct($shopId, $data )59 public function syncProduct($shopId, $data, $replaceCategories = false) 60 60 { 61 return$this->post('/ecommerce/shops/' . $shopId . '/products?with_resource_id',61 $response = $this->post('/ecommerce/shops/' . $shopId . '/products?with_resource_id', 62 62 $data); 63 if ($replaceCategories && isset($data['resource_id']) && isset($data['category_ids'])) { 64 $response = $this->put('/ecommerce/shops/' . $shopId . '/products/' . $data['resource_id'] . '/categories/multiple?with_resource_id', 65 [ 66 'replace' => true, 67 'categories' => $data['category_ids'] 68 ]); 69 } 70 return $response; 63 71 } 64 72 -
woo-mailerlite/trunk/includes/common/WooMailerLiteDBConnection.php
r3338878 r3377455 43 43 protected $hasWhere = false; 44 44 45 protected $countOnly = false; 46 47 48 private $columnsOnly = false; 49 45 50 /** 46 51 * @var string|null $table … … 63 68 $result = dbDelta($this->query); 64 69 } else { 65 $result = $this->db()->get_results($this->query); 70 if ($this->columnsOnly) { 71 $result = $this->db()->get_col($this->query); 72 } else { 73 $result = $this->db()->get_results($this->query); 74 } 66 75 } 67 76 return $result; … … 109 118 public function count() 110 119 { 120 $this->countOnly = true; 111 121 $data = $this->get(); 122 $this->countOnly = false; 112 123 if ($data instanceof WooMailerLiteCollection) { 113 124 return $data->count(); 114 125 } else { 115 return is_array($data) ? count($data) : 0;126 return is_array($data) ? count($data) : (is_int($data) ? $data : 0); 116 127 } 117 128 } … … 127 138 return static::db_connection_instance(); 128 139 } 140 141 public function columnsOnly() 142 { 143 $this->columnsOnly = true; 144 return $this; 145 } 129 146 } -
woo-mailerlite/trunk/includes/common/WooMailerLiteQueryBuilder.php
r3348600 r3377455 8 8 private $select = "*"; 9 9 10 protected $andWhere = false; 11 12 protected $withoutPrefix = false; 13 10 14 public function __construct($model) 11 15 { … … 38 42 public function get(int $count = -1) 39 43 { 44 if ($count == -1 && (get_class($this->model) === 'WooMailerLiteCustomer')) { 45 return $this->buildQuery($count)->executeQuery(); 46 } 40 47 if ($this->model->isResource() || ((get_class($this->model) === 'WooMailerLiteCustomer') && !$this->customTableEnabled())) { 41 48 … … 46 53 47 54 $data = $this->buildQuery($count)->executeQuery(); 55 if ($this->countOnly && (get_class($this->model) === 'WooMailerLiteProduct')) { 56 return $data; 57 } 48 58 foreach ($data as $item) { 49 59 if ((get_class($this->model) === 'WooMailerLiteCustomer') && empty($item->email)) { 50 60 continue; 51 61 } 62 if (get_class($this->model) === 'WooMailerLiteProduct' && !$this->model->isResource()) { 63 $item = wc_get_product($item); 64 if (!$item) { 65 continue; 66 } 67 $itemData = $item->get_data(); 68 $this->prepareResourceData(get_class($this->model), $itemData, $item->last_order_id ?? $item); 69 $item = $itemData; 70 } 52 71 $model = new $this->model(); 53 72 … … 62 81 if (!empty($this->model->getFormatArray())) { 63 82 foreach ($this->model->getFormatArray() as $key => $format) { 83 if (!isset($model->attributes[$key])) { 84 continue; 85 } 64 86 switch ($format) { 65 87 case 'array': 66 $model->attributes[$key] = json_decode($model->attributes[$key], true); 88 if (is_string($model->attributes[$key])){ 89 $model->attributes[$key] = json_decode($model->attributes[$key], true); 90 } 67 91 break; 68 92 case 'boolean': 69 93 $model->attributes[$key] = (bool) $model->attributes[$key]; 70 94 break; 71 } 72 } 73 } 95 case 'string': 96 $model->attributes[$key] = (string) $model->attributes[$key]; 97 break; 98 } 99 } 100 } 101 if (!empty($this->model->getRemoveEmptyArray())) { 102 foreach ($this->model->getRemoveEmptyArray() as $key) { 103 if (isset($model->attributes[$key])) { 104 if (empty($model->attributes[$key]) || (is_string($model->attributes[$key]) && ctype_space($model->attributes[$key]))) { 105 unset($model->attributes[$key]); 106 } 107 } 108 } 109 } 110 74 111 $collection->collect($model); 75 112 } … … 121 158 } 122 159 123 public function join(string $table, string $tableLeft, string $tableRight) 124 { 160 public function join($table, $tableLeft = null, $tableRight = null, $alias = null) 161 { 162 if ($table instanceof WooMailerLiteQueryBuilder) { 163 $alias = $this->db()->prefix . ($alias ?? 'subquery'); 164 $this->query .= " INNER JOIN (" . rtrim($table->buildQuery()->query, ';') . ") AS " . ($alias ?? 'subquery') . " ON " . $this->db()->prefix . $tableLeft . " = " . $this->db()->prefix . $tableRight; 165 return $this; 166 } 167 168 if (!$tableRight && is_array($tableLeft)) { 169 $operator = null; 170 foreach ($tableLeft as $key => $value) { 171 if (is_string($value)) { 172 if (!strpos($value, '.')) { 173 $value = "'{$value}'"; 174 } else { 175 $value = $this->db()->prefix . $value; 176 } 177 } 178 179 if (is_array($value) && is_string(array_keys($value)[0])) { 180 $operator = array_keys($value)[0]; 181 $findin = "'" . implode("','", $value[$operator]) . "'"; 182 $value = " {$operator} ({$findin})"; 183 } 184 $joins[] = $this->db()->prefix . $key . ($operator ? '' : ' = ') . $value; 185 } 186 $this->query .= " INNER JOIN " . $this->db()->prefix . $table . " ON " . implode(' AND ', $joins); 187 return $this; 188 } 125 189 $table = $this->db()->prefix . $table; 126 190 $tableLeft = $this->db()->prefix . $tableLeft; … … 130 194 } 131 195 196 public function from($table) 197 { 198 $this->model->setTable($table); 199 return $this; 200 } 201 202 public function leftJoin(string $table, $tableLeft, string $tableRight = '') 203 { 204 if (!$tableRight && is_array($tableLeft)) { 205 // this condition is for join on key = value and another key = value 206 $joins = []; 207 foreach ($tableLeft as $key => $value) { 208 if (!strpos($value, '.')) { 209 $value = "'{$value}'"; 210 } else { 211 $value = $this->db()->prefix . $value; 212 } 213 $joins[] = $this->db()->prefix . $key . ' = ' . $value; 214 } 215 $this->query .= " LEFT JOIN " . $this->db()->prefix . $table . " ON " . implode(' AND ', $joins); 216 return $this; 217 } 218 $table = $this->db()->prefix . $table; 219 $tableLeft = $this->db()->prefix . $tableLeft; 220 $tableRight = $this->db()->prefix . $tableRight; 221 $this->query .= " LEFT JOIN {$table} ON {$tableLeft} = {$tableRight}"; 222 return $this; 223 } 224 132 225 public function andWhere($column, $operation, $value) 133 226 { … … 136 229 $operation = '='; 137 230 } 231 if ($this->andWhere) { 232 $this->andWhere = false; 233 $this->query .= " {$column} {$operation} '{$value}'"; 234 return $this; 235 } 138 236 $this->query .= " AND {$column} {$operation} '{$value}'"; 237 return $this; 238 } 239 240 public function orWhere($column, $operation = '=', $value = null) 241 { 242 $prefix = $this->db()->prefix; 243 if ($value === null) { 244 $value = $operation; 245 $operation = '='; 246 } 247 if ($value === null) { 248 $value = 'IS NULL'; 249 $operation = ''; 250 } else { 251 $value = "'{$value}'"; 252 } 253 if (!$this->withoutPrefix) { 254 $column = $prefix . $column; 255 } 256 $this->query .= " OR {$column} {$operation} {$value}"; 257 return $this; 258 } 259 260 public function withoutPrefix($callback) 261 { 262 $this->withoutPrefix = true; 263 $callback($this); 264 $this->withoutPrefix = false; 265 return $this; 266 } 267 268 public function andCombine($callback) 269 { 270 $this->andWhere = true; 271 $this->query .= ' AND ('; 272 $callback($this); 273 $this->query .= ')'; 139 274 return $this; 140 275 } … … 185 320 $exists = $this->where(array_key_first($where), $where[array_key_first($where)])->first(); 186 321 if ($exists) { 322 return $exists; 323 } 324 $this->create(array_merge($where, $data)); 325 $this->query = ''; 326 $this->hasWhere = false; 327 return $this->where(array_key_first($where), $where[array_key_first($where)])->first(); 328 } 329 330 public function updateOrCreate($where, $data) 331 { 332 $exists = $this->where(array_key_first($where), $where[array_key_first($where)])->first(); 333 if ($exists) { 334 $this->query = ''; 335 $this->hasWhere = false; 336 $this->model = $exists; 337 $this->update($data); 187 338 return $exists; 188 339 } -
woo-mailerlite/trunk/includes/common/traits/WooMailerLiteResources.php
r3361673 r3377455 41 41 /** 42 42 * Get all resources 43 * @return void|WooMailerLiteCollection 43 * @return void|WooMailerLiteCollection|int 44 44 */ 45 45 public function resource_all($count = 0) … … 57 57 ], $this->args); 58 58 59 if ($this->args['limit'] == -1) { 60 $this->args = array_merge($this->args, [ 61 'paginate' => true, 62 'return' => 'ids', 63 'status' => 'publish', 64 ]); 65 return (int) (new WC_Product_Query($this->args))->get_products()->total; 66 } 59 67 $items = (new WC_Product_Query($this->args))->get_products(); 60 68 break; … … 186 194 { 187 195 $this->args = array_merge($this->args, $args); 196 $prefix = $this->db()->prefix; 197 $this->select = "{$prefix}posts.ID"; 198 if (isset($args['meta_query'][0]['key']) && ($args['meta_query'][0]['key'] !== '_woo_ml_category_tracked')) { 199 return $this; 200 } 201 if (isset($args['metaQuery'][0]['value']) && ($args['metaQuery'][0]['value'] === true) && ($args['metaQuery'][0]['key'] === '_woo_ml_product_tracked')) { 202 $this->leftJoin('postmeta', [ 203 'posts.id' => "postmeta.post_id", 204 'postmeta.meta_key' => '_woo_ml_product_tracked' 205 ]) 206 ->where('posts.post_type', 'product') 207 ->where('posts.post_status', 'publish') 208 ->andCombine(function($query) { 209 $query->where('postmeta.meta_value', '1') 210 ->orWhere('postmeta.meta_value', true); 211 }) 212 ->columnsOnly(); 213 return $this; 214 } 215 $this->leftJoin('postmeta', [ 216 'posts.id' => "postmeta.post_id", 217 'postmeta.meta_key' => '_woo_ml_product_tracked' 218 ]) 219 ->where('posts.post_type', 'product') 220 ->where('posts.post_status', 'publish') 221 ->andCombine(function($query) { 222 $query->where('postmeta.meta_value', '0') 223 ->orWhere('postmeta.meta_value', null) 224 ->orWhere('postmeta.meta_value', false); 225 }) 226 ->columnsOnly(); 188 227 return $this; 189 228 } … … 193 232 switch ($resource) { 194 233 case 'WooMailerLiteProduct': 195 $data['resource_id'] = (string) $item->get_id(); 234 $variableProduct = $item->is_type('variation') ?? false; 235 $data['resource_id'] = (string) ($variableProduct ? $item->get_parent_id() : $item->get_id()); 196 236 $data['url'] = get_permalink($item->get_id()); 237 $data['name'] = $item->get_name(); 238 $data['price'] = floatval($item->get_price()); 239 $data['url'] = get_permalink( $item->get_id() ); 240 $data['category_ids'] = $item->get_category_ids(); 197 241 $data['image'] = (string) wp_get_attachment_image_url($item->get_image_id(), 'full'); 242 $data['description'] = $item->get_description(); 243 $data['short_description'] = $item->get_short_description(); 198 244 $data['ignored'] = get_post_meta($data['resource_id'], '_woo_ml_product_ignored', true); 199 245 $data['tracked'] = get_post_meta($data['resource_id'], '_woo_ml_product_tracked', true); … … 201 247 case 'WooMailerLiteCategory': 202 248 $data['resource_id'] = (string) $item->term_id; 249 $data['name'] = $item->name; 203 250 $data['tracked'] = get_term_meta($item->term_id, '_woo_ml_category_tracked', true); 204 251 break; … … 252 299 $this->customerResourceCount[$email] = [ 253 300 'customer_id' => $data['customer_id'] ?? $this->counter, 254 'resource_id' => $ item->get_customer_id(),301 'resource_id' => $data['customer_id'] ?? $this->counter, 255 302 'email' => $item->get_billing_email(), 256 303 'name' => $item->get_billing_first_name(), -
woo-mailerlite/trunk/includes/controllers/WooMailerLiteController.php
r3338878 r3377455 36 36 $keysToUnset = []; 37 37 38 foreach ($validations as $key => $validation) { 38 if (is_array($validations)) { 39 foreach ($validations as $key => $validation) { 39 40 40 if (is_array($validation)) { 41 if ($skipSometimes === $key) { 42 continue; 41 if (is_array($validation)) { 42 if ($skipSometimes === $key) { 43 continue; 44 } 45 46 if (strpos($key, '.') !== false) { 47 $needle = explode('.', $key); 48 if (!is_array($this->request[$needle[0]])) { 49 $this->sanitizeRequestKey($needle[0]); 50 } 51 52 if (isset($this->request[$needle[0]][$needle[1]])) { 53 $this->request[$key] = $this->request[$needle[0]][$needle[1]]; 54 $keysToUnset[] = $key; 55 } 56 } 57 58 if (in_array('required', $validation) && empty($this->request[$key])) { 59 throw new Exception("The $key field is required."); 60 } 61 62 if (in_array('sometimes', $validation)) { 63 $skipSometimes = $key; 64 if (isset($this->request[$key]) && empty($this->request[$key])) { 65 $this->validated[$key] = $this->request[$key]; 66 } 67 continue; 68 } 69 70 if (in_array('string', $validation) && !is_string($this->request[$key])) { 71 throw new Exception("The $key field must be a string."); 72 } 73 74 if (in_array('int', $validation) && !ctype_digit(strval($this->request[$key]))) { 75 throw new Exception("The $key field must be an integer."); 76 } 77 78 if (in_array('bool', $validation) && !is_bool(filter_var($this->request[$key], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE))) { 79 throw new Exception("The $key field must be a boolean."); 80 } 81 } 82 if ($validation === 'nonce') { 83 check_ajax_referer('woo_mailerlite_admin', 'nonce'); 43 84 } 44 85 45 if (strpos($key, '.') !== false) { 46 $needle = explode('.', $key); 47 if (!is_array($this->request[$needle[0]])) { 48 $this->sanitizeRequestKey($needle[0]); 49 } 50 51 if (isset($this->request[$needle[0]][$needle[1]])) { 52 $this->request[$key] = $this->request[$needle[0]][$needle[1]]; 53 $keysToUnset[] = $key; 54 } 86 if (isset($this->request[$key])) { 87 $this->validated[$key] = $this->request[$key]; 55 88 } 56 57 if (in_array('required', $validation) && empty($this->request[$key])) {58 throw new Exception("The $key field is required.");59 }60 61 if (in_array('sometimes', $validation)) {62 $skipSometimes = $key;63 $this->validated[$key] = $this->request[$key];64 continue;65 }66 67 if (in_array('string', $validation) && !is_string($this->request[$key])) {68 throw new Exception("The $key field must be a string.");69 }70 71 if (in_array('int', $validation) && !ctype_digit(strval($this->request[$key]))) {72 throw new Exception("The $key field must be an integer.");73 }74 75 if (in_array('bool', $validation) && !is_bool(filter_var($this->request[$key], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE))) {76 throw new Exception("The $key field must be a boolean.");77 }78 }79 80 if (isset($this->request[$key])) {81 $this->validated[$key] = $this->request[$key];82 89 } 83 90 } -
woo-mailerlite/trunk/includes/controllers/WooMailerLiteOrderController.php
r3361673 r3377455 7 7 { 8 8 try { 9 9 10 if (WooMailerLiteCache::get('order_sent:'.$orderId)) { 10 11 return true; 11 12 } 12 13 $order = wc_get_order($orderId); 13 14 if (WooMailerLiteSession::getMLCartHash()) { 15 $order->add_meta_data('_woo_ml_cart_hash', WooMailerLiteSession::getMLCartHash()); 16 } 14 17 $customer = WooMailerLiteCustomer::selectAll(false)->where('email', $order->get_billing_email())->first(); 15 18 if (!$customer) { … … 22 25 $cart = WooMailerLiteCart::where('email', $order->get_billing_email())->first(); 23 26 if (!$cart) { 24 $cart = WooMailerLiteCart::where('hash', WooMailerLiteSession::getMLCartHash())->first(); 27 $cart = WooMailerLiteCart::where('hash', WooMailerLiteSession::getMLCartHash()) 28 ->withoutPrefix(function($query) use ($order) { 29 $query->orWhere('hash', $order->get_meta('_woo_ml_cart_hash')); 30 })->first(); 25 31 if ($cart instanceof WooMailerLiteCart) { 26 32 $cart->update([ … … 62 68 $customerFields = array_intersect_key($filteredCustomerData, array_flip($syncFields)); 63 69 $subscribe = false; 64 if ( isset($cart->subscribe)) {70 if ($cart && isset($cart->subscribe)) { 65 71 $subscribe = $cart->subscribe; 66 72 } … … 96 102 ]; 97 103 if ($this->apiClient()->isClassic()) { 104 if (!$cart) { 105 return true; 106 } 98 107 $orderData['order'] = $order->get_data(); 99 108 $orderData['checkout_id'] = $cart->data['checkout_id']; … … 112 121 'shop_url' => home_url(), 113 122 'order_url' => home_url() . "/wp-admin/post.php?post=" . $orderId . "&action=edit", 114 'checkout_data' => WooMailerLiteCheckoutDataService::getCheckoutData( )123 'checkout_data' => WooMailerLiteCheckoutDataService::getCheckoutData($order->get_billing_email()) 115 124 ]; 116 125 $this->apiClient()->sendOrderProcessing($data); … … 140 149 if (in_array($order->get_status(), ['wc-completed', 'wc-processing','completed','processing']) && !empty($cart)) { 141 150 if ($cart instanceof WooMailerLiteCart) { 142 $cart->delete(); 151 if ($this->apiClient()->isRewrite()) { 152 $this->apiClient()->deleteOrder($cart->data['checkout_id']); 153 } 154 $cart->delete(); 143 155 } 144 156 } -
woo-mailerlite/trunk/includes/controllers/WooMailerLitePluginController.php
r3338878 r3377455 79 79 if ($this->requestHas('ml_checkout')) { 80 80 $cart = WooMailerLiteCart::where('data', 'like', '%'.$this->request['ml_checkout'].'%')->first(); 81 if ($cart ->exists()) {81 if ($cart && $cart->exists()) { 82 82 WC()->session->set('cart', $cart->data); 83 83 } -
woo-mailerlite/trunk/includes/jobs/WooMailerLiteAbstractJob.php
r3339434 r3377455 3 3 abstract class WooMailerLiteAbstractJob 4 4 { 5 /**6 * Whether the job runs in serial mode (not used directly here).7 */8 5 private $serial = true; 9 6 10 /**11 * Delay in seconds before executing the job.12 */13 7 protected static $delay = 0; 14 8 15 /**16 * Holds the job record model from the database.17 */18 9 public static $jobModel; 19 10 20 /**21 * Used to skip retry logic if needed.22 */23 11 protected $retryDelay = 10; 24 12 25 /** 26 * Max retry attempts. 27 */ 28 protected $maxRetries = 0; 13 protected $maxRetries = 3; 29 14 30 15 protected $resourceLimit = 100; 31 16 32 /**33 * Each job must implement this method.34 */35 17 abstract public function handle($data = []); 36 18 37 /**38 * Returns a new instance of the job.39 */40 19 public static function getInstance() 41 20 { … … 43 22 } 44 23 45 /**46 * Dispatch the job to Action Scheduler or run it synchronously.47 */48 24 public static function dispatch(array $data = []): void 49 25 { … … 51 27 $objectId = 0; 52 28 53 if ((isset($data['selfMechanism']['sync']) && !$data['selfMechanism']['sync']) && class_exists('ActionScheduler')) { 54 if (!as_has_scheduled_action($jobClass)) { 55 $objectId = as_enqueue_async_action($jobClass, $data); 56 } 29 $data['attempts'] = $data['attempts'] ?? 0; 30 31 if ((!isset($data['selfMechanism']['sync']) || !$data['selfMechanism']['sync']) && function_exists('as_enqueue_async_action')) { 32 $objectId = as_enqueue_async_action($jobClass, [$data]); 33 WooMailerLiteCache::set('scheduled_jobs', true, 300); 57 34 } 58 35 59 static::$jobModel = WooMailerLiteJob:: firstOrCreate(36 static::$jobModel = WooMailerLiteJob::updateOrCreate( 60 37 ['job' => $jobClass], 61 38 ['object_id' => $objectId, 'data' => $data] … … 67 44 } 68 45 69 /**70 * Force synchronous execution.71 */72 46 public static function dispatchSync(array $data = []): void 73 47 { … … 82 56 static::$jobModel = WooMailerLiteJob::where('job', static::class)->first(); 83 57 } 58 84 59 $this->handle($data); 60 85 61 if (static::$jobModel) { 86 62 static::$jobModel->delete(); 87 63 } 64 WooMailerLiteCache::delete('scheduled_jobs'); 88 65 return true; 66 89 67 } catch (Throwable $th) { 90 WooMailerLiteLog()->error("Failed Job " . static::class, [$th->getMessage()]); 68 WooMailerLiteCache::delete('scheduled_jobs'); 69 WooMailerLiteLog()->error("Failed Job: " . static::class, [ 70 'error' => $th->getMessage(), 71 'trace' => $th->getTraceAsString() 72 ]); 91 73 92 // retry mechanism 93 $attempts = static::$jobModel->data['attempts'] ?? 0; 94 static::$jobModel->update([ 95 'data' => [ 96 'status' => 'failed', 97 'error' => $th->getMessage(), 98 'attempts' => $attempts + 1, 99 ] 100 ]); 74 $attempts = $data['attempts'] ?? 0; 75 $attempts++; 76 77 if (static::$jobModel) { 78 static::$jobModel->update([ 79 'data' => array_merge($data, [ 80 'status' => 'failed', 81 'error' => $th->getMessage(), 82 'attempts' => $attempts, 83 ]) 84 ]); 85 } 86 101 87 if ($attempts < $this->maxRetries) { 102 if (!isset($data['selfMechanism']['sync']) && class_exists('ActionScheduler')) { 103 as_enqueue_async_action(static::class, $data); 88 $data['attempts'] = $attempts; 89 90 if (!isset($data['selfMechanism']['sync']) && function_exists('as_enqueue_async_action')) { 91 as_enqueue_async_action(static::class, [$data]); 104 92 } 93 } else { 94 WooMailerLiteLog()->error("Job " . static::class . " failed after max retries."); 105 95 } 106 96 } 97 return true; 107 98 } 108 99 109 /**110 * Set a delay before job runs.111 */112 100 public static function delay(int $delay) 113 101 { -
woo-mailerlite/trunk/includes/jobs/WooMailerLiteCategorySyncJob.php
r3338878 r3377455 6 6 { 7 7 $categories = WooMailerLiteCategory::untracked()->get(100); 8 9 8 if (!$categories->hasItems()) { 10 self::$jobModel->delete(); 11 WooMailerLiteCustomerSyncJob::dispatch($data); 9 WooMailerLiteProductSyncJob::dispatch($data); 12 10 return; 11 } 12 $countInCache = WooMailerLiteCache::get('resource_sync_counts', false); 13 if (isset($countInCache['categories'])) { 14 $countInCache = $countInCache['categories']; 13 15 } 14 16 … … 16 18 17 19 foreach ($categories->items as $category) { 18 error_log('Category: ' . $category->name);19 20 20 $importCategories[] = [ 21 21 'name' => $category->name, … … 29 29 if (!empty($importCategories)) { 30 30 WooMailerLiteApi::client()->importCategories($importCategories); 31 } 32 33 if (static::$jobModel) { 34 static::$jobModel->delete(); 35 } 36 37 if (WooMailerLiteCategory::getUntrackedCategoriesCount()) { 38 static::dispatch($data); 31 if (WooMailerLiteCategory::getUntrackedCategoriesCount() < $countInCache) { 32 static::dispatch($data); 33 } 39 34 } else { 40 WooMailerLite CustomerSyncJob::dispatch($data);35 WooMailerLiteProductSyncJob::dispatch($data); 41 36 } 42 37 } -
woo-mailerlite/trunk/includes/jobs/WooMailerLiteCustomerSyncJob.php
r3338878 r3377455 6 6 { 7 7 $customers = WooMailerLiteCustomer::getAll(100); 8 $processed = false; 8 9 if ($customers->hasItems()) { 10 $countInCache = WooMailerLiteCache::get('resource_sync_counts', false); 11 if (isset($countInCache['customers'])) { 12 $countInCache = $countInCache['customers']; 13 } else { 14 $countInCache = 0; 15 } 9 16 foreach ($customers->items as $customer) { 10 17 $customer->markTracked(); 18 $processed = true; 19 if (WooMailerLiteApi::client()->isClassic()) { 20 $this->syncToClassic($customer); 21 } 11 22 } 12 $response = WooMailerLiteApi::client()->syncCustomers($customers->items); 23 if (WooMailerLiteApi::client()->isClassic()) { 24 static::$jobModel->delete(); 25 } else { 26 $originalCustomers = $customers->toArray(); 13 27 14 if ($response->success) { 15 static::$jobModel->delete(); 28 $transformedCustomers = array_map(function ($data) { 29 $rootKeys = [ 30 'resource_id', 31 'email', 32 'create_subscriber', 33 'accepts_marketing', 34 'total_spent', 35 'orders_count', 36 'last_order_id', 37 'last_order' 38 ]; 39 $flippedRootKeys = array_flip($rootKeys); 40 $subscriberFields = array_diff_key($data, $flippedRootKeys); 41 $rootFields = array_intersect_key($data, $flippedRootKeys); 42 43 return array_merge($rootFields, ['subscriber_fields' => $this->prepareCustomerFieldsForSync($subscriberFields)]); 44 }, $originalCustomers); 45 46 $response = WooMailerLiteApi::client()->syncCustomers($transformedCustomers); 47 48 if ($response->success) { 49 static::$jobModel->delete(); 50 } 16 51 } 17 if (WooMailerLiteCustomer::getAll()) { 52 53 if ($processed && (WooMailerLiteCustomer::getAll() < $countInCache)) { 18 54 self::dispatch($data); 19 } else {20 WooMailerLiteCache::delete('manual_sync');21 self::$jobModel->delete();22 55 } 23 56 } 24 self::$jobModel->delete(); 57 } 58 59 protected function syncToClassic($customer) 60 { 61 try { 62 $customerFields = $this->prepareCustomerFieldsForSync($customer->toArray()); 63 $customerFields['woo_orders_count'] = $customer['orders_count'] ?? 0; 64 $customerFields['woo_total_spent'] = $customer['total_spent'] ?? 0; 65 $customerFields['woo_last_order'] = $customer['last_order'] ?? null; 66 $customerFields['woo_last_order_id'] = $customer['last_order_id'] ?? null; 67 WooMailerLiteApi::client()->syncCustomers([ 68 'email' => $customer['email'], 69 'subscriber_fields' => $customerFields, 70 'shop' => home_url() 71 ]); 72 return true; 73 } catch(\Throwable $e) { 74 return true; 75 } 76 } 77 78 protected function prepareCustomerFieldsForSync($customer) 79 { 80 $syncFields = WooMailerLiteOptions::get('syncFields', []); 81 if (empty($syncFields)) { 82 $syncFields = [ 83 'name', 84 'email', 85 'company', 86 'city', 87 'zip', 88 'state', 89 'country', 90 'phone' 91 ]; 92 WooMailerLiteOptions::update('syncFields', $syncFields); 93 } 94 $syncFields[] = 'last_name'; 95 if (!in_array('name', $syncFields)) { 96 $syncFields[] = 'name'; 97 } 98 return array_intersect_key($customer, array_flip($syncFields)); 25 99 } 26 100 } -
woo-mailerlite/trunk/includes/jobs/WooMailerLiteProductSyncJob.php
r3344677 r3377455 3 3 class WooMailerLiteProductSyncJob extends WooMailerLiteAbstractJob 4 4 { 5 protected $maxRetries = 10;6 protected $retryDelay = 10;7 8 5 public function handle($data = []) 9 6 { 10 7 $products = WooMailerLiteProduct::untracked()->get(100); 11 8 $syncProducts = []; 9 if (!$products->hasItems()) { 10 WooMailerLiteCustomerSyncJob::dispatch($data); 11 return; 12 } 12 13 13 if (!$products->hasItems()) { 14 self::$jobModel->delete(); 15 WooMailerLiteCategorySyncJob::dispatch($data); 16 return; 14 $countInCache = WooMailerLiteCache::get('resource_sync_counts', false); 15 if (isset($countInCache['products'])) { 16 $countInCache = $countInCache['products']; 17 17 } 18 18 … … 33 33 $product->price = $productObj->get_price(); 34 34 } 35 if (!is_string($product->description)) { 36 $product->description = ''; 37 } 38 if ($product->description !== '' && ctype_space($product->description)) { 39 $product->description = ''; 40 } 41 35 42 36 43 $syncProducts[] = array_filter([ … … 52 59 if (!empty($syncProducts)) { 53 60 WooMailerLiteApi::client()->importProducts($syncProducts); 54 } 55 56 if (WooMailerLiteProduct::getUntrackedProductsCount()) { 57 static::dispatch($data); 61 if (WooMailerLiteProduct::getUntrackedProductsCount() < $countInCache) { 62 static::dispatch($data); 63 } 58 64 } else { 59 WooMailerLiteC ategorySyncJob::dispatch($data);65 WooMailerLiteCustomerSyncJob::dispatch($data); 60 66 } 61 67 } -
woo-mailerlite/trunk/includes/models/WooMailerLiteCategory.php
r3338878 r3377455 31 31 [ 32 32 'key' => '_woo_ml_category_tracked', 33 'value' => false,34 'compare' => ' =',33 'value' => ['1', 'true', 'yes'], 34 'compare' => 'NOT IN', 35 35 ], 36 36 [ … … 38 38 'compare' => 'NOT EXISTS', 39 39 ], 40 ] 40 ], 41 'number' => -1 41 42 ]); 42 43 } -
woo-mailerlite/trunk/includes/models/WooMailerLiteCustomer.php
r3361673 r3377455 18 18 'name', 19 19 'last_name', 20 'company', 20 21 'city', 21 22 'state', … … 24 25 'last_order_id', 25 26 'last_order', 27 ]; 28 29 protected $format = [ 30 'accepts_marketing' => 'boolean', 31 'create_subscriber' => 'boolean', 32 'resource_id' => 'string', 26 33 ]; 27 34 … … 34 41 $prefix = db()->prefix; 35 42 36 $query = static::builder()->select(" 37 {$prefix}wc_customer_lookup.customer_id, 38 {$prefix}wc_customer_lookup.email, 39 max({$prefix}wc_order_stats.order_id) AS last_order_id, 40 max({$prefix}wc_order_stats.date_created) AS last_order, 41 count(DISTINCT {$prefix}wc_order_stats.order_id) AS orders_count, 42 sum({$prefix}wc_order_stats.total_sales) AS total_spent, 43 CASE WHEN ( 44 SELECT 45 wpm.meta_value 46 FROM 47 {$prefix}postmeta wpm 48 WHERE 49 wpm.meta_key = '_woo_ml_subscribe' 50 AND wpm.post_id = max({$prefix}wc_order_stats.order_id) 51 LIMIT 1) THEN 52 TRUE 53 ELSE 54 FALSE 55 END AS create_subscriber, 56 max({$prefix}wc_order_stats.order_id) AS last_order_id, 57 max({$prefix}wc_order_stats.date_created) AS last_order, 58 count(DISTINCT ({$prefix}wc_order_stats.order_id)) AS orders_count, 59 sum(({$prefix}wc_order_stats.total_sales)) AS total_spent") 60 ->join('wc_order_stats', 'wc_order_stats.customer_id', 'wc_customer_lookup.customer_id') 61 ->whereIn('wc_order_stats.status', ['wc-processing', 'wc-completed']); 43 $statesQuery = static::builder()->select(" 44 {$prefix}wc_order_stats.customer_id, 45 {$prefix}wc_order_stats.customer_id as resource_id, 46 MAX({$prefix}wc_order_stats.order_id) AS last_order_id, 47 MAX({$prefix}wc_order_stats.date_created) AS last_order, 48 COUNT({$prefix}wc_order_stats.order_id) AS orders_count, 49 SUM({$prefix}wc_order_stats.total_sales) AS total_spent 50 ") 51 ->from("wc_order_stats") 52 ->whereIn("status", ['wc-processing','wc-completed']) 53 ->groupBy("wc_order_stats.customer_id"); 62 54 63 if (self::builder()->customTableEnabled()) { 64 $query->where('wc_order_stats.customer_id', '>', WooMailerLiteOptions::get('lastSyncedCustomer', 0)); 65 } 55 return static::builder()->select(" 56 {$prefix}wc_customer_lookup.customer_id, 57 {$prefix}wc_customer_lookup.customer_id AS resource_id, 58 {$prefix}wc_customer_lookup.email, 59 CASE WHEN {$prefix}postmeta.meta_value IS NOT NULL THEN TRUE ELSE FALSE END AS create_subscriber, 60 CASE WHEN {$prefix}postmeta.meta_value IS NOT NULL THEN TRUE ELSE FALSE END AS accepts_marketing, 61 {$prefix}order_agg.orders_count, 62 {$prefix}order_agg.total_spent, 63 {$prefix}wc_customer_lookup.first_name AS name, 64 {$prefix}wc_customer_lookup.last_name, 65 {$prefix}wc_customer_lookup.city, 66 {$prefix}wc_customer_lookup.state, 67 {$prefix}wc_customer_lookup.country, 68 {$prefix}wc_customer_lookup.postcode AS zip, 69 COALESCE({$prefix}usermeta.meta_value, '') AS company, 70 {$prefix}order_agg.last_order_id, 71 {$prefix}order_agg.last_order, 72 CASE WHEN {$prefix}postmeta.meta_value IS NOT NULL THEN TRUE ELSE FALSE END AS create_subscriber 73 ") 74 ->join($statesQuery, 'order_agg.customer_id', 'wc_customer_lookup.customer_id', 'order_agg') 75 ->leftJoin("postmeta", [ 76 'postmeta.post_id' => 'order_agg.last_order_id', 77 'postmeta.meta_key' => '_woo_ml_subscribe' 78 ]) 79 ->leftJoin("usermeta", [ 80 'usermeta.user_id' => 'wc_customer_lookup.user_id', 81 'usermeta.meta_key' => 'billing_company' 82 ]) 83 ->where('order_agg.customer_id', '>', WooMailerLiteOptions::get('lastSyncedCustomer', 0)) 84 ->orderBy('order_agg.customer_id')->get($limit); 85 } 66 86 67 return $query->groupBy('wc_order_stats.customer_id') 68 ->orderBy('wc_order_stats.customer_id') 69 ->get($limit); 87 public static function getUntrackedCustomersCount() 88 { 89 $prefix = db()->prefix; 90 return static::builder()->select("{$prefix}wc_customer_lookup.customer_id") 91 ->from("wc_customer_lookup") 92 ->join("wc_order_stats", [ 93 'wc_order_stats.customer_id' => 'wc_customer_lookup.customer_id', 94 'wc_order_stats.status' => [ 95 'in' => [ 96 'wc-processing', 'wc-completed' 97 ] 98 ] 99 ]) 100 ->where("wc_order_stats.customer_id", ">", WooMailerLiteOptions::get('lastSyncedCustomer', 0)) 101 ->groupBy("wc_customer_lookup.customer_id") 102 ->orderBy('wc_customer_lookup.customer_id') 103 ->count(); 70 104 } 71 105 … … 73 107 { 74 108 $prefix = db()->prefix; 75 $query = static::builder()->select("*, 109 $query = static::builder()->select("*, 76 110 CASE WHEN ( 77 111 SELECT … … 104 138 public function markTracked() 105 139 { 106 if (!$this->queryBuilder()->customTableEnabled()) { 107 WooMailerLiteOptions::update('lastSyncedOrder', $this->last_order_id); 108 } else { 109 WooMailerLiteOptions::update('lastSyncedCustomer', $this->customer_id); 110 } 140 WooMailerLiteOptions::update('lastSyncedOrder', $this->last_order_id); 141 WooMailerLiteOptions::update('lastSyncedCustomer', $this->customer_id); 111 142 } 112 143 113 144 public static function martUntracked() 114 145 { 115 if (!self::builder()->customTableEnabled()) { 116 WooMailerLiteOptions::update('lastSyncedOrder', 0); 117 } else { 118 WooMailerLiteOptions::update('lastSyncedCustomer', 0); 119 } 146 WooMailerLiteOptions::update('lastSyncedOrder', 0); 147 WooMailerLiteOptions::update('lastSyncedCustomer', 0); 120 148 } 121 149 } -
woo-mailerlite/trunk/includes/models/WooMailerLiteModel.php
r3348600 r3377455 152 152 $this->isResource = true; 153 153 } 154 155 public function setTable($table) 156 { 157 $this->table = $table; 158 } 154 159 } -
woo-mailerlite/trunk/includes/models/WooMailerLiteProduct.php
r3338878 r3377455 19 19 ]; 20 20 21 protected $isResource = true; 21 protected $isResource = false; 22 23 protected $table = 'posts'; 22 24 23 25 protected $format = [ 24 26 'tracked' => 'boolean', 25 27 'image' => 'string', 28 'resource_id' => 'string', 26 29 ]; 27 30 … … 31 34 'image', 32 35 ]; 36 37 const QUERYLIMIT = 1000; 38 39 public function __construct($attributes = []) 40 { 41 parent::__construct($attributes); 42 } 33 43 34 44 public static function tracked() -
woo-mailerlite/trunk/includes/services/WooMailerLiteCheckoutDataService.php
r3346224 r3377455 3 3 class WooMailerLiteCheckoutDataService 4 4 { 5 public static function getCheckoutData( )5 public static function getCheckoutData($email = null) 6 6 { 7 7 if (empty(WC()->cart)) { … … 15 15 $cartItems = $cart->get_cart(); 16 16 $customer = $cart->get_customer(); 17 $cartFromDb = WooMailerLiteCart::where('hash', WooMailerLiteSession::getMLCartHash())->first(); 17 if ($email) { 18 $customerEmail = $email; 19 } else { 20 $customerEmail = $customer->get_email(); 21 } 22 $cartFromDb = WooMailerLiteCart::where('hash', WooMailerLiteSession::getMLCartHash()) 23 ->withoutPrefix(function($query) use ($customerEmail) { 24 $query->orWhere('email', $customerEmail); 25 })->first(); 26 if (!$cartFromDb) { 27 return true; 28 } 18 29 $cartData = $cartFromDb->data; 19 $customerEmail = $customer->get_email();20 30 21 if ( !$customerEmail) {22 $customerEmail = $cartFromDb->email ?? "";31 if (!$customerEmail) { 32 $customerEmail = $cartFromDb->email; 23 33 } 24 34 -
woo-mailerlite/trunk/public/WooMailerLitePublic.php
r3340793 r3377455 44 44 45 45 if (!WooMailerLiteOptions::get('enabled')) { 46 return true; 47 } 48 if (is_plugin_active("official-mailerlite-sign-up-forms/mailerlite.php")) { 46 49 return true; 47 50 } -
woo-mailerlite/trunk/public/js/woo-mailerlite-public.js
r3361673 r3377455 1 1 jQuery(document).ready(function(a) { 2 const allowedInputs = ['billing_email', 'billing_first_name', 'email', 'billing_last_name', 'woo_ml_subscribe', 'billing-first_name', 'billing-last_name', 'shipping-first_name', 'shipping-last_name']; 2 const allowedInputs = [ 3 'billing_email', 4 'billing_first_name', 5 'email', 6 'billing_last_name', 7 'woo_ml_subscribe', 8 'billing-first_name', 9 'billing-last_name', 10 'shipping-first_name', 11 'shipping-last_name' 12 ]; 3 13 let email = null; 4 14 let firstName = null; … … 8 18 let mailerLiteCheckoutBlockActive = null; 9 19 let listeningEvents = false; 10 20 let iteratorInterrupt = 0; 11 21 12 22 // temporarily forcefully picking this js not blocks one 13 23 window.mailerlitePublicJsCaptured = true; 14 // if (document.querySelector('[data-block-name="woocommerce/checkout"]')) {15 // window.mailerlitePublicJsCaptured = false;16 // } else {17 // window.mailerlitePublicJsCaptured = true;18 // }19 24 20 25 if (wooMailerLitePublicData.checkboxSettings.enabled && document.body.classList.contains('woocommerce-checkout')) { … … 45 50 46 51 function triggerAddEvents() { 47 52 iteratorInterrupt++; 53 if (iteratorInterrupt >= 5) { 54 return false; 55 } 48 56 const mailerLiteCheckoutBlockWrapper = document.querySelector('[data-block-name="mailerlite-block/woo-mailerlite"]'); 49 57 50 if (mailerLiteCheckoutBlockWrapper && mailerLiteCheckoutBlockWrapper.querySelector('#woo_ml_subscribe')) {58 if (mailerLiteCheckoutBlockWrapper && mailerLiteCheckoutBlockWrapper.querySelector('#woo_ml_subscribe')) { 51 59 mailerLiteCheckoutBlockActive = true; 52 60 } … … 56 64 } 57 65 58 allowedInputs.forEach((val , key) => {66 allowedInputs.forEach((val) => { 59 67 if (!foundEmail && val.match('email')) { 60 68 email = document.querySelector('#' + val) … … 74 82 } 75 83 } 76 }) 84 }); 77 85 78 86 let signup = document.getElementById('woo_ml_subscribe'); … … 85 93 wooMlCheckoutCheckbox.setAttribute('id', 'woo_ml_subscribe'); 86 94 wooMlCheckoutCheckbox.setAttribute('type', 'checkbox'); 95 wooMlCheckoutCheckbox.setAttribute('name', 'woo_ml_subscribe'); 87 96 wooMlCheckoutCheckbox.setAttribute('value', wooMailerLitePublicData.checkboxSettings.preselect ? 1 : 0); 88 wooMlCheckoutCheckbox.setAttribute('checked', wooMailerLitePublicData.checkboxSettings.preselect ? 'checked' : ''); 89 90 if (!wooMailerLitePublicData.checkboxSettings.hidden) { 91 const label = document.createElement('label'); 92 93 label.style.cursor = 'pointer'; 94 label.style.display = 'inline-flex'; 95 label.style.alignItems = 'center'; 96 label.style.gap = '0.5rem'; 97 label.htmlFor = 'woo_ml_subscribe'; 98 99 // Create text span for the label 100 const labelText = document.createElement('span'); 101 labelText.textContent = wooMailerLitePublicData.checkboxSettings.label ?? 'Yes, I want to receive your newsletter.'; 102 103 // Append checkbox first, then text 104 label.appendChild(wooMlCheckoutCheckbox); 105 label.appendChild(labelText); 106 checkboxWrapper.appendChild(label); 107 // Insert the container after the email field’s wrapper 108 // email.closest('p').insertAdjacentElement('afterend', container); 97 if (wooMailerLitePublicData.checkboxSettings.preselect) { 98 wooMlCheckoutCheckbox.setAttribute('checked', 'checked'); 99 } 100 101 const label = document.createElement('label'); 102 label.style.cursor = 'pointer'; 103 label.style.display = 'inline-flex'; 104 label.style.alignItems = 'center'; 105 label.style.gap = '0.5rem'; 106 label.htmlFor = 'woo_ml_subscribe'; 107 if (wooMailerLitePublicData.checkboxSettings.hidden) { 108 label.style.display = 'none'; 109 } 110 111 const labelText = document.createElement('span'); 112 labelText.textContent = wooMailerLitePublicData.checkboxSettings.label ?? 'Yes, I want to receive your newsletter.'; 113 114 label.appendChild(wooMlCheckoutCheckbox); 115 label.appendChild(labelText); 116 checkboxWrapper.appendChild(label); 117 if (wooMailerLitePublicData.checkboxSettings.hidden) { 118 wooMlCheckoutCheckbox.setAttribute('type', 'hidden'); 109 119 } 110 120 111 121 const wrapper = email.closest('div') ?? email; 112 122 wrapper.parentNode.insertBefore(checkboxWrapper, wrapper.nextSibling); 113 // email.insertAdjacentElement('afterend', wooMlCheckoutCheckbox);114 123 signup = document.getElementById('woo_ml_subscribe'); 115 124 checkboxAdded = true; 116 triggerAddEvents(); 117 } 118 119 if (email !== null) { 125 } 126 127 if (email !== null && !listeningEvents) { 120 128 listeningEvents = true; 129 130 document.addEventListener('change', (event) => { 131 if (event.target && event.target.matches('input[name="billing_email"]')) { 132 validateMLSub(event); 133 } 134 }); 135 121 136 email.addEventListener('change', (event) => { 122 137 validateMLSub(event); 123 138 }); 124 } 125 126 if (firstName !== null) { 127 firstName.addEventListener('change', (event) => { 128 129 if(firstName.value.length > 0) { 130 validateMLSub(event); 131 } 132 }); 133 } 134 135 if (lastName !== null) { 136 lastName.addEventListener('change', (event) => { 137 if(lastName.value.length > 0) { 138 validateMLSub(event); 139 } 140 }); 141 } 142 143 if (signup !== null) { 144 a(document).on('change', signup, function(event) { 145 if (event.target.id === 'woo_ml_subscribe') { 146 validateMLSub(event); 147 } 148 }); 139 140 if (firstName !== null) { 141 firstName.addEventListener('change', (event) => { 142 if (firstName.value.length > 0) { 143 validateMLSub(event); 144 } 145 }); 146 } 147 148 if (lastName !== null) { 149 lastName.addEventListener('change', (event) => { 150 if (lastName.value.length > 0) { 151 validateMLSub(event); 152 } 153 }); 154 } 155 156 if (signup !== null) { 157 a(document).on('change', signup, function(event) { 158 if (event.target.id === 'woo_ml_subscribe') { 159 validateMLSub(event); 160 } 161 }); 162 } 149 163 } 150 164 } 151 165 152 166 function validateMLSub(e) { 153 if (email !== null && email.value.length > 0) {167 if (email !== null && email.value.length > 0) { 154 168 checkoutMLSub(e); 155 169 } … … 157 171 158 172 function checkoutMLSub(e) { 159 160 173 clearTimeout(execute); 161 174 execute = setTimeout(() => { … … 163 176 return false; 164 177 } 165 /** set cookie before sending request to server166 * since multiple checkout update requests can be sent167 * and server cookies won't get updated, so send the saved168 * cookie as a request parameter169 **/170 178 171 179 if (!getCookie('mailerlite_checkout_token')) { … … 187 195 name: firstName?.value ?? '', 188 196 last_name: lastName?.value ?? '', 189 cookie_mailerlite_checkout_token: getCookie('mailerlite_checkout_token')197 cookie_mailerlite_checkout_token: getCookie('mailerlite_checkout_token') 190 198 } 191 199 }); -
woo-mailerlite/trunk/readme.txt
r3361673 r3377455 6 6 Tested up to: 6.8.2 7 7 Requires PHP: 7.2.5 8 Stable tag: 3. 0.88 Stable tag: 3.1.0 9 9 License: GPLv3 or later 10 10 License URI: http://www.gnu.org/licenses/gpl-3.0.html … … 85 85 == Changelog == 86 86 87 = 3.1.0 (13th October 2025) = 88 * Improved data and asynchronous resource synchronization 89 * Bug fixes and performance improvements 90 87 91 = 3.0.8 (15th September 2025) = 88 92 * Improved cart synchronization and updates -
woo-mailerlite/trunk/woo-mailerlite.php
r3361673 r3377455 16 16 * Plugin URI: https://wordpress.org/plugins/woo-mailerlite/ 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. 0.818 * Version: 3.1.0 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. 0.8' );42 define( 'WOO_MAILERLITE_VERSION', '3.1.0' ); 43 43 44 44 define('WOO_MAILERLITE_ASYNC_JOBS', false); … … 97 97 98 98 add_filter('auto_update_plugin', function ($update, $item) { 99 if ( $item->plugin === 'woo-mailerlite/woo-mailerlite.php') {99 if (isset($item->plugin) && $item->plugin === 'woo-mailerlite/woo-mailerlite.php') { 100 100 return WooMailerLiteOptions::get('settings.autoUpdatePlugin'); 101 101 }
Note: See TracChangeset
for help on using the changeset viewer.