17 define(
'SCHEMA_ANNOUNCEMENT',
'announcement');
18 define(
'SCHEMA_AUTHOR',
'author');
19 define(
'SCHEMA_CONTEXT',
'context');
20 define(
'SCHEMA_EMAIL_TEMPLATE',
'emailTemplate');
21 define(
'SCHEMA_GALLEY',
'galley');
22 define(
'SCHEMA_ISSUE',
'issue');
23 define(
'SCHEMA_PUBLICATION',
'publication');
24 define(
'SCHEMA_REVIEW_ASSIGNMENT',
'reviewAssignment');
25 define(
'SCHEMA_REVIEW_ROUND',
'reviewRound');
26 define(
'SCHEMA_SECTION',
'section');
27 define(
'SCHEMA_SITE',
'site');
28 define(
'SCHEMA_SUBMISSION',
'submission');
29 define(
'SCHEMA_SUBMISSION_FILE',
'submissionFile');
30 define(
'SCHEMA_USER',
'user');
31 define(
'SCHEMA_USER_GROUP',
'userGroup');
35 private $_schemas = [];
49 public function get($schemaName, $forceReload =
false) {
51 if (!$forceReload && array_key_exists($schemaName, $this->_schemas)) {
52 return $this->_schemas[$schemaName];
55 $schemaFile = sprintf(
'%s/lib/pkp/schemas/%s.json',
BASE_SYS_DIR, $schemaName);
56 if (file_exists($schemaFile)) {
57 $schema = json_decode(file_get_contents($schemaFile));
59 fatalError(
'Schema failed to decode. This usually means it is invalid JSON. Requested: ' . $schemaFile .
'. Last JSON error: ' . json_last_error());
63 $schema = new \stdClass();
67 $appSchemaFile = sprintf(
'%s/schemas/%s.json',
BASE_SYS_DIR, $schemaName);
68 if (file_exists($appSchemaFile)) {
69 $appSchema =
json_decode(file_get_contents($appSchemaFile));
71 fatalError(
'Schema failed to decode. This usually means it is invalid JSON. Requested: ' . $appSchemaFile .
'. Last JSON error: ' . json_last_error());
73 $schema = $this->
merge($schema, $appSchema);
78 $this->_schemas[$schemaName] = $schema;
97 public function merge($baseSchema, $additionalSchema) {
98 $newSchema = clone $baseSchema;
99 if (!empty($additionalSchema->title)) {
100 $newSchema->title = $additionalSchema->title;
102 if (!empty($additionalSchema->description)) {
103 $newSchema->description = $additionalSchema->description;
105 if (!empty($additionalSchema->properties)) {
106 if (empty($newSchema->properties)) {
107 $newSchema->properties = new \stdClass();
109 foreach ($additionalSchema->properties as $propName => $propSchema) {
110 $newSchema->properties->{$propName} = $propSchema;
126 public function getSummaryProps($schemaName) {
127 $schema = $this->
get($schemaName);
129 foreach ($schema->properties as $propName => $propSchema) {
130 if (property_exists($propSchema,
'apiSummary') && $propSchema->apiSummary) {
131 $props[] = $propName;
147 public function getFullProps($schemaName) {
148 $schema = $this->
get($schemaName);
151 foreach ($schema->properties as $propName => $propSchema) {
152 if (empty($propSchema->writeOnly)) {
153 $propNames[] = $propName;
166 public function getRequiredProps($schemaName) {
167 $schema = $this->
get($schemaName);
169 if (!empty($schema->required)) {
170 return $schema->required;
181 public function getMultilingualProps($schemaName) {
182 $schema = $this->
get($schemaName);
184 $multilingualProps = [];
185 foreach ($schema->properties as $propName => $propSchema) {
186 if (!empty($propSchema->multilingual)) {
187 $multilingualProps[] = $propName;
191 return $multilingualProps;
204 public function sanitize($schemaName, $props) {
205 $schema = $this->
get($schemaName);
208 foreach ($props as $propName => $propValue) {
209 if (empty($schema->properties->{$propName})
210 || empty($schema->properties->{$propName}->type)
211 || !empty($schema->properties->{$propName}->readOnly)) {
214 $propSchema = $schema->properties->{$propName};
215 if (!empty($propSchema->multilingual)) {
217 foreach ((array) $propValue as $localeKey => $localeValue) {
218 $values[$localeKey] = $this->coerce($localeValue, $propSchema->type, $propSchema);
220 if (!empty($values)) {
221 $cleanProps[$propName] = $values;
224 $cleanProps[$propName] = $this->coerce($propValue, $propSchema->type, $propSchema);
241 public function coerce($value, $type, $schema) {
242 if (is_null($value)) {
247 return (
bool) $value;
251 return (
float) $value;
253 if (is_object($value) || is_array($value)) {
254 $value = serialize($value);
256 return (
string) $value;
259 if (is_array($schema->items)) {
260 foreach ($schema->items as $i => $itemSchema) {
261 $newArray[$i] = $this->coerce($value[$i], $itemSchema->type, $itemSchema);
263 } elseif (is_array($value)) {
264 foreach ($value as $i => $v) {
265 $newArray[$i] = $this->coerce($v, $schema->items->type, $schema->items);
268 $newArray[] = serialize($value);
273 foreach ($schema->properties as $propName => $propSchema) {
274 if (!isset($value[$propName]) || !empty($propSchema->readOnly)) {
277 $newObject[$propName] = $this->coerce($value[$propName], $propSchema->type, $propSchema);
281 fatalError(
'Requested variable coercion for a type that was not recognized: ' . $type);
294 public function getValidationRules($schemaName, $allowedLocales) {
295 $schema = $this->
get($schemaName);
298 foreach ($schema->properties as $propName => $propSchema) {
299 if (!empty($propSchema->multilingual)) {
300 foreach ($allowedLocales as $localeKey) {
301 $rules = $this->addPropValidationRules($rules, $propName .
'.' . $localeKey, $propSchema);
304 $rules = $this->addPropValidationRules($rules, $propName, $propSchema);
317 public function addPropValidationRules($rules, $ruleKey, $propSchema) {
318 if (!empty($propSchema->readOnly)) {
321 switch ($propSchema->type) {
326 $rules[$ruleKey] = [$propSchema->type];
327 if (!empty($propSchema->validation)) {
328 $rules[$ruleKey] = array_merge($rules[$ruleKey], $propSchema->validation);
332 if ($propSchema->items->type ===
'object') {
333 $rules = $this->addPropValidationRules($rules, $ruleKey .
'.*', $propSchema->items);
335 $rules[$ruleKey] = [
'array'];
336 if (!empty($propSchema->validation)) {
337 $rules[$ruleKey] = array_merge($rules[$ruleKey], $propSchema->validation);
339 $rules[$ruleKey .
'.*'] = [$propSchema->items->type];
340 if (!empty($propSchema->items->validation)) {
341 $rules[$ruleKey .
'.*'] = array_merge($rules[$ruleKey .
'.*'], $propSchema->items->validation);
346 foreach ($propSchema->properties as $subPropName => $subPropSchema) {
347 $rules = $this->addPropValidationRules($rules, $ruleKey .
'.' . $subPropName, $subPropSchema);
382 public function formatValidationErrors($errorBag, $schema, $locales) {
383 $errors = $errorBag->getMessages();
386 foreach ($errors as $ruleKey => $messages) {
387 $ruleKeyParts = explode(
'.', $ruleKey);
388 $propName = $ruleKeyParts[0];
389 if (!isset($formatted[$propName])) {
390 $formatted[$propName] = [];
392 if (!empty($schema->properties->{$propName}) && !empty($schema->properties->{$propName}->multilingual)) {
393 $localeKey = $ruleKeyParts[1];
394 if (!isset($formatted[$propName][$localeKey])) {
395 $formatted[$propName][$localeKey] = [];
397 $formatted[$propName][$localeKey] = array_merge($formatted[$propName][$localeKey], $messages);
399 $formatted[$propName] = array_merge($formatted[$propName], $messages);
429 public function setDefaults($schemaName, $object, $supportedLocales, $primaryLocale, $localeParams = array()) {
430 $schema = $this->
get($schemaName);
433 foreach ($supportedLocales as $localeKey) {
435 LOCALE_COMPONENT_PKP_DEFAULT, LOCALE_COMPONENT_APP_DEFAULT,
436 LOCALE_COMPONENT_PKP_COMMON, LOCALE_COMPONENT_APP_COMMON,
441 foreach ($schema->properties as $propName => $propSchema) {
443 if (!is_null($object->getData($propName))) {
446 if (!property_exists($propSchema,
'default') && !property_exists($propSchema,
'defaultLocaleKey')) {
449 if (!empty($propSchema->multilingual)) {
451 foreach ($supportedLocales as $localeKey) {
452 $value[$localeKey] = $this->getDefault($propSchema, $localeParams, $localeKey);
455 $value = $this->getDefault($propSchema, $localeParams, $primaryLocale);
457 $object->setData($propName, $value);
471 public function getLocaleDefaults($schemaName, $locale, $localeParams) {
472 $schema = $this->
get($schemaName);
476 foreach ($schema->properties as $propName => $propSchema) {
477 if (empty($propSchema->multilingual) || empty($propSchema->defaultLocaleKey)) {
480 $defaults[$propName] = $this->getDefault($propSchema, $localeParams, $locale);
494 public function getDefault($propSchema, $localeParams =
null, $localeKey =
null) {
495 switch ($propSchema->type) {
500 if (property_exists($propSchema,
'default')) {
501 return $propSchema->default;
502 } elseif (property_exists($propSchema,
'defaultLocaleKey')) {
503 return __($propSchema->defaultLocaleKey, $localeParams, $localeKey);
508 foreach ($propSchema->default as $default) {
509 $itemSchema = $propSchema->items;
510 $itemSchema->default = $default;
511 $value[] = $this->getDefault($itemSchema, $localeParams, $localeKey);
516 foreach ($propSchema->properties as $subPropName => $subPropSchema) {
517 if (!property_exists($propSchema->default, $subPropName)) {
520 $defaultSubProp = $propSchema->default->{$subPropName};
525 if ($subPropSchema->type ===
'string' && is_object($defaultSubProp) && property_exists($defaultSubProp,
'defaultLocaleKey')) {
526 $value[$subPropName] = __($defaultSubProp->defaultLocaleKey, $localeParams, $localeKey);
528 $value[$subPropName] = $defaultSubProp;
565 public function addMissingMultilingualValues($schemaName, $values, $localeKeys) {
566 $schema = $this->
get($schemaName);
567 $multilingualProps = $this->getMultilingualProps($schemaName);
569 foreach ($values as $key => $value) {
570 if (!in_array($key, $multilingualProps)) {
573 foreach ($localeKeys as $localeKey) {
574 if (is_array($value) && array_key_exists($localeKey, $value)) {
577 switch ($schema->properties->{$key}->type) {
579 $values[$key][$localeKey] =
'';
582 $values[$key][$localeKey] = [];
585 $values[$key][$localeKey] =
null;