class ContentEntitySource in Translation Management Tool 8
Content entity source plugin controller.
Plugin annotation
@SourcePlugin(
id = "content",
label = @Translation("Content Entity"),
description = @Translation("Source handler for entities."),
ui = "Drupal\tmgmt_content\ContentEntitySourcePluginUi"
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\tmgmt\SourcePluginBase implements SourcePluginInterface
- class \Drupal\tmgmt_content\Plugin\tmgmt\Source\ContentEntitySource implements ContinuousSourceInterface, SourcePreviewInterface
- class \Drupal\tmgmt\SourcePluginBase implements SourcePluginInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of ContentEntitySource
3 files declare their use of ContentEntitySource
- ContentEntitySourcePluginUi.php in sources/
content/ src/ ContentEntitySourcePluginUi.php - ContentTranslationPreviewController.php in sources/
content/ src/ Controller/ ContentTranslationPreviewController.php - tmgmt_content.module in sources/
content/ tmgmt_content.module - Source plugin for the Translation Management system that handles entities.
File
- sources/
content/ src/ Plugin/ tmgmt/ Source/ ContentEntitySource.php, line 38
Namespace
Drupal\tmgmt_content\Plugin\tmgmt\SourceView source
class ContentEntitySource extends SourcePluginBase implements SourcePreviewInterface, ContinuousSourceInterface {
/**
* Returns the entity for the given job item.
*
* @param \Drupal\tmgmt\JobItemInterface $job_item
* The job entity
*
* @return \Drupal\Core\Entity\ContentEntityInterface
* The entity.
*/
protected function getEntity(JobItemInterface $job_item) {
return \Drupal::entityTypeManager()
->getStorage($job_item
->getItemType())
->load($job_item
->getItemId());
}
/**
* Loads a list of entities for the given entity type ID.
*
* By providing the language code, the latest revisions affecting the
* specified translation (language code) will be returned.
*
* @param string $entity_type_id
* The entity type ID.
* @param array $entity_ids
* A list of entity IDs to load.
* @param string|null $langcode
* (optional) The language code. Defaults to source entity language.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* Returns a list of entities.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public static function loadMultiple($entity_type_id, array $entity_ids, $langcode = NULL) {
/** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
$storage = \Drupal::entityTypeManager()
->getStorage($entity_type_id);
$entities = $storage
->loadMultiple($entity_ids);
// Load the latest revision if the entity type is revisionable.
if ($storage
->getEntityType()
->isRevisionable() && $storage instanceof TranslatableRevisionableStorageInterface) {
foreach ($entities as $entity_id => $entity) {
// Use the specified langcode or fallback to the default language.
$translation_langcode = $langcode ?: $entity
->language()
->getId();
$revision_id = $storage
->getLatestTranslationAffectedRevisionId($entity
->id(), $translation_langcode);
// Get the pending revisions. If the returned revision ID is the same as
// the default one, there is no need for further checks.
if ($revision_id && $entity
->getRevisionId() != $revision_id) {
$revision = $storage
->loadRevision($revision_id);
// If the affected revision was the default one at some point, then it
// is an old revision that should be part of the already loaded
// default so we do not need to replace it here.
if (!$revision
->wasDefaultRevision()) {
$entities[$entity_id] = $revision;
}
}
}
}
return $entities;
}
/**
* Loads a single entity for the given entity type ID.
*
* By providing the language code, the latest revisions affecting the
* specified translation (language code) will be returned.
*
* @param string $entity_type_id
* The entity type ID.
* @param string $id
* The entity ID.
* @param string|null $langcode
* (optional) The language code. Defaults to source entity language.
*
* @return \Drupal\Core\Entity\ContentEntityInterface|null
* The loaded entity or null if not found.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public static function load($entity_type_id, $id, $langcode = NULL) {
$entities = static::loadMultiple($entity_type_id, [
$id,
], $langcode);
return isset($entities[$id]) ? $entities[$id] : NULL;
}
/**
* {@inheritdoc}
*/
public function getLabel(JobItemInterface $job_item) {
// Use the source language to a get label for the job item.
$langcode = $job_item
->getJob() ? $job_item
->getJob()
->getSourceLangcode() : NULL;
if ($entity = static::load($job_item
->getItemType(), $job_item
->getItemId(), $langcode)) {
return $entity
->label() ?: $entity
->id();
}
}
/**
* {@inheritdoc}
*/
public function getUrl(JobItemInterface $job_item) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$langcode = $job_item
->getJob() ? $job_item
->getJob()
->getSourceLangcode() : NULL;
if ($entity = static::load($job_item
->getItemType(), $job_item
->getItemId(), $langcode)) {
if ($entity
->hasLinkTemplate('canonical')) {
$anonymous = new AnonymousUserSession();
$url = $entity
->toUrl();
$anonymous_access = \Drupal::config('tmgmt.settings')
->get('anonymous_access');
if ($url && $anonymous_access && !$entity
->access('view', $anonymous)) {
$url
->setOption('query', [
'key' => \Drupal::service('tmgmt_content.key_access')
->getKey($job_item),
]);
}
return $url;
}
}
return NULL;
}
/**
* Implements TMGMTEntitySourcePluginController::getData().
*
* Returns the data from the fields as a structure that can be processed by
* the Translation Management system.
*/
public function getData(JobItemInterface $job_item) {
$langcode = $job_item
->getJob() ? $job_item
->getJob()
->getSourceLangcode() : NULL;
$entity = static::load($job_item
->getItemType(), $job_item
->getItemId(), $langcode);
if (!$entity) {
throw new TMGMTException(t('Unable to load entity %type with id %id', array(
'%type' => $job_item
->getItemType(),
'%id' => $job_item
->getItemId(),
)));
}
$languages = \Drupal::languageManager()
->getLanguages();
$id = $entity
->language()
->getId();
if (!isset($languages[$id])) {
throw new TMGMTException(t('Entity %entity could not be translated because the language %language is not applicable', array(
'%entity' => $entity
->language()
->getId(),
'%language' => $entity
->language()
->getName(),
)));
}
if (!$entity
->hasTranslation($job_item
->getJob()
->getSourceLangcode())) {
throw new TMGMTException(t('The %type entity %id with translation %lang does not exist.', array(
'%type' => $entity
->getEntityTypeId(),
'%id' => $entity
->id(),
'%lang' => $job_item
->getJob()
->getSourceLangcode(),
)));
}
$translation = $entity
->getTranslation($job_item
->getJob()
->getSourceLangcode());
$data = $this
->extractTranslatableData($translation);
$entity_form_display = \Drupal::service('entity_display.repository')
->getFormDisplay($job_item
->getItemType(), $entity
->bundle(), 'default');
uksort($data, function ($a, $b) use ($entity_form_display) {
$a_weight = NULL;
$b_weight = NULL;
// Get the weights.
if ($entity_form_display
->getComponent($a) && !is_null($entity_form_display
->getComponent($a)['weight'])) {
$a_weight = (int) $entity_form_display
->getComponent($a)['weight'];
}
if ($entity_form_display
->getComponent($b) && !is_null($entity_form_display
->getComponent($b)['weight'])) {
$b_weight = (int) $entity_form_display
->getComponent($b)['weight'];
}
// If neither field has a weight, sort alphabetically.
if ($a_weight === NULL && $b_weight === NULL) {
return $a > $b ? 1 : -1;
}
elseif ($a_weight === NULL) {
return 1;
}
elseif ($b_weight === NULL) {
return -1;
}
elseif ($a_weight == $b_weight) {
return 0;
}
else {
return $a_weight > $b_weight ? 1 : -1;
}
});
return $data;
}
/**
* Extracts translatable data from an entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity to get the translatable data from.
*
* @return array $data
* Translatable data.
*/
public function extractTranslatableData(ContentEntityInterface $entity) {
$field_definitions = $entity
->getFieldDefinitions();
$exclude_field_types = [
'language',
];
$exclude_field_names = [
'moderation_state',
];
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
$is_bundle_translatable = $content_translation_manager
->isEnabled($entity
->getEntityTypeId(), $entity
->bundle());
// Exclude field types from translation.
$translatable_fields = array_filter($field_definitions, function (FieldDefinitionInterface $field_definition) use ($exclude_field_types, $exclude_field_names, $is_bundle_translatable) {
if ($is_bundle_translatable) {
// Field is not translatable.
if (!$field_definition
->isTranslatable()) {
return FALSE;
}
}
elseif (!$field_definition
->getFieldStorageDefinition()
->isTranslatable()) {
return FALSE;
}
// Field type matches field types to exclude.
if (in_array($field_definition
->getType(), $exclude_field_types)) {
return FALSE;
}
// Field name matches field names to exclude.
if (in_array($field_definition
->getName(), $exclude_field_names)) {
return FALSE;
}
// User marked the field to be excluded.
if ($field_definition instanceof ThirdPartySettingsInterface) {
$is_excluded = $field_definition
->getThirdPartySetting('tmgmt_content', 'excluded', FALSE);
if ($is_excluded) {
return FALSE;
}
}
return TRUE;
});
\Drupal::moduleHandler()
->alter('tmgmt_translatable_fields', $entity, $translatable_fields);
$data = array();
foreach ($translatable_fields as $field_name => $field_definition) {
$field = $entity
->get($field_name);
$data[$field_name] = $this
->getFieldProcessor($field_definition
->getType())
->extractTranslatableData($field);
}
$embeddable_fields = static::getEmbeddableFields($entity);
foreach ($embeddable_fields as $field_name => $field_definition) {
$field = $entity
->get($field_name);
/* @var \Drupal\Core\Field\FieldItemInterface $field_item */
foreach ($field as $delta => $field_item) {
foreach ($field_item
->getProperties(TRUE) as $property_key => $property) {
// If the property is a content entity reference and it's value is
// defined, than we call this method again to get all the data.
if ($property instanceof EntityReference && $property
->getValue() instanceof ContentEntityInterface) {
// All the labels are here, to make sure we don't have empty
// labels in the UI because of no data.
$data[$field_name]['#label'] = $field_definition
->getLabel();
if (count($field) > 1) {
// More than one item, add a label for the delta.
$data[$field_name][$delta]['#label'] = t('Delta #@delta', array(
'@delta' => $delta,
));
}
// Get the referenced entity.
$referenced_entity = $property
->getValue();
// Get the source language code.
$langcode = $entity
->language()
->getId();
// If the referenced entity is translatable and has a translation
// use it instead of the default entity translation.
if ($content_translation_manager
->isEnabled($referenced_entity
->getEntityTypeId(), $referenced_entity
->bundle()) && $referenced_entity
->hasTranslation($langcode)) {
$referenced_entity = $referenced_entity
->getTranslation($langcode);
}
$data[$field_name][$delta][$property_key] = $this
->extractTranslatableData($referenced_entity);
// Use the ID of the entity to identify it later, do not rely on the
// UUID as content entities are not required to have one.
$data[$field_name][$delta][$property_key]['#id'] = $property
->getValue()
->id();
}
}
}
}
return $data;
}
/**
* Determines whether an entity is moderated.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
*
* @return bool
* TRUE if the entity is moderated. Otherwise, FALSE.
*/
public static function isModeratedEntity(EntityInterface $entity) {
if (!\Drupal::moduleHandler()
->moduleExists('content_moderation')) {
return FALSE;
}
return \Drupal::service('content_moderation.moderation_information')
->isModeratedEntity($entity);
}
/**
* Returns fields that should be embedded into the data for the given entity.
*
* Includes explicitly enabled fields and composite entities that are
* implicitly included to the translatable data.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity to get the translatable data from.
*
* @return \Drupal\Core\Field\FieldDefinitionInterface[] $embeddable_fields
* A list of field definitions that can be embedded.
*/
public static function getEmbeddableFields(ContentEntityInterface $entity) {
// Get the configurable embeddable references.
$field_definitions = $entity
->getFieldDefinitions();
$embeddable_field_names = \Drupal::config('tmgmt_content.settings')
->get('embedded_fields');
$embeddable_fields = array_filter($field_definitions, function (FieldDefinitionInterface $field_definition) use ($embeddable_field_names) {
return isset($embeddable_field_names[$field_definition
->getTargetEntityTypeId()][$field_definition
->getName()]);
});
// Get always embedded references.
$content_translation_manager = \Drupal::service('content_translation.manager');
foreach ($field_definitions as $field_name => $field_definition) {
$storage_definition = $field_definition
->getFieldStorageDefinition();
$property_definitions = $storage_definition
->getPropertyDefinitions();
foreach ($property_definitions as $property_definition) {
// Look for entity_reference properties where the storage definition
// has a target type setting.
if (in_array($property_definition
->getDataType(), [
'entity_reference',
'entity_revision_reference',
]) && ($target_type_id = $storage_definition
->getSetting('target_type'))) {
$is_target_type_enabled = $content_translation_manager
->isEnabled($target_type_id);
$target_entity_type = \Drupal::entityTypeManager()
->getDefinition($target_type_id);
// Include current entity reference field that is considered a
// composite and translatable or if the parent entity is considered a
// composite as well. This allows to embed nested untranslatable
// fields (For example: Paragraphs).
if ($target_entity_type
->get('entity_revision_parent_type_field') && ($is_target_type_enabled || $entity
->getEntityType()
->get('entity_revision_parent_type_field'))) {
$embeddable_fields[$field_name] = $field_definition;
}
}
}
}
return $embeddable_fields;
}
/**
* {@inheritdoc}
*/
public function saveTranslation(JobItemInterface $job_item, $target_langcode) {
/* @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $this
->getEntity($job_item);
if (!$entity) {
$job_item
->addMessage('The entity %id of type %type does not exist, the job can not be completed.', array(
'%id' => $job_item
->getItemId(),
'%type' => $job_item
->getItemType(),
), 'error');
return FALSE;
}
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
if ($entity_revision = $this
->getPendingRevisionWithCompositeReferenceField($job_item)) {
$title = $entity_revision
->hasLinkTemplate('latest-version') ? $entity_revision
->toLink(NULL, 'latest-version')
->toString() : $entity_revision
->label();
$job_item
->addMessage('This translation cannot be accepted as there is a pending revision in the default translation. You must publish %title first before saving this translation.', [
'%title' => $title,
], 'error');
return FALSE;
}
$data = $job_item
->getData();
$this
->doSaveTranslations($entity, $data, $target_langcode, $job_item);
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getItemTypes() {
$entity_types = \Drupal::entityTypeManager()
->getDefinitions();
$types = array();
$content_translation_manager = \Drupal::service('content_translation.manager');
foreach ($entity_types as $entity_type_name => $entity_type) {
// Entity types with this key set are considered composite entities and
// always embedded in others. Do not expose them as their own item type.
if ($entity_type
->get('entity_revision_parent_type_field')) {
continue;
}
if ($content_translation_manager
->isEnabled($entity_type
->id())) {
$types[$entity_type_name] = $entity_type
->getLabel();
}
}
return $types;
}
/**
* {@inheritdoc}
*/
public function getItemTypeLabel($type) {
return \Drupal::entityTypeManager()
->getDefinition($type)
->getLabel();
}
/**
* {@inheritdoc}
*/
public function getType(JobItemInterface $job_item) {
if ($entity = $this
->getEntity($job_item)) {
$bundles = \Drupal::service('entity_type.bundle.info')
->getBundleInfo($job_item
->getItemType());
$entity_type = $entity
->getEntityType();
$bundle = $entity
->bundle();
// Display entity type and label if we have one and the bundle isn't
// the same as the entity type.
if (isset($bundles[$bundle]) && $bundle != $job_item
->getItemType()) {
return t('@type (@bundle)', array(
'@type' => $entity_type
->getLabel(),
'@bundle' => $bundles[$bundle]['label'],
));
}
// Otherwise just display the entity type label.
return $entity_type
->getLabel();
}
}
/**
* {@inheritdoc}
*/
public function getSourceLangCode(JobItemInterface $job_item) {
$entity = $this
->getEntity($job_item);
return $entity
->getUntranslated()
->language()
->getId();
}
/**
* {@inheritdoc}
*/
public function getExistingLangCodes(JobItemInterface $job_item) {
if ($entity = $this
->getEntity($job_item)) {
return array_keys($entity
->getTranslationLanguages());
}
return array();
}
/**
* Saves translation data in an entity translation.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity for which the translation should be saved.
* @param array $data
* The translation data for the fields.
* @param string $target_langcode
* The target language.
* @param \Drupal\tmgmt\JobItemInterface $item
* The job item.
* @param bool $save
* (optional) Whether to save the translation or not.
*
* @throws \Exception
* Thrown when a field or field offset is missing.
*/
protected function doSaveTranslations(ContentEntityInterface $entity, array $data, $target_langcode, JobItemInterface $item, $save = TRUE) {
// If the translation for this language does not exist yet, initialize it.
if (!$entity
->hasTranslation($target_langcode)) {
$entity
->addTranslation($target_langcode, $entity
->toArray());
}
$translation = $entity
->getTranslation($target_langcode);
$manager = \Drupal::service('content_translation.manager');
if ($manager
->isEnabled($translation
->getEntityTypeId(), $translation
->bundle())) {
$manager
->getTranslationMetadata($translation)
->setSource($entity
->language()
->getId());
}
foreach (Element::children($data) as $field_name) {
$field_data = $data[$field_name];
if (!$translation
->hasField($field_name)) {
throw new \Exception("Field '{$field_name}' does not exist on entity " . $translation
->getEntityTypeId() . '/' . $translation
->id());
}
$field = $translation
->get($field_name);
$field_processor = $this
->getFieldProcessor($field
->getFieldDefinition()
->getType());
$field_processor
->setTranslations($field_data, $field);
}
$embeddable_fields = static::getEmbeddableFields($entity);
foreach ($embeddable_fields as $field_name => $field_definition) {
if (!isset($data[$field_name])) {
continue;
}
$field = $translation
->get($field_name);
$target_type = $field
->getFieldDefinition()
->getFieldStorageDefinition()
->getSetting('target_type');
$is_target_type_translatable = $manager
->isEnabled($target_type);
// In case the target type is not translatable, the referenced entity will
// be duplicated. As a consequence, remove all the field items from the
// translation, update the field value to use the field object from the
// source language.
if (!$is_target_type_translatable) {
$field = clone $entity
->get($field_name);
if (!$translation
->get($field_name)
->isEmpty()) {
$translation
->set($field_name, NULL);
}
}
foreach (Element::children($data[$field_name]) as $delta) {
$field_item = $data[$field_name][$delta];
foreach (Element::children($field_item) as $property) {
// Find the referenced entity. In case we are dealing with
// untranslatable target types, the source entity will be returned.
if ($target_entity = $this
->findReferencedEntity($field, $field_item, $delta, $property, $is_target_type_translatable)) {
if ($is_target_type_translatable) {
// If the field is an embeddable reference and the property is a
// content entity, process it recursively.
// If the field is ERR and the target entity supports
// the needs saving interface, do not save it immediately to avoid
// creating two versions when content moderation is used but just
// ensure it will be saved.
$target_save = TRUE;
if ($field
->getFieldDefinition()
->getType() == 'entity_reference_revisions' && $target_entity instanceof EntityNeedsSaveInterface) {
$target_save = FALSE;
$target_entity
->needsSave();
}
$this
->doSaveTranslations($target_entity, $field_item[$property], $target_langcode, $item, $target_save);
}
else {
$duplicate = $this
->createTranslationDuplicate($target_entity, $target_langcode);
// Do not save the duplicate as it's going to be saved with the
// main entity.
$this
->doSaveTranslations($duplicate, $field_item[$property], $target_langcode, $item, FALSE);
$translation
->get($field_name)
->set($delta, $duplicate);
}
}
}
}
}
if (static::isModeratedEntity($translation)) {
// Use the given moderation status if set. Otherwise, fallback to the
// configured one in TMGMT settings.
if (isset($data['#moderation_state'][0])) {
$moderation_state = $data['#moderation_state'][0];
}
else {
$moderation_info = \Drupal::service('content_moderation.moderation_information');
$workflow = $moderation_info
->getWorkflowForEntity($entity);
$moderation_state = \Drupal::config('tmgmt_content.settings')
->get('default_moderation_states.' . $workflow
->id());
}
if ($moderation_state) {
$translation
->set('moderation_state', $moderation_state);
}
}
elseif (isset($data['#published'][0]) && $translation instanceof EntityPublishedInterface) {
if ($data['#published'][0]) {
$translation
->setPublished();
}
else {
$translation
->setUnpublished();
}
}
if ($entity
->getEntityType()
->isRevisionable()) {
/** @var \Drupal\Core\Entity\TranslatableRevisionableStorageInterface $storage */
$storage = \Drupal::entityTypeManager()
->getStorage($entity
->getEntityTypeId());
if ($storage instanceof TranslatableRevisionableStorageInterface) {
// Always create a new revision of the translation.
$translation = $storage
->createRevision($translation, $translation
->isDefaultRevision());
if ($entity instanceof RevisionLogInterface) {
$translation
->setRevisionLogMessage($this
->t('Created by translation job <a href=":url">@label</a>.', [
':url' => $item
->getJob()
->toUrl()
->toString(),
'@label' => $item
->label(),
]));
}
}
}
if ($save) {
$translation
->save();
}
}
/**
* Creates a translation duplicate of the given entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $target_entity
* The target entity to clone.
* @param string $langcode
* Language code for all the clone entities created.
*
* @return \Drupal\Core\Entity\ContentEntityInterface
* New entity object with the data from the original entity. Not
* saved. No sub-entities are cloned.
*/
protected function createTranslationDuplicate(ContentEntityInterface $target_entity, $langcode) {
$duplicate = $target_entity
->createDuplicate();
// Change the original language.
if ($duplicate
->getEntityType()
->hasKey('langcode')) {
$duplicate
->set($duplicate
->getEntityType()
->getKey('langcode'), $langcode);
}
return $duplicate;
}
/**
* {@inheritdoc}
*/
public function getPreviewUrl(JobItemInterface $job_item) {
if ($job_item
->getJob()
->isActive() && !($job_item
->isAborted() || $job_item
->isAccepted())) {
return new Url('tmgmt_content.job_item_preview', [
'tmgmt_job_item' => $job_item
->id(),
], [
'query' => [
'key' => \Drupal::service('tmgmt_content.key_access')
->getKey($job_item),
],
]);
}
else {
return NULL;
}
}
/**
* {@inheritdoc}
*/
public function continuousSettingsForm(array &$form, FormStateInterface $form_state, Job $job) {
$continuous_settings = $job
->getContinuousSettings();
$element = array();
$item_types = $this
->getItemTypes();
asort($item_types);
$entity_type_manager = \Drupal::entityTypeManager();
foreach ($item_types as $item_type => $item_type_label) {
$entity_type = $entity_type_manager
->getDefinition($item_type);
$element[$entity_type
->id()]['enabled'] = array(
'#type' => 'checkbox',
'#title' => $item_type_label,
'#default_value' => isset($continuous_settings[$this
->getPluginId()][$entity_type
->id()]) ? $continuous_settings[$this
->getPluginId()][$entity_type
->id()]['enabled'] : FALSE,
);
if ($entity_type
->hasKey('bundle')) {
$bundles = \Drupal::service('entity_type.bundle.info')
->getBundleInfo($item_type);
$element[$entity_type
->id()]['bundles'] = array(
'#title' => $this
->getBundleLabel($entity_type),
'#type' => 'details',
'#open' => TRUE,
'#states' => array(
'invisible' => array(
'input[name="continuous_settings[' . $this
->getPluginId() . '][' . $entity_type
->id() . '][enabled]"]' => array(
'checked' => FALSE,
),
),
),
);
foreach ($bundles as $bundle => $bundle_label) {
if (\Drupal::service('content_translation.manager')
->isEnabled($entity_type
->id(), $bundle)) {
$element[$entity_type
->id()]['bundles'][$bundle] = array(
'#type' => 'checkbox',
'#title' => $bundle_label['label'],
'#default_value' => isset($continuous_settings[$this
->getPluginId()][$entity_type
->id()]['bundles'][$bundle]) ? $continuous_settings[$this
->getPluginId()][$entity_type
->id()]['bundles'][$bundle] : FALSE,
);
}
}
}
}
return $element;
}
/**
* {@inheritdoc}
*/
public function shouldCreateContinuousItem(Job $job, $plugin, $item_type, $item_id) {
$continuous_settings = $job
->getContinuousSettings();
$entity = static::load($item_type, $item_id, $job
->getSourceLangcode());
$translation_manager = \Drupal::service('content_translation.manager');
$translation = $entity
->hasTranslation($job
->getTargetLangcode()) ? $entity
->getTranslation($job
->getTargetLangcode()) : NULL;
$metadata = isset($translation) ? $translation_manager
->getTranslationMetadata($translation) : NULL;
// If a translation exists and is not marked as outdated, no new job items
// needs to be created.
if (isset($translation) && !$metadata
->isOutdated()) {
return FALSE;
}
else {
if ($entity && $entity
->getEntityType()
->hasKey('bundle')) {
// The entity type has bundles, check both the entity type setting and
// the bundle.
if (!empty($continuous_settings[$plugin][$item_type]['bundles'][$entity
->bundle()]) && !empty($continuous_settings[$plugin][$item_type]['enabled'])) {
return TRUE;
}
}
elseif (!empty($continuous_settings[$plugin][$item_type]['enabled'])) {
return TRUE;
}
}
return FALSE;
}
/**
* Returns the bundle label for a given entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return string
* The bundle label.
*/
protected function getBundleLabel(EntityTypeInterface $entity_type) {
if ($entity_type
->getBundleLabel()) {
return $entity_type
->getBundleLabel();
}
if ($entity_type
->getBundleEntityType()) {
return \Drupal::entityTypeManager()
->getDefinition($entity_type
->getBundleEntityType())
->getLabel();
}
return $this
->t('@label type', [
'@label' => $entity_type
->getLabel(),
]);
}
/**
* Returns the field processor for a given field type.
*
* @param string $field_type
* The field type.
*
* @return \Drupal\tmgmt_content\FieldProcessorInterface $field_processor
* The field processor for this field type.
*/
protected function getFieldProcessor($field_type) {
$definition = \Drupal::service('plugin.manager.field.field_type')
->getDefinition($field_type);
return \Drupal::service('class_resolver')
->getInstanceFromDefinition($definition['tmgmt_field_processor']);
}
/**
* @param \Drupal\Core\Field\FieldItemListInterface $field
* @param array $field_item
* @param $delta
* @param $property
* @param bool $is_target_type_translatable
* (optional) Whether the target entity type is translatable.
*
* @return \Drupal\Core\Entity\ContentEntityInterface|null
*/
protected function findReferencedEntity(FieldItemListInterface $field, array $field_item, $delta, $property, $is_target_type_translatable = TRUE) {
// If an id is provided, loop over the field item deltas until we find the
// matching entity. In case of untranslatable target types return the
// source target entity as it will be duplicated.
if (isset($field_item[$property]['#id'])) {
foreach ($field as $item_delta => $item) {
if ($item->{$property} instanceof ContentEntityInterface) {
/** @var ContentEntityInterface $referenced_entity */
$referenced_entity = $item->{$property};
if ($referenced_entity
->id() == $field_item[$property]['#id'] || $item_delta === $delta && !$is_target_type_translatable) {
return $referenced_entity;
}
}
}
// @todo Support loading an entity, throw an exception or log a warning?
}
elseif ($field
->offsetExists($delta) && $field
->offsetGet($delta)->{$property} instanceof ContentEntityInterface) {
return $field
->offsetGet($delta)->{$property};
}
}
/**
* Returns the source revision if it is a pending revision with an ERR field.
*
* @param \Drupal\tmgmt\JobItemInterface $job_item
*
* @return \Drupal\Core\Entity\ContentEntityInterface|null
* The source revision entity if it is a pending revision with an ERR field.
*/
public function getPendingRevisionWithCompositeReferenceField(JobItemInterface $job_item) {
// Get the latest revision of the default translation.
/** \Drupal\Core\Entity\ContentEntityInterface|null $entity */
$entity = static::load($job_item
->getItemType(), $job_item
->getItemId());
if (!$entity) {
return NULL;
}
// If the given revision is not the default revision, check if there is at
// least one untranslatable composite entity reference revisions field and
// fail the validation.
if (!$entity
->isDefaultRevision()) {
foreach ($entity
->getFieldDefinitions() as $definition) {
if (in_array($definition
->getType(), [
'entity_reference',
'entity_reference_revisions',
]) && !$definition
->isTranslatable()) {
$target_type_id = $definition
->getSetting('target_type');
$entity_type_manager = \Drupal::entityTypeManager();
if (!$entity_type_manager
->hasDefinition($target_type_id)) {
continue;
}
// Check if the target entity type is considered a composite.
if ($entity_type_manager
->getDefinition($target_type_id)
->get('entity_revision_parent_type_field')) {
return $entity;
}
}
}
}
return NULL;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
ContentEntitySource:: |
public | function |
Creates "Continuous settings" form element. Overrides ContinuousSourceInterface:: |
|
ContentEntitySource:: |
protected | function | Creates a translation duplicate of the given entity. | |
ContentEntitySource:: |
protected | function | Saves translation data in an entity translation. | |
ContentEntitySource:: |
public | function | Extracts translatable data from an entity. | |
ContentEntitySource:: |
protected | function | ||
ContentEntitySource:: |
protected | function | Returns the bundle label for a given entity type. | |
ContentEntitySource:: |
public | function |
Implements TMGMTEntitySourcePluginController::getData(). Overrides SourcePluginInterface:: |
|
ContentEntitySource:: |
public static | function | Returns fields that should be embedded into the data for the given entity. | |
ContentEntitySource:: |
protected | function | Returns the entity for the given job item. | |
ContentEntitySource:: |
public | function |
Gets existing translation language codes of the job item source. Overrides SourcePluginBase:: |
|
ContentEntitySource:: |
protected | function | Returns the field processor for a given field type. | |
ContentEntitySource:: |
public | function |
Returns the label of a source item type. Overrides SourcePluginBase:: |
|
ContentEntitySource:: |
public | function |
Returns an array of translatable source item types. Overrides SourcePluginBase:: |
|
ContentEntitySource:: |
public | function |
Return a title for this job item. Overrides SourcePluginBase:: |
|
ContentEntitySource:: |
public | function | Returns the source revision if it is a pending revision with an ERR field. | |
ContentEntitySource:: |
public | function |
Returns preview url if preview is supported. Overrides SourcePreviewInterface:: |
|
ContentEntitySource:: |
public | function |
Gets language code of the job item source. Overrides SourcePluginInterface:: |
|
ContentEntitySource:: |
public | function |
Returns the type of a job item. Overrides SourcePluginBase:: |
|
ContentEntitySource:: |
public | function |
Returns the Uri for this job item. Overrides SourcePluginBase:: |
|
ContentEntitySource:: |
public static | function | Determines whether an entity is moderated. | |
ContentEntitySource:: |
public static | function | Loads a single entity for the given entity type ID. | |
ContentEntitySource:: |
public static | function | Loads a list of entities for the given entity type ID. | |
ContentEntitySource:: |
public | function |
Saves a translation. Overrides SourcePluginInterface:: |
|
ContentEntitySource:: |
public | function |
Checks whether the continuous job item should be created. Overrides ContinuousSourceInterface:: |
|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginBase:: |
public | function | Constructs a \Drupal\Component\Plugin\PluginBase object. | 92 |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |