bee_hotel-1.x-dev/src/Controller/GuestMessages.php
src/Controller/GuestMessages.php
<?php
namespace Drupal\bee_hotel\Controller;
use Drupal\bee_hotel\BeeHotelGuestMessageTokens;
use Drupal\beehotel_utils\BeeHotelCommerce;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides route responses for BeeHotel module.
*/
class GuestMessages extends ControllerBase {
use StringTranslationTrait;
/**
* The Guest Message tokens object.
*
* @var \Drupal\bee_hotel\BeeHotelGuestMessageTokens
*/
protected $guestMessageTokens;
/**
* The language manager service.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The BeeHotel commerce Util.
*
* @var \Drupal\beehotel_utils\BeeHotelCommerce
*/
protected $beehotelCommerce;
/**
* Constructs a new GuestMessages object.
*
* @param \Drupal\bee_hotel\BeeHotelGuestMessageTokens $guest_message_tokens
* The tokens object.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\beehotel_utils\BeeHotelCommerce $beehotel_commerce
* BeeHotel Commerce Utils.
*/
public function __construct(BeeHotelGuestMessageTokens $guest_message_tokens, LanguageManagerInterface $language_manager, BeeHotelCommerce $beehotel_commerce) {
$this->guestMessageTokens = $guest_message_tokens;
$this->languageManager = $language_manager;
$this->beehotelCommerce = $beehotel_commerce;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('beehotel.guest_message_tokens'),
$container->get('language_manager'),
$container->get('beehotel_utils.beehotelcommerce')
);
}
/**
* Produces search result.
*
* @param object $commerce_order
* The commerce order object.
* @param string $type
* The message type.
*
* @return array
* Render array for the result.
*/
public function result($commerce_order, $type) {
$tmp = [];
$data = [];
$data['language'] = \Drupal::languageManager()->getCurrentLanguage()->getId();
$data['language_switcher'] = $this->customLanguageSwitcher();
$data['commerce_order'] = $commerce_order;
$data['guest'] = $this->beehotelCommerce->getGuestInfoFromReservatation($data['commerce_order']);
$query = \Drupal::entityQuery('node')
->accessCheck(TRUE)
->condition('status', 1)
->condition('type', 'guest_message')
->sort('changed', 'DESC');
$data['nids'] = $query->execute();
foreach ($data['nids'] as $nid) {
$data['node'] = Node::load($nid);
$data['message'] = $data['node'];
if ($data['node']->hasTranslation($data['language'])) {
$data['message'] = $data['node']->getTranslation($data['language']);
}
$data['attachments'] = $this->getAttachments($data['message']);
$data['links'] = $this->getLinks($data['message']);
$tmp['message'] = $this->messageFormat(
$data['message']->get('field_message')->value,
[
'commerce_order' => $data['commerce_order'],
'node_title' => $data['message']->get("title")->value,
'node_nid' => $data['message']->id(),
'attachments' => $data['attachments']['string'],
'links' => $data['links'],
'guest' => $data['guest'],
]
);
$data['items'][] = $tmp['message'];
}
$data['prefix'] = \Drupal::service('renderer')->render($data['language_switcher']);
$data['prefix'] .= "<div id='tokens-list'>";
$data['prefix'] .= "</div>";
return [
'#theme' => 'item_list',
'#list_type' => 'ul',
'#items' => $data['items'],
'#attributes' => ['id' => 'guest-messages'],
'#wrapper_attributes' => ['class' => 'container'],
'#prefix' => Markup::create($data['prefix']),
'#suffix' => Markup::create("<a href='/node/add/guest_message'>" . $this->t("Add a message") . "</a>"),
];
}
/**
* Apply available tokens.
*
* @param string $value
* The string value to process.
* @param object $commerce_order
* The commerce order object.
*
* @return string|null
* The processed string with tokens replaced.
*/
private function applyTokens($value, $commerce_order) {
$data = [];
$data['commerce_order'] = $commerce_order;
$data['value'] = $value;
$data['setting']['token_prefix'] = "[";
$data['setting']['token_suffix'] = "]";
$data['tokens'] = $this->guestMessageTokens->get($commerce_order);
if (!isset($data['value'])) {
return NULL;
}
// Replace string with token.
foreach ($data['tokens'] as $k => $v) {
if (isset($k) && isset($v)) {
if (isset($v['value'])) {
$data['value'] = str_replace(
$data['setting']['token_prefix'] . $k . $data['setting']['token_suffix'],
"<span class='token'>" . $v['value'] . "</span>",
$data['value']
);
}
}
}
return $data['value'];
}
/**
* Format message output.
*
* @param string $value
* The message value.
* @param array $options
* Formatting options.
*
* @return \Drupal\Component\Render\MarkupInterface|string|null
* Formatted message markup.
*/
private function messageFormat($value, array $options) {
$data = [];
$data['value'] = $value;
$data['options'] = $options;
if (!isset($data['value'])) {
return NULL;
}
// A). replace tokens.
$data['value'] = $this->applyTokens($data['value'], $data['options']['commerce_order']);
// B). add line break BR.
$data['value'] = str_replace(["\r\n'", "\n\r", "\n", "\r"], "<br/>", $data['value']);
// C). Add title + edit link.
$url = Url::fromRoute('entity.node.edit_form', ['node' => $data['options']['node_nid']]);
$link_options = [
'attributes' => [
'class' => [
'message-edit',
],
],
];
$url->setOptions($link_options);
$link = Link::fromTextAndUrl('edit', $url);
$data['build'] = ['link' => $link->toRenderable()];
$data['edit_link'] = \Drupal::service('renderer')->render($data['build']);
// D) Send mail link.
$url = Url::fromRoute('beehotel.guest_messages.mail.preview', [
'node' => $data['options']['node_nid'],
'commerce_order' => $data['options']['commerce_order']->id(),
]);
$link_options = [
'attributes' => [
'class' => [
'mail-preview',
],
],
];
$url->setOptions($link_options);
$link = Link::fromTextAndUrl('mail: ' . $data['options']['commerce_order']->get('mail')->value . "]", $url);
$data['build'] = ['link' => $link->toRenderable()];
$data['mail_link'] = \Drupal::service('renderer')->render($data['build']);
$data['copy_to_clipboard'] = "<a href='#' data-id='" . $data['options']['node_nid'];
$data['copy_to_clipboard'] .= "' class='message-copier' ";
$data['copy_to_clipboard'] .= "id='message-copier-" . $data['options']['node_nid'] . "'>" . $this->t("copy") . "</a>";
// W) Add link for whatsapp.
if (isset($data['options']['guest']['field_telephone'])) {
$data['whatsapp_message'] = UrlHelper::encodePath($data['value']);
$data['whatsapp_link'] = $this->generateWhatsAppLink($data['options']['guest']['field_telephone'], $data['value']);
}
$data['tmp'] = "<h4>" . $data['options']['node_title'] . "<span style='font-size:0.6em'>";
$data['tmp'] .= "<ul class='actions'>";
$data['tmp'] .= "<li>" . $data['mail_link'];
$data['tmp'] .= "<li>" . $data['edit_link'];
$data['tmp'] .= "<li>" . $data['copy_to_clipboard'];
if (isset($data['whatsapp_link'])) {
$data['tmp'] .= "<li>" . \Drupal::service('renderer')->render($data['whatsapp_link']);
}
$data['tmp'] .= "</ul>";
$data['tmp'] .= "</span></h4>";
$data['tmp'] .= "<div class='message' id='message-" . $data['options']['node_nid'] . "'>";
$data['tmp'] .= $data['value'] . "</div>";
$data['value'] = $data['tmp'];
$data['footer'] = "<div>";
if (isset($data['options']['attachments'])) {
$data['footer'] .= "<div class='attachments'> Attachments: " .
$data['options']['attachments'] .
"</div>";
}
if (isset($data['options']['links'])) {
$data['footer'] .= "<div class='links'> Links: " . $data['options']['links'] . "</div>";
}
$data['footer'] .= "</div>";
$data['value'] = Markup::create($data['value'] . $data['footer']);
return $data['value'];
}
/**
* Generates a WhatsApp share link render array for Drupal.
*
* This function creates a properly formatted WhatsApp link that opens
* in a new window with security attributes. The phone number is cleaned
* and the message text is URL encoded for proper WhatsApp API usage.
*
* @param string $phone
* The phone number in any format (will be cleaned to digits only).
* Example: "+1 (555) 123-4567" or "15551234567".
* @param string $text
* (Optional) Pre-filled message for WhatsApp. Default empty string.
*
* @return array
* A Drupal render array for a link element with the following properties:
* - Opens WhatsApp chat in new tab
* - Includes Font Awesome WhatsApp icon
* - Has security attributes (noopener, noreferrer)
* - Applies CSS class for styling
*/
private function generateWhatsAppLink($phone, $text = '') {
// Clean phone number (remove non-numeric characters).
$cleaned_phone = preg_replace('/[^0-9]/', '', $phone);
$encoded_text = UrlHelper::encodePath($text);
return [
'#type' => 'link',
'#title' => [
'#markup' => '<i class="fab fa-whatsapp"></i> WhatsApp',
],
'#url' => Url::fromUri("https://wa.me/+{$cleaned_phone}?text={$encoded_text}"),
'#attributes' => [
'target' => '_blank',
'rel' => 'noopener noreferrer',
'class' => ['whatsapp-link'],
],
];
}
/**
* Get attachments for a given guest_message.
*
* @param \Drupal\node\Entity\Node $node
* The node entity.
*
* @return array
* Array of attachment data.
*
* @todo themed output.
*/
private function getAttachments(Node $node) {
$data = [];
$data['referenced_entities'] = $node->get('field_attachments')->referencedEntities();
$data['attachments']['string'] = "";
foreach ($data['referenced_entities'] as $entity) {
$tmp = $entity->get("uri")->url;
$tmp = explode("/", $tmp);
$end = end($tmp);
$data['attachments']['array'][] = [
'url' => $entity->get("uri")->url,
'label' => $end,
];
$data['attachments']['string'] .= "<a href='" . $entity->get("uri")->url . "'>" . $end . "</a> | ";
}
return $data['attachments'];
}
/**
* Get links for a given guest_message.
*
* @param \Drupal\node\Entity\Node $node
* The node entity.
*
* @return string
* HTML string of links.
*/
private function getLinks(Node $node) {
$data = [];
$data['referenced_entities'] = $node->get('field_links');
$data['links']['items'] = [];
foreach ($node->field_links as $item) {
$data['links']['items'][] = "<a href='" . $item->uri . "'>" . $item->uri . "</a>";
}
$data['links']['string'] = implode("|", $data['links']['items']);
return $data['links']['string'];
}
/**
* Custom language switcher.
*
* @return array
* Render array for language switcher.
*/
public function customLanguageSwitcher() {
$languages = $this->languageManager->getLanguages();
$current_language = $this->languageManager->getCurrentLanguage();
$links = [];
foreach ($languages as $language) {
$links[] = [
'title' => $language->getName(),
'url' => Url::fromRoute('<current>', [], ['language' => $language]),
'language' => $language,
'attributes' => [
'class' => ['language-link', 'language-link-' . $language->getId()],
],
];
}
return [
'#theme' => 'links__language_switcher',
'#links' => $links,
'#attributes' => [
'class' => ['language-switcher'],
],
'#set_active_class' => TRUE,
];
}
}
