Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 94 additions & 9 deletions DataTypes/StringDataType.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
class StringDataType extends AbstractDataType
{
private $lengthMin = 0;

private $lengthMax = null;

private $regexValidator = null;

private $regexValidatorNegative = null;
private $emptyAsNULL = false;

/**
Expand All @@ -38,7 +39,10 @@ public function getValidatorRegex() : ?string
}

/**
* Defines a regular expression to validate values of this data type.
* Defines a regular expression to validate values of this data type. If the regex finds a match, validation
* succeeds.
*
* Can be used in tandem with `validator_regex_negative`.
*
* Example:
*
Expand All @@ -65,6 +69,47 @@ public function setValidatorRegex($regularExpression)
$this->regexValidator = $regularExpression;
return $this;
}

/**
* @return string|null
*/
public function getValidatorRegexNegative() : ?string
{
return $this->regexValidatorNegative;
}

/**
* Defines a regular expression to validate values of this data type. If the regex finds any match, validation
* fails. Some systems output the detected matches as "issues". To support this write your regex in such a way
* that it identifies meaningful issues with a given input string.
*
* Can be used in tandem with `validator_regex`.
*
* Example:
*
* ```
* {
* "validator_regex_negative": "/[^0-9ÄÖÜäöüßA-Za-z-_,+= .#&§$']/",
* "validation_error_text": "The string may only contain the following characters: Digits from `0-9`, Umlauts `ÄÖÜäöüß`, `A-Z`, `a-z` and these special characters `-_,+= .#&§$'`"
* }
*
* ```
*
* Use regular expressions compatible with PHP preg_match(). A good
* tool to create and test regular expressions can be found here:
* https://regex101.com/.
*
* @uxon-property validator_regex_negative
* @uxon-type string
*
* @param string $regularExpression
* @return StringDataType
*/
public function setValidatorRegexNegative(string $regularExpression) : StringDataType
{
$this->regexValidatorNegative = $regularExpression;
return $this;
}

/**
*
Expand All @@ -89,6 +134,10 @@ protected function getValidationDescription() : string
if ($this->getValidatorRegex()) {
$text = ($text ? $text . ' ' . $and . ' ' : '') . $translator->translate('DATATYPE.VALIDATION.REGEX_CONDITION', ['%regex%' => $this->getValidatorRegex()]);
}
if ($this->getValidatorRegexNegative()) {
$text = ($text ? $text . ' ' . $and . ' ' : '') .
$translator->translate('DATATYPE.VALIDATION.REGEX_CONDITION_NEGATIVE', ['%regex%' => $this->getValidatorRegexNegative()]);
}

if ($text !== '') {
$text = $translator->translate('DATATYPE.VALIDATION.MUST') . ' ' . $text . '.';
Expand Down Expand Up @@ -260,19 +309,52 @@ public function parse($string)
}

// validate against regex
if ($this->getValidatorRegexNegative()){
$matches = [];

try {
preg_match_all($this->getValidatorRegexNegative(), $value, $matches);
} catch (\Throwable $e) {
throw $this->createValidationRuleError($value, 'Validation regex "' . $this->getValidatorRegexNegative() . '" is invalid!');
}

if (!empty($matches)){
$excValue = '';
if (! $this->isSensitiveData()) {
$excValue = '"' . $value . '" ';
}

$issues = [];

foreach ($matches as $matchGroup) {
for($i = 0; $i < count($matchGroup); $i++){
$val = $matchGroup[$i];
$issues[$val] = $val;
}
}

if(!empty($issues)) {
throw $this->createValidationRuleError($value, 'Value ' . $excValue .
'must not match the regular expression mask "' . $this->getValidatorRegexNegative() .
'" of data type ' . $this->getAliasWithNamespace() . '! The following issues were detected: "' .
implode(', ', $issues) . '".', false);
}
}
}

if ($this->getValidatorRegex()){
try {
$match = preg_match($this->getValidatorRegex(), $value);
} catch (\Throwable $e) {
$match = 0;
}

if (! $match){
$excValue = '';
if (! $this->isSensitiveData()) {
$excValue = '"' . $value . '"';
$excValue = '"' . $value . '" ';
}
throw $this->createValidationRuleError($value, 'Value ' . $excValue . ' does not match the regular expression mask "' . $this->getValidatorRegex() . '" of data type ' . $this->getAliasWithNamespace() . '!', false);
throw $this->createValidationRuleError($value, 'Value ' . $excValue . 'does not match the regular expression mask "' . $this->getValidatorRegex() . '" of data type ' . $this->getAliasWithNamespace() . '!', false);
}
}

Expand Down Expand Up @@ -582,7 +664,8 @@ public static function encodeUTF8(string $string, string $originalEncoding = nul
* @param int $length
* @param bool $stickToWords prevents words getting cut in the middle
* @param bool $ellipsis adds `...` at the end if the string is really shortened
* @param bool $endHint adds `[truncated <original length> characters]` if the string is really shortened (usefull for debug output)
* @param bool $endHint adds `[truncated <original length> characters]` if the string is really shortened (usefull
* for debug output)
* @return string
*/
public static function truncate(string $string, int $length, bool $stickToWords = false, bool $removeLineBreaks = false, bool $ellipsis = false, bool $endHint = false) : string
Expand Down Expand Up @@ -718,7 +801,8 @@ public static function endSentence(string $text, string $puct = '.') : string
* - `transliterate('Änderung')` -> Anderung
* - `transliterate('Änderung', ':: Any-Latin; :: Latin-ASCII; :: Lower()')` -> anderung
* - `transliterate('ä/B', ':: Any-Latin; [:Punctuation:] Remove;')` -> a b
* - `transliterate('Aufgaben im Überblick', ':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: Lower(); :: NFC;')` -> aufgaben im uberblick
* - `transliterate('Aufgaben im Überblick', ':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove;
* :: Lower(); :: NFC;')` -> aufgaben im uberblick
*
* @link https://unicode-org.github.io/icu/userguide/transforms/general/
*
Expand Down Expand Up @@ -747,7 +831,8 @@ public static function transliterate(string $string, string $translitRules = '::
* Returns TRUE if the given string is one enclosed is quotes (single or double quotes) and FALSE otherwise
*
* Currently this does not check, if there are also some closing quotes in the middle of the string.
* Possible enhanced solution: https://stackoverflow.com/questions/74963883/php-regular-expression-to-grab-values-enclosed-in-double-quotes
* Possible enhanced solution:
* https://stackoverflow.com/questions/74963883/php-regular-expression-to-grab-values-enclosed-in-double-quotes
*
* @param string $str
* @return bool
Expand Down
81 changes: 56 additions & 25 deletions Facades/AbstractAjaxFacade/Formatters/JsStringFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ public function buildJsValidator(string $jsValue) : string
if ($type->getValidatorRegex() !== null) {
$checksOk[] = "{$type->getValidatorRegex()}.test({$jsValue}) !== false \n";
}

if ($type->getValidatorRegexNegative() !== null) {
$checksOk[] = "{$type->getValidatorRegexNegative()}.test({$jsValue}) === false \n";
}

$checksOkJs = ! empty($checksOk) ? implode(' && ', $checksOk) : 'true';

$nullStr = '" . EXF_LOGICAL_NULL . "';
Expand All @@ -65,46 +70,72 @@ public function buildJsGetValidatorIssues(string $jsValue): string
}

$regex = $dataType->getValidatorRegex();
if($regex === null) {
$regexNegative = $dataType->getValidatorRegexNegative();
if($regex === null && $regexNegative === null) {
return parent::buildJsGetValidatorIssues($jsValue);
}

$translator = $this->getWorkbench()->getCoreApp()->getTranslator();
$regexIssuePreamble = json_encode($translator->translate('DATATYPE.VALIDATION.FILENAME_INVALID_SYMBOLS'));

if(null !== $message = $dataType->getValidationErrorMessage()) {
$msg = StringDataType::endSentence($message->getTitle());
} else {
$msg = $translator->translate('DATATYPE.VALIDATION.FILENAME_INVALID');
}
$msg = json_encode($msg);

// Make sure the regex is global and not sticky.
$regex = StringDataType::removeRegexFlags($regex, ['g','y']);
$regex .= 'g';

return <<<JS

(function (sValue) {
var sIssues = {$msg};

// StringDataType::getValidatorRegex()
var regex = {$regex};
// Apply validator regex to string to extract matches.
var matches = sValue.match(regex);
$positiveJs = '';
if($regex !== null) {
$positiveJs = <<<JS

var aRegexIssues = [];
if (matches !== null && matches.length > 0) {
// Extract unqiue matches.
for (const match of matches) {
if (aRegexIssues.indexOf(match) === -1) {
aRegexIssues.push(match);
}
// If the negative regex did not produce issues, test the positive one.
// StringDataType::getValidatorRegex()
var regex = {$regex};
// Apply validator regex to string to extract matches.
if (regex.test(sValue) === false) {
return {$msg};
}
JS;
}

$negativeJs = '';
if($regexNegative !== null) {
$regexIssuePreamble = json_encode($translator->translate('DATATYPE.VALIDATION.FILENAME_INVALID_SYMBOLS'));

// Make sure the regex is global and not sticky.
$regexNegative = StringDataType::removeRegexFlags($regexNegative, ['g','y']);
$regexNegative .= 'g';

$negativeJs = <<<JS

var sIssues = {$msg};

// StringDataType::getValidatorRegexNegative()
var regexNegative = {$regexNegative};
// Apply negative validator regex to string to extract issues.
var matches = sValue.match(regexNegative);
var aRegexIssues = [];
// If we detected issues, we generate an output message.
if (matches !== null && matches.length > 0) {
// Extract unqiue matches.
for (const match of matches) {
if (aRegexIssues.indexOf(match) === -1) {
aRegexIssues.push(match);
}

return sIssues + ' ' + {$regexIssuePreamble} + JSON.stringify(aRegexIssues, null, 1).slice(1,-1) + '.';
}

return sIssues + ' ' + {$regexIssuePreamble} + JSON.stringify(aRegexIssues, null, 1).slice(1,-1) + '.';
}
JS;
}


return <<<JS

(function (sValue) {
{$negativeJs}

{$positiveJs}

return '';
})($jsValue)
JS;
Expand Down
1 change: 1 addition & 0 deletions Translations/exface.Core.de.json
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@
"DATATYPE.VALIDATION.MUST": "Muss",
"DATATYPE.VALIDATION.LENGTH_CONDITION": "%condition% Zeichen lang sein",
"DATATYPE.VALIDATION.REGEX_CONDITION": "der Maske %regex% entsprechen",
"DATATYPE.VALIDATION.REGEX_CONDITION_NEGATIVE": "nicht der Maske %regex% entsprechen",
"DATATYPE.VALIDATION.FILENAME_INVALID": "Ungültige Zeichenkette!",
"DATATYPE.VALIDATION.FILENAME_INVALID_SYMBOLS": "Unzulässige Zeichen: ",
"DATATYPE.VALIDATION.AND": "und",
Expand Down
1 change: 1 addition & 0 deletions Translations/exface.Core.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@
"DATATYPE.VALIDATION.MUST": "Must",
"DATATYPE.VALIDATION.LENGTH_CONDITION": "have %condition% characters",
"DATATYPE.VALIDATION.REGEX_CONDITION": "match the pattern %regex%",
"DATATYPE.VALIDATION.REGEX_CONDITION_NEGATIVE": "not match the pattern %regex%",
"DATATYPE.VALIDATION.FILENAME_INVALID": "Invalid string!",
"DATATYPE.VALIDATION.FILENAME_INVALID_SYMBOLS": "Illegal characters: ",
"DATATYPE.VALIDATION.MINMAX_CONDITION": "be %condition%",
Expand Down