name = 'packetery'; $this->tab = 'shipping_logistics'; $this->version = '1.18'; $this->limited_countries = array('cz', 'sk', 'pl', 'hu', 'de', 'ro'); parent::__construct(); $this->author = $this->l('Packetery, Ltd.'); $this->displayName = $this->l('Packetery'); $this->description = $this->l( 'Offers your customers the option to choose pick-up point in Packetery network, and export orders to Packetery system.' ); $this->module_key = 'aa9b6f2b47192e6caae86b500177a861'; $this->currency_conversion = array( self::CC_PRESTASHOP => $this->l('Use PrestaShop\'s currency conversion'), self::CC_CNB => $this->l('Use CNB rates with optional margin'), self::CC_FIXED => $this->l('Use fixed conversion rate'), ); // This is only used in admin of modules, and we're accessing Packetery API here, so don't do that elsewhere. if (self::_isInstalled($this->name) && strpos($_SERVER['REQUEST_URI'], 'tab=AdminModules') !== false) { $errors = array(); $this->configuration_errors($errors); foreach ($errors as $error) { $this->warning .= $error; } } } public static function _isInstalled($module_name) { if (method_exists("Packetery", "isInstalled")) { return self::isInstalled($module_name); } else { return true; } } private static function transportMethod() { if (extension_loaded('curl')) { $have_curl = true; //$curl_version = curl_version(); //$have_curl_ssl = ($curl_version['features'] & CURL_VERSION_SSL); } if (ini_get('allow_url_fopen')) { $have_url_fopen = true; //$have_https_fopen = ($have_url_fopen && extension_loaded('openssl') && // function_exists('stream_context_create')); } // Disabled - more trouble than it's worth //if ($have_curl_ssl) return 'curls'; //if ($have_https_fopen) return 'fopens'; if ($have_curl) { return 'curl'; } if ($have_url_fopen) { return 'fopen'; } return false; } public function configuration_errors(&$error = null) { $error = array(); $have_error = false; $fn = _PS_MODULE_DIR_ . "packetery/views/js/write-test.js"; @touch($fn); if (!is_writable($fn)) { $error[] = $this->l( 'The Packetery module folder must be writable for the branch selection to work properly.' ); $have_error = true; } if (!self::transportMethod()) { $error[] = $this->l( 'No way to access Packetery API is available on the web server: please allow CURL module or allow_url_fopen setting.' ); $have_error = true; } $key = Configuration::get('PACKETERY_API_KEY'); $test = "http://www.zasilkovna.cz/api/$key/test"; if (!$key) { $error[] = $this->l('Packetery API key is not set.'); $have_error = true; } elseif (!$error) { if ($this->fetch($test) != 1) { $error[] = $this->l('Cannot access Packetery API with specified key. Possibly the API key is wrong.'); $have_error = true; } else { $data = Tools::jsonDecode( $this->fetch("http://www.zasilkovna.cz/api/$key/version-check-prestashop?my=" . $this->version) ); if (self::compareVersions($data->version, $this->version) > 0) { $cookie = Context::getContext()->cookie; $def_lang = (int)($cookie->id_lang ? $cookie->id_lang : Configuration::get('PS_LANG_DEFAULT')); $def_lang_iso = Language::getIsoById($def_lang); $error[] = $this->l('New version of Prestashop Packetery module is available.') . ' ' . $data->message->$def_lang_iso; } } } return $have_error; } public function compareVersions($v1, $v2) { return array_reduce( array_map( create_function('$a,$b', 'return $a - $b;'), explode('.', $v1), explode('.', $v2) ), create_function('$a,$b', 'return ($a ? $a : $b);') ); } public function install() { $sql = array(); $db = Db::getInstance(); // backup possible old order table if (count($db->executeS('show tables like "' . _DB_PREFIX_ . 'packetery_order"')) > 0) { $db->execute('rename table `' . _DB_PREFIX_ . 'packetery_order` to `'. _DB_PREFIX_ .'packetery_order_old`'); $have_old_table = true; } else { $have_old_table = false; } // create tables if (!defined('_MYSQL_ENGINE_')) { define('_MYSQL_ENGINE_', 'MyISAM'); } include(dirname(__FILE__) . '/sql-install.php'); foreach ($sql as $s) { if (!$db->execute($s)) { return false; } } // copy data from old order table if ($have_old_table) { $fields = array(); foreach ($db->executeS('show columns from `' . _DB_PREFIX_ . 'packetery_order_old`') as $field) { $fields[] = $field['Field']; } $db->execute( 'insert into `' . _DB_PREFIX_ . 'packetery_order`(`' . implode('`, `', $fields) . '`) select * from `' . _DB_PREFIX_ . 'packetery_order_old`' ); $db->execute('drop table `' . _DB_PREFIX_ . 'packetery_order_old`'); } // module itself and hooks if (!parent::install() || !$this->registerHook('extraCarrier') || !$this->registerHook('updateCarrier') || !$this->registerHook('newOrder') || !$this->registerHook('header') || !$this->registerHook('adminOrder') ) { return false; } // for PrestaShop >= 1.4.0.2 there is one-page-checkout, more hooks are required $v = explode('.', _PS_VERSION_); if (_PS_VERSION_ > '1.4.0' || (array_slice($v, 0, 3) == array(1, 4, 0) && $v[3] >= 2)) { if (!$this->registerHook('processCarrier') || !$this->registerHook('paymentTop') ) { return false; } } // optional hooks (allow fail for older versions of PrestaShop) $this->registerHook('orderDetailDisplayed'); $this->registerHook('backOfficeTop'); $this->registerHook('beforeCarrier'); $this->registerHook('displayMobileHeader'); // create admin tab under Orders $db->execute( 'insert into `' . _DB_PREFIX_ . 'tab` (id_parent, class_name, module, position) select id_parent, "AdminOrderPacketery", "packetery", coalesce(max(position) + 1, 0) from `' . _DB_PREFIX_ . 'tab` pt where id_parent=(select if (id_parent>0, id_parent, id_tab) from `' . _DB_PREFIX_ . 'tab` as tp where tp.class_name="AdminOrders") group by id_parent' ); $tab_id = $db->insert_id(); $tab_name = array('en' => 'Packetery', 'cs' => 'Zásilkovna', 'sk' => 'Zásielkovňa'); foreach (Language::getLanguages(false) as $language) { $db->execute( 'insert into `' . _DB_PREFIX_ . 'tab_lang` (id_tab, id_lang, name) values(' . $tab_id . ', ' . $language['id_lang'] . ', "' . pSQL($tab_name[$language['iso_code']] ? $tab_name[$language['iso_code']] : $tab_name['en']) . '")' ); } if (!Tab::initAccess($tab_id)) { return false; } return true; } public function uninstall() { foreach (array('PACKETERY_API_KEY', 'PACKETERY_ESHOP_DOMAIN') as $key) { Configuration::deleteByName($key); } // remove admin tab $db = Db::getInstance(); if ($tab_id = $db->getValue( 'select id_tab from `' . _DB_PREFIX_ . 'tab` where class_name="AdminOrderPacketery"' ) ) { $db->execute('delete from `' . _DB_PREFIX_ . 'tab` WHERE id_tab=' . $tab_id); $db->execute('delete from `' . _DB_PREFIX_ . 'tab_lang` WHERE id_tab=' . $tab_id); $db->execute('delete from `' . _DB_PREFIX_ . 'access` WHERE id_tab=' . $tab_id); } // mark carriers deleted $db->execute( 'update `' . _DB_PREFIX_ . 'carrier` set deleted=1 where external_module_name="packetery" or id_carrier in (select id_carrier from `' . _DB_PREFIX_ . 'packetery_carrier`)' ); // remove our carrier and payment table, keep order table for reinstall $db->execute('drop table if exists `' . _DB_PREFIX_ . 'packetery_carrier`'); $db->execute('drop table if exists `' . _DB_PREFIX_ . 'packetery_payment`'); $db->execute('drop table if exists `' . _DB_PREFIX_ . 'packetery_address_delivery`'); // module itself and hooks if (!parent::uninstall() || !$this->unregisterHook('beforeCarrier') || !$this->unregisterHook('extraCarrier') || !$this->unregisterHook('updateCarrier') || !$this->unregisterHook('newOrder') || !$this->unregisterHook('header') || !$this->unregisterHook('processCarrier') || !$this->unregisterHook('orderDetailDisplayed') || !$this->unregisterHook('adminOrder') || !$this->unregisterHook('paymentTop') || !$this->unregisterHook('backOfficeTop') ) { return false; } return true; } private function cConfigurationPost() { if (Tools::getIsset('packetery_api_key') && Tools::getValue('packetery_api_key')) { if (trim(Tools::getValue('packetery_api_key')) != Configuration::get('PACKETERY_API_KEY')) { Configuration::updateValue('PACKETERY_API_KEY', trim(Tools::getValue('packetery_api_key'))); @unlink(_PS_MODULE_DIR_ . "packetery/views/js/api.js"); @clearstatcache(); } } if (Tools::getIsset('packetery_eshop_domain') && Tools::getValue('packetery_eshop_domain')) { Configuration::updateValue('PACKETERY_ESHOP_DOMAIN', trim(Tools::getValue('packetery_eshop_domain'))); } } public function cAddCarrierPost() { $db = Db::getInstance(); if (!Tools::getIsset('packetery_add_carrier') || !Tools::getValue('packetery_add_carrier')) { return; } $carrier = new Carrier(); $carrier->name = Tools::getValue('packetery_carrier_name'); $carrier->active = true; $carrier->shipping_method = defined('Carrier::SHIPPING_METHOD_WEIGHT') ? Carrier::SHIPPING_METHOD_WEIGHT : 1; $carrier->deleted = 0; $carrier->range_behavior = true; // true disables this carrier if outside weight range $carrier->is_module = false; $carrier->external_module_name = "packetery"; $carrier->need_range = true; foreach (Language::getLanguages(true) as $language) { if (Tools::getIsset('delay_' . $language['id_lang']) && Tools::getValue('delay_' . $language['id_lang'])) { $carrier->delay[$language['id_lang']] = Tools::getValue('delay_' . $language['id_lang']); } } if (!$carrier->add()) { return false; } $country = ""; $countries = Tools::getValue('packetery_carrier_country'); foreach ($countries as $key => $countryCode) { $country .= $countryCode; if ($key != count($countries) - 1) { $country .= ','; } } $db->execute( 'insert into `' . _DB_PREFIX_ . 'packetery_carrier` set id_carrier=' . ((int)$carrier->id) . ', country="' . pSQL($country ? $country : 'cz,sk') . '", list_type=' . ((int)Tools::getValue('packetery_carrier_list_type')) . ', is_cod=' . (Tools::getIsset('packetery_carrier_is_cod') ? (int)Tools::getValue('packetery_carrier_is_cod') : 0) ); foreach (Group::getGroups(true) as $group) { $db->autoExecute( _DB_PREFIX_ . 'carrier_group', array( 'id_carrier' => (int)$carrier->id, 'id_group' => (int)$group['id_group'] ), 'INSERT' ); } $rangeWeight = new RangeWeight(); $rangeWeight->id_carrier = $carrier->id; $rangeWeight->delimiter1 = '0'; $rangeWeight->delimiter2 = '5'; $rangeWeight->add(); $rangePrice = new RangePrice(); $rangePrice->id_carrier = $carrier->id; $rangePrice->delimiter1 = '0'; $rangePrice->delimiter2 = '1000000'; $rangePrice->add(); $zones = Zone::getZones(true); foreach ($zones as $zone) { $db->autoExecute( _DB_PREFIX_ . 'carrier_zone', array( 'id_carrier' => (int)$carrier->id, 'id_zone' => (int)$zone['id_zone'] ), 'INSERT' ); $db->autoExecuteWithNullValues( _DB_PREFIX_ . 'delivery', array( 'id_carrier' => (int)$carrier->id, 'id_range_price' => (int)$rangePrice->id, 'id_range_weight' => null, 'id_zone' => (int)$zone['id_zone'], 'price' => '0' ), 'INSERT' ); $db->autoExecuteWithNullValues( _DB_PREFIX_ . 'delivery', array( 'id_carrier' => (int)$carrier->id, 'id_range_price' => null, 'id_range_weight' => (int)$rangeWeight->id, 'id_zone' => (int)$zone['id_zone'], 'price' => '0' ), 'INSERT' ); } if (Tools::getIsset('packetery_carrier_logo') && Tools::strlen(Tools::getValue('packetery_carrier_logo')) == 2) { copy( dirname(__FILE__) . '/logo-' . Tools::getValue('packetery_carrier_logo') . '.jpg', _PS_SHIP_IMG_DIR_ . '/' . ((int)$carrier->id) . '.jpg' ); } } private function cConfiguration() { $html = ""; $html .= "
"; return $html; } private function listTypes() { return array( 1 => $this->l('Selection box only'), $this->l('Selection box with map'), $this->l('Selection box with direct details display') ); } private function cAddCarrier() { $html = ""; $html .= ""; return $html; } private function cListCarriersPost() { if (Tools::getIsset('packetery_remove_carrier') && Tools::getValue('packetery_remove_carrier')) { $db = Db::getInstance(); $db->execute( 'update `' . _DB_PREFIX_ . 'carrier` set deleted=1 where external_module_name="packetery" and id_carrier=' . ((int)Tools::getValue('packetery_remove_carrier')) ); } } private function cListCarriers() { $db = Db::getInstance(); $html = ""; $html .= ""; return $html; } private function cListPaymentsPost() { if (Tools::getIsset('packetery_payment_module') && Tools::getValue('packetery_payment_module')) { $db = Db::getInstance(); if ($db->getValue( 'select 1 from `' . _DB_PREFIX_ . 'packetery_payment` where module_name="' . pSQL(Tools::getValue('packetery_payment_module')) . '"' ) == 1 ) { $db->execute( 'update `' . _DB_PREFIX_ . 'packetery_payment` set is_cod=' . ((int)Tools::getValue('packetery_payment_is_cod')) . ' where module_name="' . pSQL(Tools::getValue('packetery_payment_module')) . '"' ); } else { $db->execute( 'insert into `' . _DB_PREFIX_ . 'packetery_payment` set is_cod=' . ((int)Tools::getValue('packetery_payment_is_cod')) . ', module_name="' . pSQL(Tools::getValue('packetery_payment_module')) . '"' ); } } } private function cListPayments() { $db = Db::getInstance(); $html = ""; $html .= ""; return $html; } private function cListAddressDeliveryCarriersPost() { if (!Tools::getIsset('address_delivery_carriers') || !Tools::getValue('address_delivery_carriers')) { return; } $data = (Tools::getIsset("data") && is_array(Tools::getValue("data")) ? Tools::getValue("data") : array()); $db = Db::getInstance(); $address_deliveries = self::addressDeliveries(); foreach ($data as $id_carrier => $attr) { if ($attr['id_branch']) { $a = $address_deliveries[$attr['id_branch']]; $db->execute( 'insert into `' . _DB_PREFIX_ . 'packetery_address_delivery`(id_carrier, id_branch, name_branch, currency_branch, is_cod) values(' . ((int)$id_carrier) . ', ' . ((int)$attr['id_branch']) . ', "' . pSQL($a->name) . '", "' . pSQL($a->currency) . '", ' . ((int)$attr['is_cod']) . ') on duplicate key update id_branch=' . ((int)$attr['id_branch']) . ', is_cod=' . ((int)$attr['is_cod']) . ', name_branch="' . pSQL($a->name) . '", currency_branch="' . pSQL($a->currency) . '"' ); } else { $db->execute( 'delete from `' . _DB_PREFIX_ . 'packetery_address_delivery` where id_carrier=' . ((int)$id_carrier) ); } } } private function cListAddressDeliveryCarriers() { $db = Db::getInstance(); $html = ""; $html .= ""; return $html; } public function getContent() { $this->ensureUpdatedAPI(); $this->cConfigurationPost(); $this->cAddCarrierPost(); $this->cListCarriersPost(); $this->cListPaymentsPost(); $this->cListAddressDeliveryCarriersPost(); $html = ''; $html .= '" . sprintf( $this->l( 'Selected packetery branch: %s' ), "" . $res['name_branch'] . "" ) . "
"; } public function hookOrderDetailDisplayed($params) { if (!($res = Db::getInstance()->getRow( 'SELECT o.name_branch FROM `' . _DB_PREFIX_ . 'packetery_order` o WHERE o.id_order = ' . ((int)$params['order']->id) )) ) { return; } return "" . sprintf( $this->l( 'Selected packetery branch: %s' ), "" . $res['name_branch'] . "" ) . "
"; } public function hookUpdateCarrier($params) { if ($params['id_carrier'] != $params['carrier']->id) { Db::getInstance()->execute( 'update `' . _DB_PREFIX_ . 'packetery_carrier` set id_carrier=' . ((int)$params['carrier']->id) . ' where id_carrier=' . ((int)$params['id_carrier']) ); } } public function hookDisplayMobileHeader($params) { return $this->hookHeader($params); } public function hookHeader($params) { if (!($file = basename(Tools::getValue('controller')))) { $file = str_replace('.php', '', basename($_SERVER['SCRIPT_NAME'])); } if (!in_array($file, array('order-opc', 'order', 'orderopc'))) { return ''; } $this->ensureUpdatedAPI(); return ''; } private function fetch($url) { $transportMethod = self::transportMethod(); if (Tools::substr($transportMethod, -1) == 's') { $url = preg_replace('/^http:/', 'https:', $url); $transportMethod = Tools::substr($transportMethod, 0, -1); $ssl = true; } else { $ssl = false; } switch($transportMethod) { case 'curl': $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_setopt($ch, CURLOPT_AUTOREFERER, false); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 10); if ($ssl) { curl_setopt($ch, CURLOPT_CAINFO, _MODULE_DIR_ . "packetery/godaddy.crt"); } $body = curl_exec($ch); if (curl_errno($ch) > 0) { return false; } return $body; case 'fopen': if (function_exists('stream_context_create')) { // set longer timeout here, because we cannot detect timeout errors $ctx = stream_context_create( array( 'http' => array( 'timeout' => 60 ), 'ssl' => array( 'cafile' => _MODULE_DIR_ . "packetery/godaddy.crt", 'verify_peer' => true ) ) ); return Tools::file_get_contents($url, false, $ctx); } return Tools::file_get_contents($url); default: return false; } } /* Try to update API JS file once a day. If it's older than five days and still can't update, then remove it - the e-shop owner must solve it. */ private function ensureUpdatedAPI() { $key = Configuration::get('PACKETERY_API_KEY'); $files = array( _PS_MODULE_DIR_ . "packetery/views/js/api.js" => "http://www.zasilkovna.cz/api/v3/$key/branch.js?lib_path=" . _MODULE_DIR_ . "packetery&sync_load=1&prestashop=1", _PS_MODULE_DIR_ . "packetery/address-delivery.xml" => "http://www.zasilkovna.cz/api/v3/$key/branch.xml?type=address-delivery" ); foreach ($files as $local => $remote) { if (date("d.m.Y", @filemtime($local)) != date("d.m.Y") && (!file_exists($local) || date("H") >= 1)) { if ($this->configuration_errors() || Tools::strlen($data = $this->fetch($remote)) <= 1024) { // if we have older data, then try again tomorrow and delete after 5 days // else keep trying with each load if (file_exists($local)) { $error_count = @Tools::file_get_contents($local . ".error"); if ($error_count > 5) { unlink($local); } else { touch($local); } @file_put_contents($local . ".error", $error_count + 1); } return; } file_put_contents($local, $data); @unlink($local . ".error"); } } } public static function addressDeliveries() { $res = array(); $fn = _PS_MODULE_DIR_ . "packetery/address-delivery.xml"; if (function_exists("simplexml_load_file") && file_exists($fn)) { $xml = simplexml_load_file($fn); foreach ($xml->branches->branch as $branch) { $res[(string)$branch->id] = (object)array( 'name' => (string)$branch->name, 'currency' => (string)$branch->currency, ); } if (function_exists('mb_convert_encoding')) { $fn = create_function( '$a,$b', 'return strcmp(mb_convert_encoding($a->name, "ascii", "utf-8"), mb_convert_encoding($b->name, "ascii", "utf-8"));' ); } else { $fn = create_function( '$a,$b', 'return strcmp($a->name, $b->name);' ); } uasort($res, $fn); } return $res; } public function hookPaymentTop($params) { $db = Db::getInstance(); $is_packetery_carrier = ($db->getValue( 'select 1 from `' . _DB_PREFIX_ . 'packetery_carrier` where id_carrier=' . ((int)$params['cart']->id_carrier) ) == 1); $has_selected_branch = ($db->getValue( 'select id_branch from `' . _DB_PREFIX_ . 'packetery_order` where id_cart=' . ((int)$params['cart']->id) ) > 0); if ($is_packetery_carrier && !$has_selected_branch) { $params['cart']->id_carrier = 0; } } public function hookProcessCarrier($params) { $this->hookPaymentTop($params); } public function hookBackOfficeTop($params) { $cookie = Context::getContext()->cookie; if ($cookie->packetery_seen_warning < 3) { $cookie->packetery_seen_warning++; $errors = array(); if (!$this->configuration_errors($errors) && count($errors) > 0) { return "