JEMBOT MAWOT Bypass Shell
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
declare(strict_types=1);
namespace PrestaShop\PrestaShop\Adapter\Category\Repository;
use Category;
use Doctrine\DBAL\Connection;
use PrestaShop\PrestaShop\Core\Domain\Category\Exception\CategoryException;
use PrestaShop\PrestaShop\Core\Domain\Category\Exception\CategoryNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Category\ValueObject\CategoryId;
use PrestaShop\PrestaShop\Core\Domain\Language\ValueObject\LanguageId;
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductId;
use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\ShopNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint;
use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShop\PrestaShop\Core\Repository\AbstractObjectModelRepository;
/**
* Provides access to Category data source
*/
class CategoryRepository extends AbstractObjectModelRepository
{
/**
* @var Connection
*/
private $connection;
/**
* @var string
*/
private $dbPrefix;
/**
* @param Connection $connection
* @param string $dbPrefix
*/
public function __construct(
Connection $connection,
string $dbPrefix
) {
$this->connection = $connection;
$this->dbPrefix = $dbPrefix;
}
/**
* @param CategoryId $categoryId
*
* @return Category
*
* @throws CategoryNotFoundException
*/
public function get(CategoryId $categoryId): Category
{
try {
/** @var Category $category */
$category = $this->getObjectModel(
$categoryId->getValue(),
Category::class,
CategoryException::class
);
} catch (CategoryException $e) {
throw new CategoryNotFoundException($categoryId, $e->getMessage());
}
return $category;
}
/**
* @todo: multishop not considered
*
* @param CategoryId[] $categoryIds
*
* @return array<int, array<int, string>> [$categoryId => [$langId => $categoryName]]
*/
public function getLocalizedNames(array $categoryIds): array
{
$categoryIds = array_map(function ($categoryId) {
return $categoryId->getValue();
}, $categoryIds
);
$qb = $this->connection->createQueryBuilder();
$qb->select('cl.name, cl.id_category, cl.id_lang')
->from($this->dbPrefix . 'category_lang', 'cl')
->where($qb->expr()->in('id_category', ':categoryIds'))
->setParameter('categoryIds', $categoryIds, Connection::PARAM_INT_ARRAY)
;
$results = $qb->execute()->fetchAllAssociative();
if (!$results) {
return [];
}
$localizedNamesByIds = [];
foreach ($results as $result) {
$categoryId = (int) $result['id_category'];
$langId = (int) $result['id_lang'];
$localizedNamesByIds[$categoryId][$langId] = $result['name'];
}
return $localizedNamesByIds;
}
/**
* @param CategoryId $categoryId
*
* @throws CategoryNotFoundException
* @throws CoreException
*/
public function assertCategoryExists(CategoryId $categoryId): void
{
try {
$this->assertObjectModelExists(
$categoryId->getValue(),
'category',
CategoryException::class
);
} catch (CategoryException $e) {
throw new CategoryNotFoundException($categoryId, $e->getMessage());
}
}
/**
* Provides ids of categories which are not unique per shop and language.
*
* @param ShopId $shopId
* @param LanguageId $languageId
*
* @return CategoryId[]
*/
public function getDuplicateNameIds(ShopId $shopId, LanguageId $languageId): array
{
$duplicateNames = $this->getDuplicateNames($shopId, $languageId);
$qb = $this->connection->createQueryBuilder();
$qb->select('c.id_category')
->from($this->dbPrefix . 'category', 'c')
->innerJoin(
'c',
$this->dbPrefix . 'category_lang',
'cl',
'c.id_category = cl.id_category'
)
->where('id_lang = :langId')
->andWhere('id_shop = :shopId')
->andWhere('c.level_depth >= 1')
->andWhere($qb->expr()->in('cl.name', ':duplicateNames'))
->setParameter('langId', $languageId->getValue())
->setParameter('shopId', $shopId->getValue())
->setParameter('duplicateNames', $duplicateNames, Connection::PARAM_STR_ARRAY)
;
$results = $qb->execute()->fetchAllAssociative();
$categoryIds = [];
foreach ($results as $result) {
$categoryIds[] = new CategoryId((int) $result['id_category']);
}
return $categoryIds;
}
/**
* @param CategoryId $categoryId
* @param LanguageId $languageId
*
* @return string[]
*/
public function getBreadcrumbParts(CategoryId $categoryId, LanguageId $languageId): array
{
$categoryQb = $this->connection->createQueryBuilder();
$categoryQb
->select('cl.name, c.nleft, c.nright')
->from($this->dbPrefix . 'category', 'c')
->innerJoin('c', $this->dbPrefix . 'category_lang', 'cl', 'c.id_category = cl.id_category')
->andWhere('c.id_category = :categoryId')
->andWhere('cl.id_lang = :languageId')
->setParameter('categoryId', $categoryId->getValue())
->setParameter('languageId', $languageId->getValue())
;
$category = $categoryQb->execute()->fetchAssociative();
if (empty($category)) {
throw new CategoryNotFoundException($categoryId, 'Cannot find breadcrumb because category does not exist');
}
$categoryName = $category['name'];
$qb = $this->connection->createQueryBuilder();
$qb
->select('c.id_category, cl.name')
->from($this->dbPrefix . 'category', 'c')
->innerJoin('c', $this->dbPrefix . 'category_lang', 'cl', 'c.id_category = cl.id_category')
->andWhere('c.nleft < :left AND c.nright > :right')
->andWhere('cl.id_lang = :languageId')
->andWhere('c.level_depth >= 1')
->addGroupBy('c.id_category')
->addOrderBy('c.id_category', 'ASC')
->addOrderBy('c.position', 'ASC')
->setParameter('left', (int) $category['nleft'])
->setParameter('right', (int) $category['nright'])
->setParameter('languageId', $languageId->getValue())
;
$results = $qb->execute()->fetchAllAssociative();
if ($results) {
$parentNames = array_column($results, 'name');
}
$parentNames[] = $categoryName;
return $parentNames;
}
/**
* @param CategoryId $categoryId
* @param LanguageId $languageId
* @param string $separator
*
* @return string
*/
public function getBreadcrumb(CategoryId $categoryId, LanguageId $languageId, string $separator = ' > '): string
{
return implode($separator, $this->getBreadcrumbParts($categoryId, $languageId));
}
/**
* @param ProductId $productId
* @param ShopConstraint $shopConstraint
*
* @return CategoryId[]
*/
public function getProductCategoryIds(ProductId $productId, ShopConstraint $shopConstraint): array
{
$qb = $this->connection->createQueryBuilder();
$qb->select('cp.id_category')
->from($this->dbPrefix . 'category_product', 'cp')
->where('cp.id_product = :productId')
->setParameter('productId', $productId->getValue())
;
if ($shopConstraint->getShopId()) {
$qb
->innerJoin(
'cp',
$this->dbPrefix . 'category_shop',
'cs',
'cp.id_category = cs.id_category'
)
->andWhere('cs.id_shop = :shopId')
->setParameter('shopId', $shopConstraint->getShopId()->getValue())
;
}
$results = $qb->execute()->fetchAllAssociative();
$categoryIds = [];
foreach ($results as $result) {
$categoryIds[] = new CategoryId((int) $result['id_category']);
}
return $categoryIds;
}
/**
* @param ProductId $productId
* @param CategoryId[] $addedCategories
*/
public function addProductAssociations(ProductId $productId, array $addedCategories): void
{
// Get current categories for all shops, only the one completely absent should be added to avoid duplicate entry
$currentCategoryIds = $this->getProductCategoryIds($productId, ShopConstraint::allShops());
$newCategories = array_filter($addedCategories, static function (CategoryId $addedCategoryId) use ($currentCategoryIds): bool {
foreach ($currentCategoryIds as $currentCategory) {
if ($currentCategory->getValue() === $addedCategoryId->getValue()) {
return false;
}
}
return true;
});
if (empty($newCategories)) {
return;
}
$categoryIds = array_unique(array_map(static function (CategoryId $categoryId): int {
return $categoryId->getValue();
}, $newCategories));
$maxPositions = $this->connection
->createQueryBuilder()
->from($this->dbPrefix . 'category_product', 'cp')
->select('cp.id_category, MAX(cp.position) AS position')
->andWhere('cp.id_category IN (:categories)')
->setParameter('categories', $categoryIds, Connection::PARAM_INT_ARRAY)
->groupBy('cp.id_category')
->execute()->fetchAllAssociative()
;
// Prepare new rows for each category if the max position was not found it's the first product associated
// to the category so its position is 1, else we increment
foreach ($categoryIds as $categoryId) {
$maxCategoryPosition = 1;
foreach ($maxPositions as $maxPosition) {
if ((int) $maxPosition['id_category'] === $categoryId) {
$maxCategoryPosition = (int) $maxPosition['position'] + 1;
}
}
$this->connection->insert(
$this->dbPrefix . 'category_product',
[
'id_category' => $categoryId,
'id_product' => $productId->getValue(),
'position' => $maxCategoryPosition,
]
);
}
}
/**
* @param ProductId $productId
* @param CategoryId[] $removedCategories
*/
public function removeProductAssociations(ProductId $productId, array $removedCategories): void
{
$categoryIds = array_values(array_unique(array_map(static function (CategoryId $categoryId): int {
return $categoryId->getValue();
}, $removedCategories)));
$currentPositions = $this->connection
->createQueryBuilder()
->from($this->dbPrefix . 'category_product', 'cp')
->select('cp.id_category, cp.position')
->where('cp.id_product = :productId')
->andWhere('cp.id_category IN (:categories)')
->setParameter('productId', $productId->getValue())
->setParameter('categories', $categoryIds, Connection::PARAM_INT_ARRAY)
->execute()->fetchAllAssociative()
;
$this->connection
->createQueryBuilder()
->delete($this->dbPrefix . 'category_product')
->where('id_product = :productId')
->andWhere('id_category IN (:categories)')
->setParameter('productId', $productId->getValue())
->setParameter('categories', $categoryIds, Connection::PARAM_INT_ARRAY)
->execute()
;
// Decrement positions for each category impacted
foreach ($currentPositions as $currentPosition) {
$this->connection
->createQueryBuilder()
->update($this->dbPrefix . 'category_position', 'cp')
->set('position', 'position - 1')
->where('cp.id_category = :categoryId AND cp.position > :position')
->setParameters([
'categoryId' => (int) $currentPosition['id_category'],
'position' => (int) $currentPosition['position'],
])
;
}
}
public function getShopDefaultCategory(ShopId $shopId): CategoryId
{
$result = $this->connection
->createQueryBuilder()
->from($this->dbPrefix . 'shop', 's')
->select('s.id_category')
->where('s.id_shop = :shopId')
->setParameter('shopId', $shopId->getValue())
->execute()
->fetchAssociative()
;
if (empty($result['id_category'])) {
throw new ShopNotFoundException(sprintf('Could not find shop with id %d', $shopId->getValue()));
}
return new CategoryId((int) $result['id_category']);
}
/**
* Returns defined product default category for the specified shop, but only if it is associated.
*
* @param ProductId $productId
* @param ShopId $shopId
*
* @return CategoryId|null
*/
public function getProductDefaultCategory(ProductId $productId, ShopId $shopId): ?CategoryId
{
$result = $this->connection
->createQueryBuilder()
->from($this->dbPrefix . 'product_shop', 'ps')
->innerJoin(
'ps',
$this->dbPrefix . 'category_product',
'cp',
'cp.id_product = ps.id_product AND cp.id_category = ps.id_category_default'
)
->innerJoin(
'cp',
$this->dbPrefix . 'category_shop',
'cs',
'cs.id_category = cp.id_category AND cs.id_shop = :shopId'
)
->select('ps.id_category_default')
->where('ps.id_product = :productId')
->andWhere('ps.id_shop = :shopId')
->setParameters([
'productId' => $productId->getValue(),
'shopId' => $shopId->getValue(),
])
->execute()
->fetchAssociative()
;
return !empty($result['id_category_default']) ? new CategoryId((int) $result['id_category_default']) : null;
}
/**
* Provides category names which are not unique per shop and language.
*
* e.g. if certain shop contains following categories in english language:
* Clothes -> Men, Bags -> Men, Clothes -> Woman, Bags -> Women,
* then method should return ["Men", "Women"]
*
* @param ShopId $shopId
* @param LanguageId $languageId
*
* @return string[]
*/
protected function getDuplicateNames(ShopId $shopId, LanguageId $languageId): array
{
$qb = $this->connection->createQueryBuilder()
->select('cl.name')
->from($this->dbPrefix . 'category', 'c')
->innerJoin(
'c',
$this->dbPrefix . 'category_lang', 'cl',
'c.id_category = cl.id_category'
)
->where('id_lang = :langId')
->andWhere('id_shop = :shopId')
->andWhere('c.level_depth >= 1')
->having('COUNT(cl.name) > 1')
->setParameter('langId', $languageId->getValue())
->setParameter('shopId', $shopId->getValue())
->groupBy('cl.name')
;
$results = $qb->execute()->fetchAllAssociative();
$names = [];
foreach ($results as $result) {
$names[] = $result['name'];
}
return $names;
}
}
xxxxx1.0, XXX xxxx