entity_translation_unified_form-2.0.x-dev/entity_translation_unified_form.module
entity_translation_unified_form.module
<?php
/**
* @file
* Provides unified entity translation form functionality.
*/
use Drupal\Core\Url;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\RevisionLogInterface;
use Drupal\Core\Access\AccessResultForbidden;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\entity_translation_unified_form\EtufHelper;
use Drupal\Component\Utility\NestedArray;
use Drupal\content_translation\BundleTranslationSettingsInterface;
define('ENTITY_TRANSLATION_UNIFIED_FORM_ENABLED', 4);
/**
* @file
* Places entity translated fields inline in a single form.
*/
/**
* Implements hook_module_implements_alter().
*
* This ensures the form_alter hook runs at the end.
*/
function entity_translation_unified_form_module_implements_alter(&$implementations, $hook) {
if ($hook == 'form_alter' && isset($implementations['entity_translation_unified_form'])) {
// Move this module's implementation of form_alter to the end of the list.
// We are doing this so that the form_node_page_form_alter
// function is called AFTER ContentTranslationHandler::entityFormAlter
// which contains the code we are trying to alter.
$hookInit = $implementations['entity_translation_unified_form'];
unset($implementations['entity_translation_unified_form']);
$implementations['entity_translation_unified_form'] = $hookInit;
}
}
/**
* Add a class only if the page is in add or edit mode.
*/
function entity_translation_unified_form_preprocess_html(&$variables) {
$current_path = \Drupal::service('path.current')->getPath();
$patterns = "/node/add/*\n/node/*/edit";
$match = \Drupal::service('path.matcher')->matchPath($current_path, $patterns);
if (!empty($match)) {
// For css.
$variables['attributes']['class'][] = 'etuf-sbs';
// For sync js.
$variables['attributes']['class'][] = 'sync';
}
}
/**
* Implements hook_page_attachments().
*
* Adds the libraries to only the edit node and add node page.
*/
function entity_translation_unified_form_page_attachments(array &$page) {
$current_route = \Drupal::routeMatch()->getRouteName();
$current_path = \Drupal::service('path.current')->getPath();
$patterns = "/node/add/*\n/node/*/edit";
$match = \Drupal::service('path.matcher')->matchPath($current_path, $patterns);
if (!empty($match)) {
$is_valid = \Drupal::service('path.validator')->isValid($current_path);
if (!$is_valid) {
return new AccessResultForbidden();
}
$entity_type_id = 'node';
$bundle = '';
if (strpos($current_path, '/edit') > 4) {
// Get the node object.
$node = \Drupal::routeMatch()->getParameter($entity_type_id);
$bundle = $node->bundle();
}
else {
// Get the bundle.
$array_of_segments = explode('/', $current_path);
$bundle = end($array_of_segments);
}
// Get the CSS for the current theme.
$theme = \Drupal::service('theme.manager')->getActiveTheme()->getName();
$base_theme = '';
if (property_exists(\Drupal::service('theme_handler')->listInfo()[$theme], 'base_theme')) {
$base_theme = \Drupal::service('theme_handler')->listInfo()[$theme]->base_theme;
}
if (!empty($base_theme)) {
if ($base_theme == 'claro') {
$theme = 'claro';
}
}
if (!empty($base_theme)) {
if ($base_theme == 'business') {
$theme = 'business';
}
}
$path_to_etuf = \Drupal::service('extension.path.resolver')->getPath('module', 'entity_translation_unified_form');
if (entity_translation_unified_form_sbs_enabled($entity_type_id, $bundle)) {
if (file_exists($path_to_etuf . "/css/etuf-side-by-side-$theme.css")) {
$page['#attached']['library'][] = 'entity_translation_unified_form/etuf-' . $theme;
}
else {
// Default theme.
$page['#attached']['library'][] = 'entity_translation_unified_form/etuf';
}
if (version_compare(\Drupal::VERSION, '10.1', '>=')) {
if (file_exists($path_to_etuf . "/css/ten-one-sbs-$theme.css")) {
$page['#attached']['library'][] = 'entity_translation_unified_form/ten-one-sbs-' . $theme;
}
}
$other_languages = EtufHelper::getOtherEnabledLanguages();
foreach ($other_languages as $other_langcode => $other_language) {
$page['#attached']['drupalSettings']['other_langs'][] = $other_langcode;
}
}
else {
if (version_compare(\Drupal::VERSION, '10.1', '>=')) {
if (file_exists($path_to_etuf . "/css/ten-one-inline-$theme.css")) {
$page['#attached']['library'][] = 'entity_translation_unified_form/ten-one-inline-' . $theme;
}
}
}
if (entity_translation_unified_form_moderation_sync_disabled($entity_type_id, $bundle) != TRUE) {
$page['#attached']['library'][] = 'entity_translation_unified_form/etuf-moderation-sync';
$other_languages = EtufHelper::getOtherEnabledLanguages();
foreach ($other_languages as $other_langcode => $other_language) {
$page['#attached']['drupalSettings']['other_langs'][] = $other_langcode;
}
}
if ($current_route == 'entity.node.preview') {
$page['#attached']['library'][] = 'entity_translation_unified_form/etuf_preview';
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add checkbox to node type form to set whether or not the node type should
* enable unified forms.
*/
function entity_translation_unified_form_form_node_type_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['#process'][] = 'entity_translation_unified_form_form_node_type_form_process';
$form['actions']['submit']['#submit'][] = 'entity_translation_unified_form_form_node_type_form_submit';
}
/**
* Add an "enable" checkbox to the node type's multilingual settings.
*/
function entity_translation_unified_form_form_node_type_form_process(&$form, $form_state) {
$entity_type_id = 'node';
$bundle = $form['type']['#default_value'];
// Hide settings when entity translation is disabled for this content type.
$states = [
'visible' => [
':input[name="language_content_type"]' => ['value' => ENTITY_TRANSLATION_UNIFIED_FORM_ENABLED],
],
];
$form['workflow']['entity_translation_unified_form'] = [
'#title' => t('Unified form'),
'#type' => 'fieldset',
'#weight' => 10,
'#states' => $states,
];
$form['workflow']['entity_translation_unified_form']['entity_translation_unified_form_enable'] = [
'#title' => t('Place all content-translatable fields for all enabled languages inline on the node add/edit form'),
'#type' => 'checkbox',
'#default_value' => $form["#id"] === 'node-type-add-form' ? 0 : entity_translation_unified_form_bundle_enabled($entity_type_id, $bundle),
'#disabled' => FALSE,
'#states' => $states,
];
$form['workflow']['entity_translation_unified_form']['entity_translation_unified_form_translate_labels'] = [
'#title' => t('Translate fields labels and descriptions in each language'),
'#type' => 'checkbox',
'#default_value' => FALSE,
'#states' => $states,
];
if (!empty($bundle)) {
$form['workflow']['entity_translation_unified_form']['entity_translation_unified_form_translate_labels']['#default_value'] = entity_translation_unified_form_translate_labels_enabled($entity_type_id, $bundle);
$form['workflow']['entity_translation_unified_form']['entity_translation_unified_form_translate_labels']['#states'] = [
'visible' => [
'and' => [
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][settings][content_translation][entity_translation_unified_form_enable]"]'
=> ['checked' => TRUE],
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][settings][content_translation][entity_translation_unified_form_theme]"]'
=> ['value' => 'EntityTranslationUnifiedFormInlineMode'],
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][translatable]"]'
=> ['checked' => TRUE],
],
],
];
}
$form['workflow']['entity_translation_unified_form']['entity_translation_unified_form_sbs_enable'] = [
'#title' => t('Enable side-by-side UI mode on the node add/edit form'),
'#type' => 'checkbox',
'#default_value' => $form["#id"] === 'node-type-add-form' ? 0 : entity_translation_unified_form_sbs_enabled($entity_type_id, $bundle),
'#disabled' => FALSE,
'#states' => $states,
];
$form['workflow']['entity_translation_unified_form']['entity_translation_unified_form_moderation_sync_disable'] = [
'#title' => t('Disable moderation sync mode on the node add/edit form'),
'#type' => 'checkbox',
'#default_value' => $form["#id"] === 'node-type-add-form' ? 0 : entity_translation_unified_form_moderation_sync_disabled($entity_type_id, $bundle),
'#disabled' => FALSE,
'#states' => $states,
];
$route_name = \Drupal::routeMatch()->getRouteName();
if ($route_name != 'node.type_add') {
// Save only button option for content types on the node type edit or node
// type add form.
$form['workflow']['entity_translation_unified_form']['entity_translation_unified_form_save_only_button'] = [
'#title' => t('Apply "Save only" button for this bundle'),
'#type' => 'checkbox',
'#default_value' => entity_translation_unified_form_save_only_button($entity_type_id, $bundle),
];
}
return $form;
}
/**
* Submit handler.
*
* Sets content type-specific variable that is used to determine whether or not
* the content type may used unified forms.
*/
function entity_translation_unified_form_form_node_type_form_submit(&$form, FormStateInterface $form_state) {
$entity_type_id = 'node';
$bundle = $form_state->getValues()['type'];
$enabled = $form_state->getValues()['entity_translation_unified_form_enable'];
entity_translation_unified_form_set_bundle_enabled($entity_type_id, $bundle, $enabled);
$enabled = $form_state->getValues()['entity_translation_unified_form_sbs_enable'];
entity_translation_unified_form_set_sbs_enabled($entity_type_id, $bundle, $enabled);
$enabled = $form_state->getValues()['entity_translation_unified_form_moderation_sync_disable'];
entity_translation_unified_form_set_moderation_sync_disabled($entity_type_id, $bundle, $enabled);
$enabled = FALSE;
if (isset($form_state->getValues()['entity_translation_unified_form_translate_labels'])) {
$enabled = $form_state->getValues()['entity_translation_unified_form_translate_labels'];
}
entity_translation_unified_form_set_translate_labels_enabled($entity_type_id, $bundle, $enabled);
$enabled = $form_state->getValues()['entity_translation_unified_form_save_only_button'];
entity_translation_unified_form_set_save_only_button_enabled($entity_type_id, $bundle, $enabled);
}
/**
* Sets the configuration for a given bundle.
*/
function entity_translation_unified_form_set_bundle_enabled($entity_type_id, $bundle, $enabled) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
$settings['entity_translation_unified_form_enable'] = $enabled;
$content_translation_manager->setBundleTranslationSettings($entity_type_id, $bundle, $settings);
}
}
/**
* Returns true if ETUF is enabled for a given bundle.
*/
function entity_translation_unified_form_bundle_enabled($entity_type_id, $bundle) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
return !empty($settings['entity_translation_unified_form_enable']);
}
return FALSE;
}
/**
* Sets the configuration for a given bundle.
*/
function entity_translation_unified_form_set_sbs_enabled($entity_type_id, $bundle, $enabled) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
$settings['entity_translation_unified_form_sbs_enable'] = $enabled;
$content_translation_manager->setBundleTranslationSettings($entity_type_id, $bundle, $settings);
}
}
/**
* Sets the configuration for a given bundle.
*/
function entity_translation_unified_form_set_moderation_sync_disabled($entity_type_id, $bundle, $enabled) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
if (isset($settings['entity_translation_unified_form_moderation_sync_disable'])) {
$settings['entity_translation_unified_form_moderation_sync_disable'] = $enabled;
}
else {
$settings['entity_translation_unified_form_moderation_sync_disable'] = FALSE;
}
$content_translation_manager->setBundleTranslationSettings($entity_type_id, $bundle, $settings);
}
}
/**
* Returns true if ETUF sbs is enabled for a given bundle.
*/
function entity_translation_unified_form_sbs_enabled($entity_type_id, $bundle) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
return !empty($settings['entity_translation_unified_form_sbs_enable']);
}
return FALSE;
}
/**
* Sets the configuration for a given bundle.
*/
function entity_translation_unified_form_set_translate_labels_enabled($entity_type_id, $bundle, $enabled) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
$settings['entity_translation_unified_form_translate_labels'] = $enabled;
$content_translation_manager->setBundleTranslationSettings($entity_type_id, $bundle, $settings);
}
}
/**
* Returns true if translate labels is enabled for a given bundle.
*/
function entity_translation_unified_form_translate_labels_enabled($entity_type_id, $bundle) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
return !empty($settings['entity_translation_unified_form_translate_labels']);
}
return FALSE;
}
/**
* Sets the configuration for a given bundle.
*/
function entity_translation_unified_form_set_save_only_button_enabled($entity_type_id, $bundle, $enabled) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
$settings['entity_translation_unified_form_save_only_button'] = $enabled;
$content_translation_manager->setBundleTranslationSettings($entity_type_id, $bundle, $settings);
}
}
/**
* Returns true if moderation sync mode is disabled for a given bundle.
*/
function entity_translation_unified_form_moderation_sync_disabled($entity_type_id, $bundle) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
return !empty($settings['entity_translation_unified_form_moderation_sync_disable']);
}
return FALSE;
}
/**
* Returns the display mode for a given bundle.
*/
function entity_translation_unified_form_bundle_display_mode($entity_type_id, $bundle) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
if (!empty($settings['entity_translation_unified_form_theme'])) {
return $settings['entity_translation_unified_form_theme'];
}
}
return NULL;
}
/**
* Returns the language display mode for a given bundle.
*/
function entity_translation_unified_form_language_display($entity_type_id, $bundle) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
if (!empty($settings['entity_translation_unified_form_language'])) {
return $settings['entity_translation_unified_form_language'];
}
}
return NULL;
}
/**
* Returns the different display modes for the language part.
*/
function entity_translation_unified_form_get_language_display_options() {
// @todo make constants rather than strings
return [
'current' => t('Current language'),
'native' => t('Native language'),
'code' => t('Language code'),
];
}
/**
* Gathers the different modes from EntityTranslationUnifiedFormMode plugins.
*
* Returns an array with mode IDs as keys and labels as values.
*/
function entity_translation_unified_form_get_mode_options() {
$mode_options = &drupal_static(__FUNCTION__);
if (empty($mode_options)) {
$type = \Drupal::service('plugin.manager.entity_translation_unified_form_mode');
$plugin_definitions = $type->getDefinitions();
$mode_options = [];
foreach ($plugin_definitions as $definition) {
$mode_id = $definition['id'];
$mode_options[$mode_id] = $definition['admin_label'];
}
}
return $mode_options;
}
/**
* Get a EntityTranslationUnifiedFormMode plugin instance by its id.
*/
function entity_translation_unified_form_get_mode_plugin($mode_plugin_id) {
$type = \Drupal::service('plugin.manager.entity_translation_unified_form_mode');
$plugin_definitions = $type->getDefinitions();
foreach ($plugin_definitions as $classname => $definition) {
if ($mode_plugin_id == $definition['id']) {
return $type->createInstance($classname);
}
}
return NULL;
}
/**
* Implements hook_form_form_language_content_settings_form_alter().
*
* Add the "Enable ETUF" checkbox to the content language page:
* admin/config/regional/content-language.
*/
function entity_translation_unified_form_form_language_content_settings_form_alter(&$form, FormStateInterface $form_state) {
// Inject into the content language settings the translation settings if the
// user has the required permission.
if (!\Drupal::currentUser()->hasPermission('administer content translation')) {
return;
}
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
$bundle_info_service = \Drupal::service('entity_type.bundle.info');
foreach ($form['#labels'] as $entity_type_id => $label) {
$entity_type_translatable = $content_translation_manager->isSupported($entity_type_id);
if ($entity_type_translatable) {
foreach ($bundle_info_service->getBundleInfo($entity_type_id) as $bundle => $bundle_info) {
$form['settings'][$entity_type_id][$bundle]['settings']['content_translation']['entity_translation_unified_form_enable'] = [
'#title' => t('Place all content-translatable fields for all enabled languages inline on the entity add/edit form'),
'#type' => 'checkbox',
'#default_value' => entity_translation_unified_form_bundle_enabled($entity_type_id, $bundle),
'#states' => [
'visible' => [
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][translatable]"]' => [
'checked' => TRUE,
],
],
],
];
$form['settings'][$entity_type_id][$bundle]['settings']['content_translation']['entity_translation_unified_form_theme'] = [
'#title' => t('Select the inline display mode'),
'#type' => 'select',
'#default_value' => entity_translation_unified_form_bundle_display_mode($entity_type_id, $bundle),
'#options' => entity_translation_unified_form_get_mode_options(),
'#states' => [
'visible' => [
'and' => [
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][translatable]"]' => [
'checked' => TRUE,
],
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][settings][content_translation][entity_translation_unified_form_enable]"]' => [
'checked' => TRUE,
],
],
],
],
];
$form['settings'][$entity_type_id][$bundle]['settings']['content_translation']['entity_translation_unified_form_language'] = [
'#title' => t('Select how to display language name'),
'#type' => 'select',
'#default_value' => entity_translation_unified_form_language_display($entity_type_id, $bundle),
'#options' => entity_translation_unified_form_get_language_display_options(),
'#states' => [
'visible' => [
'and' => [
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][settings][content_translation][entity_translation_unified_form_enable]"]' => [
'checked' => TRUE,
],
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][settings][content_translation][entity_translation_unified_form_theme]"]' => [
'value' => 'EntityTranslationUnifiedFormInlineMode',
],
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][translatable]"]' => [
'checked' => TRUE,
],
],
],
],
];
$form['settings'][$entity_type_id][$bundle]['settings']['content_translation']['entity_translation_unified_form_translate_labels'] = [
'#title' => t('Translate fields labels and descriptions in each language'),
'#type' => 'checkbox',
'#default_value' => entity_translation_unified_form_translate_labels_enabled($entity_type_id, $bundle),
'#states' => [
'visible' => [
'and' => [
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][settings][content_translation][entity_translation_unified_form_enable]"]' => [
'checked' => TRUE,
],
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][settings][content_translation][entity_translation_unified_form_theme]"]' => [
'value' => 'EntityTranslationUnifiedFormInlineMode',
],
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][translatable]"]' => [
'checked' => TRUE,
],
],
],
],
];
if ($entity_type_id == 'node') {
$form['settings'][$entity_type_id][$bundle]['settings']['content_translation']['entity_translation_unified_form_sbs_enable'] = [
'#title' => t('Enable side-by-side UI mode on the node add/edit form'),
'#type' => 'checkbox',
'#default_value' => entity_translation_unified_form_sbs_enabled('node', $bundle),
'#states' => [
'visible' => [
'and' => [
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][translatable]"]' => [
'checked' => TRUE,
],
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][settings][content_translation][entity_translation_unified_form_enable]"]' => [
'checked' => TRUE,
],
],
],
],
];
}
if ($entity_type_id == 'node') {
$form['settings'][$entity_type_id][$bundle]['settings']['content_translation']['entity_translation_unified_form_moderation_sync_disable'] = [
'#title' => t('Disable moderation sync mode on the node add/edit form for this bundle'),
'#type' => 'checkbox',
'#default_value' => entity_translation_unified_form_moderation_sync_disabled('node', $bundle),
'#states' => [
'visible' => [
':input[name="settings[' . $entity_type_id . '][' . $bundle . '][translatable]"]' => [
'checked' => TRUE,
],
],
],
];
// Save only button option for content types.
$form['settings'][$entity_type_id][$bundle]['settings']['content_translation']['entity_translation_unified_form_save_only_button'] = [
'#title' => t('Apply "Save only" button for this bundle'),
'#type' => 'checkbox',
'#default_value' => entity_translation_unified_form_save_only_button($entity_type_id, $bundle),
];
}
}
}
}
}
/**
* Perform alterations before an entity form is included in the IEF widget.
*
* This hook can be implemented by themes.
*
* @param array $entity_form
* Nested array of form elements that comprise the entity form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state of the parent form.
*/
function entity_translation_unified_form_inline_entity_form_entity_form_alter(array &$entity_form, FormStateInterface &$form_state) {
$entity = $entity_form['#entity'];// $form_object->getEntity();
$entity_type_id = $entity->getEntityTypeId();
$bundle = $entity->bundle();
if (!empty($entity) && entity_translation_unified_form_bundle_enabled($entity_type_id, $bundle)) {
// Add entity translation form elements for unified language node form.
$entityManager = \Drupal::service('entity_field.manager');
$fields = $entityManager->getFieldDefinitions($entity_type_id, $bundle);
$mode_plugin_id = entity_translation_unified_form_bundle_display_mode($entity_type_id, $bundle);
if ($mode_plugin_id != NULL) {
// If side-by-side selected only Inline plugin is allowed (note: should be treated at config level, not here)
$sbs = entity_translation_unified_form_sbs_enabled($entity_type_id, $bundle);
if ($sbs) {
$mode_plugin_id = 'EntityTranslationUnifiedFormInlineMode';
}
$mode_plugin = entity_translation_unified_form_get_mode_plugin($mode_plugin_id);
// Process each of the entity type's fields.
$form_display = EntityFormDisplay::collectRenderDisplay($entity, 'default');
foreach ($fields as $field_name => $field_definition) {
entity_translation_unified_form_node_insert_other_language_fields($entity_form, $form_state, $entity, $field_definition, $mode_plugin, $form_display);
}
// Place our submit callback right before submitCleanFormState
$submit_key = -1;
foreach($entity_form['#ief_element_submit'] as $key => $values) {
if(is_array($values) && count($values) > 1 && $values[1] == 'submitCleanFormState') {
$submit_key = $key;
break;
}
}
array_splice($entity_form['#ief_element_submit'], $submit_key, 0, 'entity_translation_unified_form_inline_entity_form_submit');
}
}
}
/**
* Form submission handler for node_form().
*
* Configure entity translation node object for entity_translation_unified_form.
*/
function entity_translation_unified_form_inline_entity_form_submit(array $form, FormStateInterface $form_state) {
if(isset($form['#entity'])) {
$entity = $form['#entity'];
$entity_type = $entity->getEntityTypeId();
$bundle = $entity->bundle();
$entityManager = \Drupal::service('entity_field.manager');
$fields = $entityManager->getFieldDefinitions($entity_type, $bundle);
// Process the translated values.
$values = $form_state->getValues();
$other_languages = EtufHelper::getOtherEnabledLanguages();
foreach ($other_languages as $other_langcode => $other_language) {
// Get the existing translation, or create one if it doesn't currently exist.
if ($entity->hasTranslation($other_langcode)) {
$translation = $entity->getTranslation($other_langcode);
}
else {
$translation = $entity->addTranslation($other_langcode);
}
foreach ($fields as $field_name => $field_definition) {
$etuf_name = EtufHelper::getEtufFieldName($field_name, $other_langcode);
$path = array_merge($form['#parents'], [$etuf_name]);
$key_exists = NULL;
$field_values = NestedArray::getValue($values, $path, $key_exists);
if ($key_exists) {
$translation->set($field_name, $field_values);
}
}
}
}
}
/**
* Add language fields to node forms.
*
* Implements hook_form_BASE_FORM_ID_alter().
*/
function entity_translation_unified_form_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form_object = $form_state->getFormObject();
if (!$form_object instanceof EntityForm) {
return;
}
if (stripos($form_id, 'layout_builder') > 0) {
// Do not modify the layout_builder form.
return;
}
if (stripos($form_id, 'revisions') > 0) {
// Do not interfere with the revisions tab either.
return;
}
if (stripos($form_id, 'scheduled_transitions') > 0) {
// Do not modify the scheduled transitions form.
return;
}
if (stripos($form_id, 'node_type_add_form') > 0) {
// Do not modify the scheduled transitions form.
return;
}
if (stripos($form_id, '_book_outline_form') > 0) {
// Do not modify the book outline form.
return;
}
$entity = $form_object->getEntity();
$entity_type_id = $entity->getEntityTypeId();
$bundle = $entity->bundle();
// Retrieve "Save only" button enable flag from configuration setting.
$save_only_button_enable = FALSE;
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
if (isset($settings['entity_translation_unified_form_save_only_button'])) {
$save_only_button_enable = $settings['entity_translation_unified_form_save_only_button'];
}
}
if (isset($save_only_button_enable)) {
if ($save_only_button_enable) {
$form['actions']['saveonly'] = [
'#type' => 'submit',
'#value' => t('Save only'),
'#weight' => 10,
'#submit' => [
'::submitForm',
'::save',
'entity_translation_unified_form_entity_translation_unified_page_submit_save_only',
],
];
}
}
if (!empty($entity) && entity_translation_unified_form_bundle_enabled($entity_type_id, $bundle)) {
// Inject our submit handler immediately before the entity save function
// If we don't find the save function, don't do anything.
$key = array_search('::save', $form['actions']['submit']['#submit']);
if (isset($form['actions']['preview'])) {
$keyp = array_search('::preview', $form['actions']['preview']['#submit']);
}
if (isset($form['actions']['saveonly'])) {
$keypp = array_search('::save', $form['actions']['saveonly']['#submit']);
}
if ($key !== FALSE) {
array_splice($form['actions']['submit']['#submit'], $key, 0, 'entity_translation_unified_form_node_form_submit');
if (isset($form['actions']['preview'])) {
array_splice($form['actions']['preview']['#submit'], $keyp, 0, 'entity_translation_unified_form_node_form_preview');
}
if (isset($form['actions']['saveonly'])) {
array_splice($form['actions']['saveonly']['#submit'], $keypp, 0, 'entity_translation_unified_form_node_form_submit');
}
// Add entity translation form elements for unified language node form.
entity_translation_unified_form_add_fields($form, $form_state);
}
}
}
/**
* Add opposite-language ET fields to a form.
*/
function entity_translation_unified_form_add_fields(&$form, FormStateInterface $form_state) {
$form_state->loadInclude('entity_translation_unified_form', 'inc', 'entity_translation_unified_form.theme');
$form_object = $form_state->getFormObject();
if ($form_object instanceof EntityForm) {
$entity = $form_object->getEntity();
$entity_type = $entity->getEntityTypeId();
$bundle = $entity->bundle();
$entityManager = \Drupal::service('entity_field.manager');
$fields = $entityManager->getFieldDefinitions($entity_type, $bundle);
$mode_plugin_id = entity_translation_unified_form_bundle_display_mode($entity_type, $bundle);
if ($mode_plugin_id != NULL) {
// If side-by-side selected only Inline plugin is allowed
// (note: should be treated at config level, not here)
$sbs = entity_translation_unified_form_sbs_enabled($entity_type, $bundle);
if ($sbs) {
$mode_plugin_id = 'EntityTranslationUnifiedFormInlineMode';
}
$mode_plugin = entity_translation_unified_form_get_mode_plugin($mode_plugin_id);
// Process each of the entity type's fields.
$form_display = EntityFormDisplay::collectRenderDisplay($entity, 'default');
foreach ($fields as $field_name => $field_definition) {
entity_translation_unified_form_node_insert_other_language_fields($form, $form_state, $entity, $field_definition, $mode_plugin, $form_display);
}
$other_languages = EtufHelper::getOtherEnabledLanguages();
// Handle other language menu link translation load title into new field.
foreach ($other_languages as $other_langcode => $other_language) {
if (isset($form['menu']['enabled']['#default_value'])) {
if ($form['menu']['enabled']['#default_value']) {
$form['menu']['link']['title']['#weight'] = -1;
$form['menu']['link']['link-title-etuf-' . $other_langcode] = [
'#type' => 'textfield',
'#title' => t('Menu link title') . ' ' . $other_langcode,
'#default_value' => '',
'#weight' => 0,
'#maxlength' => 255,
'#required' => TRUE,
];
$ml_uuid = $form['menu']['link']['id'];
if (empty($ml_uuid) || !isset($ml_uuid)) {
continue;
}
if (is_array($ml_uuid) && isset($ml_uuid['#value'])) {
$ml_uuid = $ml_uuid['#value'];
}
$storage = \Drupal::entityTypeManager()->getStorage('menu_link_content');
$uuid = str_replace('menu_link_content:', '', $ml_uuid);
$menu_link = $storage->loadByProperties(['uuid' => $uuid]);
if (is_array($menu_link)) {
$menu_link = reset($menu_link);
if ($menu_link && $menu_link->hasTranslation($other_langcode)) {
$menu_link = $menu_link->getTranslation($other_langcode);
$title = '';
if (!empty($menu_link->label())) {
// Truncate to no more than 255 chars.
$title = substr($menu_link->label(), 0, 254);
}
$form['menu']['link']['link-title-etuf-' . $other_langcode]['#default_value'] = $title;
}
elseif ($menu_link) {
$translation = $entity->getTranslation($other_langcode);
// Truncate to no more than 255 chars.
$title = substr($translation->getTitle(), 0, 254);
$form['menu']['link']['link-title-etuf-' . $other_langcode]['#default_value'] = $title;
}
}
}
}
}
}
}
}
/**
* Instanciate one value (for default_value)
*/
function entity_translation_unified_form_def_value($val, $mono = FALSE) {
if ($mono) {
return $val;
}
if (!is_array($val)) {
return $val;
}
if (isset($val['date'])) {
// Date only needs date object.
return $val['date'];
}
if (count($val) == 1) {
return current($val);
}
// This probably wont work in all cases.
return $val;
}
/**
* Creates a field definition from the existing one.
*
* Checks on the fields should have been done before calling this.
*/
function entity_translation_unified_form_build_field($form, $form_state, $entity, $entity_type, $bundle, $field_name, $langcode, &$form_display) {
$language_manager = \Drupal::languageManager();
$initial_language = $language_manager->getConfigOverrideLanguage();
$translate_labels = entity_translation_unified_form_translate_labels_enabled($entity_type, $bundle);
// Force language.
if ($translate_labels) {
$language_manager->setConfigOverrideLanguage($language_manager->getLanguage($langcode));
\Drupal::service("entity_field.manager")->clearCachedFieldDefinitions();
}
// Get data for language.
if ($entity->hasTranslation($langcode)) {
$entity = $entity->getTranslation($langcode);
}
else {
$entity = $entity->addTranslation($langcode);
}
// Get the items depending of the $entity.
if ($entity->hasTranslation($langcode)) {
if ($entity_type == 'node' && $entity->isLatestRevision()) {
// @todo Simplify this code that was written without sleep, for now,
// holding my nose.
// LatestRevision default language is not the same revision id as other
// lang or vice versa.
// Might want to add a check to see if content_moderation is activated
// for this content type.
$storage = \Drupal::entityTypeManager()->getStorage($entity_type);
if (!empty($entity->id())) {
$latestTranslationAffectedRevisionId = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $langcode);
if (is_numeric($latestTranslationAffectedRevisionId)) {
$entityOtherLangLatestRevision = $storage->loadRevision($latestTranslationAffectedRevisionId);
}
}
if (empty($entityOtherLangLatestRevision)) {
$definition = $entity->getFieldDefinition($field_name);
if (isset($definition) && $definition->isTranslatable()) {
$items = $entity->getTranslation($langcode)->get($field_name);
}
else {
$items = $entity->get($field_name);
}
}
else {
$definitionLatest = $entityOtherLangLatestRevision->getFieldDefinition($field_name);
if (!empty($definitionLatest->getType()) && $definitionLatest->isTranslatable()) {
// Now get the latest revision of $other_langcode.
$items = $entityOtherLangLatestRevision->getTranslation($langcode)->get($field_name);
}
else {
$items = $entityOtherLangLatestRevision->get($field_name);
}
}
}
else {
$items = $entity->getTranslation($langcode)->get($field_name);
}
}
else {
$items = $entity->addTranslation($langcode)->get($field_name);
}
$widget = $form_display->getRenderer($field_name);
if (empty($widget)) {
// Restore language.
if ($translate_labels) {
$language_manager->setConfigOverrideLanguage($initial_language);
\Drupal::service("entity_field.manager")->clearCachedFieldDefinitions();
}
return [];
}
$field_form = $widget->form($items, $form, $form_state);
// Manage default value (because Drupal is not able to set the default_value
// that matches the forced language, so it has to be done by hand…). This
// is probably incomplete.
if ($entity->isNew()) {
// Get default value (at least 'title' field don't have any definition)
if (!$translate_labels) {
// Default value must come with forced language.
$language_manager->setConfigOverrideLanguage($language_manager->getLanguage($langcode));
\Drupal::service("entity_field.manager")->clearCachedFieldDefinitions();
}
$field_definition = FieldConfig::loadByName($entity_type, $bundle, $field_name);
if ($field_definition) {
$def = $field_definition->getDefaultValue($entity);
}
else {
$def = [];
}
if (!$translate_labels) {
$language_manager->setConfigOverrideLanguage($initial_language);
\Drupal::service("entity_field.manager")->clearCachedFieldDefinitions();
}
if (isset($def) and !empty($def)) {
if (isset($field_form['widget']['#default_value'])) {
$field_form['widget']['#default_value'] = entity_translation_unified_form_def_value($def, TRUE);
}
else {
foreach ($def as $idx => $cont) {
if (isset($field_form['widget'][$idx]['value'])) {
if (!empty($cont)) {
$field_form['widget'][$idx]['value']['#default_value'] = entity_translation_unified_form_def_value($cont);
}
}
}
}
}
}
// Restore language.
if ($translate_labels) {
$language_manager->setConfigOverrideLanguage($initial_language);
\Drupal::service("entity_field.manager")->clearCachedFieldDefinitions();
}
return $field_form;
}
/**
* Add all enabled language fields for a single field.
*/
function entity_translation_unified_form_node_insert_other_language_fields(&$form, &$form_state, $entity, $field_definition, $mode_plugin, &$form_display) {
$language_manager = \Drupal::languageManager();
$language = $language_manager->getCurrentLanguage();
if ($entity instanceof EntityInterface) {
$entity_type = $entity->getEntityTypeId();
$bundle = $entity->bundle();
$field_name = $field_definition->getName();
$display_options = $field_definition->getDisplayOptions('form');
$hidden = empty($display_options) || (isset($display_options['type']) && $display_options['type'] == 'hidden');
// Only process entity-translatable fields.
if ($field_definition->isTranslatable() && !$hidden) {
$other_languages = EtufHelper::getOtherEnabledLanguages();
foreach ($other_languages as $other_langcode => $other_language) {
$current_language = $language_manager->getLanguage($other_langcode);
// Get the field definition for this language.
$field_form = entity_translation_unified_form_build_field($form, $form_state, $entity, $entity_type, $bundle, $field_name, $other_langcode, $form_display);
// Build and attach the translated field.
if (!empty($field_form)) {
$field_form['widget']['#field_name'] = EtufHelper::getEtufFieldName($field_name, $other_langcode);
// Update the parents array.
if (!is_array($field_form['widget']['#parents'])) {
$field_form['widget']['#parents'] = [];
$field_form['widget']['#parents'][] = $field_form['widget']['#field_name'];
}
else {
array_splice($field_form['widget']['#parents'], -1, 1, $field_form['widget']['#field_name']);
}
// Update the "Add another item" button's name.
if (isset($field_form['widget']['add_more'])) {
$field_form['widget']['add_more']['#name'] = $field_form['widget']['#field_name'] . '_add_more';
}
// Add the language to the render array, for additional information.
$field_form['#etuf_field_language'] = $current_language;
// Add the language field's theme wrapper, if any.
$field_wrapper = $mode_plugin->getFieldThemeWrapper($form, $form_state, $field_form, $field_name, $current_language);
if (!empty($field_wrapper)) {
$field_form['#theme_wrappers'][] = $field_wrapper;
}
// Give plugins a chance to alter the language field.
$mode_plugin->fieldformAlter($form, $form_state, $field_form, $field_name, $current_language);
// Add the new field to the form.
$form[$field_name][$other_langcode] = $field_form;
}
}
// Mark this field as a multilingual field.
// Without this, Content Translation will incorrectly add the
// "all languages" hints.
$form[$field_name]['#multilingual'] = TRUE;
// Add the language to the render array, for additional information.
$form[$field_name]['#etuf_field_language'] = $language;
if (!isset($form[$field_name]['#access'])) {
$form[$field_name]['#access'] = NULL;
}
// Add CSS class to identify translated fields.
$form[$field_name]['#attributes']['class'][] = 'etuf-translated-field';
// Add the original language field's theme wrapper, if any.
$field_wrapper = $mode_plugin->getFieldThemeWrapper($form, $form_state, $form[$field_name], $field_name, $language);
if (!empty($field_wrapper)) {
$form[$field_name]['#theme_wrappers'][] = $field_wrapper;
}
// Give plugins a chance to alter the language field.
$mode_plugin->fieldFormAlter($form, $form_state, $form[$field_name], $field_name, $language);
// Add a wrapper for the group of unified form fields.
$field_group_wrapper = $mode_plugin->getFieldGroupThemeWrapper($form, $form_state, $form[$field_name], $field_name);
if (!empty($field_group_wrapper)) {
$form[$field_name]['#theme_wrappers'][] = $field_group_wrapper;
}
}
elseif (entity_translation_unified_form_sbs_enabled($entity_type, $bundle) && !$hidden) {
// In case of side-by-side add a wrapper to prevent moving to the right
// in case of field height change.
if (isset($form[$field_name]['#prefix'])) {
$form[$field_name]['#prefix'] = '<div class="etuf-sbs-none">' . $form[$field_name]['#prefix'];
}
else {
$form[$field_name]['#prefix'] = '<div class="etuf-sbs-none">';
}
if (isset($form[$field_name]['#suffix'])) {
$form[$field_name]['#suffix'] = $form[$field_name]['#suffix'] . '</div>';
}
else {
$form[$field_name]['#suffix'] = '</div>';
}
}
if (entity_translation_unified_form_sbs_enabled($entity_type, $bundle)) {
if (!$hidden && isset($form[$field_name]['#weight'])) {
$form['etuf-' . $field_name . '-sep'] = [
'#markup' => '',
'#prefix' => '<div class="etuf-sbs-none">',
'#suffix' => '</div>',
'#weight' => $form[$field_name]['#weight'],
];
}
}
}
}
/**
* Form submission handler for node_form_preview().
*
* Configure entity translation node object for entity_translation_unified_form.
*/
function entity_translation_unified_form_node_form_preview(array $form, FormStateInterface $form_state) {
$other_languages = EtufHelper::getOtherEnabledLanguages();
$form_object = $form_state->getFormObject();
$entity = NULL;
if ($form_object instanceof EntityForm) {
$entity = $form_object->getEntity();
$entity_type = $entity->getEntityTypeId();
$bundle = $entity->bundle();
$entityManager = \Drupal::service('entity_field.manager');
$fields = $entityManager->getFieldDefinitions($entity_type, $bundle);
// Process the translated values.
$values = $form_state->getValues();
$stateOtherLang = '';
foreach ($other_languages as $other_langcode => $other_language) {
// Get the existing translation, or create one if it doesn't currently
// exist.
if ($entity->hasTranslation($other_langcode)) {
$translation = $entity->getTranslation($other_langcode);
}
else {
$translation = $entity->addTranslation($other_langcode);
}
$translation->setRevisionTranslationAffected(TRUE);
foreach ($fields as $field_name => $field_definition) {
$etuf_name = EtufHelper::getEtufFieldName($field_name, $other_langcode);
$field_type = $field_definition->getType();
// Copy the translated values from form_state to the entity translation.
if (isset($values[$etuf_name])) {
if ($field_type == 'metatag' && isset($values[$etuf_name])) {
// Begin processing form for metatags into the expected format.
$metatags_from_form = $values[$etuf_name];
$metatags_reset_form = reset($metatags_from_form);
$metatags_prep = [];
foreach ($metatags_reset_form as $outer_key => $metatags_part) {
if (is_array($metatags_part)) {
foreach ($metatags_part as $key => $value) {
// Flatten metatags to key=>value.
$metatags_prep[$key] = $value;
}
}
}
// End of metatag transformation, now serialize.
$metatags_serialized = serialize($metatags_prep);
// Save the serialized metatags to the field.
$translation->set($field_name, $metatags_serialized);
}
elseif ($field_name == 'created' && $field_type == 'created') {
// Fix date/time bug #3117164.
// Part 1/4 fix date/time bug #3117164, would otherwise be 1970,
// copy from source.
$dateObj = $values[$etuf_name][0]['value'];
$translation->set($field_name, $dateObj->getTimestamp());
}
elseif ($field_type == 'image' || $field_type == 'file') {
// Special treatment for managed files, not sure why? (core bug
// workaround?).
if (isset($values[$etuf_name][0]['fids'][0])) {
$numberOfFiles = count($values[$etuf_name]);
if ($numberOfFiles == 1) {
// Just one file/image to fix, otherwise seems ok.
if (!isset($values[$etuf_name][0]['target_id'])) {
$values[$etuf_name][0]['target_id'] = $values[$etuf_name][0]['fids'][0];
unset($values[$etuf_name][0]['fids']);
}
}
elseif ($numberOfFiles > 1) {
for ($i = 0; $i < $numberOfFiles; $i++) {
if (!isset($values[$etuf_name][$i]['target_id']) && isset($values[$etuf_name][$i]['fids'][0])) {
$values[$etuf_name][$i]['target_id'] = $values[$etuf_name][$i]['fids'][0];
unset($values[$etuf_name][$i]['fids']);
}
}
}
}
$translation->set($field_name, $values[$etuf_name]);
}
elseif (isset($field_name) && !is_null($translation)) {
// Default.
$translation->set($field_name, $values[$etuf_name]);
}
elseif ($field_name == 'created' && $field_type == 'created'
&& isset($values[$field_name])) {
// Fix date/time bug #3117164.
// Part 2/4 fix date/time bug #3117164, would otherwise be 1970,
// copy from source.
$dateObj = $values[$field_name][0]['value'];
if (!is_null($dateObj)) {
$translation->set($field_name, $dateObj->getTimestamp());
}
}
elseif (isset($translation) && $field_name == 'revision_timestamp') {
// Part 3/4 fix date/time bug #3117164, would otherwise be NULL.
// $values[$field_name] = \Drupal::time()->getCurrentTime();
$values[$etuf_name] = \Drupal::time()->getCurrentTime();
}
elseif (isset($translation) && $field_name == 'changed' && $field_type == 'changed') {
// Part 4/4 fix date/time bug #3117164, would otherwise be NULL.
$values[$etuf_name] = \Drupal::time()->getCurrentTime();
}
}
} // End of foreach field_definition.
if (!isset($once_only)) {
$once_only = TRUE;
if (!empty($stateOtherLang)) {
// Content Moderation is enabled.
// We must set this flag otherwise the translated revisions do not
// get the correct worfklow state.
$entity->setRevisionTranslationAffected(TRUE);
$entity->setChangedTime(time());
}
}
// Fill in title, if it isn't set.
if (method_exists($translation, 'getTitle') &&
empty($translation->getTitle())) {
$title = $entity->getTitle();
$translation->setTitle($title);
}
}
}
}
/**
* Form submission handler for node_form().
*
* Configure entity translation node object for entity_translation_unified_form.
*/
function entity_translation_unified_form_node_form_submit(array $form, FormStateInterface $form_state) {
$language = \Drupal::languageManager()->getCurrentLanguage();
$langcode = $language->getId();
$other_languages = EtufHelper::getOtherEnabledLanguages();
$form_object = $form_state->getFormObject();
$entity = NULL;
if ($form_object instanceof EntityForm) {
$entity = $form_object->getEntity();
$entity_type = $entity->getEntityTypeId();
$bundle = $entity->bundle();
$entityManager = \Drupal::service('entity_field.manager');
$fields = $entityManager->getFieldDefinitions($entity_type, $bundle);
// Process the translated values.
$values = $form_state->getValues();
if ($entity instanceof RevisionLogInterface) {
$lastRevisionLogMessage[$langcode] = $entity->getRevisionLogMessage();
}
$stateOtherLang = '';
foreach ($other_languages as $other_langcode => $other_language) {
if ($entity instanceof RevisionLogInterface) {
$lastRevisionLogMessage[$other_langcode] = $entity->revision_log->value;
}
// Get the existing translation, or create one if it doesn't currently
// exist.
if ($entity->hasTranslation($other_langcode)) {
$translation = $entity->getTranslation($other_langcode);
// Handle other language menu link translation save title.
if (isset($form['menu']['enabled']['#default_value'])) {
if ($form['menu']['enabled']['#default_value']) {
$ml_uuid = $form['menu']['link']['id']['#value'];
if (is_string($ml_uuid) && !empty($ml_uuid)) {
$storage = \Drupal::entityTypeManager()->getStorage('menu_link_content');
$uuid = str_replace('menu_link_content:', '', $ml_uuid);
$menu_link = $storage->loadByProperties(['uuid' => $uuid]);
if (is_array($menu_link)) {
$menu_link = reset($menu_link);
$menu_values = $form_state->getValue('menu');
$menu_title_other_lang = $menu_values['link-title-etuf-' . $other_langcode];
if ($menu_link && $menu_link->hasTranslation($other_langcode)) {
$menu_link = $menu_link->getTranslation($other_langcode);
$menu_link->set('title', $menu_title_other_lang);
if (!empty($menu_title_other_lang)) {
$menu_link->save();
}
}
elseif ($menu_link) {
$menu_link->addTranslation($other_langcode);
$menu_link->set('title', $menu_title_other_lang);
if (!empty($menu_title_other_lang)) {
$menu_link->save();
}
}
}
}
// No longer need the form element, remove it to avoid undefined
// notices.
}
}
}
else {
$translation = $entity->addTranslation($other_langcode);
}
foreach ($fields as $field_name => $field_definition) {
$etuf_name = EtufHelper::getEtufFieldName($field_name, $other_langcode);
$field_type = $field_definition->getType();
// Copy the translated values from form_state to the entity translation.
if (isset($values[$etuf_name])) {
if ($field_type == 'metatag' && isset($values[$etuf_name])) {
// Begin processing form for metatags into the expected format.
$metatags_from_form = $values[$etuf_name];
$metatags_reset_form = reset($metatags_from_form);
$metatags_prep = [];
foreach ($metatags_reset_form as $outer_key => $metatags_part) {
if (is_array($metatags_part)) {
foreach ($metatags_part as $key => $value) {
// Flatten metatags to key=>value.
$metatags_prep[$key] = $value;
}
}
}
// End of metatag transformation, now serialize.
$metatags_serialized = serialize($metatags_prep);
// Save the serialized metatags to the field.
$translation->set($field_name, $metatags_serialized);
}
elseif ($field_name == 'moderation_state') {
// Handle moderation_state value.
$moderationArray = reset($values[$etuf_name]);
$stateOtherLang = $moderationArray['value'];
$translation->getTranslation($other_langcode)->set($field_name, $stateOtherLang);
}
elseif ($field_name == 'created' && $field_type == 'created') {
// Fix date/time bug #3117164.
// Part 1/4 fix date/time bug #3117164, would otherwise be 1970,
// copy from source.
$dateObj = $values[$etuf_name][0]['value'];
$translation->set($field_name, $dateObj->getTimestamp());
}
elseif ($field_type == 'image' || $field_type == 'file') {
// Special treatment for managed files, not sure why? (core bug
// workaround?).
if (isset($values[$etuf_name][0]['fids'][0])) {
$numberOfFiles = count($values[$etuf_name]);
if ($numberOfFiles == 1) {
// Just one file/image to fix, otherwise seems ok.
if (!isset($values[$etuf_name][0]['target_id'])) {
$values[$etuf_name][0]['target_id'] = $values[$etuf_name][0]['fids'][0];
unset($values[$etuf_name][0]['fids']);
}
}
elseif ($numberOfFiles > 1) {
for ($i = 0; $i < $numberOfFiles; $i++) {
if (!isset($values[$etuf_name][$i]['target_id']) && isset($values[$etuf_name][$i]['fids'][0])) {
$values[$etuf_name][$i]['target_id'] = $values[$etuf_name][$i]['fids'][0];
unset($values[$etuf_name][$i]['fids']);
}
}
}
}
$translation->set($field_name, $values[$etuf_name]);
}
elseif (isset($field_name) && !is_null($translation)) {
// Default.
$translation->set($field_name, $values[$etuf_name]);
}
elseif ($field_name == 'created' && $field_type == 'created'
&& isset($values[$field_name])) {
// Fix date/time bug #3117164.
// Part 2/4 fix date/time bug #3117164, would otherwise be 1970,
// copy from source.
$dateObj = $values[$field_name][0]['value'];
if (!is_null($dateObj)) {
$translation->set($field_name, $dateObj->getTimestamp());
}
}
elseif (isset($translation) && $field_name == 'revision_timestamp') {
// Part 3/4 fix date/time bug #3117164, would otherwise be NULL.
// $values[$field_name] = \Drupal::time()->getCurrentTime();
$values[$etuf_name] = \Drupal::time()->getCurrentTime();
}
elseif (isset($translation) && $field_name == 'changed' && $field_type == 'changed') {
// Part 4/4 fix date/time bug #3117164, would otherwise be NULL.
$values[$etuf_name] = \Drupal::time()->getCurrentTime();
}
}
} // End of foreach field_definition.
if (!is_null($translation)) {
if (!empty($stateOtherLang)) {
if ($entity instanceof RevisionLogInterface) {
// Retrieve the most recent revision log message.
$lastRevisionLogMessage[$other_langcode] = $translation->getRevisionLogMessage();
}
// Set a new revision.
$translation->getTranslation($other_langcode)->setNewRevision(TRUE);
$entity->getTranslation($langcode)->set('moderation_state', $stateOtherLang);
$translation->getTranslation($other_langcode)->set('moderation_state', $stateOtherLang);
if ($translation instanceof RevisionLogInterface) {
$current_uid = \Drupal::currentUser()->id();
$translation->getTranslation($other_langcode)->setRevisionCreationTime(\Drupal::time()->getRequestTime());
if (empty($lastRevisionLogMessage[$other_langcode])) {
$revisionLogMsg = $lastRevisionLogMessage[$langcode];
}
else {
$revisionLogMsg = $lastRevisionLogMessage[$other_langcode];
}
$translation->getTranslation($other_langcode)->setRevisionLogMessage($revisionLogMsg);
$translation->getTranslation($other_langcode)->setRevisionUserId($current_uid);
}
// We must set this flag otherwise the translated revisions do not
// get the correct worfklow state.
// Content Moderation is enabled.
$translation->setRevisionTranslationAffected(TRUE);
$translation->setChangedTime(time());
}
drupal_register_shutdown_function('entity_translation_unified_form_custom_shutdown', $translation, $other_langcode, $stateOtherLang, $langcode);
}
if (!isset($once_only)) {
$once_only = TRUE;
if (!empty($stateOtherLang)) {
// Content Moderation is enabled.
// We must set this flag otherwise the translated revisions do not
// get the correct worfklow state.
$entity->setRevisionTranslationAffected(TRUE);
$entity->setChangedTime(time());
}
}
// Fill in title, if it isn't set.
if (method_exists($translation, 'getTitle') &&
empty($translation->getTitle())) {
$title = $entity->getTitle();
$translation->setTitle($title);
}
}
}
}
/**
* Ensures the previous database transaction is committed after shutdown.
*
* This function makes sure the moderation state is processed correctly.
* The main purpose is to create or update an alias for the other language
* record if it's different from the existing one.
*/
function entity_translation_unified_form_custom_shutdown_menu($entity, $langcode) {
$language = \Drupal::languageManager()->getLanguage($langcode);
$options = [
'absolute' => TRUE,
'language' => $language,
];
$absolute_base_url = Url::fromRoute('<front>', [], $options)->toString();
$base_url = \Drupal::request()->getBaseUrl();
$pos_base = stripos($absolute_base_url, $base_url);
$desired_base_url = substr($absolute_base_url, $pos_base);
$base_url = $desired_base_url;
if ($entity->hasTranslation($langcode)) {
$entity = $entity->getTranslation($langcode);
}
$e_type_id = $entity->getEntityTypeId();
$path = \Drupal::service('path_alias.manager')->getAliasByPath("/$e_type_id/" . $entity->id(), $langcode);
// Ensure base_url is used, to make this solution work for everyone.
$path = $base_url . $path;
$url = $entity->toUrl('canonical', [], $options)->toString();
if (!is_string($url)) {
$url = $path;
}
else {
// Ensure base_url is used, to make this solution work for everyone.
// $base_url . $url;.
$url = $url;
}
$debug = FALSE;
EtufHelper::addToLog('path:' . $path, $debug);
EtufHelper::addToLog('url:' . $url, $debug);
// Because $path doesn't have this and we need to compare.
$url = str_replace('/' . $langcode . '/', '/', $url);
// Just in case, to compare path and url, give same treatment.
$path = str_replace('/' . $langcode . '/', '/', $path);
$test_alias = ($path == "/$e_type_id/" . $entity->id()) ? TRUE : FALSE;
$vid = 0;
$latest_revision = entity_translation_unified_form_etuf_latest_revision($entity->id(), $vid, $langcode, $entity->getEntityTypeId());
// Ensure the alias gets updated.
if ($test_alias || $url != $path) {
if ($test_alias) {
\Drupal::service('pathauto.generator')->updateEntityAlias($latest_revision, 'insert', ['language' => $langcode]);
}
elseif ($url != $path) {
\Drupal::service('pathauto.generator')->updateEntityAlias($latest_revision, 'update', ['language' => $langcode]);
}
}
// Test plan with results.
// New draft:.
// select id, revision_id, langcode, path, alias from path_alias where path
// like '%node/3910%';
// 9911 9911 en /node/3910 /example/english-title-1.
// 9912 9912 fr /node/3910 /exemple/francais-titre-1.
// Edit above draft changing title which is part of the path auto pattern:.
// select id, revision_id, langcode, path, alias from path_alias where path
// like '%node/3910%';
// 9911 9911 en /node/3910 /example/english-title-2.
// 9912 9912 fr /node/3910 /exemple/francais-titre-2.
// Conclusion: this code prevents duplicate alias entries and ensures all
// translations get their alias.
}
/**
* Ensures the previous database transaction is committed after shutdown.
*
* This function makes sure the moderation state is processed correctly.
*/
function entity_translation_unified_form_custom_shutdown($entity, $langcode, $moderation_state, $interfacelang) {
$vid = 0;
$latest_revision = entity_translation_unified_form_etuf_latest_revision($entity->id(), $vid, $langcode, $entity->getEntityTypeId());
if (!empty($moderation_state)) {
$latest_is_valid = TRUE;
if ($latest_revision == FALSE) {
$latest_is_valid = FALSE;
}
if ($latest_is_valid) {
$latest_revision->setSyncing(TRUE);
$latest_revision->setRevisionTranslationAffected(TRUE);
$latest_revision->set('moderation_state', $moderation_state);
$latest_revision->save();
}
}
drupal_register_shutdown_function('\Drupal\entity_translation_unified_form\EtufHelper::postCreateOrUpdateAutoTranslate', $entity->id(), $langcode);
if ($latest_revision == FALSE) {
drupal_register_shutdown_function('entity_translation_unified_form_custom_shutdown_menu', $latest_revision, $langcode);
}
else {
drupal_register_shutdown_function('entity_translation_unified_form_custom_shutdown_menu', $entity, $langcode);
}
}
/**
* Register the theme_wrappers.
*
* Implements hook_theme().
*/
function entity_translation_unified_form_theme() {
return [
'entity_translation_unified_form__inline__field_wrapper' => [
'render element' => 'element',
'file' => 'entity_translation_unified_form.theme.inc',
],
'entity_translation_unified_form__inline__wrapper' => [
'render element' => 'element',
'file' => 'entity_translation_unified_form.theme.inc',
],
'entity_translation_unified_form__a11y_accordion_tabs__field_wrapper' => [
'render element' => 'element',
'file' => 'entity_translation_unified_form.theme.inc',
],
'entity_translation_unified_form__a11y_accordion_tabs__wrapper' => [
'render element' => 'element',
'file' => 'entity_translation_unified_form.theme.inc',
],
];
}
/**
* Retrieve the latest node revision of $lang.
*/
function entity_translation_unified_form_etuf_latest_revision($id, &$vid, $lang, $entity_type = 'node') {
$query = \Drupal::entityTypeManager()->getStorage($entity_type)->getQuery();
$query->latestRevision()
->accessCheck(TRUE);
if ($entity_type == 'node') {
$query->condition('nid', $id, '=');
}
if ($entity_type == 'taxonomy_term') {
$query->condition('tid', $id, '=');
}
if ($entity_type == 'paragraph') {
$query->condition('id', $id, '=');
}
if ($entity_type == 'user') {
// Likely no moderation states for user entities anyway but just in case.
$query->condition('uid', $id, '=');
}
$latestRevisionResult = $query->execute();
if (count($latestRevisionResult)) {
$node_revision_id = key($latestRevisionResult);
$vid = $node_revision_id;
$latestRevision = \Drupal::entityTypeManager()->getStorage($entity_type)->loadRevision($node_revision_id);
if ($latestRevision->hasTranslation($lang) && $latestRevision->language()->getId() != $lang) {
$latestRevision = $latestRevision->getTranslation($lang);
}
return $latestRevision;
}
return FALSE;
}
/**
* Returns true if save only button is enable for a given bundle.
*/
function entity_translation_unified_form_save_only_button($entity_type_id, $bundle) {
/** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */
$content_translation_manager = \Drupal::service('content_translation.manager');
if ($content_translation_manager instanceof BundleTranslationSettingsInterface) {
$settings = $content_translation_manager->getBundleTranslationSettings($entity_type_id, $bundle);
return !empty($settings['entity_translation_unified_form_save_only_button']);
}
return FALSE;
}
/**
* Form submit handler for save only.
*/
function entity_translation_unified_form_entity_translation_unified_page_submit_save_only(array $form, FormStateInterface $form_state) {
$entity = $form_state->getFormObject()->getEntity();
$node_id = $entity->id();
$current_url = \Drupal::request()->getUri();
$destination = '/edit\?destination=/';
if (preg_match($destination, $current_url)) {
\Drupal::request()->query->remove('destination');
}
$form_state->setRedirectUrl(Url::fromRoute('entity.node.edit_form', ['node' => $node_id]));
}
/**
* Implements hook_library_info_alter().
*
* Remove node/node.preview.js which installs dialog on almost all links.
* However it is not necessary for some links like language switch.
*/
function entity_translation_unified_form_library_info_alter(&$libraries, $extension) {
if ($extension === 'node' && isset($libraries['drupal.node.preview'])) {
// Remove the JS file in the core preview library.
$libraries['drupal.node.preview']['js'] = [];
}
}
