* @copyright 2007-2015 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ if (!defined('_PS_VERSION_')) { exit; } if (!class_exists('MailChimpClient')) { include_once _PS_MODULE_DIR_ . 'mailchimpsync/MailChimpClient.php'; } class MailchimpSync extends Module { public function __construct() { $this->name = 'mailchimpsync'; $this->version = '3.2.4'; $this->author = 'Sébastien Jousse'; $this->tab = 'advertising_marketing'; $this->module_key = '56bf075ed4d7cbc0ffe5cecdf05bf480'; $this->ps_versions_compliancy = array('min' => '1.5', 'max' => '1.7'); $this->need_instance = 0; $this->bootstrap = true; parent::__construct(); $this->displayName = $this->l('Mailchimp Sync'); $this->description = $this->l('Synchronize automatically your list of subscribers in Prestashop with your Mailchimp account, and the reverse too.'); $this->confirmUninstall = $this->l('Are you sure you want to uninstall this module ? All your settings will be lost.'); if (Configuration::get('MAILCHIMP_LIST_ID') == 0) { $this->warning = $this->l('You have not yet set your Mailchimp default list.'); } if (Configuration::get('MAILCHIMP_API_KEY') == '') { $this->warning = $this->l('You have not yet set your Mailchimp API key.'); } } public function install() { if (!parent::install() || !Configuration::updateValue('MAILCHIMP_WEBHOOK_TOKEN', MailchimpSync::generateToken()) || !Configuration::updateValue('MAILCHIMP_API_KEY', '') || !Configuration::updateValue('MAILCHIMP_LIST_ID', 0) || !Configuration::updateValue('MAILCHIMP_DOUBLE_OPTIN', false) || !Configuration::updateValue('MAILCHIMP_DELETE_MEMBER', false) || !$this->registerHook('actionObjectCustomerAddAfter') || !$this->registerHook('actionObjectCustomerUpdateAfter') || !$this->registerHook('displayHeader') ) { return false; } return true; } public function uninstall() { if (!Configuration::deleteByName('MAILCHIMP_WEBHOOK_TOKEN') || !Configuration::deleteByName('MAILCHIMP_API_KEY') || !Configuration::deleteByName('MAILCHIMP_LIST_ID') || !Configuration::deleteByName('MAILCHIMP_DOUBLE_OPTIN') || !Configuration::deleteByName('MAILCHIMP_DELETE_MEMBER') || !$this->deleteWebhook() || !$this->unregisterHook('displayHeader') || !$this->unregisterHook('actionObjectCustomerUpdateAfter') || !$this->unregisterHook('actionObjectCustomerAddAfter') || !parent::uninstall() ) { return false; } return true; } private static function generateToken() { return Tools::strtoupper(Tools::passwdGen(16)); } private function addWebhook() { $list_id = Configuration::get('MAILCHIMP_LIST_ID'); if ($this->isConfigured() && $list_id !== '0') { $token = Configuration::get('MAILCHIMP_WEBHOOK_TOKEN'); $webhook_url = $this->context->link->getModuleLink($this->name, 'webhook', array('token' => $token)); $mailchimp = $this->getClient(); try { $mailchimp->webhookAdd($list_id, $webhook_url, array(MailChimpClient::EVENT_SUBSCRIBE => true, MailChimpClient::EVENT_UNSUBSCRIBE => true)); } catch (Exception $exception) { return false; } return true; } return false; } private function deleteWebhook() { $list_id = Configuration::get('MAILCHIMP_LIST_ID'); if ($this->isConfigured() && $list_id !== '0') { $token = Configuration::get('MAILCHIMP_WEBHOOK_TOKEN'); $webhook_url = $this->context->link->getModuleLink($this->name, 'webhook', array('token' => $token)); $mailchimp = $this->getClient(); try { $mailchimp->webhookDel($list_id, $webhook_url); } catch (Exception $exception) { return false; } return true; } return true; } private function testAPI($api_key) { try { $mailchimp = $this->getClient($api_key); $response = $mailchimp->ping(); return $response; } catch (Exception $exception) { return false; } } private function isConfigured() { return Configuration::get('MAILCHIMP_API_KEY') != null; } public function getContent() { $output = null; // API key if (Tools::isSubmit('submitKey')) { $api_key = Tools::getValue('MAILCHIMP_API_KEY'); if (!$api_key || empty($api_key) || !$this->testAPI($api_key) ) { $output .= $this->displayError($this->l('Invalid API key')); } else { Configuration::updateValue('MAILCHIMP_API_KEY', $api_key); $output .= $this->displayConfirmation($this->l('Settings updated')); } } // List if (Tools::isSubmit('submitList')) { $list_id = Tools::getValue('MAILCHIMP_LIST_ID', '0'); if (empty($list_id) || $list_id === '0') { $output .= $this->displayError($this->l('Invalid list')); } else { // delete existing webhook if (Configuration::get('MAILCHIMP_LIST_ID')) { $this->deleteWebhook(); } Configuration::updateValue('MAILCHIMP_LIST_ID', $list_id); $this->addWebhook(); $output .= $this->displayConfirmation($this->l('Settings updated')); } } // Export to MailChimp if (Tools::isSubmit('submitExport')) { if ($this->exportSubscribers()) { $output .= $this->displayConfirmation($this->l('Export in progress')); } else { $output .= $this->displayError($this->l('Error')); } } // Import from MailChimp if (Tools::isSubmit('submitImport')) { if ($this->importSubscribers()) { $output .= $this->displayConfirmation($this->l('Import in progress')); } else { $output .= $this->displayError($this->l('Error')); } } // Settings if (Tools::isSubmit('submitSettings')) { $double_optin = Tools::getValue('MAILCHIMP_DOUBLE_OPTIN'); $delete_member = Tools::getValue('MAILCHIMP_DELETE_MEMBER'); if ($double_optin === false || $delete_member === false) { $output .= $this->displayError($this->l('Invalid settings')); } else { Configuration::updateValue('MAILCHIMP_DOUBLE_OPTIN', $double_optin); Configuration::updateValue('MAILCHIMP_DELETE_MEMBER', $delete_member); $output .= $this->displayConfirmation($this->l('Settings updated')); } } return $output.$this->displayForm(); } private function getClient($api_key = '') { if ($api_key == '') { if (!$this->isConfigured()) { return null; } $api_key = Configuration::get('MAILCHIMP_API_KEY'); } $mailchimp = new MailChimpClient($api_key); return $mailchimp; } private function getLists() { if (!$this->isConfigured()) { return array(); } $mailchimp = $this->getClient(); try { $response = $mailchimp->getLists(); $lists = $response['lists']; return $lists; } catch (Exception $exception) { return array(); } } /** * @return array */ private function getSubscribers() { return array_merge( $this->getCustomerSubscribers(), $this->getGuestSubscribers() ); } private function exportSubscribers() { $subscribers = $this->getSubscribers(); $list_id = Configuration::get('MAILCHIMP_LIST_ID'); if (!$this->isConfigured() || $list_id === '0') { return false; } $mailchimp = $this->getClient(); try { $response = $mailchimp->exportContacts($list_id, $subscribers); return $response; } catch (Exception $exception) { return false; } } private function importSubscribers() { // get MC subscribers $list_id = Configuration::get('MAILCHIMP_LIST_ID'); if (!$this->isConfigured() || $list_id === '0') { return false; } $mailchimp = $this->getClient(); try { $contacts = $mailchimp->getContacts($list_id); // update PS customers foreach ($contacts as $contact) { $c = new Customer(); if ($c->getByEmail($contact['email_address'])) { // subscribe contact if ($contact['status'] == 'subscribed') { if ($c->newsletter == 0) { $c->newsletter = 1; $c->ip_registration_newsletter = $contact['ip_signup']; $c->newsletter_date_add = $contact['timestamp_signup']; $c->update(); } } // unsubscribe contact elseif ($contact['status'] == 'unsubscribed' || $contact['status'] == 'cleaned') { if ($c->newsletter == 1) { $c->newsletter = 0; $c->ip_registration_newsletter = ''; $c->newsletter_date_add = ''; $c->update(); } } } } return true; } catch (Exception $exception) { return false; } } public function displayForm() { // Get default language $default_lang = (int)Configuration::get('PS_LANG_DEFAULT'); $fields_form = array(); // API key $fields_form[]['form'] = array( 'legend' => array( 'title' => $this->l('API key'), ), 'description' => $this->l('You must have a MailChimp account. You can create one on their website : ') .'http://mailchimp.com/', 'input' => array( array( 'type' => 'text', 'label' => $this->l('Mailchimp API key'), 'desc' => $this->l('Example: 123456789abcdef0123456789abcdef0-us2') .' http://eepurl.com/im9k', 'name' => 'MAILCHIMP_API_KEY', 'size' => 45, 'required' => true ) ), 'submit' => array( 'title' => $this->l('Save'), 'name' => 'submitKey', 'class' => 'btn btn-default pull-right' ) ); // List $lists = $this->getLists(); $fields_form[]['form'] = array( 'legend' => array( 'title' => $this->l('Default list'), ), 'input' => array( array( 'type' => 'select', 'label' => $this->l('Mailchimp list'), 'desc' => $this->l('How to create one:').' http://eepurl.com/gOHY', 'name' => 'MAILCHIMP_LIST_ID', 'options' => array( 'default' => array( 'label' => $this->l('Select a list'), 'value' => 0 ), 'query' => $lists, 'id' => 'id', 'name' => 'name', ), 'required' => true ) ), 'submit' => array( 'title' => $this->l('Save'), 'name' => 'submitList', 'class' => 'btn btn-default pull-right' ) ); // Export to MailChimp $fields_form[]['form'] = array( 'legend' => array( 'title' => $this->l('Export subscribers to MailChimp'), ), 'description' => $this->l('You can export all your existing subscribers from your shop to your list on MailChimp. This action only send contacts (email, lastname, firstname) to MailChimp. Unsubscribed and cleaned email addresses will remain as is.'), 'submit' => array( 'title' => $this->l('Export'), 'name' => 'submitExport', 'class' => 'btn btn-default pull-right' ) ); // Import from MailChimp $fields_form[]['form'] = array( 'legend' => array( 'title' => $this->l('Import subscribers from MailChimp'), ), 'description' => $this->l('You can import all your existing subscribers from your MailChimp list to your shop. This will set to true the newsletter preference of each customer being in MailChimp list.'), 'submit' => array( 'title' => $this->l('Import'), 'name' => 'submitImport', 'class' => 'btn btn-default pull-right' ) ); // Settings $fields_form[]['form'] = array( 'legend' => array( 'title' => $this->l('Settings'), ), 'description' => 'Webhook: ' . $this->context->link->getModuleLink($this->name, 'webhook', array('token' => Configuration::get('MAILCHIMP_WEBHOOK_TOKEN'))), 'input' => array( array( 'type' => (version_compare(_PS_VERSION_, '1.6') < 0) ? 'radio' : 'switch', 'label' => $this->l('Double Opt in', get_class($this), null, false), 'name' => 'MAILCHIMP_DOUBLE_OPTIN', 'required' => false, 'class' => 't', 'is_bool' => true, 'values' => array( array( 'id' => 'double_option_on', 'value' => 1, 'label' => $this->l('Enabled', get_class($this), null, false) ), array( 'id' => 'double_option_off', 'value' => 0, 'label' => $this->l('Disabled', get_class($this), null, false) ) ), ), array( 'type' => (version_compare(_PS_VERSION_, '1.6') < 0) ? 'radio' : 'switch', 'label' => $this->l('Delete member on unsubscribe', get_class($this), null, false), 'name' => 'MAILCHIMP_DELETE_MEMBER', 'required' => false, 'class' => 't', 'is_bool' => true, 'values' => array( array( 'id' => 'delete_member_on', 'value' => 1, 'label' => $this->l('Enabled', get_class($this), null, false) ), array( 'id' => 'delete_member_off', 'value' => 0, 'label' => $this->l('Disabled', get_class($this), null, false) ) ), ), ), 'submit' => array( 'title' => $this->l('Save'), 'name' => 'submitSettings', 'class' => 'btn btn-default pull-right' ) ); $helper = new HelperForm(); // Module, token and currentIndex $helper->module = $this; $helper->name_controller = $this->name; $helper->token = Tools::getAdminTokenLite('AdminModules'); $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name; // Language $helper->default_form_language = $default_lang; $helper->allow_employee_form_lang = $default_lang; // Title and toolbar $helper->title = $this->displayName; $helper->show_toolbar = true; $helper->toolbar_scroll = true; $helper->submit_action = 'submit'.$this->name; $helper->toolbar_btn = array(); // Load current value $helper->fields_value['MAILCHIMP_API_KEY'] = Configuration::get('MAILCHIMP_API_KEY'); $helper->fields_value['MAILCHIMP_LIST_ID'] = Configuration::get('MAILCHIMP_LIST_ID'); $helper->fields_value['MAILCHIMP_DOUBLE_OPTIN'] = Configuration::get('MAILCHIMP_DOUBLE_OPTIN'); $helper->fields_value['MAILCHIMP_DELETE_MEMBER'] = Configuration::get('MAILCHIMP_DELETE_MEMBER'); return $helper->generateForm($fields_form); } private function prepareHook() { if (Tools::isSubmit('submitNewsletter')) { $email = Tools::getValue('email'); if (empty($email) || !Validate::isEmail($email)) { return $this->l('Invalid email address'); } /* Unsubscription */ if (Tools::getValue('action') == '1') { return $this->unregister($email); } /* Subscription */ if (Tools::getValue('action') == '0') { $merge_vars = array(); $data = array('optin_ip' => Tools::getRemoteAddr()); return $this->register($email, $merge_vars, $data); } } return true; } private function register($email, $merge_vars, $data) { $list_id = Configuration::get('MAILCHIMP_LIST_ID'); if (!$this->isConfigured() || $list_id === '0') { return false; } $mailchimp = $this->getClient(); // add language if (!array_key_exists('mc_language', $data)) { $data['mc_language'] = $this->context->language->iso_code; } $double_optin = Configuration::get('MAILCHIMP_DOUBLE_OPTIN'); try { $mailchimp->subContact($list_id, $email, $merge_vars, $data, $double_optin, true); return true; } catch (Exception $exception) { return false; } } private function unregister($email) { $list_id = Configuration::get('MAILCHIMP_LIST_ID'); if (!$this->isConfigured() || $list_id === '0') { return false; } $mailchimp = $this->getClient(); $delete_member = Configuration::get('MAILCHIMP_DELETE_MEMBER'); try { $mailchimp->unsubContact($list_id, $email, $delete_member); return true; } catch (Exception $exception) { return false; } } public function hookActionObjectCustomerAddAfter($params) { $new_customer = $params['object']; if (!Validate::isLoadedObject($new_customer)) { return false; } $newsletter = $new_customer->newsletter; $email = $new_customer->email; if (!$newsletter || !Validate::isEmail($email)) { return true; } $merge_vars = array( 'FNAME' => $new_customer->firstname, 'LNAME' => $new_customer->lastname, ); $data = array( 'optin_ip' => $new_customer->ip_registration_newsletter, 'optin_time' => $new_customer->newsletter_date_add, ); return $this->register($email, $merge_vars, $data); } public function hookActionObjectCustomerUpdateAfter($params) { $customer = $params['object']; if (!Validate::isLoadedObject($customer)) { return false; } if (!isset($customer->newsletter)) { return false; } if ($customer->newsletter == 0) { return $this->unregister($customer->email); } else { $merge_vars = array( 'FNAME' => $customer->firstname, 'LNAME' => $customer->lastname, ); $data = array( 'optin_ip' => $customer->ip_registration_newsletter, 'optin_time' => $customer->newsletter_date_add, ); return $this->register($customer->email, $merge_vars, $data); } } public function hookDisplayHeader($params) { $this->prepareHook(); } public function hookDisplayLeftColumn($params) { $this->hookDisplayHeader($params); } public function hookDisplayRightColumn($params) { $this->hookDisplayHeader($params); } public function hookDisplayFooter($params) { $this->hookDisplayHeader($params); } public function confirmEvent() { $token = Configuration::get('MAILCHIMP_WEBHOOK_TOKEN'); if ($token === false) { return false; } if (!Tools::getIsset('token') || Tools::getValue('token') != $token) { return false; } if (!Tools::getIsset('type')) { return false; } $data = Tools::getValue('data'); switch (Tools::getValue('type')) { case 'unsubscribe': if (!array_key_exists('email', $data)) { return false; } $c = new Customer(); if ($c->getByEmail($data['email'])) { $c->newsletter = 0; $c->ip_registration_newsletter = null; $c->newsletter_date_add = null; $c->update(); } break; case 'subscribe': if (!array_key_exists('email', $data) || !array_key_exists('ip_opt', $data)) { return false; } $c = new Customer(); if ($c->getByEmail($data['email'])) { $c->newsletter = 1; $c->ip_registration_newsletter = $data['ip_opt']; $c->newsletter_date_add = Tools::getValue('fired_at'); $c->update(); } break; } return true; } /** * Get customer subscribers * * @return array */ private function getCustomerSubscribers() { $dbquery = new DbQuery(); $dbquery->select('c.`lastname`, c.`firstname`, c.`email`, l.`iso_code` AS `lang`'); $dbquery->from('customer', 'c'); $dbquery->leftJoin('lang', 'l', 'c.`id_lang` = l.`id_lang`'); $dbquery->where('c.`newsletter` = 1'); if (Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP) { $dbquery->where('c.`id_shop` = ' . $this->context->shop->id); } if (Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_GROUP) { $dbquery->where('c.`id_shop_group` = ' . $this->context->shop->id_shop_group); } $customers = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($dbquery->build()); return $customers; } /** * Get guest subscribers * * @return array */ private function getGuestSubscribers() { return array_merge( $this->getGuestSubscribersFromTable('newsletter'), $this->getGuestSubscribersFromTable('emailsubscription') ); } /** * @param $table * @return array */ private function getGuestSubscribersFromTable($table) { $guests = array(); Db::getInstance()->executeS('SHOW TABLES LIKE \'' . _DB_PREFIX_ . $table. '\''); if (Db::getInstance()->numRows() == 1) { $dbquery = new DbQuery(); $dbquery->select('NULL AS `lastname`, NULL AS `firstname`, n.`email`, NULL AS `lang`'); $dbquery->from($table, 'n'); $dbquery->where('n.`active` = 1'); if (Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP) { $dbquery->where('n.`id_shop` = ' . $this->context->shop->id); } if (Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_GROUP) { $dbquery->where('n.`id_shop_group` = ' . $this->context->shop->id_shop_group); } $guests = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($dbquery->build()); } return $guests; } }