iframe_consent-1.0.x-dev/src/Service/IframeConsentHelper.php
src/Service/IframeConsentHelper.php
<?php
namespace Drupal\iframe_consent\Service;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Template\Attribute;
use Drupal\iframe_consent\IframeConsentDomainInterface;
/**
* Class to handle iframe consent logic.
*
* @package Drupal\iframe_consent\Service
*/
class IframeConsentHelper implements IframeConsentHelperInterface {
/**
* The iframe consent settings.
*
* @var \Drupal\iframe_consent\Service\IframeConsentSettingsInterface
*/
public $settings;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
public $entityTypeManager;
/**
* The cache tags invalidator service.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
*/
protected $cacheTagsInvalidator;
/**
* Constructor.
*
* @param \Drupal\iframe_consent\Service\IframeConsentSettingsInterface $settings
* The iframe consent settings service.
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cacheTagsInvalidator
* The cache tags invalidator service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager service.
*/
public function __construct(IframeConsentSettingsInterface $settings, CacheTagsInvalidatorInterface $cacheTagsInvalidator, EntityTypeManagerInterface $entityTypeManager) {
$this->settings = $settings;
$this->cacheTagsInvalidator = $cacheTagsInvalidator;
$this->entityTypeManager = $entityTypeManager->getStorage('iframe_consent_domains');
}
/**
* Add consent categories to iframe attributes.
*
* @param array $iframe_attributes
* The iframe attributes.
*
* @return void
* The modified iframe attributes.
*/
private function addConsentCategories(array &$iframe_attributes): void {
$domain = $this->cleanUri($iframe_attributes['src']) ?? '';
$consent_groups = $this->getConsentGroupsByUri($domain);
if (!empty($consent_groups)) {
$consent_groups_keys = array_keys($consent_groups);
$iframe_attributes['data-required-consents'] = implode(',', $consent_groups_keys);
}
}
/**
* Add the facade template type to iframe attributes.
*
* @param array $iframe_attributes
* The iframe attributes.
*
* @return void
* The modified iframe attributes.
*/
private function addFacadeTemplate(array &$iframe_attributes): void {
$src = $iframe_attributes['src'] ?? '';
$domain = $this->cleanUri($src);
$iframe_attributes['data-placeholder-template'] = $this->getTemplateIdByDomain($domain);
$iframe_attributes['data-hash'] = md5($src);
}
/**
* {@inheritdoc}
*/
public function alterIframeAttributes(Attribute|array $iframe_attributes): Attribute|array {
$is_array = TRUE;
$attributes_array = $iframe_attributes;
if ($attributes_array instanceof Attribute) {
$attributes_array = $attributes_array->toArray();
$is_array = FALSE;
}
$this->addConsentCategories($attributes_array);
$this->addFacadeTemplate($attributes_array);
$this->obfuscateSrc($attributes_array);
$attributes_array['class'][] = 'iframe-consent';
if (!$is_array) {
$iframe_attributes = new Attribute($attributes_array);
}
return $iframe_attributes;
}
/**
* {@inheritdoc}
*/
public function attachLibraries(array &$build): void {
$provider = $this->settings->getProvider();
if (!$provider) {
return;
}
$build['#attached']['library'][] = sprintf('iframe_consent/%s', $provider);
$build['#attached']['drupalSettings']['iframeConsent']['consentGroups'] = $this->settings->getConsentGroupsList();
}
/**
* {@inheritdoc}
*/
public function cleanUri(string $uri): string {
$domain = UrlHelper::filterBadProtocol($uri);
$domain_parts = parse_url($domain);
if (empty($domain_parts['host'])) {
return '';
}
$scheme = $domain_parts['scheme'] ?? 'https';
$sanitized_domain = $scheme . '://' . $domain_parts['host'];
if (UrlHelper::isValid($sanitized_domain, TRUE)) {
return $sanitized_domain;
}
return '';
}
/**
* {@inheritdoc}
*/
public function clearCacheTags(): void {
$this->cacheTagsInvalidator->invalidateTags(['iframe_consent']);
}
/**
* {@inheritdoc}
*/
public function getDomainByTemplateId(string $template_id): string {
// Sanitize the template ID to create a valid domain.
$domain = str_replace('___', '://', $template_id);
$domain = str_replace('_', '.', $domain);
return $this->cleanUri($domain);
}
/**
* {@inheritdoc}
*/
public function getDomainConsentGroups(IframeConsentDomainInterface $entity): array {
$consent_groups = [];
$consent_groups_ids = $entity->get('consent_groups')->getValue();
foreach ($consent_groups_ids as $value) {
$key = $value['value'];
$consent_groups[$key] = $this->settings->getConsentGroupLabelById($value['value']);
}
return $consent_groups;
}
/**
* {@inheritdoc}
*/
public function getConsentGroupsByUri(string $uri): array {
$domain_entity = $this->loadDomainByUri($uri);
$consent_groups = [];
if (!$domain_entity) {
return $consent_groups;
}
return $this->getDomainConsentGroups($domain_entity);
}
/**
* {@inheritdoc}
*/
public function getTemplateIdByDomain(string $domain): string {
if (!$domain) {
return 'default';
}
// Sanitize the domain to create a valid template ID.
$template_id = str_replace('://', '___', $domain);
$template_id = str_replace('.', '_', $template_id);
return $template_id;
}
/**
* {@inheritdoc}
*/
public function getTemplatesList() : array {
$template_ids = [];
if (!$this->settings->get('templates_by_domain')) {
return $template_ids;
}
$domains = $this->entityTypeManager->getQuery()
->accessCheck(TRUE)
->range(0, 50)
->execute();
foreach ($domains as $domain) {
$entity = $this->entityTypeManager->load($domain);
if (!$entity instanceof IframeConsentDomainInterface) {
continue;
}
$template_id = $this->getTemplateIdByDomain($entity->get('domain')->value);
if (!in_array($template_id, $template_ids)) {
$template_ids[] = $template_id;
}
}
return $template_ids;
}
/**
* {@inheritdoc}
*/
public function isInDomainsList(string $uri): bool {
$domain = $this->cleanUri($uri);
if (empty($domain)) {
return FALSE;
}
// Check if the domain is in the list of domains.
$domains_count = $this->entityTypeManager->getQuery()
->accessCheck(TRUE)
->condition('domain', $domain)
->count()
->execute();
if (empty($domains_count)) {
return FALSE;
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function isGroupsInUse(string $id): bool {
$domains_mapping = $this->entityTypeManager->getQuery()
->accessCheck(TRUE)
->condition('consent_groups', $id)
->count()
->execute();
if ($domains_mapping > 0) {
return TRUE;
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function loadDomainByUri(string $uri): IframeConsentDomainInterface|NULL {
$domain = $this->cleanUri($uri);
if (empty($domain)) {
return NULL;
}
$result = $this->entityTypeManager->loadByProperties(['domain' => $domain]);
return !empty($result) ? reset($result) : NULL;
}
/**
* Obfuscate the src attribute of the iframe.
*
* @param array $iframe_attributes
* The iframe attributes.
*
* @return void
* The modified iframe attributes.
*/
private function obfuscateSrc(array &$iframe_attributes): void {
if (isset($iframe_attributes['src']) && !empty($iframe_attributes['src'])) {
$iframe_attributes['data-src'] = $iframe_attributes['src'];
$iframe_attributes['src'] = '';
}
}
/**
* {@inheritdoc}
*/
public function setCacheTags(array &$build): void {
$build['#cache']['tags'][] = 'iframe_consent';
}
}
