Skip to content

Commit

Permalink
Implementing Category permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
romainruaud committed Feb 25, 2025
1 parent 586009b commit cd21bba
Show file tree
Hide file tree
Showing 7 changed files with 321 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php
/**
* DISCLAIMER
* Do not edit or add to this file if you wish to upgrade Smile ElasticSuite to newer
* versions in the future.
*
* @category Smile
* @package Smile\ElasticsuiteCatalog
* @author Romain Ruaud <[email protected]>
* @copyright 2025 Smile
* @license Open Software License ("OSL") v. 3.0
*/
namespace Smile\ElasticsuiteCatalog\Model\CategoryPermissions\Filter;

use Smile\ElasticsuiteCore\Search\Request\Query\QueryFactory;
use Smile\ElasticsuiteCore\Search\Request\QueryInterface;

/**
* Query Provider for Catalog permissions
*
* @category Smile
* @package Smile\ElasticsuiteCatalog
* @author Romain Ruaud <[email protected]>
*/
class Provider
{
/**
* @var \Smile\ElasticsuiteCore\Search\Request\Query\QueryFactory
*/
private $queryFactory;

/**
* @param \Smile\ElasticsuiteCore\Search\Request\Query\QueryFactory $queryFactory Query Factory
*/
public function __construct(QueryFactory $queryFactory)
{
$this->queryFactory = $queryFactory;
}

/**
* Build a clause to filter on products which are not available for the current customer group id.
* By default, the clause is a "NOT equal to -2" in legacy Magento.s
*
* @param int $customerGroupId Customer Group Id
* @param int $value Permission value
* @param string $operator Operator to use (default is mustNot because the legacy query is "is not denied")
*
* @return \Smile\ElasticsuiteCore\Search\Request\QueryInterface|null
*/
public function getQueryFilter(int $customerGroupId, int $value, string $operator = 'mustNot') : ?QueryInterface
{
$query = $this->queryFactory->create(
QueryInterface::TYPE_NESTED,
[
'path' => 'category_permissions',
'query' => $this->queryFactory->create(
QueryInterface::TYPE_BOOL,
[
'must' => [
$this->queryFactory->create(
QueryInterface::TYPE_TERM,
['field' => 'category_permissions.customer_group_id', 'value' => $customerGroupId]
),
$this->queryFactory->create(
QueryInterface::TYPE_TERM,
['field' => 'category_permissions.permission', 'value' => $value]
),
],
]
),
]
);

if ('mustNot' === $operator) {
$query = $this->queryFactory->create(QueryInterface::TYPE_NOT, ['query' => $query]);
}

return $query;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php
/**
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade to newer versions in the future.
*
* @category Smile
* @package Smile\ElasticsuiteCatalog
* @author Romain Ruaud <[email protected]>
* @copyright 2025 Smile
* @license Open Software License ("OSL") v. 3.0
*/

declare(strict_types = 1);

namespace Smile\ElasticsuiteCatalog\Model\Product\Indexer\Fulltext\Datasource;

use Magento\Framework\ObjectManagerInterface;
use Smile\ElasticsuiteCore\Api\Index\DatasourceInterface;

/**
* Category Permissions data source.
*
* @category Smile
* @package Smile\ElasticsuiteCatalog
* @author Romain Ruaud <[email protected]>
*/
class CategoryPermissions implements DatasourceInterface
{
/**
* @var \Magento\Framework\ObjectManagerInterface
*/
private $objectManager;

/**
* @var \Magento\CatalogPermissions\Model\ResourceModel\Permission\Index|null|false
*/
private $categoryPermissionsIndex;

/**
* @var null|boolean
*/
private $isEnabled = null;

/**
* @param \Magento\Framework\ObjectManagerInterface $objectManager Object Manager
*/
public function __construct(
ObjectManagerInterface $objectManager
) {
$this->objectManager = $objectManager;
}

/**
* {@inheritDoc}
*/
public function addData($storeId, array $indexData)
{
if ($this->isEnabled() && $this->getPermissionsIndex() !== false) {
$permissionData = $this->getPermissionsIndex()->getIndexForProduct(array_keys($indexData), null, $storeId);

foreach ($permissionData as $permission) {
$indexData[(int) $permission['product_id']]['category_permissions'][] = [
'customer_group_id' => (int) $permission['customer_group_id'],
'permission' => (int) $permission['grant_catalog_category_view'],
];
}
}

return $indexData ?? [];
}

/**
* Fetch CategoryPermissions resource model, if the class exist.
*
* @return false|\Magento\CatalogPermissions\Model\ResourceModel\Permission\Index
*/
private function getPermissionsIndex()
{
if (null === $this->categoryPermissionsIndex) {
$this->categoryPermissionsIndex = false;
try {
// Class will be missing if not using Adobe Commerce.
$this->categoryPermissionsIndex = $this->objectManager->get(
\Magento\CatalogPermissions\Model\ResourceModel\Permission\Index::class
);
} catch (\Exception $exception) {
; // Nothing to do, it's already kinda hacky to allow this to happen.
}
}

return $this->categoryPermissionsIndex;
}

/**
* Check if category permissions feature is enabled.
*
* @return bool
*/
private function isEnabled()
{
if (null === $this->isEnabled) {
$this->isEnabled = false;
try {
// Class will be missing if not using Adobe Commerce.
$config = $this->objectManager->get(\Magento\CatalogPermissions\App\ConfigInterface::class);
$this->isEnabled = $config->isEnabled();
} catch (\Exception $exception) {
; // Nothing to do, it's already kinda hacky to allow this to happen.
}
}

return $this->isEnabled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php
/**
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Smile ElasticSuite to newer
* versions in the future.
*
* @category Smile
* @package Smile\ElasticSuiteCatalog
* @author Romain Ruaud <[email protected]>
* @copyright 2025 Smile
* @license Open Software License ("OSL") v. 3.0
*/
namespace Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter;

use Smile\ElasticsuiteCore\Api\Search\Request\Container\FilterInterface;

/**
* Category Permissions filter.
*
* @category Smile
* @package Smile\ElasticsuiteCatalog
* @author Romain Ruaud <[email protected]>
*/
class CategoryPermissions implements FilterInterface
{
/**
* @var \Smile\ElasticsuiteCore\Search\Request\Query\QueryFactory
*/
private $queryFactory;

/**
* @var \Magento\Customer\Model\Session
*/
private $customerSession;

/**
* @var \Smile\ElasticsuiteCatalog\Model\CategoryPermissions\Filter\Provider
*/
private $categoryPermissionsFilter;

/**
* @var \Magento\Framework\ObjectManagerInterface
*/
private $objectManager;

/**
* @var null|boolean
*/
private $isEnabled = null;

/**
* Search Blacklist filter constructor.
*
* @param \Smile\ElasticsuiteCore\Search\Request\Query\QueryFactory $queryFactory Query Factory
* @param \Smile\ElasticsuiteCatalog\Model\CategoryPermissions\Filter\Provider $categoryPermissionsFilter Query Filter for Permissions
* @param \Magento\Customer\Model\Session $customerSession Customer Session
* @param \Magento\Framework\ObjectManagerInterface $objectManager Object Manager
*/
public function __construct(
\Smile\ElasticsuiteCore\Search\Request\Query\QueryFactory $queryFactory,
\Smile\ElasticsuiteCatalog\Model\CategoryPermissions\Filter\Provider $categoryPermissionsFilter,
\Magento\Customer\Model\Session $customerSession,
\Magento\Framework\ObjectManagerInterface $objectManager
) {
$this->queryFactory = $queryFactory;
$this->categoryPermissionsFilter = $categoryPermissionsFilter;
$this->customerSession = $customerSession;
$this->objectManager = $objectManager;
}

/**
* {@inheritDoc}
*/
public function getFilterQuery()
{
$query = null;

if ($this->isEnabled()) {
$query = $this->categoryPermissionsFilter->getQueryFilter(
$this->customerSession->getCustomerGroupId(),
-2 // Cannot use \Magento\CatalogPermissions\Model::PERMISSION_DENY because the class can be missing.
);
}

return $query;
}

/**
* Check if category permissions feature is enabled.
*
* @return bool
*/
private function isEnabled()
{
if (null === $this->isEnabled) {
$this->isEnabled = false;
try {
// Class will be missing if not using Adobe Commerce.
$config = $this->objectManager->get(\Magento\CatalogPermissions\App\ConfigInterface::class);
$this->isEnabled = $config->isEnabled();
} catch (\Exception $exception) {
; // Nothing to do, it's already kinda hacky to allow this to happen.
}
}

return $this->isEnabled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ public function afterGetFilters(
}
}

// Discard Category Permissions fields in all cases. They don't exist in the mapping and cannot be handled like this.
unset($result['category_permissions_field']);
unset($result['category_permissions_value']);

return $result;
}

Expand Down
6 changes: 6 additions & 0 deletions src/module-elasticsuite-catalog/etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@
<item name="stock" xsi:type="object">Smile\ElasticsuiteCatalog\Model\Product\Indexer\Fulltext\Datasource\InventoryData</item>
<item name="searchPositions" xsi:type="object">Smile\ElasticsuiteCatalog\Model\Product\Indexer\Fulltext\Datasource\SearchPositionData</item>
<item name="ignoreOutOfStockPositions" xsi:type="object">Smile\ElasticsuiteCatalog\Model\Product\Indexer\Fulltext\Datasource\IgnoreOutOfStockPositionsData</item>
<item name="categoryPermissions" xsi:type="object">Smile\ElasticsuiteCatalog\Model\Product\Indexer\Fulltext\Datasource\CategoryPermissions</item>
</item>
<item name="catalog_category" xsi:type="array">
<item name="attributes" xsi:type="object">Smile\ElasticsuiteCatalog\Model\Category\Indexer\Fulltext\Datasource\AttributeData</item>
Expand Down Expand Up @@ -402,6 +403,11 @@
<argument name="customerSession" xsi:type="object">Magento\Customer\Model\Session\Proxy</argument>
</arguments>
</type>
<type name="Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\CategoryPermissions">
<arguments>
<argument name="customerSession" xsi:type="object">Magento\Customer\Model\Session\Proxy</argument>
</arguments>
</type>

<!-- register new catalog EAV attributes -->
<type name="Magento\Eav\Model\Entity\Setup\PropertyMapper\Composite">
Expand Down
3 changes: 3 additions & 0 deletions src/module-elasticsuite-catalog/etc/elasticsuite_indices.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@
<field name="search_query.position" type="integer" nestedPath="search_query" />
<field name="search_query.is_blacklisted" type="boolean" nestedPath="search_query" />

<!-- Static fields handled by the "categoryPermissions" datasource -->
<field name="category_permissions.customer_group_id" type="integer" nestedPath="category_permissions" />
<field name="category_permissions.permission" type="integer" nestedPath="category_permissions" />
</mapping>
</type>
</index>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<filter name="stockFilter">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\Stock</filter>
<filter name="visibleInSearch">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\VisibleInSearch</filter>
<filter name="blacklistedProducts">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\SearchBlacklist</filter>
<filter name="categoryPermissions">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\CategoryPermissions</filter>
</filters>
<aggregations>
<provider name="filterableAttributesProvider">searchFilterableAttributesProvider</provider>
Expand All @@ -36,6 +37,7 @@
<filter name="stockFilter">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\Stock</filter>
<filter name="visibleInSearch">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\VisibleInSearch</filter>
<filter name="blacklistedProducts">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\SearchBlacklist</filter>
<filter name="categoryPermissions">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\CategoryPermissions</filter>
</filters>
<aggregations>
<provider name="autocompleteAttributesProvider">autocompleteFilterableAttributesProvider</provider>
Expand All @@ -47,6 +49,7 @@
<filter name="stockFilter">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\Stock</filter>
<filter name="visibleInCatalog">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\VisibleInCatalog</filter>
<filter name="currentCategory">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\CurrentCategory</filter>
<filter name="categoryPermissions">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\CategoryPermissions</filter>
</filters>
<aggregations>
<provider name="filterableAttributesProvider">categoryFilterableAttributesProvider</provider>
Expand All @@ -61,6 +64,7 @@
<filters>
<filter name="stockFilter">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\Stock</filter>
<filter name="visibleInCatalog">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\VisibleInCatalog</filter>
<filter name="categoryPermissions">Smile\ElasticsuiteCatalog\Model\Product\Search\Request\Container\Filter\CategoryPermissions</filter>
</filters>
<aggregations>
<aggregation xsi:type="termBucket" name="attribute_set_id" field="attribute_set_id"/>
Expand Down

0 comments on commit cd21bba

Please sign in to comment.