'_mailerlite/webhook/']; public $upgrade_message; public function __construct() { $settings = \Settings::getDefault(); if (isset($settings['mailerlite'])) { $this->api_key = $settings['mailerlite']['api_key']; $this->groupID = $settings['mailerlite']['group']; if (isset($settings['mailerlite']['version'])) { $this->version = $settings['mailerlite']['version']; } } if ($this->api_key && $this->groupID) { } else { $this->error = 'MailerLite modul je špatně nastavený!'; } } public function getSubscriberGroup() { if ($this->api_key && $this->groupID) { $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID, 5); if (!$this->error) { $group_response = json_decode($response, true); if (!empty($group_response['error'])) { $this->error = 'MailerLite: Group Error: '.$group_response['error']['message']; } else { $this->group = $group_response; } } } else { $this->error = 'MailerLite modul je špatně nastavený!'; } } public function upgradeCustomFields() { if ($this->error) { $this->upgrade_message[] = $this->error; } if ($this->api_key) { $response = $this->execCurl('https://api.mailerlite.com/api/v2/fields', 5); $fields = json_decode($response, true); if (!empty($fields['error'])) { $this->upgrade_message[] = 'MailerLite: Fields Error: '.$fields['error']['message']; } else { $fields_error = false; $fields_titles = array_flip(array_column($fields, 'title')); foreach ($this->custom_fields as $customField) { if (isset($customField[2]) && !findModule($customField[2])) { $this->upgrade_message[] = 'Field skipped - '.$customField[0].' - module not found - '.$customField[2].'; '; continue; } if (!array_key_exists($customField[0], $fields_titles)) { $new_field = '{"title": "'.$customField[0].'", "type": "'.$customField[1].'"}'; $response = $this->execCurl('https://api.mailerlite.com/api/v2/fields', 5, 'POST', $new_field); $field_response = json_decode($response, true); if (!empty($field_response['error'])) { $fields_error = true; $this->upgrade_message[] = 'MailerLite: Fields Error: '.$field_response['error']['message']; } else { $this->upgrade_message[] = 'New field created - '.$field_response['title'].', '.$field_response['id'].', '.$field_response['key'].'; '; } } } if (!$fields_error) { $settings = \Settings::getDefault(); if ($this->version < self::VERSION) { $mailerlite_settings = ['version' => self::VERSION]; $settings->updateValue('mailerlite', $mailerlite_settings); $this->version = $settings['mailerlite']['version']; $this->upgrade_message[] = 'MailerLite: version upgraded to \''.$this->version.'\' '; } return true; } } } return false; } public function upgradeWebhooks() { if ($this->error) { $this->upgrade_message[] = $this->error; } if ($this->api_key) { $response = $this->execCurl('https://api.mailerlite.com/api/v2/webhooks', 5); $webhooks = json_decode($response, true); if (!empty($webhooks['error'])) { $this->upgrade_message[] = 'MailerLite: Webhooks Error: '.$webhooks['error']['message']; } else { $webhooks_error = false; $domain = Config::get()['Addr']['full']; $webhooks = $webhooks['webhooks']; foreach ($webhooks as $webhook) { $webhook_type = $webhook['event']; if (array_key_exists($webhook_type, $this->webhooks)) { $url = $domain.$this->webhooks[$webhook_type]; if ($webhook['url'] != $url) { $response = $this->execCurl('https://api.mailerlite.com/api/v2/webhooks/'.$webhook['id'], 5, 'DELETE'); $webhook_response = json_decode($response, true); if (!empty($webhook_response['error'])) { $webhooks_error = true; $this->upgrade_message[] = 'MailerLite: Webhooks Error: '.$webhook_response['error']['message']; } else { $this->upgrade_message[] = 'Webhook deleted - '.$webhook['id'].', '.$webhook_type.', '.$webhook['url'].'; '; } } } } foreach ($this->webhooks as $event => $url) { $new_webhook = '{"event": "'.$event.'", "url": "'.$domain.$url.'"}'; $response = $this->execCurl('https://api.mailerlite.com/api/v2/webhooks/', 5, 'POST', $new_webhook); $webhook_response = json_decode($response, true); if (!empty($webhook_response['error'])) { $webhooks_error = true; $this->upgrade_message[] = 'MailerLite: Webhooks Error: '.$webhook_response['error']['message']; } else { $this->upgrade_message[] = 'New webhook created - '.$webhook_response['id'].', '.$webhook_response['event'].', '.$webhook_response['url'].'; '; } } return !$webhooks_error; } } return false; } public function execCurl($url, $timeout = 50, $http_method = 'GET', $post_data = null) { $curl = curl_init(); curl_setopt_array( $curl, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => $timeout, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => $http_method, CURLOPT_HTTPHEADER => [ 'content-type: application/json', 'x-mailerlite-apikey: '.$this->api_key, ], ] ); if ($post_data) { curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data); } $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { $this->error = 'MailerLite: cURL Error: '.$err; } return $response; } public function updateUser($user, $newsletter) { $mailerlite_data_array['email'] = $user->email; $cur_date = date('Y-m-d H:i:s'); if ($newsletter == 'Y') { $mailerlite_data_array['type'] = 'active'; $mailerlite_data_array['date_subscribe'] = $cur_date; $user_data = $this->getKupShopUsers([$user->id]); if ($user_data['subscribers_count'] == 1) { $mailerlite_data_array += reset($user_data['subscribers']); } elseif ($user_data['unsubscribed_count'] == 1) { $mailerlite_data_array += reset($user_data['unsubscribed']); } $mailerlite_data_array['resubscribe'] = true; } else { $mailerlite_data_array['type'] = 'unsubscribed'; $mailerlite_data_array['date_unsubscribe'] = $cur_date; } $mailerlite_data = $this->prepareExportData($mailerlite_data_array); $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID.'/subscribers', 10, 'POST', $mailerlite_data); if ($this->error) { return false; } $subscriber_response = json_decode($response, true); if (!empty($subscriber_response['error'])) { $this->error = 'MailerLite: updateUser Error: '.$subscriber_response['error']['message']; return false; } return true; } public function getMailerLiteSubscribers($type = 'Unsubscribed', $page = null, $filter_date = null) { $data = []; $get_params = '?limit='.$this->limit; if ($filter_date && ($type == 'Unsubscribed')) { $get_params .= '&filters[date_unsubscribe][$gte]='.$filter_date; $data['filter_date'] = $filter_date; } if (!is_null($page)) { if ($page > 0) { $get_params .= '&offset='.($page * $this->limit); } } $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID.'/subscribers/'.strtolower($type).$get_params); if ($this->error) { $data['err'] = $this->error; } else { $subscribers = json_decode($response, true); if (is_null($subscribers)) { $data['err'] = 'MailerLite: Subscribers Error: '.$response; } else { if (!empty($subscribers['error'])) { $data['err'] = 'MailerLite: Subscribers Error: '.$subscribers['error']['message']; } else { $emails = array_column($subscribers, 'email'); $subscribers = array_combine($emails, $subscribers); $data['subscribers_count'] = count($subscribers); $data['subscribers'] = $subscribers; } } } return $data; } public function getMailerLiteSubscribers_All($type = 'Unsubscribed') { $data['subscribers_count'] = 0; $data['subscribers'] = []; $page = 0; $next_page = $this->getMailerLiteSubscribers($type, $page); while (!empty($next_page['subscribers_count']) && ($next_page['subscribers_count'] > 0)) { $data['subscribers_count'] += $next_page['subscribers_count']; $data['subscribers'] += $next_page['subscribers']; $page++; $next_page = $this->getMailerLiteSubscribers($type, $page); } if (!empty($next_page['err'])) { $data['err'] = $next_page['err']; } return $data; } public function MailerliteUnsubscribe($users_data) { $data = []; $cur_date = date('Y-m-d H:i:s'); $batch_requests['requests'] = []; $i = 0; foreach ($users_data as $email => $value) { $i++; $mailerlite_request['method'] = 'POST'; $mailerlite_request['path'] = '/api/v2/groups/'.$this->groupID.'/subscribers'; $mailerlite_request['body']['email'] = $email; $mailerlite_request['body']['type'] = 'unsubscribed'; $mailerlite_request['body']['date_unsubscribe'] = $cur_date; $batch_requests['requests'][] = $mailerlite_request; // if ($i == 50) { // break; // There is a limit of maximum 50 requests per single batch. // } } /* $batch_requests_json = json_encode($batch_requests); $response = $this->execCurl('https://api.mailerlite.com/api/v2/batch', 30, 'POST', $batch_requests_json); if ($this->error) { $data['err'] = $this->error; } else { $batch_response = json_decode($response, true); if (!empty($batch_response['error'])) { $data['err'] = 'MailerLite: Unsubscribe Error: '.$batch_response['error']['message']; } else { $data['success'] = $batch_response; } } */ // if ($data['err']) { // if (strstr($data['err'], 'A website error has occurred.')) { // 'batch' is not working - A website error has occurred. The website administrator has been notified of the issue. Sorry for the temporary inconvenience // send one by one $data['err'] = ''; $unsubscribed_count = 0; foreach ($batch_requests['requests'] as $mailerlite_request) { $mailerlite_request_json = json_encode($mailerlite_request['body']); $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID.'/subscribers', 5, 'POST', $mailerlite_request_json); if ($this->error) { $data['err'] = $this->error; } else { $unsubscribe_response = json_decode($response, true); if (!empty($unsubscribe_response['error'])) { $data['err'] = 'MailerLite: Unsubscribe Error: '.$unsubscribe_response['error']['message']; } else { if ($unsubscribe_response['type'] == 'unsubscribed') { $unsubscribed_count++; } } } if ($data['err']) { break; } } if ($unsubscribed_count > 0) { $data['success'] = 'MailerliteUnsubscribe: '.$unsubscribed_count; } // } // } return $data; } public function MailerliteSubscribe($users_data) { $data = []; if (!empty($users_data)) { $subscribers = '{"subscribers":['.implode(',', $users_data).'], "resubscribe":true}'; $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID.'/subscribers/import', 300, 'POST', $subscribers); if ($this->error) { $data['err'] = $this->error; } else { $export_result = json_decode($response, true); if (!empty($export_result['error'])) { $data['err'] = 'MailerLite: Subscribe Error: '.$export_result['error']['message']; } else { $export_success_str = 'Export: imported = '.count($export_result['imported'] ?? []); $export_success_str .= ', updated = '.count($export_result['updated'] ?? []); $export_success_str .= ', unchanged = '.count($export_result['unchanged'] ?? []); $export_success_str .= ', errors = '.count($export_result['errors'] ?? []); $data['success'] = $export_success_str; } } } return $data; } public function getKupShopUsers($user_ids = null) { $data = []; if (findModule(\Modules::CURRENCIES)) { $currency = 'u.currency'; } else { $currency = "NULL AS 'currency'"; } $sql = sqlQueryBuilder() ->select('u.id', 'LOWER(u.email) as email', 'u.get_news', 'u.figure as `registered`', 'u.name', 'u.surname as `last_name`', 'u.firm as `company`', 'u.date_reg', 'u.date_updated', 'l.name as user_price_level', 'u.country', $currency, '(SELECT CONCAT("\"", GROUP_CONCAT(ug.name SEPARATOR "\", \""), "\"") FROM users_groups_relations ugr LEFT JOIN users_groups ug ON ugr.id_group = ug.id WHERE ugr.id_user = u.id) AS user_groups', 'COUNT(o.id) AS orders_count', 'ROUND(SUM(o.total_price*o.currency_rate)) AS orders_total', 'MAX(o.date_created) AS last_order_date', 'SUM(o.date_created >= DATE_SUB(current_date, INTERVAL 1 YEAR)) as last_year_orders_count', 'ROUND(SUM(o.total_price*o.currency_rate*(o.date_created >= DATE_SUB(current_date, INTERVAL 1 YEAR)))) as last_year_orders_total') ->from('users', 'u') ->leftJoin('u', 'users_dealer_price_level', 'udpl', 'u.id = udpl.id_user') ->leftJoin('u', 'price_levels', 'l', 'udpl.id_price_level = l.id') ->leftJoin('u', 'orders', 'o', 'u.email = o.invoice_email AND o.status_storno=0') ->groupBy('u.id') ->orderBy('u.date_updated', 'DESC'); if (findModule(\Modules::BONUS_PROGRAM)) { $sql->addSelect('(SELECT SUM(bp.points) FROM bonus_points bp WHERE bp.id_user = u.id AND bp.status = "active") AS bonus_points'); } if (!is_null($user_ids)) { $sql->where(\Query\Operator::inIntArray($user_ids, 'u.id')); } $qb = $sql->execute(); $users = $qb->fetchAll(); $users_emails = array_column($users, 'email'); $users = array_combine($users_emails, $users); $subscribers = array_filter($users, function ($v) {return $v['get_news'] == 'Y'; }); $unsubscribed = array_filter($users, function ($v) {return $v['get_news'] == 'N'; }); $data['subscribers'] = $subscribers; $data['unsubscribed'] = $unsubscribed; $data['subscribers_count'] = count($data['subscribers']); $data['unsubscribed_count'] = count($data['unsubscribed']); return $data; } public function ShopUnsubscribe($users_data) { $data = []; if (!empty($users_data)) { $qb = sqlQueryBuilder()->update('users') ->set('get_news', ':news')->setParameter('news', 'N') ->set('date_unsubscribe', 'NOW()') ->set('date_updated', 'NOW()') ->where(\Query\Operator::inIntArray($users_data, 'id')) ->andWhere('get_news=\'Y\'') ->execute(); $data['success'] = 'ShopUnsubscribe: '.$qb; } return $data; } public function ShopSubscribe($users_data) { $data = []; if (!empty($users_data)) { $qb = sqlQueryBuilder()->update('users') ->set('get_news', ':news')->setParameter('news', 'Y') ->where(\Query\Operator::inIntArray($users_data, 'id')) ->execute(); $data['success'] = 'ShopSubscribe: '.$qb; } return $data; } public function Synchronize($filter_date = null) { $success = null; $error = null; if ($filter_date) { $data['Unsubscribed'] = $this->getMailerLiteSubscribers('Unsubscribed', null, $filter_date); } else { $data['Unsubscribed'] = $this->getMailerLiteSubscribers_All('Unsubscribed'); } if (array_key_exists('err', $data['Unsubscribed'])) { $error[] = $data['Unsubscribed']['err']; } $data['Junk'] = $this->getMailerLiteSubscribers('Junk'); if (!empty($data['Junk']['subscribers_count'])) { if ($error) { $data['Unsubscribed'] = $data['Junk']; } else { $data['Unsubscribed']['subscribers_count'] += $data['Junk']['subscribers_count']; $data['Unsubscribed']['subscribers'] += $data['Junk']['subscribers']; } } $data['Bounced'] = $this->getMailerLiteSubscribers('Bounced'); if (!empty($data['Bounced']['subscribers_count'])) { if ($error && empty($data['Unsubscribed'])) { $data['Unsubscribed'] = $data['Bounced']; } else { $data['Unsubscribed']['subscribers_count'] += $data['Bounced']['subscribers_count']; $data['Unsubscribed']['subscribers'] += $data['Bounced']['subscribers']; } } $data['ShopUsers'] = $this->getKupShopUsers(); // all if (!empty($data['Unsubscribed']['subscribers_count'])) { $mailerlite_unsubscribed = $data['Unsubscribed']['subscribers']; $shop_users_subscribers = $data['ShopUsers']['subscribers']; $array_unsubscribe = array_intersect_key($shop_users_subscribers, $mailerlite_unsubscribed); if ($array_unsubscribe) { // Unsubscribe on Shop $ids = array_column($array_unsubscribe, 'id'); $result = $this->ShopUnsubscribe($ids); if (!empty($result['success'])) { $success[] = $result['success']; } if (!empty($result['error'])) { $error[] = $result['error']; } } } $user_ids = null; if ($filter_date) { $sql = sqlQueryBuilder() ->select('u.id') ->from('users', 'u') ->where('u.date_updated >= "'.$filter_date.'" '); $qb = $sql->execute(); $user_ids = $qb->fetchAll(); $user_ids = array_column($user_ids, 'id'); $sql = sqlQueryBuilder() ->select('DISTINCT o.id_user') ->from('orders', 'o') ->where('o.id_user IS NOT NULL') ->andWhere('(o.date_created >= "'.$filter_date.'") OR (o.date_created >= DATE_SUB(current_date, INTERVAL 370 DAY) AND o.date_created < DATE_SUB(current_date, INTERVAL 1 YEAR) )'); $qb = $sql->execute(); $user_ids2 = $qb->fetchAll(); $user_ids2 = array_column($user_ids2, 'id_user'); $user_ids = array_unique(array_merge($user_ids, $user_ids2)); } $data['ShopUsers'] = $this->getKupShopUsers($user_ids); if ($data['ShopUsers']['subscribers_count']) { // Subscribe on MailerLite $shop_users_subscribers = $data['ShopUsers']['subscribers']; foreach (array_chunk($shop_users_subscribers, 10000) as $chunk) { $users_data = array_map([$this, 'prepareExportData'], $chunk); $result = $this->MailerliteSubscribe($users_data); if (!empty($result['success'])) { $success[] = $result['success']; } if (!empty($result['error'])) { $error[] = $result['error']; } } } if ($data['ShopUsers']['unsubscribed_count']) { $shop_users_unsubscribed = $data['ShopUsers']['unsubscribed']; $mailerlite_unsubscribed = $data['Unsubscribed']['subscribers'] ?? []; $array_unsubscribe = array_diff_key($shop_users_unsubscribed, $mailerlite_unsubscribed); if ($array_unsubscribe) { // Unsubscribe on MailerLite $result = $this->MailerliteUnsubscribe($array_unsubscribe); if (!empty($result['success'])) { $success[] = $result['success']; } if (!empty($result['error'])) { $error[] = $result['error']; } } } $result = null; if ($success) { $result['success'] = $success; } if ($error) { $result['error'] = $error; } if (empty($success) && empty($error)) { $result['success'][] = 'Nothing to Synchronize'; } return $result; } public static function prepareExportData($subscriber) { $export_data = null; $export_data['email'] = $subscriber['email']; foreach ($subscriber as $key => $value) { if (in_array($key, ['email', 'type', 'date_subscribe', 'date_unsubscribe', 'resubscribe'])) { $export_data[$key] = $value; } else { $export_data['fields'][$key] = $value; } } $unsubscribed = (isset($subscriber['type']) && ($subscriber['type'] == 'unsubscribed')); if (!$unsubscribed) { $greeting = \Greeting::getGreeting($subscriber['name'], $subscriber['last_name'], 'name'); $export_data['fields']['greeting_name'] = $greeting['greeting']; $export_data['fields']['sex'] = (!empty($greeting['sex']) ? $greeting['sex'] : ''); $greeting = \Greeting::getGreeting($subscriber['name'], $subscriber['last_name'], 'surname'); $export_data['fields']['greeting_surname'] = $greeting['greeting']; } $export_str = json_encode($export_data, 256); return $export_str; } }