/** * Get the names of properties with a given data type. * @param $propertyType mixed a valid property type description * @return array an array of string values representing valid property names */ function getPropertyNamesByType($propertyType) { assert(in_array($propertyType, MetadataProperty::getSupportedTypes())); $propertyNames = array(); foreach ($this->_properties as $property) { $allowedPropertyTypes = $property->getAllowedTypes(); if (isset($allowedPropertyTypes[$propertyType])) { $propertyNames[] = $property->getName(); } } return $propertyNames; }
/** * @covers MetadataProperty::isValid */ public function testValidateMultitype() { $metadataProperty = new MetadataProperty('testElement', array(), array(METADATA_PROPERTY_TYPE_DATE, METADATA_PROPERTY_TYPE_INTEGER), false, METADATA_PROPERTY_CARDINALITY_ONE); self::assertEquals(array(METADATA_PROPERTY_TYPE_DATE => null), $metadataProperty->isValid('2009-07-28')); self::assertEquals(array(METADATA_PROPERTY_TYPE_INTEGER => null), $metadataProperty->isValid(5)); self::assertFalse($metadataProperty->isValid(null)); self::assertFalse($metadataProperty->isValid('string')); }
/** * Validate a given input against the property specification * * The given value must validate against at least one of the * allowed types. The first allowed type id will be returned as * validation result. If the given value fits none of the allowed * types, then we'll return 'false'. * * @param $value mixed the input to be validated * @param $locale string the locale to be used for validation * @return array|boolean an array with a single entry of the format * "type => additional type parameter" against which the value * validated or boolean false if not validated at all. */ function isValid($value, $locale = null) { // We never accept null values or arrays. if (is_null($value) || is_array($value)) { return false; } // Translate the locale. if (is_null($locale)) { $locale = ''; } // MetadataProperty::getSupportedTypes() returns an ordered // list of possible meta-data types with the most specific // type coming first so that we always correctly identify // specializations (e.g. a date is a specialized string). $allowedTypes = $this->getAllowedTypes(); foreach (MetadataProperty::getSupportedTypes() as $testedType) { if (isset($allowedTypes[$testedType])) { foreach ($allowedTypes[$testedType] as $allowedTypeParam) { // Type specific validation switch ($testedType) { case METADATA_PROPERTY_TYPE_COMPOSITE: // Composites can either be represented by a meta-data description // or by a string of the form AssocType:AssocId if the composite // has already been persisted in the database. switch (true) { // Test for MetadataDescription format case is_a($value, 'MetadataDescription'): $assocType = $value->getAssocType(); break; // Test for AssocType:AssocId format // Test for AssocType:AssocId format case is_string($value): $valueParts = explode(':', $value); if (count($valueParts) != 2) { break 2; } // break the outer switch list($assocType, $assocId) = $valueParts; if (!(is_numeric($assocType) && is_numeric($assocId))) { break 2; } // break the outer switch $assocType = (int) $assocType; break; default: // None of the allowed types break; } // Check that the association type matches // with the allowed association type (which // is configured as an additional type parameter). if (isset($assocType) && $assocType === $allowedTypeParam) { return array(METADATA_PROPERTY_TYPE_COMPOSITE => $assocType); } break; case METADATA_PROPERTY_TYPE_VOCABULARY: // Interpret the type parameter of this type like this: // symbolic[:assoc-type:assoc-id]. If no assoc type/id are // given then we assume :0:0 to represent site-wide vocabs. $vocabNameParts = explode(':', $allowedTypeParam); $vocabNamePartsCount = count($vocabNameParts); switch ($vocabNamePartsCount) { case 1: // assume a site-wide vocabulary $symbolic = $allowedTypeParam; $assocType = $assocId = 0; break; case 3: // assume a context-specific vocabulary list($symbolic, $assocType, $assocId) = $vocabNameParts; break; default: // Invalid configuration assert(false); } if (is_string($value)) { // Try to translate the string value into a controlled vocab entry $controlledVocabEntryDao =& DAORegistry::getDao('ControlledVocabEntryDAO'); /* @var $controlledVocabEntryDao ControlledVocabEntryDAO */ if (!is_null($controlledVocabEntryDao->getBySetting($value, $symbolic, $assocType, $assocId, 'name', $locale))) { // The string was successfully translated so mark it as "valid". return array(METADATA_PROPERTY_TYPE_VOCABULARY => $allowedTypeParam); } } if (is_integer($value)) { // Validate with controlled vocabulary validator import('lib.pkp.classes.validation.ValidatorControlledVocab'); $validator = new ValidatorControlledVocab($symbolic, $assocType, $assocId); if ($validator->isValid($value)) { return array(METADATA_PROPERTY_TYPE_VOCABULARY => $allowedTypeParam); } } break; case METADATA_PROPERTY_TYPE_URI: // Validate with the URI validator import('lib.pkp.classes.validation.ValidatorUri'); $validator = new ValidatorUri(); if ($validator->isValid($value)) { return array(METADATA_PROPERTY_TYPE_URI => null); } break; case METADATA_PROPERTY_TYPE_DATE: // We allow the following patterns: // YYYY-MM-DD, YYYY-MM and YYYY $datePattern = '/^[0-9]{4}(-[0-9]{2}(-[0-9]{2})?)?$/'; if (!preg_match($datePattern, $value)) { break; } // Check whether the given string is really a valid date $dateParts = explode('-', $value); // Set the day and/or month to 1 if not set $dateParts = array_pad($dateParts, 3, 1); // Extract the date parts list($year, $month, $day) = $dateParts; // Validate the date (only leap days will pass unnoticed ;-) ) // Who invented this argument order? if (checkdate($month, $day, $year)) { return array(METADATA_PROPERTY_TYPE_DATE => null); } break; case METADATA_PROPERTY_TYPE_INTEGER: if (is_integer($value)) { return array(METADATA_PROPERTY_TYPE_INTEGER => null); } break; case METADATA_PROPERTY_TYPE_STRING: if (is_string($value)) { return array(METADATA_PROPERTY_TYPE_STRING => null); } break; default: // Unknown type. As we validate type in the setter, this // should be unreachable code. assert(false); } } } } // Return false if the value didn't validate against any // of the allowed types. return false; }
/** * Constructor * @param $name string the unique name of the property within a meta-data schema (can be a property URI) * @param $assocTypes array an array of integers that define the application entities that can * be described with this property. * @param $types mixed must be a scalar or an array with the supported types, default: METADATA_PROPERTY_TYPE_STRING * @param $translated boolean whether the property may have various language versions, default: false * @param $cardinality integer must be on of the supported cardinalities, default: METADATA_PROPERTY_CARDINALITY_ONE * @param $compositeType integer an association type, mandatory if $type is METADATA_PROPERTY_TYPE_COMPOSITE */ function MetadataProperty($name, $assocTypes = array(), $types = METADATA_PROPERTY_TYPE_STRING, $translated = false, $cardinality = METADATA_PROPERTY_CARDINALITY_ONE, $displayName = null) { // Validate name and assoc type array assert(is_string($name)); assert(is_array($assocTypes)); // A single type (scalar or composite) will be // transformed to an array of types so that we // can treat them uniformly. if (is_scalar($types) || count($types) == 1) { $types = array($types); } // Validate types foreach ($types as $type) { if (is_array($type)) { // Validate composite types assert(count($type) == 1 && isset($type[METADATA_PROPERTY_TYPE_COMPOSITE]) && is_integer($type[METADATA_PROPERTY_TYPE_COMPOSITE])); // Properties that allow composite types cannot be translated assert(!$translated); } else { // Validate all other types assert($type != METADATA_PROPERTY_TYPE_COMPOSITE && in_array($type, MetadataProperty::getSupportedTypes())); } } // Validate translation and cardinality assert(is_bool($translated)); assert(in_array($cardinality, MetadataProperty::getSupportedCardinalities())); // Default display name if (is_null($displayName)) { $displayName = 'metadata.property.displayName.' . $name; } assert(is_string($displayName)); // Initialize the class $this->_name = (string) $name; $this->_assocTypes =& $assocTypes; $this->_types =& $types; $this->_translated = (bool) $translated; $this->_cardinality = (int) $cardinality; $this->_displayName = (string) $displayName; }