· 8 years ago · Jun 11, 2018, 03:56 AM
1<?php
2/*
3 * 2007-2016 PrestaShop
4 *
5 * NOTICE OF LICENSE
6 * This source file is subject to the Academic Free License (AFL 3.0)
7 * that is bundled with this package in the file LICENSE.txt.
8 * It is also available through the world-wide-web at this URL:
9 * http://opensource.org/licenses/afl-3.0.php* If you did not receive a copy of the license and are unable to
10 * obtain it through the world-wide-web, please send an email
11 * to license@prestashop.com so we can send you a copy immediately.
12 *
13 * DISCLAIMER
14 *
15 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
16 * versions in the future. If you wish to customize PrestaShop for your
17 * needs please refer to http://www.prestashop.com for more information.
18 *
19 * @author PrestaShop SA <contact@prestashop.com>
20 * @copyright 2007-2016 PrestaShop SA
21 * @version Release: $Revision: 7515 $
22 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
23 * International Registered Trademark & Property of PrestaShop SA
24 */
25
26if (!defined('_PS_VERSION_'))
27 exit;
28
29class Gsitemap extends Module
30{
31 const HOOK_ADD_URLS = 'gSitemapAppendUrls';
32
33 public $cron = false;
34 protected $sql_checks = array();
35
36 public function __construct()
37 {
38 $this->name = 'gsitemap';
39 $this->tab = 'seo';
40 $this->version = '3.2.2';
41 $this->author = 'PrestaShop';
42 $this->need_instance = 0;
43
44 parent::__construct();
45
46 $this->displayName = $this->l('Google sitemap');
47 $this->description = $this->l('Generate your Google sitemap file');
48
49 $this->type_array = array('home', 'meta', 'product', 'category', 'manufacturer', 'supplier', 'cms', 'module');
50
51 $metas = Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_.'meta` ORDER BY `id_meta` ASC');
52 $disabled_metas = explode(',', Configuration::get('GSITEMAP_DISABLE_LINKS'));
53 foreach ($metas as $meta)
54 if (in_array($meta['id_meta'], $disabled_metas))
55 if (($key = array_search($meta['page'], $this->type_array)) !== false)
56 unset($this->type_array[$key]);
57
58 }
59
60 /**
61 * Google Sitemap installation process:
62 *
63 * Step 1 - Pre-set Configuration option values
64 * Step 2 - Install the Addon and create a database table to store Sitemap files name by shop
65 *
66 * @return boolean Installation result
67 */
68 public function install()
69 {
70 foreach (array(
71 'GSITEMAP_PRIORITY_HOME' => 1.0,
72 'GSITEMAP_PRIORITY_PRODUCT' => 0.9,
73 'GSITEMAP_PRIORITY_CATEGORY' => 0.8,
74 'GSITEMAP_PRIORITY_MANUFACTURER' => 0.7,
75 'GSITEMAP_PRIORITY_SUPPLIER' => 0.6,
76 'GSITEMAP_PRIORITY_CMS' => 0.5,
77 'GSITEMAP_FREQUENCY' => 'weekly',
78 'GSITEMAP_CHECK_IMAGE_FILE' => false,
79 'GSITEMAP_LAST_EXPORT' => false
80 ) as $key => $val)
81 if (!Configuration::updateValue($key, $val))
82 return false;
83
84 return parent::install() &&
85 Db::getInstance()->Execute('CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'gsitemap_sitemap` (`link` varchar(255) DEFAULT NULL, `id_shop` int(11) DEFAULT 0) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;') &&
86 $this->_installHook();
87 }
88
89 /**
90 * Registers hook(s)
91 *
92 * @return boolean
93 */
94 protected function _installHook()
95 {
96 $hook = new Hook();
97 $hook->name = self::HOOK_ADD_URLS;
98 $hook->title = 'GSitemap Append URLs';
99 $hook->description = 'This hook allows a module to add URLs to a generated sitemap';
100 $hook->position = true;
101 return $hook->save();
102 }
103
104 /**
105 * Google Sitemap uninstallation process:
106 *
107 * Step 1 - Remove Configuration option values from database
108 * Step 2 - Remove the database containing the generated Sitemap files names
109 * Step 3 - Uninstallation of the Addon itself
110 *
111 * @return boolean Uninstallation result
112 */
113 public function uninstall()
114 {
115 foreach (array(
116 'GSITEMAP_PRIORITY_HOME' => '',
117 'GSITEMAP_PRIORITY_PRODUCT' => '',
118 'GSITEMAP_PRIORITY_CATEGORY' => '',
119 'GSITEMAP_PRIORITY_MANUFACTURER' => '',
120 'GSITEMAP_PRIORITY_SUPPLIER' => '',
121 'GSITEMAP_PRIORITY_CMS' => '',
122 'GSITEMAP_FREQUENCY' => '',
123 'GSITEMAP_CHECK_IMAGE_FILE' => '',
124 'GSITEMAP_LAST_EXPORT' => ''
125 ) as $key => $val)
126 if (!Configuration::deleteByName($key))
127 return false;
128
129 $hook = new Hook(Hook::getIdByName(self::HOOK_ADD_URLS));
130 if (Validate::isLoadedObject($hook))
131 $hook->delete();
132
133 return parent::uninstall() && $this->removeSitemap();
134 }
135
136 /**
137 * Delete all the generated Sitemap files and drop the addon table.
138 * @return boolean
139 */
140 public function removeSitemap()
141 {
142 $links = Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_.'gsitemap_sitemap`');
143 if ($links)
144 foreach ($links as $link)
145 if (!@unlink($this->normalizeDirectory(_PS_ROOT_DIR_).$link['link']))
146 return false;
147 if (!Db::getInstance()->Execute('DROP TABLE `'._DB_PREFIX_.'gsitemap_sitemap`'))
148 return false;
149
150 return true;
151 }
152
153 public function getContent()
154 {
155 /* Store the posted parameters and generate a new Google Sitemap files for the current Shop */
156 if (Tools::isSubmit('SubmitGsitemap'))
157 {
158 Configuration::updateValue('GSITEMAP_FREQUENCY', pSQL(Tools::getValue('gsitemap_frequency')));
159 Configuration::updateValue('GSITEMAP_INDEX_CHECK', '');
160 Configuration::updateValue('GSITEMAP_CHECK_IMAGE_FILE', pSQL(Tools::getValue('gsitemap_check_image_file')));
161 $meta = '';
162 if (Tools::getValue('gsitemap_meta'))
163 $meta .= implode(', ', Tools::getValue('gsitemap_meta'));
164 Configuration::updateValue('GSITEMAP_DISABLE_LINKS', $meta);
165 $this->emptySitemap();
166 $this->createSitemap();
167 }
168 /* if no posted form and the variable [continue] is found in the HTTP request variable keep creating sitemap */
169 elseif (Tools::getValue('continue'))
170 $this->createSitemap();
171
172 /* Empty the Shop domain cache */
173 if (method_exists('ShopUrl', 'resetMainDomainCache'))
174 ShopUrl::resetMainDomainCache();
175
176 $this->context->smarty->assign(
177 array(
178 'gsitemap_form' => './index.php?tab=AdminModules&configure=gsitemap&token='.Tools::getAdminTokenLite('AdminModules').'&tab_module='.$this->tab.'&module_name=gsitemap',
179 'gsitemap_cron' => _PS_BASE_URL_._MODULE_DIR_.'gsitemap/gsitemap-cron.php?token='.substr(Tools::encrypt('gsitemap/cron'), 0, 10).'&id_shop='.$this->context->shop->id,
180 'gsitemap_feed_exists' => file_exists($this->normalizeDirectory(_PS_ROOT_DIR_).'index_sitemap.xml'),
181 'gsitemap_last_export' => Configuration::get('GSITEMAP_LAST_EXPORT'),
182 'gsitemap_frequency' => Configuration::get('GSITEMAP_FREQUENCY'),
183 'gsitemap_store_url' => 'http://'.Tools::getShopDomain(false, true).__PS_BASE_URI__,
184 'gsitemap_links' => Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_.'gsitemap_sitemap` WHERE id_shop = '.(int)$this->context->shop->id),
185 'store_metas' => Meta::getMetasByIdLang((int)$this->context->cookie->id_lang),
186 'gsitemap_disable_metas' => explode(',', Configuration::get('GSITEMAP_DISABLE_LINKS')),
187 'gsitemap_customer_limit' => array(
188 'max_exec_time' => (int)ini_get('max_execution_time'),
189 'memory_limit' => intval(ini_get('memory_limit'))
190 ),
191 'prestashop_ssl' => Configuration::get('PS_SSL_ENABLED'),
192 'gsitemap_check_image_file' => Configuration::get('GSITEMAP_CHECK_IMAGE_FILE'),
193 'shop' => $this->context->shop
194 )
195 );
196
197 return $this->display(__FILE__, 'views/templates/admin/configuration.tpl');
198 }
199
200 /**
201 * Delete all the generated Sitemap files from the files system and the database.
202 *
203 * @param int $id_shop
204 *
205 * @return bool
206 */
207 public function emptySitemap($id_shop = 0)
208 {
209 if (!isset($this->context))
210 $this->context = new Context();
211 if ($id_shop != 0)
212 $this->context->shop = new Shop((int)$id_shop);
213 $links = Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_.'gsitemap_sitemap` WHERE id_shop = '.(int)$this->context->shop->id);
214 if ($links)
215 {
216 foreach ($links as $link)
217 @unlink($this->normalizeDirectory(_PS_ROOT_DIR_).$link['link']);
218
219 return Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'gsitemap_sitemap` WHERE id_shop = '.(int)$this->context->shop->id);
220 }
221
222 return true;
223 }
224
225 /**
226 * @param array $link_sitemap contain all the links for the Google Sitemap file to be generated
227 * @param array $new_link contain the link elements
228 * @param string $lang language of link to add
229 * @param int $index index of the current Google Sitemap file
230 * @param int $i count of elements added to sitemap main array
231 * @param int $id_obj identifier of the object of the link to be added to the Gogle Sitemap file
232 *
233 * @return bool
234 */
235 public function _addLinkToSitemap(&$link_sitemap, $new_link, $lang, &$index, &$i, $id_obj)
236 {
237 if ($i <= 25000 && memory_get_usage() < 100000000)
238 {
239 $link_sitemap[] = $new_link;
240 $i++;
241
242 return true;
243 }
244 else
245 {
246 $this->_recursiveSitemapCreator($link_sitemap, $lang, $index);
247 if ($index % 20 == 0 && !$this->cron)
248 {
249 $this->context->smarty->assign(
250 array(
251 'gsitemap_number' => (int)$index,
252 'gsitemap_refresh_page' => './index.php?tab=AdminModules&configure=gsitemap&token='.Tools::getAdminTokenLite('AdminModules').'&tab_module='.$this->tab.'&module_name=gsitemap&continue=1&type='.$new_link['type'].'&lang='.$lang.'&index='.$index.'&id='.intval($id_obj).'&id_shop='.$this->context->shop->id
253 )
254 );
255
256 return false;
257 }
258 else if ($index % 20 == 0 && $this->cron)
259 {
260 header('Refresh: 5; url=http'.(Configuration::get('PS_SSL_ENABLED') ? 's' : '').'://'.Tools::getShopDomain(false, true).__PS_BASE_URI__.'modules/gsitemap/gsitemap-cron.php?continue=1&token='.substr(Tools::encrypt('gsitemap/cron'), 0, 10).'&type='.$new_link['type'].'&lang='.$lang.'&index='.$index.'&id='.intval($id_obj).'&id_shop='.$this->context->shop->id);
261 die();
262 }
263 else
264 {
265 if ($this->cron)
266 header('location: http'.(Configuration::get('PS_SSL_ENABLED') ? 's' : '').'://'.Tools::getShopDomain(false, true).__PS_BASE_URI__.'modules/gsitemap/gsitemap-cron.php?continue=1&token='.substr(Tools::encrypt('gsitemap/cron'), 0, 10).'&type='.$new_link['type'].'&lang='.$lang.'&index='.$index.'&id='.intval($id_obj).'&id_shop='.$this->context->shop->id);
267 else
268 {
269 $admin_folder = str_replace(_PS_ROOT_DIR_, '', _PS_ADMIN_DIR_);
270 $admin_folder = substr($admin_folder, 1);
271 header('location: http'.(Configuration::get('PS_SSL_ENABLED') ? 's' : '').'://'.Tools::getShopDomain(false, true).__PS_BASE_URI__.$admin_folder.'/index.php?tab=AdminModules&configure=gsitemap&token='.Tools::getAdminTokenLite('AdminModules').'&tab_module='.$this->tab.'&module_name=gsitemap&continue=1&type='.$new_link['type'].'&lang='.$lang.'&index='.$index.'&id='.intval($id_obj).'&id_shop='.$this->context->shop->id);
272 }
273 die();
274 }
275 }
276 }
277
278 /**
279 * Hydrate $link_sitemap with home link
280 *
281 * @param array $link_sitemap contain all the links for the Google Sitemap file to be generated
282 * @param string $lang language of link to add
283 * @param int $index index of the current Google Sitemap file
284 * @param int $i count of elements added to sitemap main array
285 *
286 * @return bool
287 */
288 protected function _getHomeLink(&$link_sitemap, $lang, &$index, &$i)
289 {
290 if (Configuration::get('PS_SSL_ENABLED') && Configuration::get('PS_SSL_ENABLED_EVERYWHERE'))
291 $protocol = 'https://';
292 else
293 $protocol = 'http://';
294
295 return $this->_addLinkToSitemap(
296 $link_sitemap, array(
297 'type' => 'home',
298 'page' => 'home',
299 'link' => $protocol.Tools::getShopDomainSsl(false).$this->context->shop->getBaseURI().(method_exists('Language', 'isMultiLanguageActivated') ? Language::isMultiLanguageActivated() ? $lang['iso_code'].'/' : '' : ''),
300 'image' => false
301 ), $lang['iso_code'], $index, $i, -1
302 );
303 }
304
305 /**
306 * Hydrate $link_sitemap with meta link
307 *
308 * @param array $link_sitemap contain all the links for the Google Sitemap file to be generated
309 * @param string $lang language of link to add
310 * @param int $index index of the current Google Sitemap file
311 * @param int $i count of elements added to sitemap main array
312 * @param int $id_meta meta object identifier
313 *
314 * @return bool
315 */
316 protected function _getMetaLink(&$link_sitemap, $lang, &$index, &$i, $id_meta = 0)
317 {
318 if (method_exists('ShopUrl', 'resetMainDomainCache'))
319 ShopUrl::resetMainDomainCache();
320 $link = new Link();
321 if (version_compare(_PS_VERSION_, '1.6', '>='))
322 $metas = Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_.'meta` WHERE `configurable` > 0 AND `id_meta` >= '.(int)$id_meta.' ORDER BY `id_meta` ASC');
323 else
324 $metas = Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_.'meta` WHERE `id_meta` >= '.(int)$id_meta.' ORDER BY `id_meta` ASC');
325 foreach ($metas as $meta)
326 {
327 $url = '';
328 if (!in_array($meta['id_meta'], explode(',', Configuration::get('GSITEMAP_DISABLE_LINKS'))))
329 {
330 $url_rewrite = Db::getInstance()->getValue('SELECT url_rewrite, id_shop FROM `'._DB_PREFIX_.'meta_lang` WHERE `id_meta` = '.(int)$meta['id_meta'].' AND `id_shop` ='.(int)$this->context->shop->id.' AND `id_lang` = '.(int)$lang['id_lang']);
331 Dispatcher::getInstance()->addRoute($meta['page'], (isset($url_rewrite) ? $url_rewrite : $meta['page']), $meta['page'], $lang['id_lang']);
332 $uri_path = Dispatcher::getInstance()->createUrl($meta['page'], $lang['id_lang'], array(), (bool)Configuration::get('PS_REWRITING_SETTINGS'));
333 $url .= Tools::getShopDomainSsl(true).(($this->context->shop->virtual_uri) ? __PS_BASE_URI__.$this->context->shop->virtual_uri : __PS_BASE_URI__).(Language::isMultiLanguageActivated() ? $lang['iso_code'].'/' : '').ltrim($uri_path, '/');
334
335 if (!$this->_addLinkToSitemap(
336 $link_sitemap, array(
337 'type' => 'meta',
338 'page' => $meta['page'],
339 'link' => $url,
340 'image' => false
341 ), $lang['iso_code'], $index, $i, $meta['id_meta']
342 ))
343 return false;
344 }
345 }
346
347 return true;
348 }
349
350 /**
351 * Hydrate $link_sitemap with products link
352 *
353 * @param array $link_sitemap contain all the links for the Google Sitemap file to be generated
354 * @param string $lang language of link to add
355 * @param int $index index of the current Google Sitemap file
356 * @param int $i count of elements added to sitemap main array
357 * @param int $id_product product object identifier
358 *
359 * @return bool
360 */
361 protected function _getProductLink(&$link_sitemap, $lang, &$index, &$i, $id_product = 0)
362 {
363 $link = new Link();
364 if (method_exists('ShopUrl', 'resetMainDomainCache'))
365 ShopUrl::resetMainDomainCache();
366
367 $products_id = Db::getInstance()->ExecuteS('SELECT `id_product` FROM `'._DB_PREFIX_.'product_shop` WHERE `id_product` >= '.intval($id_product).' AND `active` = 1 AND `visibility` != \'none\' AND `id_shop`='.$this->context->shop->id.' ORDER BY `id_product` ASC');
368
369 foreach ($products_id as $product_id)
370 {
371 $product = new Product((int)$product_id['id_product'], false, (int)$lang['id_lang']);
372
373 $url = $link->getProductLink($product, $product->link_rewrite, htmlspecialchars(strip_tags($product->category)), $product->ean13, (int)$lang['id_lang'], (int)$this->context->shop->id, 0, true);
374
375 $id_image = Product::getCover((int)$product_id['id_product']);
376 if (isset($id_image['id_image']))
377 {
378 $image_link = $this->context->link->getImageLink($product->link_rewrite, $product->id.'-'.(int)$id_image['id_image'], 'large_default');
379 $image_link = (!in_array(rtrim(Context::getContext()->shop->virtual_uri, '/'), explode('/', $image_link))) ? str_replace(
380 array(
381 'https',
382 Context::getContext()->shop->domain.Context::getContext()->shop->physical_uri
383 ), array(
384 'http',
385 Context::getContext()->shop->domain.Context::getContext()->shop->physical_uri.Context::getContext()->shop->virtual_uri
386 ), $image_link
387 ) : $image_link;
388 }
389 $file_headers = (Configuration::get('GSITEMAP_CHECK_IMAGE_FILE')) ? @get_headers($image_link) : true;
390 $image_product = array();
391 if (isset($image_link) && ($file_headers[0] != 'HTTP/1.1 404 Not Found' || $file_headers === true))
392 $image_product = array(
393 'title_img' => htmlspecialchars(strip_tags($product->name)),
394 'caption' => htmlspecialchars(strip_tags($product->description_short)),
395 'link' => $image_link
396 );
397 if (!$this->_addLinkToSitemap(
398 $link_sitemap, array(
399 'type' => 'product',
400 'page' => 'product',
401 'lastmod' => $product->date_upd,
402 'link' => $url,
403 'image' => $image_product
404 ), $lang['iso_code'], $index, $i, $product_id['id_product']
405 ))
406 return false;
407
408 unset($image_link);
409 }
410
411 return true;
412 }
413
414 /**
415 * Hydrate $link_sitemap with categories link
416 *
417 * @param array $link_sitemap contain all the links for the Google Sitemap file to be generated
418 * @param string $lang language of link to add
419 * @param int $index index of the current Google Sitemap file
420 * @param int $i count of elements added to sitemap main array
421 * @param int $id_category category object identifier
422 *
423 * @return bool
424 */
425 protected function _getCategoryLink(&$link_sitemap, $lang, &$index, &$i, $id_category = 0)
426 {
427 $link = new Link();
428 if (method_exists('ShopUrl', 'resetMainDomainCache'))
429 ShopUrl::resetMainDomainCache();
430
431 $categories_id = Db::getInstance()->ExecuteS(
432 'SELECT c.id_category FROM `'._DB_PREFIX_.'category` c
433 INNER JOIN `'._DB_PREFIX_.'category_shop` cs ON c.`id_category` = cs.`id_category`
434 WHERE c.`id_category` >= '.intval($id_category).' AND c.`active` = 1 AND c.`id_category` != 1 AND c.id_parent > 0 AND c.`id_category` > 0 AND cs.`id_shop` = '.(int)$this->context->shop->id.' ORDER BY c.`id_category` ASC'
435 );
436
437 foreach ($categories_id as $category_id)
438 {
439 $category = new Category((int)$category_id['id_category'], (int)$lang['id_lang']);
440 $url = $link->getCategoryLink($category, urlencode($category->link_rewrite), (int)$lang['id_lang']);
441
442 if ($category->id_image)
443 {
444 $image_link = $this->context->link->getCatImageLink($category->link_rewrite, (int)$category->id_image, 'category_default');
445 $image_link = (!in_array(rtrim(Context::getContext()->shop->virtual_uri, '/'), explode('/', $image_link))) ? str_replace(
446 array(
447 'https',
448 Context::getContext()->shop->domain.Context::getContext()->shop->physical_uri
449 ), array(
450 'http',
451 Context::getContext()->shop->domain.Context::getContext()->shop->physical_uri.Context::getContext()->shop->virtual_uri
452 ), $image_link
453 ) : $image_link;
454 }
455 $file_headers = (Configuration::get('GSITEMAP_CHECK_IMAGE_FILE')) ? @get_headers($image_link) : true;
456 $image_category = array();
457 if (isset($image_link) && ($file_headers[0] != 'HTTP/1.1 404 Not Found' || $file_headers === true))
458 $image_category = array(
459 'title_img' => htmlspecialchars(strip_tags($category->name)),
460 'link' => $image_link
461 );
462
463 if (!$this->_addLinkToSitemap(
464 $link_sitemap, array(
465 'type' => 'category',
466 'page' => 'category',
467 'lastmod' => $category->date_upd,
468 'link' => $url,
469 'image' => $image_category
470 ), $lang['iso_code'], $index, $i, (int)$category_id['id_category']
471 ))
472 return false;
473
474 unset($image_link);
475 }
476
477 return true;
478 }
479
480 /**
481 * return the link elements for the manufacturer object
482 *
483 * @param array $link_sitemap contain all the links for the Google Sitemap file to be generated
484 * @param string $lang language of link to add
485 * @param int $index index of the current Google Sitemap file
486 * @param int $i count of elements added to sitemap main array
487 * @param int $id_manufacturer manufacturer object identifier
488 *
489 * @return bool
490 */
491 protected function _getManufacturerLink(&$link_sitemap, $lang, &$index, &$i, $id_manufacturer = 0)
492 {
493 $link = new Link();
494 if (method_exists('ShopUrl', 'resetMainDomainCache'))
495 ShopUrl::resetMainDomainCache();
496 $manufacturers_id = Db::getInstance()->ExecuteS(
497 'SELECT m.`id_manufacturer` FROM `'._DB_PREFIX_.'manufacturer` m
498 INNER JOIN `'._DB_PREFIX_.'manufacturer_lang` ml on m.`id_manufacturer` = ml.`id_manufacturer`'.
499 ($this->tableColumnExists(_DB_PREFIX_.'manufacturer_shop') ? ' INNER JOIN `'._DB_PREFIX_.'manufacturer_shop` ms ON m.`id_manufacturer` = ms.`id_manufacturer` ' : '').
500 ' WHERE m.`active` = 1 AND m.`id_manufacturer` >= '.(int)$id_manufacturer.
501 ($this->tableColumnExists(_DB_PREFIX_.'manufacturer_shop') ? ' AND ms.`id_shop` = '.(int)$this->context->shop->id : '').
502 ' AND ml.`id_lang` = '.(int)$lang['id_lang'].
503 ' ORDER BY m.`id_manufacturer` ASC'
504 );
505 foreach ($manufacturers_id as $manufacturer_id)
506 {
507 $manufacturer = new Manufacturer((int)$manufacturer_id['id_manufacturer'], $lang['id_lang']);
508 $url = $link->getManufacturerLink($manufacturer, $manufacturer->link_rewrite, $lang['id_lang']);
509
510 $image_link = 'http'.(Configuration::get('PS_SSL_ENABLED') && Configuration::get('PS_SSL_ENABLED_EVERYWHERE') ? 's' : '').'://'.Tools::getMediaServer(_THEME_MANU_DIR_)._THEME_MANU_DIR_.((!file_exists(_PS_MANU_IMG_DIR_.'/'.(int)$manufacturer->id.'-medium_default.jpg')) ? $lang['iso_code'].'-default' : (int)$manufacturer->id).'-medium_default.jpg';
511 $image_link = (!in_array(rtrim(Context::getContext()->shop->virtual_uri, '/'), explode('/', $image_link))) ? str_replace(
512 array(
513 'https',
514 Context::getContext()->shop->domain.Context::getContext()->shop->physical_uri
515 ), array(
516 'http',
517 Context::getContext()->shop->domain.Context::getContext()->shop->physical_uri.Context::getContext()->shop->virtual_uri
518 ), $image_link
519 ) : $image_link;
520
521 $file_headers = (Configuration::get('GSITEMAP_CHECK_IMAGE_FILE')) ? @get_headers($image_link) : true;
522 $manifacturer_image = array();
523 if ($file_headers[0] != 'HTTP/1.1 404 Not Found' || $file_headers === true)
524 $manifacturer_image = array(
525 'title_img' => htmlspecialchars(strip_tags($manufacturer->name)),
526 'caption' => htmlspecialchars(strip_tags($manufacturer->short_description)),
527 'link' => $image_link
528 );
529 if (!$this->_addLinkToSitemap(
530 $link_sitemap, array(
531 'type' => 'manufacturer',
532 'page' => 'manufacturer',
533 'lastmod' => $manufacturer->date_upd,
534 'link' => $url,
535 'image' => $manifacturer_image
536 ), $lang['iso_code'], $index, $i, $manufacturer_id['id_manufacturer']
537 ))
538 return false;
539 }
540
541 return true;
542 }
543
544 /**
545 * @param array $link_sitemap contain all the links for the Google Sitemap file to be generated
546 * @param string $lang language of link to add
547 * @param int $index index of the current Google Sitemap file
548 * @param int $i count of elements added to sitemap main array
549 * @param int $id_supplier supplier object identifier
550 *
551 * @return bool
552 */
553 protected function _getSupplierLink(&$link_sitemap, $lang, &$index, &$i, $id_supplier = 0)
554 {
555 $link = new Link();
556 if (method_exists('ShopUrl', 'resetMainDomainCache'))
557 ShopUrl::resetMainDomainCache();
558 $suppliers_id = Db::getInstance()->ExecuteS(
559 'SELECT s.`id_supplier` FROM `'._DB_PREFIX_.'supplier` s
560 INNER JOIN `'._DB_PREFIX_.'supplier_lang` sl ON s.`id_supplier` = sl.`id_supplier` '.
561 ($this->tableColumnExists(_DB_PREFIX_.'supplier_shop') ? 'INNER JOIN `'._DB_PREFIX_.'supplier_shop` ss ON s.`id_supplier` = ss.`id_supplier`' : '').'
562 WHERE s.`active` = 1 AND s.`id_supplier` >= '.(int)$id_supplier.
563 ($this->tableColumnExists(_DB_PREFIX_.'supplier_shop') ? ' AND ss.`id_shop` = '.(int)$this->context->shop->id : '').'
564 AND sl.`id_lang` = '.(int)$lang['id_lang'].'
565 ORDER BY s.`id_supplier` ASC'
566 );
567 foreach ($suppliers_id as $supplier_id)
568 {
569 $supplier = new Supplier((int)$supplier_id['id_supplier'], $lang['id_lang']);
570 $url = $link->getSupplierLink($supplier, $supplier->link_rewrite, $lang['id_lang']);
571
572 $image_link = 'http://'.Tools::getMediaServer(_THEME_SUP_DIR_)._THEME_SUP_DIR_.((!file_exists(_THEME_SUP_DIR_.'/'.(int)$supplier->id.'-medium_default.jpg')) ? $lang['iso_code'].'-default' : (int)$supplier->id).'-medium_default.jpg';
573 $image_link = (!in_array(rtrim(Context::getContext()->shop->virtual_uri, '/'), explode('/', $image_link))) ? str_replace(
574 array(
575 'https',
576 Context::getContext()->shop->domain.Context::getContext()->shop->physical_uri
577 ), array(
578 'http',
579 Context::getContext()->shop->domain.Context::getContext()->shop->physical_uri.Context::getContext()->shop->virtual_uri
580 ), $image_link
581 ) : $image_link;
582
583 $file_headers = (Configuration::get('GSITEMAP_CHECK_IMAGE_FILE')) ? @get_headers($image_link) : true;
584 $supplier_image = array();
585 if ($file_headers[0] != 'HTTP/1.1 404 Not Found' || $file_headers === true)
586 $supplier_image = array(
587 'title_img' => htmlspecialchars(strip_tags($supplier->name)),
588 'link' => 'http'.(Configuration::get('PS_SSL_ENABLED') ? 's' : '').'://'.Tools::getMediaServer(_THEME_SUP_DIR_)._THEME_SUP_DIR_.((!file_exists(_THEME_SUP_DIR_.'/'.(int)$supplier->id.'-medium_default.jpg')) ? $lang['iso_code'].'-default' : (int)$supplier->id).'-medium_default.jpg'
589 );
590 if (!$this->_addLinkToSitemap(
591 $link_sitemap, array(
592 'type' => 'supplier',
593 'page' => 'supplier',
594 'lastmod' => $supplier->date_upd,
595 'link' => $url,
596 'image' => $supplier_image
597 ), $lang['iso_code'], $index, $i, $supplier_id['id_supplier']
598 ))
599 return false;
600 }
601
602 return true;
603 }
604
605 /**
606 * return the link elements for the CMS object
607 *
608 * @param array $link_sitemap contain all the links for the Google Sitemap file to be generated
609 * @param string $lang the language of link to add
610 * @param int $index the index of the current Google Sitemap file
611 * @param int $i the count of elements added to sitemap main array
612 * @param int $id_cms the CMS object identifier
613 *
614 * @return bool
615 */
616 protected function _getCmsLink(&$link_sitemap, $lang, &$index, &$i, $id_cms = 0)
617 {
618 $link = new Link();
619 if (method_exists('ShopUrl', 'resetMainDomainCache'))
620 ShopUrl::resetMainDomainCache();
621 $cmss_id = Db::getInstance()->ExecuteS(
622 'SELECT c.`id_cms` FROM `'._DB_PREFIX_.'cms` c INNER JOIN `'._DB_PREFIX_.'cms_lang` cl ON c.`id_cms` = cl.`id_cms` '.
623 ($this->tableColumnExists(_DB_PREFIX_.'supplier_shop') ? 'INNER JOIN `'._DB_PREFIX_.'cms_shop` cs ON c.`id_cms` = cs.`id_cms` ' : '').
624 'INNER JOIN `'._DB_PREFIX_.'cms_category` cc ON c.id_cms_category = cc.id_cms_category AND cc.active = 1
625 WHERE c.`active` =1 AND c.`indexation` =1 AND c.`id_cms` >= '.(int)$id_cms.
626 ($this->tableColumnExists(_DB_PREFIX_.'supplier_shop') ? ' AND cs.id_shop = '.(int)$this->context->shop->id : '').
627 ' AND cl.`id_lang` = '.(int)$lang['id_lang'].
628 ' ORDER BY c.`id_cms` ASC'
629 );
630
631 if (is_array($cmss_id))
632 foreach ($cmss_id as $cms_id)
633 {
634 $cms = new CMS((int)$cms_id['id_cms'], $lang['id_lang']);
635 $cms->link_rewrite = urlencode((is_array($cms->link_rewrite) ? $cms->link_rewrite[(int)$lang['id_lang']] : $cms->link_rewrite));
636 $url = $link->getCMSLink($cms, null, null, $lang['id_lang']);
637
638 if (!$this->_addLinkToSitemap(
639 $link_sitemap, array(
640 'type' => 'cms',
641 'page' => 'cms',
642 'link' => $url,
643 'image' => false
644 ), $lang['iso_code'], $index, $i, $cms_id['id_cms']
645 ))
646 return false;
647 }
648
649 return true;
650 }
651
652 /**
653 * Returns link elements generated by modules subscribes to hook gsitemap::HOOK_ADD_URLS
654 *
655 * The hook expects modules to return a vector of associative arrays each of them being acceptable by
656 * the gsitemap::_addLinkToSitemap() second attribute (minus the 'type' index).
657 * The 'type' index is automatically set to 'module' (not sure here, should we be safe or trust modules?).
658 *
659 * @param array $link_sitemap by ref. accumulator for all the links for the Google Sitemap file to be generated
660 * @param string $lang the language being processed
661 * @param int $index the index of the current Google Sitemap file
662 * @param int $i the count of elements added to sitemap main array
663 * @param int $num_link restart at link number #$num_link
664 * @return boolean
665 */
666 protected function _getModuleLink(&$link_sitemap, $lang, &$index, &$i, $num_link = 0)
667 {
668 $modules_links = Hook::exec(self::HOOK_ADD_URLS, array('lang' => $lang), null, true);
669 if (empty($modules_links) || !is_array($modules_links))
670 return true;
671 $links = array();
672 foreach ($modules_links as $module_links)
673 $links = array_merge($links, $module_links);
674 foreach ($module_links as $n => $link)
675 {
676 if ($num_link > $n)
677 continue;
678 $link['type'] = 'module';
679 if (!$this->_addLinkToSitemap($link_sitemap, $link, $lang['iso_code'], $index, $i, $n))
680 return false;
681 }
682 return true;
683 }
684
685 /**
686 * Create the Google Sitemap by Shop
687 *
688 * @param int $id_shop Shop identifier
689 *
690 * @return bool
691 */
692 public function createSitemap($id_shop = 0)
693 {
694 if (@fopen($this->normalizeDirectory(_PS_ROOT_DIR_).'/test.txt', 'w') == false)
695 {
696 $this->context->smarty->assign('google_maps_error', $this->l('An error occured while trying to check your file permissions. Please adjust your permissions to allow PrestaShop to write a file in your root directory.'));
697
698 return false;
699 }
700 else
701 @unlink($this->normalizeDirectory(_PS_ROOT_DIR_).'test.txt');
702
703 if ($id_shop != 0)
704 $this->context->shop = new Shop((int)$id_shop);
705
706 $type = Tools::getValue('type') ? Tools::getValue('type') : '';
707 $languages = Language::getLanguages(true, $id_shop);
708 $lang_stop = Tools::getValue('lang') ? true : false;
709 $id_obj = Tools::getValue('id') ? (int)Tools::getValue('id') : 0;
710 foreach ($languages as $lang)
711 {
712 $i = 0;
713 $index = (Tools::getValue('index') && Tools::getValue('lang') == $lang['iso_code']) ? (int)Tools::getValue('index') : 0;
714 if ($lang_stop && $lang['iso_code'] != Tools::getValue('lang'))
715 continue;
716 elseif ($lang_stop && $lang['iso_code'] == Tools::getValue('lang'))
717 $lang_stop = false;
718
719 $link_sitemap = array();
720 foreach ($this->type_array as $type_val)
721 {
722 if ($type == '' || $type == $type_val)
723 {
724 $function = '_get'.ucfirst($type_val).'Link';
725 if (!$this->$function($link_sitemap, $lang, $index, $i, $id_obj))
726 return false;
727 $type = '';
728 $id_obj = 0;
729 }
730 }
731 $this->_recursiveSitemapCreator($link_sitemap, $lang['iso_code'], $index);
732 $page = '';
733 $index = 0;
734 }
735
736 $this->_createIndexSitemap();
737 Configuration::updateValue('GSITEMAP_LAST_EXPORT', date('r'));
738 Tools::file_get_contents('http://www.google.com/webmasters/sitemaps/ping?sitemap='.urlencode('http'.(Configuration::get('PS_SSL_ENABLED') ? 's' : '').'://'.Tools::getShopDomain(false, true).$this->context->shop->physical_uri.$this->context->shop->virtual_uri.$this->context->shop->id.'_index_sitemap.xml'));
739
740 if ($this->cron)
741 die();
742 header('location: ./index.php?tab=AdminModules&configure=gsitemap&token='.Tools::getAdminTokenLite('AdminModules').'&tab_module='.$this->tab.'&module_name=gsitemap&validation');
743 die();
744 }
745
746 /**
747 * Store the generated Sitemap file to the database
748 *
749 * @param string $sitemap the name of the generated Google Sitemap file
750 *
751 * @return bool
752 */
753 protected function _saveSitemapLink($sitemap)
754 {
755 if ($sitemap)
756 return Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'gsitemap_sitemap` (`link`, id_shop) VALUES (\''.pSQL($sitemap).'\', '.(int)$this->context->shop->id.')');
757
758 return false;
759 }
760
761 /**
762 * @param array $link_sitemap contain all the links for the Google Sitemap file to be generated
763 * @param string $lang the language of link to add
764 * @param int $index the index of the current Google Sitemap file
765 *
766 * @return bool
767 */
768 protected function _recursiveSitemapCreator($link_sitemap, $lang, &$index)
769 {
770 if (!count($link_sitemap))
771 return false;
772
773 $sitemap_link = $this->context->shop->id.'_'.$lang.'_'.$index.'_sitemap.xml';
774 $write_fd = fopen($this->normalizeDirectory(_PS_ROOT_DIR_).$sitemap_link, 'w');
775
776 fwrite($write_fd, '<?xml version="1.0" encoding="UTF-8"?>'."\r\n".'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">'."\r\n");
777 foreach ($link_sitemap as $key => $file)
778 {
779 fwrite($write_fd, '<url>'."\r\n");
780 $lastmod = (isset($file['lastmod']) && !empty($file['lastmod'])) ? date('c', strtotime($file['lastmod'])) : null;
781 $this->_addSitemapNode($write_fd, htmlspecialchars(strip_tags($file['link'])), $this->_getPriorityPage($file['page']), Configuration::get('GSITEMAP_FREQUENCY'), $lastmod);
782 if ($file['image'])
783 {
784 $this->_addSitemapNodeImage(
785 $write_fd, htmlspecialchars(strip_tags($file['image']['link'])), isset($file['image']['title_img']) ? htmlspecialchars(
786 str_replace(
787 array(
788 "\r\n",
789 "\r",
790 "\n"
791 ), '', strip_tags($file['image']['title_img'])
792 )
793 ) : '', isset($file['image']['caption']) ? htmlspecialchars(
794 str_replace(
795 array(
796 "\r\n",
797 "\r",
798 "\n"
799 ), '', strip_tags($file['image']['caption'])
800 )
801 ) : ''
802 );
803 }
804 fwrite($write_fd, '</url>'."\r\n");
805 }
806 fwrite($write_fd, '</urlset>'."\r\n");
807 fclose($write_fd);
808 $this->_saveSitemapLink($sitemap_link);
809 $index++;
810
811 return true;
812 }
813
814 /**
815 * return the priority value set in the configuration parameters
816 *
817 * @param string $page
818 *
819 * @return float|string|bool
820 */
821 protected function _getPriorityPage($page)
822 {
823 return Configuration::get('GSITEMAP_PRIORITY_'.Tools::strtoupper($page)) ? Configuration::get('GSITEMAP_PRIORITY_'.Tools::strtoupper($page)) : 0.1;
824 }
825
826 /**
827 * Add a new line to the sitemap file
828 *
829 * @param resource $fd file system object resource
830 * @param string $loc string the URL of the object page
831 * @param string $priority
832 * @param string $change_freq
833 * @param int $last_mod the last modification date/time as a timestamp
834 */
835 protected function _addSitemapNode($fd, $loc, $priority, $change_freq, $last_mod = null)
836 {
837 fwrite($fd, '<loc>'.(Configuration::get('PS_REWRITING_SETTINGS') ? '<![CDATA['.$loc.']]>' : $loc).'</loc>'."\r\n".'<priority>'.number_format($priority, 1, '.', '').'</priority>'."\r\n".($last_mod ? '<lastmod>'.date('c', strtotime($last_mod)).'</lastmod>' : '')."\r\n".'<changefreq>'.$change_freq.'</changefreq>'."\r\n");
838 }
839
840 protected function _addSitemapNodeImage($fd, $link, $title, $caption)
841 {
842 fwrite($fd, '<image:image>'."\r\n".'<image:loc>'.(Configuration::get('PS_REWRITING_SETTINGS') ? '<![CDATA['.$link.']]>' : $link).'</image:loc>'."\r\n".'<image:caption><![CDATA['.$caption.']]></image:caption>'."\r\n".'<image:title><![CDATA['.$title.']]></image:title>'."\r\n".'</image:image>'."\r\n");
843 }
844
845 /**
846 * Create the index file for all generated sitemaps
847 * @return boolean
848 */
849 protected function _createIndexSitemap()
850 {
851 $sitemaps = Db::getInstance()->ExecuteS('SELECT `link` FROM `'._DB_PREFIX_.'gsitemap_sitemap` WHERE id_shop = '.$this->context->shop->id);
852 if (!$sitemaps)
853 return false;
854
855 $xml = '<?xml version="1.0" encoding="UTF-8"?><sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></sitemapindex>';
856 $xml_feed = new SimpleXMLElement($xml);
857
858 foreach ($sitemaps as $link)
859 {
860 $sitemap = $xml_feed->addChild('sitemap');
861 $sitemap->addChild('loc', 'http'.(Configuration::get('PS_SSL_ENABLED') && Configuration::get('PS_SSL_ENABLED_EVERYWHERE') ? 's' : '').'://'.Tools::getShopDomain(false, true).__PS_BASE_URI__.$link['link']);
862 $sitemap->addChild('lastmod', date('c'));
863 }
864 file_put_contents($this->normalizeDirectory(_PS_ROOT_DIR_).$this->context->shop->id.'_index_sitemap.xml', $xml_feed->asXML());
865
866 return true;
867 }
868
869 protected function tableColumnExists($table_name, $column = null)
870 {
871 if (array_key_exists($table_name, $this->sql_checks))
872 if (!empty($column) && array_key_exists($column, $this->sql_checks[$table_name]))
873 return $this->sql_checks[$table_name][$column];
874 else
875 return $this->sql_checks[$table_name];
876
877 $table = Db::getInstance()->ExecuteS('SHOW TABLES LIKE \''.$table_name.'\'');
878 if (empty($column))
879 if (count($table) < 1)
880 return $this->sql_checks[$table_name] = false;
881 else
882 $this->sql_checks[$table_name] = true;
883
884 else
885 {
886 $table = Db::getInstance()->ExecuteS('SELECT * FROM `'.$table_name.'` LIMIT 1');
887
888 return $this->sql_checks[$table_name][$column] = array_key_exists($column, current($table));
889 }
890
891 return true;
892 }
893
894 protected function normalizeDirectory($directory)
895 {
896 $last = $directory[strlen($directory) - 1];
897
898 if (in_array($last, array('/', '\\')))
899 {
900 $directory[strlen($directory) - 1] = DIRECTORY_SEPARATOR;
901 return $directory;
902 }
903
904 $directory .= DIRECTORY_SEPARATOR;
905 return $directory;
906 }
907}