/**
  * Checks access to the overview based on permissions and translatability.
  *
  * @param \Symfony\Component\Routing\Route $route
  *   The route to check against.
  * @param \Drupal\Core\Session\AccountInterface $account
  *   The currently logged in account.
  *
  * @return \Drupal\Core\Access\AccessResultInterface
  *   The access result.
  */
 public function access(Route $route, AccountInterface $account)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($route->getDefault('plugin_id'));
     $this->sourceLanguage = $this->languageManager->getLanguage($mapper->getLangcode());
     // Allow access to the translation overview if the proper permission is
     // granted, the configuration has translatable pieces, and the source
     // language is not locked if it is present.
     $source_language_access = is_null($this->sourceLanguage) || !$this->sourceLanguage->isLocked();
     $access = $account->hasPermission('translate configuration') && $mapper->hasSchema() && $mapper->hasTranslatable() && $source_language_access;
     return AccessResult::allowedIf($access)->cachePerRole();
 }
 /**
  * Checks access to the overview based on permissions and translatability.
  *
  * @param \Symfony\Component\Routing\Route $route
  *   The route to check against.
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object.
  * @param \Drupal\Core\Session\AccountInterface $account
  *   The currently logged in account.
  *
  * @return string
  *   A \Drupal\Core\Access\AccessInterface constant value.
  */
 public function access(Route $route, Request $request, AccountInterface $account)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($route->getDefault('plugin_id'));
     $mapper->populateFromRequest($request);
     $this->sourceLanguage = $mapper->getLanguageWithFallback();
     // Allow access to the translation overview if the proper permission is
     // granted, the configuration has translatable pieces, and the source
     // language is not locked.
     $access = $account->hasPermission('translate configuration') && $mapper->hasSchema() && $mapper->hasTranslatable() && !$this->sourceLanguage->locked;
     return $access ? static::ALLOW : static::DENY;
 }
 /**
  * Provides the listing page for any entity type.
  *
  * @param string $mapper_id
  *   The name of the mapper.
  *
  * @return array
  *   A render array as expected by drupal_render().
  *
  * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  *   Throws an exception if a mapper plugin could not be instantiated from the
  *   mapper definition in the constructor.
  */
 public function listing($mapper_id)
 {
     $mapper_definition = $this->mapperManager->getDefinition($mapper_id);
     $mapper = $this->mapperManager->createInstance($mapper_id, $mapper_definition);
     if (!$mapper) {
         throw new NotFoundHttpException();
     }
     $entity_type = $mapper->getType();
     // If the mapper, for example the mapper for fields, has a custom list
     // controller defined, use it. Other mappers, for examples the ones for
     // node_type and block, fallback to the generic configuration translation
     // list controller.
     $build = $this->entityManager()->getHandler($entity_type, 'config_translation_list')->setMapperDefinition($mapper_definition)->render();
     $build['#title'] = $mapper->getTypeLabel();
     return $build;
 }
 /**
  * Language translations overview page for a configuration name.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   Page request object.
  * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  *   The route match.
  * @param string $plugin_id
  *   The plugin ID of the mapper.
  *
  * @return array
  *   Page render array.
  */
 public function itemPage(Request $request, RouteMatchInterface $route_match, $plugin_id)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRequest($request);
     $page = array();
     $page['#title'] = $this->t('Translations for %label', array('%label' => $mapper->getTitle()));
     $languages = $this->languageManager->getLanguages();
     if (count($languages) == 1) {
         drupal_set_message($this->t('In order to translate configuration, the website must have at least two <a href="@url">languages</a>.', array('@url' => $this->url('entity.configurable_language.collection'))), 'warning');
     }
     $original_langcode = $mapper->getLangcode();
     if (!isset($languages[$original_langcode])) {
         // If the language is not configured on the site, create a dummy language
         // object for this listing only to ensure the user gets useful info.
         $language_name = $this->languageManager->getLanguageName($original_langcode);
         $languages[$original_langcode] = new Language(array('id' => $original_langcode, 'name' => $language_name));
     }
     // We create a fake request object to pass into
     // ConfigMapperInterface::populateFromRequest() for the different languages.
     // Creating a separate request for each language and route is neither easily
     // possible nor performant.
     $fake_request = $request->duplicate();
     $page['languages'] = array('#type' => 'table', '#header' => array($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_request->attributes->set('langcode', $langcode);
         $mapper->populateFromRequest($fake_request);
         // Prepare the language name and the operations depending on whether this
         // is the original language or not.
         if ($langcode == $original_langcode) {
             $language_name = '<strong>' . $this->t('@language (original)', array('@language' => $language->getName())) . '</strong>';
             // Check access for the path/route for editing, so we can decide to
             // include a link to edit or not.
             $edit_access = $this->accessManager->checkNamedRoute($mapper->getBaseRouteName(), $route_match->getRawParameters()->all(), $this->account);
             // Build list of operations.
             $operations = array();
             if ($edit_access) {
                 $operations['edit'] = array('title' => $this->t('Edit'), 'url' => Url::fromRoute($mapper->getBaseRouteName(), $mapper->getBaseRouteParameters(), ['query' => ['destination' => $mapper->getOverviewPath()]]));
             }
         } else {
             $language_name = $language->getName();
             $operations = array();
             // If no translation exists for this language, link to add one.
             if (!$mapper->hasTranslation($language)) {
                 $operations['add'] = array('title' => $this->t('Add'), 'url' => Url::fromRoute($mapper->getAddRouteName(), $mapper->getAddRouteParameters()));
             } else {
                 // Otherwise, link to edit the existing translation.
                 $operations['edit'] = array('title' => $this->t('Edit'), 'url' => Url::fromRoute($mapper->getEditRouteName(), $mapper->getEditRouteParameters()));
                 $operations['delete'] = array('title' => $this->t('Delete'), 'url' => Url::fromRoute($mapper->getDeleteRouteName(), $mapper->getDeleteRouteParameters()));
             }
         }
         $page['languages'][$langcode]['language'] = array('#markup' => $language_name);
         $page['languages'][$langcode]['operations'] = array('#type' => 'operations', '#links' => $operations);
     }
     return $page;
 }
 /**
  * {@inheritdoc}
  */
 public function getTranslateRoute()
 {
     // @todo There should be some way for Config Translation module to alter
     //   this information in on its own.
     if ($this->mapperManager) {
         $entity_type = 'menu_link_config';
         /** @var \Drupal\menu_link_config\MenuLinkConfigMapper $mapper */
         $mapper = $this->mapperManager->createInstance($entity_type);
         $mapper->setEntity($this->getEntity());
         return array('route_name' => $mapper->getOverviewRouteName(), 'route_parameters' => $mapper->getOverviewRouteParameters());
     }
 }
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, array &$form_state, Request $request = NULL, $plugin_id = NULL, $langcode = NULL)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRequest($request);
     $language = language_load($langcode);
     if (!$language) {
         throw new NotFoundHttpException();
     }
     $this->mapper = $mapper;
     $this->language = $language;
     return parent::buildForm($form, $form_state);
 }
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, RouteMatchInterface $route_match = NULL, $plugin_id = NULL, $langcode = NULL)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRouteMatch($route_match);
     $language = $this->languageManager->getLanguage($langcode);
     if (!$language) {
         throw new NotFoundHttpException();
     }
     $this->mapper = $mapper;
     $this->language = $language;
     return parent::buildForm($form, $form_state);
 }
 /**
  * Implements \Drupal\Core\Form\FormInterface::buildForm().
  *
  * Builds configuration form with metadata and values from the source
  * language.
  *
  * @param array $form
  *   An associative array containing the structure of the form.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  *   (optional) The route match.
  * @param string $plugin_id
  *   (optional) The plugin ID of the mapper.
  * @param string $langcode
  *   (optional) The language code of the language the form is adding or
  *   editing.
  *
  * @return array
  *   The form structure.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  *   Throws an exception if the language code provided as a query parameter in
  *   the request does not match an active language.
  */
 public function buildForm(array $form, FormStateInterface $form_state, RouteMatchInterface $route_match = NULL, $plugin_id = NULL, $langcode = NULL)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRouteMatch($route_match);
     $language = $this->languageManager->getLanguage($langcode);
     if (!$language) {
         throw new NotFoundHttpException();
     }
     $this->mapper = $mapper;
     $this->language = $language;
     // ConfigTranslationFormAccess will not grant access if this raises an
     // exception, so we can call this without a try-catch block here.
     $langcode = $this->mapper->getLangcode();
     $this->sourceLanguage = $this->languageManager->getLanguage($langcode);
     // Get base language configuration to display in the form before setting the
     // language to use for the form. This avoids repetitively settings and
     // resetting the language to get original values later.
     $this->baseConfigData = $this->mapper->getConfigData();
     // Set the translation target language on the configuration factory.
     $original_language = $this->languageManager->getConfigOverrideLanguage();
     $this->languageManager->setConfigOverrideLanguage($this->language);
     // Add some information to the form state for easier form altering.
     $form_state->set('config_translation_mapper', $this->mapper);
     $form_state->set('config_translation_language', $this->language);
     $form_state->set('config_translation_source_language', $this->sourceLanguage);
     $form['#attached']['library'][] = 'config_translation/drupal.config_translation.admin';
     // Even though this is a nested form, we do not set #tree to TRUE because
     // the form value structure is generated by using #parents for each element.
     // @see \Drupal\config_translation\FormElement\FormElementBase::getElements()
     $form['config_names'] = array('#type' => 'container');
     foreach ($this->mapper->getConfigNames() as $name) {
         $form['config_names'][$name] = array('#type' => 'container');
         $schema = $this->typedConfigManager->get($name);
         $source_config = $this->baseConfigData[$name];
         $translation_config = $this->configFactory()->get($name)->get();
         if ($form_element = $this->createFormElement($schema)) {
             $parents = array('config_names', $name);
             $form['config_names'][$name] += $form_element->getTranslationBuild($this->sourceLanguage, $this->language, $source_config, $translation_config, $parents);
         }
     }
     $form['actions']['#type'] = 'actions';
     $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save translation'), '#button_type' => 'primary');
     // Set the configuration language back.
     $this->languageManager->setConfigOverrideLanguage($original_language);
     return $form;
 }
 /**
  * Implements \Drupal\Core\Form\FormInterface::buildForm().
  *
  * Builds configuration form with metadata and values from the source
  * language.
  *
  * @param array $form
  *   An associative array containing the structure of the form.
  * @param array $form_state
  *   An associative array containing the current state of the form.
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   (optional) Page request object.
  * @param string $plugin_id
  *   (optional) The plugin ID of the mapper.
  * @param string $langcode
  *   (optional) The language code of the language the form is adding or
  *   editing.
  *
  * @return array
  *   The form structure.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  *   Throws an exception if the language code provided as a query parameter in
  *   the request does not match an active language.
  */
 public function buildForm(array $form, array &$form_state, Request $request = NULL, $plugin_id = NULL, $langcode = NULL)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRequest($request);
     $language = language_load($langcode);
     if (!$language) {
         throw new NotFoundHttpException();
     }
     $this->mapper = $mapper;
     $this->language = $language;
     $this->sourceLanguage = $this->mapper->getLanguageWithFallback();
     // Get base language configuration to display in the form before setting the
     // language to use for the form. This avoids repetitively settings and
     // resetting the language to get original values later.
     $config_factory = $this->configFactory();
     $old_state = $config_factory->getOverrideState();
     $config_factory->setOverrideState(FALSE);
     $this->baseConfigData = $this->mapper->getConfigData();
     $config_factory->setOverrideState($old_state);
     // Set the translation target language on the configuration factory.
     $original_language = $this->languageManager->getConfigOverrideLanguage();
     $this->languageManager->setConfigOverrideLanguage($this->language);
     // Add some information to the form state for easier form altering.
     $form_state['config_translation_mapper'] = $this->mapper;
     $form_state['config_translation_language'] = $this->language;
     $form_state['config_translation_source_language'] = $this->sourceLanguage;
     $form['#attached']['library'][] = 'config_translation/drupal.config_translation.admin';
     $form['config_names'] = array('#type' => 'container', '#tree' => TRUE);
     foreach ($this->mapper->getConfigNames() as $name) {
         $form['config_names'][$name] = array('#type' => 'container');
         $form['config_names'][$name] += $this->buildConfigForm($this->typedConfigManager->get($name), $config_factory->get($name)->get(), $this->baseConfigData[$name]);
     }
     $form['actions']['#type'] = 'actions';
     $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save translation'), '#button_type' => 'primary');
     // Set the configuration language back.
     $this->languageManager->setConfigOverrideLanguage($original_language);
     return $form;
 }
