Sitemap.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Sitemap\Model;
  7. use Magento\Config\Model\Config\Reader\Source\Deployed\DocumentRoot;
  8. use Magento\Framework\App\ObjectManager;
  9. use Magento\Framework\DataObject;
  10. use Magento\Framework\Exception\LocalizedException;
  11. use Magento\Framework\UrlInterface;
  12. use Magento\Robots\Model\Config\Value;
  13. use Magento\Sitemap\Model\ItemProvider\ItemProviderInterface;
  14. use Magento\Sitemap\Model\ResourceModel\Sitemap as SitemapResource;
  15. /**
  16. * @method string getSitemapType()
  17. * @method \Magento\Sitemap\Model\Sitemap setSitemapType(string $value)
  18. * @method string getSitemapFilename()
  19. * @method \Magento\Sitemap\Model\Sitemap setSitemapFilename(string $value)
  20. * @method string getSitemapPath()
  21. * @method \Magento\Sitemap\Model\Sitemap setSitemapPath(string $value)
  22. * @method string getSitemapTime()
  23. * @method \Magento\Sitemap\Model\Sitemap setSitemapTime(string $value)
  24. * @method int getStoreId()
  25. * @method \Magento\Sitemap\Model\Sitemap setStoreId(int $value)
  26. * @SuppressWarnings(PHPMD.TooManyFields)
  27. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  28. * @api
  29. * @since 100.0.2
  30. */
  31. class Sitemap extends \Magento\Framework\Model\AbstractModel implements \Magento\Framework\DataObject\IdentityInterface
  32. {
  33. const OPEN_TAG_KEY = 'start';
  34. const CLOSE_TAG_KEY = 'end';
  35. const INDEX_FILE_PREFIX = 'sitemap';
  36. const TYPE_INDEX = 'sitemap';
  37. const TYPE_URL = 'url';
  38. /**
  39. * Last mode date min value
  40. */
  41. const LAST_MOD_MIN_VAL = '0000-01-01 00:00:00';
  42. /**
  43. * Real file path
  44. *
  45. * @var string
  46. */
  47. protected $_filePath;
  48. /**
  49. * Sitemap items
  50. *
  51. * @var array
  52. */
  53. protected $_sitemapItems = [];
  54. /**
  55. * Current sitemap increment
  56. *
  57. * @var int
  58. */
  59. protected $_sitemapIncrement = 0;
  60. /**
  61. * Sitemap start and end tags
  62. *
  63. * @var array
  64. */
  65. protected $_tags = [];
  66. /**
  67. * Number of lines in sitemap
  68. *
  69. * @var int
  70. */
  71. protected $_lineCount = 0;
  72. /**
  73. * Current sitemap file size
  74. *
  75. * @var int
  76. */
  77. protected $_fileSize = 0;
  78. /**
  79. * New line possible symbols
  80. *
  81. * @var array
  82. */
  83. private $_crlf = ["win" => "\r\n", "unix" => "\n", "mac" => "\r"];
  84. /**
  85. * @var \Magento\Framework\Filesystem\Directory\Write
  86. */
  87. protected $_directory;
  88. /**
  89. * @var \Magento\Framework\Filesystem\File\Write
  90. */
  91. protected $_stream;
  92. /**
  93. * Sitemap data
  94. *
  95. * @var \Magento\Sitemap\Helper\Data
  96. */
  97. protected $_sitemapData;
  98. /**
  99. * @var \Magento\Framework\Escaper
  100. */
  101. protected $_escaper;
  102. /**
  103. * @var \Magento\Sitemap\Model\ResourceModel\Catalog\CategoryFactory
  104. */
  105. protected $_categoryFactory;
  106. /**
  107. * @var \Magento\Sitemap\Model\ResourceModel\Catalog\ProductFactory
  108. */
  109. protected $_productFactory;
  110. /**
  111. * @var \Magento\Sitemap\Model\ResourceModel\Cms\PageFactory
  112. */
  113. protected $_cmsFactory;
  114. /**
  115. * @var \Magento\Framework\Stdlib\DateTime\DateTime
  116. */
  117. protected $_dateModel;
  118. /**
  119. * @var \Magento\Store\Model\StoreManagerInterface
  120. */
  121. protected $_storeManager;
  122. /**
  123. * @var \Magento\Framework\App\RequestInterface
  124. */
  125. protected $_request;
  126. /**
  127. * @var \Magento\Framework\Stdlib\DateTime
  128. */
  129. protected $dateTime;
  130. /**
  131. * Model cache tag for clear cache in after save and after delete
  132. *
  133. * @var string
  134. * @since 100.1.5
  135. */
  136. protected $_cacheTag = true;
  137. /**
  138. * Item resolver
  139. *
  140. * @var ItemProviderInterface
  141. */
  142. private $itemProvider;
  143. /**
  144. * Sitemap config reader
  145. *
  146. * @var SitemapConfigReaderInterface
  147. */
  148. private $configReader;
  149. /**
  150. * Sitemap Item Factory
  151. *
  152. * @var \Magento\Sitemap\Model\SitemapItemInterfaceFactory
  153. */
  154. private $sitemapItemFactory;
  155. /**
  156. * Last mode min timestamp value
  157. *
  158. * @var int
  159. */
  160. private $lastModMinTsVal;
  161. /**
  162. * Initialize dependencies.
  163. *
  164. * @param \Magento\Framework\Model\Context $context
  165. * @param \Magento\Framework\Registry $registry
  166. * @param \Magento\Framework\Escaper $escaper
  167. * @param \Magento\Sitemap\Helper\Data $sitemapData
  168. * @param \Magento\Framework\Filesystem $filesystem
  169. * @param ResourceModel\Catalog\CategoryFactory $categoryFactory
  170. * @param ResourceModel\Catalog\ProductFactory $productFactory
  171. * @param ResourceModel\Cms\PageFactory $cmsFactory
  172. * @param \Magento\Framework\Stdlib\DateTime\DateTime $modelDate
  173. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  174. * @param \Magento\Framework\App\RequestInterface $request
  175. * @param \Magento\Framework\Stdlib\DateTime $dateTime
  176. * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
  177. * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
  178. * @param array $data
  179. * @param DocumentRoot|null $documentRoot
  180. * @param ItemProviderInterface|null $itemProvider
  181. * @param SitemapConfigReaderInterface|null $configReader
  182. * @param \Magento\Sitemap\Model\SitemapItemInterfaceFactory|null $sitemapItemFactory
  183. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  184. */
  185. public function __construct(
  186. \Magento\Framework\Model\Context $context,
  187. \Magento\Framework\Registry $registry,
  188. \Magento\Framework\Escaper $escaper,
  189. \Magento\Sitemap\Helper\Data $sitemapData,
  190. \Magento\Framework\Filesystem $filesystem,
  191. \Magento\Sitemap\Model\ResourceModel\Catalog\CategoryFactory $categoryFactory,
  192. \Magento\Sitemap\Model\ResourceModel\Catalog\ProductFactory $productFactory,
  193. \Magento\Sitemap\Model\ResourceModel\Cms\PageFactory $cmsFactory,
  194. \Magento\Framework\Stdlib\DateTime\DateTime $modelDate,
  195. \Magento\Store\Model\StoreManagerInterface $storeManager,
  196. \Magento\Framework\App\RequestInterface $request,
  197. \Magento\Framework\Stdlib\DateTime $dateTime,
  198. \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
  199. \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
  200. array $data = [],
  201. \Magento\Config\Model\Config\Reader\Source\Deployed\DocumentRoot $documentRoot = null,
  202. ItemProviderInterface $itemProvider = null,
  203. SitemapConfigReaderInterface $configReader = null,
  204. \Magento\Sitemap\Model\SitemapItemInterfaceFactory $sitemapItemFactory = null
  205. ) {
  206. $this->_escaper = $escaper;
  207. $this->_sitemapData = $sitemapData;
  208. $documentRoot = $documentRoot ?: ObjectManager::getInstance()->get(DocumentRoot::class);
  209. $this->_directory = $filesystem->getDirectoryWrite($documentRoot->getPath());
  210. $this->_categoryFactory = $categoryFactory;
  211. $this->_productFactory = $productFactory;
  212. $this->_cmsFactory = $cmsFactory;
  213. $this->_dateModel = $modelDate;
  214. $this->_storeManager = $storeManager;
  215. $this->_request = $request;
  216. $this->dateTime = $dateTime;
  217. $this->itemProvider = $itemProvider ?: ObjectManager::getInstance()->get(ItemProviderInterface::class);
  218. $this->configReader = $configReader ?: ObjectManager::getInstance()->get(SitemapConfigReaderInterface::class);
  219. $this->sitemapItemFactory = $sitemapItemFactory ?: ObjectManager::getInstance()->get(
  220. \Magento\Sitemap\Model\SitemapItemInterfaceFactory::class
  221. );
  222. parent::__construct($context, $registry, $resource, $resourceCollection, $data);
  223. }
  224. /**
  225. * Init model
  226. *
  227. * @return void
  228. */
  229. protected function _construct()
  230. {
  231. $this->_init(SitemapResource::class);
  232. }
  233. /**
  234. * Get file handler
  235. *
  236. * @return \Magento\Framework\Filesystem\File\WriteInterface
  237. * @throws LocalizedException
  238. */
  239. protected function _getStream()
  240. {
  241. if ($this->_stream) {
  242. return $this->_stream;
  243. } else {
  244. throw new LocalizedException(__('File handler unreachable'));
  245. }
  246. }
  247. /**
  248. * Add a sitemap item to the array of sitemap items
  249. *
  250. * @param DataObject $sitemapItem
  251. * @return $this
  252. * @deprecated 100.3.0
  253. * @see ItemProviderInterface
  254. * @since 100.2.0
  255. */
  256. public function addSitemapItem(DataObject $sitemapItem)
  257. {
  258. $this->_sitemapItems[] = $sitemapItem;
  259. return $this;
  260. }
  261. /**
  262. * Collect all sitemap items
  263. *
  264. * @return void
  265. * @deprecated 100.3.0
  266. * @see ItemProviderInterface
  267. * @since 100.2.0
  268. */
  269. public function collectSitemapItems()
  270. {
  271. /** @var $helper \Magento\Sitemap\Helper\Data */
  272. $helper = $this->_sitemapData;
  273. $storeId = $this->getStoreId();
  274. $this->addSitemapItem(new DataObject(
  275. [
  276. 'changefreq' => $helper->getCategoryChangefreq($storeId),
  277. 'priority' => $helper->getCategoryPriority($storeId),
  278. 'collection' => $this->_categoryFactory->create()->getCollection($storeId),
  279. ]
  280. ));
  281. $this->addSitemapItem(new DataObject(
  282. [
  283. 'changefreq' => $helper->getProductChangefreq($storeId),
  284. 'priority' => $helper->getProductPriority($storeId),
  285. 'collection' => $this->_productFactory->create()->getCollection($storeId),
  286. ]
  287. ));
  288. $this->addSitemapItem(new DataObject(
  289. [
  290. 'changefreq' => $helper->getPageChangefreq($storeId),
  291. 'priority' => $helper->getPagePriority($storeId),
  292. 'collection' => $this->_cmsFactory->create()->getCollection($storeId),
  293. ]
  294. ));
  295. }
  296. /**
  297. * Initialize sitemap
  298. *
  299. * @return void
  300. */
  301. protected function _initSitemapItems()
  302. {
  303. $sitemapItems = $this->itemProvider->getItems($this->getStoreId());
  304. $mappedItems = $this->mapToSitemapItem();
  305. $this->_sitemapItems = array_merge($sitemapItems, $mappedItems);
  306. $this->_tags = [
  307. self::TYPE_INDEX => [
  308. self::OPEN_TAG_KEY => '<?xml version="1.0" encoding="UTF-8"?>' .
  309. PHP_EOL .
  310. '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' .
  311. PHP_EOL,
  312. self::CLOSE_TAG_KEY => '</sitemapindex>',
  313. ],
  314. self::TYPE_URL => [
  315. self::OPEN_TAG_KEY => '<?xml version="1.0" encoding="UTF-8"?>' .
  316. PHP_EOL .
  317. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"' .
  318. ' xmlns:content="http://www.google.com/schemas/sitemap-content/1.0"' .
  319. ' xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">' .
  320. PHP_EOL,
  321. self::CLOSE_TAG_KEY => '</urlset>',
  322. ],
  323. ];
  324. }
  325. /**
  326. * Check sitemap file location and permissions
  327. *
  328. * @return \Magento\Framework\Model\AbstractModel
  329. * @throws LocalizedException
  330. */
  331. public function beforeSave()
  332. {
  333. $path = $this->getSitemapPath();
  334. /**
  335. * Check path is allow
  336. */
  337. if ($path && preg_match('#\.\.[\\\/]#', $path)) {
  338. throw new LocalizedException(__('Please define a correct path.'));
  339. }
  340. /**
  341. * Check exists and writable path
  342. */
  343. if (!$this->_directory->isExist($path)) {
  344. throw new LocalizedException(
  345. __(
  346. 'Please create the specified folder "%1" before saving the sitemap.',
  347. $this->_escaper->escapeHtml($this->getSitemapPath())
  348. )
  349. );
  350. }
  351. if (!$this->_directory->isWritable($path)) {
  352. throw new LocalizedException(
  353. __('Please make sure that "%1" is writable by the web-server.', $this->getSitemapPath())
  354. );
  355. }
  356. /**
  357. * Check allow filename
  358. */
  359. if (!preg_match('#^[a-zA-Z0-9_\.]+$#', $this->getSitemapFilename())) {
  360. throw new LocalizedException(
  361. __(
  362. 'Please use only letters (a-z or A-Z), numbers (0-9) or underscores (_) in the filename.'
  363. . ' No spaces or other characters are allowed.'
  364. )
  365. );
  366. }
  367. if (!preg_match('#\.xml$#', $this->getSitemapFilename())) {
  368. $this->setSitemapFilename($this->getSitemapFilename() . '.xml');
  369. }
  370. $this->setSitemapPath(rtrim(str_replace(str_replace('\\', '/', $this->_getBaseDir()), '', $path), '/') . '/');
  371. return parent::beforeSave();
  372. }
  373. /**
  374. * Generate XML file
  375. *
  376. * @see http://www.sitemaps.org/protocol.html
  377. *
  378. * @return $this
  379. */
  380. public function generateXml()
  381. {
  382. $this->_initSitemapItems();
  383. /** @var $item SitemapItemInterface */
  384. foreach ($this->_sitemapItems as $item) {
  385. $xml = $this->_getSitemapRow(
  386. $item->getUrl(),
  387. $item->getUpdatedAt(),
  388. $item->getChangeFrequency(),
  389. $item->getPriority(),
  390. $item->getImages()
  391. );
  392. if ($this->_isSplitRequired($xml) && $this->_sitemapIncrement > 0) {
  393. $this->_finalizeSitemap();
  394. }
  395. if (!$this->_fileSize) {
  396. $this->_createSitemap();
  397. }
  398. $this->_writeSitemapRow($xml);
  399. // Increase counters
  400. $this->_lineCount++;
  401. $this->_fileSize += strlen($xml);
  402. }
  403. $this->_finalizeSitemap();
  404. if ($this->_sitemapIncrement == 1) {
  405. // In case when only one increment file was created use it as default sitemap
  406. $path = rtrim(
  407. $this->getSitemapPath(),
  408. '/'
  409. ) . '/' . $this->_getCurrentSitemapFilename(
  410. $this->_sitemapIncrement
  411. );
  412. $destination = rtrim($this->getSitemapPath(), '/') . '/' . $this->getSitemapFilename();
  413. $this->_directory->renameFile($path, $destination);
  414. } else {
  415. // Otherwise create index file with list of generated sitemaps
  416. $this->_createSitemapIndex();
  417. }
  418. $this->setSitemapTime($this->_dateModel->gmtDate('Y-m-d H:i:s'));
  419. $this->save();
  420. return $this;
  421. }
  422. /**
  423. * Generate sitemap index XML file
  424. *
  425. * @return void
  426. */
  427. protected function _createSitemapIndex()
  428. {
  429. $this->_createSitemap($this->getSitemapFilename(), self::TYPE_INDEX);
  430. for ($i = 1; $i <= $this->_sitemapIncrement; $i++) {
  431. $xml = $this->_getSitemapIndexRow($this->_getCurrentSitemapFilename($i), $this->_getCurrentDateTime());
  432. $this->_writeSitemapRow($xml);
  433. }
  434. $this->_finalizeSitemap(self::TYPE_INDEX);
  435. }
  436. /**
  437. * Get current date time
  438. *
  439. * @return string
  440. */
  441. protected function _getCurrentDateTime()
  442. {
  443. return (new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT);
  444. }
  445. /**
  446. * Check is split required
  447. *
  448. * @param string $row
  449. * @return bool
  450. */
  451. protected function _isSplitRequired($row)
  452. {
  453. $storeId = $this->getStoreId();
  454. if ($this->_lineCount + 1 > $this->configReader->getMaximumLinesNumber($storeId)) {
  455. return true;
  456. }
  457. if ($this->_fileSize + strlen($row) > $this->configReader->getMaximumFileSize($storeId)) {
  458. return true;
  459. }
  460. return false;
  461. }
  462. /**
  463. * Get sitemap row
  464. *
  465. * @param string $url
  466. * @param null|string $lastmod
  467. * @param null|string $changefreq
  468. * @param null|string $priority
  469. * @param null|array|\Magento\Framework\DataObject $images
  470. * @return string
  471. * Sitemap images
  472. * @see http://support.google.com/webmasters/bin/answer.py?hl=en&answer=178636
  473. *
  474. * Sitemap PageMap
  475. * @see http://support.google.com/customsearch/bin/answer.py?hl=en&answer=1628213
  476. */
  477. protected function _getSitemapRow($url, $lastmod = null, $changefreq = null, $priority = null, $images = null)
  478. {
  479. $url = $this->_getUrl($url);
  480. $row = '<loc>' . htmlspecialchars($url) . '</loc>';
  481. if ($lastmod) {
  482. $row .= '<lastmod>' . $this->_getFormattedLastmodDate($lastmod) . '</lastmod>';
  483. }
  484. if ($changefreq) {
  485. $row .= '<changefreq>' . $changefreq . '</changefreq>';
  486. }
  487. if ($priority) {
  488. $row .= sprintf('<priority>%.1f</priority>', $priority);
  489. }
  490. if ($images) {
  491. // Add Images to sitemap
  492. foreach ($images->getCollection() as $image) {
  493. $row .= '<image:image>';
  494. $row .= '<image:loc>' . htmlspecialchars($image->getUrl()) . '</image:loc>';
  495. $row .= '<image:title>' . htmlspecialchars($images->getTitle()) . '</image:title>';
  496. if ($image->getCaption()) {
  497. $row .= '<image:caption>' . htmlspecialchars($image->getCaption()) . '</image:caption>';
  498. }
  499. $row .= '</image:image>';
  500. }
  501. // Add PageMap image for Google web search
  502. $row .= '<PageMap xmlns="http://www.google.com/schemas/sitemap-pagemap/1.0"><DataObject type="thumbnail">';
  503. $row .= '<Attribute name="name" value="' . htmlspecialchars($images->getTitle()) . '"/>';
  504. $row .= '<Attribute name="src" value="' . htmlspecialchars($images->getThumbnail()) . '"/>';
  505. $row .= '</DataObject></PageMap>';
  506. }
  507. return '<url>' . $row . '</url>';
  508. }
  509. /**
  510. * Get sitemap index row
  511. *
  512. * @param string $sitemapFilename
  513. * @param null|string $lastmod
  514. * @return string
  515. */
  516. protected function _getSitemapIndexRow($sitemapFilename, $lastmod = null)
  517. {
  518. $url = $this->getSitemapUrl($this->getSitemapPath(), $sitemapFilename);
  519. $row = '<loc>' . htmlspecialchars($url) . '</loc>';
  520. if ($lastmod) {
  521. $row .= '<lastmod>' . $this->_getFormattedLastmodDate($lastmod) . '</lastmod>';
  522. }
  523. return '<sitemap>' . $row . '</sitemap>';
  524. }
  525. /**
  526. * Create new sitemap file
  527. *
  528. * @param null|string $fileName
  529. * @param string $type
  530. * @return void
  531. * @throws LocalizedException
  532. */
  533. protected function _createSitemap($fileName = null, $type = self::TYPE_URL)
  534. {
  535. if (!$fileName) {
  536. $this->_sitemapIncrement++;
  537. $fileName = $this->_getCurrentSitemapFilename($this->_sitemapIncrement);
  538. }
  539. $path = rtrim($this->getSitemapPath(), '/') . '/' . $fileName;
  540. $this->_stream = $this->_directory->openFile($path);
  541. $fileHeader = sprintf($this->_tags[$type][self::OPEN_TAG_KEY], $type);
  542. $this->_stream->write($fileHeader);
  543. $this->_fileSize = strlen($fileHeader . sprintf($this->_tags[$type][self::CLOSE_TAG_KEY], $type));
  544. }
  545. /**
  546. * Write sitemap row
  547. *
  548. * @param string $row
  549. * @return void
  550. */
  551. protected function _writeSitemapRow($row)
  552. {
  553. $this->_getStream()->write($row . PHP_EOL);
  554. }
  555. /**
  556. * Write closing tag and close stream
  557. *
  558. * @param string $type
  559. * @return void
  560. */
  561. protected function _finalizeSitemap($type = self::TYPE_URL)
  562. {
  563. if ($this->_stream) {
  564. $this->_stream->write(sprintf($this->_tags[$type][self::CLOSE_TAG_KEY], $type));
  565. $this->_stream->close();
  566. }
  567. // Reset all counters
  568. $this->_lineCount = 0;
  569. $this->_fileSize = 0;
  570. }
  571. /**
  572. * Get current sitemap filename
  573. *
  574. * @param int $index
  575. * @return string
  576. */
  577. protected function _getCurrentSitemapFilename($index)
  578. {
  579. return str_replace('.xml', '', $this->getSitemapFilename()) . '-' . $this->getStoreId() . '-' . $index . '.xml';
  580. }
  581. /**
  582. * Get base dir
  583. *
  584. * @return string
  585. */
  586. protected function _getBaseDir()
  587. {
  588. return $this->_directory->getAbsolutePath();
  589. }
  590. /**
  591. * Get store base url
  592. *
  593. * @param string $type
  594. * @return string
  595. */
  596. protected function _getStoreBaseUrl($type = UrlInterface::URL_TYPE_LINK)
  597. {
  598. /** @var \Magento\Store\Model\Store $store */
  599. $store = $this->_storeManager->getStore($this->getStoreId());
  600. $isSecure = $store->isUrlSecure();
  601. return rtrim($store->getBaseUrl($type, $isSecure), '/') . '/';
  602. }
  603. /**
  604. * Get url
  605. *
  606. * @param string $url
  607. * @param string $type
  608. * @return string
  609. */
  610. protected function _getUrl($url, $type = UrlInterface::URL_TYPE_LINK)
  611. {
  612. return $this->_getStoreBaseUrl($type) . ltrim($url, '/');
  613. }
  614. /**
  615. * Get media url
  616. *
  617. * @param string $url
  618. * @return string
  619. * @deprecated 100.2.0 No longer used, as we're generating product image URLs inside collection instead
  620. * @see \Magento\Sitemap\Model\ResourceModel\Catalog\Product::_loadProductImages()
  621. */
  622. protected function _getMediaUrl($url)
  623. {
  624. return $this->_getUrl($url, UrlInterface::URL_TYPE_MEDIA);
  625. }
  626. /**
  627. * Get date in correct format applicable for lastmod attribute
  628. *
  629. * @param string $date
  630. * @return string
  631. */
  632. protected function _getFormattedLastmodDate($date)
  633. {
  634. if ($this->lastModMinTsVal === null) {
  635. $this->lastModMinTsVal = strtotime(self::LAST_MOD_MIN_VAL);
  636. }
  637. $timestamp = max(strtotime($date), $this->lastModMinTsVal);
  638. return date('c', $timestamp);
  639. }
  640. /**
  641. * Get Document root of Magento instance
  642. *
  643. * @return string
  644. */
  645. protected function _getDocumentRoot()
  646. {
  647. return realpath($this->_request->getServer('DOCUMENT_ROOT'));
  648. }
  649. /**
  650. * Get domain from store base url
  651. *
  652. * @return string
  653. */
  654. protected function _getStoreBaseDomain()
  655. {
  656. $storeParsedUrl = parse_url($this->_getStoreBaseUrl());
  657. $url = $storeParsedUrl['scheme'] . '://' . $storeParsedUrl['host'];
  658. $documentRoot = trim(str_replace('\\', '/', $this->_getDocumentRoot()), '/');
  659. $baseDir = trim(str_replace('\\', '/', $this->_getBaseDir()), '/');
  660. if (strpos($baseDir, $documentRoot) === 0) {
  661. //case when basedir is in document root
  662. $installationFolder = trim(str_replace($documentRoot, '', $baseDir), '/');
  663. $storeDomain = rtrim($url . '/' . $installationFolder, '/');
  664. } else {
  665. //case when documentRoot contains symlink to basedir
  666. $url = $this->_getStoreBaseUrl(UrlInterface::URL_TYPE_WEB);
  667. $storeDomain = rtrim($url, '/');
  668. }
  669. return $storeDomain;
  670. }
  671. /**
  672. * Get sitemap.xml URL according to all config options
  673. *
  674. * @param string $sitemapPath
  675. * @param string $sitemapFileName
  676. * @return string
  677. */
  678. public function getSitemapUrl($sitemapPath, $sitemapFileName)
  679. {
  680. return $this->_getStoreBaseDomain() . str_replace('//', '/', $sitemapPath . '/' . $sitemapFileName);
  681. }
  682. /**
  683. * Check is enabled submission to robots.txt
  684. *
  685. * @return bool
  686. * @deprecated 100.1.5 Because the robots.txt file is not generated anymore,
  687. * this method is not needed and will be removed in major release.
  688. */
  689. protected function _isEnabledSubmissionRobots()
  690. {
  691. $storeId = $this->getStoreId();
  692. return (bool)$this->configReader->getEnableSubmissionRobots($storeId);
  693. }
  694. /**
  695. * Add sitemap file to robots.txt
  696. *
  697. * @param string $sitemapFileName
  698. * @return void
  699. * @deprecated 100.1.5 Because the robots.txt file is not generated anymore,
  700. * this method is not needed and will be removed in major release.
  701. */
  702. protected function _addSitemapToRobotsTxt($sitemapFileName)
  703. {
  704. $robotsSitemapLine = 'Sitemap: ' . $this->getSitemapUrl($this->getSitemapPath(), $sitemapFileName);
  705. $filename = 'robots.txt';
  706. $content = '';
  707. if ($this->_directory->isExist($filename)) {
  708. $content = $this->_directory->readFile($filename);
  709. }
  710. if (strpos($content, $robotsSitemapLine) === false) {
  711. if (!empty($content)) {
  712. $content .= $this->_findNewLinesDelimiter($content);
  713. }
  714. $content .= $robotsSitemapLine;
  715. }
  716. $this->_directory->writeFile($filename, $content);
  717. }
  718. /**
  719. * Find new lines delimiter
  720. *
  721. * @param string $text
  722. * @return string
  723. */
  724. private function _findNewLinesDelimiter($text)
  725. {
  726. foreach ($this->_crlf as $delimiter) {
  727. if (strpos($text, $delimiter) !== false) {
  728. return $delimiter;
  729. }
  730. }
  731. return PHP_EOL;
  732. }
  733. /**
  734. * Sitemap item mapper for backwards compatibility
  735. *
  736. * @return array
  737. */
  738. private function mapToSitemapItem()
  739. {
  740. $items = [];
  741. foreach ($this->_sitemapItems as $data) {
  742. foreach ($data->getCollection() as $item) {
  743. $items[] = $this->sitemapItemFactory->create([
  744. 'url' => $item->getUrl(),
  745. 'updatedAt' => $item->getUpdatedAt(),
  746. 'images' => $item->getImages(),
  747. 'priority' => $data->getPriority(),
  748. 'changeFrequency' => $data->getChangeFrequency(),
  749. ]);
  750. }
  751. }
  752. return $items;
  753. }
  754. /**
  755. * Get unique page cache identities
  756. *
  757. * @return array
  758. * @since 100.1.5
  759. */
  760. public function getIdentities()
  761. {
  762. return [
  763. Value::CACHE_TAG . '_' . $this->getStoreId(),
  764. ];
  765. }
  766. }