diff --git a/_config.php b/_config.php index 9e519d4a..71c7914c 100644 --- a/_config.php +++ b/_config.php @@ -5,5 +5,4 @@ // Avoid creating global variables call_user_func(function () { - }); diff --git a/lang/_manifest_exclude b/lang/_manifest_exclude new file mode 100644 index 00000000..e69de29b diff --git a/lang/en.yml b/lang/en.yml new file mode 100644 index 00000000..f70fe3f3 --- /dev/null +++ b/lang/en.yml @@ -0,0 +1,7 @@ +en: + SilverStripe\LinkField\Form\ExternalLinkField: + INVALID: 'Please enter a valid url' + SilverStripe\LinkField\Form\PhoneField: + INVALID: 'Please enter a valid phone number' + SilverStripe\LinkField\Validators\HasOneCanViewValidator: + CANNOTBEVIEWED: '{name} cannot be viewed' diff --git a/src/Form/ExternalLinkField.php b/src/Form/ExternalLinkField.php new file mode 100644 index 00000000..44545d75 --- /dev/null +++ b/src/Form/ExternalLinkField.php @@ -0,0 +1,62 @@ + element type="text" attribute + * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/tel + */ + protected $inputType = 'url'; + + /** + * This is added as a classname to the element + */ + public function Type() + { + return 'url text'; + } + + /** + * @param Validator $validator + * + * @return string + */ + public function validate($validator) + { + $result = true; + $this->value = trim($this->value ?? ''); + if ($this->value && !preg_match('#' . self::RX . '#', $this->value)) { + $validator->validationError( + $this->name, + _t(__CLASS__ . '.INVALID', 'Please enter a valid url'), + 'validation' + ); + $result = false; + } + return $this->extendValidationResult($result, $validator); + } + + /** + * This is passed to the frontent via FormField::getSchemaValidation() + * and used in Validator.js + */ + public function getSchemaValidation() + { + $rules = parent::getSchemaValidation(); + $rules['regex'] = ['pattern' => self::RX]; + return $rules; + } +} diff --git a/src/Form/LinkFieldTreeDropdownField.php b/src/Form/LinkFieldTreeDropdownField.php new file mode 100644 index 00000000..881edb57 --- /dev/null +++ b/src/Form/LinkFieldTreeDropdownField.php @@ -0,0 +1,27 @@ + '[^0]']; + return $rules; + } +} diff --git a/src/Form/PhoneField.php b/src/Form/PhoneField.php new file mode 100644 index 00000000..089e09df --- /dev/null +++ b/src/Form/PhoneField.php @@ -0,0 +1,62 @@ + element type="tel" attribute + * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/tel + */ + protected $inputType = 'tel'; + + /** + * This is added as a classname to the element + */ + public function Type() + { + return 'phone text'; + } + + /** + * @param Validator $validator + * + * @return string + */ + public function validate($validator) + { + $result = true; + $this->value = trim($this->value ?? ''); + if ($this->value && !preg_match('#' . self::RX . '#', $this->value)) { + $validator->validationError( + $this->name, + _t(__CLASS__ . '.INVALID', 'Please enter a valid phone number'), + 'validation' + ); + $result = false; + } + return $this->extendValidationResult($result, $validator); + } + + /** + * This is passed to the frontent via FormField::getSchemaValidation() + * and used in Validator.js + */ + public function getSchemaValidation() + { + $rules = parent::getSchemaValidation(); + $rules['regex'] = ['pattern' => self::RX]; + return $rules; + } +} diff --git a/src/Models/EmailLink.php b/src/Models/EmailLink.php index 410455db..ea3d1410 100644 --- a/src/Models/EmailLink.php +++ b/src/Models/EmailLink.php @@ -4,6 +4,8 @@ use SilverStripe\Forms\EmailField; use SilverStripe\Forms\FieldList; +use SilverStripe\Forms\CompositeValidator; +use SilverStripe\Forms\RequiredFields; /** * A link to an Email address. @@ -28,7 +30,6 @@ public function getCMSFields(): FieldList $this->beforeUpdateCMSFields(static function (FieldList $fields) { $fields->replaceField('Email', EmailField::create('Email')); }); - return parent::getCMSFields(); } @@ -36,4 +37,11 @@ public function getURL(): string { return $this->Email ? sprintf('mailto:%s', $this->Email) : ''; } + + public function getCMSCompositeValidator(): CompositeValidator + { + $validator = parent::getCMSCompositeValidator(); + $validator->addValidator(RequiredFields::create(['Email'])); + return $validator; + } } diff --git a/src/Models/ExternalLink.php b/src/Models/ExternalLink.php index c51fb50c..fdecb9e4 100644 --- a/src/Models/ExternalLink.php +++ b/src/Models/ExternalLink.php @@ -2,6 +2,11 @@ namespace SilverStripe\LinkField\Models; +use SilverStripe\Forms\CompositeValidator; +use SilverStripe\Forms\RequiredFields; +use SilverStripe\Forms\FieldList; +use SilverStripe\LinkField\Form\ExternalLinkField; + /** * A link to an external URL. * @@ -15,6 +20,14 @@ class ExternalLink extends Link 'ExternalUrl' => 'Varchar', ]; + public function getCMSFields(): FieldList + { + $this->beforeUpdateCMSFields(function (FieldList $fields) { + $fields->replaceField('ExternalUrl', ExternalLinkField::create('ExternalUrl')); + }); + return parent::getCMSFields(); + } + public function generateLinkDescription(array $data): string { return isset($data['ExternalUrl']) ? $data['ExternalUrl'] : ''; @@ -24,4 +37,11 @@ public function getURL(): string { return $this->ExternalUrl ?? ''; } + + public function getCMSCompositeValidator(): CompositeValidator + { + $validator = parent::getCMSCompositeValidator(); + $validator->addValidator(RequiredFields::create(['ExternalUrl'])); + return $validator; + } } diff --git a/src/Models/FileLink.php b/src/Models/FileLink.php index 0d40b933..0dbbe34b 100644 --- a/src/Models/FileLink.php +++ b/src/Models/FileLink.php @@ -3,6 +3,8 @@ namespace SilverStripe\LinkField\Models; use SilverStripe\Assets\File; +use SilverStripe\Forms\CompositeValidator; +use SilverStripe\Forms\RequiredFields; /** * A link to a File track in asset-admin @@ -42,4 +44,11 @@ public function getURL(): string return $file->exists() ? (string) $file->getURL() : ''; } + + public function getCMSCompositeValidator(): CompositeValidator + { + $validator = parent::getCMSCompositeValidator(); + $validator->addValidator(RequiredFields::create(['File'])); + return $validator; + } } diff --git a/src/Models/Link.php b/src/Models/Link.php index 87d2de0f..3d41d5ea 100644 --- a/src/Models/Link.php +++ b/src/Models/Link.php @@ -16,6 +16,9 @@ use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\View\Requirements; +use SilverStripe\Forms\Form; +use SilverStripe\ORM\ValidationResult; +use SilverStripe\Forms\TreeDropdownField; /** * A Link Data Object. This class should be a subclass, and you should never directly interact with a plain Link @@ -110,6 +113,37 @@ public function getCMSCompositeValidator(): CompositeValidator return $validator; } + /** + * This method is used to validate the dataobject before saving as part of DataObject::write() + * Linkfield works a bit differently from normal forms, because where the modal data is turned + * into JSON and saved into a single JsonField and in saveInto() the JSON is loaded into the + * dataobject using DataObject::setData($data) + * Because of this alternate method of saving, the getCMSCompositeValidator() isn't called + * so we need to call it manually here by loading it into a temporary Form + * + * @return ValidationResult + */ + public function validate() + { + $parentResult = parent::validate(); + $validator = $this->getCMSCompositeValidator(); + $form = Form::create(null, Form::DEFAULT_NAME, $this->getCMSFields()); + $form->setValidator($validator); + $form->loadDataFrom($this); + // Workaround an issue where RequiredFields does not treat RelationID's of 0 as missing + // by changing the value of any TreeDropdowns on the field with a value of 0 to empty string + /** @var FormField $field */ + foreach ($form->Fields()->flattenFields() as $field) { + if (is_a($field, TreeDropdownField::class) && $field->Value() === 0) { + $field->setValue(''); + } + } + $formResult = $form->validationResult(); + $combinedResult = $parentResult->combineAnd($formResult); + $this->extend('updateValidate', $combinedResult); + return $combinedResult; + } + /** * Form hook defined in @see Form::saveInto() * We use this to work with an in-memory only field diff --git a/src/Models/PhoneLink.php b/src/Models/PhoneLink.php index 855e3b1c..c0d20dd4 100644 --- a/src/Models/PhoneLink.php +++ b/src/Models/PhoneLink.php @@ -2,6 +2,11 @@ namespace SilverStripe\LinkField\Models; +use SilverStripe\Forms\CompositeValidator; +use SilverStripe\Forms\RequiredFields; +use SilverStripe\Forms\FieldList; +use SilverStripe\LinkField\Form\PhoneField; + /** * A link to a phone number * @@ -15,6 +20,14 @@ class PhoneLink extends Link 'Phone' => 'Varchar(255)', ]; + public function getCMSFields(): FieldList + { + $this->beforeUpdateCMSFields(function (FieldList $fields) { + $fields->replaceField('Phone', PhoneField::create('Phone')); + }); + return parent::getCMSFields(); + } + public function generateLinkDescription(array $data): string { return isset($data['Phone']) ? $data['Phone'] : ''; @@ -24,4 +37,11 @@ public function getURL(): string { return $this->Phone ? sprintf('tel:%s', $this->Phone) : ''; } + + public function getCMSCompositeValidator(): CompositeValidator + { + $validator = parent::getCMSCompositeValidator(); + $validator->addValidator(RequiredFields::create(['Phone'])); + return $validator; + } } diff --git a/src/Models/SiteTreeLink.php b/src/Models/SiteTreeLink.php index f3f611d8..dc928e59 100644 --- a/src/Models/SiteTreeLink.php +++ b/src/Models/SiteTreeLink.php @@ -7,7 +7,9 @@ use SilverStripe\Control\Controller; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\TextField; -use SilverStripe\Forms\TreeDropdownField; +use SilverStripe\Forms\CompositeValidator; +use SilverStripe\Forms\RequiredFields; +use SilverStripe\LinkField\Form\LinkFieldTreeDropdownField; /** * A link to a Page in the CMS @@ -63,7 +65,7 @@ public function getCMSFields(): FieldList $fields->insertAfter( 'Title', - TreeDropdownField::create( + LinkFieldTreeDropdownField::create( 'PageID', 'Page', SiteTree::class, @@ -128,4 +130,11 @@ public function getTitle(): ?string // Use page title as a default value in case CMS user didn't provide the title return $page->Title; } + + public function getCMSCompositeValidator(): CompositeValidator + { + $validator = parent::getCMSCompositeValidator(); + $validator->addValidator(RequiredFields::create(['PageID'])); + return $validator; + } }