Example #10
0
 /**
  * Gets the mapper.
  *
  * @param \Drupal\tmgmt\JobItemInterface $job_item
  *   Gets a job item as a parameter.
  *
  * @return \Drupal\config_translation\ConfigMapperInterface $config_mapper
  *   Returns the config mapper.
  *
  * @throws \Drupal\tmgmt\TMGMTException
  *   If there is no entity, throws an exception.
  */
 protected function getMapper(JobItemInterface $job_item)
 {
     // @todo: Inject dependencies.
     if ($job_item->getItemType() == static::SIMPLE_CONFIG) {
         $config_mapper = $this->configMapperManager->createInstance($job_item->getItemId());
     } else {
         $config_mapper = $this->configMapperManager->createInstance($job_item->getItemType());
         /** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type */
         $entity_type = $this->entityManager->getDefinition($config_mapper->getType());
         $pos = strpos($job_item->getItemId(), $entity_type->getConfigPrefix());
         if ($pos !== FALSE) {
             $entity_id = str_replace($entity_type->getConfigPrefix() . '.', '', $job_item->getItemId());
         } else {
             throw new TMGMTException(t('Item ID does not contain the full config object name.'));
         }
         $entity = $this->entityManager->getStorage($config_mapper->getType())->load($entity_id);
         if (!$entity) {
             throw new TMGMTException(t('Unable to load entity %type with id %id', array('%type' => $job_item->getItemType(), '%id' => $entity_id)));
         }
         $config_mapper->setEntity($entity);
     }
     return $config_mapper;
 }
 /**
  * Language translations overview page for a configuration name.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   Page request object.
  * @param string $plugin_id
  *   The plugin ID of the mapper.
  *
  * @return array
  *   Page render array.
  */
 public function itemPage(Request $request, $plugin_id)
 {
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRequest($request);
     $page = array();
     $page['#title'] = $this->t('Translations for %label', array('%label' => $mapper->getTitle()));
     // It is possible the original language this configuration was saved with is
     // not on the system. For example, the configuration shipped in English but
     // the site has no English configured. Represent the original language in
     // the table even if it is not currently configured.
     $languages = $this->languageManager->getLanguages();
     $original_langcode = $mapper->getLangcode();
     if (!isset($languages[$original_langcode])) {
         $language_name = $this->languageManager->getLanguageName($original_langcode);
         if ($original_langcode == 'en') {
             $language_name = $this->t('Built-in English');
         }
         // Create a dummy language object for this listing only.
         $languages[$original_langcode] = new Language(array('id' => $original_langcode, 'name' => $language_name));
     }
     // We create a fake request object to pass into
     // ConfigMapperInterface::populateFromRequest() for the different languages.
     // Creating a separate request for each language and route is neither easily
     // possible nor performant.
     $fake_request = $request->duplicate();
     $page['languages'] = array('#type' => 'table', '#header' => array($this->t('Language'), $this->t('Operations')));
     foreach ($languages as $language) {
         $langcode = $language->id;
         // This is needed because
         // ConfigMapperInterface::getAddRouteParameters(), for example,
         // needs to return the correct language code for each table row.
         $fake_request->attributes->set('langcode', $langcode);
         $mapper->populateFromRequest($fake_request);
         // Prepare the language name and the operations depending on whether this
         // is the original language or not.
         if ($langcode == $original_langcode) {
             $language_name = '<strong>' . $this->t('@language (original)', array('@language' => $language->name)) . '</strong>';
             // Check access for the path/route for editing, so we can decide to
             // include a link to edit or not.
             $route_request = $this->getRequestForPath($request, $mapper->getBasePath());
             $edit_access = FALSE;
             if (!empty($route_request)) {
                 $route_name = $route_request->attributes->get(RouteObjectInterface::ROUTE_NAME);
                 // Note that the parameters don't really matter here since we're
                 // passing in the request which already has the upcast attributes.
                 $parameters = array();
                 $edit_access = $this->accessManager->checkNamedRoute($route_name, $parameters, $this->account, $route_request);
             }
             // Build list of operations.
             $operations = array();
             if ($edit_access) {
                 $operations['edit'] = array('title' => $this->t('Edit'), 'route_name' => $mapper->getBaseRouteName(), 'route_parameters' => $mapper->getBaseRouteParameters(), 'query' => array('destination' => $mapper->getOverviewPath()));
             }
         } else {
             $language_name = $language->name;
             $operations = array();
             // If no translation exists for this language, link to add one.
             if (!$mapper->hasTranslation($language)) {
                 $operations['add'] = array('title' => $this->t('Add'), 'route_name' => $mapper->getAddRouteName(), 'route_parameters' => $mapper->getAddRouteParameters());
             } else {
                 // Otherwise, link to edit the existing translation.
                 $operations['edit'] = array('title' => $this->t('Edit'), 'route_name' => $mapper->getEditRouteName(), 'route_parameters' => $mapper->getEditRouteParameters());
                 $operations['delete'] = array('title' => $this->t('Delete'), 'route_name' => $mapper->getDeleteRouteName(), 'route_parameters' => $mapper->getDeleteRouteParameters());
             }
         }
         $page['languages'][$langcode]['language'] = array('#markup' => $language_name);
         $page['languages'][$langcode]['operations'] = array('#type' => 'operations', '#links' => $operations);
     }
     return $page;
 }
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, array $build = NULL, $plugin_id = NULL)
 {
     // Store the entity in the form state so we can easily create the job in the
     // submit handler.
     $mapper_definition = \Drupal::service('plugin.manager.config_translation.mapper')->getDefinition($plugin_id);
     /** @var \Drupal\config_translation\ConfigMapperInterface $mapper */
     $mapper = $this->configMapperManager->createInstance($plugin_id);
     $mapper->populateFromRouteMatch($this->routeMatch);
     $form_state->set('mapper', $mapper);
     if (!isset($mapper_definition['entity_type'])) {
         $form_state->set('item_type', ConfigSource::SIMPLE_CONFIG);
         $form_state->set('item_id', $mapper_definition['id']);
     } else {
         $id = $mapper->getConfigNames()[0];
         $form_state->set('id', $id);
         $form_state->set('item_type', $plugin_id);
         $form_state->set('item_id', $id);
     }
     $form['#title'] = $this->t('Translations of @title', array('@title' => $mapper->getTitle()));
     $overview = $build['languages'];
     $form['top_actions'] = array('#type' => 'details', '#title' => t('Operations'), '#open' => TRUE, '#attributes' => array('class' => array('tmgmt-source-operations-wrapper')));
     $form['top_actions']['request'] = array('#type' => 'submit', '#button_type' => 'primary', '#value' => $this->t('Request translation'), '#submit' => array('::submitForm'));
     tmgmt_add_cart_form($form['top_actions'], $form_state, 'config', $form_state->get('item_type'), $form_state->get('item_id'));
     // Inject our additional column into the header.
     array_splice($overview['#header'], -1, 0, array(t('Pending Translations')));
     // Make this a tableselect form.
     $form['languages'] = array('#type' => 'tableselect', '#header' => $overview['#header'], '#options' => array());
     $languages = \Drupal::languageManager()->getLanguages();
     // Check if there is a job / job item that references this translation.
     $items = tmgmt_job_item_load_latest('config', $form_state->get('item_type'), $form_state->get('item_id'), $mapper->getLangcode());
     foreach ($languages as $langcode => $language) {
         if ($langcode == LanguageInterface::LANGCODE_DEFAULT) {
             // Never show language neutral on the overview.
             continue;
         }
         // Since the keys are numeric and in the same order we can shift one element
         // after the other from the original non-form rows.
         $option = $overview[$langcode];
         if ($langcode == $mapper->getLangcode()) {
             $additional = array('data' => array('#markup' => '<strong>' . t('Source') . '</strong>'));
             // This is the source object so we disable the checkbox for this row.
             $form['languages'][$langcode] = array('#type' => 'checkbox', '#disabled' => TRUE);
         } elseif (isset($items[$langcode])) {
             $item = $items[$langcode];
             $states = JobItem::getStates();
             $additional = \Drupal::l($states[$item->getState()], $item->urlInfo()->setOption('query', array('destination' => Url::fromRoute('<current>')->getInternalPath())));
             // Disable the checkbox for this row since there is already a translation
             // in progress that has not yet been finished. This way we make sure that
             // we don't stack multiple active translations for the same item on top
             // of each other.
             $form['languages'][$langcode] = array('#type' => 'checkbox', '#disabled' => TRUE);
         } else {
             // There is no translation job / job item for this target language.
             $additional = t('None');
         }
         // Inject the additional column into the array.
         // The generated form structure has changed, support both an additional
         // 'data' key (that is not supported by tableselect) and the old version
         // without.
         if (isset($option['data'])) {
             array_splice($option['data'], -1, 0, array($additional));
             // Append the current option array to the form.
             $form['languages']['#options'][$langcode] = $option['data'];
         } else {
             array_splice($option, -1, 0, array($additional));
             // Append the current option array to the form.
             $form['languages']['#options'][$langcode] = array(drupal_render($option['language']), $additional, drupal_render($option['operations']));
         }
     }
     return $form;
 }
 /**
  * Gets a configuration mapper using a route match.
  *
  * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  *   The route match to populate the mapper with.
  *
  * @return \Drupal\config_translation\ConfigMapperInterface
  *   The configuration mapper.
  */
 protected function getMapperFromRouteMatch(RouteMatchInterface $route_match)
 {
     $mapper = $this->configMapperManager->createInstance($route_match->getRouteObject()->getDefault('plugin_id'));
     $mapper->populateFromRouteMatch($route_match);
     return $mapper;
 }