url: protocol required

This commit is contained in:
Yurii
2026-03-04 14:05:48 +02:00
parent 6fa1ff811b
commit 0fbfe21258
5 changed files with 65 additions and 19 deletions
@@ -30,19 +30,17 @@
namespace Espo\Classes\FieldValidators;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Defs;
use Espo\ORM\Entity;
class UrlType
{
private Metadata $metadata;
private VarcharType $varcharType;
public function __construct(Metadata $metadata, VarcharType $varcharType)
{
$this->metadata = $metadata;
$this->varcharType = $varcharType;
}
public function __construct(
private Metadata $metadata,
private VarcharType $varcharType,
private Defs $defs,
) {}
public function checkRequired(Entity $entity, string $field): bool
{
@@ -62,6 +60,15 @@ class UrlType
return true;
}
if (
$this->defs
->getEntity($entity->getEntityType())
->tryGetField($field)
?->getParam('protocolRequired')
) {
return filter_var($value, FILTER_VALIDATE_URL) !== false;
}
/** @var string $pattern */
$pattern = $this->metadata->get(['app', 'regExpPatterns', 'uriOptionalProtocol', 'pattern']);
@@ -245,7 +245,8 @@
"notStorable": "Not Storable",
"itemsEditable": "Items Editable",
"openInNewTab": "Open in new tab",
"notLockable": "Not Lockable"
"notLockable": "Not Lockable",
"protocolRequired": "Protocol Required"
},
"strings" : {
"rebuildRequired": "Rebuild is required"
@@ -18,6 +18,10 @@
"type": "bool",
"tooltip": "urlStrip"
},
{
"name": "protocolRequired",
"type": "bool"
},
{
"name": "copyToClipboard",
"type": "bool",
+40 -10
View File
@@ -52,6 +52,7 @@ class UrlFieldView extends VarcharFieldView {
* @property {boolean} [required] Required.
* @property {boolean} [copyToClipboard] To display a Copy-to-clipboard button.
* @property {boolean} [strip] To strip.
* @property {boolean} [protocolRequired] Require protocol. As of v9.4.
*/
/**
@@ -83,7 +84,7 @@ class UrlFieldView extends VarcharFieldView {
noSpellCheck = true
optionalProtocol = true
DEFAULT_MAX_LENGTH =255
DEFAULT_MAX_LENGTH = 255
data() {
const data = super.data();
@@ -93,6 +94,14 @@ class UrlFieldView extends VarcharFieldView {
return data;
}
setup() {
super.setup();
if (this.params.protocolRequired) {
this.optionalProtocol = false;
}
}
afterRender() {
super.afterRender();
@@ -141,7 +150,7 @@ class UrlFieldView extends VarcharFieldView {
parse(value) {
value = value.trim();
if (this.params.strip) {
if (this.params.strip && !this.params.protocolRequired) {
value = this.strip(value);
}
@@ -194,14 +203,7 @@ class UrlFieldView extends VarcharFieldView {
return false;
}
const patternName = this.optionalProtocol ? 'uriOptionalProtocol' : 'uri';
/** @var {string} */
const pattern = this.getMetadata().get(['app', 'regExpPatterns', patternName, 'pattern']);
const regExp = new RegExp('^' + pattern + '$');
if (regExp.test(value)) {
if (this.isValid(value)) {
return false;
}
@@ -213,6 +215,34 @@ class UrlFieldView extends VarcharFieldView {
return true;
}
/**
* @private
* @param {string} value
* @return {boolean}
*/
isValid(value) {
if (!this.optionalProtocol) {
try {
new URL(value);
return true;
} catch (e) {
return false;
}
}
/** @var {string} */
const pattern = this.getMetadata().get(['app', 'regExpPatterns', 'uriOptionalProtocol', 'pattern']);
const regExp = new RegExp('^' + pattern + '$');
if (regExp.test(value)) {
return true;
}
return false;
}
// noinspection JSUnusedGlobalSymbols
validateMaxLength() {
const maxLength = this.params.maxLength || this.DEFAULT_MAX_LENGTH;
+4
View File
@@ -495,6 +495,10 @@
"copyToClipboard": {
"type": "boolean",
"description": "Display 'Copy to Clipboard' button."
},
"protocolRequired": {
"type": "boolean",
"description": "Require protocol. As of v9.4."
}
}
}