Open Journal Systems  3.3.0
MetadataProperty.inc.php
1 <?php
2 
33 // literal values (plain)
34 define('METADATA_PROPERTY_TYPE_STRING', 0x01);
35 
36 // literal values (typed)
37 define('METADATA_PROPERTY_TYPE_DATE', 0x02); // This is W3CDTF encoding without time (YYYY[-MM[-DD]])!
38 define('METADATA_PROPERTY_TYPE_INTEGER', 0x03);
39 
40 // non-literal value string from a controlled vocabulary
41 define('METADATA_PROPERTY_TYPE_VOCABULARY', 0x04);
42 
43 // non-literal value URI
44 define('METADATA_PROPERTY_TYPE_URI', 0x05);
45 
46 // non-literal value pointing to a separate description set instance (=another MetadataRecord object)
47 define('METADATA_PROPERTY_TYPE_COMPOSITE', 0x06);
48 
49 // allowed cardinality of statements for a given property type in a meta-data schema
50 define('METADATA_PROPERTY_CARDINALITY_ONE', 0x01);
51 define('METADATA_PROPERTY_CARDINALITY_MANY', 0x02);
52 
55  var $_name;
56 
59 
61  var $_assocTypes;
62 
65 
67  var $_translated;
68 
71 
74 
77 
90  function __construct($name, $assocTypes = array(), $allowedTypes = METADATA_PROPERTY_TYPE_STRING,
91  $translated = false, $cardinality = METADATA_PROPERTY_CARDINALITY_ONE, $displayName = null, $validationMessage = null, $mandatory = false) {
92 
93  // Validate name and assoc type array
94  if (!is_string($name)) throw new InvalidArgumentException('$name should be a string.');
95  if (!is_array($assocTypes)) throw new InvalidArgumentException('$assocTypes should be an array.');
96 
97  // A single type will be transformed to an
98  // array of types so that we can handle them
99  // uniformly.
100  if (is_scalar($allowedTypes) || count($allowedTypes) == 1) {
101  $allowedTypes = array($allowedTypes);
102  }
103 
104  // Validate types
105  $canonicalizedTypes = array();
106  foreach($allowedTypes as $allowedType) {
107  if (is_array($allowedType)) {
108  // We expect an array with a single entry
109  // of the form "type => additional parameter".
110  assert(count($allowedType) == 1);
111  // Reset the array, just in case...
112  reset($allowedType);
113  // Extract the type and the additional parameter
114  $allowedTypeId = key($allowedType);
115  $allowedTypeParam = current($allowedType);
116  } else {
117  // No additional parameter has been set.
118  $allowedTypeId = $allowedType;
119  $allowedTypeParam = null;
120  }
121 
122  // Validate type
123  if (!in_array($allowedTypeId, MetadataProperty::getSupportedTypes())) throw new InvalidArgumentException('Allowed types must be supported types!');
124 
125  // Transform the type array in a
126  // structure that is easy to handle
127  // in for loops.
128  $canonicalizedTypes[$allowedTypeId][] = $allowedTypeParam;
129 
130  // Validate additional type parameter.
131  switch($allowedTypeId) {
132  case METADATA_PROPERTY_TYPE_COMPOSITE:
133  // Validate the assoc id of the composite.
134  if (!is_integer($allowedTypeParam)) throw new InvalidArgumentException('Allowed type parameter should be an integer.');
135  // Properties that allow composite types cannot be translated.
136  if ($translated) throw new InvalidArgumentException('Properties that allow composite types cannot be translated.');
137  break;
138 
139  case METADATA_PROPERTY_TYPE_VOCABULARY:
140  // Validate the symbolic name of the vocabulary.
141  if (!is_string($allowedTypeParam)) throw new InvalidArgumentException('Allowed type parameter should be a string.');
142  break;
143 
144  default:
145  // No other types support an additional parameter
146  if (!is_null($allowedTypeParam)) throw new InvalidArgumentException('An additional parameter was supplied for an unsupported metadata property type.');
147  }
148  }
149 
150  // Validate translation and cardinality
151  if (!is_bool($translated)) throw new InvalidArgumentException('$translated must be a boolean');
152  if (!in_array($cardinality, MetadataProperty::getSupportedCardinalities())) throw new InvalidArgumentException('$cardinality must be a supported cardinality.');
153 
154  // Default display name
155  if (is_null($displayName)) $displayName = 'metadata.property.displayName.'.$name;
156  if (!is_string($displayName)) throw new InvalidArgumentException('$displayName must be a string.');
157 
158  // Default validation message
159  if (is_null($validationMessage)) $validationMessage = 'metadata.property.validationMessage.'.$name;
160  if (!is_string($validationMessage)) throw new InvalidArgumentException('$validationMessage must be a string.');
161 
162 
163  // Initialize the class
164  $this->_name = (string)$name;
165  $this->_assocTypes =& $assocTypes;
166  $this->_allowedTypes =& $canonicalizedTypes;
167  $this->_translated = (boolean)$translated;
168  $this->_cardinality = (integer)$cardinality;
169  $this->_displayName = (string)$displayName;
170  $this->_validationMessage = (string)$validationMessage;
171  $this->_mandatory = (boolean)$mandatory;
172  }
173 
178  function getName() {
179  return $this->_name;
180  }
181 
188  function getId() {
189  // Replace special characters in XPath-like names
190  // as 'person-group[@person-group-type="author"]'.
191  $from = array(
192  '[', ']', '@', '"', '='
193  );
194  $to = array(
195  '-', '', '', '', '-'
196  );
197  $propertyId = trim(str_replace($from, $to, $this->getName()), '-');
198  $propertyId = PKPString::camelize($propertyId);
199  return $propertyId;
200  }
201 
207  function getDisplayName() {
208  return $this->_displayName;
209  }
210 
218  function &getAssocTypes() {
219  return $this->_assocTypes;
220  }
221 
226  function getAllowedTypes() {
227  return $this->_allowedTypes;
228  }
229 
234  function getTranslated() {
235  return $this->_translated;
236  }
237 
242  function getCardinality() {
243  return $this->_cardinality;
244  }
245 
250  function getValidationMessage() {
252  }
253 
258  function getMandatory() {
259  return $this->_mandatory;
260  }
261 
262 
263  //
264  // Public methods
265  //
280  function isValid($value, $locale = null) {
281  // We never accept null values or arrays.
282  if (is_null($value) || is_array($value)) return false;
283 
284  // Translate the locale.
285  if (is_null($locale)) $locale = '';
286 
287  // MetadataProperty::getSupportedTypes() returns an ordered
288  // list of possible meta-data types with the most specific
289  // type coming first so that we always correctly identify
290  // specializations (e.g. a date is a specialized string).
291  $allowedTypes = $this->getAllowedTypes();
292  foreach (MetadataProperty::getSupportedTypes() as $testedType) {
293  if (isset($allowedTypes[$testedType])) {
294  foreach ($allowedTypes[$testedType] as $allowedTypeParam) {
295  // Type specific validation
296  switch ($testedType) {
297  case METADATA_PROPERTY_TYPE_COMPOSITE:
298  // Composites can either be represented by a meta-data description
299  // or by a string of the form AssocType:AssocId if the composite
300  // has already been persisted in the database.
301  switch(true) {
302  // Test for MetadataDescription format
303  case is_a($value, 'MetadataDescription'):
304  $assocType = $value->getAssocType();
305  break;
306 
307  // Test for AssocType:AssocId format
308  case is_string($value):
309  $valueParts = explode(':', $value);
310  if (count($valueParts) != 2) break 2; // break the outer switch
311  list($assocType, $assocId) = $valueParts;
312  if (!(is_numeric($assocType) && is_numeric($assocId))) break 2; // break the outer switch
313  $assocType = (integer)$assocType;
314  break;
315 
316  default:
317  // None of the allowed types
318  break;
319  }
320 
321  // Check that the association type matches
322  // with the allowed association type (which
323  // is configured as an additional type parameter).
324  if (isset($assocType) && $assocType === $allowedTypeParam) return array(METADATA_PROPERTY_TYPE_COMPOSITE => $assocType);
325  break;
326 
327  case METADATA_PROPERTY_TYPE_VOCABULARY:
328  // Interpret the type parameter of this type like this:
329  // symbolic[:assoc-type:assoc-id]. If no assoc type/id are
330  // given then we assume :0:0 to represent site-wide vocabs.
331  $vocabNameParts = explode(':', $allowedTypeParam);
332  $vocabNamePartsCount = count($vocabNameParts);
333  switch ($vocabNamePartsCount) {
334  case 1:
335  // assume a site-wide vocabulary
336  $symbolic = $allowedTypeParam;
337  $assocType = $assocId = 0;
338  break;
339 
340  case 3:
341  // assume a context-specific vocabulary
342  list($symbolic, $assocType, $assocId) = $vocabNameParts;
343  break;
344 
345  default:
346  // Invalid configuration
347  assert(false);
348  }
349 
350  if (is_string($value)) {
351  // Try to translate the string value into a controlled vocab entry
352  $controlledVocabEntryDao = DAORegistry::getDAO('ControlledVocabEntryDAO'); /* @var $controlledVocabEntryDao ControlledVocabEntryDAO */
353  if (!is_null($controlledVocabEntryDao->getBySetting($value, $symbolic, $assocType, $assocId, 'name', $locale))) {
354  // The string was successfully translated so mark it as "valid".
355  return array(METADATA_PROPERTY_TYPE_VOCABULARY => $allowedTypeParam);
356  }
357  }
358 
359  if (is_integer($value)) {
360  // Validate with controlled vocabulary validator
361  import('lib.pkp.classes.validation.ValidatorControlledVocab');
362  $validator = new ValidatorControlledVocab($symbolic, $assocType, $assocId);
363  if ($validator->isValid($value)) {
364  return array(METADATA_PROPERTY_TYPE_VOCABULARY => $allowedTypeParam);
365  }
366  }
367 
368  break;
369 
370  case METADATA_PROPERTY_TYPE_URI:
371  import('lib.pkp.classes.validation.ValidatorFactory');
372  $validator = ValidatorFactory::make(
373  array('uri' => $value),
374  array('uri' => 'url')
375  );
376  if (!$validator->fails()) {
377  return array(METADATA_PROPERTY_TYPE_URI => null);
378  }
379  break;
380 
381  case METADATA_PROPERTY_TYPE_DATE:
382  // We allow the following patterns:
383  // YYYY-MM-DD, YYYY-MM and YYYY
384  $datePattern = '/^[0-9]{4}(-[0-9]{2}(-[0-9]{2})?)?$/';
385  if (!preg_match($datePattern, $value)) break;
386 
387  // Check whether the given string is really a valid date
388  $dateParts = explode('-', $value);
389  // Set the day and/or month to 1 if not set
390  $dateParts = array_pad($dateParts, 3, 1);
391  // Extract the date parts
392  list($year, $month, $day) = $dateParts;
393  // Validate the date (only leap days will pass unnoticed ;-) )
394  // Who invented this argument order?
395  if (checkdate($month, $day, $year)) return array(METADATA_PROPERTY_TYPE_DATE => null);
396  break;
397 
398  case METADATA_PROPERTY_TYPE_INTEGER:
399  if (is_integer($value)) return array(METADATA_PROPERTY_TYPE_INTEGER => null);
400  break;
401 
402  case METADATA_PROPERTY_TYPE_STRING:
403  if (is_string($value)) return array(METADATA_PROPERTY_TYPE_STRING => null);
404  break;
405 
406  default:
407  // Unknown type. As we validate type in the setter, this
408  // should be unreachable code.
409  assert(false);
410  }
411  }
412  }
413  }
414 
415  // Return false if the value didn't validate against any
416  // of the allowed types.
417  return false;
418  }
419 
420  //
421  // Public static methods
422  //
434  static function getSupportedTypes() {
435  static $_supportedTypes = array(
436  METADATA_PROPERTY_TYPE_COMPOSITE,
437  METADATA_PROPERTY_TYPE_VOCABULARY,
438  METADATA_PROPERTY_TYPE_URI,
439  METADATA_PROPERTY_TYPE_DATE,
440  METADATA_PROPERTY_TYPE_INTEGER,
441  METADATA_PROPERTY_TYPE_STRING
442  );
443  return $_supportedTypes;
444  }
445 
450  static function getSupportedCardinalities() {
451  static $_supportedCardinalities = array(
452  METADATA_PROPERTY_CARDINALITY_ONE,
453  METADATA_PROPERTY_CARDINALITY_MANY
454  );
455  return $_supportedCardinalities;
456  }
457 }
458 
459 
MetadataProperty\getValidationMessage
getValidationMessage()
Definition: MetadataProperty.inc.php:274
MetadataProperty\$_name
$_name
Definition: MetadataProperty.inc.php:58
MetadataProperty\getName
getName()
Definition: MetadataProperty.inc.php:202
DAORegistry\getDAO
static & getDAO($name, $dbconn=null)
Definition: DAORegistry.inc.php:57
MetadataProperty\getAssocTypes
& getAssocTypes()
Definition: MetadataProperty.inc.php:242
MetadataProperty\getDisplayName
getDisplayName()
Definition: MetadataProperty.inc.php:231
MetadataProperty\$_cardinality
$_cardinality
Definition: MetadataProperty.inc.php:88
MetadataProperty\$_validationMessage
$_validationMessage
Definition: MetadataProperty.inc.php:94
MetadataProperty\getAllowedTypes
getAllowedTypes()
Definition: MetadataProperty.inc.php:250
MetadataProperty\$_displayName
$_displayName
Definition: MetadataProperty.inc.php:64
MetadataProperty\getSupportedCardinalities
static getSupportedCardinalities()
Definition: MetadataProperty.inc.php:474
MetadataProperty\getMandatory
getMandatory()
Definition: MetadataProperty.inc.php:282
ValidatorFactory\make
static make($props, $rules, $messages=[])
Definition: ValidatorFactory.inc.php:38
MetadataProperty\getTranslated
getTranslated()
Definition: MetadataProperty.inc.php:258
ValidatorControlledVocab
Validation check that checks if value is within a certain set retrieved from the database.
Definition: ValidatorControlledVocab.inc.php:19
MetadataProperty\isValid
isValid($value, $locale=null)
Definition: MetadataProperty.inc.php:304
MetadataProperty\$_translated
$_translated
Definition: MetadataProperty.inc.php:82
MetadataProperty\getId
getId()
Definition: MetadataProperty.inc.php:212
PKPString\camelize
static camelize($string, $type=CAMEL_CASE_HEAD_UP)
Definition: PKPString.inc.php:435
MetadataProperty\$_mandatory
$_mandatory
Definition: MetadataProperty.inc.php:100
MetadataProperty\$_assocTypes
$_assocTypes
Definition: MetadataProperty.inc.php:70
MetadataProperty
Class representing metadata properties. It specifies type and cardinality of a meta-data property (=t...
Definition: MetadataProperty.inc.php:53
MetadataProperty\getSupportedTypes
static getSupportedTypes()
Definition: MetadataProperty.inc.php:458
MetadataProperty\getCardinality
getCardinality()
Definition: MetadataProperty.inc.php:266
MetadataProperty\$_allowedTypes
$_allowedTypes
Definition: MetadataProperty.inc.php:76
MetadataProperty\__construct
__construct($name, $assocTypes=array(), $allowedTypes=METADATA_PROPERTY_TYPE_STRING, $translated=false, $cardinality=METADATA_PROPERTY_CARDINALITY_ONE, $displayName=null, $validationMessage=null, $mandatory=false)
Definition: MetadataProperty.inc.php:114