<?php
namespace Drupal\tmgmt_content;
use Drupal\content_translation\ContentTranslationManager;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\tmgmt\JobItemInterface;
use Drupal\tmgmt\SourcePluginUiBase;
use Drupal\tmgmt_content\Plugin\tmgmt\Source\ContentEntitySource;
class ContentEntitySourcePluginUi extends SourcePluginUiBase {
public $pagerLimit = 25;
public function overviewSearchFormPart(array $form, FormStateInterface $form_state, $type) {
$form = parent::overviewSearchFormPart($form, $form_state, $type);
$entity_type = \Drupal::entityTypeManager()
->getDefinition($type);
$field_definitions = \Drupal::service('entity_field.manager')
->getBaseFieldDefinitions($type);
$label_key = $entity_type
->getKey('label');
if (!empty($label_key)) {
$label = (string) $field_definitions[$label_key]
->getlabel();
$form['search_wrapper']['search'][$label_key] = array(
'#type' => 'textfield',
'#title' => $label,
'#size' => 25,
'#default_value' => isset($_GET[$label_key]) ? $_GET[$label_key] : NULL,
);
}
$form['search_wrapper']['search']['langcode'] = array(
'#type' => 'language_select',
'#title' => t('Source Language'),
'#empty_option' => t('- Any -'),
'#default_value' => isset($_GET['langcode']) ? $_GET['langcode'] : NULL,
);
$bundle_key = $entity_type
->getKey('bundle');
$bundle_options = $this
->getTranslatableBundles($type);
if (count($bundle_options) > 1) {
$form['search_wrapper']['search'][$bundle_key] = array(
'#type' => 'select',
'#title' => $entity_type
->getBundleLabel(),
'#options' => $bundle_options,
'#empty_option' => t('- Any -'),
'#default_value' => isset($_GET[$bundle_key]) ? $_GET[$bundle_key] : NULL,
);
}
elseif (count($bundle_options) == 0) {
$this
->messenger()
->addWarning($this
->t('Entity translation is not enabled for any of existing content types. To use this functionality go to Content types administration and enable entity translation for desired content types.'));
unset($form['search_wrapper']);
return $form;
}
$form['search_wrapper']['search']['target_language'] = array(
'#type' => 'language_select',
'#title' => $this
->t('Target language'),
'#empty_option' => $this
->t('- Any -'),
'#default_value' => isset($_GET['target_language']) ? $_GET['target_language'] : NULL,
);
$form['search_wrapper']['search']['target_status'] = array(
'#type' => 'select',
'#title' => $this
->t('Target status'),
'#options' => array(
'untranslated_or_outdated' => $this
->t('Untranslated or outdated'),
'untranslated' => $this
->t('Untranslated'),
'outdated' => $this
->t('Outdated'),
),
'#default_value' => isset($_GET['target_status']) ? $_GET['target_status'] : NULL,
'#states' => array(
'invisible' => array(
':input[name="search[target_language]"]' => array(
'value' => '',
),
),
),
);
return $form;
}
public function overviewFormHeader($type) {
$entity_type = \Drupal::entityTypeManager()
->getDefinition($type);
$header = array(
'title' => array(
'data' => $this
->t('Title (in source language)'),
),
);
if (count($this
->getTranslatableBundles($type)) > 1) {
$header['bundle'] = array(
'data' => $this
->t('@entity_name type', array(
'@entity_name' => $entity_type
->getLabel(),
)),
);
}
$header += $this
->getLanguageHeader();
return $header;
}
public function overviewRow(ContentEntityInterface $entity, array $bundles) {
$entity_label = $entity
->label();
$storage = \Drupal::entityTypeManager()
->getStorage($entity
->getEntityTypeId());
$use_latest_revisions = $entity
->getEntityType()
->isRevisionable() && ContentTranslationManager::isPendingRevisionSupportEnabled($entity
->getEntityTypeId(), $entity
->bundle());
$default_revision = $use_latest_revisions ? $storage
->load($entity
->id()) : $entity;
$translations = $entity
->getTranslationLanguages();
$source_lang = $entity
->language()
->getId();
$current_job_items = tmgmt_job_item_load_latest('content', $entity
->getEntityTypeId(), $entity
->id(), $source_lang);
$row = array(
'id' => $entity
->id(),
);
if (count($bundles) > 1) {
$row['bundle'] = isset($bundles[$entity
->bundle()]) ? $bundles[$entity
->bundle()] : t('Unknown');
}
$manager = \Drupal::service('content_translation.manager');
foreach (\Drupal::languageManager()
->getLanguages() as $langcode => $language) {
if ($use_latest_revisions) {
$entity = $default_revision;
$latest_revision_id = $storage
->getLatestTranslationAffectedRevisionId($entity
->id(), $langcode);
if ($latest_revision_id) {
$latest_revision = $storage
->loadRevision($latest_revision_id);
if (!$latest_revision
->wasDefaultRevision() || $default_revision
->hasTranslation($langcode)) {
$entity = $latest_revision;
if ($langcode === $source_lang) {
$entity_label = $entity
->label();
}
}
}
$translations = $entity
->getTranslationLanguages();
}
$translation_status = 'current';
if ($langcode == $source_lang) {
$translation_status = 'original';
}
elseif (!isset($translations[$langcode])) {
$translation_status = 'missing';
}
elseif ($translation = $entity
->getTranslation($langcode)) {
$metadata = $manager
->getTranslationMetadata($translation);
if ($metadata
->isOutdated()) {
$translation_status = 'outofdate';
}
}
$build = $this
->buildTranslationStatus($translation_status, isset($current_job_items[$langcode]) ? $current_job_items[$langcode] : NULL);
if ($translation_status != 'missing' && $entity
->hasLinkTemplate('canonical')) {
$build['source'] = [
'#type' => 'link',
'#url' => $entity
->toUrl('canonical', [
'language' => $language,
]),
'#title' => $build['source'],
];
}
$row['langcode-' . $langcode] = [
'data' => \Drupal::service('renderer')
->render($build),
'class' => array(
'langstatus-' . $langcode,
),
];
}
$label = $entity_label ?: $this
->t('@type: @id', [
'@type' => $entity
->getEntityTypeId(),
'@id' => $entity
->id(),
]);
$row['title'] = $entity
->hasLinkTemplate('canonical') ? $entity
->toLink($label, 'canonical')
->toString() : ($entity_label ?: $entity
->id());
return $row;
}
public function overviewForm(array $form, FormStateInterface $form_state, $type) {
$form = parent::overviewForm($form, $form_state, $type);
$entity_type = \Drupal::entityTypeManager()
->getDefinition($type);
$whitelist = array(
'langcode',
'target_language',
'target_status',
);
if ($entity_type
->hasKey('bundle')) {
$whitelist[] = $entity_type
->getKey('bundle');
}
if ($entity_type
->hasKey('label')) {
$whitelist[] = $entity_type
->getKey('label');
}
$search_property_params = array_filter(\Drupal::request()->query
->all());
$search_property_params = array_intersect_key($search_property_params, array_flip($whitelist));
$bundles = $this
->getTranslatableBundles($type);
foreach (static::getTranslatableEntities($type, $search_property_params, TRUE) as $entity) {
if ($entity
->id()) {
$form['items']['#options'][$entity
->id()] = $this
->overviewRow($entity, $bundles);
}
}
$form['pager'] = array(
'#type' => 'pager',
);
return $form;
}
public function overviewFormValidate(array $form, FormStateInterface $form_state, $type) {
$target_language = $form_state
->getValue(array(
'search',
'target_language',
));
if (!empty($target_language) && $form_state
->getValue(array(
'search',
'langcode',
)) == $target_language) {
$form_state
->setErrorByName('search[target_language]', $this
->t('The source and target languages must not be the same.'));
}
}
public function overviewSubmitToContinuousJobs(FormStateInterface $form_state, $item_type) {
if ($form_state
->getValue('add_all_to_continuous_jobs')) {
$entity_type = \Drupal::entityTypeManager()
->getDefinition($item_type);
$whitelist = array(
'langcode',
'target_language',
'target_status',
);
$whitelist[] = $entity_type
->getKey('bundle');
$whitelist[] = $entity_type
->getKey('label');
$search_property_params = array_filter(\Drupal::request()->query
->all());
$search_property_params = array_intersect_key($search_property_params, array_flip($whitelist));
$operations = array(
array(
array(
ContentEntitySourcePluginUi::class,
'createContinuousJobItemsBatch',
),
array(
$item_type,
$search_property_params,
),
),
);
$batch = array(
'title' => t('Creating continuous job items'),
'operations' => $operations,
'finished' => 'tmgmt_content_create_continuous_job_items_batch_finished',
);
batch_set($batch);
}
else {
$entities = ContentEntitySource::loadMultiple($item_type, array_filter($form_state
->getValue('items')));
$job_items = 0;
foreach ($entities as $entity) {
$job_items += tmgmt_content_create_continuous_job_items($entity);
}
if ($job_items !== 0) {
\Drupal::messenger()
->addStatus(\Drupal::translation()
->formatPlural($job_items, '1 continuous job item has been created.', '@count continuous job items have been created.'));
}
else {
\Drupal::messenger()
->addWarning(t('None of the selected sources can be added to continuous jobs.'));
}
}
}
function getTranslatableBundles($entity_type) {
$enabled_types = \Drupal::service('plugin.manager.tmgmt.source')
->createInstance('content')
->getItemTypes();
if (!isset($enabled_types[$entity_type])) {
return array();
}
$translatable_bundle_types = array();
$content_translation_manager = \Drupal::service('content_translation.manager');
foreach (\Drupal::service('entity_type.bundle.info')
->getBundleInfo($entity_type) as $bundle_type => $bundle_definition) {
if ($content_translation_manager
->isEnabled($entity_type, $bundle_type)) {
$translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
}
}
return $translatable_bundle_types;
}
public static function getTranslatableEntities($entity_type_id, $property_conditions = array(), $pager = FALSE, $offset = 0, $limit = 0) {
$query = static::buildTranslatableEntitiesQuery($entity_type_id, $property_conditions);
if ($query) {
if ($pager) {
$query = $query
->extend('Drupal\\Core\\Database\\Query\\PagerSelectExtender')
->limit(\Drupal::config('tmgmt.settings')
->get('source_list_limit'));
}
elseif ($limit) {
$query
->range($offset, $limit);
}
else {
$query
->range(0, \Drupal::config('tmgmt.settings')
->get('source_list_limit'));
}
$result = $query
->execute();
$entity_ids = $result
->fetchCol();
$entities = array();
if (!empty($entity_ids)) {
$entities = \Drupal::entityTypeManager()
->getStorage($entity_type_id)
->loadMultiple($entity_ids);
}
return $entities;
}
return array();
}
public static function buildTranslatableEntitiesQuery($entity_type_id, $property_conditions = array()) {
$enabled_types = \Drupal::service('plugin.manager.tmgmt.source')
->createInstance('content')
->getItemTypes();
if (!isset($enabled_types[$entity_type_id])) {
return NULL;
}
$langcodes = array_keys(\Drupal::languageManager()
->getLanguages());
$languages = array_combine($langcodes, $langcodes);
$entity_type = \Drupal::entityTypeManager()
->getDefinition($entity_type_id);
$label_key = $entity_type
->getKey('label');
$id_key = $entity_type
->getKey('id');
$query = \Drupal::database()
->select($entity_type
->getBaseTable(), 'e');
$query
->addTag('tmgmt_entity_get_translatable_entities');
$query
->addField('e', $id_key);
$query
->distinct();
$langcode_table_alias = 'e';
$data_table = $entity_type
->isRevisionable() ? $entity_type
->getRevisionDataTable() : $entity_type
->getDataTable();
if ($data_table) {
$langcode_table_alias = $query
->innerJoin($data_table, 'data_table', '%alias.' . $id_key . ' = e.' . $id_key . ' AND %alias.default_langcode = 1');
}
$property_conditions += array(
'langcode' => $langcodes,
);
if (!empty($property_conditions['target_status']) && !empty($property_conditions['target_language']) && in_array($property_conditions['target_language'], $languages)) {
$translation_table_alias = \Drupal::database()
->escapeTable('translation_' . $property_conditions['target_language']);
$query
->leftJoin($data_table, $translation_table_alias, "%alias.{$id_key}= e.{$id_key} AND %alias.langcode = :language", array(
':language' => $property_conditions['target_language'],
));
$query
->condition($langcode_table_alias . '.langcode', $property_conditions['target_language'], '<>');
if ($property_conditions['target_status'] == 'untranslated_or_outdated') {
$or = \Drupal::database()
->condition('OR');
$or
->isNull("{$translation_table_alias}.langcode");
$or
->condition("{$translation_table_alias}.content_translation_outdated", 1);
$query
->condition($or);
}
elseif ($property_conditions['target_status'] == 'outdated') {
$query
->condition("{$translation_table_alias}.content_translation_outdated", 1);
}
elseif ($property_conditions['target_status'] == 'untranslated') {
$query
->isNull("{$translation_table_alias}.langcode");
}
}
unset($property_conditions['target_language']);
unset($property_conditions['target_status']);
if (!empty($label_key) && isset($property_conditions[$label_key])) {
$search_token = trim($property_conditions[$label_key]);
if ($search_token !== '') {
$query
->condition('data_table.' . $label_key, '%' . \Drupal::database()
->escapeLike($search_token) . '%', 'LIKE');
}
unset($property_conditions[$label_key]);
}
if ($bundle_key = $entity_type
->getKey('bundle')) {
$bundles = array();
$content_translation_manager = \Drupal::service('content_translation.manager');
foreach (array_keys(\Drupal::service('entity_type.bundle.info')
->getBundleInfo($entity_type_id)) as $bundle) {
if ($content_translation_manager
->isEnabled($entity_type_id, $bundle)) {
$bundles[] = $bundle;
}
}
if (!$bundles) {
return NULL;
}
if (isset($property_conditions[$bundle_key])) {
$query
->condition('e.' . $bundle_key, $property_conditions[$bundle_key]);
unset($property_conditions[$bundle_key]);
}
else {
$query
->condition('e.' . $bundle_key, $bundles, 'IN');
}
}
foreach ($property_conditions as $property_name => $property_value) {
$alias = $property_name == 'langcode' ? $langcode_table_alias : 'e';
$query
->condition($alias . '.' . $property_name, (array) $property_value, 'IN');
}
$query
->orderBy($entity_type
->getKey('id'), 'DESC');
return $query;
}
public static function createContinuousJobItemsBatch($item_type, array $search_property_params, &$context) {
if (empty($context['sandbox'])) {
$context['sandbox']['offset'] = 0;
$context['results']['job_items'] = 0;
$context['sandbox']['progress'] = 0;
$query = static::buildTranslatableEntitiesQuery($item_type, $search_property_params);
$context['sandbox']['max'] = $query
->countQuery()
->execute()
->fetchField();
}
$limit = \Drupal::config('tmgmt.settings')
->get('source_list_limit');
$entities = static::getTranslatableEntities($item_type, $search_property_params, FALSE, $context['sandbox']['offset'], $limit);
$context['sandbox']['offset'] += $limit;
foreach ($entities as $entity) {
$context['results']['job_items'] += tmgmt_content_create_continuous_job_items($entity);
$context['sandbox']['progress']++;
}
$context['message'] = t('Processed @number sources out of @max', array(
'@number' => $context['sandbox']['progress'],
'@max' => $context['sandbox']['max'],
));
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
}
elseif (count($entities) < $limit) {
$context['finished'] = 1;
}
}
public function reviewForm(array $form, FormStateInterface $form_state, JobItemInterface $item) {
$form = parent::reviewForm($form, $form_state, $item);
if (!$item
->isNeedsReview() && !$item
->isActive()) {
return $form;
}
$entity = ContentEntitySource::load($item
->getItemType(), $item
->getItemId());
if (!$form_state
->isRebuilding() && $entity) {
if (ContentEntitySource::isModeratedEntity($entity)) {
$form['moderation_state'] = $this
->buildContentModerationElement($item, $entity);
}
elseif ($entity instanceof EntityPublishedInterface) {
$form['status'] = $this
->buildPublishStateElement($item, $entity);
}
}
return $form;
}
public function reviewFormSubmit(array $form, FormStateInterface $form_state, JobItemInterface $item) {
if ($form_state
->hasValue([
'moderation_state',
'new_state',
])) {
$moderation_state = (array) $form_state
->getValue([
'moderation_state',
'new_state',
]);
$item
->updateData([
'#moderation_state',
], $moderation_state, TRUE);
}
elseif ($form_state
->hasValue([
'status',
'published',
])) {
$published = (array) (bool) $form_state
->getValue([
'status',
'published',
]);
$item
->updateData([
'#published',
], $published, TRUE);
}
parent::reviewFormSubmit($form, $form_state, $item);
}
protected function buildPublishStateElement(JobItemInterface $item, EntityPublishedInterface $entity) {
$element = [
'#type' => 'fieldset',
'#title' => $this
->t('Translation publish status'),
'#tree' => TRUE,
];
$published = $item
->getData([
'#published',
], 0);
$default_value = isset($published[0]) ? $published[0] : $entity
->isPublished();
$published_title = $this
->t('Published');
$published_field = $entity
->getEntityType()
->getKey('published');
if ($entity instanceof FieldableEntityInterface && $entity
->hasField($published_field)) {
$published_field_definition = $entity
->get($published_field)
->getFieldDefinition();
$published_title = $published_field_definition
->getConfig($entity
->bundle())
->getLabel();
if (!$published_field_definition
->isTranslatable()) {
$published_title = $this
->t('@published_title (all languages)', [
'@published_title' => $published_title,
]);
}
}
$element['published'] = [
'#type' => 'checkbox',
'#default_value' => $default_value,
'#title' => $published_title,
];
return $element;
}
protected function buildContentModerationElement(JobItemInterface $item, ContentEntityInterface $entity) {
$element = [];
$moderation_info = \Drupal::service('content_moderation.moderation_information');
$workflow = $moderation_info
->getWorkflowForEntity($entity);
$moderation_validator = \Drupal::service('content_moderation.state_transition_validation');
$moderation_state = $item
->getData([
'#moderation_state',
], 0);
$current_state = isset($moderation_state[0]) ? $moderation_state[0] : $entity
->get('moderation_state')->value;
$default = $workflow
->getTypePlugin()
->getState($current_state);
$transitions = $moderation_validator
->getValidTransitions($entity, \Drupal::currentUser());
$transition_labels = [];
$default_value = NULL;
foreach ($transitions as $transition) {
$transition_to_state = $transition
->to();
$transition_labels[$transition_to_state
->id()] = $transition_to_state
->label();
if ($default
->id() === $transition_to_state
->id()) {
$default_value = $default
->id();
}
}
if ($default_moderation_state = \Drupal::config('tmgmt_content.settings')
->get('default_moderation_states.' . $workflow
->id())) {
$default_value = $default_moderation_state;
}
$element += [
'#type' => 'container',
'#tree' => TRUE,
'current' => [
'#type' => 'item',
'#title' => $this
->t('Current source state'),
'#markup' => $default
->label(),
'#wrapper_attributes' => [
'class' => [
'container-inline',
],
],
],
'new_state' => [
'#type' => 'select',
'#title' => $this
->t('Translation state'),
'#options' => $transition_labels,
'#default_value' => $default_value,
'#access' => !empty($transition_labels),
'#wrapper_attributes' => [
'class' => [
'container-inline',
],
],
],
];
$element['#theme'] = [
'entity_moderation_form',
];
$element['#attached']['library'][] = 'content_moderation/content_moderation';
return $element;
}
}