diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9009583f9240612093a99c947e4a954dcde5a22d..f073158322298835855f1dfba7d2aa2604e7bb3f 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -65,6 +65,21 @@ parameters: count: 1 path: src/Plugin/EntityUsage/Track/EntityReference.php + - + message: "#^Access to an undefined property Drupal\\\\filter\\\\FilterPluginCollection\\|Drupal\\\\filter\\\\Plugin\\\\FilterInterface\\:\\:\\$status\\.$#" + count: 1 + path: src/Plugin/EntityUsage/Track/HtmlLink.php + + - + message: "#^Access to an undefined property Drupal\\\\Core\\\\Field\\\\FieldItemInterface\\:\\:\\$summary\\.$#" + count: 1 + path: src/Plugin/EntityUsage/Track/TextFieldEmbedBase.php + + - + message: "#^Access to an undefined property Drupal\\\\Core\\\\Field\\\\FieldItemInterface\\:\\:\\$value\\.$#" + count: 1 + path: src/Plugin/EntityUsage/Track/TextFieldEmbedBase.php + - message: "#^Function entity_usage_test_entity_usage_block_tracking\\(\\) has no return type specified\\.$#" count: 1 diff --git a/src/Plugin/EntityUsage/Track/HtmlLink.php b/src/Plugin/EntityUsage/Track/HtmlLink.php index 0d016fd44afc5d3a87c869aaa8f36d045afab166..ca2506c77972c4cc576bb764b4b7daed0733bacb 100644 --- a/src/Plugin/EntityUsage/Track/HtmlLink.php +++ b/src/Plugin/EntityUsage/Track/HtmlLink.php @@ -3,7 +3,17 @@ namespace Drupal\entity_usage\Plugin\EntityUsage\Track; use Drupal\Component\Utility\Html; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\ImmutableConfig; +use Drupal\Core\Entity\EntityFieldManagerInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Field\FieldItemInterface; +use Drupal\entity_usage\EntityUsageInterface; use Drupal\entity_usage\EntityUsageTrackUrlUpdateInterface; +use Drupal\entity_usage\UrlToEntityInterface; +use Drupal\filter\Entity\FilterFormat; +use Psr\Log\LoggerInterface; /** * Tracks usage of entities referenced from regular HTML Links. @@ -18,6 +28,19 @@ use Drupal\entity_usage\EntityUsageTrackUrlUpdateInterface; */ class HtmlLink extends TextFieldEmbedBase implements EntityUsageTrackUrlUpdateInterface { + /** + * The filter settings. + */ + protected ImmutableConfig $filterConfig; + + /** + * {@inheritdoc} + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityUsageInterface $usage_service, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, ConfigFactoryInterface $config_factory, EntityRepositoryInterface $entity_repository, ?LoggerInterface $entityUsageLogger = NULL, ?UrlToEntityInterface $urlToEntity = NULL, ?array $always_track_base_fields = NULL) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $usage_service, $entity_type_manager, $entity_field_manager, $config_factory, $entity_repository, $entityUsageLogger, $urlToEntity, $always_track_base_fields); + $this->filterConfig = $config_factory->get('filter.settings'); + } + /** * {@inheritdoc} */ @@ -74,4 +97,21 @@ class HtmlLink extends TextFieldEmbedBase implements EntityUsageTrackUrlUpdateIn return $entities; } + /** + * {@inheritdoc} + */ + protected function getTextFromField(FieldItemInterface $item): string { + $text = parent::getTextFromField($item); + + // Use the fallback format if necessary. + // @see \Drupal\filter\Element\ProcessedText::preRenderText() + $format = FilterFormat::load($item->format ?? $this->filterConfig->get('fallback_format')); + // If the text format convert URLs in text to real URLs we should too. + if ($format instanceof FilterFormat && $format->filters()->has('filter_url') && $format->filters('filter_url')->status) { + $filter = $format->filters()->get('filter_url'); + $text = $filter->process($text, $item->getLangcode())->getProcessedText(); + } + return $text; + } + } diff --git a/src/Plugin/EntityUsage/Track/TextFieldEmbedBase.php b/src/Plugin/EntityUsage/Track/TextFieldEmbedBase.php index 31618b618d55e4331801a661e8f629cddaaf8472..62349caafe158d3c05f51a4ebd664f2baa24f49b 100644 --- a/src/Plugin/EntityUsage/Track/TextFieldEmbedBase.php +++ b/src/Plugin/EntityUsage/Track/TextFieldEmbedBase.php @@ -19,10 +19,8 @@ abstract class TextFieldEmbedBase extends EntityUsageTrackBase implements EmbedT if (empty($item_value['value'])) { return []; } - $text = $item_value['value']; - if ($item->getFieldDefinition()->getType() === 'text_with_summary') { - $text .= $item_value['summary']; - } + $text = $this->getTextFromField($item); + $entities_in_text = $this->parseEntitiesFromText($text); $valid_entities = []; @@ -52,4 +50,21 @@ abstract class TextFieldEmbedBase extends EntityUsageTrackBase implements EmbedT return $valid_entities; } + /** + * Gets the text from the field. + * + * @param \Drupal\Core\Field\FieldItemInterface $item + * The field item. + * + * @return string + * The field text. + */ + protected function getTextFromField(FieldItemInterface $item): string { + $text = $item->value; + if ($item->getFieldDefinition()->getType() === 'text_with_summary') { + $text .= $item->summary; + } + return $text; + } + } diff --git a/tests/src/Kernel/EntityUsageFilterUrlTest.php b/tests/src/Kernel/EntityUsageFilterUrlTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5e59541ab9764a1cdf4f57b42201cbf7d6100f1e --- /dev/null +++ b/tests/src/Kernel/EntityUsageFilterUrlTest.php @@ -0,0 +1,137 @@ +<?php + +namespace Drupal\Tests\entity_usage\Kernel; + +use Drupal\filter\Entity\FilterFormat; +use Drupal\KernelTests\KernelTestBase; +use Drupal\entity_test\Entity\EntityTest; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; + +/** + * Tests URLs in text tracking. + * + * @group entity_usage + */ +class EntityUsageFilterUrlTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'entity_test', + 'entity_usage', + 'field', + 'filter', + 'system', + 'text', + 'user', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + FieldStorageConfig::create([ + 'type' => 'text_long', + 'entity_type' => 'entity_test', + 'field_name' => 'text_no_url_filter', + ])->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + 'field_name' => 'text_no_url_filter', + 'label' => 'Text No URL Filter', + ])->save(); + + FieldStorageConfig::create([ + 'type' => 'text_long', + 'entity_type' => 'entity_test', + 'field_name' => 'text_url_filter', + ])->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + 'field_name' => 'text_url_filter', + 'label' => 'Text URL Filter', + ])->save(); + + $format = FilterFormat::create([ + 'format' => 'no_url_format', + 'name' => 'No URL format', + ]); + $format->setFilterConfig('filter_url', [ + 'status' => 0, + ]); + $format->save(); + + // Add another text format specifying the URL filter. + $format = FilterFormat::create([ + 'format' => 'url_format', + 'name' => 'URL format', + ]); + $format->setFilterConfig('filter_url', [ + 'status' => 1, + 'settings' => [ + 'filter_url_length' => 30, + ], + ]); + $format->save(); + + $this->installEntitySchema('entity_test'); + $this->installSchema('entity_usage', ['entity_usage']); + $this->installConfig(['filter']); + + $this->config('entity_usage.settings') + ->set('track_enabled_source_entity_types', ['entity_test']) + ->set('track_enabled_target_entity_types', ['entity_test']) + ->set('track_enabled_plugins', ['html_link']) + ->set('site_domains', ['http://localhost']) + ->save(); + $this->container->get('kernel')->resetContainer(); + } + + /** + * Tests text URLs are tracked if the filter is set up. + */ + public function testUrlTracking(): void { + $entity1 = EntityTest::create([ + 'type' => 'entity_test', + 'name' => $this->randomString(), + ]); + $entity1->save(); + + $entity2 = EntityTest::create([ + 'type' => 'entity_test', + 'name' => $this->randomString(), + ]); + $entity2->save(); + + $entity3 = EntityTest::create([ + 'type' => 'entity_test', + 'name' => $this->randomString(), + 'text_no_url_filter' => ['value' => $entity1->toUrl()->setAbsolute()->toString(), 'format' => 'no_url_format'], + 'text_url_filter' => ['value' => $entity2->toUrl()->setAbsolute()->toString(), 'format' => 'url_format'], + ]); + $entity3->save(); + + // We should have a single usage in the text_url_filter field. + /** @var \Drupal\entity_usage\EntityUsage $entity_usage */ + $entity_usage = $this->container->get('entity_usage.usage'); + $targets = $entity_usage->listTargets($entity3); + $this->assertCount(1, $targets['entity_test']); + $this->assertSame( + [ + [ + 'method' => 'html_link', + 'field_name' => 'text_url_filter', + 'count' => '1', + ], + ], + $targets['entity_test'][2] + ); + } + +} diff --git a/tests/src/Kernel/EntityUsageTrackExceptionTest.php b/tests/src/Kernel/EntityUsageTrackExceptionTest.php index 2577b5837494185f2b81cc220dfdbc809f357692..339f3b61561810fa853a54757f6cdbf49bad5ea2 100644 --- a/tests/src/Kernel/EntityUsageTrackExceptionTest.php +++ b/tests/src/Kernel/EntityUsageTrackExceptionTest.php @@ -58,6 +58,7 @@ class EntityUsageTrackExceptionTest extends KernelTestBase { $this->installEntitySchema('entity_test'); $this->installSchema('entity_usage', ['entity_usage']); + $this->installConfig(['filter']); $this->config('entity_usage.settings') ->set('track_enabled_source_entity_types', ['entity_test'])