public function testGetFieldTypes() { $fieldTypes = array('one' => $this->getFieldTypeMock(), 'two' => $this->getClosure($this->getFieldTypeMock())); $registry = new FieldTypeRegistry($fieldTypes); $fieldTypes = $registry->getFieldTypes(); $this->assertInternalType('array', $fieldTypes); $this->assertCount(2, $fieldTypes); $this->assertArrayHasKey('one', $fieldTypes); $this->assertInstanceOf('eZ\\Publish\\SPI\\FieldType\\FieldType', $fieldTypes['one']); $this->assertArrayHasKey('two', $fieldTypes); $this->assertInstanceOf('eZ\\Publish\\SPI\\FieldType\\FieldType', $fieldTypes['two']); }
/** * Fetches the list of available Field identifiers in the token and returns * an array of their current title value. * * @see \eZ\Publish\Core\Repository\FieldType::getName() * * @param string[] $schemaIdentifiers * @param \eZ\Publish\SPI\Persistence\Content\Type|\eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType * @param array $fieldMap * @param string $languageCode * * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType * * @return string[] Key is the field identifier, value is the title value */ protected function getFieldTitles(array $schemaIdentifiers, $contentType, array $fieldMap, $languageCode) { $fieldTitles = array(); foreach ($schemaIdentifiers as $fieldDefinitionIdentifier) { if (isset($fieldMap[$fieldDefinitionIdentifier][$languageCode])) { if ($contentType instanceof SPIContentType) { $fieldDefinition = null; foreach ($contentType->fieldDefinitions as $spiFieldDefinition) { if ($spiFieldDefinition->identifier === $fieldDefinitionIdentifier) { $fieldDefinition = $spiFieldDefinition; break; } } if ($fieldDefinition === null) { $fieldTitles[$fieldDefinitionIdentifier] = ''; continue; } $fieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldType); } elseif ($contentType instanceof ContentType) { $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); $fieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); } else { throw new InvalidArgumentType('$contentType', 'API or SPI variant of ContentType'); } $fieldTitles[$fieldDefinitionIdentifier] = $fieldType->getName($fieldMap[$fieldDefinitionIdentifier][$languageCode]); } } return $fieldTitles; }
/** * Returns an array of domain fields created from given array of SPI fields. * * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields * @param ContentType|SPIType $contentType * * @return array */ public function buildDomainFields(array $spiFields, $contentType) { $fieldIdentifierMap = array(); if (!$contentType instanceof SPIType && !$contentType instanceof ContentType) { throw new InvalidArgumentType('$contentType', 'SPI ContentType | API ContentType'); } foreach ($contentType->fieldDefinitions as $fieldDefinitions) { $fieldIdentifierMap[$fieldDefinitions->id] = $fieldDefinitions->identifier; } $fields = array(); foreach ($spiFields as $spiField) { $fields[] = new Field(array('id' => $spiField->id, 'value' => $this->fieldTypeRegistry->getFieldType($spiField->type)->fromPersistenceValue($spiField->value), 'languageCode' => $spiField->languageCode, 'fieldDefIdentifier' => $fieldIdentifierMap[$spiField->fieldDefinitionId])); } return $fields; }
/** * Builds SPIFieldDefinition object using API FieldDefinitionUpdateStruct * and API FieldDefinition. * * @throws \eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException if validator configuration or * field setting do not validate * * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition * * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition */ public function buildSPIFieldDefinitionUpdate(APIFieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct, APIFieldDefinition $fieldDefinition) { /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */ $fieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); $validatorConfiguration = $fieldDefinitionUpdateStruct->validatorConfiguration === null ? $fieldDefinition->validatorConfiguration : $fieldDefinitionUpdateStruct->validatorConfiguration; $fieldSettings = $fieldDefinitionUpdateStruct->fieldSettings === null ? $fieldDefinition->fieldSettings : $fieldDefinitionUpdateStruct->fieldSettings; $validationErrors = array(); if ($fieldDefinitionUpdateStruct->isSearchable && !$fieldType->isSearchable()) { $validationErrors[] = new ValidationError("FieldType '{$fieldDefinition->fieldTypeIdentifier}' is not searchable"); } $validationErrors = array_merge($validationErrors, $fieldType->validateValidatorConfiguration($validatorConfiguration), $fieldType->validateFieldSettings($fieldSettings)); if (!empty($validationErrors)) { throw new ContentTypeFieldDefinitionValidationException($validationErrors); } $spiFieldDefinition = new SPIFieldDefinition(array('id' => $fieldDefinition->id, 'fieldType' => $fieldDefinition->fieldTypeIdentifier, 'name' => $fieldDefinitionUpdateStruct->names === null ? $fieldDefinition->getNames() : $fieldDefinitionUpdateStruct->names, 'description' => $fieldDefinitionUpdateStruct->descriptions === null ? $fieldDefinition->getDescriptions() : $fieldDefinitionUpdateStruct->descriptions, 'identifier' => $fieldDefinitionUpdateStruct->identifier === null ? $fieldDefinition->identifier : $fieldDefinitionUpdateStruct->identifier, 'fieldGroup' => $fieldDefinitionUpdateStruct->fieldGroup === null ? $fieldDefinition->fieldGroup : $fieldDefinitionUpdateStruct->fieldGroup, 'position' => $fieldDefinitionUpdateStruct->position === null ? $fieldDefinition->position : $fieldDefinitionUpdateStruct->position, 'isTranslatable' => $fieldDefinitionUpdateStruct->isTranslatable === null ? $fieldDefinition->isTranslatable : $fieldDefinitionUpdateStruct->isTranslatable, 'isRequired' => $fieldDefinitionUpdateStruct->isRequired === null ? $fieldDefinition->isRequired : $fieldDefinitionUpdateStruct->isRequired, 'isInfoCollector' => $fieldDefinitionUpdateStruct->isInfoCollector === null ? $fieldDefinition->isInfoCollector : $fieldDefinitionUpdateStruct->isInfoCollector, 'isSearchable' => $fieldDefinitionUpdateStruct->isSearchable === null ? $fieldDefinition->isSearchable : $fieldDefinitionUpdateStruct->isSearchable)); $spiFieldDefinition->fieldTypeConstraints->validators = $validatorConfiguration; $spiFieldDefinition->fieldTypeConstraints->fieldSettings = $fieldSettings; $spiFieldDefinition->defaultValue = $fieldType->toPersistenceValue($fieldType->acceptValue($fieldDefinitionUpdateStruct->defaultValue)); return $spiFieldDefinition; }
/** * Returns an array of domain fields created from given array of SPI fields. * * @throws InvalidArgumentType On invalid $contentType * * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields * @param ContentType|SPIType $contentType * @param array|null $languages Language codes to filter fields on * @param string|null $alwaysAvailableLanguage Language code fallback if a given field is not found in $languages * * @return array */ public function buildDomainFields(array $spiFields, $contentType, array $languages = null, $alwaysAvailableLanguage = null) { if (!$contentType instanceof SPIType && !$contentType instanceof ContentType) { throw new InvalidArgumentType('$contentType', 'SPI ContentType | API ContentType'); } $fieldIdentifierMap = array(); foreach ($contentType->fieldDefinitions as $fieldDefinitions) { $fieldIdentifierMap[$fieldDefinitions->id] = $fieldDefinitions->identifier; } $fieldInFilterLanguagesMap = array(); if ($languages !== null && $alwaysAvailableLanguage !== null) { foreach ($spiFields as $spiField) { if (in_array($spiField->languageCode, $languages)) { $fieldInFilterLanguagesMap[$spiField->fieldDefinitionId] = true; } } } $fields = array(); foreach ($spiFields as $spiField) { // We ignore fields in content not part of the content type if (!isset($fieldIdentifierMap[$spiField->fieldDefinitionId])) { continue; } if ($languages !== null && !in_array($spiField->languageCode, $languages)) { // If filtering is enabled we ignore fields in other languages then $fieldLanguages, if: if ($alwaysAvailableLanguage === null) { // Ignore field if we don't have $alwaysAvailableLanguageCode fallback continue; } elseif (!empty($fieldInFilterLanguagesMap[$spiField->fieldDefinitionId])) { // Ignore field if it exists in one of the filtered languages continue; } elseif ($spiField->languageCode !== $alwaysAvailableLanguage) { // Also ignore if field is not in $alwaysAvailableLanguageCode continue; } } $fields[] = new Field(array('id' => $spiField->id, 'value' => $this->fieldTypeRegistry->getFieldType($spiField->type)->fromPersistenceValue($spiField->value), 'languageCode' => $spiField->languageCode, 'fieldDefIdentifier' => $fieldIdentifierMap[$spiField->fieldDefinitionId])); } return $fields; }
/** * Adds a new field definition to an existing content type. * * The content type must be in state DRAFT. * * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the identifier in already exists in the content type * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit a content type * @throws \eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException * if a field definition in the $contentTypeCreateStruct is not valid * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If field definition of the same non-repeatable type is being * added to the ContentType that already contains one * or field definition that can't be added to a ContentType that * has Content instances is being added to such ContentType * * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct */ public function addFieldDefinition(APIContentTypeDraft $contentTypeDraft, FieldDefinitionCreateStruct $fieldDefinitionCreateStruct) { if ($this->repository->hasAccess('class', 'update') !== true) { throw new UnauthorizedException('ContentType', 'update'); } $this->validateInputFieldDefinitionCreateStruct($fieldDefinitionCreateStruct); $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id); if ($loadedContentTypeDraft->getFieldDefinition($fieldDefinitionCreateStruct->identifier) !== null) { throw new InvalidArgumentException("\$fieldDefinitionCreateStruct", "Another FieldDefinition with identifier '{$fieldDefinitionCreateStruct->identifier}' exists in the ContentType"); } /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */ $fieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinitionCreateStruct->fieldTypeIdentifier); $fieldType->applyDefaultSettings($fieldDefinitionCreateStruct->fieldSettings); $fieldType->applyDefaultValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration); $validationErrors = $this->validateFieldDefinitionCreateStruct($fieldDefinitionCreateStruct, $fieldType); if (!empty($validationErrors)) { $validationErrors = array($fieldDefinitionCreateStruct->identifier => $validationErrors); throw new ContentTypeFieldDefinitionValidationException($validationErrors); } if ($fieldType->isSingular()) { foreach ($loadedContentTypeDraft->getFieldDefinitions() as $fieldDefinition) { if ($fieldDefinition->fieldTypeIdentifier === $fieldDefinitionCreateStruct->fieldTypeIdentifier) { throw new BadStateException("\$contentTypeDraft", "ContentType already contains field definition of non-repeatable field type '{$fieldDefinition->fieldTypeIdentifier}'"); } } } if ($fieldType->onlyEmptyInstance() && $this->contentTypeHandler->getContentCount($loadedContentTypeDraft->id)) { throw new BadStateException("\$contentTypeDraft", "Field definition of '{$fieldDefinitionCreateStruct->fieldTypeIdentifier}' field type cannot be added because ContentType has Content instances"); } $spiFieldDefinitionCreateStruct = $this->buildSPIFieldDefinitionCreate($fieldDefinitionCreateStruct, $fieldType); $this->repository->beginTransaction(); try { $this->contentTypeHandler->addFieldDefinition($contentTypeDraft->id, $contentTypeDraft->status, $spiFieldDefinitionCreateStruct); $this->repository->commit(); } catch (Exception $e) { $this->repository->rollback(); throw $e; } }
/** * Updates the fields of a draft. * * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentUpdateStruct is not valid * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set to an empty value * * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct * * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the updated fields */ public function updateContent(APIVersionInfo $versionInfo, APIContentUpdateStruct $contentUpdateStruct) { $contentUpdateStruct = clone $contentUpdateStruct; /** @var $content \eZ\Publish\Core\Repository\Values\Content\Content */ $content = $this->loadContent($versionInfo->getContentInfo()->id, null, $versionInfo->versionNo); if ($content->versionInfo->status !== APIVersionInfo::STATUS_DRAFT) { throw new BadStateException('$versionInfo', 'Version is not a draft and can not be updated'); } if (!$this->repository->canUser('content', 'edit', $content)) { throw new UnauthorizedException('content', 'edit', array('contentId' => $content->id)); } $mainLanguageCode = $content->contentInfo->mainLanguageCode; $languageCodes = $this->getLanguageCodesForUpdate($contentUpdateStruct, $content); $contentType = $this->repository->getContentTypeService()->loadContentType($content->contentInfo->contentTypeId); $fields = $this->mapFieldsForUpdate($contentUpdateStruct, $contentType, $mainLanguageCode); $fieldValues = array(); $spiFields = array(); $allFieldErrors = array(); $inputRelations = array(); $locationIdToContentIdMapping = array(); foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */ $fieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); foreach ($languageCodes as $languageCode) { $isCopied = $isEmpty = $isRetained = false; $isLanguageNew = !in_array($languageCode, $content->versionInfo->languageCodes); $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $mainLanguageCode; $isFieldUpdated = isset($fields[$fieldDefinition->identifier][$valueLanguageCode]); $isProcessed = isset($fieldValues[$fieldDefinition->identifier][$valueLanguageCode]); if (!$isFieldUpdated && !$isLanguageNew) { $isRetained = true; $fieldValue = $content->getField($fieldDefinition->identifier, $valueLanguageCode)->value; } elseif (!$isFieldUpdated && $isLanguageNew && !$fieldDefinition->isTranslatable) { $isCopied = true; $fieldValue = $content->getField($fieldDefinition->identifier, $valueLanguageCode)->value; } elseif ($isFieldUpdated) { $fieldValue = $fields[$fieldDefinition->identifier][$valueLanguageCode]->value; } else { $fieldValue = $fieldDefinition->defaultValue; } $fieldValue = $fieldType->acceptValue($fieldValue); if ($fieldType->isEmptyValue($fieldValue)) { $isEmpty = true; if ($fieldDefinition->isRequired) { throw new ContentValidationException("Value for required field definition '%identifier%' with language '%languageCode%' is empty", ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode]); } } else { $fieldErrors = $fieldType->validate($fieldDefinition, $fieldValue); if (!empty($fieldErrors)) { $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors; } } if (!empty($allFieldErrors)) { continue; } $this->relationProcessor->appendFieldRelations($inputRelations, $locationIdToContentIdMapping, $fieldType, $fieldValue, $fieldDefinition->id); $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue; if ($isRetained || $isCopied || $isLanguageNew && $isEmpty || $isProcessed) { continue; } $spiFields[] = new SPIField(array('id' => $isLanguageNew ? null : $content->getField($fieldDefinition->identifier, $languageCode)->id, 'fieldDefinitionId' => $fieldDefinition->id, 'type' => $fieldDefinition->fieldTypeIdentifier, 'value' => $fieldType->toPersistenceValue($fieldValue), 'languageCode' => $languageCode, 'versionNo' => $versionInfo->versionNo)); } } if (!empty($allFieldErrors)) { throw new ContentFieldValidationException($allFieldErrors); } $spiContentUpdateStruct = new SPIContentUpdateStruct(array('name' => $this->nameSchemaService->resolveNameSchema($content, $fieldValues, $languageCodes, $contentType), 'creatorId' => $contentUpdateStruct->creatorId ?: $this->repository->getCurrentUserReference()->getUserId(), 'fields' => $spiFields, 'modificationDate' => time(), 'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode($contentUpdateStruct->initialLanguageCode)->id)); $existingRelations = $this->loadRelations($versionInfo); $this->repository->beginTransaction(); try { $spiContent = $this->persistenceHandler->contentHandler()->updateContent($versionInfo->getContentInfo()->id, $versionInfo->versionNo, $spiContentUpdateStruct); $this->relationProcessor->processFieldRelations($inputRelations, $spiContent->versionInfo->contentInfo->id, $spiContent->versionInfo->versionNo, $contentType, $existingRelations); $this->repository->commit(); } catch (Exception $e) { $this->repository->rollback(); throw $e; } return $this->domainMapper->buildContentDomainObject($spiContent, $contentType); }
/** * Returns if there is a FieldType registered under $identifier * * @param string $identifier * * @return boolean */ public function hasFieldType($identifier) { return $this->fieldTypeRegistry->hasFieldType($identifier); }