Skip to content

Commit 4947ac7

Browse files
mglamanbojanz
authored andcommitted
Issue #2999508 by mglaman, bojanz: Support variation translations from variations tab
1 parent fe163d5 commit 4947ac7

File tree

4 files changed

+270
-1
lines changed

4 files changed

+270
-1
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Drupal\commerce_product\Controller;
4+
5+
use Drupal\content_translation\Controller\ContentTranslationController;
6+
use Drupal\Core\Routing\RouteMatchInterface;
7+
8+
class ProductVariationTranslationController extends ContentTranslationController {
9+
10+
/**
11+
* {@inheritdoc}
12+
*
13+
* Workaround for #3004037.
14+
* Core does not generate URLs via the given entity, causing the required
15+
* 'commerce_product' parameter to be missing for every variation URL.
16+
*/
17+
public function overview(RouteMatchInterface $route_match, $entity_type_id = NULL) {
18+
$build = parent::overview($route_match, $entity_type_id);
19+
/** @var \Drupal\commerce_product\Entity\ProductVariationInterface $entity */
20+
$entity = $build['#entity'];
21+
foreach ($build['content_translation_overview']['#rows'] as &$row) {
22+
foreach ($row as &$column) {
23+
if (!is_array($column) || empty($column['data']['#type']) || $column['data']['#type'] != 'operations') {
24+
continue;
25+
}
26+
foreach ($column['data']['#links'] as &$link) {
27+
/** @var \Drupal\Core\Url $url */
28+
$url = $link['url'];
29+
$url->setRouteParameter('commerce_product', $entity->getProductId());
30+
}
31+
}
32+
}
33+
return $build;
34+
}
35+
36+
}

modules/product/src/Entity/ProductVariation.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@
4949
* },
5050
* admin_permission = "administer commerce_product",
5151
* translatable = TRUE,
52-
* content_translation_ui_skip = TRUE,
52+
* translation = {
53+
* "content_translation" = {
54+
* "access_callback" = "content_translation_translate_access"
55+
* },
56+
* },
5357
* base_table = "commerce_product_variation",
5458
* data_table = "commerce_product_variation_field_data",
5559
* entity_keys = {
@@ -66,6 +70,10 @@
6670
* "duplicate-form" = "/product/{commerce_product}/variations/{commerce_product_variation}/duplicate",
6771
* "delete-form" = "/product/{commerce_product}/variations/{commerce_product_variation}/delete",
6872
* "collection" = "/product/{commerce_product}/variations",
73+
* "drupal:content-translation-overview" = "/product/{commerce_product}/variations/{commerce_product_variation}/translations",
74+
* "drupal:content-translation-add" = "/product/{commerce_product}/variations/{commerce_product_variation}/translations/add/{source}/{target}",
75+
* "drupal:content-translation-edit" = "/product/{commerce_product}/variations/{commerce_product_variation}/translations/edit/{language}",
76+
* "drupal:content-translation-delete" = "/product/{commerce_product}/variations/{commerce_product_variation}/translations/delete/{language}",
6977
* },
7078
* bundle_entity_type = "commerce_product_variation_type",
7179
* field_ui_base_route = "entity.commerce_product_variation_type.edit_form",

modules/product/src/ProductVariationRouteProvider.php

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,54 @@
33
namespace Drupal\commerce_product;
44

55
use Drupal\commerce_product\Controller\ProductVariationController;
6+
use Drupal\commerce_product\Controller\ProductVariationTranslationController;
7+
use Drupal\Core\Entity\EntityFieldManagerInterface;
68
use Drupal\Core\Entity\EntityTypeInterface;
9+
use Drupal\Core\Entity\EntityTypeManagerInterface;
710
use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
11+
use Drupal\Core\Extension\ModuleHandlerInterface;
12+
use Symfony\Component\DependencyInjection\ContainerInterface;
813
use Symfony\Component\Routing\Route;
914

