From 5f3c2e0edbe1211fdacb90e0fc5dfe157b3b4015 Mon Sep 17 00:00:00 2001 From: pookmish Date: Fri, 13 Sep 2024 09:03:03 -0700 Subject: [PATCH] Added relative internal link validation option for link fields (#54) --- config/schema/stanford_fields.schema.yml | 8 ++ .../RelativeLinkFieldItemConstraint.php | 20 +++++ ...lativeLinkFieldItemConstraintValidator.php | 51 +++++++++++ stanford_fields.module | 54 +++++++++++ .../LinkFieldItemConstraintValidatorTest.php | 90 +++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 src/Plugin/Validation/Constraint/RelativeLinkFieldItemConstraint.php create mode 100644 src/Plugin/Validation/Constraint/RelativeLinkFieldItemConstraintValidator.php create mode 100644 tests/src/Unit/Plugin/Validation/Constraint/LinkFieldItemConstraintValidatorTest.php diff --git a/config/schema/stanford_fields.schema.yml b/config/schema/stanford_fields.schema.yml index f8bfaf3..fc7951a 100644 --- a/config/schema/stanford_fields.schema.yml +++ b/config/schema/stanford_fields.schema.yml @@ -33,3 +33,11 @@ field.widget.settings.localist_url: select_distinct: type: boolean label: 'Select Distinct' + +field.field.*.third_party.stanford_fields: + type: config_entity + label: "Stanford Fields third party settings" + mapping: + force_relative: + type: boolean + label: "Force relative internal links" diff --git a/src/Plugin/Validation/Constraint/RelativeLinkFieldItemConstraint.php b/src/Plugin/Validation/Constraint/RelativeLinkFieldItemConstraint.php new file mode 100644 index 0000000..993c2e0 --- /dev/null +++ b/src/Plugin/Validation/Constraint/RelativeLinkFieldItemConstraint.php @@ -0,0 +1,20 @@ + 'Validation']) +)] +class RelativeLinkFieldItemConstraint extends SymfonyConstraint { + + public $absoluteLink = 'Please use relative links that start with "/" for paths on this site.'; + +} diff --git a/src/Plugin/Validation/Constraint/RelativeLinkFieldItemConstraintValidator.php b/src/Plugin/Validation/Constraint/RelativeLinkFieldItemConstraintValidator.php new file mode 100644 index 0000000..d47b171 --- /dev/null +++ b/src/Plugin/Validation/Constraint/RelativeLinkFieldItemConstraintValidator.php @@ -0,0 +1,51 @@ +get('request_stack')); + } + + /** + * Validation constructor. + * + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * Current request stack. + */ + public function __construct(RequestStack $request_stack) { + $this->request = $request_stack->getCurrentRequest(); + } + + /** + * {@inheritdoc} + */ + public function validate($value, Constraint $constraint) { + /** @var \Drupal\Core\Field\FieldItemListInterface $value */ + $link_uri = $value->get(0)?->get('uri')?->getString(); + if ($link_uri && str_contains($link_uri, $this->request->getSchemeAndHttpHost())) { + $this->context->addViolation($constraint->absoluteLink); + } + } + +} diff --git a/stanford_fields.module b/stanford_fields.module index 5daa852..8a03379 100755 --- a/stanford_fields.module +++ b/stanford_fields.module @@ -5,7 +5,61 @@ * stanford_fields.module */ +use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\field\FieldConfigInterface; + +/** + * Implements hook_form_BASE_FORM_ID_alter(). + */ +function stanford_fields_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { + /** @var \Drupal\field\FieldConfigInterface $field_config */ + $field_config = $form_state->get('field_config'); + if ($field_config->getType() == 'link') { + $form['settings']['force_relative'] = [ + '#type' => 'checkbox', + '#title' => t('Force relative internal links'), + '#default_value' => $field_config->getThirdPartySetting('stanford_fields', 'force_relative'), + '#states' => [ + 'invisible' => [ + ':input[name="settings[link_type]"]' => ['value' => '16'], + ], + ], + ]; + $form['#entity_builders'][] = 'stanford_fields_form_field_config_entity_builder'; + } +} + +/** + * Field config form submission to save the third party settings. + * + * @param string $entity_type + * Entity type ID. + * @param \Drupal\field\FieldConfigInterface $entity + * Submitted entity object. + * @param array $form + * Submitted form render array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Submitted form state. + */ +function stanford_fields_form_field_config_entity_builder(string $entity_type, FieldConfigInterface $entity, array &$form, FormStateInterface $form_state) { + if ($form_state->getValue(['settings', 'force_relative'])) { + $entity->setThirdPartySetting('stanford_fields', 'force_relative', TRUE); + return; + } + $entity->unsetThirdPartySetting('stanford_fields', 'force_relative'); +} + +/** + * Implements hook_entity_bundle_field_info_alter(). + */ +function stanford_fields_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) { + foreach ($fields as $field) { + if ($field->getType() == 'link' && $field->getThirdPartySetting('stanford_fields', 'force_relative')) { + $field->addConstraint('relative_internal_link', []); + } + } +} /** * Implements hook_form_FORM_ID_alter(). diff --git a/tests/src/Unit/Plugin/Validation/Constraint/LinkFieldItemConstraintValidatorTest.php b/tests/src/Unit/Plugin/Validation/Constraint/LinkFieldItemConstraintValidatorTest.php new file mode 100644 index 0000000..02825b9 --- /dev/null +++ b/tests/src/Unit/Plugin/Validation/Constraint/LinkFieldItemConstraintValidatorTest.php @@ -0,0 +1,90 @@ +createMock(RequestStack::class); + + $field_item_list = $this->createMock(FieldItemListInterface::class); + $field_item = $this->createMock(FieldItemInterface::class); + $request = $this->createMock(Request::class); + + // Configure the request mock to return a specific scheme and host. + $request->method('getSchemeAndHttpHost')->willReturn($currentDomain); + $request_stack->method('getCurrentRequest')->willReturn($request); + + // Configure the field item list mock to return a field item with a specific URI. + $field_item->method('get')->willReturnSelf(); + $field_item->method('getString')->willReturn($linkUrl); + $field_item_list->method('get')->with(0)->willReturn($field_item); + + $container = new Container(); + $container->set('request_stack', $request_stack); + + $validator = $this->createMock(ValidatorInterface::class); + $translator = $this->createMock(TranslatorInterface::class); + $translator->method('trans')->willReturnCallback(fn($message) => $message); + $context = new ExecutionContext($validator, NULL, $translator); + + // Instantiate the validator and set the context. + $validator = TestRelativeLinkValidator::create($container); + $validator->initialize($context); + + $constraint = new RelativeLinkFieldItemConstraint(); + $context->setConstraint($constraint); + + // Call the validate method. + $validator->validate($field_item_list, $constraint); + if ($shouldHaveViolations) { + $this->assertTrue($validator->hasViolation()); + } + else { + $this->assertFalse($validator->hasViolation()); + } + } + +} + +class TestRelativeLinkValidator extends RelativeLinkFieldItemConstraintValidator { + + public function hasViolation() { + return count($this->context->getViolations()) > 0; + } + +}