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;
+ }
}