* @copyright 2007-2011 PrestaShop SA * @version Release: $Revision: 7660 $ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ if (!defined('_CAN_LOAD_FILES_')) exit; class MailAlerts extends Module { private $_html = ''; private $_postErrors = array(); private $_merchant_mails; private $_merchant_order; private $_merchant_oos; private $_customer_qty; const __MA_MAIL_DELIMITOR__ = ','; public function __construct() { $this->name = 'mailalerts'; $this->tab = 'administration'; $this->version = '2.2'; $this->author = 'PrestaShop'; parent::__construct(); if ($this->id) $this->_refreshProperties(); $this->displayName = $this->l('Mail alerts'); $this->description = $this->l('Sends e-mail notifications to customers and merchants.'); $this->confirmUninstall = $this->l('Are you sure you want to delete all customer notifications?'); } public function install() { if (!parent::install() OR !$this->registerHook('newOrder') OR !$this->registerHook('updateQuantity') OR !$this->registerHook('productOutOfStock') OR !$this->registerHook('customerAccount') OR !$this->registerHook('updateProduct') OR !$this->registerHook('deleteProduct') OR !$this->registerHook('deleteProductAttribute') OR !$this->registerHook('updateProductAttribute') ) return false; Configuration::updateValue('MA_MERCHANT_ORDER', 1); Configuration::updateValue('MA_MERCHANT_OOS', 1); Configuration::updateValue('MA_CUSTOMER_QTY', 1); Configuration::updateValue('MA_MERCHANT_MAILS', Configuration::get('PS_SHOP_EMAIL')); Configuration::updateValue('MA_LAST_QTIES', Configuration::get('PS_LAST_QTIES')); if (!Db::getInstance()->Execute(' CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'mailalert_customer_oos` ( `id_customer` int(10) unsigned NOT NULL, `customer_email` varchar(128) NOT NULL, `id_product` int(10) unsigned NOT NULL, `id_product_attribute` int(10) unsigned NOT NULL, PRIMARY KEY (`id_customer`,`customer_email`,`id_product`,`id_product_attribute`) ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci') ) return false; /* This hook is optional */ $this->registerHook('myAccountBlock'); return true; } public function uninstall() { Configuration::deleteByName('MA_MERCHANT_ORDER'); Configuration::deleteByName('MA_MERCHANT_OOS'); Configuration::deleteByName('MA_CUSTOMER_QTY'); Configuration::deleteByName('MA_MERCHANT_MAILS'); Configuration::deleteByName('MA_LAST_QTIES'); if (!Db::getInstance()->Execute('DROP TABLE '._DB_PREFIX_.'mailalert_customer_oos')) return false; return parent::uninstall(); } private function _refreshProperties() { $this->_merchant_mails = Configuration::get('MA_MERCHANT_MAILS'); $this->_merchant_order = (int)Configuration::get('MA_MERCHANT_ORDER'); $this->_merchant_oos = (int)Configuration::get('MA_MERCHANT_OOS'); $this->_customer_qty = (int)Configuration::get('MA_CUSTOMER_QTY'); } public function hookNewOrder($params) { if (!$this->_merchant_order OR empty($this->_merchant_mails)) return; // Getting differents vars $id_lang = (int)Configuration::get('PS_LANG_DEFAULT'); $currency = $params['currency']; $configuration = Configuration::getMultiple(array('PS_SHOP_EMAIL', 'PS_MAIL_METHOD', 'PS_MAIL_SERVER', 'PS_MAIL_USER', 'PS_MAIL_PASSWD', 'PS_SHOP_NAME')); $order = $params['order']; $customer = $params['customer']; $delivery = new Address((int)($order->id_address_delivery)); $invoice = new Address((int)($order->id_address_invoice)); $order_date_text = Tools::displayDate($order->date_add, (int)($id_lang)); $carrier = new Carrier((int)($order->id_carrier)); $message = $order->getFirstMessage(); if (!$message OR empty($message)) $message = $this->l('No message'); $itemsTable = ''; $products = $params['order']->getProducts(); $customizedDatas = Product::getAllCustomizedDatas(intval($params['cart']->id)); Product::addCustomizationPrice($products, $customizedDatas); foreach ($products AS $key => $product) { $unit_price = $product['product_price_wt']; $price = $product['total_price']; $customizationText = ''; if (isset($customizedDatas[$product['product_id']][$product['product_attribute_id']])) { foreach ($customizedDatas[$product['product_id']][$product['product_attribute_id']] AS $customization) { if (isset($customization['datas'][_CUSTOMIZE_TEXTFIELD_])) foreach ($customization['datas'][_CUSTOMIZE_TEXTFIELD_] AS $text) $customizationText .= $text['name'].':'.' '.$text['value'].'
'; if (isset($customization['datas'][_CUSTOMIZE_FILE_])) $customizationText .= sizeof($customization['datas'][_CUSTOMIZE_FILE_]) .' '. Tools::displayError('image(s)').'
'; $customizationText .= '---
'; } $customizationText = rtrim($customizationText, '---
'); } $itemsTable .= ' '.$product['product_reference'].' '.$product['product_name'].(isset($product['attributes_small']) ? ' '.$product['attributes_small'] : '').(!empty($customizationText) ? '
'.$customizationText : '').'
'.Tools::displayPrice($unit_price, $currency, false).' '.(int)($product['product_quantity']).' '.Tools::displayPrice(($unit_price * $product['product_quantity']), $currency, false).' '; } foreach ($params['order']->getDiscounts() AS $discount) { $itemsTable .= ' '.$this->l('Voucher code:').' '.$discount['name'].' -'.Tools::displayPrice($discount['value'], $currency, false).' '; } if ($delivery->id_state) $delivery_state = new State((int)($delivery->id_state)); if ($invoice->id_state) $invoice_state = new State((int)($invoice->id_state)); // Filling-in vars for email $template = 'new_order'; $subject = $this->l('New order', $id_lang); $templateVars = array( '{firstname}' => $customer->firstname, '{lastname}' => $customer->lastname, '{email}' => $customer->email, '{delivery_block_txt}' => $this->_getFormatedAddress($delivery, "\n"), '{invoice_block_txt}' => $this->_getFormatedAddress($invoice, "\n"), '{delivery_block_html}' => $this->_getFormatedAddress($delivery, "
", array( 'firstname' => '%s', 'lastname' => '%s')), '{invoice_block_html}' => $this->_getFormatedAddress($invoice, "
", array( 'firstname' => '%s', 'lastname' => '%s')), '{delivery_company}' => $delivery->company, '{delivery_firstname}' => $delivery->firstname, '{delivery_lastname}' => $delivery->lastname, '{delivery_address1}' => $delivery->address1, '{delivery_address2}' => $delivery->address2, '{delivery_city}' => $delivery->city, '{delivery_postal_code}' => $delivery->postcode, '{delivery_country}' => $delivery->country, '{delivery_state}' => $delivery->id_state ? $delivery_state->name : '', '{delivery_phone}' => $delivery->phone, '{delivery_other}' => $delivery->other, '{invoice_company}' => $invoice->company, '{invoice_firstname}' => $invoice->firstname, '{invoice_lastname}' => $invoice->lastname, '{invoice_address2}' => $invoice->address2, '{invoice_address1}' => $invoice->address1, '{invoice_city}' => $invoice->city, '{invoice_postal_code}' => $invoice->postcode, '{invoice_country}' => $invoice->country, '{invoice_state}' => $invoice->id_state ? $invoice_state->name : '', '{invoice_phone}' => $invoice->phone, '{invoice_other}' => $invoice->other, '{order_name}' => sprintf("%06d", $order->id), '{shop_name}' => Configuration::get('PS_SHOP_NAME'), '{date}' => $order_date_text, '{carrier}' => (($carrier->name == '0') ? Configuration::get('PS_SHOP_NAME') : $carrier->name), '{payment}' => Tools::substr($order->payment, 0, 32), '{items}' => $itemsTable, '{total_paid}' => Tools::displayPrice($order->total_paid, $currency), '{total_products}' => Tools::displayPrice($order->getTotalProductsWithTaxes(), $currency), '{total_discounts}' => Tools::displayPrice($order->total_discounts, $currency), '{total_shipping}' => Tools::displayPrice($order->total_shipping, $currency), '{total_wrapping}' => Tools::displayPrice($order->total_wrapping, $currency), '{currency}' => $currency->sign, '{message}' => $message ); $iso = Language::getIsoById((int)($id_lang)); if (file_exists(dirname(__FILE__).'/mails/'.$iso.'/'.$template.'.txt') AND file_exists(dirname(__FILE__).'/mails/'.$iso.'/'.$template.'.html')) Mail::Send($id_lang, $template, $subject, $templateVars, explode(self::__MA_MAIL_DELIMITOR__, $this->_merchant_mails), NULL, $configuration['PS_SHOP_EMAIL'], $configuration['PS_SHOP_NAME'], NULL, NULL, dirname(__FILE__).'/mails/'); } /* ** Generate correctly the address for an email */ private function _getFormatedAddress(Address $the_address, $line_sep, $fields_style = array()) { return AddressFormat::generateAddress($the_address, array('avoid' => array()), $line_sep, ' ', $fields_style); } public function hookProductOutOfStock($params) { global $smarty, $cookie; if (!$this->_customer_qty) return ; $id_product = (int)($params['product']->id); $id_product_attribute = 0; if (!$cookie->isLogged()) $smarty->assign('email', 1); else { $id_customer = (int)($params['cookie']->id_customer); if ($this->customerHasNotification($id_customer, $id_product, $id_product_attribute)) return ; } $smarty->assign(array( 'id_product' => $id_product, 'id_product_attribute' => $id_product_attribute)); return $this->display(__FILE__, 'product.tpl'); } public function customerHasNotification($id_customer, $id_product, $id_product_attribute) { $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(' SELECT * FROM `'._DB_PREFIX_.'mailalert_customer_oos` WHERE `id_customer` = '.(int)($id_customer).' AND `id_product` = '.(int)($id_product).' AND `id_product_attribute` = '.(int)($id_product_attribute)); return sizeof($result); } public function hookUpdateQuantity($params) { global $cookie; if (is_object($params['product'])) $params['product'] = get_object_vars($params['product']); if (is_array($params['product']['name'])) { $params['product']['name'] = $params['product']['name'][(int)Configuration::get('PS_LANG_DEFAULT')]; } if (isset($params['product']['id_product'])) $params['product']['id'] = (int)$params['product']['id_product']; $qty = (int)$params['product']['quantity']; if ($qty <= (int)(Configuration::get('MA_LAST_QTIES')) AND !(!$this->_merchant_oos OR empty($this->_merchant_mails)) AND Configuration::get('PS_STOCK_MANAGEMENT')) { $templateVars = array( '{qty}' => $qty, '{last_qty}' => (int)(Configuration::get('MA_LAST_QTIES')), '{product}' => strval($params['product']['name']).(isset($params['product']['attributes_small']) ? ' '.$params['product']['attributes_small'] : '')); $id_lang = (is_object($cookie) AND isset($cookie->id_lang)) ? (int)$cookie->id_lang : (int)Configuration::get('PS_LANG_DEFAULT'); $iso = Language::getIsoById((int)$id_lang); if ($params['product']['active'] == 1) { if (file_exists(dirname(__FILE__).'/mails/'.$iso.'/productoutofstock.txt') AND file_exists(dirname(__FILE__).'/mails/'.$iso.'/productoutofstock.html')) Mail::Send((int)Configuration::get('PS_LANG_DEFAULT'), 'productoutofstock', Mail::l('Product out of stock'), $templateVars, explode(self::__MA_MAIL_DELIMITOR__, $this->_merchant_mails), NULL, strval(Configuration::get('PS_SHOP_EMAIL')), strval(Configuration::get('PS_SHOP_NAME')), NULL, NULL, dirname(__FILE__).'/mails/'); } } if ($this->_customer_qty AND $params['product']['quantity'] > 0) $this->sendCustomerAlert((int)$params['product']['id'], 0); } public function hookUpdateProduct($params) { /* We specify 0 as an id_product_attribute because this hook is called when the main product is updated */ if ($this->_customer_qty AND $params['product']->quantity > 0) $this->sendCustomerAlert((int)$params['product']->id, 0); } public function hookUpdateProductAttribute($params) { $result = Db::getInstance()->getRow(' SELECT `id_product`, `quantity` FROM `'._DB_PREFIX_.'product_attribute` WHERE `id_product_attribute` = '.(int)$params['id_product_attribute']); if ($this->_customer_qty AND $result['quantity'] > 0) $this->sendCustomerAlert((int)$result['id_product'], (int)$params['id_product_attribute']); } public function sendCustomerAlert($id_product, $id_product_attribute) { global $cookie, $link; $link = new Link(); $customers = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(' SELECT id_customer, customer_email FROM `'._DB_PREFIX_.'mailalert_customer_oos` WHERE `id_product` = '.(int)$id_product.' AND `id_product_attribute` = '.(int)$id_product_attribute); $product = new Product((int)$id_product, false, (int)$cookie->id_lang); $templateVars = array( '{product}' => (is_array($product->name) ? $product->name[(int)Configuration::get('PS_LANG_DEFAULT')] : $product->name), '{product_link}' => $link->getProductLink($product) ); foreach ($customers AS $cust) { if ($cust['id_customer']) { $customer = new Customer((int)$cust['id_customer']); $customer_email = $customer->email; $customer_id = (int)$customer->id; } else { $customer_email = $cust['customer_email']; $customer_id = 0; } $id_lang = (is_object($cookie) AND isset($cookie->id_lang)) ? (int)$cookie->id_lang : (int)Configuration::get('PS_LANG_DEFAULT'); $iso = Language::getIsoById((int)$id_lang); if (file_exists(dirname(__FILE__).'/mails/'.$iso.'/customer_qty.txt') AND file_exists(dirname(__FILE__).'/mails/'.$iso.'/customer_qty.html')) Mail::Send((int)(Configuration::get('PS_LANG_DEFAULT')), 'customer_qty', Mail::l('Product available'), $templateVars, strval($customer_email), NULL, strval(Configuration::get('PS_SHOP_EMAIL')), strval(Configuration::get('PS_SHOP_NAME')), NULL, NULL, dirname(__FILE__).'/mails/'); if ($customer_id) $customer_email = 0; self::deleteAlert((int)$customer_id, strval($customer_email), (int)$id_product, (int)$id_product_attribute); } } public function hookCustomerAccount($params) { return $this->_customer_qty ? $this->display(__FILE__, 'my-account.tpl') : NULL; } public function hookMyAccountBlock($params) { return $this->hookCustomerAccount($params); } public function getContent() { $this->_html = '

'.$this->displayName.'

'; $this->_postProcess(); $this->_displayForm(); return $this->_html; } private function _displayForm() { global $currentIndex; $tab = Tools::getValue('tab'); $token = Tools::getValue('token'); $this->_html .= '
'.$this->l('Customer notification').'
_customer_qty) == 1 ? 'checked' : '').'>  

'.$this->l('Merchant notification').'
_merchant_order) == 1 ? 'checked' : '').'>  
_merchant_oos) == 1 ? 'checked' : '').'>  

'.$this->l('Quantity for which a product is regarded as out of stock').'

'.$this->l('One e-mail address per line').'
'.$this->l('e.g.,').' bob@example.com
 
'; } private function _postProcess() { if (Tools::isSubmit('submitMACustomer')) { if (!Configuration::updateValue('MA_CUSTOMER_QTY', (int)(Tools::getValue('mA_customer_qty')))) $this->_html .= '
'.$this->l('Cannot update settings').'
'; else $this->_html .= '
'.$this->l('Confirmation').''.$this->l('Settings updated').'
'; } elseif (Tools::isSubmit('submitMAMerchant')) { $emails = strval(Tools::getValue('ma_merchant_mails')); if (!$emails OR empty($emails)) $this->_html .= '
'.$this->l('Please type one (or more) e-mail address').'
'; else { $emails = explode("\n", $emails); foreach ($emails AS $k => $email) { $email = trim($email); if (!empty($email) AND !Validate::isEmail($email)) return ($this->_html .= '
'.$this->l('Invalid e-mail:').' '.$email.'
'); if (!empty($email) AND sizeof($email)) $emails[$k] = $email; else unset($emails[$k]); } $emails = implode(self::__MA_MAIL_DELIMITOR__, $emails); if (!Configuration::updateValue('MA_MERCHANT_MAILS', strval($emails))) $this->_html .= '
'.$this->l('Cannot update settings').'
'; elseif (!Configuration::updateValue('MA_MERCHANT_ORDER', (int)(Tools::getValue('mA_merchand_order')))) $this->_html .= '
'.$this->l('Cannot update settings').'
'; elseif (!Configuration::updateValue('MA_MERCHANT_OOS', (int)(Tools::getValue('mA_merchand_oos')))) $this->_html .= '
'.$this->l('Cannot update settings').'
'; elseif (!Configuration::updateValue('MA_LAST_QTIES', (int)(Tools::getValue('MA_LAST_QTIES')))) $this->_html .= '
'.$this->l('Cannot update settings').'
'; else $this->_html .= '
'.$this->l('Confirmation').''.$this->l('Settings updated').'
'; } } $this->_refreshProperties(); } public static function getProductsAlerts($id_customer, $id_lang) { if (!Validate::isUnsignedId($id_customer) OR !Validate::isUnsignedId($id_lang) ) die (Tools::displayError()); $products = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(' SELECT ma.`id_product`, p.`quantity` AS product_quantity, pl.`name`, ma.`id_product_attribute` FROM `'._DB_PREFIX_.'mailalert_customer_oos` ma JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = ma.`id_product` JOIN `'._DB_PREFIX_.'product_lang` pl ON pl.`id_product` = ma.`id_product` WHERE p.`active` = 1 AND ma.`id_customer` = '.(int)($id_customer).' AND pl.`id_lang` = '.(int)($id_lang)); if (empty($products) === true OR !sizeof($products)) return array(); for ($i = 0; $i < sizeof($products); ++$i) { $obj = new Product((int)($products[$i]['id_product']), false, (int)($id_lang)); if (!Validate::isLoadedObject($obj)) continue; if (isset($products[$i]['id_product_attribute']) AND Validate::isUnsignedInt($products[$i]['id_product_attribute'])) { $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(' SELECT al.`name` AS attribute_name FROM `'._DB_PREFIX_.'product_attribute_combination` pac LEFT JOIN `'._DB_PREFIX_.'attribute` a ON (a.`id_attribute` = pac.`id_attribute`) LEFT JOIN `'._DB_PREFIX_.'attribute_group` ag ON (ag.`id_attribute_group` = a.`id_attribute_group`) LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)($id_lang).') LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)($id_lang).') LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pac.`id_product_attribute` = pa.`id_product_attribute`) WHERE pac.`id_product_attribute` = '.(int)($products[$i]['id_product_attribute'])); $products[$i]['attributes_small'] = ''; if ($result) foreach ($result AS $k => $row) $products[$i]['attributes_small'] .= $row['attribute_name'].', '; $products[$i]['attributes_small'] = rtrim($products[$i]['attributes_small'], ', '); // cover $attrgrps = $obj->getAttributesGroups((int)($id_lang)); foreach ($attrgrps AS $attrgrp) if ($attrgrp['id_product_attribute'] == (int)($products[$i]['id_product_attribute']) AND $images = Product::_getAttributeImageAssociations((int)($attrgrp['id_product_attribute']))) { $products[$i]['cover'] = $obj->id.'-'.array_pop($images); break; } } if (!isset($products[$i]['cover']) OR !$products[$i]['cover']) { $images = $obj->getImages((int)($id_lang)); foreach ($images AS $k => $image) if ($image['cover']) { $products[$i]['cover'] = $obj->id.'-'.$image['id_image']; break; } } if (!isset($products[$i]['cover'])) $products[$i]['cover'] = Language::getIsoById($id_lang).'-default'; $products[$i]['link'] = $obj->getLink(); $products[$i]['link_rewrite'] = $obj->link_rewrite; } return ($products); } public static function deleteAlert($id_customer, $customer_email, $id_product, $id_product_attribute) { return Db::getInstance()->Execute(' DELETE FROM `'._DB_PREFIX_.'mailalert_customer_oos` WHERE `id_customer` = '.(int)($id_customer).' AND `customer_email` = \''.pSQL($customer_email).'\' AND `id_product` = '.(int)($id_product).' AND `id_product_attribute` = '.(int)($id_product_attribute)); } public function hookDeleteProduct($params) { Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'mailalert_customer_oos` WHERE `id_product` = '.(int)$params['product']->id); } public function hookDeleteProductAttribute($params) { if ($params['deleteAllAttributes']) Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'mailalert_customer_oos` WHERE `id_product` = '.(int)$params['id_product']); else Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'mailalert_customer_oos` WHERE `id_product_attribute` = '.(int)$params['id_product_attribute'].' AND `id_product` = '.(int)$params['id_product']); } }