Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# WIP Release notes for Commerce 5.6
# WIP Release notes for Commerce 5.6

- Added `craft\commerce\elements\db\ProductQuery::$savable`.
- Added `craft\commerce\elements\db\ProductQuery::savable()`.
- Added `craft\commerce\elements\db\VariantQuery::$savable`.
- Added `craft\commerce\elements\db\VariantQuery::savable()`.
- Added `craft\commerce\elements\db\VariantQuery::editable()`.
- `craft\commerce\elements\db\ProductQuery::$editable` is now nullable.
- `craft\commerce\elements\db\VariantQuery::$editable` is now nullable.
2 changes: 1 addition & 1 deletion src/elements/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ protected static function defineSources(string $context = null): array
$editable = true;
} else {
$productTypes = Plugin::getInstance()->getProductTypes()->getAllProductTypes();
$editable = false;
$editable = null;
}

$productTypeIds = [];
Expand Down
87 changes: 73 additions & 14 deletions src/elements/db/ProductQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,17 @@
class ProductQuery extends ElementQuery
{
/**
* @var bool Whether to only return products that the user has permission to edit.
* @var bool|null Whether to only return products that the user has permission to view.
* @used-by editable()
*/
public bool $editable = false;
public ?bool $editable = null;

/**
* @var bool|null Whether to only return products that the user has permission to save.
* @used-by savable()
* @since 5.6.0
*/
public ?bool $savable = null;

/**
* @var mixed The Post Date that the resulting products must have.
Expand Down Expand Up @@ -529,17 +537,32 @@ public function after(DateTime|string $value): static
}

/**
* Sets the [[editable]] property.
* Sets the [[$editable]] property.
*
* @param bool $value The property value (defaults to true)
* @param bool|null $value The property value (defaults to true)
* @return static self reference
* @uses $editable
*/
public function editable(bool $value = true): static
public function editable(?bool $value = true): static
{
$this->editable = $value;
return $this;
}

/**
* Sets the [[$savable]] property.
*
* @param bool|null $value The property value (defaults to true)
* @return static self reference
* @uses $savable
* @since 5.6.0
*/
public function savable(?bool $value = true): static
{
$this->savable = $value;
return $this;
}

/**
* Narrows the query results based on the products’ types, per the types’ IDs.
*
Expand Down Expand Up @@ -825,7 +848,8 @@ protected function beforePrepare(): bool
}

$this->_applyHasVariantParam();
$this->_applyEditableParam();
$this->_applyEditableParam($this->editable, 'commerce-editProductType');
$this->_applyEditableParam($this->savable, 'commerce-editProductType');
$this->_applyRefParam();

return parent::beforePrepare();
Expand Down Expand Up @@ -858,26 +882,61 @@ private function _normalizeTypeId(): void
}

/**
* Applies the 'editable' param to the query being prepared.
* Applies an authorization param to the query being prepared.
*
* @param bool|null $value
* @param string $permissionPrefix
* @throws QueryAbortedException
*/
private function _applyEditableParam(): void
private function _applyEditableParam(?bool $value, string $permissionPrefix): void
{
if (!$this->editable) {
if ($value === null) {
return;
}

$user = Craft::$app->getUser()->getIdentity();

if (!$user) {
throw new QueryAbortedException('Could not execute query for product when no user found');
throw new QueryAbortedException();
}

// Limit the query to only the sections the user has permission to edit
$this->subQuery->andWhere([
'commerce_products.typeId' => Plugin::getInstance()->getProductTypes()->getEditableProductTypeIds(),
]);
$productTypes = Plugin::getInstance()->getProductTypes()->getAllProductTypes();

if (empty($productTypes)) {
return;
}

$authorizedTypeIds = [];

foreach ($productTypes as $productType) {
if ($user->can("$permissionPrefix:$productType->uid")) {
$authorizedTypeIds[] = $productType->id;
}
}

if (count($authorizedTypeIds) === count($productTypes)) {
// They have access to everything
if (!$value) {
throw new QueryAbortedException();
}
return;
}

if (empty($authorizedTypeIds)) {
// They don't have access to anything
if ($value) {
throw new QueryAbortedException();
}
return;
}

$condition = ['commerce_products.typeId' => $authorizedTypeIds];

if (!$value) {
$condition = ['not', $condition];
}

$this->subQuery->andWhere($condition);
}

/**
Expand Down
99 changes: 97 additions & 2 deletions src/elements/db/VariantQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,17 @@ class VariantQuery extends PurchasableQuery
protected array $defaultOrderBy = ['elements_owners.sortOrder' => SORT_ASC];

/**
* @var bool Whether to only return variants that the user has permission to edit.
* @var bool|null Whether to only return variants that the user has permission to view.
* @used-by editable()
*/
public bool $editable = false;
public ?bool $editable = null;

/**
* @var bool|null Whether to only return variants that the user has permission to save.
* @used-by savable()
* @since 5.6.0
*/
public ?bool $savable = null;

/**
* @var bool|null
Expand Down Expand Up @@ -432,6 +440,33 @@ public function maxQty(mixed $value): VariantQuery
return $this;
}

/**
* Sets the [[$editable]] property.
*
* @param bool|null $value The property value (defaults to true)
* @return static self reference
* @uses $editable
*/
public function editable(?bool $value = true): static
{
$this->editable = $value;
return $this;
}

/**
* Sets the [[$savable]] property.
*
* @param bool|null $value The property value (defaults to true)
* @return static self reference
* @uses $savable
* @since 5.6.0
*/
public function savable(?bool $value = true): static
{
$this->savable = $value;
return $this;
}

/**
* @param Connection|null $db
* @return VariantCollection
Expand Down Expand Up @@ -741,6 +776,8 @@ protected function beforePrepare(): bool
}

$this->_applyHasProductParam();
$this->_applyEditableParam($this->editable, 'commerce-editProductType');
$this->_applyEditableParam($this->savable, 'commerce-editProductType');

return parent::beforePrepare();
}
Expand Down Expand Up @@ -808,6 +845,64 @@ private function _applyHasProductParam(): void
$this->subQuery->andWhere(['commerce_variants.primaryOwnerId' => $productQuery]);
}

/**
* Applies an authorization param to the query being prepared.
*
* @param bool|null $value
* @param string $permissionPrefix
* @throws QueryAbortedException
*/
private function _applyEditableParam(?bool $value, string $permissionPrefix): void
{
if ($value === null) {
return;
}

$user = Craft::$app->getUser()->getIdentity();

if (!$user) {
throw new QueryAbortedException();
}

$productTypes = Plugin::getInstance()->getProductTypes()->getAllProductTypes();

if (empty($productTypes)) {
return;
}

$authorizedTypeIds = [];

foreach ($productTypes as $productType) {
if ($user->can("$permissionPrefix:$productType->uid")) {
$authorizedTypeIds[] = $productType->id;
}
}

if (count($authorizedTypeIds) === count($productTypes)) {
// They have access to everything
if (!$value) {
throw new QueryAbortedException();
}
return;
}

if (empty($authorizedTypeIds)) {
// They don't have access to anything
if ($value) {
throw new QueryAbortedException();
}
return;
}

$condition = ['commerce_products.typeId' => $authorizedTypeIds];

if (!$value) {
$condition = ['not', $condition];
}

$this->subQuery->andWhere($condition);
}

/**
* Applies the 'productStatus' param to the query being prepared.
*
Expand Down
2 changes: 0 additions & 2 deletions src/fields/Products.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
namespace craft\commerce\fields;

use Craft;
use craft\base\ElementInterface;
use craft\commerce\elements\Product;
use craft\commerce\gql\arguments\elements\Product as ProductArguments;
use craft\commerce\gql\interfaces\elements\Product as ProductInterface;
use craft\commerce\gql\resolvers\elements\Product as ProductResolver;
use craft\commerce\web\assets\editproduct\EditProductAsset;
use craft\fields\BaseRelationField;
use craft\helpers\Gql as GqlHelper;
use craft\services\Gql as GqlService;
Expand Down
Loading