Skip to content

Commit e2a6187

Browse files
adriendupuismnoconbarw4julitafalconduszadabrt
authored
V5.0.6 (#3069)
--------- Co-authored-by: Marek Nocoń <mnocon@users.noreply.github.com> Co-authored-by: Bartek Wajda <bartlomiej.wajda@ibexa.co> Co-authored-by: adriendupuis <adriendupuis@users.noreply.github.com> Co-authored-by: julitafalcondusza <117284672+julitafalcondusza@users.noreply.github.com> Co-authored-by: Tomasz Dąbrowski <64841871+dabrt@users.noreply.github.com>
1 parent a500b91 commit e2a6187

909 files changed

Lines changed: 61692 additions & 6647 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

code_samples/api/product_catalog/src/Command/ProductVariantCommand.php

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
use Ibexa\Contracts\ProductCatalog\Local\LocalProductServiceInterface;
1010
use Ibexa\Contracts\ProductCatalog\Local\Values\Product\ProductVariantCreateStruct;
1111
use Ibexa\Contracts\ProductCatalog\ProductServiceInterface;
12+
use Ibexa\Contracts\ProductCatalog\Values\Content\Query\Criterion\ProductCriterionAdapter;
1213
use Ibexa\Contracts\ProductCatalog\Values\Product\ProductVariantQuery;
14+
use Ibexa\Contracts\ProductCatalog\Values\Product\Query\Criterion;
1315
use Symfony\Component\Console\Attribute\AsCommand;
1416
use Symfony\Component\Console\Command\Command;
1517
use Symfony\Component\Console\Input\InputArgument;
@@ -46,12 +48,24 @@ protected function execute(InputInterface $input, OutputInterface $output): int
4648
$productCode = $input->getArgument('productCode');
4749
$product = $this->productService->getProduct($productCode);
4850

49-
// Get variants
50-
$variantQuery = new ProductVariantQuery(0, 5);
51+
// Get variants filtered by variant codes
52+
$codeQuery = new ProductVariantQuery();
53+
$codeQuery->setVariantCodes(['DESK-red', 'DESK-blue']);
54+
$specificVariants = $this->productService->findProductVariants($product, $codeQuery)->getVariants();
5155

52-
$variants = $this->productService->findProductVariants($product, $variantQuery)->getVariants();
56+
// Get variants with specific attributes
57+
$combinedQuery = new ProductVariantQuery();
58+
$combinedQuery->setAttributesCriterion(
59+
new ProductCriterionAdapter(
60+
new Criterion\LogicalAnd([
61+
new Criterion\ColorAttribute('color', ['red', 'blue']),
62+
new Criterion\IntegerAttribute('size', 42),
63+
])
64+
)
65+
);
66+
$filteredVariants = $this->productService->findProductVariants($product, $combinedQuery)->getVariants();
5367

54-
foreach ($variants as $variant) {
68+
foreach ($specificVariants as $variant) {
5569
$output->writeln($variant->getName());
5670
$attributes = $variant->getDiscriminatorAttributes();
5771
foreach ($attributes as $attribute) {
@@ -61,12 +75,30 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6175

6276
// Create a variant
6377
$variantCreateStructs = [
64-
new ProductVariantCreateStruct(['color' => 'oak', 'frame_color' => 'white'], 'DESK1'),
65-
new ProductVariantCreateStruct(['color' => 'white', 'frame_color' => 'black'], 'DESK2'),
78+
new ProductVariantCreateStruct(['color' => 'oak', 'frame_color' => 'white'], 'DESK-red'),
79+
new ProductVariantCreateStruct(['color' => 'white', 'frame_color' => 'black'], 'DESK-blue'),
6680
];
6781

6882
$this->localProductService->createProductVariants($product, $variantCreateStructs);
6983

84+
// Search variants across all products
85+
$query = new ProductVariantQuery();
86+
$query->setVariantCodes(['DESK-red', 'DESK-blue']);
87+
$variantList = $this->productService->findVariants($query);
88+
89+
foreach ($variantList->getVariants() as $variant) {
90+
$output->writeln($variant->getName());
91+
}
92+
93+
// Search variants with attribute criterion
94+
$colorQuery = new ProductVariantQuery();
95+
$colorQuery->setAttributesCriterion(
96+
new ProductCriterionAdapter(
97+
new Criterion\ColorAttribute('color', ['red'])
98+
)
99+
);
100+
$redVariants = $this->productService->findVariants($colorQuery);
101+
70102
return self::SUCCESS;
71103
}
72104
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<div>
2+
{# The required attribute for the displayed zone #}
3+
<div data-ibexa-zone-id="{{ zones[0].id }}">
4+
{# If a zone with [0] index contains any blocks #}
5+
{% if zones[0].blocks %}
6+
{# for each block #}
7+
{% for block in blocks %}
8+
{# create a new layer with appropriate ID #}
9+
<div class="landing-page__block block_{{ block.type }}" data-ibexa-block-id="{{ block.id }}">
10+
{# render the block by using the "Ibexa\\Bundle\\FieldTypePage\\Controller\\BlockController::renderAction" controller #}
11+
{# location.id is the ID of the Location of the current content item, block.id is the ID of the current block #}
12+
{{ render_esi(controller('Ibexa\\Bundle\\FieldTypePage\\Controller\\BlockController::renderAction', {
13+
'locationId': locationId,
14+
'blockId': block.id,
15+
'versionNo': versionInfo.versionNo,
16+
'languageCode': field.languageCode
17+
}, ibexa_append_cacheable_query_params(block))) }}
18+
</div>
19+
{% endfor %}
20+
{% endif %}
21+
</div>
22+
</div>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Shopping list service
2+
import ShoppingList from '@ibexa-shopping-list/src/bundle/Resources/public/js/component/shopping.list';
3+
// The Add to shopping list interaction
4+
import { AddToShoppingList } from '@ibexa-shopping-list/src/bundle/Resources/public/js/component/add.to.shopping.list';
5+
// List of all user's shopping lists
6+
import { ShoppingListsList } from '@ibexa-shopping-list/src/bundle/Resources/public/js/component/shopping.lists.list';
7+
8+
(function (global: Window, doc: Document) {
9+
const shoppingList = new ShoppingList();
10+
shoppingList.init(); // Fetch user's shopping lists
11+
12+
const addToShoppingListsNodes = doc.querySelectorAll<HTMLDivElement>('.ibexa-sl-add-to-shopping-list');
13+
addToShoppingListsNodes.forEach((addToShoppingListNode) => {
14+
const addToShoppingList = new AddToShoppingList({ node: addToShoppingListNode, ListClass: ShoppingListsList });
15+
16+
addToShoppingList.init();
17+
});
18+
})(window, window.document);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
ibexa:
2+
system:
3+
default:
4+
content_view:
5+
full:
6+
product:
7+
controller: 'App\Controller\ProductViewController::viewAction'
8+
template: '@ibexadesign/full/product.html.twig'
9+
match:
10+
'@Ibexa\Contracts\ProductCatalog\ViewMatcher\ProductBased\IsProduct': true
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace App\Controller;
4+
5+
use Ibexa\Contracts\Core\Repository\Iterator\BatchIterator;
6+
use Ibexa\Contracts\ProductCatalog\Iterator\BatchIteratorAdapter\ProductVariantFetchAdapter;
7+
use Ibexa\Contracts\ProductCatalog\Local\LocalProductServiceInterface;
8+
use Ibexa\Contracts\ProductCatalog\Values\Product\ProductVariantQuery;
9+
use Ibexa\Core\MVC\Symfony\View\ContentView;
10+
use Ibexa\Core\MVC\Symfony\View\View;
11+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
12+
use Symfony\Component\HttpFoundation\Request;
13+
14+
class ProductViewController extends AbstractController
15+
{
16+
public function __construct(private readonly LocalProductServiceInterface $productService)
17+
{
18+
}
19+
20+
public function viewAction(Request $request, ContentView $view): View
21+
{
22+
$product = $this->productService->getProductFromContent($view->getContent());
23+
if ($product->isBaseProduct()) {
24+
$view->addParameters([
25+
'variants' => new BatchIterator(new ProductVariantFetchAdapter(
26+
$this->productService,
27+
$product,
28+
new ProductVariantQuery(),
29+
)),
30+
]);
31+
}
32+
33+
return $view;
34+
}
35+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{% extends '@ibexadesign/pagelayout.html.twig' %}
2+
3+
{% set product = content|ibexa_get_product %}
4+
5+
{% block meta %}
6+
{% set token = csrf_token ?? csrf_token(ibexa_get_rest_csrf_token_intention()) %}
7+
<meta name="CSRF-Token" content="{{ token }}"/>
8+
<meta name="SiteAccess" content="{{ app.request.get('siteaccess').name }}"/>
9+
{% endblock %}
10+
11+
{% block content %}
12+
<span>{{ ibexa_content_name(content) }}</span>
13+
<code>{{ product.code }}</code>
14+
{% if not product.isBaseProduct() and can_view_shopping_list and can_edit_shopping_list %}
15+
{% set component %}
16+
{% include '@ibexadesign/shopping_list/component/add_to_shopping_list/add_to_shopping_list.html.twig' with {
17+
product_code: product.code,
18+
} %}
19+
{% endset %}
20+
{{ _self.add_to_shopping_list(product, component) }}
21+
{% endif %}
22+
23+
{% if product.isBaseProduct() %}
24+
<ul>
25+
{% for variant in variants %}
26+
<li>
27+
<span>{{ variant.name }}</span>
28+
<code>{{ variant.code }}</code>
29+
{% if can_view_shopping_list and can_edit_shopping_list %}
30+
{% set component %}
31+
{% include '@ibexadesign/shopping_list/component/add_to_shopping_list/add_to_shopping_list.html.twig' with {
32+
product_code: variant.code,
33+
} %}
34+
{% endset %}
35+
{{ _self.add_to_shopping_list(variant, component) }}
36+
{% endif %}
37+
</li>
38+
{% endfor %}
39+
</ul>
40+
{% endif %}
41+
{% endblock %}
42+
43+
{% block javascripts %}
44+
{{ encore_entry_script_tags('add-to-shopping-list-js') }}
45+
{% endblock %}
46+
47+
{% macro add_to_shopping_list(product, component) %}
48+
{% set widget_id = 'add-to-shopping-list-' ~ product.code|slug %}
49+
<div style="display: inline-block;">
50+
<button onclick="(function(){let e=document.getElementById('{{ widget_id }}'); e.style.display=('none'===window.getComputedStyle(e).display)?'block':'none';})()">
51+
Add to shopping list
52+
</button>
53+
<div id="{{ widget_id }}" style="display: none; position: absolute; background: #fff;">
54+
{{ component }}
55+
</div>
56+
</div>
57+
{% endmacro %}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const Encore = require('@symfony/webpack-encore');
4+
const getWebpackConfigs = require('@ibexa/frontend-config/webpack-config/get-configs');
5+
const customConfigsPaths = require('./var/encore/ibexa.webpack.custom.config.js');
6+
7+
const customConfigs = getWebpackConfigs(Encore, customConfigsPaths);
8+
const isReactBlockPathCreated = fs.existsSync('./assets/page-builder/react/blocks');
9+
10+
Encore.reset();
11+
Encore
12+
.setOutputPath('public/build/')
13+
.setPublicPath('/build')
14+
.enableSassLoader()
15+
.enableReactPreset((options) => {
16+
options.runtime = 'classic';
17+
})
18+
.enableSingleRuntimeChunk()
19+
.copyFiles({
20+
from: './assets/images',
21+
to: 'images/[path][name].[ext]',
22+
pattern: /\.(png|svg)$/,
23+
})
24+
.configureBabelPresetEnv((config) => {
25+
config.useBuiltIns = 'usage';
26+
config.corejs = 3;
27+
});
28+
29+
// Welcome page stylesheets
30+
Encore.addEntry('welcome-page-css', [
31+
path.resolve(__dirname, './assets/scss/welcome-page.scss'),
32+
]);
33+
34+
// Welcome page javascripts
35+
Encore.addEntry('welcome-page-js', [
36+
path.resolve(__dirname, './assets/js/welcome.page.js'),
37+
]);
38+
39+
if (isReactBlockPathCreated) {
40+
// React Blocks javascript
41+
Encore.addEntry('react-blocks-js', './assets/js/react.blocks.js');
42+
}
43+
44+
//Encore.addEntry('app', './assets/app.js');
45+
46+
Encore
47+
.enableTypeScriptLoader()
48+
.addAliases({
49+
'@ibexa-shopping-list': path.resolve('./vendor/ibexa/shopping-list'),
50+
'@ibexa-admin-ui': path.resolve('./vendor/ibexa/admin-ui'), // @ibexa-admin-ui/…/text.helper dependency
51+
})
52+
.addEntry('add-to-shopping-list-js', [
53+
path.resolve(__dirname, './assets/js/add-to-shopping-list.ts'),
54+
])
55+
;
56+
57+
const projectConfig = Encore.getWebpackConfig();
58+
59+
projectConfig.name = 'app';
60+
61+
module.exports = [...customConfigs, projectConfig];
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
CREATE TABLE ibexa_shopping_list (
2+
id INT AUTO_INCREMENT NOT NULL,
3+
owner_id INT NOT NULL,
4+
identifier CHAR(36) NOT NULL COMMENT '(DC2Type:guid)',
5+
name VARCHAR(190) DEFAULT NULL,
6+
created_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)',
7+
updated_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)',
8+
is_default TINYINT(1) DEFAULT 0 NOT NULL,
9+
UNIQUE INDEX ibexa_shopping_list_identifier_idx (identifier),
10+
INDEX ibexa_shopping_list_owner_idx (owner_id),
11+
INDEX ibexa_shopping_list_default_idx (is_default),
12+
PRIMARY KEY(id)
13+
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_520_ci` ENGINE = InnoDB;
14+
CREATE TABLE ibexa_shopping_list_entry (
15+
id INT AUTO_INCREMENT NOT NULL,
16+
shopping_list_id INT NOT NULL,
17+
product_code VARCHAR(64) NOT NULL,
18+
identifier CHAR(36) NOT NULL COMMENT '(DC2Type:guid)',
19+
added_at DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)',
20+
UNIQUE INDEX ibexa_shopping_list_entry_identifier_idx (identifier),
21+
INDEX ibexa_shopping_list_entry_list_idx (shopping_list_id),
22+
INDEX ibexa_shopping_list_entry_product_idx (product_code),
23+
INDEX ibexa_shopping_list_entry_added_at_idx (added_at),
24+
UNIQUE INDEX ibexa_shopping_list_entry_unique (shopping_list_id, product_code),
25+
PRIMARY KEY(id)
26+
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_520_ci` ENGINE = InnoDB;
27+
ALTER TABLE ibexa_shopping_list
28+
ADD CONSTRAINT ibexa_shopping_list_owner_fk FOREIGN KEY (owner_id) REFERENCES ibexa_user (contentobject_id) ON UPDATE CASCADE ON DELETE CASCADE;
29+
ALTER TABLE ibexa_shopping_list_entry
30+
ADD CONSTRAINT ibexa_shopping_list_entry_list_fk FOREIGN KEY (shopping_list_id) REFERENCES ibexa_shopping_list (id) ON UPDATE CASCADE ON DELETE CASCADE;
31+
ALTER TABLE ibexa_shopping_list_entry
32+
ADD CONSTRAINT ibexa_shopping_list_entry_product_fk FOREIGN KEY (product_code) REFERENCES ibexa_product (code) ON UPDATE CASCADE ON DELETE CASCADE;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
CREATE TABLE ibexa_shopping_list (
2+
id SERIAL NOT NULL,
3+
owner_id INT NOT NULL,
4+
identifier UUID NOT NULL,
5+
name VARCHAR(190) DEFAULT NULL,
6+
created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
7+
updated_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
8+
is_default BOOLEAN DEFAULT false NOT NULL,
9+
PRIMARY KEY (id)
10+
);
11+
CREATE UNIQUE INDEX ibexa_shopping_list_identifier_idx ON ibexa_shopping_list (identifier);
12+
CREATE INDEX ibexa_shopping_list_owner_idx ON ibexa_shopping_list (owner_id);
13+
CREATE INDEX ibexa_shopping_list_default_idx ON ibexa_shopping_list (is_default);
14+
COMMENT ON COLUMN ibexa_shopping_list.created_at IS '(DC2Type:datetime_immutable)';
15+
COMMENT ON COLUMN ibexa_shopping_list.updated_at IS '(DC2Type:datetime_immutable)';
16+
CREATE TABLE ibexa_shopping_list_entry (
17+
id SERIAL NOT NULL,
18+
shopping_list_id INT NOT NULL,
19+
product_code VARCHAR(64) NOT NULL,
20+
identifier UUID NOT NULL,
21+
added_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
22+
PRIMARY KEY (id)
23+
);
24+
CREATE UNIQUE INDEX ibexa_shopping_list_entry_identifier_idx ON ibexa_shopping_list_entry (identifier);
25+
CREATE INDEX ibexa_shopping_list_entry_list_idx ON ibexa_shopping_list_entry (shopping_list_id);
26+
CREATE INDEX ibexa_shopping_list_entry_product_idx ON ibexa_shopping_list_entry (product_code);
27+
CREATE INDEX ibexa_shopping_list_entry_added_at_idx ON ibexa_shopping_list_entry (added_at);
28+
CREATE UNIQUE INDEX ibexa_shopping_list_entry_unique ON ibexa_shopping_list_entry (shopping_list_id, product_code);
29+
COMMENT ON COLUMN ibexa_shopping_list_entry.added_at IS '(DC2Type:datetime_immutable)';
30+
ALTER TABLE ibexa_shopping_list
31+
ADD CONSTRAINT ibexa_shopping_list_owner_fk FOREIGN KEY (owner_id) REFERENCES ibexa_user (contentobject_id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE;
32+
ALTER TABLE ibexa_shopping_list_entry
33+
ADD CONSTRAINT ibexa_shopping_list_entry_list_fk FOREIGN KEY (shopping_list_id) REFERENCES ibexa_shopping_list (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE;
34+
ALTER TABLE ibexa_shopping_list_entry
35+
ADD CONSTRAINT ibexa_shopping_list_entry_product_fk FOREIGN KEY (product_code) REFERENCES ibexa_product (code) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
- type: reference
2+
mode: load
3+
filename: references/customer_group_references.yml
4+
5+
- type: role
6+
mode: create
7+
metadata:
8+
identifier: Shopping List User
9+
policies:
10+
- module: shopping_list
11+
function: create
12+
limitations:
13+
- identifier: ShoppingListOwner
14+
values: [self]
15+
- module: shopping_list
16+
function: view
17+
limitations:
18+
- identifier: ShoppingListOwner
19+
values: [self]
20+
- module: shopping_list
21+
function: edit
22+
limitations:
23+
- identifier: ShoppingListOwner
24+
values: [self]
25+
- module: shopping_list
26+
function: delete
27+
limitations:
28+
- identifier: ShoppingListOwner
29+
values: [self]
30+
actions:
31+
- action: assign_role_to_user_group
32+
value:
33+
id: 'reference:ref__checkout__customers_user_group__content_id'

0 commit comments

Comments
 (0)