diff --git a/modules/log/commerce_log.commerce_log_categories.yml b/modules/log/commerce_log.commerce_log_categories.yml
index b921707752..7e5acba560 100644
--- a/modules/log/commerce_log.commerce_log_categories.yml
+++ b/modules/log/commerce_log.commerce_log_categories.yml
@@ -5,3 +5,7 @@ commerce_cart:
commerce_order:
label: Order
entity_type: commerce_order
+
+commerce_product:
+ label: Product
+ entity_type: commerce_product
diff --git a/modules/log/commerce_log.commerce_log_templates.yml b/modules/log/commerce_log.commerce_log_templates.yml
index a9aee4860d..4ee3b4aaf2 100644
--- a/modules/log/commerce_log.commerce_log_templates.yml
+++ b/modules/log/commerce_log.commerce_log_templates.yml
@@ -27,3 +27,16 @@ order_assigned:
category: commerce_order
label: 'Order assigned'
template: '
The order was assigned to {{ user }}.
'
+
+variation_added:
+ category: commerce_product
+ label: 'Variation added'
+ template: 'Variation added
id: {{ id }}
sku: {{ sku }}
title: {{ label }}
'
+variation_field_changed:
+ category: commerce_product
+ label: 'Variation changed'
+ template: 'Variation changed
id: {{ id }}
sku: {{ sku }}
changed fields: {{ changed_fields }}
'
+variation_deleted:
+ category: commerce_product
+ label: 'Variation deleted'
+ template: 'Variation deleted
id: {{ id }}
sku: {{ sku }}
'
diff --git a/modules/log/commerce_log.module b/modules/log/commerce_log.module
index 9c4927c624..fa02d01646 100644
--- a/modules/log/commerce_log.module
+++ b/modules/log/commerce_log.module
@@ -6,7 +6,7 @@
*/
/**
- * Implements hook_preprocess_commerce_order().
+ * Implements hook_preprocess_HOOK() for commerce_order.
*/
function commerce_log_preprocess_commerce_order(&$variables) {
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
@@ -20,3 +20,25 @@ function commerce_log_preprocess_commerce_order(&$variables) {
'#title' => t('Order activity'),
];
}
+
+/**
+ * Implements hook_preprocess_HOOK() for commerce_product_form.
+ */
+function commerce_log_preprocess_commerce_product_form(&$variables) {
+ /** @var \Drupal\commerce_product\Entity\ProductInterface $product */
+ if ($product = \Drupal::service('current_route_match')->getParameter('commerce_product')) {
+ $variables['form']['actions']['activity'] = [
+ '#weight' => 100,
+ 'title' => [
+ '#markup' => '' . t('Product activity') . '
',
+ ],
+ 'log' => [
+ '#type' => 'view',
+ '#name' => 'commerce_activity',
+ '#display_id' => 'default',
+ '#arguments' => [$product->id(), 'commerce_product'],
+ '#weight' => 100,
+ ],
+ ];
+ }
+}
diff --git a/modules/log/src/CommerceLogServiceProvider.php b/modules/log/src/CommerceLogServiceProvider.php
index 769cc5fc21..547b6f0dd6 100644
--- a/modules/log/src/CommerceLogServiceProvider.php
+++ b/modules/log/src/CommerceLogServiceProvider.php
@@ -2,6 +2,7 @@
namespace Drupal\commerce_log;
+use Drupal\commerce_log\EventSubscriber\ProductVariationEventSubscriber;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Symfony\Component\DependencyInjection\Reference;
@@ -29,6 +30,12 @@ public function register(ContainerBuilder $container) {
->addTag('event_subscriber')
->addArgument(new Reference('entity_type.manager'));
}
+ if (isset($modules['commerce_product'])) {
+ $container->register('commerce_log.product_variation_subscriber', ProductVariationEventSubscriber::class)
+ ->addTag('event_subscriber')
+ ->addArgument(new Reference('entity_type.manager'))
+ ->addArgument(new Reference('event_dispatcher'));
+ }
}
}
diff --git a/modules/log/src/Event/LogEvents.php b/modules/log/src/Event/LogEvents.php
new file mode 100644
index 0000000000..8198cf8e67
--- /dev/null
+++ b/modules/log/src/Event/LogEvents.php
@@ -0,0 +1,19 @@
+fields = $fields;
+ }
+
+ /**
+ * Gets the fields.
+ *
+ * @return array
+ * The fields.
+ */
+ public function getFields() {
+ return $this->fields;
+ }
+
+ /**
+ * Sets the fields.
+ *
+ * @return \Drupal\commerce_log\Event\ProductVariationChangedFieldsFilterEvent
+ * The product variation changed field filter event.
+ */
+ public function setFields(array $fields) {
+ $this->fields = $fields;
+ return $this;
+ }
+
+}
diff --git a/modules/log/src/EventSubscriber/ProductVariationEventSubscriber.php b/modules/log/src/EventSubscriber/ProductVariationEventSubscriber.php
new file mode 100644
index 0000000000..a21aab8acb
--- /dev/null
+++ b/modules/log/src/EventSubscriber/ProductVariationEventSubscriber.php
@@ -0,0 +1,129 @@
+logStorage = $entityTypeManager->getStorage('commerce_log');
+ $this->eventDispatcher = $eventDispatcher;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getSubscribedEvents() {
+ $events = [
+ ProductEvents::PRODUCT_VARIATION_PRESAVE => ['onVariationPresave', -100],
+ ProductEvents::PRODUCT_VARIATION_PREDELETE => ['onVariationPredelete', -100],
+ ];
+ return $events;
+ }
+
+ /**
+ * Creates a log before saving a product variation.
+ *
+ * @param \Drupal\commerce_product\Event\ProductVariationEvent $event
+ * The variation event.
+ *
+ * @throws \Drupal\Core\Entity\EntityStorageException
+ */
+ public function onVariationPresave(ProductVariationEvent $event) {
+ if ($product = $event->getProductVariation()->getProduct()) {
+ $variation = $event->getProductVariation();
+ $original = $variation->original;
+ // If there isn't a related product yet, then it is a new variation.
+ if (!$original->getProduct()) {
+ $this->logStorage->generate($product, 'variation_added', [
+ 'id' => $event->getProductVariation()->id(),
+ 'sku' => $event->getProductVariation()->getSku(),
+ 'label' => $event->getProductVariation()->label(),
+ ])->save();
+ }
+ elseif ($changedValues = $this->getChangedFields($original, $variation)) {
+ $this->logStorage->generate($product, 'variation_field_changed', [
+ 'id' => $event->getProductVariation()->id(),
+ 'sku' => $event->getProductVariation()->getSku(),
+ 'changed_fields' => json_encode($changedValues, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT),
+ ])->save();
+ }
+ }
+ }
+
+ /**
+ * Creates a log when a product variation is deleted.
+ *
+ * @param \Drupal\commerce_product\Event\ProductVariationEvent $event
+ * The variation event.
+ *
+ * @throws \Drupal\Core\Entity\EntityStorageException
+ */
+ public function onVariationPredelete(ProductVariationEvent $event) {
+ $this->logStorage->generate($event->getProductVariation()->getProduct(), 'variation_deleted', [
+ 'id' => $event->getProductVariation()->id(),
+ 'sku' => $event->getProductVariation()->getSku(),
+ ])->save();
+ }
+
+ /**
+ * Determine the changed fields and their values.
+ *
+ * @param \Drupal\commerce_product\Entity\ProductVariationInterface $original
+ * The original product variation.
+ * @param \Drupal\commerce_product\Entity\ProductVariationInterface $new
+ * The new product variation.
+ *
+ * @return array
+ * A list of changed values
+ */
+ protected function getChangedFields(ProductVariationInterface $original, ProductVariationInterface $new) {
+ $changedFields = new ProductVariationChangedFieldsFilterEvent(['price', 'sku', 'status', 'title']);
+ $this->eventDispatcher->dispatch(LogEvents::PRODUCT_VARIATION_CHANGED_FIELDS_FILTER, $changedFields);
+ $changedValues = [];
+ foreach ($changedFields->getFields() as $field) {
+ $newValue = $new->{$field}->getValue();
+ $originalValue = $original->{$field}->getValue();
+ if ($newValue != $originalValue) {
+ $changedValues[$field] = [
+ 'Original value' => $originalValue,
+ 'New value' => $newValue,
+ ];
+ }
+ }
+ return $changedValues;
+ }
+
+}
diff --git a/modules/log/tests/src/Kernel/OrderIntegrationTest.php b/modules/log/tests/src/Kernel/OrderIntegrationTest.php
index 988c3809fa..3dea83bc3f 100644
--- a/modules/log/tests/src/Kernel/OrderIntegrationTest.php
+++ b/modules/log/tests/src/Kernel/OrderIntegrationTest.php
@@ -167,7 +167,7 @@ public function testPlaceValidateFulfillLogs() {
$logs = $this->logStorage->loadMultipleByEntity($this->order);
$this->assertEquals(2, count($logs));
- $log = $logs[2];
+ $log = array_pop($logs);
$build = $this->logViewBuilder->view($log);
$this->render($build);
@@ -180,7 +180,7 @@ public function testPlaceValidateFulfillLogs() {
$logs = $this->logStorage->loadMultipleByEntity($this->order);
$this->assertEquals(3, count($logs));
- $log = $logs[3];
+ $log = array_pop($logs);
$build = $this->logViewBuilder->view($log);
$this->render($build);