1015
/**
1116
* Provides routes for the product variation entity.
1217
*/
1318
class ProductVariationRouteProvider extends DefaultHtmlRouteProvider {
1419

20+
/**
21+
* The module handler.
22+
*
23+
* @var \Drupal\Core\Extension\ModuleHandlerInterface
24+
*/
25+
protected $moduleHandler;
26+
27+
/**
28+
* Constructs a new ProductVariationRouteProvider.
29+
*
30+
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
31+
* The entity type manager.
32+
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
33+
* The entity field manager.
34+
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
35+
* The module handler.
36+
*/
37+
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, ModuleHandlerInterface $module_handler) {
38+
parent::__construct($entity_type_manager, $entity_field_manager);
39+
40+
$this->moduleHandler = $module_handler;
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
47+
return new static(
48+
$container->get('entity_type.manager'),
49+
$container->get('entity_field.manager'),
50+
$container->get('module_handler')
51+
);
52+
}
53+
1554
/**
1655
* {@inheritdoc}
1756
*/
@@ -21,6 +60,91 @@ public function getRoutes(EntityTypeInterface $entity_type) {
2160
$collection->add('entity.commerce_product_variation.duplicate_form', $duplicate_route);
2261
}
2362

63+
// Core can't generate the translation routes until #3004038 gets fixed.
64+
if ($this->moduleHandler->moduleExists('content_translation')) {
65+
$default_parameters = [
66+
'commerce_product' => [
67+
'type' => 'entity:commerce_product',
68+
],
69+
'commerce_product_variation' => [
70+
'type' => 'entity:' . 'commerce_product_variation',
71+
],
72+
];
73+
74+
$overview_route = new Route($entity_type->getLinkTemplate('drupal:content-translation-overview'));
75+
$overview_route
76+
->addDefaults([
77+
'_controller' => ProductVariationTranslationController::class . '::overview',
78+
'entity_type_id' => 'commerce_product_variation',
79+
])
80+
->setRequirements([
81+
'_entity_access' => 'commerce_product_variation.view',
82+
'_access_content_translation_overview' => 'commerce_product_variation',
83+
])
84+
->setOption('parameters', $default_parameters)
85+
->setOption('_admin_route', TRUE);
86+
87+
$add_route = new Route($entity_type->getLinkTemplate('drupal:content-translation-add'));
88+
$add_route
89+
->addDefaults([
90+
'_controller' => ProductVariationTranslationController::class . '::add',
91+
'source' => NULL,
92+
'target' => NULL,
93+
'_title' => 'Add',
94+
'entity_type_id' => 'commerce_product_variation',
95+
])
96+
->setRequirements([
97+
'_entity_access' => 'commerce_product_variation.view',
98+
'_access_content_translation_manage' => 'create',
99+
])
100+
->setOption('parameters', $default_parameters + [
101+
'source' => [
102+
'type' => 'language',
103+
],
104+
'target' => [
105+
'type' => 'language',
106+
],
107+
])
108+
->setOption('_admin_route', TRUE);
109+
110+
$edit_route = new Route($entity_type->getLinkTemplate('drupal:content-translation-edit'));
111+
$edit_route
112+
->addDefaults([
113+
'_controller' => ProductVariationTranslationController::class . '::edit',
114+
'language' => NULL,
115+
'_title' => 'Edit',
116+
'entity_type_id' => 'commerce_product_variation',
117+
])
118+
->setRequirement('_access_content_translation_manage', 'update')
119+
->setOption('parameters', $default_parameters + [
120+
'language' => [
121+
'type' => 'language',
122+
],
123+
])
124+
->setOption('_admin_route', TRUE);
125+
126+
$delete_route = new Route($entity_type->getLinkTemplate('drupal:content-translation-delete'));
127+
$delete_route
128+
->addDefaults([
129+
'_entity_form' => 'commerce_product_variation.content_translation_deletion',
130+
'language' => NULL,
131+
'_title' => 'Delete',
132+
'entity_type_id' => 'commerce_product_variation',
133+
])
134+
->setRequirement('_access_content_translation_manage', 'delete')
135+
->setOption('parameters', $default_parameters + [
136+
'language' => [
137+
'type' => 'language',
138+
],
139+
])
140+
->setOption('_admin_route', TRUE);
141+
142+
$collection->add('entity.commerce_product_variation.content_translation_overview', $overview_route);
143+
$collection->add('entity.commerce_product_variation.content_translation_add', $add_route);
144+
$collection->add("entity.commerce_product_variation.content_translation_edit", $edit_route);
145+
$collection->add("entity.commerce_product_variation.content_translation_delete", $delete_route);
146+
}
147+
24148
return $collection;
25149
}
26150

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
namespace Drupal\Tests\commerce_product\Functional;
4+
5+
use Drupal\commerce_price\Price;
6+
use Drupal\Core\Url;
7+
8+
/**
9+
* Tests translating products and variations.
10+
*
11+
* @group commerce
12+
*/
13+
class ProductTranslationTest extends ProductBrowserTestBase {
14+
15+
/**
16+
* Modules to enable.
17+
*
18+
* @var array
19+
*/
20+
public static $modules = [
21+
'config_translation',
22+
'content_translation',
23+
];
24+
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
protected function getAdministratorPermissions() {
29+
return array_merge([
30+
'administer commerce_product_attribute',
31+
'administer languages',
32+
'administer content translation',
33+
'translate any entity',
34+
'translate configuration',
35+
'access content overview',
36+
'create content translations',
37+
'update content translations',
38+
'delete content translations',
39+
], parent::getAdministratorPermissions());
40+
}
41+
42+
/**
43+
* {@inheritdoc}
44+
*/
45+
public function setUp() {
46+
parent::setUp();
47+
48+
// Add the French language.
49+
$edit = ['predefined_langcode' => 'fr'];
50+
$this->drupalGet('admin/config/regional/language/add');
51+
$this->submitForm($edit, t('Add language'));
52+
53+
// Enable content translation on products and variations.
54+
$this->drupalGet('admin/config/regional/content-language');
55+
$edit = [
56+
'entity_types[commerce_product]' => TRUE,
57+
'settings[commerce_product][default][translatable]' => TRUE,
58+
'entity_types[commerce_product_variation]' => TRUE,
59+
'settings[commerce_product_variation][default][translatable]' => TRUE,
60+
];
61+
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
62+
// Adding languages requires a container rebuild in the test running
63+
// environment so that multilingual services are used.
64+
$this->resetAll();
65+
}
66+
67+
/**
68+
* Test translating a product and its variations.
69+
*/
70+
public function testProductTranslation() {
71+
$product = $this->createEntity('commerce_product', [
72+
'type' => 'default',
73+
'title' => 'Translation test product',
74+
'stores' => $this->stores,
75+
]);
76+
$this->createEntity('commerce_product_variation', [
77+
'type' => 'default',
78+
'product_id' => $product->id(),
79+
'sku' => $this->randomMachineName(),
80+
'price' => new Price('9.99', 'USD'),
81+
]);
82+
$this->drupalGet($product->toUrl('edit-form'));
83+
$this->getSession()->getPage()->clickLink('Translate');
84+
$this->assertSession()->linkByHrefExists("/product/{$product->id()}/translations/add/en/fr");
85+
$this->getSession()->getPage()->clickLink('Add');
86+
$this->getSession()->getPage()->fillField('Title', 'Produit de test de traduction');
87+
$this->getSession()->getPage()->pressButton('Save (this translation)');
88+
$this->assertSession()->pageTextContains('The product Produit de test de traduction has been successfully saved.');
89+
90+
$this->drupalGet(Url::fromRoute('entity.commerce_product_variation.collection', [
91+
'commerce_product' => $product->id(),
92+
]));
93+
$this->assertSession()->linkByHrefExists('/product/1/variations/1/translations');
94+
$this->getSession()->getPage()->clickLink('Translate');
95+
$this->assertSession()->linkByHrefExists('/fr/product/1/variations/1/translations/add/en/fr');
96+
$this->getSession()->getPage()->clickLink('Add');
97+
$this->getSession()->getPage()->pressButton('Save');
98+
$this->assertSession()->pageTextContains('Saved the Produit de test de traduction variation.');
99+
}
100+
101+
}

0 commit comments

Comments
 (0)