diff --git a/core/modules/outside_in/outside_in.module b/core/modules/outside_in/outside_in.module index a7fab7b..8af7df3 100644 --- a/core/modules/outside_in/outside_in.module +++ b/core/modules/outside_in/outside_in.module @@ -10,6 +10,7 @@ use Drupal\outside_in\Block\BlockEntityOffCanvasForm; use Drupal\outside_in\Form\SystemBrandingOffCanvasForm; use Drupal\outside_in\Form\SystemMenuOffCanvasForm; +use Drupal\block\Entity\Block; /** * Implements hook_help(). @@ -41,6 +42,17 @@ function outside_in_contextual_links_view_alter(&$element, $items) { 'data-outside-in-edit' => TRUE, ]; + $block_id = $element['#contextual_links']['block']['route_parameters']['block']; + $block = Block::load($block_id); + $lang = $block->language(); + $current_lang = Drupal::languageManager()->getCurrentLanguage(); + /** @var \Drupal\Core\Url $url */ + $url = $element['#links']['outside-inblock-configure']['url']; + $query = $url->getOption('query'); + $query['config_lang'] = $current_lang->getId(); + $url->setOption('query', $query); + $element['#links']['outside-inblock-configure']['options']['query']['config_lang'] = $current_lang->getId(); + $element['#attached']['library'][] = 'outside_in/drupal.off_canvas'; } } diff --git a/core/modules/outside_in/src/Block/BlockEntityOffCanvasForm.php b/core/modules/outside_in/src/Block/BlockEntityOffCanvasForm.php index a77f51f..526c094 100644 --- a/core/modules/outside_in/src/Block/BlockEntityOffCanvasForm.php +++ b/core/modules/outside_in/src/Block/BlockEntityOffCanvasForm.php @@ -4,9 +4,12 @@ use Drupal\block\BlockForm; use Drupal\block\BlockInterface; +use Drupal\config_translation\Exception\ConfigMapperLanguageException; use Drupal\Core\Block\BlockPluginInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginWithFormsInterface; +use Drupal\Core\Routing\RouteMatch; +use Drupal\Core\Url; /** * Provides form for block instance forms when used in the off-canvas tray. @@ -37,11 +40,10 @@ public function title(BlockInterface $block) { public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); + $this->addTranslationElements($form, $form_state); + // Create link to full block form. - $query = []; - if ($destination = $this->getRequest()->query->get('destination')) { - $query['destination'] = $destination; - } + $query = \Drupal::destination()->getAsArray(); $form['advanced_link'] = [ '#type' => 'link', '#title' => $this->t('Advanced options'), @@ -97,4 +99,172 @@ protected function getPluginForm(BlockPluginInterface $block) { return $block; } + /** + * Add translation related elements if needed. + * + * @todo This code is mostly copied from + * \Drupal\config_translation\Controller\ConfigTranslationController + * clean up. + * + * @param $form + * @param $form_state + */ + protected function addTranslationElements(&$form, $form_state) { + if (!\Drupal::moduleHandler()->moduleExists('config_translation')) { + return; + } + + $lang_manager = \Drupal::languageManager(); + $languages = $lang_manager->getLanguages(); + if (count($languages) == 1) { + // No need to translate. + return; + } + + $current_lang = \Drupal::request()->get('config_lang'); + $mapper = $this->getConfigEntityMapper($current_lang); + + try { + $original_langcode = $mapper->getLangcode(); + $operations_access = TRUE; + } + catch (ConfigMapperLanguageException $exception) { + $items = []; + foreach ($mapper->getConfigNames() as $config_name) { + $langcode = $mapper->getLangcodeFromConfig($config_name); + $items[] = $this->t('@name: @langcode', [ + '@name' => $config_name, + '@langcode' => $langcode, + ]); + } + $message = [ + 'message' => ['#markup' => $this->t('The configuration objects have different language codes so they cannot be translated:')], + 'items' => [ + '#theme' => 'item_list', + '#items' => $items, + ], + ]; + drupal_set_message($message, 'warning'); + + return; + } + + // We create a fake request object to pass into + // ConfigMapperInterface::populateFromRouteMatch() for the different languages. + // Creating a separate request for each language and route is neither easily + // possible nor performant. + $fake_request = \Drupal::request()->duplicate(); + + $current_lang = $lang_manager->getLanguage($current_lang); + + $viewing_translation = FALSE; + $query = \Drupal::destination()->getAsArray(); + if ($mapper->hasTranslation($current_lang)) { + $viewing_translation = TRUE; + + + $form['translation_edit'] = [ + '#type' => 'link', + '#title' => $this->t('This page is currently using the @lang translation.', ['@lang' => $current_lang->getName()]), + '#url' => Url::fromRoute($mapper->getEditRouteName(), $mapper->getEditRouteParameters(), ['query' => $query]), + '#attributes' => $this->getOffCanvasAttributes(), + ]; + + } + $form['languages_details'] = [ + '#type' => 'details', + '#title' => $this->t('Translations'), + '#open' => $viewing_translation, + ]; + + $form['languages_details']['languages'] = [ + '#type' => 'table', + '#header' => [$this->t('Language'), $this->t('Operations')], + ]; + foreach ($languages as $language) { + $langcode = $language->getId(); + + // This is needed because + // ConfigMapperInterface::getAddRouteParameters(), for example, + // needs to return the correct language code for each table row. + $fake_route_match = RouteMatch::createFromRequest($fake_request); + $mapper->populateFromRouteMatch($fake_route_match); + $mapper->setLangcode($langcode); + + // Prepare the language name and the operations depending on whether this + // is the original language or not. + if ($langcode !== $original_langcode) { + $language_name = $language->getName(); + + $operations = []; + // If no translation exists for this language, link to add one. + if (!$mapper->hasTranslation($language)) { + $operations['add'] = [ + 'title' => $this->t('Add'), + 'url' => Url::fromRoute($mapper->getAddRouteName(), $mapper->getAddRouteParameters(), ['query' => $query]), + 'attributes' => $this->getOffCanvasAttributes(), + ]; + } + else { + // Otherwise, link to edit the existing translation. + $operations['edit'] = [ + 'title' => $this->t('Edit'), + 'url' => Url::fromRoute($mapper->getEditRouteName(), $mapper->getEditRouteParameters(), ['query' => $query]), + 'attributes' => $this->getOffCanvasAttributes(), + ]; + + $operations['delete'] = [ + 'title' => $this->t('Delete'), + 'url' => Url::fromRoute($mapper->getDeleteRouteName(), $mapper->getDeleteRouteParameters(), ['query' => $query]), + 'attributes' => $this->getOffCanvasAttributes(), + ]; + } + $form['languages_details']['languages'][$langcode]['language'] = [ + '#markup' => $language_name, + ]; + + $form['languages_details']['languages'][$langcode]['operations'] = [ + '#type' => 'operations', + '#links' => $operations, + // Even if the mapper contains multiple language codes, the source + // configuration can still be edited. + '#access' => ($langcode == $original_langcode) || $operations_access, + ]; + } + + + } + } + + /** + * Get the translation config mapper for the block + * + * @param $current_lang + * @return \Drupal\config_translation\ConfigEntityMapper + */ + protected function getConfigEntityMapper($current_lang) { + /** @var \Drupal\config_translation\ConfigEntityMapper $mapper */ + $mapper = \Drupal::service('plugin.manager.config_translation.mapper') + ->createInstance('block'); + $mapper->setLangcode($current_lang); + $mapper->setEntity($this->entity); + return $mapper; + } + + /** + * Get attributes for off-canvas links. + * + * @todo Move this in a utility class so .module file can use it. + * + * @return array + * The attributes. + */ + protected function getOffCanvasAttributes() { + return [ + 'class' => ['use-ajax'], + 'data-dialog-type' => 'dialog', + 'data-dialog-renderer' => 'offcanvas', + ]; + } + }