Loading core/lib/Drupal/Core/Form/FormBase.php +2 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Htmx\HtmxRequestInfoTrait; use Drupal\Core\Logger\LoggerChannelTrait; use Drupal\Core\Routing\RedirectDestinationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait; Loading Loading @@ -44,6 +45,7 @@ abstract class FormBase implements FormInterface, ContainerInjectionInterface { use DependencySerializationTrait; use HtmxRequestInfoTrait; use LoggerChannelTrait; use MessengerTrait; use RedirectDestinationTrait; Loading core/lib/Drupal/Core/Htmx/HtmxRequestInfoTrait.php 0 → 100644 +102 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Core\Htmx; /** * Provides methods for getting information about the HTMX request. */ trait HtmxRequestInfoTrait { /** * Gets the request object. * * @return \Symfony\Component\HttpFoundation\Request * The request object. */ abstract protected function getRequest(); /** * Determines if the request is sent by HTMX. * * @return bool * TRUE if the 'HX-Request' header is present. */ protected function isHtmxRequest(): bool { return $this->getRequest()->headers->has('HX-Request'); } /** * Determines if the request is boosted by HTMX. * * @return bool * TRUE if the 'HX-Boosted' header is present. */ protected function isHtmxBoosted(): bool { return $this->getRequest()->headers->has('HX-Boosted'); } /** * Retrieves the URL of the requesting page from an HTMX request header. * * @return string * The value of the 'HX-Current-URL' header, or an empty string if not set. */ protected function getHtmxCurrentUrl(): string { return $this->getRequest()->headers->get('HX-Current-URL', ''); } /** * Determines if if the request is for history restoration. * * Sent after a miss in the local history cache * * @return bool * TRUE if the 'HX-History-Restore-Request' header is present. */ protected function isHtmxHistoryRestoration(): bool { return $this->getRequest()->headers->has('HX-History-Restore-Request'); } /** * Retrieves the prompt from an HTMX request header. * * @return string * The value of the 'HX-Prompt' header, or an empty string if not set. */ protected function getHtmxPrompt(): string { return $this->getRequest()->headers->get('HX-Prompt', ''); } /** * Retrieves the target identifier from an HTMX request header. * * @return string * The value of the 'HX-Target' header, or an empty string if not set. */ protected function getHtmxTarget(): string { return $this->getRequest()->headers->get('HX-Target', ''); } /** * Retrieves the trigger identifier from an HTMX request header. * * @return string * The value of the 'HX-Trigger' header, or an empty string if not set. */ protected function getHtmxTrigger(): string { return $this->getRequest()->headers->get('HX-Trigger', ''); } /** * Retrieves the trigger name from an HTMX request header. * * @return string * The value of the 'HX-Trigger-Name' header, or an empty string if not set. */ protected function getHtmxTriggerName(): string { return $this->getRequest()->headers->get('HX-Trigger-Name', ''); } } core/tests/Drupal/Tests/Core/Htmx/HtmxRequestInfoTest.php 0 → 100644 +138 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Tests\Core\Htmx; use Drupal\Core\Htmx\HtmxRequestInfoTrait; use Drupal\Tests\UnitTestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use Symfony\Component\HttpFoundation\Request; /** * Test all HtmxRequestInfoTrait methods. */ #[CoversClass(HtmxRequestInfoTrait::class)] #[Group('Htmx')] class HtmxRequestInfoTest extends UnitTestCase { use HtmxRequestInfoTrait; /** * A simulated request. */ protected Request $request; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); $this->request = new Request(); } /** * Tests the isHtmxRequest method. */ public function testIsHtmxRequest(): void { // Test with the header not present. $this->assertFalse($this->isHtmxRequest()); // Test with the header present. $this->request->headers->set('HX-Request', 'true'); $this->assertTrue($this->isHtmxRequest()); } /** * Tests the isHtmxBoosted method. */ public function testIsHtmxBoosted(): void { // Test with the header not present. $this->assertFalse($this->isHtmxBoosted()); // Test with the header present. $this->request->headers->set('HX-Boosted', 'true'); $this->assertTrue($this->isHtmxBoosted()); } /** * Tests the getHtmxCurrentUrl method. */ public function testGetHtmxCurrentUrl(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxCurrentUrl()); // Test with the header present. $this->request->headers->set('HX-Current-URL', 'https://example.com/page'); $this->assertEquals('https://example.com/page', $this->getHtmxCurrentUrl()); } /** * Tests the isHtmxHistoryRestoration method. */ public function testIsHtmxHistoryRestoration(): void { // Test with the header not present. $this->assertFalse($this->isHtmxHistoryRestoration()); // Test with the header present. $this->request->headers->set('HX-History-Restore-Request', 'true'); $this->assertTrue($this->isHtmxHistoryRestoration()); } /** * Tests the getHtmxPrompt method. */ public function testGetHtmxPrompt(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxPrompt()); // Test with the header present. $this->request->headers->set('HX-Prompt', 'Enter a value'); $this->assertEquals('Enter a value', $this->getHtmxPrompt()); } /** * Tests the getHtmxTarget method. */ public function testGetHtmxTarget(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxTarget()); // Test with the header present. $this->request->headers->set('HX-Target', 'submit-button'); $this->assertEquals('submit-button', $this->getHtmxTarget()); } /** * Tests the getHtmxTrigger method. */ public function testGetHtmxTrigger(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxTrigger()); // Test with the header present. $this->request->headers->set('HX-Trigger', 'submit-button'); $this->assertEquals('submit-button', $this->getHtmxTrigger()); } /** * Tests the getHtmxTriggerName method. */ public function testGetHtmxTriggerName(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxTriggerName()); // Test with the header present. $this->request->headers->set('HX-Trigger-Name', 'submit-button'); $this->assertEquals('submit-button', $this->getHtmxTriggerName()); } /** * {@inheritdoc} */ protected function getRequest() { return $this->request; } } Loading
core/lib/Drupal/Core/Form/FormBase.php +2 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Htmx\HtmxRequestInfoTrait; use Drupal\Core\Logger\LoggerChannelTrait; use Drupal\Core\Routing\RedirectDestinationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait; Loading Loading @@ -44,6 +45,7 @@ abstract class FormBase implements FormInterface, ContainerInjectionInterface { use DependencySerializationTrait; use HtmxRequestInfoTrait; use LoggerChannelTrait; use MessengerTrait; use RedirectDestinationTrait; Loading
core/lib/Drupal/Core/Htmx/HtmxRequestInfoTrait.php 0 → 100644 +102 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Core\Htmx; /** * Provides methods for getting information about the HTMX request. */ trait HtmxRequestInfoTrait { /** * Gets the request object. * * @return \Symfony\Component\HttpFoundation\Request * The request object. */ abstract protected function getRequest(); /** * Determines if the request is sent by HTMX. * * @return bool * TRUE if the 'HX-Request' header is present. */ protected function isHtmxRequest(): bool { return $this->getRequest()->headers->has('HX-Request'); } /** * Determines if the request is boosted by HTMX. * * @return bool * TRUE if the 'HX-Boosted' header is present. */ protected function isHtmxBoosted(): bool { return $this->getRequest()->headers->has('HX-Boosted'); } /** * Retrieves the URL of the requesting page from an HTMX request header. * * @return string * The value of the 'HX-Current-URL' header, or an empty string if not set. */ protected function getHtmxCurrentUrl(): string { return $this->getRequest()->headers->get('HX-Current-URL', ''); } /** * Determines if if the request is for history restoration. * * Sent after a miss in the local history cache * * @return bool * TRUE if the 'HX-History-Restore-Request' header is present. */ protected function isHtmxHistoryRestoration(): bool { return $this->getRequest()->headers->has('HX-History-Restore-Request'); } /** * Retrieves the prompt from an HTMX request header. * * @return string * The value of the 'HX-Prompt' header, or an empty string if not set. */ protected function getHtmxPrompt(): string { return $this->getRequest()->headers->get('HX-Prompt', ''); } /** * Retrieves the target identifier from an HTMX request header. * * @return string * The value of the 'HX-Target' header, or an empty string if not set. */ protected function getHtmxTarget(): string { return $this->getRequest()->headers->get('HX-Target', ''); } /** * Retrieves the trigger identifier from an HTMX request header. * * @return string * The value of the 'HX-Trigger' header, or an empty string if not set. */ protected function getHtmxTrigger(): string { return $this->getRequest()->headers->get('HX-Trigger', ''); } /** * Retrieves the trigger name from an HTMX request header. * * @return string * The value of the 'HX-Trigger-Name' header, or an empty string if not set. */ protected function getHtmxTriggerName(): string { return $this->getRequest()->headers->get('HX-Trigger-Name', ''); } }
core/tests/Drupal/Tests/Core/Htmx/HtmxRequestInfoTest.php 0 → 100644 +138 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Tests\Core\Htmx; use Drupal\Core\Htmx\HtmxRequestInfoTrait; use Drupal\Tests\UnitTestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use Symfony\Component\HttpFoundation\Request; /** * Test all HtmxRequestInfoTrait methods. */ #[CoversClass(HtmxRequestInfoTrait::class)] #[Group('Htmx')] class HtmxRequestInfoTest extends UnitTestCase { use HtmxRequestInfoTrait; /** * A simulated request. */ protected Request $request; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); $this->request = new Request(); } /** * Tests the isHtmxRequest method. */ public function testIsHtmxRequest(): void { // Test with the header not present. $this->assertFalse($this->isHtmxRequest()); // Test with the header present. $this->request->headers->set('HX-Request', 'true'); $this->assertTrue($this->isHtmxRequest()); } /** * Tests the isHtmxBoosted method. */ public function testIsHtmxBoosted(): void { // Test with the header not present. $this->assertFalse($this->isHtmxBoosted()); // Test with the header present. $this->request->headers->set('HX-Boosted', 'true'); $this->assertTrue($this->isHtmxBoosted()); } /** * Tests the getHtmxCurrentUrl method. */ public function testGetHtmxCurrentUrl(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxCurrentUrl()); // Test with the header present. $this->request->headers->set('HX-Current-URL', 'https://example.com/page'); $this->assertEquals('https://example.com/page', $this->getHtmxCurrentUrl()); } /** * Tests the isHtmxHistoryRestoration method. */ public function testIsHtmxHistoryRestoration(): void { // Test with the header not present. $this->assertFalse($this->isHtmxHistoryRestoration()); // Test with the header present. $this->request->headers->set('HX-History-Restore-Request', 'true'); $this->assertTrue($this->isHtmxHistoryRestoration()); } /** * Tests the getHtmxPrompt method. */ public function testGetHtmxPrompt(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxPrompt()); // Test with the header present. $this->request->headers->set('HX-Prompt', 'Enter a value'); $this->assertEquals('Enter a value', $this->getHtmxPrompt()); } /** * Tests the getHtmxTarget method. */ public function testGetHtmxTarget(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxTarget()); // Test with the header present. $this->request->headers->set('HX-Target', 'submit-button'); $this->assertEquals('submit-button', $this->getHtmxTarget()); } /** * Tests the getHtmxTrigger method. */ public function testGetHtmxTrigger(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxTrigger()); // Test with the header present. $this->request->headers->set('HX-Trigger', 'submit-button'); $this->assertEquals('submit-button', $this->getHtmxTrigger()); } /** * Tests the getHtmxTriggerName method. */ public function testGetHtmxTriggerName(): void { // Test with the header not present. $this->assertEquals('', $this->getHtmxTriggerName()); // Test with the header present. $this->request->headers->set('HX-Trigger-Name', 'submit-button'); $this->assertEquals('submit-button', $this->getHtmxTriggerName()); } /** * {@inheritdoc} */ protected function getRequest() { return $this->request; } }