diff --git a/modules/log/commerce_log.commerce_log_templates.yml b/modules/log/commerce_log.commerce_log_templates.yml
index 469d9d5b..f07d5eb2 100644
--- a/modules/log/commerce_log.commerce_log_templates.yml
+++ b/modules/log/commerce_log.commerce_log_templates.yml
@@ -63,7 +63,15 @@ mail_order_receipt_failure:
commerce_order_admin_comment:
category: commerce_order
label: 'Admin comment'
- template: '
Admin comment:
{{ comment }}
'
+ template: 'Admin comment:
{{ comment|raw }}
'
+commerce_order_from_customer_comment:
+ category: commerce_order
+ label: 'Comment from customer'
+ template: 'From customer:
{{ comment|raw }}
'
+commerce_order_to_customer_comment:
+ category: commerce_order
+ label: 'Comment to customer'
+ template: '{% if notify %}Emailed comment to customer{% else %}Comment to customer{% endif %}:
{{ comment|raw }}
'
payment_added:
category: commerce_payment
label: 'Payment added'
diff --git a/modules/log/commerce_log.module b/modules/log/commerce_log.module
index 43280b8f..ceaa5403 100644
--- a/modules/log/commerce_log.module
+++ b/modules/log/commerce_log.module
@@ -41,3 +41,45 @@ function commerce_log_add_form_submit($form, FormStateInterface $form_state) {
$order = $order_storage->load($form_state->getValue('order_id'));
$log_storage->generate($order, 'order_created_admin')->save();
}
+
+/**
+ * Implements hook_theme().
+ */
+function commerce_log_theme($existing, $type, $theme, $path) {
+ return [
+ 'commerce_log_order_comments' => [
+ 'variables' => [
+ 'order_entity' => NULL,
+ 'billing_information' => NULL,
+ 'shipping_information' => NULL,
+ 'payment_method' => NULL,
+ 'totals' => NULL,
+ 'order_comments' => [],
+ ],
+ ],
+ ];
+}
+
+/**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+function commerce_log_theme_suggestions_commerce_log_order_comments(array $variables) {
+ $suggestions = [];
+ if (!empty($variables['order_entity'])) {
+ $suggestions[] = $variables['theme_hook_original'] . '__' . $variables['order_entity']->bundle();
+ }
+ return $suggestions;
+}
+
+/**
+ * Implements hook_mail().
+ */
+function commerce_log_mail($key, &$message, $params) {
+ if (!empty($params['headers'])) {
+ $message['headers'] = array_merge($message['headers'], $params['headers']);
+ }
+
+ $message['from'] = $params['from'];
+ $message['subject'] = $params['subject'];
+ $message['body'][] = $params['body'];
+}
diff --git a/modules/log/src/CommerceLogServiceProvider.php b/modules/log/src/CommerceLogServiceProvider.php
index 120fe59d..288b2304 100644
--- a/modules/log/src/CommerceLogServiceProvider.php
+++ b/modules/log/src/CommerceLogServiceProvider.php
@@ -32,7 +32,9 @@ class CommerceLogServiceProvider extends ServiceProviderBase {
if (isset($modules['commerce_order'])) {
$container->register('commerce_log.order_subscriber', 'Drupal\commerce_log\EventSubscriber\OrderEventSubscriber')
->addTag('event_subscriber')
- ->addArgument(new Reference('entity_type.manager'));
+ ->addArgument(new Reference('entity_type.manager'))
+ ->addArgument(new Reference('plugin.manager.mail'))
+ ->addArgument(new Reference('renderer'));
$container->register('commerce_log.order_mail_subscriber', 'Drupal\commerce_log\EventSubscriber\OrderMailEventSubscriber')
->addTag('event_subscriber')
diff --git a/modules/log/src/Event/OrderCommentEvent.php b/modules/log/src/Event/OrderCommentEvent.php
new file mode 100644
index 00000000..4bf4f7cd
--- /dev/null
+++ b/modules/log/src/Event/OrderCommentEvent.php
@@ -0,0 +1,122 @@
+order = $order;
+ $this->type = $type;
+ $this->comment = $comment;
+ $this->notify = $notify;
+ }
+
+ /**
+ * Gets the order.
+ *
+ * @return \Drupal\commerce_order\Entity\OrderInterface
+ * The order.
+ */
+ public function getOrder(): OrderInterface {
+ return $this->order;
+ }
+
+ /**
+ * Gets the order comment.
+ *
+ * @return string
+ * The comment.
+ */
+ public function getComment(): string {
+ return $this->comment;
+ }
+
+ /**
+ * Returns the choice to email customer or not.
+ *
+ * @return bool
+ * The choice to email customer or not.
+ */
+ public function notifyCustomer(): bool {
+ return $this->notify;
+ }
+
+ /**
+ * Returns EventType ID.
+ *
+ * @return string
+ * The EventType ID.
+ */
+ public function getEventType(): string {
+ return $this->type;
+ }
+
+}
diff --git a/modules/log/src/EventSubscriber/OrderEventSubscriber.php b/modules/log/src/EventSubscriber/OrderEventSubscriber.php
index 59d9ed18..d8908d29 100644
--- a/modules/log/src/EventSubscriber/OrderEventSubscriber.php
+++ b/modules/log/src/EventSubscriber/OrderEventSubscriber.php
@@ -2,13 +2,20 @@
namespace Drupal\commerce_log\EventSubscriber;
+use Drupal\commerce_log\Event\OrderCommentEvent;
use Drupal\commerce_order\Event\OrderAssignEvent;
+use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Mail\MailManagerInterface;
+use Drupal\Core\Render\Renderer;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\state_machine\Event\WorkflowTransitionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class OrderEventSubscriber implements EventSubscriberInterface {
+ use StringTranslationTrait;
+
/**
* The log storage.
*
@@ -16,23 +23,54 @@ class OrderEventSubscriber implements EventSubscriberInterface {
*/
protected $logStorage;
+ /**
+ * The mail manager.
+ *
+ * @var \Drupal\Core\Mail\MailManagerInterface
+ */
+ protected $mailManager;
+
+ /**
+ * The renderer.
+ *
+ * @var \Drupal\Core\Render\RendererInterface
+ */
+ protected $renderer;
+
+ /**
+ * The profile view builder.
+ *
+ * @var \Drupal\profile\ProfileViewBuilder
+ */
+ protected $profileViewBuilder;
+
/**
* Constructs a new OrderEventSubscriber object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
+ * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
+ * The mail manager.
+ * @param \Drupal\Core\Render\Renderer $renderer
+ * The renderer.
*/
- public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+ public function __construct(EntityTypeManagerInterface $entity_type_manager, MailManagerInterface $mail_manager, Renderer $renderer) {
$this->logStorage = $entity_type_manager->getStorage('commerce_log');
+ $this->mailManager = $mail_manager;
+ $this->renderer = $renderer;
+ $this->profileViewBuilder = $entity_type_manager->getViewBuilder('profile');
}
/**
* {@inheritdoc}
*/
- public static function getSubscribedEvents() {
+ public static function getSubscribedEvents(): array {
return [
'commerce_order.order.assign' => ['onOrderAssign', -100],
'commerce_order.post_transition' => ['onOrderPostTransition'],
+ 'commerce_order.comment.admin' => ['onAdminComment', -100],
+ 'commerce_order.comment.from_customer' => ['onFromCustomerComment', -100],
+ 'commerce_order.comment.to_customer' => ['onToCustomerComment', -100],
];
}
@@ -41,8 +79,10 @@ class OrderEventSubscriber implements EventSubscriberInterface {
*
* @param \Drupal\commerce_order\Event\OrderAssignEvent $event
* The order assign event.
+ *
+ * @throws \Drupal\Core\Entity\EntityStorageException
*/
- public function onOrderAssign(OrderAssignEvent $event) {
+ public function onOrderAssign(OrderAssignEvent $event): void {
$order = $event->getOrder();
$this->logStorage->generate($order, 'order_assigned', [
'user' => $event->getCustomer()->getDisplayName(),
@@ -54,8 +94,10 @@ class OrderEventSubscriber implements EventSubscriberInterface {
*
* @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
* The transition event.
+ *
+ * @throws \Drupal\Core\Entity\EntityStorageException
*/
- public function onOrderPostTransition(WorkflowTransitionEvent $event) {
+ public function onOrderPostTransition(WorkflowTransitionEvent $event): void {
$transition = $event->getTransition();
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $event->getEntity();
@@ -69,4 +111,98 @@ class OrderEventSubscriber implements EventSubscriberInterface {
])->save();
}
+ /**
+ * Adds admin facing comment to an order.
+ *
+ * @param \Drupal\commerce_log\Event\OrderCommentEvent $event
+ * The event.
+ *
+ * @throws \Drupal\Core\Entity\EntityStorageException
+ */
+ public function onAdminComment(OrderCommentEvent $event): void {
+ $this->logStorage->generate($event->getOrder(), 'commerce_order_admin_comment', [
+ 'comment' => $event->getComment(),
+ 'notify' => $event->notifyCustomer(),
+ ])->save();
+ }
+
+ /**
+ * Adds customer facing order checkout comment.
+ *
+ * @param \Drupal\commerce_log\Event\OrderCommentEvent $event
+ * The event.
+ *
+ * @throws \Drupal\Core\Entity\EntityStorageException
+ */
+ public function onFromCustomerComment(OrderCommentEvent $event): void {
+ $this->logStorage->generate($event->getOrder(), 'commerce_order_from_customer_comment', [
+ 'comment' => $event->getComment(),
+ 'notify' => $event->notifyCustomer(),
+ ])->save();
+
+ // Because we display customer comments on their order view, we need to
+ // flush the caches, so it updates.
+ $order = $event->getOrder();
+ Cache::invalidateTags($order->getCacheTags());
+ }
+
+ /**
+ * Adds customer facing order comment and sends email notification if checked.
+ *
+ * @param \Drupal\commerce_log\Event\OrderCommentEvent $event
+ * The event.
+ *
+ * @throws \Exception
+ */
+ public function onToCustomerComment(OrderCommentEvent $event): void {
+ $this->logStorage->generate($event->getOrder(), 'commerce_order_to_customer_comment', [
+ 'comment' => $event->getComment(),
+ 'notify' => $event->notifyCustomer(),
+ ])->save();
+
+ // Because we display customer comments on their order view, we need to
+ // flush the caches, so it updates.
+ $order = $event->getOrder();
+ Cache::invalidateTags($order->getCacheTags());
+
+ // If notify customer is selected, send email of order comment.
+ if ($event->notifyCustomer() && $order->getEmail()) {
+ $build = [
+ '#theme' => 'commerce_log_order_comments',
+ '#order_entity' => $order,
+ '#totals' => \Drupal::service('commerce_order.order_total_summary')->buildTotals($order),
+ ];
+
+ // Get billing profile.
+ if ($order->getBillingProfile()) {
+ $build['#billing_information'] = $this->profileViewBuilder->view($order->getBillingProfile());
+ }
+
+ // Get shipping profile.
+ if (\Drupal::service('module_handler')->moduleExists('commerce_shipping')) {
+ $summary = \Drupal::service('commerce_shipping.order_shipment_summary')->build($order);
+ if (!empty($summary)) {
+ $build['#shipping_information'] = $summary;
+ }
+ }
+
+ // Get order comments.
+ $build['#order_comments'] = \Drupal::service('commerce_order.order_receipt_mail')->getOrderComments($order);
+
+ $params = [
+ 'headers' => [
+ 'Content-Type' => 'text/html; charset=UTF-8;',
+ 'Content-Transfer-Encoding' => '8Bit',
+ ],
+ 'from' => $order->getStore()->getEmail(),
+ 'subject' => $this->t('New comment on order #@number', ['@number' => $order->getOrderNumber()]),
+ 'order' => $order,
+ 'body' => $this->renderer->render($build),
+ ];
+
+ $customer = $order->getCustomer();
+ $this->mailManager->mail('commerce_log', 'comment', $order->getEmail(), $customer->getPreferredLangcode(), $params);
+ }
+ }
+
}
diff --git a/modules/log/src/Form/LogCommentForm.php b/modules/log/src/Form/LogCommentForm.php
index ee8ba96f..9b0967e3 100644
--- a/modules/log/src/Form/LogCommentForm.php
+++ b/modules/log/src/Form/LogCommentForm.php
@@ -2,8 +2,10 @@
namespace Drupal\commerce_log\Form;
+use Drupal\commerce_log\Event\OrderCommentEvent;
use Drupal\commerce_log\LogStorageInterface;
use Drupal\commerce_log\LogTemplateManagerInterface;
+use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
@@ -11,6 +13,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class LogCommentForm extends FormBase {
@@ -28,6 +31,13 @@ class LogCommentForm extends FormBase {
*/
protected $logTemplateManager;
+ /**
+ * The event dispatcher.
+ *
+ * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+ */
+ protected $eventDispatcher;
+
/**
* Constructs a new LogCommentForm object.
*
@@ -35,10 +45,13 @@ class LogCommentForm extends FormBase {
* The entity type manager.
* @param \Drupal\commerce_log\LogTemplateManagerInterface $log_template_manager
* The log template manager.
+ * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
+ * The event dispatcher.
*/
- public function __construct(EntityTypeManagerInterface $entity_type_manager, LogTemplateManagerInterface $log_template_manager) {
+ public function __construct(EntityTypeManagerInterface $entity_type_manager, LogTemplateManagerInterface $log_template_manager, EventDispatcherInterface $event_dispatcher) {
$this->entityTypeManager = $entity_type_manager;
$this->logTemplateManager = $log_template_manager;
+ $this->eventDispatcher = $event_dispatcher;
}
/**
@@ -47,7 +60,8 @@ class LogCommentForm extends FormBase {
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
- $container->get('plugin.manager.commerce_log_template')
+ $container->get('plugin.manager.commerce_log_template'),
+ $container->get('event_dispatcher')
);
}
@@ -94,6 +108,29 @@ class LogCommentForm extends FormBase {
'#title_display' => 'invisible',
'#required' => TRUE,
];
+
+ if ($source_entity_type == 'commerce_order') {
+ $form['log_comment']['type'] = [
+ '#type' => 'radios',
+ '#title' => $this->t('Comment type'),
+ '#default_value' => OrderCommentEvent::ORDER_ADMIN_COMMENT,
+ '#options' => [
+ OrderCommentEvent::ORDER_ADMIN_COMMENT => $this->t('Admin-only comment'),
+ OrderCommentEvent::ORDER_TO_CUSTOMER_COMMENT => $this->t('Customer-facing comment'),
+ ],
+ ];
+ $form['log_comment']['notify'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Notify customer by email'),
+ '#default_value' => FALSE,
+ '#states' => [
+ 'enabled' => [
+ ':input[name="type"]' => ['value' => OrderCommentEvent::ORDER_TO_CUSTOMER_COMMENT],
+ ],
+ ],
+ ];
+ }
+
$form['log_comment']['actions']['#type'] = 'actions';
$form['log_comment']['actions']['submit'] = [
'#type' => 'submit',
@@ -114,7 +151,14 @@ class LogCommentForm extends FormBase {
$entity = $storage->load($form_state->getValue('source_entity_id'));
assert($entity instanceof ContentEntityInterface);
$comment = nl2br(Html::escape($form_state->getValue('comment')));
- $log_storage->generate($entity, $form_state->getValue('log_template_id'), ['comment' => $comment])->save();
+ if ($entity instanceof OrderInterface) {
+ // Dispatch the comment event.
+ $event = new OrderCommentEvent($entity, $form_state->getValue('type'), $comment, $form_state->getValue('notify'));
+ $this->eventDispatcher->dispatch($event, $event->getEventType());
+ }
+ else {
+ $log_storage->generate($entity, $form_state->getValue('log_template_id'), ['comment' => $comment])->save();
+ }
$this->messenger()->addStatus($this->t('Comment saved'));
}
diff --git a/modules/log/src/LogCommentPermissions.php b/modules/log/src/LogCommentPermissions.php
index e7802e70..5db7b79a 100644
--- a/modules/log/src/LogCommentPermissions.php
+++ b/modules/log/src/LogCommentPermissions.php
@@ -49,7 +49,7 @@ class LogCommentPermissions implements ContainerInjectionInterface {
}
/**
- * Builds a list of permissions for entity types that support comments..
+ * Builds a list of permissions for entity types that support comments.
*
* @return array
* The permissions.
@@ -66,6 +66,12 @@ class LogCommentPermissions implements ContainerInjectionInterface {
'restrict access' => TRUE,
'provider' => $entity_type->getProvider(),
];
+ $permissions["add commerce_log {$entity_type_id} customer comment"] = [
+ 'title' => $this->t('Add customer comments to own @label', ['@label' => $entity_type->getSingularLabel()]),
+ 'description' => $this->t('Provides the ability to add customer comments to @label.', ['@label' => $entity_type->getPluralLabel()]),
+ 'restrict access' => TRUE,
+ 'provider' => $entity_type->getProvider(),
+ ];
}
}
return $permissions;
diff --git a/modules/log/src/Plugin/Commerce/CheckoutPane/CustomerCommentsPane.php b/modules/log/src/Plugin/Commerce/CheckoutPane/CustomerCommentsPane.php
new file mode 100644
index 00000000..09fd51cf
--- /dev/null
+++ b/modules/log/src/Plugin/Commerce/CheckoutPane/CustomerCommentsPane.php
@@ -0,0 +1,119 @@
+eventDispatcher = $event_dispatcher;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, CheckoutFlowInterface $checkout_flow = NULL) {
+ return new static(
+ $configuration,
+ $plugin_id,
+ $plugin_definition,
+ $checkout_flow,
+ $container->get('entity_type.manager'),
+ $container->get('event_dispatcher')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isVisible() {
+ return \Drupal::currentUser()->hasPermission('add commerce_log commerce_order customer comment');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildPaneSummary() {
+ $summary = parent::buildPaneSummary();
+ $logStorage = $this->entityTypeManager->getStorage('commerce_log');
+ /** @var \Drupal\commerce_log\Entity\Log $logEntry */
+ foreach ($logStorage->loadMultipleByEntity($this->order) as $logEntry) {
+ if ($logEntry->getTemplateId() == 'commerce_order_from_customer_comment' && !empty($logEntry->getParams()['comment'])) {
+ $summary[] = ['#plain_text' => $logEntry->getParams()['comment']];
+ }
+ }
+
+ return $summary;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) {
+ $pane_form['comments'] = [
+ '#type' => 'textarea',
+ '#title' => $this->t('Comments'),
+ '#title_display' => 'invisible',
+ '#default_value' => '',
+ ];
+
+ return $pane_form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) {
+ parent::submitPaneForm($pane_form, $form_state, $complete_form);
+ // Dispatch the comment event.
+ if (!empty($form_state->getValue('customer_comments')['comments'])) {
+ $comment = nl2br(Html::escape($form_state->getValue('customer_comments')['comments']));
+ $event = new OrderCommentEvent($this->order, OrderCommentEvent::ORDER_FROM_CUSTOMER_COMMENT, $comment, FALSE);
+ $this->eventDispatcher->dispatch($event, $event->getEventType());
+ }
+ }
+
+}
diff --git a/modules/log/templates/commerce-log-order-comments.html.twig b/modules/log/templates/commerce-log-order-comments.html.twig
new file mode 100644
index 00000000..b8e25e6a
--- /dev/null
+++ b/modules/log/templates/commerce-log-order-comments.html.twig
@@ -0,0 +1 @@
+{% extends 'commerce-order-receipt.html.twig' %}
diff --git a/modules/log/tests/src/Functional/OrderAdminTest.php b/modules/log/tests/src/Functional/OrderAdminTest.php
index 330a7c29..705ccbdb 100644
--- a/modules/log/tests/src/Functional/OrderAdminTest.php
+++ b/modules/log/tests/src/Functional/OrderAdminTest.php
@@ -3,7 +3,6 @@
namespace Drupal\Tests\commerce_log\Functional;
use Drupal\commerce_order\Entity\Order;
-use Drupal\Component\Utility\Html;
use Drupal\Tests\commerce_order\Functional\OrderBrowserTestBase;
/**
@@ -65,8 +64,7 @@ class OrderAdminTest extends OrderBrowserTestBase {
$this->getSession()->getPage()->fillField('Comment', $test_filtered_comment);
$this->getSession()->getPage()->pressButton('Add comment');
- $this->assertSession()->pageTextNotContains($test_filtered_comment);
- $this->assertSession()->pageTextContains(Html::escape($test_filtered_comment));
+ $this->assertSession()->pageTextContains($test_filtered_comment);
}
/**
diff --git a/modules/order/commerce_order.module b/modules/order/commerce_order.module
index af2620fa..830a4300 100644
--- a/modules/order/commerce_order.module
+++ b/modules/order/commerce_order.module
@@ -46,6 +46,7 @@ function commerce_order_theme($existing, $type, $theme, $path) {
'shipping_information' => NULL,
'payment_method' => NULL,
'totals' => NULL,
+ 'order_comments' => [],
],
],
'commerce_order_receipt__entity_print' => [
@@ -181,8 +182,8 @@ function template_preprocess_commerce_order(array &$variables) {
}
if ($billing_profile = $order->getBillingProfile()) {
- $profile_view_bulder = \Drupal::entityTypeManager()->getViewBuilder('profile');
- $variables['order']['billing_information'] = $profile_view_bulder->view($billing_profile, $view_mode);
+ $profile_view_builder = \Drupal::entityTypeManager()->getViewBuilder('profile');
+ $variables['order']['billing_information'] = $profile_view_builder->view($billing_profile, $view_mode);
}
$route_name = \Drupal::routeMatch()->getRouteName();
@@ -201,6 +202,9 @@ function template_preprocess_commerce_order(array &$variables) {
$messenger->addStatus(t('This order is locked and cannot be edited or deleted. You can unlock it here.', [':link' => $order_unlock_link]));
$messenger->addStatus(t('Orders are typically locked during the payment step in checkout to ensure prices on it do not change during a payment attempt. If the customer is currently paying for this order on a hosted payment page, editing this order could result in a mismatch between the order total and payment amount.'));
}
+
+ // Get order comments.
+ $variables['order_comments'] = \Drupal::service('commerce_order.order_receipt_mail')->getOrderComments($order);
}
/**
diff --git a/modules/order/src/Mail/OrderReceiptMail.php b/modules/order/src/Mail/OrderReceiptMail.php
index 67affa1e..f3a2ad10 100644
--- a/modules/order/src/Mail/OrderReceiptMail.php
+++ b/modules/order/src/Mail/OrderReceiptMail.php
@@ -3,6 +3,7 @@
namespace Drupal\commerce_order\Mail;
use Drupal\commerce\MailHandlerInterface;
+use Drupal\commerce_log\Event\OrderCommentEvent;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_order\OrderTotalSummaryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
@@ -30,7 +31,7 @@ class OrderReceiptMail implements OrderReceiptMailInterface {
/**
* {@inheritdoc}
*/
- public function send(OrderInterface $order, $to = NULL, $bcc = NULL, bool $resend = FALSE) {
+ public function send(OrderInterface $order, $to = NULL, $bcc = NULL, bool $resend = FALSE): bool {
$to = $to ?? $order->getEmail();
if (!$to) {
// The email should not be empty.
@@ -61,6 +62,9 @@ class OrderReceiptMail implements OrderReceiptMailInterface {
$body['#billing_information'] = $profile_view_builder->view($billing_profile);
}
+ // Get order comments.
+ $body['#order_comments'] = $this->getOrderComments($order);
+
$params = [
'id' => 'order_receipt',
'from' => $order->getStore()->getEmailFromHeader(),
@@ -76,4 +80,35 @@ class OrderReceiptMail implements OrderReceiptMailInterface {
return $this->mailHandler->sendMail($to, $subject, $body, $params);
}
+ /**
+ * Get customer viewable order comments.
+ *
+ * @param \Drupal\commerce_order\Entity\OrderInterface $order
+ * The order entity.
+ *
+ * @return array
+ * The order comments.
+ */
+ public function getOrderComments(OrderInterface $order): array {
+ $orderComments = [];
+ if (\Drupal::moduleHandler()->moduleExists('commerce_log')) {
+ $logStorage = \Drupal::entityTypeManager()->getStorage('commerce_log');
+ /** @var \Drupal\commerce_log\Entity\LogInterface $logEntry */
+ foreach (array_reverse($logStorage->loadMultipleByEntity($order)) as $logEntry) {
+ if ($logEntry->getTemplateId() === 'commerce_order_to_customer_comment') {
+ $orderComments[$logEntry->id()]['type'] = OrderCommentEvent::ORDER_TO_CUSTOMER_COMMENT;
+ $orderComments[$logEntry->id()]['date'] = $logEntry->getCreatedTime();
+ $orderComments[$logEntry->id()]['comment'] = $logEntry->getParams()['comment'];
+ }
+ elseif ($logEntry->getTemplateId() === 'commerce_order_from_customer_comment') {
+ $orderComments[$logEntry->id()]['type'] = OrderCommentEvent::ORDER_FROM_CUSTOMER_COMMENT;
+ $orderComments[$logEntry->id()]['date'] = $logEntry->getCreatedTime();
+ $orderComments[$logEntry->id()]['comment'] = $logEntry->getParams()['comment'];
+ }
+ }
+ }
+
+ return $orderComments;
+ }
+
}
diff --git a/modules/order/templates/commerce-order--user.html.twig b/modules/order/templates/commerce-order--user.html.twig
index 7ef237c3..0b545c0b 100644
--- a/modules/order/templates/commerce-order--user.html.twig
+++ b/modules/order/templates/commerce-order--user.html.twig
@@ -13,6 +13,7 @@
* {{ order|without('order_number') }}
* @endcode
* - order_entity: The order entity.
+ * - order_comments: Customer viewable comments.
*
* @ingroup themeable
*/
@@ -33,6 +34,34 @@
{% endif %}
+
{{ order.completed }}
{{ order.placed }}
diff --git a/modules/order/templates/commerce-order-receipt.html.twig b/modules/order/templates/commerce-order-receipt.html.twig
index 1268d853..65ee10f0 100644
--- a/modules/order/templates/commerce-order-receipt.html.twig
+++ b/modules/order/templates/commerce-order-receipt.html.twig
@@ -16,6 +16,7 @@
* - total: The adjustment total price.
* - weight: The adjustment weight, taken from the adjustment type.
* - total: The order total price.
+ * - order_comments: Customer viewable comments.
*
* @ingroup themeable
*/
@@ -133,6 +134,44 @@
+ {% if order_comments %}
+
+
+
+ |
+
+ {% endif %}
{% block additional_information %}
diff --git a/modules/order/tests/src/FunctionalJavascript/OrderAdminTest.php b/modules/order/tests/src/FunctionalJavascript/OrderAdminTest.php
index 9a1e3b3a..89aadd16 100644
--- a/modules/order/tests/src/FunctionalJavascript/OrderAdminTest.php
+++ b/modules/order/tests/src/FunctionalJavascript/OrderAdminTest.php
@@ -424,7 +424,7 @@ class OrderAdminTest extends OrderWebDriverTestBase {
]);
// First test that the current admin user can see the order.
- $this->drupalGet($order->toUrl()->toString());
+ $this->drupalGet($order->toUrl());
$this->assertSession()->pageTextContains($this->loggedInUser->getEmail());
// Confirm that the order item table is showing the empty text.
@@ -462,14 +462,14 @@ class OrderAdminTest extends OrderWebDriverTestBase {
$order->setItems([$order_item]);
$order->save();
- $this->drupalGet($order->toUrl()->toString());
+ $this->drupalGet($order->toUrl());
$this->assertSession()->pageTextNotContains('There are no order items yet.');
$this->assertSession()->pageTextContains('$999.00');
$this->assertSession()->pageTextContains('Subtotal');
// Logout and check that anonymous users cannot see the order admin screen.
$this->drupalLogout();
- $this->drupalGet($order->toUrl()->toString());
+ $this->drupalGet($order->toUrl());
$this->assertSession()->pageTextContains('Access denied');
}
diff --git a/modules/payment/commerce_payment.module b/modules/payment/commerce_payment.module
index eaf71cd5..00c1736b 100644
--- a/modules/payment/commerce_payment.module
+++ b/modules/payment/commerce_payment.module
@@ -211,3 +211,10 @@ function commerce_payment_validate_checkout_flow(array $form, FormStateInterface
$form_state->setError($form, t('Payment information and Payment process panes need to be on separate steps.'));
}
}
+
+/**
+ * Implements hook_preprocess_HOOK().
+ */
+function commerce_payment_preprocess_commerce_log_order_comments(&$variables) {
+ commerce_payment_preprocess_commerce_order($variables);
+}
diff --git a/modules/payment/tests/src/Functional/DefaultPaymentAdminTest.php b/modules/payment/tests/src/Functional/DefaultPaymentAdminTest.php
index 99f9b2c6..53b1514d 100644
--- a/modules/payment/tests/src/Functional/DefaultPaymentAdminTest.php
+++ b/modules/payment/tests/src/Functional/DefaultPaymentAdminTest.php
@@ -132,7 +132,7 @@ class DefaultPaymentAdminTest extends CommerceBrowserTestBase {
[
'absolute' => TRUE,
],
- )->toString();
+ )->getInternalPath();
}
/**
|
{{ comment.comment|raw }}
{{ comment.comment|raw }}