*/ declare(strict_types = 1); /** * A class of static functions for validating single fields of noda entities. */ final class NodaValidationHelper { private const ACTOR_DESCRIPTION_REQUIRED_DISTINCT_CHARS = 3; /** * Validates an actor description for completeness. Of course, only an informed * guess based on the length and character composition of the description can be * made. * * @param string $description Input descrition. * @param string $name Names of the actor. Optional. Setting this enables * checks e.g. to prevent duplicating the actor name * as a description. * * @return void */ public static function validateTagDescription(string $description, string $name = ""):void { // For nodac, empty tag descriptions are to be allowed. Thus, a // dedicated check for fully empty ones with a dedicated exception // is needed. if (empty($description)) { throw new MDInvalidEmptyInputException("No tag name is provided"); } // Throw error on descriptions that are too short if (\mb_strlen($description) < 10) { throw new MDgenericInvalidInputsException("Tag description is too short"); } // Validate tag description based on character composition. // Ensure more than 3 distinct characters are used. $chars = \str_split($description); $uniqueChars = array_unique($chars); if (count($uniqueChars) <= self::ACTOR_DESCRIPTION_REQUIRED_DISTINCT_CHARS) { throw new MDgenericInvalidInputsException("There need to be more than " . self::ACTOR_DESCRIPTION_REQUIRED_DISTINCT_CHARS . " distinct characters."); } // Ensure more than the actor name is used. $clearedChars = [' ' => ' ', ',' => ' ', ';' => ' ', '.' => ' ']; $uniqueNames = array_unique(array_diff(explode(' ', strtr($name, $clearedChars)), [''])); sort($uniqueNames); $descCleared = strtr($description, $clearedChars); $descWords = array_unique(array_diff(explode(' ', $descCleared), [''])); sort($descWords); if ($uniqueNames === $descWords) { throw new MDgenericInvalidInputsException("The tag name was simply repeated in the description. This is not enough."); } } /** * Validates an actor description for completeness. Of course, only an informed * guess based on the length and character composition of the description can be * made. * * @param string $description Input descrition. * @param string[] $names Names of the actor. Optional. Setting this enables * checks e.g. to prevent duplicating the actor name * as a description. * * @return void */ public static function validateActorDescription(string $description, array $names = []):void { // For nodac, empty actor descriptions are to be allowed. Thus, a // dedicated check for fully empty ones with a dedicated exception // is needed. if (empty($description)) { throw new MDInvalidEmptyInputException("No author name is provided"); } // Throw error on descriptions that are too short if (\mb_strlen($description) < 10) { throw new MDgenericInvalidInputsException("Author description is too short"); } // Validate actor description based on character composition. $chars = \str_split($description); $uniqueChars = array_unique($chars); if (count($uniqueChars) <= self::ACTOR_DESCRIPTION_REQUIRED_DISTINCT_CHARS) { throw new MDgenericInvalidInputsException("There need to be more than " . self::ACTOR_DESCRIPTION_REQUIRED_DISTINCT_CHARS . " distinct characters."); } if (!empty($names)) { $clearedChars = [' ' => ' ', ',' => ' ', ';' => ' ', '.' => ' ']; $namesMerged = implode(' ', $names); $namesMerged = strtr($namesMerged, $clearedChars); $uniqueNames = array_unique(array_diff(explode(' ', $namesMerged), [''])); sort($uniqueNames); $descCleared = strtr($description, $clearedChars); $descWords = array_unique(array_diff(explode(' ', $descCleared), [''])); sort($descWords); if ($uniqueNames === $descWords) { throw new MDgenericInvalidInputsException("The actor name was simply repeated in the description. This is not enough."); } } } /** * Checks if an actor name is valid. * Returns 0 if the actor can be added as a new one / actor name can be used as a new one. * Returns the ID for already known actors. * Throws an exception if the actor is blacklisted. * * @param MDMysqli $mysqli_noda DB connection to vocabulary. * @param string $lang User language. * @param string $persinst_name Actor name. * @param string $persinst_name_en Actor name (short). * @param string $persinst_geburtsjahr Birth year. * @param string $persinst_sterbejahr Death year. * @param string $context_identifier Context / instance identifier. * @param integer $institution_id Institution ID. * * @return integer */ public static function checkPersinstNameIsUsable(MDMysqli $mysqli_noda, string $lang, string $persinst_name, string $persinst_name_en, string $persinst_geburtsjahr, string $persinst_sterbejahr, string $context_identifier, int $institution_id):int { if ($storedType = NodaDistinctlyTypedStrings::lookup($mysqli_noda, $lang, $persinst_name_en)) { if ($storedType !== 'persinst') { throw new MDpageParameterMissingException("This term is marked to be distinctly a " . $storedType); } } // Special case: ? or "unbekannt" as a given name. if (str_starts_with($persinst_name_en, '? ') || str_starts_with(strtolower($persinst_name_en), 'unbekannt ') ) { throw new MDBlacklistedInputException("Term is blacklisted"); } // Check if the place name is in the current language's blacklist. if (NodaBlacklistedTerms::checkPersinstBlacklistedInDb($mysqli_noda, $lang, $persinst_name) === true) { throw new MDBlacklistedInputException("Term is blacklisted"); } if (NodaBlacklistedTerms::checkPersinstBlacklistedInDb($mysqli_noda, $lang, $persinst_name_en) === true) { throw new MDBlacklistedInputException("Term is blacklisted"); } if ($existingPersinstId = NodaIDGetter::getPersinstIDByNamesAndRewrites($mysqli_noda, $lang, $persinst_name_en, $persinst_geburtsjahr, $persinst_sterbejahr, $context_identifier, $institution_id)) { return $existingPersinstId; } if ($existingPersinstId = NodaIDGetter::getPersinstIDByNamesAndRewrites($mysqli_noda, $lang, $persinst_name, $persinst_geburtsjahr, $persinst_sterbejahr, $context_identifier, $institution_id)) { return $existingPersinstId; } return 0; } /** * Checks if an place name is valid. * Returns 0 if the place can be added as a new one / place name can be used as a new one. * Returns the ID for already known places. * Throws an exception if the place is blacklisted. * * @param MDMysqli $mysqli_noda DB connection to vocabulary. * @param string $lang User language. * @param string $place_name Place name. * @param string $context_identifier Context / instance identifier. * @param integer $institution_id Institution ID. * * @return integer */ public static function checkPlaceNameIsUsable(MDMysqli $mysqli_noda, string $lang, string $place_name, string $context_identifier, int $institution_id):int { if ($storedType = NodaDistinctlyTypedStrings::lookup($mysqli_noda, $lang, $place_name)) { if ($storedType !== 'orte') { throw new MDpageParameterMissingException("This term is marked to be distinctly a " . $storedType); } } // Check if the place name is in the current language's blacklist. if (NodaBlacklistedTerms::checkPlaceBlacklistedInDb($mysqli_noda, $lang, $place_name) === true) { throw new MDBlacklistedInputException("Term is blacklisted"); } if ($existingPlaceId = NodaIDGetter::getPlaceIDByNamesAndRewrites($mysqli_noda, $lang, $place_name, $context_identifier, $institution_id)) { return $existingPlaceId; } return 0; } }