diff --git a/Block/Sitemap.php b/Block/Sitemap.php index 04b6fde..8faed3c 100755 --- a/Block/Sitemap.php +++ b/Block/Sitemap.php @@ -21,20 +21,23 @@ namespace Mageplaza\Sitemap\Block; +use Exception; use Magento\Catalog\Helper\Category; use Magento\Catalog\Model\CategoryRepository; use Magento\Catalog\Model\Product\Visibility as ProductVisibility; use Magento\Catalog\Model\ResourceModel\Category\Collection; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; -use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollection; use Magento\CatalogInventory\Helper\Stock; use Magento\Cms\Model\Page; use Magento\Cms\Model\ResourceModel\Page\Collection as PageCollection; -use Magento\Framework\Data\Tree\Node\Collection as TreeCollection; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\View\Element\Template; use Magento\Framework\View\Element\Template\Context; use Mageplaza\Sitemap\Helper\Data as HelperConfig; +use Mageplaza\Sitemap\Model\Source\SortProduct; /** * Class Sitemap @@ -136,28 +139,160 @@ public function __construct( public function getProductCollection() { $limit = $this->_helper->getProductLimit() ?: self::DEFAULT_PRODUCT_LIMIT; - $collection = $this->productCollection + $collection = $this->productCollection->create() ->setVisibility($this->productVisibility->getVisibleInCatalogIds()) ->addMinimalPrice() ->addFinalPrice() ->addTaxPercents() - ->setPageSize($limit) ->addAttributeToSelect('*'); - if (!$this->_helper->getConfigValue('cataloginventory/options/show_out_of_stock')) { + + $sortProductBy = $this->_helper->getHtmlSitemapConfig('product_sorting'); + $sortProductDir = $this->_helper->getHtmlSitemapConfig('product_sorting_dir'); + + switch ($sortProductBy) { + case SortProduct::PRODUCT_NAME: + $collection->setOrder('name', $sortProductDir); + break; + case SortProduct::PRICE: + $collection->setOrder('minimal_price', $sortProductDir); + break; + default: + $collection->setOrder('entity_id', $sortProductDir); + break; + } + + if ($this->_helper->getHtmlSitemapConfig('out_of_stock_products')) { $this->_stockFilter->addInStockFilterToCollection($collection); } + $collection->setPageSize($limit); + return $collection; } /** - * Get category collection - * - * @return TreeCollection + * @return Collection|AbstractDb + * @throws NoSuchEntityException + * @throws LocalizedException */ public function getCategoryCollection() { - return $this->_categoryHelper->getStoreCategories(false, true); + $storeRootCategoryId = $this->_storeManager->getStore()->getRootCategoryId(); + $storeRootCategory = $this->categoryRepository->get($storeRootCategoryId); + $categoryCollection = $this->_categoryCollection->create()->addAttributeToSelect('*') + ->addFieldToFilter('entity_id', ['in' => $storeRootCategory->getAllChildren(true)]) + ->addFieldToFilter('is_active', 1) + ->addFieldToFilter('include_in_menu', 1) + ->addFieldToFilter('entity_id', ['nin' => [$storeRootCategoryId]]); + + $excludeCategories = $this->_helper->getHtmlSitemapConfig('category_page'); + if (!empty($excludeCategories)) { + $excludeCategories = array_map('trim', explode( + "\n", + $excludeCategories + )); + + $allExcludeIds = ''; + foreach ($excludeCategories as $excludeCategory) { + if (!empty($excludeCategory)) { + try { + $testRegex = preg_match($excludeCategory, ''); + if ($testRegex) { + $allExcludeIds .= '-' . $this->filterCategoryWithRegex($excludeCategory); + } else { + $excludePath = $this->getExcludePath($excludeCategory); + $allExcludeIds .= '-' . $this->filterCategoryWithPath($excludePath, $categoryCollection); + } + } catch (Exception $e) { + $excludePath = $this->getExcludePath($excludeCategory); + $allExcludeIds .= '-' . $this->filterCategoryWithPath($excludePath, $categoryCollection); + } + } + } + + $excludeIds = explode('-', $allExcludeIds); + $categoryCollection->addFieldToFilter('entity_id', ['nin' => $excludeIds]); + } + + return $this->_categoryCollection->create()->addAttributeToSelect('*') + ->addFieldToFilter('entity_id', ['in' => $categoryCollection->getAllIds()])->setOrder('path'); + } + + /** + * @param $path + * @param $categoryCollection + * + * @return string + */ + protected function filterCategoryWithPath($path, $categoryCollection) + { + $excludeIds = []; + foreach ($categoryCollection as $category) { + if ($this->isExcludeCategory($category, $path)) { + $excludeIds[] = $category->getData('entity_id'); + } + } + + return implode('-', $excludeIds); + } + + /** + * @param $category + * @param $path + * + * @return bool + */ + public function isExcludeCategory($category, $path) + { + $filterPath = explode('/', $path); + $categoryPath = $category->getUrlPath(); + $categoryPath = explode('/', $categoryPath); + + foreach ($filterPath as $pathInfo) { + if (!in_array($pathInfo, $categoryPath)) { + return false; + } + } + + return true; + } + + /** + * @param $regex + * + * @return string + * @throws LocalizedException + * @throws NoSuchEntityException + */ + protected function filterCategoryWithRegex($regex) + { + $excludeCategoriesIds = []; + $categoryCollection = $this->_categoryCollection->create()->addAttributeToSelect('*') + ->setStoreId($this->_storeManager->getStore()->getId()); + foreach ($categoryCollection as $category) { + if (!preg_match($regex, $category->getUrlPath())) { + $excludeCategoriesIds[] = $category->getId(); + } + } + + return implode('-', $excludeCategoriesIds); + } + + /** + * @param $excludeCategory + * + * @return string + */ + protected function getExcludePath($excludeCategory) + { + if ($excludeCategory[0] == '/') { + $excludeCategory = substr($excludeCategory, 1); + } + if ($excludeCategory[-1] == '/') { + $excludeCategory = substr($excludeCategory, 0, -1); + } + + return $excludeCategory; } /** @@ -223,16 +358,15 @@ public function getAdditionLinksCollection() } /** - * Render link element - * - * @param string $link - * @param string $title + * @param $link + * @param $title + * @param $level * * @return string */ - public function renderLinkElement($link, $title) + public function renderLinkElement($link, $title, $level = null) { - return '
  • ' . __($title) . '
  • '; + return '
  • ' . __($title) . '
  • '; } // phpcs:disable Generic.Metrics.NestingLevel @@ -260,7 +394,8 @@ public function renderSection($section, $config, $title, $collection) if (!$category->getData('mp_exclude_sitemap')) { $html .= $this->renderLinkElement( $this->getCategoryUrl($item->getId()), - $item->getName() + $item->getName(), + $item->getLevel() ); } break; @@ -334,4 +469,12 @@ public function isEnableHtmlSitemap() { return $this->_helper->isEnableHtmlSiteMap(); } + + /** + * @return array|bool|mixed + */ + public function getCategoryDisplayType() + { + return $this->_helper->getHtmlSitemapConfig('display_type'); + } } diff --git a/Model/Source/DisplayType.php b/Model/Source/DisplayType.php new file mode 100644 index 0000000..85e6db4 --- /dev/null +++ b/Model/Source/DisplayType.php @@ -0,0 +1,63 @@ +toArray() as $value => $label) { + $options[] = [ + 'value' => $value, + 'label' => $label + ]; + } + + return $options; + } + + /** + * Get options in "key-value" format + * + * @return array + */ + public function toArray() + { + return [ + self::LIST => __('List'), + self::DROPDOWN => __('Dropdown'), + ]; + } +} diff --git a/Model/Source/SortDirection.php b/Model/Source/SortDirection.php new file mode 100644 index 0000000..e543f53 --- /dev/null +++ b/Model/Source/SortDirection.php @@ -0,0 +1,63 @@ +toArray() as $value => $label) { + $options[] = [ + 'value' => $value, + 'label' => $label + ]; + } + + return $options; + } + + /** + * Get options in "key-value" format + * + * @return array + */ + public function toArray() + { + return [ + self::ASC => __('Ascending'), + self::DESC => __('Descending'), + ]; + } +} diff --git a/Model/Source/SortProduct.php b/Model/Source/SortProduct.php new file mode 100644 index 0000000..e29f982 --- /dev/null +++ b/Model/Source/SortProduct.php @@ -0,0 +1,65 @@ +toArray() as $value => $label) { + $options[] = [ + 'value' => $value, + 'label' => $label + ]; + } + + return $options; + } + + /** + * Get options in "key-value" format + * + * @return array + */ + public function toArray() + { + return [ + self::PRODUCT_IDS => __('Product IDs'), + self::PRODUCT_NAME => __('Product Name'), + self::PRICE => __('Price') + ]; + } +} diff --git a/composer.json b/composer.json index b54281c..cd1f86e 100755 --- a/composer.json +++ b/composer.json @@ -2,11 +2,11 @@ "name": "mageplaza/module-sitemap", "description": "Magento 2 Google XML Sitemap extension", "require": { - "mageplaza/module-core": "^1.4.5", - "mageplaza/magento-2-seo-extension": "^4.0.0" + "mageplaza/module-core": "^1.4.12", + "mageplaza/magento-2-seo-extension": "^4.2.0" }, "type": "magento2-module", - "version": "4.1.0", + "version": "4.2.0", "license": "proprietary", "authors": [ { diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index affe70e..1091831 100755 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -1,154 +1,203 @@ - - - - -
    - - - - - Magento\Config\Model\Config\Source\Yesno - Yes to enable this module.
    - Default URL: http://your-domain.com/sitemap/; See this article
    - 1. It helps to reduce abandonment cart with One Step Checkout.
    - 2. How to setup abandoned cart email series]]>
    -
    - - - Magento\Config\Model\Config\Source\Yesno - - 1 - - - - - Magento\Config\Model\Config\Source\Yesno - - 1 - - - - - validate-digits validate-not-negative-number - - - 1 - 1 - - - - - Magento\Config\Model\Config\Source\Yesno - - 1 - - - - - Magento\Config\Model\Config\Source\Yesno - - 1 - 1 - - - - - Mageplaza\Sitemap\Model\Source\Page - - 1 - 1 - 1 - - - - - Magento\Config\Model\Config\Source\Yesno - - 1 - - - - - - - - One link per line.
    - Example:
    -
    - http://www.mageplaza.com/,Mageplaza
    - https://magento.com/,Magento -
    - ]]> -
    - - 1 - 1 - -
    - - - Magento\Config\Model\Config\Source\Yesno - - 1 - - -
    - - - - - Magento\Config\Model\Config\Source\Yesno - In sitemap file, it will remove the link of the CMS page using for homepage. - - - - Magento\Config\Model\Config\Source\Yesno - Yes, if you want to add more custom links into sitemap XML file. - - - - - 1 - - One link per line. - - - - Magento\Sitemap\Model\Config\Source\Frequency - - 1 - - always, hourly, daily, weekly, monthly, yearly, never. Learn more.]]> - - - - validate-number validate-digits-range digits-range-0-1 - - 1 - - - - -
    -
    -
    + + + + +
    + + + + + Magento\Config\Model\Config\Source\Yesno + Yes to enable this module.
    + Default URL: http://your-domain.com/sitemap/; See this article
    + 1. It helps to reduce abandonment cart with One Step Checkout.
    + 2. How to setup abandoned cart email series]]>
    +
    + + + Magento\Config\Model\Config\Source\Yesno + + 1 + + + + + + E.g: Do not display with the category pages including /men/ such as:
    + /men/top
    + /men/top/t-shirt
    + /clothes/men/
    + Support Regular expression + ]]> +
    + + 1 + 1 + +
    + + + Mageplaza\Sitemap\Model\Source\DisplayType + Choose style to display categories in HTML Sitemap. + + 1 + 1 + + + + + Magento\Config\Model\Config\Source\Yesno + + 1 + + + + + Mageplaza\Sitemap\Model\Source\SortProduct + + 1 + + Choose how to arrange and display products in HTML Site Map. + + + + Mageplaza\Sitemap\Model\Source\SortDirection + + 1 + + + + + Magento\Config\Model\Config\Source\Yesno + If yes, will hide out of stock products in HTML Site Map. + + 1 + + + + + validate-digits validate-not-negative-number + + + 1 + 1 + + + + + Magento\Config\Model\Config\Source\Yesno + + 1 + + + + + Magento\Config\Model\Config\Source\Yesno + + 1 + 1 + + + + + Mageplaza\Sitemap\Model\Source\Page + + 1 + 1 + 1 + + + + + Magento\Config\Model\Config\Source\Yesno + + 1 + + + + + + + - One link per line.
    + Example:
    +
    + http://www.mageplaza.com/,Mageplaza
    + https://magento.com/,Magento +
    + ]]> +
    + + 1 + 1 + +
    + + + Magento\Config\Model\Config\Source\Yesno + + 1 + + +
    + + + + + Magento\Config\Model\Config\Source\Yesno + In sitemap file, it will remove the link of the CMS page using for homepage. + + + + Magento\Config\Model\Config\Source\Yesno + Yes, if you want to add more custom links into sitemap XML file. + + + + + 1 + + One link per line. + + + + Magento\Sitemap\Model\Config\Source\Frequency + + 1 + + always, hourly, daily, weekly, monthly, yearly, never. Learn more.]]> + + + + validate-number validate-digits-range digits-range-0-1 + + 1 + + + + +
    +
    +
    diff --git a/etc/config.xml b/etc/config.xml index 3fe8201..4b18a8a 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -1,35 +1,40 @@ - - - - - - - 1 - - - 1 - daily - - - - \ No newline at end of file + + + + + + + 1 + 0 + list + product_ids + asc + 1 + + + 1 + daily + + + + diff --git a/view/frontend/templates/sitemap.phtml b/view/frontend/templates/sitemap.phtml index 5fad0f3..19b29ca 100755 --- a/view/frontend/templates/sitemap.phtml +++ b/view/frontend/templates/sitemap.phtml @@ -19,10 +19,30 @@ * @license https://www.mageplaza.com/LICENSE.txt */ -/** @var \Mageplaza\Sitemap\Block\Sitemap $block */ +use Mageplaza\Sitemap\Block\Sitemap; + +/** @var Sitemap $block */ ?>
    renderHtmlSiteMap() ?>
    - +getCategoryDisplayType() == 'dropdown') : +?> + +