',
@@ -273,8 +280,8 @@ class BlockForm extends EntityForm {
/**
* {@inheritdoc}
*/
- public function validate(array $form, FormStateInterface $form_state) {
- parent::validate($form, $form_state);
+ public function validateForm(array &$form, FormStateInterface $form_state) {
+ parent::validateForm($form, $form_state);
// The Block Entity form puts all block plugin form elements in the
// settings form element, so just pass that to the block for validation.
diff --git a/core/modules/block/src/BlockInterface.php b/core/modules/block/src/BlockInterface.php
index 39ad9a9bf..8955be18d 100644
--- a/core/modules/block/src/BlockInterface.php
+++ b/core/modules/block/src/BlockInterface.php
@@ -95,24 +95,6 @@ interface BlockInterface extends ConfigEntityInterface {
*/
public function setVisibilityConfig($instance_id, array $configuration);
- /**
- * Get all available contexts.
- *
- * @return \Drupal\Component\Plugin\Context\ContextInterface[]
- * An array of set contexts, keyed by context name.
- */
- public function getContexts();
-
- /**
- * Set the contexts that are available for use within the block entity.
- *
- * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
- * An array of contexts to set on the block.
- *
- * @return $this
- */
- public function setContexts(array $contexts);
-
/**
* Returns the weight of this block (used for sorting).
*
diff --git a/core/modules/block/src/BlockListBuilder.php b/core/modules/block/src/BlockListBuilder.php
index aa669fa71..43d7a1c7a 100644
--- a/core/modules/block/src/BlockListBuilder.php
+++ b/core/modules/block/src/BlockListBuilder.php
@@ -8,15 +8,16 @@
namespace Drupal\block;
use Drupal\Component\Utility\Html;
-use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -28,13 +29,6 @@ use Symfony\Component\HttpFoundation\Request;
*/
class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface {
- /**
- * The regions containing the blocks.
- *
- * @var array
- */
- protected $regions;
-
/**
* The theme containing the blocks.
*
@@ -50,11 +44,23 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
protected $request;
/**
- * The block manager.
+ * The theme manager.
*
- * @var \Drupal\Core\Block\BlockManagerInterface
+ * @var \Drupal\Core\Theme\ThemeManagerInterface
*/
- protected $blockManager;
+ protected $themeManager;
+
+ /**
+ * The form builder.
+ *
+ * @var \Drupal\Core\Form\FormBuilderInterface
+ */
+ protected $formBuilder;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $limit = FALSE;
/**
* Constructs a new BlockListBuilder object.
@@ -63,13 +69,16 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
* The entity type definition.
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The entity storage class.
- * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
- * The block manager.
+ * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
+ * The theme manager.
+ * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
+ * The form builder.
*/
- public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, BlockManagerInterface $block_manager) {
+ public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, ThemeManagerInterface $theme_manager, FormBuilderInterface $form_builder) {
parent::__construct($entity_type, $storage);
- $this->blockManager = $block_manager;
+ $this->themeManager = $theme_manager;
+ $this->formBuilder = $form_builder;
}
/**
@@ -79,31 +88,11 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
return new static(
$entity_type,
$container->get('entity.manager')->getStorage($entity_type->id()),
- $container->get('plugin.manager.block')
+ $container->get('theme.manager'),
+ $container->get('form_builder')
);
}
- /**
- * {@inheritdoc}
- */
- public function load() {
- // If no theme was specified, use the current theme.
- if (!$this->theme) {
- $this->theme = \Drupal::theme()->getActiveTheme()->getName();
- }
-
- // Store the region list.
- $this->regions = system_region_list($this->theme, REGIONS_VISIBLE);
-
- // Load only blocks for this theme, and sort them.
- // @todo Move the functionality of _block_rehash() out of the listing page.
- $entities = _block_rehash($this->theme);
-
- // Sort the blocks using \Drupal\block\Entity\Block::sort().
- uasort($entities, array($this->entityType->getClass(), 'sort'));
- return $entities;
- }
-
/**
* {@inheritdoc}
*
@@ -118,10 +107,9 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
*/
public function render($theme = NULL, Request $request = NULL) {
$this->request = $request;
- // If no theme was specified, use the current theme.
- $this->theme = $theme ?: \Drupal::theme()->getActiveTheme()->getName();
+ $this->theme = $theme;
- return \Drupal::formBuilder()->getForm($this);
+ return $this->formBuilder->getForm($this);
}
/**
@@ -132,55 +120,40 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
}
/**
- * Implements \Drupal\Core\Form\FormInterface::buildForm().
- *
- * Form constructor for the main block administration form.
+ * {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
- $placement = FALSE;
- if ($this->request->query->has('block-placement')) {
- $placement = $this->request->query->get('block-placement');
- $form['#attached']['drupalSettings']['blockPlacement'] = $placement;
- }
- $entities = $this->load();
- $form['#theme'] = array('block_list');
$form['#attached']['library'][] = 'core/drupal.tableheader';
$form['#attached']['library'][] = 'block/drupal.block';
$form['#attached']['library'][] = 'block/drupal.block.admin';
$form['#attributes']['class'][] = 'clearfix';
- // Add a last region for disabled blocks.
- $block_regions_with_disabled = $this->regions + array(BlockInterface::BLOCK_REGION_NONE => BlockInterface::BLOCK_REGION_NONE);
- $form['block_regions'] = array(
- '#type' => 'value',
- '#value' => $block_regions_with_disabled,
- );
-
- // Weights range from -delta to +delta, so delta should be at least half
- // of the amount of blocks present. This makes sure all blocks in the same
- // region get an unique weight.
- $weight_delta = round(count($entities) / 2);
-
// Build the form tree.
- $form['edited_theme'] = array(
- '#type' => 'value',
- '#value' => $this->theme,
+ $form['blocks'] = $this->buildBlocksForm();
+
+ $form['actions'] = array(
+ '#tree' => FALSE,
+ '#type' => 'actions',
);
- $form['blocks'] = array(
- '#type' => 'table',
- '#header' => array(
- t('Block'),
- t('Category'),
- t('Region'),
- t('Weight'),
- t('Operations'),
- ),
- '#attributes' => array(
- 'id' => 'blocks',
- ),
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => $this->t('Save blocks'),
+ '#button_type' => 'primary',
);
+ return $form;
+ }
+
+ /**
+ * Builds the main "Blocks" portion of the form.
+ *
+ * @return array
+ */
+ protected function buildBlocksForm() {
// Build blocks first for each region.
+ $blocks = [];
+ $entities = $this->load();
+ /** @var \Drupal\block\BlockInterface[] $entities */
foreach ($entities as $entity_id => $entity) {
$definition = $entity->getPlugin()->getPluginDefinition();
$blocks[$entity->getRegion()][$entity_id] = array(
@@ -192,36 +165,73 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
);
}
+ $form = array(
+ '#type' => 'table',
+ '#header' => array(
+ $this->t('Block'),
+ $this->t('Category'),
+ $this->t('Region'),
+ $this->t('Weight'),
+ $this->t('Operations'),
+ ),
+ '#attributes' => array(
+ 'id' => 'blocks',
+ ),
+ );
+
+ // Weights range from -delta to +delta, so delta should be at least half
+ // of the amount of blocks present. This makes sure all blocks in the same
+ // region get an unique weight.
+ $weight_delta = round(count($entities) / 2);
+
+ $placement = FALSE;
+ if ($this->request->query->has('block-placement')) {
+ $placement = $this->request->query->get('block-placement');
+ $form['#attached']['drupalSettings']['blockPlacement'] = $placement;
+ }
+
// Loop over each region and build blocks.
+ $regions = $this->systemRegionList($this->getThemeName(), REGIONS_VISIBLE);
+ $block_regions_with_disabled = $regions + array(BlockInterface::BLOCK_REGION_NONE => $this->t('Disabled', array(), array('context' => 'Plural')));
foreach ($block_regions_with_disabled as $region => $title) {
- $form['blocks']['#tabledrag'][] = array(
+ $form['#tabledrag'][] = array(
'action' => 'match',
'relationship' => 'sibling',
'group' => 'block-region-select',
'subgroup' => 'block-region-' . $region,
'hidden' => FALSE,
);
- $form['blocks']['#tabledrag'][] = array(
+ $form['#tabledrag'][] = array(
'action' => 'order',
'relationship' => 'sibling',
'group' => 'block-weight',
'subgroup' => 'block-weight-' . $region,
);
- $form['blocks'][$region] = array(
+ $form['region-' . $region] = array(
'#attributes' => array(
'class' => array('region-title', 'region-title-' . $region),
'no_striping' => TRUE,
),
);
- $form['blocks'][$region]['title'] = array(
- '#markup' => $region != BlockInterface::BLOCK_REGION_NONE ? $title : t('Disabled', array(), array('context' => 'Plural')),
+ $form['region-' . $region]['title'] = array(
+ '#prefix' => $region != BlockInterface::BLOCK_REGION_NONE ? $title : $block_regions_with_disabled[$region],
+ '#type' => 'link',
+ '#title' => $this->t('Place block
in the %region region ', ['%region' => $block_regions_with_disabled[$region]]),
+ '#url' => Url::fromRoute('block.admin_library', ['theme' => $this->getThemeName()], ['query' => ['region' => $region]]),
'#wrapper_attributes' => array(
'colspan' => 5,
),
+ '#attributes' => [
+ 'class' => ['use-ajax', 'button', 'button--small'],
+ 'data-dialog-type' => 'modal',
+ 'data-dialog-options' => Json::encode([
+ 'width' => 700,
+ ]),
+ ],
);
- $form['blocks'][$region . '-message'] = array(
+ $form['region-' . $region . '-message'] = array(
'#attributes' => array(
'class' => array(
'region-message',
@@ -230,8 +240,8 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
),
),
);
- $form['blocks'][$region . '-message']['message'] = array(
- '#markup' => '
' . t('No blocks in this region') . ' ',
+ $form['region-' . $region . '-message']['message'] = array(
+ '#markup' => '
' . $this->t('No blocks in this region') . ' ',
'#wrapper_attributes' => array(
'colspan' => 5,
),
@@ -241,137 +251,87 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
foreach ($blocks[$region] as $info) {
$entity_id = $info['entity_id'];
- $form['blocks'][$entity_id] = array(
+ $form[$entity_id] = array(
'#attributes' => array(
'class' => array('draggable'),
),
);
if ($placement && $placement == Html::getClass($entity_id)) {
- $form['blocks'][$entity_id]['#attributes']['class'][] = 'color-warning';
- $form['blocks'][$entity_id]['#attributes']['class'][] = 'js-block-placed';
+ $form[$entity_id]['#attributes']['class'][] = 'color-warning';
+ $form[$entity_id]['#attributes']['class'][] = 'js-block-placed';
}
- $form['blocks'][$entity_id]['info'] = array(
+ $form[$entity_id]['info'] = array(
'#markup' => SafeMarkup::checkPlain($info['label']),
'#wrapper_attributes' => array(
'class' => array('block'),
),
);
- $form['blocks'][$entity_id]['type'] = array(
+ $form[$entity_id]['type'] = array(
'#markup' => $info['category'],
);
- $form['blocks'][$entity_id]['region-theme']['region'] = array(
+ $form[$entity_id]['region-theme']['region'] = array(
'#type' => 'select',
'#default_value' => $region,
'#empty_value' => BlockInterface::BLOCK_REGION_NONE,
- '#title' => t('Region for @block block', array('@block' => $info['label'])),
+ '#title' => $this->t('Region for @block block', array('@block' => $info['label'])),
'#title_display' => 'invisible',
- '#options' => $this->regions,
+ '#options' => $regions,
'#attributes' => array(
'class' => array('block-region-select', 'block-region-' . $region),
),
'#parents' => array('blocks', $entity_id, 'region'),
);
- $form['blocks'][$entity_id]['region-theme']['theme'] = array(
+ $form[$entity_id]['region-theme']['theme'] = array(
'#type' => 'hidden',
- '#value' => $this->theme,
+ '#value' => $this->getThemeName(),
'#parents' => array('blocks', $entity_id, 'theme'),
);
- $form['blocks'][$entity_id]['weight'] = array(
+ $form[$entity_id]['weight'] = array(
'#type' => 'weight',
'#default_value' => $info['weight'],
'#delta' => $weight_delta,
- '#title' => t('Weight for @block block', array('@block' => $info['label'])),
+ '#title' => $this->t('Weight for @block block', array('@block' => $info['label'])),
'#title_display' => 'invisible',
'#attributes' => array(
'class' => array('block-weight', 'block-weight-' . $region),
),
);
- $form['blocks'][$entity_id]['operations'] = $this->buildOperations($info['entity']);
+ $form[$entity_id]['operations'] = $this->buildOperations($info['entity']);
}
}
}
// Do not allow disabling the main system content block when it is present.
- if (isset($form['blocks']['system_main']['region'])) {
- $form['blocks']['system_main']['region']['#required'] = TRUE;
- }
-
- $form['actions'] = array(
- '#tree' => FALSE,
- '#type' => 'actions',
- );
- $form['actions']['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Save blocks'),
- '#button_type' => 'primary',
- );
-
- $form['place_blocks']['title'] = array(
- '#type' => 'container',
- '#markup' => '
' . t('Place blocks') . ' ',
- '#attributes' => array(
- 'class' => array(
- 'entity-meta__header',
- ),
- ),
- );
-
- $form['place_blocks']['filter'] = array(
- '#type' => 'search',
- '#title' => t('Filter'),
- '#title_display' => 'invisible',
- '#size' => 30,
- '#placeholder' => t('Filter by block name'),
- '#attributes' => array(
- 'class' => array('block-filter-text'),
- 'data-element' => '.entity-meta',
- 'title' => t('Enter a part of the block name to filter by.'),
- ),
- );
-
- $form['place_blocks']['list']['#type'] = 'container';
- $form['place_blocks']['list']['#attributes']['class'][] = 'entity-meta';
-
- // Only add blocks which work without any available context.
- $definitions = $this->blockManager->getDefinitionsForContexts();
- $sorted_definitions = $this->blockManager->getSortedDefinitions($definitions);
- foreach ($sorted_definitions as $plugin_id => $plugin_definition) {
- $category = SafeMarkup::checkPlain($plugin_definition['category']);
- $category_key = 'category-' . $category;
- if (!isset($form['place_blocks']['list'][$category_key])) {
- $form['place_blocks']['list'][$category_key] = array(
- '#type' => 'details',
- '#title' => $category,
- '#open' => TRUE,
- 'content' => array(
- '#theme' => 'links',
- '#links' => array(),
- '#attributes' => array(
- 'class' => array(
- 'block-list',
- ),
- ),
- ),
- );
- }
- $form['place_blocks']['list'][$category_key]['content']['#links'][$plugin_id] = array(
- 'title' => $plugin_definition['admin_label'],
- 'url' => Url::fromRoute('block.admin_add', [
- 'plugin_id' => $plugin_id,
- 'theme' => $this->theme
- ]),
- 'attributes' => array(
- 'class' => array('use-ajax', 'block-filter-text-source'),
- 'data-dialog-type' => 'modal',
- 'data-dialog-options' => Json::encode(array(
- 'width' => 700,
- )),
- ),
- );
+ if (isset($form['system_main']['region'])) {
+ $form['system_main']['region']['#required'] = TRUE;
}
return $form;
}
+ /**
+ * Gets the name of the theme used for this block listing.
+ *
+ * @return string
+ * The name of the theme.
+ */
+ protected function getThemeName() {
+ // If no theme was specified, use the current theme.
+ if (!$this->theme) {
+ $this->theme = $this->themeManager->getActiveTheme()->getName();
+ }
+ return $this->theme;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getEntityIds() {
+ return $this->getStorage()->getQuery()
+ ->condition('theme', $this->getThemeName())
+ ->sort($this->entityType->getKey('id'))
+ ->execute();
+ }
+
/**
* {@inheritdoc}
*/
@@ -379,26 +339,25 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
$operations = parent::getDefaultOperations($entity);
if (isset($operations['edit'])) {
- $operations['edit']['title'] = t('Configure');
+ $operations['edit']['title'] = $this->t('Configure');
}
return $operations;
}
/**
- * Implements \Drupal\Core\Form\FormInterface::validateForm().
+ * {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// No validation.
}
/**
- * Implements \Drupal\Core\Form\FormInterface::submitForm().
- *
- * Form submission handler for the main block administration form.
+ * {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$entities = $this->storage->loadMultiple(array_keys($form_state->getValue('blocks')));
+ /** @var \Drupal\block\BlockInterface[] $entities */
foreach ($entities as $entity_id => $entity) {
$entity_values = $form_state->getValue(array('blocks', $entity_id));
$entity->setWeight($entity_values['weight']);
@@ -417,4 +376,11 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface
$this->request->query->remove('block-placement');
}
+ /**
+ * Wraps system_region_list().
+ */
+ protected function systemRegionList($theme, $show = REGIONS_ALL) {
+ return system_region_list($theme, $show);
+ }
+
}
diff --git a/core/modules/block/src/BlockPluginCollection.php b/core/modules/block/src/BlockPluginCollection.php
index c1905a794..b757d2322 100644
--- a/core/modules/block/src/BlockPluginCollection.php
+++ b/core/modules/block/src/BlockPluginCollection.php
@@ -9,7 +9,6 @@ namespace Drupal\block;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Plugin\PluginManagerInterface;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
/**
@@ -56,7 +55,7 @@ class BlockPluginCollection extends DefaultSingleLazyPluginCollection {
*/
protected function initializePlugin($instance_id) {
if (!$instance_id) {
- throw new PluginException(SafeMarkup::format("The block '@block' did not specify a plugin.", array('@block' => $this->blockId)));
+ throw new PluginException("The block '{$this->blockId}' did not specify a plugin.");
}
try {
diff --git a/core/modules/block/src/BlockRepository.php b/core/modules/block/src/BlockRepository.php
index 980e0adb8..9d56fde84 100644
--- a/core/modules/block/src/BlockRepository.php
+++ b/core/modules/block/src/BlockRepository.php
@@ -7,6 +7,7 @@
namespace Drupal\block;
+use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
@@ -49,7 +50,7 @@ class BlockRepository implements BlockRepositoryInterface {
/**
* {@inheritdoc}
*/
- public function getVisibleBlocksPerRegion(array $contexts) {
+ public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []) {
$active_theme = $this->themeManager->getActiveTheme();
// Build an array of the region names in the right order.
$empty = array_fill_keys($active_theme->getRegions(), array());
@@ -57,9 +58,18 @@ class BlockRepository implements BlockRepositoryInterface {
$full = array();
foreach ($this->blockStorage->loadByProperties(array('theme' => $active_theme->getName())) as $block_id => $block) {
/** @var \Drupal\block\BlockInterface $block */
+ $access = $block->access('view', NULL, TRUE);
+ $region = $block->getRegion();
+ if (!isset($cacheable_metadata[$region])) {
+ $cacheable_metadata[$region] = CacheableMetadata::createFromObject($access);
+ }
+ else {
+ $cacheable_metadata[$region] = $cacheable_metadata[$region]->merge(CacheableMetadata::createFromObject($access));
+ }
+
// Set the contexts on the block before checking access.
- if ($block->setContexts($contexts)->access('view')) {
- $full[$block->getRegion()][$block_id] = $block;
+ if ($access->isAllowed()) {
+ $full[$region][$block_id] = $block;
}
}
diff --git a/core/modules/block/src/BlockRepositoryInterface.php b/core/modules/block/src/BlockRepositoryInterface.php
index 082456f0d..59dc26012 100644
--- a/core/modules/block/src/BlockRepositoryInterface.php
+++ b/core/modules/block/src/BlockRepositoryInterface.php
@@ -12,13 +12,14 @@ interface BlockRepositoryInterface {
/**
* Returns an array of regions and their block entities.
*
- * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
- * An array of contexts to set on the blocks.
+ * @param \Drupal\Core\Cache\CacheableMetadata[] $cacheable_metadata
+ * (optional) List of CacheableMetadata objects, keyed by region. This is
+ * by reference and is used to pass this information back to the caller.
*
* @return array
* The array is first keyed by region machine name, with the values
* containing an array keyed by block ID, with block entities as the values.
*/
- public function getVisibleBlocksPerRegion(array $contexts);
+ public function getVisibleBlocksPerRegion(array &$cacheable_metadata = []);
}
diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index a68b6eb89..187692de5 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -48,6 +48,9 @@ class BlockViewBuilder extends EntityViewBuilder {
$derivative_id = $plugin->getDerivativeId();
$configuration = $plugin->getConfiguration();
+ $cache_tags = Cache::mergeTags($this->getCacheTags(), $entity->getCacheTags());
+ $cache_tags = Cache::mergeTags($cache_tags, $plugin->getCacheTags());
+
// Create the render array for the block as a whole.
// @see template_preprocess_block().
$build[$entity_id] = array(
@@ -67,12 +70,11 @@ class BlockViewBuilder extends EntityViewBuilder {
'#id' => $entity->id(),
'#cache' => [
'keys' => ['entity_view', 'block', $entity->id()],
- 'contexts' => $plugin->getCacheContexts(),
- 'tags' => Cache::mergeTags(
- $this->getCacheTags(), // Block view builder cache tag.
- $entity->getCacheTags(), // Block entity cache tag.
- $plugin->getCacheTags() // Block plugin cache tags.
+ 'contexts' => Cache::mergeContexts(
+ $entity->getCacheContexts(),
+ $plugin->getCacheContexts()
),
+ 'tags' => $cache_tags,
'max-age' => $plugin->getCacheMaxAge(),
],
'#pre_render' => [
diff --git a/core/modules/block/src/Controller/BlockLibraryController.php b/core/modules/block/src/Controller/BlockLibraryController.php
new file mode 100644
index 000000000..2a0148fda
--- /dev/null
+++ b/core/modules/block/src/Controller/BlockLibraryController.php
@@ -0,0 +1,179 @@
+blockManager = $block_manager;
+ $this->routeMatch = $route_match;
+ $this->localActionManager = $local_action_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('plugin.manager.block'),
+ $container->get('current_route_match'),
+ $container->get('plugin.manager.menu.local_action')
+ );
+ }
+
+ /**
+ * Shows a list of blocks that can be added to a theme's layout.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The current request.
+ * @param string $theme
+ * Theme key of the block list.
+ *
+ * @return array
+ * A render array as expected by the renderer.
+ */
+ public function listBlocks(Request $request, $theme) {
+ // Since modals do not render any other part of the page, we need to render
+ // them manually as part of this listing.
+ if ($request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT) === 'drupal_modal') {
+ $build['local_actions'] = $this->buildLocalActions();
+ }
+
+ $headers = [
+ ['data' => $this->t('Block')],
+ ['data' => $this->t('Category')],
+ ['data' => $this->t('Operations')],
+ ];
+
+ // Only add blocks which work without any available context.
+ $definitions = $this->blockManager->getDefinitionsForContexts();
+ // Order by category, and then by admin label.
+ $definitions = $this->blockManager->getSortedDefinitions($definitions);
+
+ $region = $request->query->get('region');
+ $rows = [];
+ foreach ($definitions as $plugin_id => $plugin_definition) {
+ $row = [];
+ $row['title']['data'] = [
+ '#markup' => $plugin_definition['admin_label'],
+ '#prefix' => '
',
+ '#suffix' => '
',
+ ];
+ $row['category']['data'] = SafeMarkup::checkPlain($plugin_definition['category']);
+ $links['add'] = [
+ 'title' => $this->t('Place block'),
+ 'url' => Url::fromRoute('block.admin_add', ['plugin_id' => $plugin_id, 'theme' => $theme]),
+ 'attributes' => [
+ 'class' => ['use-ajax'],
+ 'data-dialog-type' => 'modal',
+ 'data-dialog-options' => Json::encode([
+ 'width' => 700,
+ ]),
+ ],
+ ];
+ if ($region) {
+ $links['add']['query']['region'] = $region;
+ }
+ $row['operations']['data'] = [
+ '#type' => 'operations',
+ '#links' => $links,
+ ];
+ $rows[] = $row;
+ }
+
+ $build['#attached']['library'][] = 'block/drupal.block.admin';
+
+ $build['filter'] = [
+ '#type' => 'search',
+ '#title' => $this->t('Filter'),
+ '#title_display' => 'invisible',
+ '#size' => 30,
+ '#placeholder' => $this->t('Filter by block name'),
+ '#attributes' => [
+ 'class' => ['block-filter-text'],
+ 'data-element' => '.block-add-table',
+ 'title' => $this->t('Enter a part of the block name to filter by.'),
+ ],
+ ];
+
+ $build['blocks'] = [
+ '#type' => 'table',
+ '#header' => $headers,
+ '#rows' => $rows,
+ '#empty' => $this->t('No blocks available.'),
+ '#attributes' => [
+ 'class' => ['block-add-table'],
+ ],
+ ];
+
+ return $build;
+ }
+
+ /**
+ * Builds the local actions for this listing.
+ *
+ * @return array
+ * An array of local actions for this listing.
+ */
+ protected function buildLocalActions() {
+ $build = $this->localActionManager->getActionsForRoute($this->routeMatch->getRouteName());
+ // Without this workaround, the action links will be rendered as
with
+ // no wrapping element.
+ if (!empty($build)) {
+ $build['#prefix'] = '';
+ $build['#suffix'] = ' ';
+ }
+ return $build;
+ }
+
+}
diff --git a/core/modules/block/src/Entity/Block.php b/core/modules/block/src/Entity/Block.php
index 948d29370..d2b303199 100644
--- a/core/modules/block/src/Entity/Block.php
+++ b/core/modules/block/src/Entity/Block.php
@@ -246,25 +246,10 @@ class Block extends ConfigEntityBase implements BlockInterface, EntityWithPlugin
// so we must invalidate the associated block's cache tag (which includes
// the theme cache tag).
if (!$update) {
- Cache::invalidateTags($this->getCacheTags());
+ Cache::invalidateTags($this->getCacheTagsToInvalidate());
}
}
- /**
- * {@inheritdoc}
- */
- public function setContexts(array $contexts) {
- $this->contexts = $contexts;
- return $this;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getContexts() {
- return $this->contexts;
- }
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/block/src/Event/BlockContextEvent.php b/core/modules/block/src/Event/BlockContextEvent.php
deleted file mode 100644
index 99b0bbdde..000000000
--- a/core/modules/block/src/Event/BlockContextEvent.php
+++ /dev/null
@@ -1,53 +0,0 @@
-contexts[$name] = $context;
- return $this;
- }
-
- /**
- * Returns the context objects.
- *
- * @return \Drupal\Component\Plugin\Context\ContextInterface[]
- * An array of contexts that have been provided.
- */
- public function getContexts() {
- return $this->contexts;
- }
-
-}
diff --git a/core/modules/block/src/Event/BlockEvents.php b/core/modules/block/src/Event/BlockEvents.php
deleted file mode 100644
index 118f2f8ef..000000000
--- a/core/modules/block/src/Event/BlockEvents.php
+++ /dev/null
@@ -1,55 +0,0 @@
-setContextValue($node);
- * $event->setContext('node.node', $context);
- * @endcode
- *
- * @param \Drupal\block\Event\BlockContextEvent $event
- * The Event to which to register available contexts.
- */
- abstract public function onBlockActiveContext(BlockContextEvent $event);
-
- /**
- * Determines the available configuration-time contexts.
- *
- * When a block is being configured, the configuration UI must know which
- * named contexts are potentially available, but does not care about the
- * value, since the value can be different for each request, and might not
- * be available at all during the configuration UI's request.
- *
- * For example:
- * @code
- * // During configuration, there is no specific node to pass as context.
- * // However, inform the system that a context named 'node.node' is
- * // available, and provide its definition, so that blocks can be
- * // configured to use it. When the block is rendered, the value of this
- * // context will be supplied by onBlockActiveContext().
- * $context = new Context(new ContextDefinition('entity:node'));
- * $event->setContext('node.node', $context);
- * @endcode
- *
- * @param \Drupal\block\Event\BlockContextEvent $event
- * The Event to which to register available contexts.
- *
- * @see static::onBlockActiveContext()
- */
- abstract public function onBlockAdministrativeContext(BlockContextEvent $event);
-
-}
diff --git a/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php b/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php
index cbc0a73df..c91bc7ce1 100644
--- a/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php
+++ b/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php
@@ -69,7 +69,7 @@ class ThemeLocalTask extends DeriverBase implements ContainerDeriverInterface {
}
// Default task!
if ($default_theme == $theme_name) {
- $this->derivatives[$theme_name]['route_name'] = 'block.admin_display';
+ $this->derivatives[$theme_name]['route_name'] = $base_plugin_definition['parent_id'];
// Emulate default logic because without the base plugin id we can't
// change the base_route.
$this->derivatives[$theme_name]['weight'] = -10;
diff --git a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
index 7b6709372..650cf7834 100644
--- a/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
+++ b/core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php
@@ -8,17 +8,14 @@
namespace Drupal\block\Plugin\DisplayVariant;
use Drupal\block\BlockRepositoryInterface;
-use Drupal\block\Event\BlockContextEvent;
-use Drupal\block\Event\BlockEvents;
use Drupal\Core\Block\MainContentBlockPluginInterface;
use Drupal\Core\Block\MessagesBlockPluginInterface;
+use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Display\PageVariantInterface;
-use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityViewBuilderInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Display\VariantBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Provides a page display variant that decorates the main content with blocks.
@@ -79,16 +76,13 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
* The block repository.
* @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder
* The block view builder.
- * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
- * The event dispatcher.
* @param string[] $block_list_cache_tags
* The Block entity type list cache tags.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, EventDispatcherInterface $dispatcher, array $block_list_cache_tags) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, array $block_list_cache_tags) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->blockRepository = $block_repository;
$this->blockViewBuilder = $block_view_builder;
- $this->dispatcher = $dispatcher;
$this->blockListCacheTags = $block_list_cache_tags;
}
@@ -102,7 +96,6 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
$plugin_definition,
$container->get('block.repository'),
$container->get('entity.manager')->getViewBuilder('block'),
- $container->get('event_dispatcher'),
$container->get('entity.manager')->getDefinition('block')->getListCacheTags()
);
}
@@ -128,9 +121,9 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
'tags' => $this->blockListCacheTags,
],
];
- $contexts = $this->getActiveBlockContexts();
// Load all region content assigned via blocks.
- foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts) as $region => $blocks) {
+ $cacheable_metadata_list = [];
+ foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata_list) as $region => $blocks) {
/** @var $blocks \Drupal\block\BlockInterface[] */
foreach ($blocks as $key => $block) {
$block_plugin = $block->getPlugin();
@@ -172,17 +165,18 @@ class BlockPageVariant extends VariantBase implements PageVariantInterface, Cont
];
}
+ // The access results' cacheability is currently added to the top level of the
+ // render array. This is done to prevent issues with empty regions being
+ // displayed.
+ // This would need to be changed to allow caching of block regions, as each
+ // region must then have the relevant cacheable metadata.
+ $merged_cacheable_metadata = CacheableMetadata::createFromRenderArray($build);
+ foreach ($cacheable_metadata_list as $cacheable_metadata) {
+ $merged_cacheable_metadata = $merged_cacheable_metadata->merge($cacheable_metadata);
+ }
+ $merged_cacheable_metadata->applyTo($build);
+
return $build;
}
- /**
- * Returns an array of context objects to set on the blocks.
- *
- * @return \Drupal\Component\Plugin\Context\ContextInterface[]
- * An array of contexts to set on the blocks.
- */
- protected function getActiveBlockContexts() {
- return $this->dispatcher->dispatch(BlockEvents::ACTIVE_CONTEXT, new BlockContextEvent())->getContexts();
- }
-
}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockPluginId.php b/core/modules/block/src/Plugin/migrate/process/d6/BlockPluginId.php
similarity index 95%
rename from core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockPluginId.php
rename to core/modules/block/src/Plugin/migrate/process/d6/BlockPluginId.php
index 01854bd90..55cbd9686 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockPluginId.php
+++ b/core/modules/block/src/Plugin/migrate/process/d6/BlockPluginId.php
@@ -2,10 +2,10 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Plugin\migrate\process\d6\BlockPluginId.
+ * Contains \Drupal\block\Plugin\migrate\process\d6\BlockPluginId.
*/
-namespace Drupal\migrate_drupal\Plugin\migrate\process\d6;
+namespace Drupal\block\Plugin\migrate\process\d6;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockRegion.php b/core/modules/block/src/Plugin/migrate/process/d6/BlockRegion.php
similarity index 86%
rename from core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockRegion.php
rename to core/modules/block/src/Plugin/migrate/process/d6/BlockRegion.php
index 0f61515e5..672645b3d 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/process/d6/BlockRegion.php
+++ b/core/modules/block/src/Plugin/migrate/process/d6/BlockRegion.php
@@ -1,12 +1,12 @@
set('block_test.cache_contexts', []);
$current_content = $this->randomMachineName();
@@ -134,9 +136,12 @@ class BlockCacheTest extends WebTestBase {
$current_content = $this->randomMachineName();
\Drupal::state()->set('block_test.content', $current_content);
- $this->drupalLogout();
$this->drupalGet('user');
$this->assertText($old_content, 'Block content served from cache.');
+
+ $this->drupalLogout();
+ $this->drupalGet('user');
+ $this->assertText($current_content, 'Block content not served from cache.');
}
/**
diff --git a/core/modules/block/src/Tests/BlockLanguageCacheTest.php b/core/modules/block/src/Tests/BlockLanguageCacheTest.php
index 45f6803d1..a11bdd627 100644
--- a/core/modules/block/src/Tests/BlockLanguageCacheTest.php
+++ b/core/modules/block/src/Tests/BlockLanguageCacheTest.php
@@ -62,6 +62,7 @@ class BlockLanguageCacheTest extends WebTestBase {
// Create the block cache for all languages.
foreach ($this->langcodes as $langcode) {
$this->drupalGet('admin/structure/block', array('language' => $langcode));
+ $this->clickLinkPartialName('Place block');
}
// Create a menu in the default language.
@@ -73,6 +74,7 @@ class BlockLanguageCacheTest extends WebTestBase {
// Check that the block is listed for all languages.
foreach ($this->langcodes as $langcode) {
$this->drupalGet('admin/structure/block', array('language' => $langcode));
+ $this->clickLinkPartialName('Place block');
$this->assertText($edit['label']);
}
}
diff --git a/core/modules/block/src/Tests/BlockLanguageTest.php b/core/modules/block/src/Tests/BlockLanguageTest.php
index db908b0d0..265524f90 100644
--- a/core/modules/block/src/Tests/BlockLanguageTest.php
+++ b/core/modules/block/src/Tests/BlockLanguageTest.php
@@ -90,6 +90,7 @@ class BlockLanguageTest extends WebTestBase {
'langcodes' => array(
'fr' => 'fr',
),
+ 'context_mapping' => ['language' => '@language.current_language_context:language_interface'],
),
),
);
@@ -141,7 +142,7 @@ class BlockLanguageTest extends WebTestBase {
// Enable a standard block and set visibility to French only.
$block_id = strtolower($this->randomMachineName(8));
$edit = [
- 'visibility[language][context_mapping][language]' => 'language.language_interface',
+ 'visibility[language][context_mapping][language]' => '@language.current_language_context:language_interface',
'visibility[language][langcodes][fr]' => TRUE,
'id' => $block_id,
'region' => 'sidebar_first',
@@ -167,7 +168,7 @@ class BlockLanguageTest extends WebTestBase {
// Change visibility to now depend on content language for this block.
$edit = [
- 'visibility[language][context_mapping][language]' => 'language.language_content'
+ 'visibility[language][context_mapping][language]' => '@language.current_language_context:language_content'
];
$this->drupalPostForm('admin/structure/block/manage/' . $block_id, $edit, t('Save block'));
diff --git a/core/modules/block/src/Tests/BlockSystemBrandingTest.php b/core/modules/block/src/Tests/BlockSystemBrandingTest.php
index 57ebc857f..a78f67511 100644
--- a/core/modules/block/src/Tests/BlockSystemBrandingTest.php
+++ b/core/modules/block/src/Tests/BlockSystemBrandingTest.php
@@ -53,6 +53,14 @@ class BlockSystemBrandingTest extends BlockTestBase {
$this->assertTrue(!empty($site_slogan_element), 'The branding block slogan was found.');
$this->assertCacheTag('config:system.site');
+ // Be sure the slogan is XSS-filtered.
+ $this->config('system.site')
+ ->set('slogan', '')
+ ->save();
+ $this->drupalGet('');
+ $site_slogan_element = $this->xpath($site_slogan_xpath);
+ $this->assertEqual($site_slogan_element[0], 'alert("Community carpentry");', 'The site slogan was XSS-filtered.');
+
// Turn just the logo off.
$this->config('block.block.site-branding')
->set('settings.use_site_logo', 0)
diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php
index 8c65bc303..5ab46e13c 100644
--- a/core/modules/block/src/Tests/BlockTest.php
+++ b/core/modules/block/src/Tests/BlockTest.php
@@ -334,11 +334,13 @@ class BlockTest extends BlockTestBase {
'config:block_list',
'block_view',
'config:block.block.powered',
+ 'config:user.role.anonymous',
'rendered',
);
sort($expected_cache_tags);
+ $keys = \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:language_interface', 'theme', 'user.permissions'])->getKeys();
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
- $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:en:classy');
+ $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:' . implode(':', $keys));
$expected_cache_tags = array(
'block_view',
'config:block.block.powered',
@@ -373,6 +375,7 @@ class BlockTest extends BlockTestBase {
'block_view',
'config:block.block.powered',
'config:block.block.powered-2',
+ 'config:user.role.anonymous',
'rendered',
);
sort($expected_cache_tags);
@@ -383,7 +386,8 @@ class BlockTest extends BlockTestBase {
'rendered',
);
sort($expected_cache_tags);
- $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:en:classy');
+ $keys = \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:language_interface', 'theme', 'user.permissions'])->getKeys();
+ $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:' . implode(':', $keys));
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
$expected_cache_tags = array(
'block_view',
@@ -391,7 +395,8 @@ class BlockTest extends BlockTestBase {
'rendered',
);
sort($expected_cache_tags);
- $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered-2:en:classy');
+ $keys = \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:language_interface', 'theme', 'user.permissions'])->getKeys();
+ $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered-2:' . implode(':', $keys));
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
// Now we should have a cache hit again.
diff --git a/core/modules/block/src/Tests/BlockTitleXSSTest.php b/core/modules/block/src/Tests/BlockTitleXSSTest.php
deleted file mode 100644
index 3790d56d1..000000000
--- a/core/modules/block/src/Tests/BlockTitleXSSTest.php
+++ /dev/null
@@ -1,46 +0,0 @@
-drupalPlaceBlock('test_xss_title', array('label' => ''));
- }
-
- /**
- * Test XSS in title.
- */
- function testXSSInTitle() {
- \Drupal::state()->set('block_test.content', $this->randomMachineName());
- $this->drupalGet('');
- $this->assertNoRaw('', 'The block title was properly sanitized when rendered.');
-
- $this->drupalLogin($this->drupalCreateUser(array('administer blocks', 'access administration pages')));
- $default_theme = $this->config('system.theme')->get('default');
- $this->drupalGet('admin/structure/block/list/' . $default_theme);
- $this->assertNoRaw("", 'The block title was properly sanitized in Block Plugin UI Admin page.');
- }
-
-}
diff --git a/core/modules/block/src/Tests/BlockUiTest.php b/core/modules/block/src/Tests/BlockUiTest.php
index aba4ba152..37fce6e60 100644
--- a/core/modules/block/src/Tests/BlockUiTest.php
+++ b/core/modules/block/src/Tests/BlockUiTest.php
@@ -126,6 +126,12 @@ class BlockUiTest extends WebTestBase {
'The block "' . $label . '" has the correct weight assignment (' . $values['test_weight'] . ').'
);
}
+
+ // Add a block with a machine name the same as a region name.
+ $this->drupalPlaceBlock('system_powered_by_block', ['region' => 'header', 'id' => 'header']);
+ $this->drupalGet('admin/structure/block');
+ $element = $this->xpath('//tr[contains(@class, :class)]', [':class' => 'region-title-header']);
+ $this->assertTrue(!empty($element));
}
/**
@@ -133,14 +139,15 @@ class BlockUiTest extends WebTestBase {
*/
public function testCandidateBlockList() {
$arguments = array(
- ':ul_class' => 'block-list',
- ':li_class' => 'test-block-instantiation',
+ ':title' => 'Display message',
+ ':category' => 'Block test',
':href' => 'admin/structure/block/add/test_block_instantiation/classy',
- ':text' => 'Display message',
);
+ $pattern = '//tr[.//td/div[text()=:title] and .//td[text()=:category] and .//td//a[contains(@href, :href)]]';
$this->drupalGet('admin/structure/block');
- $elements = $this->xpath('//details[@id="edit-category-block-test"]//ul[contains(@class, :ul_class)]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
+ $this->clickLinkPartialName('Place block');
+ $elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The test block appears in the category for its module.');
// Trigger the custom category addition in block_test_block_alter().
@@ -148,7 +155,9 @@ class BlockUiTest extends WebTestBase {
$this->container->get('plugin.manager.block')->clearCachedDefinitions();
$this->drupalGet('admin/structure/block');
- $elements = $this->xpath('//details[@id="edit-category-custom-category"]//ul[contains(@class, :ul_class)]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
+ $this->clickLinkPartialName('Place block');
+ $arguments[':category'] = 'Custom category';
+ $elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The test block appears in a custom category controlled by block_test_block_alter().');
}
diff --git a/core/modules/block/src/Tests/BlockViewBuilderTest.php b/core/modules/block/src/Tests/BlockViewBuilderTest.php
index ccb53e40e..7e3c39a74 100644
--- a/core/modules/block/src/Tests/BlockViewBuilderTest.php
+++ b/core/modules/block/src/Tests/BlockViewBuilderTest.php
@@ -9,7 +9,7 @@ namespace Drupal\block\Tests;
use Drupal\Component\Utility\Html;
use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\Context\UrlCacheContext;
+use Drupal\Core\Language\LanguageInterface;
use Drupal\simpletest\KernelTestBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
@@ -27,7 +27,7 @@ class BlockViewBuilderTest extends KernelTestBase {
*
* @var array
*/
- public static $modules = array('block', 'block_test', 'system');
+ public static $modules = array('block', 'block_test', 'system', 'user');
/**
* The block being tested.
@@ -160,7 +160,7 @@ class BlockViewBuilderTest extends KernelTestBase {
// Test that a cache entry is created.
$build = $this->getBlockRenderArray();
- $cid = 'entity_view:block:test_block:en:core';
+ $cid = 'entity_view:block:test_block:' . implode(':', \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
$this->renderer->renderRoot($build);
$this->assertTrue($this->container->get('cache.render')->get($cid), 'The block render element has been cached.');
@@ -190,14 +190,14 @@ class BlockViewBuilderTest extends KernelTestBase {
public function testBlockViewBuilderAlter() {
// Establish baseline.
$build = $this->getBlockRenderArray();
- $this->assertIdentical($this->renderer->renderRoot($build), 'Llamas > unicorns!');
+ $this->assertIdentical((string) $this->renderer->renderRoot($build), 'Llamas > unicorns!');
// Enable the block view alter hook that adds a suffix, for basic testing.
\Drupal::state()->set('block_test_view_alter_suffix', TRUE);
- Cache::invalidateTags($this->block->getCacheTags());
+ Cache::invalidateTags($this->block->getCacheTagsToInvalidate());
$build = $this->getBlockRenderArray();
$this->assertTrue(isset($build['#suffix']) && $build['#suffix'] === ' Goodbye!', 'A block with content is altered.');
- $this->assertIdentical($this->renderer->renderRoot($build), 'Llamas > unicorns! Goodbye!');
+ $this->assertIdentical((string) $this->renderer->renderRoot($build), 'Llamas > unicorns! Goodbye!');
\Drupal::state()->set('block_test_view_alter_suffix', FALSE);
// Force a request via GET so we can test the render cache.
@@ -206,7 +206,7 @@ class BlockViewBuilderTest extends KernelTestBase {
$request->setMethod('GET');
\Drupal::state()->set('block_test.content', NULL);
- Cache::invalidateTags($this->block->getCacheTags());
+ Cache::invalidateTags($this->block->getCacheTagsToInvalidate());
$default_keys = array('entity_view', 'block', 'test_block');
$default_tags = array('block_view', 'config:block.block.test_block');
@@ -214,11 +214,11 @@ class BlockViewBuilderTest extends KernelTestBase {
// Advanced: cached block, but an alter hook adds an additional cache key.
$alter_add_key = $this->randomMachineName();
\Drupal::state()->set('block_test_view_alter_cache_key', $alter_add_key);
- $cid = 'entity_view:block:test_block:' . $alter_add_key . ':en:core';
+ $cid = 'entity_view:block:test_block:' . $alter_add_key . ':' . implode(':', \Drupal::service('cache_contexts_manager')->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
$expected_keys = array_merge($default_keys, array($alter_add_key));
$build = $this->getBlockRenderArray();
$this->assertIdentical($expected_keys, $build['#cache']['keys'], 'An altered cacheable block has the expected cache keys.');
- $this->assertIdentical($this->renderer->renderRoot($build), '');
+ $this->assertIdentical((string) $this->renderer->renderRoot($build), '');
$cache_entry = $this->container->get('cache.render')->get($cid);
$this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
$expected_tags = array_merge($default_tags, ['rendered']);
@@ -233,7 +233,7 @@ class BlockViewBuilderTest extends KernelTestBase {
$build = $this->getBlockRenderArray();
sort($build['#cache']['tags']);
$this->assertIdentical($expected_tags, $build['#cache']['tags'], 'An altered cacheable block has the expected cache tags.');
- $this->assertIdentical($this->renderer->renderRoot($build), '');
+ $this->assertIdentical((string) $this->renderer->renderRoot($build), '');
$cache_entry = $this->container->get('cache.render')->get($cid);
$this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
$expected_tags = array_merge($default_tags, [$alter_add_tag, 'rendered']);
@@ -246,7 +246,7 @@ class BlockViewBuilderTest extends KernelTestBase {
\Drupal::state()->set('block_test_view_alter_append_pre_render_prefix', TRUE);
$build = $this->getBlockRenderArray();
$this->assertFalse(isset($build['#prefix']), 'The appended #pre_render callback has not yet run before rendering.');
- $this->assertIdentical($this->renderer->renderRoot($build), 'Hiya! ');
+ $this->assertIdentical((string) $this->renderer->renderRoot($build), 'Hiya! ');
$this->assertTrue(isset($build['#prefix']) && $build['#prefix'] === 'Hiya! ', 'A cached block without content is altered.');
// Restore the previous request method.
diff --git a/core/modules/block/src/Tests/BlockXssTest.php b/core/modules/block/src/Tests/BlockXssTest.php
index f24811a32..3d9bfd39d 100644
--- a/core/modules/block/src/Tests/BlockXssTest.php
+++ b/core/modules/block/src/Tests/BlockXssTest.php
@@ -26,7 +26,34 @@ class BlockXssTest extends WebTestBase {
*
* @var array
*/
- public static $modules = ['block', 'block_content', 'menu_ui', 'views'];
+ public static $modules = ['block', 'block_content', 'block_test', 'menu_ui', 'views'];
+
+ /**
+ * Test XSS in title.
+ */
+ public function testXssInTitle() {
+ $this->drupalPlaceBlock('test_xss_title', ['label' => '']);
+
+ \Drupal::state()->set('block_test.content', $this->randomMachineName());
+ $this->drupalGet('');
+ $this->assertNoRaw('', 'The block title was properly sanitized when rendered.');
+
+ $this->drupalLogin($this->drupalCreateUser(['administer blocks', 'access administration pages']));
+ $default_theme = $this->config('system.theme')->get('default');
+ $this->drupalGet('admin/structure/block/list/' . $default_theme);
+ $this->assertNoRaw("", 'The block title was properly sanitized in Block Plugin UI Admin page.');
+ }
+
+ /**
+ * Tests XSS in category.
+ */
+ public function testXssInCategory() {
+ $this->drupalPlaceBlock('test_xss_title');
+ $this->drupalLogin($this->drupalCreateUser(['administer blocks', 'access administration pages']));
+ $this->drupalGet(Url::fromRoute('block.admin_display'));
+ $this->clickLinkPartialName('Place block');
+ $this->assertNoRaw("");
+ }
/**
* Tests various modules that provide blocks for XSS.
@@ -51,8 +78,9 @@ class BlockXssTest extends WebTestBase {
$view->save();
$this->drupalGet(Url::fromRoute('block.admin_display'));
- $this->clickLink('');
- $this->assertRaw('<script>alert("view");</script>');
+ $this->clickLinkPartialName('Place block');
+ // The block admin label is automatically XSS admin filtered.
+ $this->assertRaw('alert("view");');
$this->assertNoRaw('');
}
@@ -66,8 +94,9 @@ class BlockXssTest extends WebTestBase {
])->save();
$this->drupalGet(Url::fromRoute('block.admin_display'));
- $this->clickLink('');
- $this->assertRaw('<script>alert("menu");</script>');
+ $this->clickLinkPartialName('Place block');
+ // The block admin label is automatically XSS admin filtered.
+ $this->assertRaw('alert("menu");');
$this->assertNoRaw('');
}
@@ -86,8 +115,9 @@ class BlockXssTest extends WebTestBase {
])->save();
$this->drupalGet(Url::fromRoute('block.admin_display'));
- $this->clickLink('');
- $this->assertRaw('<script>alert("block_content");</script>');
+ $this->clickLinkPartialName('Place block');
+ // The block admin label is automatically XSS admin filtered.
+ $this->assertRaw('alert("block_content");');
$this->assertNoRaw('');
}
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php b/core/modules/block/src/Tests/Migrate/d6/MigrateBlockTest.php
similarity index 92%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php
rename to core/modules/block/src/Tests/Migrate/d6/MigrateBlockTest.php
index a1ec049c6..232fa4a32 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockTest.php
+++ b/core/modules/block/src/Tests/Migrate/d6/MigrateBlockTest.php
@@ -2,19 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateBlockTest.
+ * Contains \Drupal\block\Tests\Migrate\d6\MigrateBlockTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\block\Tests\Migrate\d6;
-use Drupal\migrate\MigrateExecutable;
-use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
use Drupal\block\Entity\Block;
+use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade block settings to block.block.*.yml.
*
- * @group migrate_drupal
+ * @group block
*/
class MigrateBlockTest extends MigrateDrupal6TestBase {
@@ -72,16 +71,8 @@ class MigrateBlockTest extends MigrateDrupal6TestBase {
// Install one of D8's test themes.
\Drupal::service('theme_handler')->install(array('test_theme'));
- /** @var \Drupal\migrate\entity\Migration $migration */
- $migration = entity_load('migration', 'd6_block');
- $dumps = array(
- $this->getDumpDirectory() . '/Blocks.php',
- $this->getDumpDirectory() . '/BlocksRoles.php',
- $this->getDumpDirectory() . '/AggregatorFeed.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
+ $this->loadDumps(['Blocks.php', 'BlocksRoles.php', 'AggregatorFeed.php']);
+ $this->executeMigration('d6_block');
}
/**
diff --git a/core/modules/block/src/Tests/Update/BlockContextMappingUpdateTest.php b/core/modules/block/src/Tests/Update/BlockContextMappingUpdateTest.php
new file mode 100644
index 000000000..da03fa054
--- /dev/null
+++ b/core/modules/block/src/Tests/Update/BlockContextMappingUpdateTest.php
@@ -0,0 +1,110 @@
+databaseDumpFiles = [
+ __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
+ __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.block-context-manager-2354889.php',
+ ];
+ parent::setUp();
+ }
+
+ /**
+ * Tests that block context mapping is updated properly.
+ */
+ public function testUpdateHookN() {
+ $this->runUpdates();
+ $this->assertRaw('Encountered an unknown context mapping key coming probably from a contributed or custom module: One or more mappings could not be updated. Please manually review your visibility settings for the following blocks, which are disabled now:User login (Visibility: Baloney spam) ');
+
+ // Disable maintenance mode.
+ \Drupal::state()->set('system.maintenance_mode', FALSE);
+
+ // We finished updating so we can login the user now.
+ $this->drupalLogin($this->rootUser);
+
+ // The block that we are testing has the following visibility rules:
+ // - only visible on node pages
+ // - only visible to authenticated users.
+ $block_title = 'Test for 2354889';
+
+ // Create two nodes, a page and an article.
+ $page = Node::create([
+ 'type' => 'page',
+ 'title' => 'Page node',
+ ]);
+ $page->save();
+
+ $article = Node::create([
+ 'type' => 'article',
+ 'title' => 'Article node',
+ ]);
+ $article->save();
+
+ // Check that the block appears only on Page nodes for authenticated users.
+ $this->drupalGet('node/' . $page->id());
+ $this->assertRaw($block_title, 'Test block is visible on a Page node as an authenticated user.');
+
+ $this->drupalGet('node/' . $article->id());
+ $this->assertNoRaw($block_title, 'Test block is not visible on a Article node as an authenticated user.');
+
+ $this->drupalLogout();
+
+ // Check that the block does not appear on any page for anonymous users.
+ $this->drupalGet('node/' . $page->id());
+ $this->assertNoRaw($block_title, 'Test block is not visible on a Page node as an anonymous user.');
+
+ $this->drupalGet('node/' . $article->id());
+ $this->assertNoRaw($block_title, 'Test block is not visible on a Article node as an anonymous user.');
+
+ // Ensure that all the context mappings got updated properly.
+ $block = Block::load('testfor2354889');
+ $visibility = $block->get('visibility');
+ $this->assertEqual('@node.node_route_context:node', $visibility['node_type']['context_mapping']['node']);
+ $this->assertEqual('@user.current_user_context:current_user', $visibility['user_role']['context_mapping']['user']);
+ $this->assertEqual('@language.current_language_context:language_interface', $visibility['language']['context_mapping']['language']);
+
+ // Check that a block with invalid context is being disabled and that it can
+ // still be edited afterward.
+ $disabled_block = Block::load('thirdtestfor2354889');
+ $this->assertFalse($disabled_block->status(), 'Block with invalid context is disabled');
+
+ $this->assertEqual(['thirdtestfor2354889' => ['missing_context_ids' => ['baloney.spam' => ['node_type']], 'status' => TRUE]], \Drupal::keyValue('update_backup')->get('block_update_8001'));
+
+ $disabled_block_visibility = $disabled_block->get('visibility');
+ $this->assertTrue(!isset($disabled_block_visibility['node_type']), 'The problematic visibility condition has been removed.');
+
+ $admin_user = $this->drupalCreateUser(['administer blocks']);
+ $this->drupalLogin($admin_user);
+
+ $this->drupalGet('admin/structure/block/manage/thirdtestfor2354889');
+ $this->assertResponse('200');
+ }
+
+}
diff --git a/core/modules/block/src/Tests/Views/DisplayBlockTest.php b/core/modules/block/src/Tests/Views/DisplayBlockTest.php
index 3f96ab10f..880f68ced 100644
--- a/core/modules/block/src/Tests/Views/DisplayBlockTest.php
+++ b/core/modules/block/src/Tests/Views/DisplayBlockTest.php
@@ -8,8 +8,6 @@
namespace Drupal\block\Tests\Views;
use Drupal\Component\Serialization\Json;
-use Drupal\Component\Utility\Html;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\views\Views;
use Drupal\views\Tests\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
@@ -60,19 +58,20 @@ class DisplayBlockTest extends ViewTestBase {
$edit['block[style][row_plugin]'] = 'fields';
$this->drupalPostForm('admin/structure/views/add', $edit, t('Save and edit'));
+ $pattern = '//tr[.//td[text()=:category] and .//td//a[contains(@href, :href)]]';
+
// Test that the block was given a default category corresponding to its
// base table.
$arguments = array(
- ':id' => 'edit-category-lists-views',
- ':li_class' => 'views-block' . Html::getClass($edit['id']) . '-block-1',
':href' => \Drupal::Url('block.admin_add', array(
'plugin_id' => 'views_block:' . $edit['id'] . '-block_1',
'theme' => 'classy',
)),
- ':text' => $edit['label'],
+ ':category' => t('Lists (Views)'),
);
$this->drupalGet('admin/structure/block');
- $elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
+ $this->clickLinkPartialName('Place block');
+ $elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The test block appears in the category for its base table.');
// Duplicate the block before changing the category.
@@ -81,10 +80,9 @@ class DisplayBlockTest extends ViewTestBase {
// Change the block category to a random string.
$this->drupalGet('admin/structure/views/view/' . $edit['id'] . '/edit/block_1');
- $label = t('Lists (Views)');
- $link = $this->xpath('//a[@id="views-block-1-block-category" and normalize-space(text())=:label]', array(':label' => $label));
+ $link = $this->xpath('//a[@id="views-block-1-block-category" and normalize-space(text())=:category]', $arguments);
$this->assertTrue(!empty($link));
- $this->clickLink($label);
+ $this->clickLink(t('Lists (Views)'));
$category = $this->randomString();
$this->drupalPostForm(NULL, array('block_category' => $category), t('Apply'));
@@ -95,34 +93,30 @@ class DisplayBlockTest extends ViewTestBase {
$this->drupalPostForm(NULL, array(), t('Save'));
// Test that the blocks are listed under the correct categories.
- $category_id = Html::getUniqueId('edit-category-' . SafeMarkup::checkPlain($category));
- $arguments[':id'] = $category_id;
+ $arguments[':category'] = $category;
$this->drupalGet('admin/structure/block');
- $elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
+ $this->clickLinkPartialName('Place block');
+ $elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The test block appears in the custom category.');
$arguments = array(
- ':id' => 'edit-category-lists-views',
- ':li_class' => 'views-block' . Html::getClass($edit['id']) . '-block-2',
':href' => \Drupal::Url('block.admin_add', array(
'plugin_id' => 'views_block:' . $edit['id'] . '-block_2',
'theme' => 'classy',
)),
- ':text' => $edit['label'],
+ ':category' => t('Lists (Views)'),
);
- $elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
+ $elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The first duplicated test block remains in the original category.');
$arguments = array(
- ':id' => $category_id,
- ':li_class' => 'views-block' . Html::getClass($edit['id']) . '-block-3',
':href' => \Drupal::Url('block.admin_add', array(
'plugin_id' => 'views_block:' . $edit['id'] . '-block_3',
'theme' => 'classy',
)),
- ':text' => $edit['label'],
+ ':category' => $category,
);
- $elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments);
+ $elements = $this->xpath($pattern, $arguments);
$this->assertTrue(!empty($elements), 'The second duplicated test block appears in the custom category.');
}
diff --git a/core/modules/block/tests/modules/block_test/src/Plugin/Condition/BaloneySpam.php b/core/modules/block/tests/modules/block_test/src/Plugin/Condition/BaloneySpam.php
new file mode 100644
index 000000000..55e12abaa
--- /dev/null
+++ b/core/modules/block/tests/modules/block_test/src/Plugin/Condition/BaloneySpam.php
@@ -0,0 +1,37 @@
+conditionManager = $this->getMock('Drupal\Core\Executable\ExecutableManagerInterface');
$this->language = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
- $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+ $this->contextRepository = $this->getMock('Drupal\Core\Plugin\Context\ContextRepositoryInterface');
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$this->storage = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
@@ -104,7 +104,7 @@ class BlockFormTest extends UnitTestCase {
->method('getQuery')
->will($this->returnValue($query));
- $block_form_controller = new BlockForm($this->entityManager, $this->conditionManager, $this->dispatcher, $this->language, $this->themeHandler);
+ $block_form_controller = new BlockForm($this->entityManager, $this->conditionManager, $this->contextRepository, $this->language, $this->themeHandler);
// Ensure that the block with just one other instance gets the next available
// name suggestion.
diff --git a/core/modules/block/tests/src/Unit/BlockRepositoryTest.php b/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
index 56b90caae..aab11fe9f 100644
--- a/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
+++ b/core/modules/block/tests/src/Unit/BlockRepositoryTest.php
@@ -8,6 +8,7 @@
namespace Drupal\Tests\block\Unit;
use Drupal\block\BlockRepository;
+use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Tests\UnitTestCase;
@@ -59,7 +60,7 @@ class BlockRepositoryTest extends UnitTestCase {
]);
$theme_manager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
- $theme_manager->expects($this->once())
+ $theme_manager->expects($this->atLeastOnce())
->method('getActiveTheme')
->will($this->returnValue($active_theme));
@@ -84,15 +85,18 @@ class BlockRepositoryTest extends UnitTestCase {
$blocks = [];
foreach ($blocks_config as $block_id => $block_config) {
$block = $this->getMock('Drupal\block\BlockInterface');
- $block->expects($this->once())
- ->method('setContexts')
- ->willReturnSelf();
$block->expects($this->once())
->method('access')
->will($this->returnValue($block_config[0]));
$block->expects($block_config[0] ? $this->atLeastOnce() : $this->never())
->method('getRegion')
->willReturn($block_config[1]);
+ $block->expects($this->any())
+ ->method('label')
+ ->willReturn($block_id);
+ $block->expects($this->any())
+ ->method('getWeight')
+ ->willReturn($block_config[2]);
$blocks[$block_id] = $block;
}
@@ -101,30 +105,34 @@ class BlockRepositoryTest extends UnitTestCase {
->with(['theme' => $this->theme])
->willReturn($blocks);
$result = [];
- foreach ($this->blockRepository->getVisibleBlocksPerRegion([]) as $region => $resulting_blocks) {
+ $cacheable_metadata = [];
+ foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata) as $region => $resulting_blocks) {
$result[$region] = [];
foreach ($resulting_blocks as $plugin_id => $block) {
$result[$region][] = $plugin_id;
}
}
- $this->assertSame($result, $expected_blocks);
+ $this->assertEquals($expected_blocks, $result);
}
public function providerBlocksConfig() {
$blocks_config = array(
'block1' => array(
- TRUE, 'top', 0
+ AccessResult::allowed(), 'top', 0
),
// Test a block without access.
'block2' => array(
- FALSE, 'bottom', 0
- ),
- // Test two blocks in the same region with specific weight.
- 'block3' => array(
- TRUE, 'bottom', 5
+ AccessResult::forbidden(), 'bottom', 0
),
+ // Test some blocks in the same region with specific weight.
'block4' => array(
- TRUE, 'bottom', -5
+ AccessResult::allowed(), 'bottom', 5
+ ),
+ 'block3' => array(
+ AccessResult::allowed(), 'bottom', 5
+ ),
+ 'block5' => array(
+ AccessResult::allowed(), 'bottom', -5
),
);
@@ -133,7 +141,7 @@ class BlockRepositoryTest extends UnitTestCase {
[
'top' => ['block1'],
'center' => [],
- 'bottom' => ['block4', 'block3'],
+ 'bottom' => ['block5', 'block3', 'block4'],
]
];
return $test_cases;
@@ -146,24 +154,21 @@ class BlockRepositoryTest extends UnitTestCase {
*/
public function testGetVisibleBlocksPerRegionWithContext() {
$block = $this->getMock('Drupal\block\BlockInterface');
- $block->expects($this->once())
- ->method('setContexts')
- ->willReturnSelf();
$block->expects($this->once())
->method('access')
- ->willReturn(TRUE);
+ ->willReturn(AccessResult::allowed()->addCacheTags(['config:block.block.block_id']));
$block->expects($this->once())
->method('getRegion')
->willReturn('top');
$blocks['block_id'] = $block;
- $contexts = [];
$this->blockStorage->expects($this->once())
->method('loadByProperties')
->with(['theme' => $this->theme])
->willReturn($blocks);
$result = [];
- foreach ($this->blockRepository->getVisibleBlocksPerRegion($contexts) as $region => $resulting_blocks) {
+ $cacheable_metadata = [];
+ foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata) as $region => $resulting_blocks) {
$result[$region] = [];
foreach ($resulting_blocks as $plugin_id => $block) {
$result[$region][] = $plugin_id;
@@ -177,6 +182,10 @@ class BlockRepositoryTest extends UnitTestCase {
'bottom' => [],
];
$this->assertSame($expected, $result);
+
+ // Assert that the cacheable metadata from the block access results was
+ // collected.
+ $this->assertEquals(['config:block.block.block_id'], $cacheable_metadata['top']->getCacheTags());
}
}
diff --git a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
index bb5390ce2..147e64a04 100644
--- a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
+++ b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
@@ -7,6 +7,8 @@
namespace Drupal\Tests\block\Unit\Plugin\DisplayVariant;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\DependencyInjection\Container;
use Drupal\Tests\UnitTestCase;
/**
@@ -29,13 +31,6 @@ class BlockPageVariantTest extends UnitTestCase {
*/
protected $blockViewBuilder;
- /**
- * The event dispatcher.
- *
- * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject
- */
- protected $dispatcher;
-
/**
* The plugin context handler.
*
@@ -55,14 +50,23 @@ class BlockPageVariantTest extends UnitTestCase {
* A mocked display variant plugin.
*/
public function setUpDisplayVariant($configuration = array(), $definition = array()) {
+
+ $container = new Container();
+ $cache_context_manager = $this->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $container->set('cache_contexts_manager', $cache_context_manager);
+ $cache_context_manager->expects($this->any())
+ ->method('validateTokens')
+ ->with([])
+ ->willReturn([]);
+ \Drupal::setContainer($container);
+
$this->blockRepository = $this->getMock('Drupal\block\BlockRepositoryInterface');
$this->blockViewBuilder = $this->getMock('Drupal\Core\Entity\EntityViewBuilderInterface');
- $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
- $this->dispatcher->expects($this->any())
- ->method('dispatch')
- ->willReturnArgument(1);
+
return $this->getMockBuilder('Drupal\block\Plugin\DisplayVariant\BlockPageVariant')
- ->setConstructorArgs(array($configuration, 'test', $definition, $this->blockRepository, $this->blockViewBuilder, $this->dispatcher, ['config:block_list']))
+ ->setConstructorArgs(array($configuration, 'test', $definition, $this->blockRepository, $this->blockViewBuilder, ['config:block_list']))
->setMethods(array('getRegionNames'))
->getMock();
}
@@ -96,7 +100,10 @@ class BlockPageVariantTest extends UnitTestCase {
'#cache' => [
'tags' => [
'config:block_list',
+ 'route',
],
+ 'contexts' => [],
+ 'max-age' => -1,
],
'top' => [
'block1' => [],
@@ -121,7 +128,10 @@ class BlockPageVariantTest extends UnitTestCase {
'#cache' => [
'tags' => [
'config:block_list',
+ 'route',
],
+ 'contexts' => [],
+ 'max-age' => -1,
],
'top' => [
'block1' => [],
@@ -152,7 +162,10 @@ class BlockPageVariantTest extends UnitTestCase {
'#cache' => [
'tags' => [
'config:block_list',
+ 'route',
],
+ 'contexts' => [],
+ 'max-age' => -1,
],
'top' => [
'block1' => [],
@@ -194,20 +207,26 @@ class BlockPageVariantTest extends UnitTestCase {
$messages_block_plugin = $this->getMock('Drupal\Core\Block\MessagesBlockPluginInterface');
foreach ($blocks_config as $block_id => $block_config) {
$block = $this->getMock('Drupal\block\BlockInterface');
+ $block->expects($this->any())
+ ->method('getContexts')
+ ->willReturn([]);
$block->expects($this->atLeastOnce())
->method('getPlugin')
->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : $block_plugin));
$blocks[$block_config[0]][$block_id] = $block;
}
-
$this->blockViewBuilder->expects($this->exactly($visible_block_count))
->method('view')
->will($this->returnValue(array()));
$this->blockRepository->expects($this->once())
->method('getVisibleBlocksPerRegion')
- ->will($this->returnValue($blocks));
+ ->willReturnCallback(function (&$cacheable_metadata) use ($blocks) {
+ $cacheable_metadata['top'] = (new CacheableMetadata())->addCacheTags(['route']);
+ return $blocks;
+ });
- $this->assertSame($expected_render_array, $display_variant->build());
+ $value = $display_variant->build();
+ $this->assertSame($expected_render_array, $value);
}
/**
@@ -226,6 +245,8 @@ class BlockPageVariantTest extends UnitTestCase {
'tags' => [
'config:block_list',
],
+ 'contexts' => [],
+ 'max-age' => -1,
],
'content' => [
'system_main' => [],
diff --git a/core/modules/migrate_drupal/tests/src/Unit/source/d6/BlockTest.php b/core/modules/block/tests/src/Unit/Plugin/migrate/source/d6/BlockTest.php
similarity index 85%
rename from core/modules/migrate_drupal/tests/src/Unit/source/d6/BlockTest.php
rename to core/modules/block/tests/src/Unit/Plugin/migrate/source/d6/BlockTest.php
index 79e3344cf..861aa2f67 100644
--- a/core/modules/migrate_drupal/tests/src/Unit/source/d6/BlockTest.php
+++ b/core/modules/block/tests/src/Unit/Plugin/migrate/source/d6/BlockTest.php
@@ -2,23 +2,24 @@
/**
* @file
- * Contains \Drupal\Tests\migrate_drupal\Unit\source\d6\BlockTest.
+ * Contains \Drupal\Tests\block\Unit\Plugin\migrate\source\d6\BlockTest.
*/
-namespace Drupal\Tests\migrate_drupal\Unit\source\d6;
+namespace Drupal\Tests\block\Unit\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase;
/**
* Tests D6 block source plugin.
*
- * @group migrate_drupal
+ * @coversDefaultClass \Drupal\block\Plugin\migrate\source\d6\Block
+ * @group block
*/
class BlockTest extends MigrateSqlSourceTestCase {
// The plugin system is not working during unit testing so the source plugin
// class needs to be manually specified.
- const PLUGIN_CLASS = 'Drupal\migrate_drupal\Plugin\migrate\source\d6\Block';
+ const PLUGIN_CLASS = 'Drupal\block\Plugin\migrate\source\d6\Block';
// The fake Migration configuration entity.
protected $migrationConfiguration = array(
diff --git a/core/modules/block_content/block_content.links.action.yml b/core/modules/block_content/block_content.links.action.yml
index d94ca3fc4..4772a6fd7 100644
--- a/core/modules/block_content/block_content.links.action.yml
+++ b/core/modules/block_content/block_content.links.action.yml
@@ -8,7 +8,6 @@ block_content_add_action:
route_name: block_content.add_page
title: 'Add custom block'
appears_on:
- - block.admin_display
- - block.admin_display_theme
+ - block.admin_library
- entity.block_content.collection
class: \Drupal\block_content\Plugin\Menu\LocalAction\BlockContentAddLocalAction
diff --git a/core/modules/block_content/block_content.pages.inc b/core/modules/block_content/block_content.pages.inc
index 23a7deafc..9e019cd3e 100644
--- a/core/modules/block_content/block_content.pages.inc
+++ b/core/modules/block_content/block_content.pages.inc
@@ -5,7 +5,6 @@
* Provides page callbacks for custom blocks.
*/
-use Drupal\Component\Utility\Xss;
use Drupal\Core\Url;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\block_content\Entity\BlockContent;
@@ -28,7 +27,9 @@ function template_preprocess_block_content_add_list(&$variables) {
foreach ($variables['content'] as $type) {
$variables['types'][$type->id()] = array(
'link' => \Drupal::l($type->label(), new Url('block_content.add_form', array('block_content_type' => $type->id()), array('query' => $query))),
- 'description' => Xss::filterAdmin($type->getDescription()),
+ 'description' => array(
+ '#markup' => $type->getDescription(),
+ ),
'title' => $type->label(),
'localized_options' => array(
'query' => $query,
diff --git a/core/modules/block_content/config/schema/block_content.schema.yml b/core/modules/block_content/config/schema/block_content.schema.yml
index 2e7c338b2..756f0204d 100644
--- a/core/modules/block_content/config/schema/block_content.schema.yml
+++ b/core/modules/block_content/config/schema/block_content.schema.yml
@@ -16,3 +16,15 @@ block_content.type.*:
description:
type: text
label: 'Description'
+
+migrate.source.d6_box:
+ type: migrate_source_sql
+ label: 'Drupal 6 box'
+ mapping:
+ constants:
+ type: mapping
+ label: 'Constants'
+ mapping:
+ type:
+ type: string
+ label: 'Type'
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_block_content_body_field.yml b/core/modules/block_content/migration_templates/d6_block_content_body_field.yml
similarity index 83%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_block_content_body_field.yml
rename to core/modules/block_content/migration_templates/d6_block_content_body_field.yml
index 08935eed9..e8164e17c 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_block_content_body_field.yml
+++ b/core/modules/block_content/migration_templates/d6_block_content_body_field.yml
@@ -23,10 +23,3 @@ destination:
migration_dependencies:
required:
- d6_block_content_type
-dependencies:
- config:
- - migrate.migration.d6_block_content_type
- module:
- - block_content
- - field
- - migrate_drupal
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_block_content_type.yml b/core/modules/block_content/migration_templates/d6_block_content_type.yml
similarity index 84%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_block_content_type.yml
rename to core/modules/block_content/migration_templates/d6_block_content_type.yml
index f055a6180..8dcea2f0f 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_block_content_type.yml
+++ b/core/modules/block_content/migration_templates/d6_block_content_type.yml
@@ -14,7 +14,3 @@ process:
label: 'constants/label'
destination:
plugin: entity:block_content_type
-dependencies:
- module:
- - block_content
- - migrate_drupal
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_custom_block.yml b/core/modules/block_content/migration_templates/d6_custom_block.yml
similarity index 72%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_custom_block.yml
rename to core/modules/block_content/migration_templates/d6_custom_block.yml
index be6cebfad..0564f8920 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_custom_block.yml
+++ b/core/modules/block_content/migration_templates/d6_custom_block.yml
@@ -22,10 +22,3 @@ migration_dependencies:
required:
- d6_filter_format
- d6_block_content_body_field
-dependencies:
- config:
- - migrate.migration.d6_block_content_body_field
- - migrate.migration.d6_filter_format
- module:
- - block_content
- - migrate_drupal
diff --git a/core/modules/block_content/src/BlockContentForm.php b/core/modules/block_content/src/BlockContentForm.php
index 379e57635..ec267cc39 100644
--- a/core/modules/block_content/src/BlockContentForm.php
+++ b/core/modules/block_content/src/BlockContentForm.php
@@ -224,7 +224,8 @@ class BlockContentForm extends ContentEntityForm {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
- if ($this->entity->isNew()) {
+ $entity = parent::validateForm($form, $form_state);
+ if ($entity->isNew()) {
$exists = $this->blockContentStorage->loadByProperties(array('info' => $form_state->getValue(['info', 0, 'value'])));
if (!empty($exists)) {
$form_state->setErrorByName('info', $this->t('A block with description %name already exists.', array(
@@ -232,6 +233,7 @@ class BlockContentForm extends ContentEntityForm {
)));
}
}
+ return $entity;
}
}
diff --git a/core/modules/block_content/src/BlockContentTypeListBuilder.php b/core/modules/block_content/src/BlockContentTypeListBuilder.php
index 2a51159c9..c62502181 100644
--- a/core/modules/block_content/src/BlockContentTypeListBuilder.php
+++ b/core/modules/block_content/src/BlockContentTypeListBuilder.php
@@ -7,7 +7,6 @@
namespace Drupal\block_content;
-use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
@@ -45,7 +44,7 @@ class BlockContentTypeListBuilder extends ConfigEntityListBuilder {
*/
public function buildRow(EntityInterface $entity) {
$row['type'] = $entity->link();
- $row['description'] = Xss::filterAdmin($entity->getDescription());
+ $row['description']['data']['#markup'] = $entity->getDescription();
return $row + parent::buildRow($entity);
}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/Box.php b/core/modules/block_content/src/Plugin/migrate/source/d6/Box.php
similarity index 87%
rename from core/modules/migrate_drupal/src/Plugin/migrate/source/d6/Box.php
rename to core/modules/block_content/src/Plugin/migrate/source/d6/Box.php
index f8eec40fc..9596088ec 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/Box.php
+++ b/core/modules/block_content/src/Plugin/migrate/source/d6/Box.php
@@ -2,10 +2,10 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Plugin\migrate\source\d6\Box.
+ * Contains \Drupal\block_content\Plugin\migrate\source\d6\Box.
*/
-namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
+namespace Drupal\block_content\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
diff --git a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
index fe2c789ba..1aecb8523 100644
--- a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
+++ b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
@@ -90,13 +90,15 @@ class BlockContentCacheTagsTest extends EntityCacheTagsTestBase {
// Expected keys, contexts, and tags for the block.
// @see \Drupal\block\BlockViewBuilder::viewMultiple()
$expected_block_cache_keys = ['entity_view', 'block', $block->id()];
- $expected_block_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme'];
- $expected_block_cache_tags = Cache::mergeTags(['block_view', 'rendered'], $block->getCacheTags(), $block->getPlugin()->getCacheTags());
+ $expected_block_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
+ $expected_block_cache_tags = Cache::mergeTags(['block_view', 'rendered'], $block->getCacheTags());
+ $expected_block_cache_tags = Cache::mergeTags($expected_block_cache_tags, $block->getPlugin()->getCacheTags());
// Expected contexts and tags for the BlockContent entity.
// @see \Drupal\Core\Entity\EntityViewBuilder::getBuildDefaults().
$expected_entity_cache_contexts = ['theme'];
- $expected_entity_cache_tags = Cache::mergeTags(['block_content_view'], $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntity($this->entity));
+ $expected_entity_cache_tags = Cache::mergeTags(['block_content_view'], $this->entity->getCacheTags());
+ $expected_entity_cache_tags = Cache::mergeTags($expected_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
// Verify that what was render cached matches the above expectations.
$cid = $this->createCacheId($expected_block_cache_keys, $expected_block_cache_contexts);
diff --git a/core/modules/block_content/src/Tests/BlockContentTypeTest.php b/core/modules/block_content/src/Tests/BlockContentTypeTest.php
index 42705380e..20dc1a2ed 100644
--- a/core/modules/block_content/src/Tests/BlockContentTypeTest.php
+++ b/core/modules/block_content/src/Tests/BlockContentTypeTest.php
@@ -189,6 +189,7 @@ class BlockContentTypeTest extends BlockContentTestBase {
// block configure form.
$path = $theme == $default_theme ? 'admin/structure/block' : "admin/structure/block/list/$theme";
$this->drupalGet($path);
+ $this->clickLinkPartialName('Place block');
$this->clickLink(t('Add custom block'));
// The seven theme has markup inside the link, we cannot use clickLink().
if ($default_theme == 'seven') {
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockContentTest.php b/core/modules/block_content/src/Tests/Migrate/d6/MigrateBlockContentTest.php
similarity index 63%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateBlockContentTest.php
rename to core/modules/block_content/src/Tests/Migrate/d6/MigrateBlockContentTest.php
index eb13b49ef..3a6816105 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateBlockContentTest.php
+++ b/core/modules/block_content/src/Tests/Migrate/d6/MigrateBlockContentTest.php
@@ -2,22 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateBlockContentTest.
+ * Contains \Drupal\block_content\Tests\Migrate\d6\MigrateBlockContentTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\block_content\Tests\Migrate\d6;
-use Drupal\Core\Language\Language;
use Drupal\block_content\Entity\BlockContent;
-use Drupal\Core\Language\LanguageInterface;
-use Drupal\field\Entity\FieldStorageConfig;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade custom blocks.
*
- * @group migrate_drupal
+ * @group block_content
*/
class MigrateBlockContentTest extends MigrateDrupal6TestBase {
@@ -31,26 +27,16 @@ class MigrateBlockContentTest extends MigrateDrupal6TestBase {
$this->installConfig(array('block_content'));
$this->installEntitySchema('block_content');
- $migration = entity_load('migration', 'd6_block_content_type');
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
- $migration = entity_load('migration', 'd6_block_content_body_field');
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
+ $this->executeMigration('d6_block_content_type');
+ $this->executeMigration('d6_block_content_body_field');
$this->prepareMigrations(array(
'd6_filter_format' => array(
array(array(2), array('full_html'))
)
));
- /** @var \Drupal\migrate\entity\Migration $migration */
- $migration = entity_load('migration', 'd6_custom_block');
- $dumps = array(
- $this->getDumpDirectory() . '/Boxes.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
+ $this->loadDumps(['Boxes.php']);
+ $this->executeMigration('d6_custom_block');
}
/**
diff --git a/core/modules/block_content/tests/modules/block_content_test/block_content_test.module b/core/modules/block_content/tests/modules/block_content_test/block_content_test.module
index d714cb20e..3450b5f04 100644
--- a/core/modules/block_content/tests/modules/block_content_test/block_content_test.module
+++ b/core/modules/block_content/tests/modules/block_content_test/block_content_test.module
@@ -60,6 +60,7 @@ function block_content_test_block_content_insert(BlockContent $block_content) {
// Set the block_content title to the block_content ID and save.
if ($block_content->label() == 'new') {
$block_content->setInfo('BlockContent ' . $block_content->id());
+ $block_content->setNewRevision(FALSE);
$block_content->save();
}
if ($block_content->label() == 'fail_creation') {
diff --git a/core/modules/migrate_drupal/tests/src/Unit/source/d6/BoxTest.php b/core/modules/block_content/tests/src/Unit/Plugin/migrate/source/d6/BoxTest.php
similarity index 83%
rename from core/modules/migrate_drupal/tests/src/Unit/source/d6/BoxTest.php
rename to core/modules/block_content/tests/src/Unit/Plugin/migrate/source/d6/BoxTest.php
index fdd33edd5..6c7ef2182 100644
--- a/core/modules/migrate_drupal/tests/src/Unit/source/d6/BoxTest.php
+++ b/core/modules/block_content/tests/src/Unit/Plugin/migrate/source/d6/BoxTest.php
@@ -2,23 +2,23 @@
/**
* @file
- * Contains \Drupal\Tests\migrate_drupal\Unit\source\d6\BoxTest.
+ * Contains \Drupal\Tests\block_content\Unit\Plugin\migrate\source\d6\BoxTest.
*/
-namespace Drupal\Tests\migrate_drupal\Unit\source\d6;
+namespace Drupal\Tests\block_content\Unit\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase;
/**
* Tests D6 block boxes source plugin.
*
- * @group migrate_drupal
+ * @group block_content
*/
class BoxTest extends MigrateSqlSourceTestCase {
// The plugin system is not working during unit testing so the source plugin
// class needs to be manually specified.
- const PLUGIN_CLASS = 'Drupal\migrate_drupal\Plugin\migrate\source\d6\Box';
+ const PLUGIN_CLASS = 'Drupal\block_content\Plugin\migrate\source\d6\Box';
// The fake Migration configuration entity.
protected $migrationConfiguration = array(
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_book.yml b/core/modules/book/migration_templates/d6_book.yml
similarity index 77%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_book.yml
rename to core/modules/book/migration_templates/d6_book.yml
index c8007e0e6..683f56aaa 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_book.yml
+++ b/core/modules/book/migration_templates/d6_book.yml
@@ -21,10 +21,3 @@ destination:
migration_dependencies:
required:
- d6_node
-dependencies:
- config:
- - migrate.migration.d6_node
- module:
- - book
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_book_settings.yml b/core/modules/book/migration_templates/d6_book_settings.yml
similarity index 86%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_book_settings.yml
rename to core/modules/book/migration_templates/d6_book_settings.yml
index 35302d031..e885d37e9 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_book_settings.yml
+++ b/core/modules/book/migration_templates/d6_book_settings.yml
@@ -15,7 +15,3 @@ process:
destination:
plugin: config
config_name: book.settings
-dependencies:
- module:
- - book
- - migrate_drupal
diff --git a/core/modules/book/src/Cache/BookNavigationCacheContext.php b/core/modules/book/src/Cache/BookNavigationCacheContext.php
index 15ce15ad8..d165b95a3 100644
--- a/core/modules/book/src/Cache/BookNavigationCacheContext.php
+++ b/core/modules/book/src/Cache/BookNavigationCacheContext.php
@@ -7,6 +7,7 @@
namespace Drupal\book\Cache;
+use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\RequestStack;
@@ -14,6 +15,8 @@ use Symfony\Component\HttpFoundation\RequestStack;
/**
* Defines the book navigation cache context service.
*
+ * Cache context ID: 'route.book_navigation'.
+ *
* This allows for book navigation location-aware caching. It depends on:
* - whether the current route represents a book node at all
* - and if so, where in the book hierarchy we are
@@ -65,7 +68,27 @@ class BookNavigationCacheContext extends ContainerAware implements CacheContextI
// If we're looking at a book node, get the trail for that node.
$active_trail = $this->container->get('book.manager')
->getActiveTrailIds($node->book['bid'], $node->book);
- return 'book.' . implode('|', $active_trail);
+ return implode('|', $active_trail);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheableMetadata() {
+ // The book active trail depends on the node and data attached to it.
+ // That information is however not stored as part of the node.
+ $cacheable_metadata = new CacheableMetadata();
+ if ($node = $this->requestStack->getCurrentRequest()->get('node')) {
+ // If the node is part of a book then we can use the cache tag for that
+ // book. If not, then it can't be optimized away.
+ if (!empty($node->book['bid'])) {
+ $cacheable_metadata->addCacheTags(['bid:' . $node->book['bid']]);
+ }
+ else {
+ $cacheable_metadata->setCacheMaxAge(0);
+ }
+ }
+ return $cacheable_metadata;
}
}
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/Book.php b/core/modules/book/src/Plugin/migrate/destination/Book.php
similarity index 73%
rename from core/modules/migrate/src/Plugin/migrate/destination/Book.php
rename to core/modules/book/src/Plugin/migrate/destination/Book.php
index f6ff9325a..784e7a1ee 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/Book.php
+++ b/core/modules/book/src/Plugin/migrate/destination/Book.php
@@ -2,12 +2,13 @@
/**
* @file
- * Contains \Drupal\migrate\Plugin\migrate\destination\Book.
+ * Contains \Drupal\book\Plugin\migrate\destination\Book.
*/
-namespace Drupal\migrate\Plugin\migrate\destination;
+namespace Drupal\book\Plugin\migrate\destination;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
use Drupal\migrate\Row;
/**
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/Book.php b/core/modules/book/src/Plugin/migrate/source/d6/Book.php
similarity index 93%
rename from core/modules/migrate_drupal/src/Plugin/migrate/source/d6/Book.php
rename to core/modules/book/src/Plugin/migrate/source/d6/Book.php
index ed02351fc..a1ecbb6b3 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/Book.php
+++ b/core/modules/book/src/Plugin/migrate/source/d6/Book.php
@@ -2,10 +2,10 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Plugin\migrate\source\d6\Book.
+ * Contains \Drupal\book\Plugin\migrate\source\d6\Book.
*/
-namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
+namespace Drupal\book\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
diff --git a/core/modules/book/src/ProxyClass/BookUninstallValidator.php b/core/modules/book/src/ProxyClass/BookUninstallValidator.php
new file mode 100644
index 000000000..12f91bbd7
--- /dev/null
+++ b/core/modules/book/src/ProxyClass/BookUninstallValidator.php
@@ -0,0 +1,92 @@
+container = $container;
+ $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
+ }
+
+ /**
+ * Lazy loads the real service from the container.
+ *
+ * @return object
+ * Returns the constructed real service.
+ */
+ protected function lazyLoadItself()
+ {
+ if (!isset($this->service)) {
+ $this->service = $this->container->get($this->drupalProxyOriginalServiceId);
+ }
+
+ return $this->service;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($module)
+ {
+ return $this->lazyLoadItself()->validate($module);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setStringTranslation(\Drupal\Core\StringTranslation\TranslationInterface $translation)
+ {
+ return $this->lazyLoadItself()->setStringTranslation($translation);
+ }
+
+ }
+
+}
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateBookConfigsTest.php b/core/modules/book/src/Tests/Migrate/d6/MigrateBookConfigsTest.php
similarity index 67%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateBookConfigsTest.php
rename to core/modules/book/src/Tests/Migrate/d6/MigrateBookConfigsTest.php
index deb320890..9ae103057 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateBookConfigsTest.php
+++ b/core/modules/book/src/Tests/Migrate/d6/MigrateBookConfigsTest.php
@@ -2,20 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateBookConfigsTest.
+ * Contains \Drupal\book\Tests\Migrate\d6\MigrateBookConfigsTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\book\Tests\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
-use Drupal\migrate\MigrateMessage;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to book.settings.yml.
*
- * @group migrate_drupal
+ * @group book
*/
class MigrateBookConfigsTest extends MigrateDrupal6TestBase {
@@ -33,14 +31,8 @@ class MigrateBookConfigsTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
-
- $migration = entity_load('migration', 'd6_book_settings');
- $dumps = array(
- $this->getDumpDirectory() . '/Variable.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, new MigrateMessage());
- $executable->import();
+ $this->loadDumps(['Variable.php']);
+ $this->executeMigration('d6_book_settings');
}
/**
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateBookTest.php b/core/modules/book/src/Tests/Migrate/d6/MigrateBookTest.php
similarity index 83%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateBookTest.php
rename to core/modules/book/src/Tests/Migrate/d6/MigrateBookTest.php
index a98e8278f..ea7846e1b 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateBookTest.php
+++ b/core/modules/book/src/Tests/Migrate/d6/MigrateBookTest.php
@@ -2,19 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateBookTest.
+ * Contains \Drupal\book\Tests\Migrate\d6\MigrateBookTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\book\Tests\Migrate\d6;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
use Drupal\node\Entity\Node;
/**
* Upgrade book structure.
*
- * @group migrate_drupal
+ * @group book
*/
class MigrateBookTest extends MigrateDrupal6TestBase {
@@ -44,15 +43,8 @@ class MigrateBookTest extends MigrateDrupal6TestBase {
}
$this->prepareMigrations($id_mappings);
// Load database dumps to provide source data.
- $dumps = array(
- $this->getDumpDirectory() . '/Book.php',
- $this->getDumpDirectory() . '/MenuLinks.php',
- );
- $this->loadDumps($dumps);
- // Migrate books..
- $migration = entity_load('migration', 'd6_book');
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
+ $this->loadDumps(['Book.php', 'MenuLinks.php']);
+ $this->executeMigration('d6_book');
}
/**
diff --git a/core/modules/breakpoint/src/BreakpointManager.php b/core/modules/breakpoint/src/BreakpointManager.php
index b23418f41..66a0830e0 100644
--- a/core/modules/breakpoint/src/BreakpointManager.php
+++ b/core/modules/breakpoint/src/BreakpointManager.php
@@ -169,6 +169,7 @@ class BreakpointManager extends DefaultPluginManager implements BreakpointManage
$this->breakpointsByGroup[$group] = $breakpoints;
}
}
+
$instances = array();
foreach ($this->breakpointsByGroup[$group] as $plugin_id => $definition) {
if (!isset($this->instances[$plugin_id])) {
diff --git a/core/modules/breakpoint/src/Tests/BreakpointDiscoveryTest.php b/core/modules/breakpoint/src/Tests/BreakpointDiscoveryTest.php
index b39c2a814..0668c86f7 100644
--- a/core/modules/breakpoint/src/Tests/BreakpointDiscoveryTest.php
+++ b/core/modules/breakpoint/src/Tests/BreakpointDiscoveryTest.php
@@ -34,34 +34,22 @@ class BreakpointDiscoveryTest extends KernelTestBase {
public function testThemeBreakpoints() {
// Verify the breakpoint group for breakpoint_theme_test was created.
$expected_breakpoints = array(
- 'breakpoint_theme_test.tv' => array(
- 'label' => 'tv',
- 'mediaQuery' => 'only screen and (min-width: 1220px)',
+ 'breakpoint_theme_test.mobile' => array(
+ 'label' => 'mobile',
+ 'mediaQuery' => '(min-width: 0px)',
'weight' => 0,
'multipliers' => array(
'1x',
),
'provider' => 'breakpoint_theme_test',
- 'id' => 'breakpoint_theme_test.tv',
- 'group' => 'breakpoint_theme_test',
- 'class' => 'Drupal\\breakpoint\\Breakpoint',
- ),
- 'breakpoint_theme_test.wide' => array(
- 'label' => 'wide',
- 'mediaQuery' => '(min-width: 851px)',
- 'weight' => 1,
- 'multipliers' => array(
- '1x',
- ),
- 'provider' => 'breakpoint_theme_test',
- 'id' => 'breakpoint_theme_test.wide',
+ 'id' => 'breakpoint_theme_test.mobile',
'group' => 'breakpoint_theme_test',
'class' => 'Drupal\\breakpoint\\Breakpoint',
),
'breakpoint_theme_test.narrow' => array(
'label' => 'narrow',
'mediaQuery' => '(min-width: 560px)',
- 'weight' => 2,
+ 'weight' => 1,
'multipliers' => array(
'1x',
),
@@ -70,15 +58,27 @@ class BreakpointDiscoveryTest extends KernelTestBase {
'group' => 'breakpoint_theme_test',
'class' => 'Drupal\\breakpoint\\Breakpoint',
),
- 'breakpoint_theme_test.mobile' => array(
- 'label' => 'mobile',
- 'mediaQuery' => '(min-width: 0px)',
+ 'breakpoint_theme_test.wide' => array(
+ 'label' => 'wide',
+ 'mediaQuery' => '(min-width: 851px)',
+ 'weight' => 2,
+ 'multipliers' => array(
+ '1x',
+ ),
+ 'provider' => 'breakpoint_theme_test',
+ 'id' => 'breakpoint_theme_test.wide',
+ 'group' => 'breakpoint_theme_test',
+ 'class' => 'Drupal\\breakpoint\\Breakpoint',
+ ),
+ 'breakpoint_theme_test.tv' => array(
+ 'label' => 'tv',
+ 'mediaQuery' => 'only screen and (min-width: 1220px)',
'weight' => 3,
'multipliers' => array(
'1x',
),
'provider' => 'breakpoint_theme_test',
- 'id' => 'breakpoint_theme_test.mobile',
+ 'id' => 'breakpoint_theme_test.tv',
'group' => 'breakpoint_theme_test',
'class' => 'Drupal\\breakpoint\\Breakpoint',
),
@@ -102,7 +102,7 @@ class BreakpointDiscoveryTest extends KernelTestBase {
'breakpoint_theme_test.group2.narrow' => array(
'label' => 'narrow',
'mediaQuery' => '(min-width: 560px)',
- 'weight' => 2,
+ 'weight' => 0,
'multipliers' => array(
'1x',
'2x',
@@ -128,7 +128,7 @@ class BreakpointDiscoveryTest extends KernelTestBase {
'breakpoint_module_test.breakpoint_theme_test.group2.tv' => array(
'label' => 'tv',
'mediaQuery' => '(min-width: 6000px)',
- 'weight' => 0,
+ 'weight' => 2,
'multipliers' => array(
'1x',
),
@@ -153,7 +153,7 @@ class BreakpointDiscoveryTest extends KernelTestBase {
'breakpoint_module_test.mobile' => array(
'label' => 'mobile',
'mediaQuery' => '(min-width: 0px)',
- 'weight' => 1,
+ 'weight' => 0,
'multipliers' => array(
'1x',
),
@@ -165,7 +165,7 @@ class BreakpointDiscoveryTest extends KernelTestBase {
'breakpoint_module_test.standard' => array(
'label' => 'standard',
'mediaQuery' => '(min-width: 560px)',
- 'weight' => 0,
+ 'weight' => 1,
'multipliers' => array(
'1x',
'2x',
@@ -178,9 +178,7 @@ class BreakpointDiscoveryTest extends KernelTestBase {
);
$breakpoints = \Drupal::service('breakpoint.manager')->getBreakpointsByGroup('breakpoint_module_test');
- foreach ($expected_breakpoints as $id => $expected_breakpoint) {
- $this->assertEqual($expected_breakpoint, $breakpoints[$id]->getPluginDefinition());
- }
+ $this->assertEqual(array_keys($expected_breakpoints), array_keys($breakpoints));
}
/**
diff --git a/core/modules/breakpoint/tests/modules/breakpoint_module_test/breakpoint_module_test.breakpoints.yml b/core/modules/breakpoint/tests/modules/breakpoint_module_test/breakpoint_module_test.breakpoints.yml
index e1edeb235..c490e5f6f 100644
--- a/core/modules/breakpoint/tests/modules/breakpoint_module_test/breakpoint_module_test.breakpoints.yml
+++ b/core/modules/breakpoint/tests/modules/breakpoint_module_test/breakpoint_module_test.breakpoints.yml
@@ -1,12 +1,12 @@
breakpoint_module_test.mobile:
label: mobile
mediaQuery: '(min-width: 0px)'
- weight: 1
+ weight: 0
# Don't include multipliers. A 1x multiplier this will be enforced by default.
breakpoint_module_test.standard:
label: standard
mediaQuery: '(min-width: 560px)'
- weight: 0
+ weight: 1
# Don't include a 1x multiplier this will be enforced by default.
multipliers:
- 2x
@@ -15,7 +15,7 @@ breakpoint_module_test.standard:
breakpoint_module_test.breakpoint_theme_test.group2.tv:
label: tv
mediaQuery: '(min-width: 6000px)'
- weight: 0
+ weight: 2
multipliers:
- 1x
group: breakpoint_theme_test.group2
diff --git a/core/modules/breakpoint/tests/themes/breakpoint_theme_test/breakpoint_theme_test.breakpoints.yml b/core/modules/breakpoint/tests/themes/breakpoint_theme_test/breakpoint_theme_test.breakpoints.yml
index 3e565e18f..f07aba8dc 100644
--- a/core/modules/breakpoint/tests/themes/breakpoint_theme_test/breakpoint_theme_test.breakpoints.yml
+++ b/core/modules/breakpoint/tests/themes/breakpoint_theme_test/breakpoint_theme_test.breakpoints.yml
@@ -1,32 +1,32 @@
breakpoint_theme_test.mobile:
label: mobile
mediaQuery: '(min-width: 0px)'
- weight: 3
+ weight: 0
multipliers:
- 1x
breakpoint_theme_test.narrow:
label: narrow
mediaQuery: '(min-width: 560px)'
- weight: 2
+ weight: 1
multipliers:
- 1x
# Out of order breakpoint to test sorting.
breakpoint_theme_test.tv:
label: tv
mediaQuery: 'only screen and (min-width: 1220px)'
- weight: 0
+ weight: 3
multipliers:
- 1x
breakpoint_theme_test.wide:
label: wide
mediaQuery: '(min-width: 851px)'
- weight: 1
+ weight: 2
multipliers:
- 1x
breakpoint_theme_test.group2.narrow:
label: narrow
mediaQuery: '(min-width: 560px)'
- weight: 2
+ weight: 0
multipliers:
- 1x
- 2x
diff --git a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
index 0e237b17e..d5bd0b9a7 100644
--- a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
+++ b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php
@@ -157,7 +157,7 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
'library' => array('ckeditor/drupal.ckeditor.admin'),
'drupalSettings' => [
'ckeditor' => [
- 'toolbarAdmin' => $this->renderer->renderPlain($ckeditor_settings_toolbar),
+ 'toolbarAdmin' => (string) $this->renderer->renderPlain($ckeditor_settings_toolbar),
],
],
),
diff --git a/core/modules/ckeditor/src/Tests/CKEditorAdminTest.php b/core/modules/ckeditor/src/Tests/CKEditorAdminTest.php
index d113a7609..3ce82714a 100644
--- a/core/modules/ckeditor/src/Tests/CKEditorAdminTest.php
+++ b/core/modules/ckeditor/src/Tests/CKEditorAdminTest.php
@@ -117,6 +117,18 @@ class CKEditorAdminTest extends WebTestBase {
$editor = entity_load('editor', 'filtered_html');
$this->assertFalse($editor, 'No Editor config entity exists yet.');
+ // Ensure that drupalSettings is correct.
+ $ckeditor_settings_toolbar = array(
+ '#theme' => 'ckeditor_settings_toolbar',
+ '#editor' => Editor::create(['editor' => 'ckeditor']),
+ '#plugins' => $this->container->get('plugin.manager.ckeditor.plugin')->getButtons(),
+ );
+ $this->assertEqual(
+ $this->drupalSettings['ckeditor']['toolbarAdmin'],
+ $this->container->get('renderer')->renderPlain($ckeditor_settings_toolbar),
+ 'CKEditor toolbar settings are rendered as part of drupalSettings.'
+ );
+
// Ensure the toolbar buttons configuration value is initialized to the
// expected default value.
$expected_buttons_value = json_encode($expected_default_settings['toolbar']['rows']);
diff --git a/core/modules/color/color.js b/core/modules/color/color.js
index 1a9d8a579..b41672e54 100644
--- a/core/modules/color/color.js
+++ b/core/modules/color/color.js
@@ -8,7 +8,12 @@
"use strict";
/**
+ * Displays farbtastic color selector and initialize color administration UI.
+ *
* @type {Drupal~behavior}
+ *
+ * @prop {Drupal~behaviorAttach} attach
+ * Attach color selection behavior to relevant context.
*/
Drupal.behaviors.color = {
attach: function (context, settings) {
@@ -95,10 +100,14 @@
* @function Drupal.color~shift_color
*
* @param {string} given
- * @param {Array} ref1
- * @param {Array} ref2
+ * A hex color code to shift.
+ * @param {Array.} ref1
+ * First HSL color reference.
+ * @param {Array.} ref2
+ * Second HSL color reference.
*
* @return {string}
+ * A hex color, shifted.
*/
function shift_color(given, ref1, ref2) {
var d;
@@ -143,9 +152,14 @@
* Callback for Farbtastic when a new color is chosen.
*
* @param {HTMLElement} input
+ * The input element where the color is chosen.
* @param {string} color
+ * The color that was chosen through the input.
* @param {bool} propagate
+ * Whether or not to propagate the color to a locked pair value
* @param {bool} colorScheme
+ * Flag to indicate if the user is using a color scheme when changing
+ * the color.
*/
function callback(input, color, propagate, colorScheme) {
var matched;
@@ -201,6 +215,7 @@
* Focuses Farbtastic on a particular field.
*
* @param {jQuery.Event} e
+ * The focus event on the field.
*/
function focus(e) {
var input = e.target;
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index f5622c833..fa9499b10 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -76,25 +76,27 @@ function color_library_info_alter(&$libraries, $extension) {
$color_paths = \Drupal::config('color.theme.' . $extension)->get('stylesheets');
if (!empty($color_paths)) {
foreach (array_keys($libraries) as $name) {
- // Override stylesheets.
- foreach ($libraries[$name]['css'] as $category => $css_assets) {
- foreach ($css_assets as $path => $metadata) {
- // Loop over the path array with recolored CSS files to find matching
- // paths which could replace the non-recolored paths.
- foreach ($color_paths as $color_path) {
- // Color module currently requires unique file names to be used,
- // which allows us to compare different file paths.
- if (drupal_basename($path) == drupal_basename($color_path)) {
- // Replace the path to the new css file.
- // This keeps the order of the stylesheets intact.
- $index = array_search($path, array_keys($libraries[$name]['css'][$category]));
- $preceding_css_assets = array_slice($libraries[$name]['css'][$category], 0, $index);
- $succeeding_css_assets = array_slice($libraries[$name]['css'][$category], $index + 1);
- $libraries[$name]['css'][$category] = array_merge(
- $preceding_css_assets,
- [$color_path => $metadata],
- $succeeding_css_assets
- );
+ if(isset($libraries[$name]['css'])) {
+ // Override stylesheets.
+ foreach ($libraries[$name]['css'] as $category => $css_assets) {
+ foreach ($css_assets as $path => $metadata) {
+ // Loop over the path array with recolored CSS files to find matching
+ // paths which could replace the non-recolored paths.
+ foreach ($color_paths as $color_path) {
+ // Color module currently requires unique file names to be used,
+ // which allows us to compare different file paths.
+ if (drupal_basename($path) == drupal_basename($color_path)) {
+ // Replace the path to the new css file.
+ // This keeps the order of the stylesheets intact.
+ $index = array_search($path, array_keys($libraries[$name]['css'][$category]));
+ $preceding_css_assets = array_slice($libraries[$name]['css'][$category], 0, $index);
+ $succeeding_css_assets = array_slice($libraries[$name]['css'][$category], $index + 1);
+ $libraries[$name]['css'][$category] = array_merge(
+ $preceding_css_assets,
+ [$color_path => $metadata],
+ $succeeding_css_assets
+ );
+ }
}
}
}
@@ -284,7 +286,7 @@ function template_preprocess_color_scheme_form(&$variables) {
// Attempt to load preview HTML if the theme provides it.
$preview_html_path = \Drupal::root() . '/' . (isset($info['preview_html']) ? drupal_get_path('theme', $theme) . '/' . $info['preview_html'] : drupal_get_path('module', 'color') . '/preview.html');
- $variables['html_preview'] = SafeMarkup::set(file_get_contents($preview_html_path));
+ $variables['html_preview']['#markup'] = file_get_contents($preview_html_path);
}
/**
diff --git a/core/modules/color/preview.js b/core/modules/color/preview.js
index 956b53ef5..551cfd7a1 100644
--- a/core/modules/color/preview.js
+++ b/core/modules/color/preview.js
@@ -8,17 +8,27 @@
"use strict";
/**
+ * Namespace for color-related functionality for Drupal.
+ *
* @namespace
*/
Drupal.color = {
/**
+ * The callback for when the color preview has been attached.
+ *
* @param {Element} context
+ * The context to initiate the color behaviour.
* @param {object} settings
+ * Settings for the color functionality.
* @param {HTMLFormElement} form
+ * The form to initiate the color behaviour on.
* @param {object} farb
+ * The farbtastic object.
* @param {number} height
+ * Height of gradient.
* @param {number} width
+ * Width of gradient.
*/
callback: function (context, settings, form, farb, height, width) {
var accum;
diff --git a/core/modules/color/src/Tests/ColorSafePreviewTest.php b/core/modules/color/src/Tests/ColorSafePreviewTest.php
new file mode 100644
index 000000000..9e42f96db
--- /dev/null
+++ b/core/modules/color/src/Tests/ColorSafePreviewTest.php
@@ -0,0 +1,63 @@
+bigUser = $this->drupalCreateUser(['administer themes']);
+ }
+
+ /**
+ * Ensures color preview.html is sanitized.
+ */
+ function testColorPreview() {
+ // Install the color test theme.
+ \Drupal::service('theme_handler')->install(['color_test_theme']);
+ $this->drupalLogin($this->bigUser);
+
+ // Markup is being printed from a HTML file located in:
+ // core/modules/color/tests/modules/color_test/themes/color_test_theme/color/preview.html
+ $url = Url::fromRoute('system.theme_settings_theme', ['theme' => 'color_test_theme']);
+ $this->drupalGet($url);
+ $this->assertText('TEST COLOR PREVIEW');
+
+ $this->assertNoRaw('');
+ $this->assertRaw('TEST COLOR PREVIEW ');
+ }
+
+
+}
diff --git a/core/modules/color/tests/modules/color_test/themes/color_test_theme/color/color.inc b/core/modules/color/tests/modules/color_test/themes/color_test_theme/color/color.inc
index bf0affe5a..b88e8eae7 100644
--- a/core/modules/color/tests/modules/color_test/themes/color_test_theme/color/color.inc
+++ b/core/modules/color/tests/modules/color_test/themes/color_test_theme/color/color.inc
@@ -29,4 +29,5 @@ $info = array(
'css' => array(
'css/colors.css',
),
+ 'preview_html' => 'color/preview.html',
);
diff --git a/core/modules/color/tests/modules/color_test/themes/color_test_theme/color/preview.html b/core/modules/color/tests/modules/color_test/themes/color_test_theme/color/preview.html
new file mode 100644
index 000000000..f7346ca6b
--- /dev/null
+++ b/core/modules/color/tests/modules/color_test/themes/color_test_theme/color/preview.html
@@ -0,0 +1,8 @@
+
+
+
TEST COLOR PREVIEW
+
Sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+
+
diff --git a/core/modules/color/tests/modules/color_test/themes/color_test_theme/color_test_theme.libraries.yml b/core/modules/color/tests/modules/color_test/themes/color_test_theme/color_test_theme.libraries.yml
index afe431838..e4d536dc4 100644
--- a/core/modules/color/tests/modules/color_test/themes/color_test_theme/color_test_theme.libraries.yml
+++ b/core/modules/color/tests/modules/color_test/themes/color_test_theme/color_test_theme.libraries.yml
@@ -3,3 +3,10 @@ base:
css:
theme:
css/colors.css: {}
+
+fontsizer:
+ version: VERSION
+ js:
+ js/color_test_theme-fontsize.js: {}
+ dependencies:
+ - core/jquery
diff --git a/core/modules/color/tests/modules/color_test/themes/color_test_theme/js/color_test_theme-fontsize.js b/core/modules/color/tests/modules/color_test/themes/color_test_theme/js/color_test_theme-fontsize.js
new file mode 100644
index 000000000..109f7438a
--- /dev/null
+++ b/core/modules/color/tests/modules/color_test/themes/color_test_theme/js/color_test_theme-fontsize.js
@@ -0,0 +1,9 @@
+/**
+ * @file
+ * Adds javascript functions for font resizing.
+ */
+(function ($) {
+ "use strict";
+
+ $(document).ready(function () {});
+})(jQuery);
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 8bcac2ced..20ba4a3b1 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -257,6 +257,9 @@ function comment_node_view_alter(array &$build, EntityInterface $node, EntityVie
*
* @return array
* An array as expected by drupal_render().
+ *
+ * @deprecated in Drupal 8.x and will be removed before Drupal 9.0.
+ * Use \Drupal::entityManager()->getViewBuilder('comment')->view().
*/
function comment_view(CommentInterface $comment, $view_mode = 'full', $langcode = NULL) {
return entity_view($comment, $view_mode, $langcode);
@@ -277,6 +280,9 @@ function comment_view(CommentInterface $comment, $view_mode = 'full', $langcode
* @return array
* An array in the format expected by drupal_render().
*
+ * @deprecated in Drupal 8.x and will be removed before Drupal 9.0.
+ * Use \Drupal::entityManager()->getViewBuilder('comment')->viewMultiple().
+ *
* @see drupal_render()
*/
function comment_view_multiple($comments, $view_mode = 'full', $langcode = NULL) {
@@ -339,7 +345,8 @@ function comment_entity_storage_load($entities, $entity_type) {
if (!\Drupal::entityManager()->getDefinition($entity_type)->isSubclassOf('Drupal\Core\Entity\FieldableEntityInterface')) {
return;
}
- if (!\Drupal::service('comment.manager')->getFields($entity_type)) {
+ // @todo Investigate in https://www.drupal.org/node/2527866 why we need that.
+ if (!\Drupal::hasService('comment.manager') || !\Drupal::service('comment.manager')->getFields($entity_type)) {
// Do not query database when entity has no comment fields.
return;
}
diff --git a/core/modules/comment/comment.tokens.inc b/core/modules/comment/comment.tokens.inc
index 1d20b112e..3a73f39b6 100644
--- a/core/modules/comment/comment.tokens.inc
+++ b/core/modules/comment/comment.tokens.inc
@@ -7,6 +7,8 @@
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
+use Drupal\Core\Datetime\Entity\DateFormat;
+use Drupal\Core\Render\BubbleableMetadata;
/**
* Implements hook_token_info().
@@ -105,7 +107,7 @@ function comment_token_info() {
/**
* Implements hook_tokens().
*/
-function comment_tokens($type, $tokens, array $data = array(), array $options = array()) {
+function comment_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
$token_service = \Drupal::token();
$url_options = array('absolute' => TRUE);
@@ -138,6 +140,11 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
case 'mail':
$mail = $comment->getAuthorEmail();
+ // Add the user cacheability metadata in case the author of the comment
+ // is not the anonymous user.
+ if ($comment->getOwnerId()) {
+ $bubbleable_metadata->addCacheableDependency($comment->getOwner());
+ }
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($mail) : $mail;
break;
@@ -170,26 +177,37 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
case 'author':
$name = $comment->getAuthorName();
+ // Add the user cacheability metadata in case the author of the comment
+ // is not the anonymous user.
+ if ($comment->getOwnerId()) {
+ $bubbleable_metadata->addCacheableDependency($comment->getOwner());
+ }
$replacements[$original] = $sanitize ? Xss::filter($name) : $name;
break;
case 'parent':
if ($comment->hasParentComment()) {
$parent = $comment->getParentComment();
+ $bubbleable_metadata->addCacheableDependency($parent);
$replacements[$original] = $sanitize ? Xss::filter($parent->getSubject()) : $parent->getSubject();
}
break;
case 'created':
+ $date_format = DateFormat::load('medium');
+ $bubbleable_metadata->addCacheableDependency($date_format);
$replacements[$original] = format_date($comment->getCreatedTime(), 'medium', '', NULL, $langcode);
break;
case 'changed':
+ $date_format = DateFormat::load('medium');
+ $bubbleable_metadata->addCacheableDependency($date_format);
$replacements[$original] = format_date($comment->getChangedTime(), 'medium', '', NULL, $langcode);
break;
case 'entity':
$entity = $comment->getCommentedEntity();
+ $bubbleable_metadata->addCacheableDependency($entity);
$title = $entity->label();
$replacements[$original] = $sanitize ? Xss::filter($title) : $title;
break;
@@ -199,23 +217,23 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
// Chained token relationships.
if ($entity_tokens = $token_service->findwithPrefix($tokens, 'entity')) {
$entity = $comment->getCommentedEntity();
- $replacements += $token_service->generate($comment->getCommentedEntityTypeId(), $entity_tokens, array($comment->getCommentedEntityTypeId() => $entity), $options);
+ $replacements += $token_service->generate($comment->getCommentedEntityTypeId(), $entity_tokens, array($comment->getCommentedEntityTypeId() => $entity), $options, $bubbleable_metadata);
}
if ($date_tokens = $token_service->findwithPrefix($tokens, 'created')) {
- $replacements += $token_service->generate('date', $date_tokens, array('date' => $comment->getCreatedTime()), $options);
+ $replacements += $token_service->generate('date', $date_tokens, array('date' => $comment->getCreatedTime()), $options, $bubbleable_metadata);
}
if ($date_tokens = $token_service->findwithPrefix($tokens, 'changed')) {
- $replacements += $token_service->generate('date', $date_tokens, array('date' => $comment->getChangedTime()), $options);
+ $replacements += $token_service->generate('date', $date_tokens, array('date' => $comment->getChangedTime()), $options, $bubbleable_metadata);
}
if (($parent_tokens = $token_service->findwithPrefix($tokens, 'parent')) && $parent = $comment->getParentComment()) {
- $replacements += $token_service->generate('comment', $parent_tokens, array('comment' => $parent), $options);
+ $replacements += $token_service->generate('comment', $parent_tokens, array('comment' => $parent), $options, $bubbleable_metadata);
}
if (($author_tokens = $token_service->findwithPrefix($tokens, 'author')) && $account = $comment->getOwner()) {
- $replacements += $token_service->generate('user', $author_tokens, array('user' => $account), $options);
+ $replacements += $token_service->generate('user', $author_tokens, array('user' => $account), $options, $bubbleable_metadata);
}
}
elseif ($type == 'entity' & !empty($data['entity'])) {
diff --git a/core/modules/comment/js/comment-new-indicator.js b/core/modules/comment/js/comment-new-indicator.js
index 8a2bf074c..756796381 100644
--- a/core/modules/comment/js/comment-new-indicator.js
+++ b/core/modules/comment/js/comment-new-indicator.js
@@ -65,7 +65,7 @@
var $comment = $(placeholder)
.removeClass('hidden')
.text(newCommentString)
- .closest('.comment')
+ .closest('.js-comment')
// Add 'new' class to the comment, so it can be styled.
.addClass('new');
diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php
index 0bcacee9e..7dcb4c807 100644
--- a/core/modules/comment/src/CommentForm.php
+++ b/core/modules/comment/src/CommentForm.php
@@ -67,22 +67,6 @@ class CommentForm extends ContentEntityForm {
$this->renderer = $renderer;
}
- /**
- * {@inheritdoc}
- */
- protected function init(FormStateInterface $form_state) {
- $comment = $this->entity;
-
- // Make the comment inherit the current content language unless specifically
- // set.
- if ($comment->isNew()) {
- $language_content = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT);
- $comment->langcode->value = $language_content->getId();
- }
-
- parent::init($form_state);
- }
-
/**
* Overrides Drupal\Core\Entity\EntityForm::form().
*/
@@ -258,7 +242,6 @@ class CommentForm extends ContentEntityForm {
'#type' => 'submit',
'#value' => $this->t('Preview'),
'#access' => $preview_mode != DRUPAL_DISABLED,
- '#validate' => array('::validate'),
'#submit' => array('::submitForm', '::preview'),
);
diff --git a/core/modules/comment/src/CommentLazyBuilders.php b/core/modules/comment/src/CommentLazyBuilders.php
index 9d74881b6..d37ab611e 100644
--- a/core/modules/comment/src/CommentLazyBuilders.php
+++ b/core/modules/comment/src/CommentLazyBuilders.php
@@ -12,9 +12,9 @@ use Drupal\Core\Entity\EntityFormBuilderInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
-use Drupal\Core\Render\Renderer;
/**
* Defines a service for comment #lazy_builder callbacks.
@@ -59,7 +59,7 @@ class CommentLazyBuilders {
/**
* The renderer service.
*
- * @var \Drupal\Core\Render\Renderer
+ * @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
@@ -76,10 +76,10 @@ class CommentLazyBuilders {
* The comment manager service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
- * @param \Drupal\Core\Render\Renderer $renderer
+ * @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
*/
- public function __construct(EntityManagerInterface $entity_manager, EntityFormBuilderInterface $entity_form_builder, AccountInterface $current_user, CommentManagerInterface $comment_manager, ModuleHandlerInterface $module_handler, Renderer $renderer) {
+ public function __construct(EntityManagerInterface $entity_manager, EntityFormBuilderInterface $entity_form_builder, AccountInterface $current_user, CommentManagerInterface $comment_manager, ModuleHandlerInterface $module_handler, RendererInterface $renderer) {
$this->entityManager = $entity_manager;
$this->entityFormBuilder = $entity_form_builder;
$this->currentUser = $current_user;
diff --git a/core/modules/comment/src/CommentTypeListBuilder.php b/core/modules/comment/src/CommentTypeListBuilder.php
index 6f42f8467..4f998718e 100644
--- a/core/modules/comment/src/CommentTypeListBuilder.php
+++ b/core/modules/comment/src/CommentTypeListBuilder.php
@@ -46,7 +46,7 @@ class CommentTypeListBuilder extends ConfigEntityListBuilder {
*/
public function buildRow(EntityInterface $entity) {
$row['type'] = SafeMarkup::checkPlain($entity->label());
- $row['description'] = Xss::filterAdmin($entity->getDescription());
+ $row['description']['data'] = ['#markup' => $entity->getDescription()];
return $row + parent::buildRow($entity);
}
diff --git a/core/modules/comment/src/CommentViewBuilder.php b/core/modules/comment/src/CommentViewBuilder.php
index 692322046..66bb19315 100644
--- a/core/modules/comment/src/CommentViewBuilder.php
+++ b/core/modules/comment/src/CommentViewBuilder.php
@@ -71,11 +71,9 @@ class CommentViewBuilder extends EntityViewBuilder {
->getFieldDefinition($entity->getFieldName())
->getSetting('default_mode') === CommentManagerInterface::COMMENT_MODE_THREADED;
// If threading is enabled, don't render cache individual comments, but do
- // keep the cache tags, so they can bubble up.
+ // keep the cacheability metadata, so it can bubble up.
if ($build['#comment_threaded']) {
- $cache_tags = $build['#cache']['tags'];
- $build['#cache'] = [];
- $build['#cache']['tags'] = $cache_tags;
+ unset($build['#cache']['keys']);
}
return $build;
diff --git a/core/modules/comment/src/Controller/CommentController.php b/core/modules/comment/src/Controller/CommentController.php
index add533331..e52e3b9f6 100644
--- a/core/modules/comment/src/Controller/CommentController.php
+++ b/core/modules/comment/src/Controller/CommentController.php
@@ -11,6 +11,7 @@ use Drupal\comment\CommentInterface;
use Drupal\comment\CommentManagerInterface;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Cache\CacheableResponseInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
@@ -129,7 +130,8 @@ class CommentController extends ControllerBase {
// Find the current display page for this comment.
$page = $this->entityManager()->getStorage('comment')->getDisplayOrdinal($comment, $field_definition->getSetting('default_mode'), $field_definition->getSetting('per_page'));
// @todo: Cleaner sub request handling.
- $redirect_request = Request::create($entity->url(), 'GET', $request->query->all(), $request->cookies->all(), array(), $request->server->all());
+ $subrequest_url = $entity->urlInfo()->toString(TRUE);
+ $redirect_request = Request::create($subrequest_url->getGeneratedUrl(), 'GET', $request->query->all(), $request->cookies->all(), array(), $request->server->all());
$redirect_request->query->set('page', $page);
// Carry over the session to the subrequest.
if ($session = $request->getSession()) {
@@ -137,7 +139,16 @@ class CommentController extends ControllerBase {
}
// @todo: Convert the pager to use the request object.
$request->query->set('page', $page);
- return $this->httpKernel->handle($redirect_request, HttpKernelInterface::SUB_REQUEST);
+ $response = $this->httpKernel->handle($redirect_request, HttpKernelInterface::SUB_REQUEST);
+ if ($response instanceof CacheableResponseInterface) {
+ // @todo Once path aliases have cache tags (see
+ // https://www.drupal.org/node/2480077), add test coverage that
+ // the cache tag for a commented entity's path alias is added to the
+ // comment's permalink response, because there can be blocks or
+ // other content whose renderings depend on the subrequest's URL.
+ $response->addCacheableDependency($subrequest_url);
+ }
+ return $response;
}
throw new NotFoundHttpException();
}
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index bd3fbfc15..fc3411e25 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -81,7 +81,7 @@ class Comment extends ContentEntityBase implements CommentInterface {
}
if ($this->isNew()) {
// Add the comment to database. This next section builds the thread field.
- // Also see the documentation for comment_view().
+ // @see \Drupal\comment\CommentViewBuilder::buildComponents()
$thread = $this->getThread();
if (empty($thread)) {
if ($this->threadLock) {
diff --git a/core/modules/comment/src/Plugin/Action/UnpublishByKeywordComment.php b/core/modules/comment/src/Plugin/Action/UnpublishByKeywordComment.php
index 6b70450f7..370a7e6b0 100644
--- a/core/modules/comment/src/Plugin/Action/UnpublishByKeywordComment.php
+++ b/core/modules/comment/src/Plugin/Action/UnpublishByKeywordComment.php
@@ -9,8 +9,12 @@ namespace Drupal\comment\Plugin\Action;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Action\ConfigurableActionBase;
+use Drupal\Core\Entity\EntityViewBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Unpublishes a comment containing certain keywords.
@@ -21,14 +25,62 @@ use Drupal\Core\Session\AccountInterface;
* type = "comment"
* )
*/
-class UnpublishByKeywordComment extends ConfigurableActionBase {
+class UnpublishByKeywordComment extends ConfigurableActionBase implements ContainerFactoryPluginInterface {
+
+ /**
+ * The comment entity builder handler.
+ *
+ * @var \Drupal\Core\Entity\EntityViewBuilderInterface
+ */
+ protected $viewBuilder;
+
+ /**
+ * The renderer.
+ *
+ * @var \Drupal\Core\Render\RendererInterface
+ */
+ protected $renderer;
+
+ /**
+ * Constructs a UnpublishByKeywordComment object.
+ *
+ * @param array $configuration
+ * A configuration array containing information about the plugin instance.
+ * @param string $plugin_id
+ * The plugin ID for the plugin instance.
+ * @param mixed $plugin_definition
+ * The plugin implementation definition.
+ * @param \Drupal\Core\Entity\EntityViewBuilderInterface $comment_view_builder
+ * The comment entity builder handler.
+ * @param \Drupal\Core\Render\RendererInterface $renderer
+ * The renderer.
+ */
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityViewBuilderInterface $comment_view_builder, RendererInterface $renderer) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+ $this->viewBuilder = $comment_view_builder;
+ $this->renderer = $renderer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $configuration,
+ $plugin_id,
+ $plugin_definition,
+ $container->get('entity.manager')->getViewBuilder('comment'),
+ $container->get('renderer')
+ );
+ }
/**
* {@inheritdoc}
*/
public function execute($comment = NULL) {
- $build = comment_view($comment);
- $text = \Drupal::service('renderer')->renderPlain($build);
+ $build = $this->viewBuilder->view($comment);
+ $text = $this->renderer->renderPlain($build);
foreach ($this->configuration['keywords'] as $keyword) {
if (strpos($text, $keyword) !== FALSE) {
$comment->setPublished(FALSE);
@@ -52,9 +104,9 @@ class UnpublishByKeywordComment extends ConfigurableActionBase {
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['keywords'] = array(
- '#title' => t('Keywords'),
+ '#title' => $this->t('Keywords'),
'#type' => 'textarea',
- '#description' => t('The comment will be unpublished if it contains any of the phrases above. Use a case-sensitive, comma-separated list of phrases. Example: funny, bungee jumping, "Company, Inc."'),
+ '#description' => $this->t('The comment will be unpublished if it contains any of the phrases above. Use a case-sensitive, comma-separated list of phrases. Example: funny, bungee jumping, "Company, Inc."'),
'#default_value' => Tags::implode($this->configuration['keywords']),
);
return $form;
diff --git a/core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php b/core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php
index 0b8f6da46..bbbfa8eb4 100644
--- a/core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php
+++ b/core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php
@@ -10,12 +10,14 @@ namespace Drupal\comment\Plugin\Menu\LocalTask;
use Drupal\comment\CommentStorageInterface;
use Drupal\Core\Menu\LocalTaskDefault;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a local task that shows the amount of unapproved comments.
*/
class UnapprovedComments extends LocalTaskDefault implements ContainerFactoryPluginInterface {
+ use StringTranslationTrait;
/**
* The comment storage service.
@@ -57,7 +59,7 @@ class UnapprovedComments extends LocalTaskDefault implements ContainerFactoryPlu
* {@inheritdoc}
*/
public function getTitle() {
- return t('Unapproved comments (@count)', array('@count' => $this->commentStorage->getUnapprovedCount()));
+ return $this->t('Unapproved comments (@count)', array('@count' => $this->commentStorage->getUnapprovedCount()));
}
}
diff --git a/core/modules/comment/src/Tests/CommentCacheTagsTest.php b/core/modules/comment/src/Tests/CommentCacheTagsTest.php
index 52264340f..2393d96ab 100644
--- a/core/modules/comment/src/Tests/CommentCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentCacheTagsTest.php
@@ -86,11 +86,7 @@ class CommentCacheTagsTest extends EntityWithUriCacheTagsTestBase {
* {@inheritdoc}
*/
protected function getAdditionalCacheContextsForEntity(EntityInterface $entity) {
- return [
- // Field access for the user picture rendered as part of the node that
- // this comment is created on.
- 'user.permissions',
- ];
+ return [];
}
/**
diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
index 8e7bf271e..9c40b0c8d 100644
--- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
@@ -7,6 +7,7 @@
namespace Drupal\comment\Tests;
+use Drupal\Core\Cache\Cache;
use Drupal\Core\Session\UserSession;
use Drupal\comment\CommentInterface;
use Drupal\system\Tests\Entity\EntityUnitTestBase;
@@ -69,7 +70,8 @@ class CommentDefaultFormatterCacheTagsTest extends EntityUnitTestBase {
->getViewBuilder('entity_test')
->view($commented_entity);
$renderer->renderRoot($build);
- $expected_cache_tags = array(
+ $cache_context_tags = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($build['#cache']['contexts'])->getCacheTags();
+ $expected_cache_tags = Cache::mergeTags($cache_context_tags, [
'entity_test_view',
'entity_test:' . $commented_entity->id(),
'comment_list',
@@ -78,9 +80,9 @@ class CommentDefaultFormatterCacheTagsTest extends EntityUnitTestBase {
'config:field.field.entity_test.entity_test.comment',
'config:field.storage.comment.comment_body',
'config:user.settings',
- );
+ ]);
sort($expected_cache_tags);
- $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags before it has comments.');
+ $this->assertEqual($build['#cache']['tags'], $expected_cache_tags);
// Create a comment on that entity. Comment loading requires that the uid
// also exists in the {users} table.
@@ -111,7 +113,8 @@ class CommentDefaultFormatterCacheTagsTest extends EntityUnitTestBase {
->getViewBuilder('entity_test')
->view($commented_entity);
$renderer->renderRoot($build);
- $expected_cache_tags = array(
+ $cache_context_tags = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($build['#cache']['contexts'])->getCacheTags();
+ $expected_cache_tags = Cache::mergeTags($cache_context_tags, [
'entity_test_view',
'entity_test:' . $commented_entity->id(),
'comment_list',
@@ -125,9 +128,9 @@ class CommentDefaultFormatterCacheTagsTest extends EntityUnitTestBase {
'config:field.field.entity_test.entity_test.comment',
'config:field.storage.comment.comment_body',
'config:user.settings',
- );
+ ]);
sort($expected_cache_tags);
- $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags when it has comments.');
+ $this->assertEqual($build['#cache']['tags'], $expected_cache_tags);
}
}
diff --git a/core/modules/comment/src/Tests/CommentFieldsTest.php b/core/modules/comment/src/Tests/CommentFieldsTest.php
index a5efac081..ede2e919f 100644
--- a/core/modules/comment/src/Tests/CommentFieldsTest.php
+++ b/core/modules/comment/src/Tests/CommentFieldsTest.php
@@ -165,12 +165,12 @@ class CommentFieldsTest extends CommentTestBase {
// Install core content type module (book).
$edit = array();
$edit['modules[Core][book][enable]'] = 'book';
- $this->drupalPostForm('admin/modules', $edit, t('Save configuration'));
+ $this->drupalPostForm('admin/modules', $edit, t('Install'));
// Now install the comment module.
$edit = array();
$edit['modules[Core][comment][enable]'] = 'comment';
- $this->drupalPostForm('admin/modules', $edit, t('Save configuration'));
+ $this->drupalPostForm('admin/modules', $edit, t('Install'));
$this->rebuildContainer();
$this->assertTrue($this->container->get('module_handler')->moduleExists('comment'), 'Comment module enabled.');
diff --git a/core/modules/comment/src/Tests/CommentNonNodeTest.php b/core/modules/comment/src/Tests/CommentNonNodeTest.php
index ee08aa2c9..8d8e38bb5 100644
--- a/core/modules/comment/src/Tests/CommentNonNodeTest.php
+++ b/core/modules/comment/src/Tests/CommentNonNodeTest.php
@@ -180,7 +180,7 @@ class CommentNonNodeTest extends WebTestBase {
* @param bool $reply
* Boolean indicating whether the comment is a reply to another comment.
*
- * @return boolean
+ * @return bool
* Boolean indicating whether the comment was found.
*/
function commentExists(CommentInterface $comment = NULL, $reply = FALSE) {
@@ -201,7 +201,7 @@ class CommentNonNodeTest extends WebTestBase {
/**
* Checks whether the commenter's contact information is displayed.
*
- * @return boolean
+ * @return bool
* Contact info is available.
*/
function commentContactInfoAvailable() {
diff --git a/core/modules/comment/src/Tests/CommentRssTest.php b/core/modules/comment/src/Tests/CommentRssTest.php
index b82da4877..2a878591c 100644
--- a/core/modules/comment/src/Tests/CommentRssTest.php
+++ b/core/modules/comment/src/Tests/CommentRssTest.php
@@ -8,6 +8,7 @@
namespace Drupal\comment\Tests;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
+use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
@@ -52,16 +53,23 @@ class CommentRssTest extends CommentTestBase {
$this->postComment($this->node, $this->randomMachineName(), $this->randomMachineName());
$this->drupalGet('rss.xml');
- $this->assertCacheTags([
- 'config:views.view.frontpage', 'node:1', 'node_list', 'node_view', 'user:3',
- ]);
- $this->assertCacheContexts([
+ $cache_contexts = [
'languages:language_interface',
'theme',
+ 'url.site',
'user.node_grants:view',
'user.permissions',
'timezone',
- ]);
+ ];
+ $this->assertCacheContexts($cache_contexts);
+
+ $cache_context_tags = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts)->getCacheTags();
+ $this->assertCacheTags(Cache::mergeTags($cache_context_tags, [
+ 'config:views.view.frontpage',
+ 'node:1', 'node_list',
+ 'node_view',
+ 'user:3',
+ ]));
$raw = '' . $this->node->url('canonical', array('fragment' => 'comments', 'absolute' => TRUE)) . ' ';
$this->assertRaw($raw, 'Comments as part of RSS feed.');
diff --git a/core/modules/comment/src/Tests/CommentTestBase.php b/core/modules/comment/src/Tests/CommentTestBase.php
index 8ebc4e7dd..a52cbb397 100644
--- a/core/modules/comment/src/Tests/CommentTestBase.php
+++ b/core/modules/comment/src/Tests/CommentTestBase.php
@@ -182,7 +182,7 @@ abstract class CommentTestBase extends WebTestBase {
* @param bool $reply
* Boolean indicating whether the comment is a reply to another comment.
*
- * @return boolean
+ * @return bool
* Boolean indicating whether the comment was found.
*/
function commentExists(CommentInterface $comment = NULL, $reply = FALSE) {
@@ -323,7 +323,7 @@ abstract class CommentTestBase extends WebTestBase {
/**
* Checks whether the commenter's contact information is displayed.
*
- * @return boolean
+ * @return bool
* Contact info is available.
*/
function commentContactInfoAvailable() {
diff --git a/core/modules/comment/src/Tests/CommentTestTrait.php b/core/modules/comment/src/Tests/CommentTestTrait.php
index 58aa9c73b..d8c54c2ca 100644
--- a/core/modules/comment/src/Tests/CommentTestTrait.php
+++ b/core/modules/comment/src/Tests/CommentTestTrait.php
@@ -7,7 +7,6 @@
namespace Drupal\comment\Tests;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Unicode;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
@@ -42,10 +41,7 @@ trait CommentTestTrait {
$comment_type_storage = $entity_manager->getStorage('comment_type');
if ($comment_type = $comment_type_storage->load($comment_type_id)) {
if ($comment_type->getTargetEntityTypeId() !== $entity_type) {
- throw new \InvalidArgumentException(SafeMarkup::format('The given comment type id %id can only be used with the %entity_type entity type', array(
- '%id' => $comment_type_id,
- '%entity_type' => $entity_type,
- )));
+ throw new \InvalidArgumentException("The given comment type id $comment_type_id can only be used with the $entity_type entity type");
}
}
else {
diff --git a/core/modules/comment/src/Tests/CommentTokenReplaceTest.php b/core/modules/comment/src/Tests/CommentTokenReplaceTest.php
index 3c6b48a3d..c1f4303a2 100644
--- a/core/modules/comment/src/Tests/CommentTokenReplaceTest.php
+++ b/core/modules/comment/src/Tests/CommentTokenReplaceTest.php
@@ -10,6 +10,7 @@ namespace Drupal\comment\Tests;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
use Drupal\comment\Entity\Comment;
+use Drupal\Core\Render\BubbleableMetadata;
use Drupal\node\Entity\Node;
/**
@@ -60,6 +61,7 @@ class CommentTokenReplaceTest extends CommentTestBase {
$tests['[comment:langcode]'] = SafeMarkup::checkPlain($comment->language()->getId());
$tests['[comment:url]'] = $comment->url('canonical', $url_options + array('fragment' => 'comment-' . $comment->id()));
$tests['[comment:edit-url]'] = $comment->url('edit-form', $url_options);
+ $tests['[comment:created]'] = \Drupal::service('date.formatter')->format($comment->getCreatedTime(), 'medium', array('langcode' => $language_interface->getId()));
$tests['[comment:created:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($comment->getCreatedTime(), array('langcode' => $language_interface->getId()));
$tests['[comment:changed:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($comment->getChangedTimeAcrossTranslations(), array('langcode' => $language_interface->getId()));
$tests['[comment:parent:cid]'] = $comment->hasParentComment() ? $comment->getParentComment()->id() : NULL;
@@ -71,12 +73,48 @@ class CommentTokenReplaceTest extends CommentTestBase {
$tests['[comment:author:uid]'] = $comment->getOwnerId();
$tests['[comment:author:name]'] = SafeMarkup::checkPlain($this->adminUser->getUsername());
+ $base_bubbleable_metadata = BubbleableMetadata::createFromObject($comment);
+ $metadata_tests = [];
+ $metadata_tests['[comment:cid]'] = $base_bubbleable_metadata;
+ $metadata_tests['[comment:hostname]'] = $base_bubbleable_metadata;
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $bubbleable_metadata->addCacheableDependency($this->adminUser);
+ $metadata_tests['[comment:author]'] = $bubbleable_metadata;
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $bubbleable_metadata->addCacheableDependency($this->adminUser);
+ $metadata_tests['[comment:mail]'] = $bubbleable_metadata;
+ $metadata_tests['[comment:homepage]'] = $base_bubbleable_metadata;
+ $metadata_tests['[comment:title]'] = $base_bubbleable_metadata;
+ $metadata_tests['[comment:body]'] = $base_bubbleable_metadata;
+ $metadata_tests['[comment:langcode]'] = $base_bubbleable_metadata;
+ $metadata_tests['[comment:url]'] = $base_bubbleable_metadata;
+ $metadata_tests['[comment:edit-url]'] = $base_bubbleable_metadata;
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $metadata_tests['[comment:created]'] = $bubbleable_metadata->addCacheTags(['rendered']);
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $metadata_tests['[comment:created:since]'] = $bubbleable_metadata->setCacheMaxAge(0);
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $metadata_tests['[comment:changed:since]'] = $bubbleable_metadata->setCacheMaxAge(0);
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $metadata_tests['[comment:parent:cid]'] = $bubbleable_metadata->addCacheTags(['comment:1']);
+ $metadata_tests['[comment:parent:title]'] = $bubbleable_metadata;
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $metadata_tests['[comment:entity]'] = $bubbleable_metadata->addCacheTags(['node:2']);
+ // Test node specific tokens.
+ $metadata_tests['[comment:entity:nid]'] = $bubbleable_metadata;
+ $metadata_tests['[comment:entity:title]'] = $bubbleable_metadata;
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $metadata_tests['[comment:author:uid]'] = $bubbleable_metadata->addCacheTags(['user:2']);
+ $metadata_tests['[comment:author:name]'] = $bubbleable_metadata;
+
// Test to make sure that we generated something for each token.
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
foreach ($tests as $input => $expected) {
- $output = $token_service->replace($input, array('comment' => $comment), array('langcode' => $language_interface->getId()));
+ $bubbleable_metadata = new BubbleableMetadata();
+ $output = $token_service->replace($input, array('comment' => $comment), array('langcode' => $language_interface->getId()), $bubbleable_metadata);
$this->assertEqual($output, $expected, format_string('Sanitized comment token %token replaced.', array('%token' => $input)));
+ $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]);
}
// Generate and test unsanitized tokens.
diff --git a/core/modules/comment/src/Tests/CommentTranslationUITest.php b/core/modules/comment/src/Tests/CommentTranslationUITest.php
index cfb63c86e..dc1bf9e54 100644
--- a/core/modules/comment/src/Tests/CommentTranslationUITest.php
+++ b/core/modules/comment/src/Tests/CommentTranslationUITest.php
@@ -32,6 +32,18 @@ class CommentTranslationUITest extends ContentTranslationUITestBase {
*/
protected $adminUser;
+ /**
+ * {inheritdoc}
+ */
+ protected $defaultCacheContexts = [
+ 'languages:language_interface',
+ 'theme',
+ 'user.permissions',
+ 'timezone',
+ 'url.query_args.pagers:0',
+ 'user.roles'
+ ];
+
/**
* Modules to install.
*
diff --git a/core/modules/comment/src/Tests/Views/CommentFieldNameTest.php b/core/modules/comment/src/Tests/Views/CommentFieldNameTest.php
index ab40e6f89..2fcf4686d 100644
--- a/core/modules/comment/src/Tests/Views/CommentFieldNameTest.php
+++ b/core/modules/comment/src/Tests/Views/CommentFieldNameTest.php
@@ -8,6 +8,7 @@
namespace Drupal\comment\Tests\Views;
use Drupal\comment\Entity\Comment;
+use Drupal\Core\Render\RenderContext;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\user\RoleInterface;
use Drupal\views\Views;
@@ -58,6 +59,8 @@ class CommentFieldNameTest extends CommentTestBase {
* Test comment field name.
*/
public function testCommentFieldName() {
+ /** @var \Drupal\Core\Render\RendererInterface $renderer */
+ $renderer = \Drupal::service('renderer');
$view = Views::getView('test_comment_field_name');
$this->executeView($view);
@@ -85,8 +88,14 @@ class CommentFieldNameTest extends CommentTestBase {
$view = Views::getView('test_comment_field_name');
$this->executeView($view);
// Test that data rendered.
- $this->assertIdentical($this->comment->getFieldName(), $view->field['field_name']->advancedRender($view->result[0]));
- $this->assertIdentical($this->customComment->getFieldName(), $view->field['field_name']->advancedRender($view->result[1]));
+ $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+ return $view->field['field_name']->advancedRender($view->result[0]);
+ });
+ $this->assertIdentical($this->comment->getFieldName(), $output);
+ $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+ return $view->field['field_name']->advancedRender($view->result[1]);
+ });
+ $this->assertIdentical($this->customComment->getFieldName(), $output);
}
}
diff --git a/core/modules/comment/templates/comment.html.twig b/core/modules/comment/templates/comment.html.twig
index 31b82e25b..14c52eba0 100644
--- a/core/modules/comment/templates/comment.html.twig
+++ b/core/modules/comment/templates/comment.html.twig
@@ -67,32 +67,22 @@
*/
#}
-
- {% if title %}
- {{ title_prefix }}
- {% endif %}
-
+
{#
- Hide the "new" indicator by default, let a piece of JavaScript ask
- the server which comments are new for the user. Rendering the final
- "new" indicator here would break the render cache.
+ Hide the "new" indicator by default, let a piece of JavaScript ask the
+ server which comments are new for the user. Rendering the final "new"
+ indicator here would break the render cache.
#}
- {% if title %}
- {{ title }}
- {{ title_suffix }}
- {% endif %}
-
-
{{ user_picture }}
{{ submitted }}
{#
- Indicate the semantic relationship between parent and child comments
- for accessibility. The list is difficult to navigate in a screen
- reader without this information.
+ Indicate the semantic relationship between parent and child comments for
+ accessibility. The list is difficult to navigate in a screen reader
+ without this information.
#}
{% if parent %}
{{ parent }}
@@ -102,9 +92,11 @@
- {{ content|without('links') }}
+ {% if title %}
+ {{ title_prefix }}
+
{{ title }}
+ {{ title_suffix }}
+ {% endif %}
+ {{ content }}
- {% if content.links %}
- {{ content.links }}
- {% endif %}
diff --git a/core/modules/config/config.links.menu.yml b/core/modules/config/config.links.menu.yml
index 87a9b9240..50d881391 100644
--- a/core/modules/config/config.links.menu.yml
+++ b/core/modules/config/config.links.menu.yml
@@ -1,5 +1,5 @@
config.sync:
- title: 'Configuration management'
- description: 'Import, export, or synchronize your site configuration.'
+ title: 'Configuration synchronization'
+ description: 'Import and export your configuration.'
route_name: config.sync
parent: system.admin_config_development
diff --git a/core/modules/config/config.module b/core/modules/config/config.module
index 3be5c4983..f58dff97a 100644
--- a/core/modules/config/config.module
+++ b/core/modules/config/config.module
@@ -15,9 +15,20 @@ function config_help($route_name, RouteMatchInterface $route_match) {
case 'help.page.config':
$output = '';
$output .= '' . t('About') . ' ';
- $output .= '' . t('The Configuration manager module provides a user interface for importing and exporting configuration changes; i.e., for staging configuration data between multiple instances of this web site. For more information, see the online handbook entry for Configuration manager module ', array(
- '!url' => 'https://www.drupal.org/documentation/administer/config',
- )) . '
';
+ $output .= '' . t('The Configuration Manager module provides a user interface for importing and exporting configuration changes between installations of your website in different environments. Configuration is stored in YAML format. For more information, see the online documentation for the Configuration Manager module .', array('!url' => 'https://www.drupal.org/documentation/administer/config')) . '
';
+ $output .= '' . t('Uses') . ' ';
+ $output .= '';
+ $output .= '' . t('Exporting the full configuration') . ' ';
+ $output .= '' . t('You can create and download an archive consisting of all your site\'s configuration exported as *.yml files on the Export page.' , array('!url' => \Drupal::url('config.export_full'))) . ' ';
+ $output .= '' . t('Importing a full configuration') . ' ';
+ $output .= '' . t('You can upload a full site configuration from an archive file on the Import page. When importing data from a different environment, the site and import files must have matching configuration values for UUID in the system.site configuration item. That means that your other environments should initially be set up as clones of the target site. Migrations are not supported.', array('!url' => \Drupal::url('config.import_full'))) . ' ';
+ $output .= '' . t('Synchronizing configuration'). ' ';
+ $output .= '' . t('You can review differences between the active configuration and an imported configuration archive on the Synchronize page to ensure that the changes are as expected, before finalizing the import. The Synchronize page also shows configuration items that would be added or removed.', array('!synchronize' => \Drupal::url('config.sync'))) . ' ';
+ $output .= '' . t('Exporting a single configuration item') . ' ';
+ $output .= '' . t('You can export a single configuration item by selecting a Configuration type and Configuration name on the Single export page. The configuration and its corresponding *.yml file name are then displayed on the page for you to copy.', array('!single-export' => \Drupal::url('config.export_single'))) . ' ';
+ $output .= '' . t('Importing a single configuration item') . ' ';
+ $output .= '' . t('You can import a single configuration item by pasting it in YAML format into the form on the Single import page.', array('!single-import' => \Drupal::url('config.import_single'))) . ' ';
+ $output .= ' ';
return $output;
case 'config.sync':
@@ -25,9 +36,24 @@ function config_help($route_name, RouteMatchInterface $route_match) {
$output .= '' . t('Import configuration that is placed in your staging directory. All changes, deletions, renames, and additions are listed below.') . '
';
return $output;
+ case 'config.export_full':
+ $output = '';
+ $output .= '' . t('The full export page can be used to export the full configuration of this site, and download it as a gzipped tar file.') . '
';
+ return $output;
+
case 'config.import_full':
$output = '';
- $output .= '' . t('After uploading a configuration archive, you will be able to examine the changes and import them.') . '
';
+ $output .= '' . t('The full import page can be used to import a full set of configuration for this site by uploading a gzipped or bzipped tar file consisting of all configuration YAML files. The results will be placed in a the staging directory, so they can be compared in the Synchronize tab. There the import can be finalized.') . '
';
+ return $output;
+
+ case 'config.export_single':
+ $output = '';
+ $output .= '' . t('The single export page can be used to display a single configuration item in a YAML structure.') . '
';
+ return $output;
+
+ case 'config.import_single':
+ $output = '';
+ $output .= '' . t('The single import page can be used to import a single configuration item by pasting a YAML structure into the text field.') . '
';
return $output;
}
}
diff --git a/core/modules/config/src/Tests/CacheabilityMetadataConfigOverrideIntegrationTest.php b/core/modules/config/src/Tests/CacheabilityMetadataConfigOverrideIntegrationTest.php
new file mode 100644
index 000000000..22d0b114a
--- /dev/null
+++ b/core/modules/config/src/Tests/CacheabilityMetadataConfigOverrideIntegrationTest.php
@@ -0,0 +1,65 @@
+set('block_test.content', 'Needs to have some content');
+
+ $this->drupalLogin($this->drupalCreateUser());
+ }
+
+ /**
+ * Tests if config overrides correctly set cacheability metadata.
+ */
+ public function testConfigOverride() {
+ // Check the default (disabled) state of the cache context. The block label
+ // should not be overridden.
+ $this->drupalGet('');
+ $this->assertNoText('Overridden block label');
+
+ // Both the cache context and tag should be present.
+ $this->assertCacheContext('config_override_integration_test');
+ $this->assertCacheTag('config_override_integration_test_tag');
+
+ // Flip the state of the cache context. The block label should now be
+ // overridden.
+ \Drupal::state()->set('config_override_integration_test.enabled', TRUE);
+ $this->drupalGet('');
+ $this->assertText('Overridden block label');
+
+ // Both the cache context and tag should still be present.
+ $this->assertCacheContext('config_override_integration_test');
+ $this->assertCacheTag('config_override_integration_test_tag');
+ }
+
+}
diff --git a/core/modules/config/src/Tests/CacheabilityMetadataConfigOverrideTest.php b/core/modules/config/src/Tests/CacheabilityMetadataConfigOverrideTest.php
new file mode 100644
index 000000000..b4752b5f9
--- /dev/null
+++ b/core/modules/config/src/Tests/CacheabilityMetadataConfigOverrideTest.php
@@ -0,0 +1,95 @@
+installEntitySchema('block_content');
+ $this->installConfig(['config_override_test']);
+ }
+
+ /**
+ * Tests if config overrides correctly set cacheability metadata.
+ */
+ public function testConfigOverride() {
+ // It's pirate day today!
+ $GLOBALS['it_is_pirate_day'] = TRUE;
+
+ $config_factory = $this->container->get('config.factory');
+ $config = $config_factory->get('system.theme');
+
+ // Check that we are using the Pirate theme.
+ $theme = $config->get('default');
+ $this->assertEqual('pirate', $theme);
+
+ // Check that the cacheability metadata is correct.
+ $this->assertEqual(['pirate_day'], $config->getCacheContexts());
+ $this->assertEqual(['config:system.theme', 'pirate-day-tag'], $config->getCacheTags());
+ $this->assertEqual(PirateDayCacheContext::PIRATE_DAY_MAX_AGE, $config->getCacheMaxAge());
+ }
+
+ /**
+ * Tests if config overrides set cacheability metadata on config entities.
+ */
+ public function testConfigEntityOverride() {
+ // It's pirate day today!
+ $GLOBALS['it_is_pirate_day'] = TRUE;
+
+ // Load the User login block and check that its cacheability metadata is
+ // overridden correctly. This verifies that the metadata is correctly
+ // applied to config entities.
+ /** @var EntityManagerInterface $entity_manager */
+ $entity_manager = $this->container->get('entity.manager');
+ $block = $entity_manager->getStorage('block')->load('call_to_action');
+
+ // Check that our call to action message is appealing to filibusters.
+ $this->assertEqual($block->label(), 'Draw yer cutlasses!');
+
+ // Check that the cacheability metadata is correct.
+ $this->assertEqual(['pirate_day'], $block->getCacheContexts());
+ $this->assertEqual(['config:block.block.call_to_action', 'pirate-day-tag'], $block->getCacheTags());
+ $this->assertEqual(PirateDayCacheContext::PIRATE_DAY_MAX_AGE, $block->getCacheMaxAge());
+
+ // Check that duplicating a config entity does not have the original config
+ // entity's cache tag.
+ $this->assertEqual(['config:block.block.', 'pirate-day-tag'], $block->createDuplicate()->getCacheTags());
+
+ // Check that renaming a config entity does not have the original config
+ // entity's cache tag.
+ $block->set('id', 'call_to_looting')->save();
+ $this->assertEqual(['pirate_day'], $block->getCacheContexts());
+ $this->assertEqual(['config:block.block.call_to_looting', 'pirate-day-tag'], $block->getCacheTags());
+ $this->assertEqual(PirateDayCacheContext::PIRATE_DAY_MAX_AGE, $block->getCacheMaxAge());
+ }
+
+}
diff --git a/core/modules/config/src/Tests/ConfigEntityFormOverrideTest.php b/core/modules/config/src/Tests/ConfigEntityFormOverrideTest.php
index a07da59ed..632d2dcf1 100644
--- a/core/modules/config/src/Tests/ConfigEntityFormOverrideTest.php
+++ b/core/modules/config/src/Tests/ConfigEntityFormOverrideTest.php
@@ -31,6 +31,8 @@ class ConfigEntityFormOverrideTest extends WebTestBase {
$overridden_label = 'Overridden label';
$edited_label = 'Edited label';
+ $config_test_storage = $this->container->get('entity.manager')->getStorage('config_test');
+
// Set up an override.
$settings['config']['config_test.dynamic.dotted.default']['label'] = (object) array(
'value' => $overridden_label,
@@ -39,7 +41,7 @@ class ConfigEntityFormOverrideTest extends WebTestBase {
$this->writeSettings($settings);
// Test that the overridden label is loaded with the entity.
- $this->assertEqual(config_test_load('dotted.default')->label(), $overridden_label);
+ $this->assertEqual($config_test_storage->load('dotted.default')->label(), $overridden_label);
// Test that the original label on the listing page is intact.
$this->drupalGet('admin/structure/config_test');
@@ -67,7 +69,7 @@ class ConfigEntityFormOverrideTest extends WebTestBase {
$this->assertIdentical((string) $elements[0]['value'], $edited_label);
// Test that the overridden label is still loaded with the entity.
- $this->assertEqual(config_test_load('dotted.default')->label(), $overridden_label);
+ $this->assertEqual($config_test_storage->load('dotted.default')->label(), $overridden_label);
}
}
diff --git a/core/modules/config/src/Tests/ConfigImporterTest.php b/core/modules/config/src/Tests/ConfigImporterTest.php
index ad21c7831..e39baf624 100644
--- a/core/modules/config/src/Tests/ConfigImporterTest.php
+++ b/core/modules/config/src/Tests/ConfigImporterTest.php
@@ -295,8 +295,8 @@ class ConfigImporterTest extends KernelTestBase {
$logs = $this->configImporter->getErrors();
$this->assertEqual(count($logs), 1);
- $message = SafeMarkup::format('config_test entity with ID @name already exists', array('@name' => 'secondary'));
- $this->assertEqual($logs[0], SafeMarkup::format('Unexpected error during import with operation @op for @name: @message.', array('@op' => 'create', '@name' => $name_primary, '@message' => $message)));
+ $message = SafeMarkup::format("'config_test' entity with ID '@name' already exists", array('@name' => 'secondary'));
+ $this->assertEqual($logs[0], SafeMarkup::format('Unexpected error during import with operation @op for @name: !message.', array('@op' => 'create', '@name' => $name_primary, '!message' => $message)));
}
/**
diff --git a/core/modules/config/src/Tests/ConfigInstallWebTest.php b/core/modules/config/src/Tests/ConfigInstallWebTest.php
index cff60a450..221d09fb3 100644
--- a/core/modules/config/src/Tests/ConfigInstallWebTest.php
+++ b/core/modules/config/src/Tests/ConfigInstallWebTest.php
@@ -127,7 +127,7 @@ class ConfigInstallWebTest extends WebTestBase {
// will install the config_test module first because it is a dependency of
// config_install_fail_test.
// @see \Drupal\system\Form\ModulesListForm::submitForm()
- $this->drupalPostForm('admin/modules', array('modules[Testing][config_test][enable]' => TRUE, 'modules[Testing][config_install_fail_test][enable]' => TRUE), t('Save configuration'));
+ $this->drupalPostForm('admin/modules', array('modules[Testing][config_test][enable]' => TRUE, 'modules[Testing][config_install_fail_test][enable]' => TRUE), t('Install'));
$this->assertRaw('Unable to install Configuration install fail test, config_test.dynamic.dotted.default already exists in active configuration.');
// Uninstall the config_test module to test the confirm form.
@@ -138,7 +138,7 @@ class ConfigInstallWebTest extends WebTestBase {
// The user is shown a confirm form because the config_test module is a
// dependency.
// @see \Drupal\system\Form\ModulesListConfirmForm::submitForm()
- $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_fail_test][enable]' => TRUE), t('Save configuration'));
+ $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_fail_test][enable]' => TRUE), t('Install'));
$this->drupalPostForm(NULL, array(), t('Continue'));
$this->assertRaw('Unable to install Configuration install fail test, config_test.dynamic.dotted.default already exists in active configuration.');
@@ -152,7 +152,7 @@ class ConfigInstallWebTest extends WebTestBase {
->set('label', 'Je suis Charlie')
->save();
- $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_fail_test][enable]' => TRUE), t('Save configuration'));
+ $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_fail_test][enable]' => TRUE), t('Install'));
$this->assertRaw('Unable to install Configuration install fail test, config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default already exist in active configuration.');
// Test installing a theme through the UI that has existing configuration.
@@ -183,12 +183,12 @@ class ConfigInstallWebTest extends WebTestBase {
$this->drupalLogin($this->adminUser);
// We need to install separately since config_install_dependency_test does
// not depend on config_test and order is important.
- $this->drupalPostForm('admin/modules', array('modules[Testing][config_test][enable]' => TRUE), t('Save configuration'));
- $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
+ $this->drupalPostForm('admin/modules', array('modules[Testing][config_test][enable]' => TRUE), t('Install'));
+ $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Install'));
$this->assertRaw('Unable to install Config install dependency test, config_test.dynamic.other_module_test_with_dependency has unmet dependencies.');
- $this->drupalPostForm('admin/modules', array('modules[Testing][config_other_module_config_test][enable]' => TRUE), t('Save configuration'));
- $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
+ $this->drupalPostForm('admin/modules', array('modules[Testing][config_other_module_config_test][enable]' => TRUE), t('Install'));
+ $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Install'));
$this->rebuildContainer();
$this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
}
diff --git a/core/modules/config/tests/config_entity_static_cache_test/src/ConfigOverrider.php b/core/modules/config/tests/config_entity_static_cache_test/src/ConfigOverrider.php
index 2867c7d92..3f7937911 100644
--- a/core/modules/config/tests/config_entity_static_cache_test/src/ConfigOverrider.php
+++ b/core/modules/config/tests/config_entity_static_cache_test/src/ConfigOverrider.php
@@ -7,6 +7,7 @@
namespace Drupal\config_entity_static_cache_test;
+use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
use Drupal\Core\Config\StorageInterface;
@@ -40,4 +41,11 @@ class ConfigOverrider implements ConfigFactoryOverrideInterface {
return NULL;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheableMetadata($name) {
+ return new CacheableMetadata();
+ }
+
}
diff --git a/core/modules/config/tests/config_override_integration_test/config/install/block.block.config_override_test.yml b/core/modules/config/tests/config_override_integration_test/config/install/block.block.config_override_test.yml
new file mode 100644
index 000000000..be0616ff5
--- /dev/null
+++ b/core/modules/config/tests/config_override_integration_test/config/install/block.block.config_override_test.yml
@@ -0,0 +1,24 @@
+id: config_override_test
+theme: classy
+weight: 0
+status: true
+langcode: en
+region: content
+plugin: test_cache
+settings:
+ label: 'Test HTML block'
+ provider: block_test
+ label_display: visible
+ status: true
+ info: ''
+ view_mode: default
+dependencies:
+ module:
+ - block_test
+ theme:
+ - classy
+visibility:
+ request_path:
+ id: request_path
+ pages: ''
+ negate: false
diff --git a/core/modules/config/tests/config_override_integration_test/config_override_integration_test.info.yml b/core/modules/config/tests/config_override_integration_test/config_override_integration_test.info.yml
new file mode 100644
index 000000000..22b1e94cd
--- /dev/null
+++ b/core/modules/config/tests/config_override_integration_test/config_override_integration_test.info.yml
@@ -0,0 +1,9 @@
+name: 'Configuration override integration test'
+type: module
+package: Testing
+version: VERSION
+core: 8.x
+
+dependencies:
+ - block
+ - block_test
diff --git a/core/modules/config/tests/config_override_integration_test/config_override_integration_test.services.yml b/core/modules/config/tests/config_override_integration_test/config_override_integration_test.services.yml
new file mode 100644
index 000000000..147874e27
--- /dev/null
+++ b/core/modules/config/tests/config_override_integration_test/config_override_integration_test.services.yml
@@ -0,0 +1,9 @@
+services:
+ cache_context.config_override_integration_test:
+ class: Drupal\config_override_integration_test\Cache\ConfigOverrideIntegrationTestCacheContext
+ tags:
+ - { name: cache.context }
+ config_override_integration_test.config_override:
+ class: Drupal\config_override_integration_test\CacheabilityMetadataConfigOverride
+ tags:
+ - { name: config.factory.override }
diff --git a/core/modules/config/tests/config_override_integration_test/src/Cache/ConfigOverrideIntegrationTestCacheContext.php b/core/modules/config/tests/config_override_integration_test/src/Cache/ConfigOverrideIntegrationTestCacheContext.php
new file mode 100644
index 000000000..f2192aa96
--- /dev/null
+++ b/core/modules/config/tests/config_override_integration_test/src/Cache/ConfigOverrideIntegrationTestCacheContext.php
@@ -0,0 +1,47 @@
+get('config_override_integration_test.enabled', FALSE) ? 'yes' : 'no';
+ return 'config_override_integration_test.' . $state;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheableMetadata() {
+ // Since this depends on State this can change at any time and is not
+ // cacheable.
+ $metadata = new CacheableMetadata();
+ $metadata->setCacheMaxAge(0);
+ return $metadata;
+ }
+
+}
diff --git a/core/modules/config/tests/config_override_integration_test/src/CacheabilityMetadataConfigOverride.php b/core/modules/config/tests/config_override_integration_test/src/CacheabilityMetadataConfigOverride.php
new file mode 100644
index 000000000..fc6a271e0
--- /dev/null
+++ b/core/modules/config/tests/config_override_integration_test/src/CacheabilityMetadataConfigOverride.php
@@ -0,0 +1,65 @@
+get('config_override_integration_test.enabled', FALSE);
+ if (in_array('block.block.config_override_test', $names) && $state !== FALSE) {
+ $overrides = $overrides + [
+ 'block.block.config_override_test' => [
+ 'settings' => ['label' => 'Overridden block label'],
+ ],
+ ];
+ }
+
+ return $overrides;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheSuffix() {
+ return 'config_override_integration_test';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
+ return NULL;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheableMetadata($name) {
+ $metadata = new CacheableMetadata();
+ if ($name === 'block.block.config_override_test') {
+ $metadata
+ ->setCacheContexts(['config_override_integration_test'])
+ ->setCacheTags(['config_override_integration_test_tag']);
+ }
+ return $metadata;
+ }
+
+}
diff --git a/core/modules/config/tests/config_override_test/config/install/block.block.call_to_action.yml b/core/modules/config/tests/config_override_test/config/install/block.block.call_to_action.yml
new file mode 100644
index 000000000..8951c0d22
--- /dev/null
+++ b/core/modules/config/tests/config_override_test/config/install/block.block.call_to_action.yml
@@ -0,0 +1,26 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - block_content
+ theme:
+ - classy
+id: call_to_action
+theme: classy
+region: content
+weight: null
+provider: null
+plugin: 'block_content:d7c9d8ba-663f-41b4-8756-86bc55c44653'
+settings:
+ id: 'block_content:d7c9d8ba-663f-41b4-8756-86bc55c44653'
+ label: 'Shop for cheap now!'
+ provider: block_content
+ label_display: visible
+ status: true
+ info: ''
+ view_mode: default
+visibility:
+ request_path:
+ id: request_path
+ pages: ''
+ negate: false
diff --git a/core/modules/config/tests/config_override_test/config_override_test.info.yml b/core/modules/config/tests/config_override_test/config_override_test.info.yml
index f1f1109cc..051729d3d 100644
--- a/core/modules/config/tests/config_override_test/config_override_test.info.yml
+++ b/core/modules/config/tests/config_override_test/config_override_test.info.yml
@@ -3,3 +3,7 @@ type: module
package: Testing
version: VERSION
core: 8.x
+
+dependencies:
+ - block
+ - block_content
diff --git a/core/modules/config/tests/config_override_test/config_override_test.services.yml b/core/modules/config/tests/config_override_test/config_override_test.services.yml
index c3fae6491..1a074120d 100644
--- a/core/modules/config/tests/config_override_test/config_override_test.services.yml
+++ b/core/modules/config/tests/config_override_test/config_override_test.services.yml
@@ -1,4 +1,8 @@
services:
+ cache_context.pirate_day:
+ class: Drupal\config_override_test\Cache\PirateDayCacheContext
+ tags:
+ - { name: cache.context }
config_override_test.overrider:
class: Drupal\config_override_test\ConfigOverrider
tags:
@@ -7,3 +11,7 @@ services:
class: Drupal\config_override_test\ConfigOverriderLowPriority
tags:
- { name: config.factory.override, priority: -100 }
+ config_override_test.pirate_day_cacheability_metadata_override:
+ class: Drupal\config_override_test\PirateDayCacheabilityMetadataConfigOverride
+ tags:
+ - { name: config.factory.override }
diff --git a/core/modules/config/tests/config_override_test/src/Cache/PirateDayCacheContext.php b/core/modules/config/tests/config_override_test/src/Cache/PirateDayCacheContext.php
new file mode 100644
index 000000000..c40cc6ab4
--- /dev/null
+++ b/core/modules/config/tests/config_override_test/src/Cache/PirateDayCacheContext.php
@@ -0,0 +1,66 @@
+ ['default' => 'pirate']];
+ }
+ if (in_array('block.block.call_to_action', $names)) {
+ $overrides = $overrides + [
+ 'block.block.call_to_action' => [
+ 'settings' => ['label' => 'Draw yer cutlasses!'],
+ ],
+ ];
+ }
+ }
+
+ return $overrides;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheSuffix() {
+ return 'PirateDayConfigOverrider';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
+ return NULL;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheableMetadata($name) {
+ $metadata = new CacheableMetadata();
+ $metadata
+ ->setCacheContexts(['pirate_day'])
+ ->setCacheTags(['pirate-day-tag'])
+ ->setCacheMaxAge(PirateDayCacheContext::PIRATE_DAY_MAX_AGE);
+ return $metadata;
+ }
+
+ /**
+ * Returns whether or not our overrides are potentially applicable.
+ *
+ * @param string $name
+ * The name of the config object that is being constructed.
+ *
+ * @return bool
+ * TRUE if the merchant ship will be boarded. FALSE if we drink rum instead.
+ */
+ protected function isCacheabilityMetadataApplicable($name) {
+ return in_array($name, ['system.theme', 'block.block.call_to_action']);
+ }
+
+}
diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module
index c17d688f4..9510abab6 100644
--- a/core/modules/config/tests/config_test/config_test.module
+++ b/core/modules/config/tests/config_test/config_test.module
@@ -10,16 +10,6 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
require_once dirname(__FILE__) . '/config_test.hooks.inc';
-/**
- * Loads a ConfigTest object.
- *
- * @param string $id
- * The ID of the ConfigTest object to load.
- */
-function config_test_load($id) {
- return entity_load('config_test', $id);
-}
-
/**
* Implements hook_cache_flush().
*/
diff --git a/core/modules/config/tests/config_test/src/ConfigTestForm.php b/core/modules/config/tests/config_test/src/ConfigTestForm.php
index 0967206c3..ac0bb17ae 100644
--- a/core/modules/config/tests/config_test/src/ConfigTestForm.php
+++ b/core/modules/config/tests/config_test/src/ConfigTestForm.php
@@ -8,13 +8,41 @@
namespace Drupal\config_test;
use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Form\FormStateInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form controller for the test config edit forms.
*/
class ConfigTestForm extends EntityForm {
+ /**
+ * The entity query.
+ *
+ * @var \Drupal\Core\Entity\Query\QueryFactory
+ */
+ protected $entityQuery;
+
+ /**
+ * Constructs a new ConfigTestForm.
+ *
+ * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
+ * The entity query.
+ */
+ public function __construct(QueryFactory $entity_query) {
+ $this->entityQuery = $entity_query;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('entity.query')
+ );
+ }
+
/**
* {@inheritdoc}
*/
@@ -33,7 +61,7 @@ class ConfigTestForm extends EntityForm {
'#default_value' => $entity->id(),
'#required' => TRUE,
'#machine_name' => array(
- 'exists' => 'config_test_load',
+ 'exists' => [$this, 'exists'],
'replace_pattern' => '[^a-z0-9_.]+',
),
);
@@ -142,4 +170,25 @@ class ConfigTestForm extends EntityForm {
$form_state->setRedirectUrl($this->entity->urlInfo('collection'));
}
+ /**
+ * Determines if the entity already exists.
+ *
+ * @param string|int $entity_id
+ * The entity ID.
+ * @param array $element
+ * The form element.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ *
+ * @return bool
+ * TRUE if the entity exists, FALSE otherwise.
+ */
+ public function exists($entity_id, array $element, FormStateInterface $form_state) {
+ /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
+ $entity = $form_state->getFormObject()->getEntity();
+ return (bool) $this->entityQuery->get($entity->getEntityTypeId())
+ ->condition($entity->getEntityType()->getKey('id'), $entity_id)
+ ->execute();
+ }
+
}
diff --git a/core/modules/config_translation/config_translation.links.contextual.yml b/core/modules/config_translation/config_translation.links.contextual.yml
index 71c4544fd..0ad2b35b2 100644
--- a/core/modules/config_translation/config_translation.links.contextual.yml
+++ b/core/modules/config_translation/config_translation.links.contextual.yml
@@ -1,4 +1,3 @@
config_translation.contextual_links:
- title: 'Translate @type_name'
deriver: 'Drupal\config_translation\Plugin\Derivative\ConfigTranslationContextualLinks'
weight: 100
diff --git a/core/modules/config_translation/config_translation.links.task.yml b/core/modules/config_translation/config_translation.links.task.yml
index 513eb58e5..7140da10a 100644
--- a/core/modules/config_translation/config_translation.links.task.yml
+++ b/core/modules/config_translation/config_translation.links.task.yml
@@ -1,4 +1,3 @@
config_translation.local_tasks:
- title: 'Translate @type_name'
deriver: 'Drupal\config_translation\Plugin\Derivative\ConfigTranslationLocalTasks'
weight: 100
diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module
index b4f882b75..3c81f78e3 100644
--- a/core/modules/config_translation/config_translation.module
+++ b/core/modules/config_translation/config_translation.module
@@ -188,6 +188,7 @@ function config_translation_config_schema_info_alter(&$definitions) {
'text_format' => '\Drupal\config_translation\FormElement\TextFormat',
'mapping' => '\Drupal\config_translation\FormElement\ListElement',
'sequence' => '\Drupal\config_translation\FormElement\ListElement',
+ 'plural_label' => '\Drupal\config_translation\FormElement\PluralVariants',
);
// Enhance the text and date type definitions with classes to generate proper
diff --git a/core/modules/config_translation/src/ConfigEntityMapper.php b/core/modules/config_translation/src/ConfigEntityMapper.php
index 269bfac86..4a6d275db 100644
--- a/core/modules/config_translation/src/ConfigEntityMapper.php
+++ b/core/modules/config_translation/src/ConfigEntityMapper.php
@@ -8,8 +8,8 @@
namespace Drupal\config_translation;
use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
-use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Routing\RouteMatch;
@@ -43,7 +43,7 @@ class ConfigEntityMapper extends ConfigNamesMapper {
/**
* Loaded entity instance to help produce the translation interface.
*
- * @var \Drupal\Core\Entity\EntityInterface
+ * @var \Drupal\Core\Config\Entity\ConfigEntityInterface
*/
protected $entity;
@@ -125,13 +125,13 @@ class ConfigEntityMapper extends ConfigNamesMapper {
* configuration names to use to check permissions or display a translation
* screen.
*
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity to set.
+ * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
+ * The configuration entity to set.
*
* @return bool
* TRUE, if the entity was set successfully; FALSE otherwise.
*/
- public function setEntity(EntityInterface $entity) {
+ public function setEntity(ConfigEntityInterface $entity) {
if (isset($this->entity)) {
return FALSE;
}
@@ -143,6 +143,7 @@ class ConfigEntityMapper extends ConfigNamesMapper {
// page with more names if form altering added more configuration to an
// entity. This is not a Drupal 8 best practice (ideally the configuration
// would have pluggable components), but this may happen as well.
+ /** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type_info */
$entity_type_info = $this->entityManager->getDefinition($this->entityType);
$this->addConfigName($entity_type_info->getConfigPrefix() . '.' . $entity->id());
diff --git a/core/modules/config_translation/src/ConfigFieldMapper.php b/core/modules/config_translation/src/ConfigFieldMapper.php
index e9d48e4e7..a593fffd2 100644
--- a/core/modules/config_translation/src/ConfigFieldMapper.php
+++ b/core/modules/config_translation/src/ConfigFieldMapper.php
@@ -7,6 +7,8 @@
namespace Drupal\config_translation;
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
/**
* Configuration mapper for fields.
*
@@ -49,4 +51,22 @@ class ConfigFieldMapper extends ConfigEntityMapper {
return $this->t('@label fields', array('@label' => $base_entity_info->getLabel()));
}
+ /**
+ * {@inheritdoc}
+ */
+ public function setEntity(ConfigEntityInterface $entity) {
+ if (parent::setEntity($entity)) {
+
+ // Field storage config can also contain translatable values. Add the name
+ // of the config as well to the list of configs for this entity.
+ /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
+ $field_storage = $this->entity->getFieldStorageDefinition();
+ /** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type_info */
+ $entity_type_info = $this->entityManager->getDefinition($field_storage->getEntityTypeId());
+ $this->addConfigName($entity_type_info->getConfigPrefix() . '.' . $field_storage->id());
+ return TRUE;
+ }
+ return FALSE;
+ }
+
}
diff --git a/core/modules/config_translation/src/ConfigMapperManager.php b/core/modules/config_translation/src/ConfigMapperManager.php
index 56b2182eb..2731aa0e5 100644
--- a/core/modules/config_translation/src/ConfigMapperManager.php
+++ b/core/modules/config_translation/src/ConfigMapperManager.php
@@ -7,7 +7,6 @@
namespace Drupal\config_translation;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
@@ -134,7 +133,7 @@ class ConfigMapperManager extends DefaultPluginManager implements ConfigMapperMa
parent::processDefinition($definition, $plugin_id);
if (!isset($definition['base_route_name'])) {
- throw new InvalidPluginDefinitionException($plugin_id, SafeMarkup::format("The plugin definition of the mapper '%plugin_id' does not contain a base_route_name.", array('%plugin_id' => $plugin_id)));
+ throw new InvalidPluginDefinitionException($plugin_id, "The plugin definition of the mapper '$plugin_id' does not contain a base_route_name.");
}
}
diff --git a/core/modules/config_translation/src/Controller/ConfigTranslationEntityListBuilder.php b/core/modules/config_translation/src/Controller/ConfigTranslationEntityListBuilder.php
index 1db0164a2..0c50f2a49 100644
--- a/core/modules/config_translation/src/Controller/ConfigTranslationEntityListBuilder.php
+++ b/core/modules/config_translation/src/Controller/ConfigTranslationEntityListBuilder.php
@@ -84,8 +84,8 @@ class ConfigTranslationEntityListBuilder extends ConfigEntityListBuilder impleme
/**
* {@inheritdoc}
*/
- public function getDefaultOperations(EntityInterface $entity) {
- $operations = parent::getDefaultOperations($entity);
+ public function getOperations(EntityInterface $entity) {
+ $operations = parent::getOperations($entity);
foreach (array_keys($operations) as $operation) {
// This is a translation UI for translators. Show the translation
// operation only.
diff --git a/core/modules/config_translation/src/FormElement/FormElementBase.php b/core/modules/config_translation/src/FormElement/FormElementBase.php
index e13cc3122..7db01e1c4 100644
--- a/core/modules/config_translation/src/FormElement/FormElementBase.php
+++ b/core/modules/config_translation/src/FormElement/FormElementBase.php
@@ -91,7 +91,6 @@ abstract class FormElementBase implements ElementInterface {
* A render array for the source value.
*/
protected function getSourceElement(LanguageInterface $source_language, $source_config) {
- // @todo Should support singular+plurals https://www.drupal.org/node/2454829
if ($source_config) {
$value = '' . nl2br($source_config) . ' ';
}
@@ -162,7 +161,6 @@ abstract class FormElementBase implements ElementInterface {
*/
protected function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) {
// Add basic properties that apply to all form elements.
- // @todo Should support singular+plurals https://www.drupal.org/node/2454829
return array(
'#title' => $this->t('!label (!source_language) ', array(
'!label' => $this->t($this->definition['label']),
diff --git a/core/modules/config_translation/src/FormElement/PluralVariants.php b/core/modules/config_translation/src/FormElement/PluralVariants.php
new file mode 100644
index 000000000..ec6b97763
--- /dev/null
+++ b/core/modules/config_translation/src/FormElement/PluralVariants.php
@@ -0,0 +1,82 @@
+getNumberOfPlurals($source_language->getId());
+ $values = explode(LOCALE_PLURAL_DELIMITER, $source_config);
+ $element = array(
+ '#type' => 'fieldset',
+ '#title' => SafeMarkup::format('@label (@source_language) ', array(
+ '@label' => $this->t($this->definition->getLabel()),
+ '@source_language' => $source_language->getName(),
+ )),
+ '#tree' => TRUE,
+ );
+ for ($i = 0; $i < $plurals; $i++) {
+ $element[$i] = array(
+ '#type' => 'item',
+ // @todo Should use better labels https://www.drupal.org/node/2499639
+ '#title' => $i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form'),
+ '#markup' => SafeMarkup::format('@value ', array(
+ '@langcode' => $source_language->getId(),
+ '@value' => isset($values[$i]) ? $values[$i] : $this->t('(Empty)'),
+ )),
+ );
+ }
+ return $element;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getTranslationElement(LanguageInterface $translation_language, $source_config, $translation_config) {
+ $plurals = $this->getNumberOfPlurals($translation_language->getId());
+ $values = explode(LOCALE_PLURAL_DELIMITER, $translation_config);
+ $element = array(
+ '#type' => 'fieldset',
+ '#title' => SafeMarkup::format('@label (@translation_language) ', array(
+ '@label' => $this->t($this->definition->getLabel()),
+ '@translation_language' => $translation_language->getName(),
+ )),
+ '#tree' => TRUE,
+ );
+ for ($i = 0; $i < $plurals; $i++) {
+ $element[$i] = array(
+ '#type' => 'textfield',
+ // @todo Should use better labels https://www.drupal.org/node/2499639
+ '#title' => $i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form'),
+ '#default_value' => isset($values[$i]) ? $values[$i] : '',
+ '#attributes' => array('lang' => $translation_language->getId()),
+ );
+ }
+ return $element;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setConfig(Config $base_config, LanguageConfigOverride $config_translation, $config_values, $base_key = NULL) {
+ $config_values = implode(LOCALE_PLURAL_DELIMITER, $config_values);
+ parent::setConfig($base_config, $config_translation, $config_values, $base_key);
+ }
+
+}
diff --git a/core/modules/config_translation/src/Plugin/Menu/ContextualLink/ConfigTranslationContextualLink.php b/core/modules/config_translation/src/Plugin/Menu/ContextualLink/ConfigTranslationContextualLink.php
index 98ffb0e58..c8989fc48 100644
--- a/core/modules/config_translation/src/Plugin/Menu/ContextualLink/ConfigTranslationContextualLink.php
+++ b/core/modules/config_translation/src/Plugin/Menu/ContextualLink/ConfigTranslationContextualLink.php
@@ -9,11 +9,13 @@ namespace Drupal\config_translation\Plugin\Menu\ContextualLink;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Menu\ContextualLinkDefault;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Defines a contextual link plugin with a dynamic title.
*/
class ConfigTranslationContextualLink extends ContextualLinkDefault {
+ use StringTranslationTrait;
/**
* The mapper plugin discovery service.
@@ -26,17 +28,12 @@ class ConfigTranslationContextualLink extends ContextualLinkDefault {
* {@inheritdoc}
*/
public function getTitle() {
- $options = array();
- if (!empty($this->pluginDefinition['title_context'])) {
- $options['context'] = $this->pluginDefinition['title_context'];
- }
-
- // Take custom 'config_translation_plugin_id' plugin definition key to
- // retrieve title. We need to retrieve a runtime title (as opposed to
- // storing the title on the plugin definition for the link) because
- // it contains translated parts that we need in the runtime language.
+ // Use the custom 'config_translation_plugin_id' plugin definition key to
+ // retrieve the title. We need to retrieve a runtime title (as opposed to
+ // storing the title on the plugin definition for the link) because it
+ // contains translated parts that we need in the runtime language.
$type_name = Unicode::strtolower($this->mapperManager()->createInstance($this->pluginDefinition['config_translation_plugin_id'])->getTypeLabel());
- return $this->t($this->pluginDefinition['title'], array('@type_name' => $type_name), $options);
+ return $this->t('Translate @type_name', array('@type_name' => $type_name));
}
/**
diff --git a/core/modules/config_translation/src/Plugin/Menu/LocalTask/ConfigTranslationLocalTask.php b/core/modules/config_translation/src/Plugin/Menu/LocalTask/ConfigTranslationLocalTask.php
index 46a2ec2b7..496ed9629 100644
--- a/core/modules/config_translation/src/Plugin/Menu/LocalTask/ConfigTranslationLocalTask.php
+++ b/core/modules/config_translation/src/Plugin/Menu/LocalTask/ConfigTranslationLocalTask.php
@@ -9,11 +9,13 @@ namespace Drupal\config_translation\Plugin\Menu\LocalTask;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Menu\LocalTaskDefault;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Defines a local task plugin with a dynamic title.
*/
class ConfigTranslationLocalTask extends LocalTaskDefault {
+ use StringTranslationTrait;
/**
* The mapper plugin discovery service.
@@ -26,17 +28,12 @@ class ConfigTranslationLocalTask extends LocalTaskDefault {
* {@inheritdoc}
*/
public function getTitle() {
- $options = array();
- if (!empty($this->pluginDefinition['title_context'])) {
- $options['context'] = $this->pluginDefinition['title_context'];
- }
-
// Take custom 'config_translation_plugin_id' plugin definition key to
// retrieve title. We need to retrieve a runtime title (as opposed to
// storing the title on the plugin definition for the link) because
// it contains translated parts that we need in the runtime language.
$type_name = Unicode::strtolower($this->mapperManager()->createInstance($this->pluginDefinition['config_translation_plugin_id'])->getTypeLabel());
- return $this->t($this->pluginDefinition['title'], array('@type_name' => $type_name), $options);
+ return $this->t('Translate @type_name', array('@type_name' => $type_name));
}
/**
diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationOverviewTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationOverviewTest.php
index a2f6a6cf9..0916dcd79 100644
--- a/core/modules/config_translation/src/Tests/ConfigTranslationOverviewTest.php
+++ b/core/modules/config_translation/src/Tests/ConfigTranslationOverviewTest.php
@@ -23,7 +23,16 @@ class ConfigTranslationOverviewTest extends WebTestBase {
*
* @var array
*/
- public static $modules = array('contact', 'config_translation', 'views', 'views_ui', 'contextual', 'config_test', 'config_translation_test');
+ public static $modules = [
+ 'config_test',
+ 'config_translation',
+ 'config_translation_test',
+ 'contact',
+ 'contextual',
+ 'entity_test_operation',
+ 'views',
+ 'views_ui',
+ ];
/**
* Languages to enable.
@@ -67,6 +76,14 @@ class ConfigTranslationOverviewTest extends WebTestBase {
$this->drupalGet('admin/config/regional/config-translation');
$this->assertLinkByHref('admin/config/regional/config-translation/config_test');
$this->assertLinkByHref('admin/config/people/accounts/translate');
+ // Make sure there is only a single operation for each dropbutton, either
+ // 'List' or 'Translate'.
+ foreach ($this->cssSelect('ul.dropbutton') as $i => $dropbutton) {
+ $this->assertIdentical(1, $dropbutton->count());
+ foreach ($dropbutton->li as $link) {
+ $this->assertTrue(((string) $link->a === 'Translate') || ((string) $link->a === 'List'));
+ }
+ }
$labels = array(
'&$nxd~i0',
@@ -86,6 +103,15 @@ class ConfigTranslationOverviewTest extends WebTestBase {
$this->assertLinkByHref($base_url . '/translate');
$this->assertText(SafeMarkup::checkPlain($test_entity->label()));
+ // Make sure there is only a single 'Translate' operation for each
+ // dropbutton.
+ foreach ($this->cssSelect('ul.dropbutton') as $i => $dropbutton) {
+ $this->assertIdentical(1, $dropbutton->count());
+ foreach ($dropbutton->li as $link) {
+ $this->assertIdentical('Translate', (string) $link->a);
+ }
+ }
+
$entity_type = \Drupal::entityManager()->getDefinition($test_entity->getEntityTypeId());
$this->drupalGet($base_url . '/translate');
@@ -127,6 +153,8 @@ class ConfigTranslationOverviewTest extends WebTestBase {
$original_label = 'Default';
$overridden_label = 'Overridden label';
+ $config_test_storage = $this->container->get('entity.manager')->getStorage('config_test');
+
// Set up an override.
$settings['config']['config_test.dynamic.dotted.default']['label'] = (object) array(
'value' => $overridden_label,
@@ -135,7 +163,7 @@ class ConfigTranslationOverviewTest extends WebTestBase {
$this->writeSettings($settings);
// Test that the overridden label is loaded with the entity.
- $this->assertEqual(config_test_load('dotted.default')->label(), $overridden_label);
+ $this->assertEqual($config_test_storage->load('dotted.default')->label(), $overridden_label);
// Test that the original label on the listing page is intact.
$this->drupalGet('admin/config/regional/config-translation/config_test');
diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
index c7218dffc..a05f7e70a 100644
--- a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
+++ b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
@@ -13,6 +13,8 @@ use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\simpletest\WebTestBase;
@@ -28,7 +30,21 @@ class ConfigTranslationUiTest extends WebTestBase {
*
* @var array
*/
- public static $modules = array('node', 'contact', 'contact_test', 'config_translation', 'config_translation_test', 'views', 'views_ui', 'contextual', 'filter', 'filter_test');
+ public static $modules = [
+ 'config_translation',
+ 'config_translation_test',
+ 'contact',
+ 'contact_test',
+ 'contextual',
+ 'entity_test',
+ 'field_test',
+ 'field_ui',
+ 'filter',
+ 'filter_test',
+ 'node',
+ 'views',
+ 'views_ui',
+ ];
/**
* Languages to enable.
@@ -89,6 +105,7 @@ class ConfigTranslationUiTest extends WebTestBase {
'administer themes',
'bypass node access',
'administer content types',
+ 'translate interface',
]
);
// Create and login user.
@@ -609,6 +626,136 @@ class ConfigTranslationUiTest extends WebTestBase {
$this->assertFieldByName('translation[config_names][views.view.frontpage][display][default][display_options][title]', $display_options_master . " FR");
}
+ /**
+ * Test the number of source elements for plural strings in config translation forms.
+ */
+ public function testPluralConfigStringsSourceElements() {
+ $this->drupalLogin($this->adminUser);
+
+ // Languages to test, with various number of plural forms.
+ $languages = array(
+ 'vi' => array('plurals' => 1, 'expected' => array(TRUE, FALSE, FALSE, FALSE)),
+ 'fr' => array('plurals' => 2, 'expected' => array(TRUE, TRUE, FALSE, FALSE)),
+ 'sl' => array('plurals' => 4, 'expected' => array(TRUE, TRUE, TRUE, TRUE)),
+ );
+
+ foreach ($languages as $langcode => $data) {
+ // Import a .po file to add a new language with a given number of plural forms
+ $name = tempnam('temporary://', $langcode . '_') . '.po';
+ file_put_contents($name, $this->getPoFile($data['plurals']));
+ $this->drupalPostForm('admin/config/regional/translate/import', array(
+ 'langcode' => $langcode,
+ 'files[file]' => $name,
+ ), t('Import'));
+
+ // Change the config langcode of the 'files' view.
+ $config = \Drupal::service('config.factory')->getEditable('views.view.files');
+ $config->set('langcode', $langcode);
+ $config->save();
+
+ // Go to the translation page of the 'files' view.
+ $translation_url = 'admin/structure/views/view/files/translate/' . $langcode . '/add';
+ $this->drupalGet($translation_url);
+
+ // Check if the expected number of source elements are present.
+ foreach ($data['expected'] as $index => $expected) {
+ if ($expected) {
+ $this->assertRaw('edit-source-config-names-viewsviewfiles-display-default-display-options-fields-count-format-plural-string-' . $index);
+ }
+ else {
+ $this->assertNoRaw('edit-source-config-names-viewsviewfiles-display-default-display-options-fields-count-format-plural-string-' . $index);
+ }
+ }
+ }
+ }
+
+ /**
+ * Test translation of plural strings with multiple plural forms in config.
+ */
+ public function testPluralConfigStrings() {
+ $this->drupalLogin($this->adminUser);
+
+ // First import a .po file with multiple plural forms.
+ // This will also automatically add the 'sl' language.
+ $name = tempnam('temporary://', "sl_") . '.po';
+ file_put_contents($name, $this->getPoFile(4));
+ $this->drupalPostForm('admin/config/regional/translate/import', array(
+ 'langcode' => 'sl',
+ 'files[file]' => $name,
+ ), t('Import'));
+
+ // Translate the files view, as this one uses numeric formatters.
+ $description = 'Singular form';
+ $field_value = '1 place';
+ $field_value_plural = '@count places';
+ $translation_url = 'admin/structure/views/view/files/translate/sl/add';
+ $this->drupalGet($translation_url);
+
+ // Make sure original text is present on this page, in addition to 2 new
+ // empty fields.
+ $this->assertRaw($description);
+ $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][0]', $field_value);
+ $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][1]', $field_value_plural);
+ $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][2]', '');
+ $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][3]', '');
+
+ // Then make sure it also works.
+ $edit = [
+ 'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][0]' => $field_value . ' SL',
+ 'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][1]' => $field_value_plural . ' 1 SL',
+ 'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][2]' => $field_value_plural . ' 2 SL',
+ 'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][3]' => $field_value_plural . ' 3 SL',
+ ];
+ $this->drupalPostForm($translation_url, $edit, t('Save translation'));
+
+ // Make sure the values have changed.
+ $this->drupalGet($translation_url);
+ $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][0]', "$field_value SL");
+ $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][1]', "$field_value_plural 1 SL");
+ $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][2]', "$field_value_plural 2 SL");
+ $this->assertFieldByName('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][3]', "$field_value_plural 3 SL");
+ }
+
+ /**
+ * Tests the translation of field and field storage configuration.
+ */
+ public function testFieldConfigTranslation() {
+ // Add a test field which has a translatable field setting and a
+ // translatable field storage setting.
+ $field_name = strtolower($this->randomMachineName());
+ $field_storage = FieldStorageConfig::create([
+ 'field_name' => $field_name,
+ 'entity_type' => 'entity_test',
+ 'type' => 'test_field',
+ ]);
+
+ $translatable_storage_setting = $this->randomString();
+ $field_storage->setSetting('translatable_storage_setting', $translatable_storage_setting);
+ $field_storage->save();
+
+ $bundle = strtolower($this->randomMachineName());
+ entity_test_create_bundle($bundle);
+ $field = FieldConfig::create([
+ 'field_name' => $field_name,
+ 'entity_type' => 'entity_test',
+ 'bundle' => $bundle,
+ ]);
+
+ $translatable_field_setting = $this->randomString();
+ $field->setSetting('translatable_field_setting', $translatable_field_setting);
+ $field->save();
+
+ $this->drupalLogin($this->translatorUser);
+
+ $this->drupalGet("/entity_test/structure/$bundle/fields/entity_test.$bundle.$field_name/translate");
+ $this->clickLink('Add');
+
+ $this->assertText('Translatable field setting');
+ $this->assertRaw(SafeMarkup::checkPlain($translatable_field_setting));
+ $this->assertText('Translatable storage setting');
+ $this->assertRaw(SafeMarkup::checkPlain($translatable_storage_setting));
+ }
+
/**
* Test translation storage in locale storage.
*/
@@ -936,4 +1083,43 @@ class ConfigTranslationUiTest extends WebTestBase {
)));
}
+ /**
+ * Helper function that returns a .po file with a given number of plural forms.
+ */
+ public function getPoFile($plurals) {
+ $po_file = array();
+
+ $po_file[1] = <<< EOF
+msgid ""
+msgstr ""
+"Project-Id-Version: Drupal 8\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: nplurals=1; plural=0;\\n"
+EOF;
+
+ $po_file[2] = <<< EOF
+msgid ""
+msgstr ""
+"Project-Id-Version: Drupal 8\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: nplurals=2; plural=(n>1);\\n"
+EOF;
+
+ $po_file[4] = <<< EOF
+msgid ""
+msgstr ""
+"Project-Id-Version: Drupal 8\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: nplurals=4; plural=(((n%100)==1)?(0):(((n%100)==2)?(1):((((n%100)==3)||((n%100)==4))?(2):3)));\\n"
+EOF;
+
+ return $po_file[$plurals];
+ }
+
}
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
index 8a2fc59e6..804193197 100644
--- a/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
+++ b/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
@@ -57,7 +57,7 @@ class ConfigEntityMapperTest extends UnitTestCase {
protected function setUp() {
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
- $this->entity = $this->getMock('Drupal\Core\Entity\EntityInterface');
+ $this->entity = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityInterface');
$this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
@@ -109,6 +109,10 @@ class ConfigEntityMapperTest extends UnitTestCase {
->will($this->returnValue('entity_id'));
$entity_type = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
+ $entity_type
+ ->expects($this->any())
+ ->method('getConfigPrefix')
+ ->will($this->returnValue('config_prefix'));
$this->entityManager
->expects($this->once())
->method('getDefinition')
@@ -118,6 +122,10 @@ class ConfigEntityMapperTest extends UnitTestCase {
$result = $this->configEntityMapper->setEntity($this->entity);
$this->assertTrue($result);
+ // Ensure that the configuration name was added to the mapper.
+ $plugin_definition = $this->configEntityMapper->getPluginDefinition();
+ $this->assertTrue(in_array('config_prefix.entity_id', $plugin_definition['names']));
+
// Make sure setEntity() returns FALSE when called a second time.
$result = $this->configEntityMapper->setEntity($this->entity);
$this->assertFalse($result);
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigFieldMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigFieldMapperTest.php
new file mode 100644
index 000000000..740b7226c
--- /dev/null
+++ b/core/modules/config_translation/tests/src/Unit/ConfigFieldMapperTest.php
@@ -0,0 +1,116 @@
+entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+ $this->entity = $this->getMock('Drupal\field\FieldConfigInterface');
+
+ $definition = array(
+ 'class' => '\Drupal\config_translation\ConfigFieldMapper',
+ 'base_route_name' => 'entity.field_config.node_field_edit_form',
+ 'title' => '!label field',
+ 'names' => array(),
+ 'entity_type' => 'field_config',
+ );
+
+ $locale_config_manager = $this->getMockBuilder('Drupal\locale\LocaleConfigManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->configFieldMapper = new ConfigFieldMapper(
+ 'node_fields',
+ $definition,
+ $this->getConfigFactoryStub(),
+ $this->getMock('Drupal\Core\Config\TypedConfigManagerInterface'),
+ $locale_config_manager,
+ $this->getMock('Drupal\config_translation\ConfigMapperManagerInterface'),
+ $this->getMock('Drupal\Core\Routing\RouteProviderInterface'),
+ $this->getStringTranslationStub(),
+ $this->entityManager,
+ $this->getMock('Drupal\Core\Language\LanguageManagerInterface')
+ );
+ }
+
+ /**
+ * Tests ConfigFieldMapper::setEntity().
+ *
+ * @covers ::setEntity
+ */
+ public function testSetEntity() {
+ $entity_type = $this->getMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
+ $entity_type
+ ->expects($this->any())
+ ->method('getConfigPrefix')
+ ->will($this->returnValue('config_prefix'));
+
+ $this->entityManager
+ ->expects($this->any())
+ ->method('getDefinition')
+ ->will($this->returnValue($entity_type));
+
+ $field_storage = $this->getMock('Drupal\field\FieldStorageConfigInterface');
+ $field_storage
+ ->expects($this->any())
+ ->method('id')
+ ->will($this->returnValue('field_storage_id'));
+
+ $this->entity
+ ->expects($this->any())
+ ->method('getFieldStorageDefinition')
+ ->will($this->returnValue($field_storage));
+
+ $result = $this->configFieldMapper->setEntity($this->entity);
+ $this->assertTrue($result);
+
+ // Ensure that the configuration name was added to the mapper.
+ $plugin_definition = $this->configFieldMapper->getPluginDefinition();
+ $this->assertTrue(in_array('config_prefix.field_storage_id', $plugin_definition['names']));
+
+ // Make sure setEntity() returns FALSE when called a second time.
+ $result = $this->configFieldMapper->setEntity($this->entity);
+ $this->assertFalse($result);
+ }
+
+}
diff --git a/core/modules/contact/config/schema/contact.schema.yml b/core/modules/contact/config/schema/contact.schema.yml
index 5a62e9da3..e8df71aba 100644
--- a/core/modules/contact/config/schema/contact.schema.yml
+++ b/core/modules/contact/config/schema/contact.schema.yml
@@ -43,3 +43,14 @@ contact.settings:
user_default_enabled:
type: boolean
label: 'Personal contact form enabled by default'
+
+migrate.source.d6_contact_settings:
+ type: migrate_source_sql
+ label: 'Drupal 6 contact settings'
+ mapping:
+ variables:
+ type: sequence
+ label: 'Variables'
+ sequence:
+ type: string
+ label: 'Variable'
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 5d4c26bc4..1f479f2bd 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -143,7 +143,7 @@ function contact_mail($key, &$message, $params) {
$message['subject'] .= t('[!form] !subject', $variables, $options);
$message['body'][] = t("!sender-name (!sender-url) sent a message using the contact form at !form-url.", $variables, $options);
$build = entity_view($contact_message, 'mail', $language->getId());
- $message['body'][] = \Drupal::service('renderer')->renderPlain($build);
+ $message['body'][] = (string) \Drupal::service('renderer')->renderPlain($build);
break;
case 'page_autoreply':
@@ -162,7 +162,7 @@ function contact_mail($key, &$message, $params) {
$message['body'][] = t("!sender-name (!sender-url) has sent you a message via your contact form at !site-name.", $variables, $options);
$message['body'][] = t("If you don't want to receive such emails, you can change your settings at !recipient-edit-url.", $variables, $options);
$build = entity_view($contact_message, 'mail', $language->getId());
- $message['body'][] = \Drupal::service('renderer')->renderPlain($build);
+ $message['body'][] = (string) \Drupal::service('renderer')->renderPlain($build);
break;
}
}
diff --git a/core/modules/contact/contact.routing.yml b/core/modules/contact/contact.routing.yml
index 2608a377f..d81bb9fc2 100644
--- a/core/modules/contact/contact.routing.yml
+++ b/core/modules/contact/contact.routing.yml
@@ -39,7 +39,7 @@ contact.site_page:
requirements:
_permission: 'access site-wide contact form'
-contact.site_page_form:
+entity.contact_form.canonical:
path: '/contact/{contact_form}'
defaults:
_title: 'Contact'
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_contact_category.yml b/core/modules/contact/migration_templates/d6_contact_category.yml
similarity index 87%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_contact_category.yml
rename to core/modules/contact/migration_templates/d6_contact_category.yml
index cab9f3be1..d50e4fe75 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_contact_category.yml
+++ b/core/modules/contact/migration_templates/d6_contact_category.yml
@@ -20,7 +20,3 @@ process:
weight: weight
destination:
plugin: entity:contact_form
-dependencies:
- module:
- - contact
- - migrate_drupal
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_contact_settings.yml b/core/modules/contact/migration_templates/d6_contact_settings.yml
similarity index 82%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_contact_settings.yml
rename to core/modules/contact/migration_templates/d6_contact_settings.yml
index d0e4a3da8..e0897feec 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_contact_settings.yml
+++ b/core/modules/contact/migration_templates/d6_contact_settings.yml
@@ -20,9 +20,3 @@ destination:
migration_dependencies:
required:
- d6_contact_category
-dependencies:
- config:
- - migrate.migration.d6_contact_category
- module:
- - contact
- - migrate_drupal
diff --git a/core/modules/contact/src/ContactFormEditForm.php b/core/modules/contact/src/ContactFormEditForm.php
index 3300c593b..c545b0e53 100644
--- a/core/modules/contact/src/ContactFormEditForm.php
+++ b/core/modules/contact/src/ContactFormEditForm.php
@@ -112,8 +112,8 @@ class ContactFormEditForm extends EntityForm implements ContainerInjectionInterf
/**
* {@inheritdoc}
*/
- public function validate(array $form, FormStateInterface $form_state) {
- parent::validate($form, $form_state);
+ public function validateForm(array &$form, FormStateInterface $form_state) {
+ parent::validateForm($form, $form_state);
// Validate and each email recipient.
$recipients = explode(',', $form_state->getValue('recipients'));
diff --git a/core/modules/contact/src/ContactFormListBuilder.php b/core/modules/contact/src/ContactFormListBuilder.php
index fd4727f63..5a2b6b0fa 100644
--- a/core/modules/contact/src/ContactFormListBuilder.php
+++ b/core/modules/contact/src/ContactFormListBuilder.php
@@ -10,6 +10,7 @@ namespace Drupal\contact;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Link;
/**
* Defines a class to build a listing of contact form entities.
@@ -32,13 +33,14 @@ class ContactFormListBuilder extends ConfigEntityListBuilder {
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
- $row['form'] = $this->getLabel($entity);
// Special case the personal form.
if ($entity->id() == 'personal') {
+ $row['form'] = $this->getLabel($entity);
$row['recipients'] = t('Selected user');
$row['selected'] = t('No');
}
else {
+ $row['form'] = $entity->link(NULL, 'canonical');
$row['recipients'] = SafeMarkup::checkPlain(implode(', ', $entity->getRecipients()));
$default_form = \Drupal::config('contact.settings')->get('default_form');
$row['selected'] = ($default_form == $entity->id() ? t('Yes') : t('No'));
diff --git a/core/modules/contact/src/Entity/ContactForm.php b/core/modules/contact/src/Entity/ContactForm.php
index 0967c907e..1a9f73a81 100644
--- a/core/modules/contact/src/Entity/ContactForm.php
+++ b/core/modules/contact/src/Entity/ContactForm.php
@@ -36,6 +36,7 @@ use Drupal\contact\ContactFormInterface;
* "delete-form" = "/admin/structure/contact/manage/{contact_form}/delete",
* "edit-form" = "/admin/structure/contact/manage/{contact_form}",
* "collection" = "/admin/structure/contact",
+ * "canonical" = "/contact/{contact_form}",
* },
* config_export = {
* "id",
diff --git a/core/modules/contact/src/MessageForm.php b/core/modules/contact/src/MessageForm.php
index 78de6ddc5..adad13102 100644
--- a/core/modules/contact/src/MessageForm.php
+++ b/core/modules/contact/src/MessageForm.php
@@ -168,8 +168,8 @@ class MessageForm extends ContentEntityForm {
$elements = parent::actions($form, $form_state);
$elements['submit']['#value'] = $this->t('Send message');
$elements['preview'] = array(
+ '#type' => 'submit',
'#value' => $this->t('Preview'),
- '#validate' => array('::validate'),
'#submit' => array('::submitForm', '::preview'),
);
return $elements;
@@ -187,10 +187,8 @@ class MessageForm extends ContentEntityForm {
/**
* {@inheritdoc}
*/
- public function validate(array $form, FormStateInterface $form_state) {
- parent::validate($form, $form_state);
-
- $message = $this->entity;
+ public function validateForm(array &$form, FormStateInterface $form_state) {
+ $message = parent::validateForm($form, $form_state);
// Check if flood control has been activated for sending emails.
if (!$this->currentUser()->hasPermission('administer contact forms') && (!$message->isPersonal() || !$this->currentUser()->hasPermission('administer users'))) {
@@ -204,6 +202,8 @@ class MessageForm extends ContentEntityForm {
)));
}
}
+
+ return $message;
}
/**
@@ -231,20 +231,4 @@ class MessageForm extends ContentEntityForm {
$message->save();
}
- /**
- * {@inheritdoc}
- */
- protected function init(FormStateInterface $form_state) {
- $message = $this->entity;
-
- // Make the message inherit the current content language unless specifically
- // set.
- if ($message->isNew() && !$message->langcode->value) {
- $language_content = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT);
- $message->langcode->value = $language_content->getId();
- }
-
- parent::init($form_state);
- }
-
}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/ContactCategory.php b/core/modules/contact/src/Plugin/migrate/source/d6/ContactCategory.php
similarity index 91%
rename from core/modules/migrate_drupal/src/Plugin/migrate/source/d6/ContactCategory.php
rename to core/modules/contact/src/Plugin/migrate/source/d6/ContactCategory.php
index 97ecdc3d0..153b597f0 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/ContactCategory.php
+++ b/core/modules/contact/src/Plugin/migrate/source/d6/ContactCategory.php
@@ -2,10 +2,10 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Plugin\migrate\source\d6\ContactCategory.
+ * Contains \Drupal\contact\Plugin\migrate\source\d6\ContactCategory.
*/
-namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
+namespace Drupal\contact\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
use Drupal\migrate\Row;
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/ContactSettings.php b/core/modules/contact/src/Plugin/migrate/source/d6/ContactSettings.php
similarity index 80%
rename from core/modules/migrate_drupal/src/Plugin/migrate/source/d6/ContactSettings.php
rename to core/modules/contact/src/Plugin/migrate/source/d6/ContactSettings.php
index 6bcfd7ac8..48e7a5e5c 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/ContactSettings.php
+++ b/core/modules/contact/src/Plugin/migrate/source/d6/ContactSettings.php
@@ -2,10 +2,11 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Plugin\migrate\source\d6\ContactSettings.
+ * Contains \Drupal\contact\Plugin\migrate\source\d6\ContactSettings.
*/
-namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
+namespace Drupal\contact\Plugin\migrate\source\d6;
+
use Drupal\migrate_drupal\Plugin\migrate\source\Variable;
/**
diff --git a/core/modules/contact/src/Tests/ContactSitewideTest.php b/core/modules/contact/src/Tests/ContactSitewideTest.php
index c815d35ae..87d693a38 100644
--- a/core/modules/contact/src/Tests/ContactSitewideTest.php
+++ b/core/modules/contact/src/Tests/ContactSitewideTest.php
@@ -250,10 +250,17 @@ class ContactSitewideTest extends WebTestBase {
// Test field UI and field integration.
$this->drupalGet('admin/structure/contact');
+ $view_link = $this->xpath('//table/tbody/tr/td/a[contains(@href, :href) and text()=:text]', [
+ ':href' => \Drupal::url('entity.contact_form.canonical', ['contact_form' => $contact_form]),
+ ':text' => $label,
+ ]
+ );
+ $this->assertTrue(!empty($view_link), 'Contact listing links to contact form.');
+
// Find out in which row the form we want to add a field to is.
$i = 0;
foreach($this->xpath('//table/tbody/tr') as $row) {
- if (((string)$row->td[0]) == $label) {
+ if (((string) $row->td[0]->a) == $label) {
break;
}
$i++;
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateContactCategoryTest.php b/core/modules/contact/src/Tests/Migrate/d6/MigrateContactCategoryTest.php
similarity index 77%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateContactCategoryTest.php
rename to core/modules/contact/src/Tests/Migrate/d6/MigrateContactCategoryTest.php
index dd01a6c52..459b515c1 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateContactCategoryTest.php
+++ b/core/modules/contact/src/Tests/Migrate/d6/MigrateContactCategoryTest.php
@@ -2,20 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateContactCategoryTest.
+ * Contains \Drupal\contact\Tests\Migrate\d6\MigrateContactCategoryTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\contact\Tests\Migrate\d6;
use Drupal\contact\Entity\ContactForm;
-use Drupal\migrate\MigrateExecutable;
-use Drupal\migrate\MigrateMessage;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Migrate contact categories to contact.form.*.yml.
*
- * @group migrate_drupal
+ * @group contact
*/
class MigrateContactCategoryTest extends MigrateDrupal6TestBase {
@@ -31,13 +29,8 @@ class MigrateContactCategoryTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
- $migration = entity_load('migration', 'd6_contact_category');
- $dumps = array(
- $this->getDumpDirectory() . '/Contact.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, new MigrateMessage());
- $executable->import();
+ $this->loadDumps(['Contact.php']);
+ $this->executeMigration('d6_contact_category');
}
/**
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateContactConfigsTest.php b/core/modules/contact/src/Tests/Migrate/d6/MigrateContactConfigsTest.php
similarity index 69%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateContactConfigsTest.php
rename to core/modules/contact/src/Tests/Migrate/d6/MigrateContactConfigsTest.php
index 1b4874427..99eb40560 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateContactConfigsTest.php
+++ b/core/modules/contact/src/Tests/Migrate/d6/MigrateContactConfigsTest.php
@@ -2,20 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateContactConfigsTest.
+ * Contains \Drupal\contact\Tests\Migrate\d6\MigrateContactConfigsTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\contact\Tests\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
-use Drupal\migrate\MigrateMessage;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to contact.settings.yml.
*
- * @group migrate_drupal
+ * @group contact
*/
class MigrateContactConfigsTest extends MigrateDrupal6TestBase {
@@ -41,14 +39,8 @@ class MigrateContactConfigsTest extends MigrateDrupal6TestBase {
),
);
$this->prepareMigrations($id_mappings);
- $migration = entity_load('migration', 'd6_contact_settings');
- $dumps = array(
- $this->getDumpDirectory() . '/Variable.php',
- $this->getDumpDirectory() . '/Contact.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, new MigrateMessage());
- $executable->import();
+ $this->loadDumps(['Variable.php', 'Contact.php']);
+ $this->executeMigration('d6_contact_settings');
}
/**
diff --git a/core/modules/contact/src/Tests/Views/ContactLinkTest.php b/core/modules/contact/src/Tests/Views/ContactLinkTest.php
index 4c82d1878..cb137d3fb 100644
--- a/core/modules/contact/src/Tests/Views/ContactLinkTest.php
+++ b/core/modules/contact/src/Tests/Views/ContactLinkTest.php
@@ -86,7 +86,7 @@ class ContactLinkTest extends ViewTestBase {
// Disable contact link for no_contact.
$this->userData->set('contact', $no_contact_account->id(), 'enabled', FALSE);
// @todo Remove cache invalidation in https://www.drupal.org/node/2477903.
- Cache::invalidateTags($no_contact_account->getCacheTags());
+ Cache::invalidateTags($no_contact_account->getCacheTagsToInvalidate());
$this->drupalGet('test-contact-link');
$this->assertContactLinks($accounts, array('root', 'admin'));
}
diff --git a/core/modules/migrate_drupal/tests/src/Unit/source/d6/ContactCategoryTest.php b/core/modules/contact/tests/src/Unit/Plugin/migrate/source/d6/ContactCategoryTest.php
similarity index 82%
rename from core/modules/migrate_drupal/tests/src/Unit/source/d6/ContactCategoryTest.php
rename to core/modules/contact/tests/src/Unit/Plugin/migrate/source/d6/ContactCategoryTest.php
index a3c40552f..0a77e644e 100644
--- a/core/modules/migrate_drupal/tests/src/Unit/source/d6/ContactCategoryTest.php
+++ b/core/modules/contact/tests/src/Unit/Plugin/migrate/source/d6/ContactCategoryTest.php
@@ -2,21 +2,21 @@
/**
* @file
- * Contains \Drupal\Tests\migrate_drupal\Unit\source\d6\ContactCategoryTest.
+ * Contains \Drupal\Tests\contact\Unit\Plugin\migrate\source\d6\ContactCategoryTest.
*/
-namespace Drupal\Tests\migrate_drupal\Unit\source\d6;
+namespace Drupal\Tests\contact\Unit\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase;
/**
* Tests D6 contact category source plugin.
*
- * @group migrate_drupal
+ * @group contact
*/
class ContactCategoryTest extends MigrateSqlSourceTestCase {
- const PLUGIN_CLASS = 'Drupal\migrate_drupal\Plugin\migrate\source\d6\ContactCategory';
+ const PLUGIN_CLASS = 'Drupal\contact\Plugin\migrate\source\d6\ContactCategory';
protected $migrationConfiguration = array(
'id' => 'test',
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 3b29ecc99..da027710f 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -13,6 +13,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\StringTranslation\TranslationWrapper;
/**
* Implements hook_help().
@@ -251,8 +252,8 @@ function content_translation_views_data_alter(array &$data) {
*/
function content_translation_menu_links_discovered_alter(array &$links) {
// Clarify where translation settings are located.
- $links['language.content_settings_page']['title'] = 'Content language and translation';
- $links['language.content_settings_page']['description'] = 'Configure language and translation support for content.';
+ $links['language.content_settings_page']['title'] = new TranslationWrapper('Content language and translation');
+ $links['language.content_settings_page']['description'] = new TranslationWrapper('Configure language and translation support for content.');
}
/**
@@ -466,7 +467,6 @@ function content_translation_language_configuration_element_process(array $eleme
// default to no translatability.
'#default_value' => $context['bundle'] ? \Drupal::service('content_translation.manager')->isEnabled($context['entity_type'], $context['bundle']) : FALSE,
'#element_validate' => array('content_translation_language_configuration_element_validate'),
- '#prefix' => '' . t('Translation') . ' ',
);
$submit_name = isset($form['actions']['save_continue']) ? 'save_continue' : 'submit';
diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php
index 28529eb0b..10cf5f3b8 100644
--- a/core/modules/content_translation/src/ContentTranslationHandler.php
+++ b/core/modules/content_translation/src/ContentTranslationHandler.php
@@ -473,9 +473,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
$form['#entity_builders'][] = array($this, 'entityFormEntityBuild');
// Handle entity validation.
- if (isset($form['actions']['submit'])) {
- $form['actions']['submit']['#validate'][] = array($this, 'entityFormValidate');
- }
+ $form['#validate'][] = array($this, 'entityFormValidate');
// Handle entity deletion.
if (isset($form['actions']['delete'])) {
diff --git a/core/modules/content_translation/src/Controller/ContentTranslationController.php b/core/modules/content_translation/src/Controller/ContentTranslationController.php
index ec884b120..9c6879ac9 100644
--- a/core/modules/content_translation/src/Controller/ContentTranslationController.php
+++ b/core/modules/content_translation/src/Controller/ContentTranslationController.php
@@ -8,6 +8,7 @@
namespace Drupal\content_translation\Controller;
use Drupal\content_translation\ContentTranslationManagerInterface;
+use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Language\LanguageInterface;
@@ -92,6 +93,10 @@ class ContentTranslationController extends ControllerBase {
$manager = $this->manager;
$entity_type = $entity->getEntityType();
+ // Start collecting the cacheability metadata, starting with the entity and
+ // later merge in the access result cacheability metadata.
+ $cacheability = CacheableMetadata::createFromObject($entity);
+
$languages = $this->languageManager()->getLanguages();
$original = $entity->getUntranslated()->language()->getId();
$translations = $entity->getTranslationLanguages();
@@ -180,11 +185,16 @@ class ContentTranslationController extends ControllerBase {
// If the user is allowed to edit the entity we point the edit link to
// the entity form, otherwise if we are not dealing with the original
// language we point the link to the translation form.
- if ($entity->access('update') && $entity_type->hasLinkTemplate('edit-form')) {
+ $update_access = $entity->access('update', NULL, TRUE);
+ $translation_access = $handler->getTranslationAccess($entity, 'update');
+ $cacheability = $cacheability
+ ->merge(CacheableMetadata::createFromObject($update_access))
+ ->merge(CacheableMetadata::createFromObject($translation_access));
+ if ($update_access->isAllowed() && $entity_type->hasLinkTemplate('edit-form')) {
$links['edit']['url'] = $entity->urlInfo('edit-form');
$links['edit']['language'] = $language;
}
- elseif (!$is_original && $handler->getTranslationAccess($entity, 'update')->isAllowed()) {
+ elseif (!$is_original && $translation_access->isAllowed()) {
$links['edit']['url'] = $edit_url;
}
@@ -206,6 +216,11 @@ class ContentTranslationController extends ControllerBase {
}
else {
$source_name = isset($languages[$source]) ? $languages[$source]->getName() : $this->t('n/a');
+ $delete_access = $entity->access('delete', NULL, TRUE);
+ $translation_access = $handler->getTranslationAccess($entity, 'delete');
+ $cacheability = $cacheability
+ ->merge(CacheableMetadata::createFromObject($delete_access))
+ ->merge(CacheableMetadata::createFromObject($translation_access));
if ($entity->access('delete') && $entity_type->hasLinkTemplate('delete-form')) {
$links['delete'] = array(
'title' => $this->t('Delete'),
@@ -213,7 +228,7 @@ class ContentTranslationController extends ControllerBase {
'language' => $language,
);
}
- elseif ($handler->getTranslationAccess($entity, 'delete')->isAllowed()) {
+ elseif ($translation_access->isAllowed()) {
$links['delete'] = array(
'title' => $this->t('Delete'),
'url' => $delete_url,
@@ -226,7 +241,10 @@ class ContentTranslationController extends ControllerBase {
$row_title = $source_name = $this->t('n/a');
$source = $entity->language()->getId();
- if ($source != $langcode && $handler->getTranslationAccess($entity, 'create')->isAllowed()) {
+ $create_translation_access = $handler->getTranslationAccess($entity, 'create');
+ $cacheability = $cacheability
+ ->merge(CacheableMetadata::createFromObject($create_translation_access));
+ if ($source != $langcode && $create_translation_access->isAllowed()) {
if ($translatable) {
$links['add'] = array(
'title' => $this->t('Add'),
@@ -284,6 +302,9 @@ class ContentTranslationController extends ControllerBase {
// Add metadata to the build render array to let other modules know about
// which entity this is.
$build['#entity'] = $entity;
+ $cacheability
+ ->addCacheTags($entity->getCacheTags())
+ ->applyTo($build);
$build['content_translation_overview'] = array(
'#theme' => 'table',
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php b/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php
index 4d51eb5c8..c928bacc5 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php
@@ -231,7 +231,7 @@ class ContentTranslationSettingsTest extends WebTestBase {
* @param array $edit
* An array of values to submit to the content translation settings page.
*
- * @return boolean
+ * @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertSettings($entity_type, $bundle, $enabled, $edit) {
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
index e5243594c..5faa85cf6 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
@@ -7,18 +7,22 @@
namespace Drupal\content_translation\Tests;
+use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Component\Utility\SafeMarkup;
+use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
/**
* Tests the Content Translation UI.
*/
abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
+ use AssertPageCacheContextsAndTagsTrait;
+
/**
* The id of the entity being translated.
*
@@ -33,6 +37,15 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
*/
protected $testLanguageSelector = TRUE;
+ /**
+ * Default cache contexts expected on a non-translated entity.
+ *
+ * Cache contexts will not be checked if this list is empty.
+ *
+ * @var string[]
+ */
+ protected $defaultCacheContexts = ['languages:language_interface', 'theme', 'user.permissions'];
+
/**
* Tests the basic translation UI.
*/
@@ -64,6 +77,11 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$this->assertTrue($entity, 'Entity found in the database.');
$this->drupalGet($entity->urlInfo());
$this->assertResponse(200, 'Entity URL is valid.');
+
+ // Ensure that the content language cache context is not yet added to the
+ // page.
+ $this->assertCacheContexts($this->defaultCacheContexts);
+
$this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
$this->assertNoText('Source language', 'Source language column correctly hidden.');
@@ -87,9 +105,14 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
], array('language' => $language));
$this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), $this->getFormSubmitActionForNewTranslation($entity, $langcode));
- // Get the entity and reset its cache, so that the new translation gets the
- // updated values.
+ // Ensure that the content language cache context is not yet added to the
+ // page.
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
+ $this->drupalGet($entity->urlInfo());
+ $this->assertCacheContexts(Cache::mergeContexts(['languages:language_content'], $this->defaultCacheContexts));
+
+ // Reset the cache of the entity, so that the new translation gets the
+ // updated values.
$metadata_source_translation = $this->manager->getTranslationMetadata($entity->getTranslation($default_langcode));
$metadata_target_translation = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
index 3de797ec5..223bb8193 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
@@ -113,6 +113,13 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
$this->drupalLogin($user);
$this->drupalGet($translations_url);
+ // Make sure that the user.permissions cache context and the cache tags
+ // for the entity are present.
+ $this->assertCacheContext('user.permissions');
+ foreach ($this->entity->getCacheTags() as $cache_tag) {
+ $this->assertCacheTag($cache_tag);
+ }
+
foreach ($ops as $op => $label) {
if ($op != $current_op) {
$this->assertNoLink($label, format_string('No %op link found.', array('%op' => $label)));
diff --git a/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php b/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php
index 512e73544..6c95f7eb9 100644
--- a/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php
+++ b/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php
@@ -9,6 +9,7 @@ namespace Drupal\Tests\content_translation\Unit\Access;
use Drupal\content_translation\Access\ContentTranslationManageAccessCheck;
use Drupal\Core\Access\AccessResult;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Language\Language;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\Routing\Route;
@@ -22,6 +23,28 @@ use Symfony\Component\Routing\Route;
*/
class ContentTranslationManageAccessCheckTest extends UnitTestCase {
+ /**
+ * The cache contexts manager.
+ *
+ * @var \Drupal\Core\Cache\Context\CacheContextsManager|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $cacheContextsManager;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ $this->cacheContextsManager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $container = new ContainerBuilder();
+ $container->set('cache_contexts_manager', $this->cacheContextsManager);
+ \Drupal::setContainer($container);
+ }
+
/**
* Tests the create access method.
*
@@ -76,6 +99,9 @@ class ContentTranslationManageAccessCheckTest extends UnitTestCase {
$entity->expects($this->once())
->method('getCacheTags')
->will($this->returnValue(array('node:1337')));
+ $entity->expects($this->once())
+ ->method('getCacheContexts')
+ ->willReturn(array());
// Set the route requirements.
$route = new Route('test_route');
diff --git a/core/modules/contextual/css/contextual.theme.css b/core/modules/contextual/css/contextual.theme.css
index 92e9b745c..065065d11 100644
--- a/core/modules/contextual/css/contextual.theme.css
+++ b/core/modules/contextual/css/contextual.theme.css
@@ -107,7 +107,6 @@
text-decoration: none;
}
.no-touch .contextual-region .contextual .contextual-links li a:hover {
- color: white;
- background-image: -webkit-linear-gradient(rgb(78,159,234) 0%, rgb(65,126,210) 100%);
- background-image: linear-gradient(rgb(78,159,234) 0%,rgb(65,126,210) 100%);
+ color: #000;
+ background: #f7fcff;
}
diff --git a/core/modules/contextual/js/toolbar/models/StateModel.js b/core/modules/contextual/js/toolbar/models/StateModel.js
index 0c261c5d0..691eaab7c 100644
--- a/core/modules/contextual/js/toolbar/models/StateModel.js
+++ b/core/modules/contextual/js/toolbar/models/StateModel.js
@@ -66,20 +66,17 @@
*/
initialize: function (attrs, options) {
// Respond to new/removed contextual links.
- this.listenTo(options.contextualCollection, {
- 'reset remove add': this.countContextualLinks,
- 'add': this.lockNewContextualLinks
- });
+ this.listenTo(options.contextualCollection, 'reset remove add', this.countContextualLinks);
+ this.listenTo(options.contextualCollection, 'add', this.lockNewContextualLinks);
- this.listenTo(this, {
- // Automatically determine visibility.
- 'change:contextualCount': this.updateVisibility,
- // Whenever edit mode is toggled, lock all contextual links.
- 'change:isViewing': function (model, isViewing) {
- options.contextualCollection.each(function (contextualModel) {
- contextualModel.set('isLocked', !isViewing);
- });
- }
+ // Automatically determine visibility.
+ this.listenTo(this, 'change:contextualCount', this.updateVisibility);
+
+ // Whenever edit mode is toggled, lock all contextual links.
+ this.listenTo(this, 'change:isViewing', function (model, isViewing) {
+ options.contextualCollection.each(function (contextualModel) {
+ contextualModel.set('isLocked', !isViewing);
+ });
});
},
diff --git a/core/modules/contextual/src/ContextualController.php b/core/modules/contextual/src/ContextualController.php
index 975113dc9..ca248d196 100644
--- a/core/modules/contextual/src/ContextualController.php
+++ b/core/modules/contextual/src/ContextualController.php
@@ -44,7 +44,7 @@ class ContextualController implements ContainerAwareInterface {
'#type' => 'contextual_links',
'#contextual_links' => _contextual_id_to_links($id),
);
- $rendered[$id] = $this->container->get('renderer')->renderRoot($element);
+ $rendered[$id] = (string) $this->container->get('renderer')->renderRoot($element);
}
return new JsonResponse($rendered);
diff --git a/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php b/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php
index 8846ab935..b8ed11c37 100644
--- a/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php
+++ b/core/modules/contextual/src/Tests/ContextualDynamicContextTest.php
@@ -8,6 +8,7 @@
namespace Drupal\contextual\Tests;
use Drupal\Component\Serialization\Json;
+use Drupal\Core\Url;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\simpletest\WebTestBase;
use Drupal\Core\Template\Attribute;
@@ -46,7 +47,7 @@ class ContextualDynamicContextTest extends WebTestBase {
*
* @var array
*/
- public static $modules = array('contextual', 'node', 'views', 'views_ui', 'language');
+ public static $modules = array('contextual', 'node', 'views', 'views_ui', 'language', 'menu_test');
protected function setUp() {
parent::setUp();
@@ -137,6 +138,11 @@ class ContextualDynamicContextTest extends WebTestBase {
$id = 'node:node=' . $node3->id() . ':changed=' . $node3->getChangedTime() . '&langcode=it';
$this->drupalGet('node', ['language' => ConfigurableLanguage::createFromLangcode('it')]);
$this->assertContextualLinkPlaceHolder($id);
+
+ // Get a page where contextual links are directly rendered.
+ $this->drupalGet(Url::fromRoute('menu_test.contextual_test'));
+ $this->assertEscaped("");
+ $this->assertLink('Edit menu - contextual');
}
/**
diff --git a/core/modules/datetime/datetime.views.inc b/core/modules/datetime/datetime.views.inc
new file mode 100644
index 000000000..c12a3aca4
--- /dev/null
+++ b/core/modules/datetime/datetime.views.inc
@@ -0,0 +1,49 @@
+ $table_data) {
+ // Set the 'datetime' filter type.
+ $data[$table_name][$field_storage->getName() . '_value']['filter']['id'] = 'datetime';
+
+ // Set the 'datetime' argument type.
+ $data[$table_name][$field_storage->getName() . '_value']['argument']['id'] = 'datetime';
+
+ // Create year, month, and day arguments.
+ $group = $data[$table_name][$field_storage->getName() . '_value']['group'];
+ $arguments = [
+ // Argument type => help text.
+ 'year' => t('Date in the form of YYYY.'),
+ 'month' => t('Date in the form of MM.'),
+ 'day' => t('Date in the form of DD.'),
+ ];
+ foreach ($arguments as $argument_type => $help_text) {
+ $data[$table_name][$field_storage->getName() . '_value_' . $argument_type] = [
+ 'title' => $field_storage->getLabel() . ' (' . $argument_type . ')',
+ 'help' => $help_text,
+ 'argument' => [
+ 'field' => $field_storage->getName() . '_value',
+ 'id' => 'datetime_' . $argument_type,
+ ],
+ 'group' => $group,
+ ];
+ }
+
+ // Set the 'datetime' sort handler.
+ $data[$table_name][$field_storage->getName() . '_value']['sort']['id'] = 'datetime';
+ }
+
+ return $data;
+}
diff --git a/core/modules/datetime/src/Plugin/views/argument/Date.php b/core/modules/datetime/src/Plugin/views/argument/Date.php
new file mode 100644
index 000000000..7bd5cdf33
--- /dev/null
+++ b/core/modules/datetime/src/Plugin/views/argument/Date.php
@@ -0,0 +1,45 @@
+tableAlias.$this->realField";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDateFormat($format) {
+ // Pass in the string-field option.
+ return $this->query->getDateFormat($this->getDateField(), $format, TRUE);
+ }
+}
diff --git a/core/modules/datetime/src/Plugin/views/argument/DayDate.php b/core/modules/datetime/src/Plugin/views/argument/DayDate.php
new file mode 100644
index 000000000..f100db044
--- /dev/null
+++ b/core/modules/datetime/src/Plugin/views/argument/DayDate.php
@@ -0,0 +1,22 @@
+dateFormatter = $date_formatter;
+ $this->requestStack = $request_stack;
+
+ // Date format depends on field storage format.
+ $definition = $this->getFieldStorageDefinition();
+ if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
+ $this->dateFormat = DATETIME_DATE_STORAGE_FORMAT;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $configuration,
+ $plugin_id,
+ $plugin_definition,
+ $container->get('date.formatter'),
+ $container->get('request_stack')
+ );
+ }
+
+ /**
+ * Override parent method, which deals with dates as integers.
+ */
+ protected function opBetween($field) {
+ $origin = ($this->value['type'] == 'offset') ? $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME') : 0;
+ $a = intval(strtotime($this->value['min'], $origin));
+ $b = intval(strtotime($this->value['max'], $origin));
+
+ // Formatting will vary on date storage.
+
+
+ // Convert to ISO format and format for query. UTC timezone is used since
+ // dates are stored in UTC.
+ $a = $this->query->getDateFormat("'" . $this->dateFormatter->format($a, 'custom', DATETIME_DATETIME_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE) . "'", $this->dateFormat, TRUE);
+ $b = $this->query->getDateFormat("'" . $this->dateFormatter->format($b, 'custom', DATETIME_DATETIME_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE) . "'", $this->dateFormat, TRUE);
+
+ // This is safe because we are manually scrubbing the values.
+ $operator = strtoupper($this->operator);
+ $field = $this->query->getDateFormat($field, $this->dateFormat, TRUE);
+ $this->query->addWhereExpression($this->options['group'], "$field $operator $a AND $b");
+ }
+
+ /**
+ * Override parent method, which deals with dates as integers.
+ */
+ protected function opSimple($field) {
+ $origin = (!empty($this->value['type']) && $this->value['type'] == 'offset') ? $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME') : 0;
+ $value = intval(strtotime($this->value['value'], $origin));
+
+ // Convert to ISO. UTC is used since dates are stored in UTC.
+ $value = $this->query->getDateFormat("'" . $this->dateFormatter->format($value, 'custom', DATETIME_DATETIME_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE) . "'", $this->dateFormat, TRUE);
+
+ // This is safe because we are manually scrubbing the value.
+ $field = $this->query->getDateFormat($field, $this->dateFormat, TRUE);
+ $this->query->addWhereExpression($this->options['group'], "$field $this->operator $value");
+ }
+
+}
diff --git a/core/modules/datetime/src/Plugin/views/sort/Date.php b/core/modules/datetime/src/Plugin/views/sort/Date.php
new file mode 100644
index 000000000..12eddc6ed
--- /dev/null
+++ b/core/modules/datetime/src/Plugin/views/sort/Date.php
@@ -0,0 +1,56 @@
+tableAlias.$this->realField";
+ }
+
+ /**
+ * Override query to provide 'second' granularity.
+ */
+ public function query() {
+ $this->ensureMyTable();
+ switch ($this->options['granularity']) {
+ case 'second':
+ $formula = $this->getDateFormat('YmdHis');
+ $this->query->addOrderBy(NULL, $formula, $this->options['order'], $this->tableAlias . '_' . $this->field . '_' . $this->options['granularity']);
+ return;
+ }
+
+ // All other granularities are handled by the numeric sort handler.
+ parent::query();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * Overridden in order to pass in the string date flag.
+ */
+ public function getDateFormat($format) {
+ return $this->query->getDateFormat($this->getDateField(), $format, TRUE);
+ }
+
+
+}
diff --git a/core/modules/datetime/src/Tests/DateTimeFieldTest.php b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
index d54dbb55a..60637ffc2 100644
--- a/core/modules/datetime/src/Tests/DateTimeFieldTest.php
+++ b/core/modules/datetime/src/Tests/DateTimeFieldTest.php
@@ -438,6 +438,46 @@ class DateTimeFieldTest extends WebTestBase {
$this->assertOptionSelected("edit-$field_name-0-value-hour", '5', 'Correct hour selected.');
$this->assertOptionSelected("edit-$field_name-0-value-minute", '15', 'Correct minute selected.');
$this->assertOptionSelected("edit-$field_name-0-value-ampm", 'am', 'Correct ampm selected.');
+
+ // Test the widget using increment other than 1 and 24 hour mode.
+ entity_get_form_display($this->field->getTargetEntityTypeId(), $this->field->getTargetBundle(), 'default')
+ ->setComponent($field_name, array(
+ 'type' => 'datetime_datelist',
+ 'settings' => array(
+ 'increment' => 15,
+ 'date_order' => 'YMD',
+ 'time_type' => '24',
+ ),
+ ))
+ ->save();
+ \Drupal::entityManager()->clearCachedFieldDefinitions();
+
+ // Display creation form.
+ $this->drupalGet('entity_test/add');
+
+ // Other elements are unaffected by the changed settings.
+ $this->assertFieldByXPath("//*[@id=\"edit-$field_name-0-value-hour\"]", NULL, 'Hour element found.');
+ $this->assertOptionSelected("edit-$field_name-0-value-hour", '', 'No hour selected.');
+ $this->assertNoFieldByXPath("//*[@id=\"edit-$field_name-0-value-ampm\"]", NULL, 'AMPM element not found.');
+
+ // Submit a valid date and ensure it is accepted.
+ $date_value = array('year' => 2012, 'month' => 12, 'day' => 31, 'hour' => 17, 'minute' => 15);
+
+ $edit = array();
+ foreach ($date_value as $part => $value) {
+ $edit["{$field_name}[0][value][$part]"] = $value;
+ }
+
+ $this->drupalPostForm(NULL, $edit, t('Save'));
+ preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
+ $id = $match[1];
+ $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
+
+ $this->assertOptionSelected("edit-$field_name-0-value-year", '2012', 'Correct year selected.');
+ $this->assertOptionSelected("edit-$field_name-0-value-month", '12', 'Correct month selected.');
+ $this->assertOptionSelected("edit-$field_name-0-value-day", '31', 'Correct day selected.');
+ $this->assertOptionSelected("edit-$field_name-0-value-hour", '17', 'Correct hour selected.');
+ $this->assertOptionSelected("edit-$field_name-0-value-minute", '15', 'Correct minute selected.');
}
/**
diff --git a/core/modules/datetime/src/Tests/Views/ArgumentDateTimeTest.php b/core/modules/datetime/src/Tests/Views/ArgumentDateTimeTest.php
new file mode 100644
index 000000000..18906846f
--- /dev/null
+++ b/core/modules/datetime/src/Tests/Views/ArgumentDateTimeTest.php
@@ -0,0 +1,141 @@
+nodes[] = $this->drupalCreateNode([
+ 'field_date' => [
+ 'value' => $date,
+ ]
+ ]);
+ }
+ }
+
+ /**
+ * Test year argument.
+ *
+ * @see \Drupal\datetime\Plugin\views\argument\YearDate
+ */
+ public function testDatetimeArgumentYear() {
+ $view = Views::getView('test_argument_datetime');
+
+ // The 'default' display has the 'year' argument.
+ $view->setDisplay('default');
+ $this->executeView($view, ['2000']);
+ $expected = [];
+ $expected[] = ['nid' => $this->nodes[0]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
+
+ $view->setDisplay('default');
+ $this->executeView($view, ['2002']);
+ $expected = [];
+ $expected[] = ['nid' => $this->nodes[2]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
+ }
+
+ /**
+ * Test month argument.
+ *
+ * @see \Drupal\datetime\Plugin\views\argument\MonthDate
+ */
+ public function testDatetimeArgumentMonth() {
+ $view = Views::getView('test_argument_datetime');
+ // The 'embed_1' display has the 'month' argument.
+ $view->setDisplay('embed_1');
+
+ $this->executeView($view, ['10']);
+ $expected = [];
+ $expected[] = ['nid' => $this->nodes[0]->id()];
+ $expected[] = ['nid' => $this->nodes[1]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
+
+ $view->setDisplay('embed_1');
+ $this->executeView($view, ['01']);
+ $expected = [];
+ $expected[] = ['nid' => $this->nodes[2]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
+ }
+
+ /**
+ * Test day argument.
+ *
+ * @see \Drupal\datetime\Plugin\views\argument\DayDate
+ */
+ public function testDatetimeArgumentDay() {
+ $view = Views::getView('test_argument_datetime');
+
+ // The 'embed_2' display has the 'day' argument.
+ $view->setDisplay('embed_2');
+ $this->executeView($view, ['10']);
+ $expected = [];
+ $expected[] = ['nid' => $this->nodes[0]->id()];
+ $expected[] = ['nid' => $this->nodes[1]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
+
+ $view->setDisplay('embed_2');
+ $this->executeView($view, ['01']);
+ $expected = [];
+ $expected[] = ['nid' => $this->nodes[2]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
+ }
+
+ /**
+ * Test year, month, and day arguments combined.
+ */
+ public function testDatetimeArgumentAll() {
+ $view = Views::getView('test_argument_datetime');
+ // The 'embed_3' display has year, month, and day arguments.
+ $view->setDisplay('embed_3');
+
+ $this->executeView($view, ['2000', '10', '10']);
+ $expected = [];
+ $expected[] = ['nid' => $this->nodes[0]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
+
+ $view->setDisplay('embed_3');
+ $this->executeView($view, ['2002', '01', '01']);
+ $expected = [];
+ $expected[] = ['nid' => $this->nodes[2]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
+ }
+
+}
diff --git a/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php b/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php
new file mode 100644
index 000000000..b9a46992f
--- /dev/null
+++ b/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php
@@ -0,0 +1,78 @@
+ 'page',
+ 'name' => 'page'
+ ]);
+ $node_type->save();
+ $fieldStorage = entity_create('field_storage_config', [
+ 'field_name' => static::$field_name,
+ 'entity_type' => 'node',
+ 'type' => 'datetime',
+ 'settings' => ['datetime_type' => DateTimeItem::DATETIME_TYPE_DATETIME],
+ ]);
+ $fieldStorage->save();
+ $field = entity_create('field_config', [
+ 'field_storage' => $fieldStorage,
+ 'bundle' => 'page',
+ 'required' => TRUE,
+ ]);
+ $field->save();
+
+ // Views needs to be aware of the new field.
+ $this->container->get('views.views_data')->clear();
+
+ // Set column map.
+ $this->map = [
+ 'nid' => 'nid',
+ ];
+
+ // Load test views.
+ ViewTestData::createTestViews(get_class($this), ['datetime_test']);
+ }
+
+}
diff --git a/core/modules/datetime/src/Tests/Views/FilterDateTest.php b/core/modules/datetime/src/Tests/Views/FilterDateTest.php
new file mode 100644
index 000000000..5e54f78e2
--- /dev/null
+++ b/core/modules/datetime/src/Tests/Views/FilterDateTest.php
@@ -0,0 +1,115 @@
+setSetting('datetime_type', DateTimeItem::DATETIME_TYPE_DATE);
+ $storage->save();
+
+ $dates = [
+ // Tomorrow.
+ \Drupal::service('date.formatter')->format(static::$date + 86400, 'custom', DATETIME_DATE_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
+ // Today.
+ \Drupal::service('date.formatter')->format(static::$date, 'custom', DATETIME_DATE_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
+ // Yesterday.
+ \Drupal::service('date.formatter')->format(static::$date - 86400, 'custom', DATETIME_DATE_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
+ ];
+
+ foreach ($dates as $date) {
+ $this->nodes[] = $this->drupalCreateNode([
+ 'field_date' => [
+ 'value' => $date,
+ ]
+ ]);
+ }
+ }
+
+ /**
+ * Test offsets with date-only fields.
+ */
+ public function testDateOffsets() {
+ $view = Views::getView('test_filter_datetime');
+ $field = static::$field_name . '_value';
+
+ // Test simple operations.
+ $view->initHandlers();
+
+ // A greater than or equal to 'now', should return the 'today' and
+ // the 'tomorrow' node.
+ $view->filter[$field]->operator = '>=';
+ $view->filter[$field]->value['type'] = 'offset';
+ $view->filter[$field]->value['value'] = 'now';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[0]->id()],
+ ['nid' => $this->nodes[1]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Only dates in the past.
+ $view->initHandlers();
+ $view->filter[$field]->operator = '<';
+ $view->filter[$field]->value['type'] = 'offset';
+ $view->filter[$field]->value['value'] = 'now';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[2]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Test offset for between operator. Only the 'tomorrow' node should appear.
+ $view->initHandlers();
+ $view->filter[$field]->operator = 'between';
+ $view->filter[$field]->value['type'] = 'offset';
+ $view->filter[$field]->value['max'] = '+2 days';
+ $view->filter[$field]->value['min'] = '+1 day';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[0]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ }
+
+}
diff --git a/core/modules/datetime/src/Tests/Views/FilterDateTimeTest.php b/core/modules/datetime/src/Tests/Views/FilterDateTimeTest.php
new file mode 100644
index 000000000..137f45ccf
--- /dev/null
+++ b/core/modules/datetime/src/Tests/Views/FilterDateTimeTest.php
@@ -0,0 +1,195 @@
+format(static::$date, 'custom', DATETIME_DATETIME_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
+ ];
+ foreach ($dates as $date) {
+ $this->nodes[] = $this->drupalCreateNode([
+ 'field_date' => [
+ 'value' => $date,
+ ]
+ ]);
+ }
+ }
+
+ /**
+ * Test filter operations.
+ */
+ public function testDatetimeFilter() {
+ $this->_testOffset();
+ $this->_testBetween();
+ $this->_testExact();
+ }
+
+ /**
+ * Test offset operations.
+ */
+ protected function _testOffset() {
+ $view = Views::getView('test_filter_datetime');
+ $field = static::$field_name . '_value';
+
+ // Test simple operations.
+ $view->initHandlers();
+
+ $view->filter[$field]->operator = '>';
+ $view->filter[$field]->value['type'] = 'offset';
+ $view->filter[$field]->value['value'] = '+1 hour';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[3]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Test offset for between operator.
+ $view->initHandlers();
+ $view->filter[$field]->operator = 'between';
+ $view->filter[$field]->value['type'] = 'offset';
+ $view->filter[$field]->value['max'] = '+2 days';
+ $view->filter[$field]->value['min'] = '+1 hour';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[3]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ }
+
+ /**
+ * Test between operations.
+ */
+ protected function _testBetween() {
+ $view = Views::getView('test_filter_datetime');
+ $field = static::$field_name . '_value';
+
+ // Test between with min and max.
+ $view->initHandlers();
+ $view->filter[$field]->operator = 'between';
+ $view->filter[$field]->value['min'] = '2001-01-01';
+ $view->filter[$field]->value['max'] = '2002-01-01';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[1]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Test between with just max.
+ $view->initHandlers();
+ $view->filter[$field]->operator = 'between';
+ $view->filter[$field]->value['max'] = '2002-01-01';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[0]->id()],
+ ['nid' => $this->nodes[1]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Test not between with min and max.
+ $view->initHandlers();
+ $view->filter[$field]->operator = 'not between';
+ $view->filter[$field]->value['min'] = '2001-01-01';
+ $view->filter[$field]->value['max'] = '2002-01-01';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[0]->id()],
+ ['nid' => $this->nodes[2]->id()],
+ ['nid' => $this->nodes[3]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Test not between with just max.
+ $view->initHandlers();
+ $view->filter[$field]->operator = 'not between';
+ $view->filter[$field]->value['max'] = '2001-01-01';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[1]->id()],
+ ['nid' => $this->nodes[2]->id()],
+ ['nid' => $this->nodes[3]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ }
+
+ /**
+ * Test exact date matching.
+ */
+ protected function _testExact() {
+ $view = Views::getView('test_filter_datetime');
+ $field = static::$field_name . '_value';
+
+ // Test between with min and max.
+ $view->initHandlers();
+ $view->filter[$field]->operator = '=';
+ $view->filter[$field]->value['min'] = '';
+ $view->filter[$field]->value['max'] = '';
+ // Use the date from node 3. Use the site timezone (mimics a value entered
+ // through the UI).
+ $view->filter[$field]->value['value'] = \Drupal::service('date.formatter')->format(static::$date, 'custom', DATETIME_DATETIME_STORAGE_FORMAT, static::$timezone);
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[3]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ }
+
+}
diff --git a/core/modules/datetime/src/Tests/Views/SortDateTimeTest.php b/core/modules/datetime/src/Tests/Views/SortDateTimeTest.php
new file mode 100644
index 000000000..f3f89366c
--- /dev/null
+++ b/core/modules/datetime/src/Tests/Views/SortDateTimeTest.php
@@ -0,0 +1,99 @@
+nodes[] = $this->drupalCreateNode([
+ 'field_date' => [
+ 'value' => $date,
+ ]
+ ]);
+ }
+ }
+
+ /**
+ * Tests the datetime sort handler.
+ */
+ public function testDateTimeSort() {
+ $field = static::$field_name . '_value';
+ $view = Views::getView('test_sort_datetime');
+
+ // Sort order is DESC.
+ $view->initHandlers();
+ $view->sort[$field]->options['granularity'] = 'minute';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[0]->id()],
+ ['nid' => $this->nodes[3]->id()],
+ ['nid' => $this->nodes[2]->id()],
+ ['nid' => $this->nodes[1]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Check ASC.
+ $view->initHandlers();
+ $field = static::$field_name . '_value';
+ $view->sort[$field]->options['order'] = 'ASC';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[1]->id()],
+ ['nid' => $this->nodes[2]->id()],
+ ['nid' => $this->nodes[3]->id()],
+ ['nid' => $this->nodes[0]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Change granularity to 'year', and the secondary node ID order should
+ // define the order of nodes with the same year.
+ $view->initHandlers();
+ $view->sort[$field]->options['granularity'] = 'year';
+ $view->sort[$field]->options['order'] = 'DESC';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[0]->id()],
+ ['nid' => $this->nodes[1]->id()],
+ ['nid' => $this->nodes[2]->id()],
+ ['nid' => $this->nodes[3]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+ }
+
+}
diff --git a/core/modules/datetime/tests/modules/datetime_test/datetime_test.info.yml b/core/modules/datetime/tests/modules/datetime_test/datetime_test.info.yml
new file mode 100644
index 000000000..84ba9e41d
--- /dev/null
+++ b/core/modules/datetime/tests/modules/datetime_test/datetime_test.info.yml
@@ -0,0 +1,8 @@
+name: 'Datetime test'
+type: module
+description: 'Provides default views for tests.'
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+ - views
diff --git a/core/modules/datetime/tests/modules/datetime_test/test_views/views.view.test_argument_datetime.yml b/core/modules/datetime/tests/modules/datetime_test/test_views/views.view.test_argument_datetime.yml
new file mode 100644
index 000000000..e22ad30c8
--- /dev/null
+++ b/core/modules/datetime/tests/modules/datetime_test/test_views/views.view.test_argument_datetime.yml
@@ -0,0 +1,99 @@
+langcode: und
+status: true
+dependencies: { }
+id: test_argument_datetime
+label: ''
+module: views
+description: ''
+tag: ''
+base_table: node_field_data
+base_field: nid
+core: '8'
+display:
+ default:
+ display_options:
+ defaults:
+ fields: false
+ pager: false
+ sorts: false
+ arguments:
+ field_date_value_year:
+ field: field_date_value_year
+ id: field_date_value
+ table: node__field_date
+ plugin_id: datetime_year
+ fields:
+ id:
+ field: nid
+ id: nid
+ relationship: none
+ table: node
+ plugin_id: numeric
+ pager:
+ options:
+ offset: 0
+ type: none
+ sorts:
+ id:
+ field: nid
+ id: nid
+ order: ASC
+ relationship: none
+ table: node
+ plugin_id: numeric
+ display_plugin: default
+ display_title: Master
+ id: default
+ position: 0
+ embed_1:
+ display_options:
+ defaults:
+ arguments: false
+ arguments:
+ field_date_value_month:
+ field: field_date_value_month
+ id: field_date_value
+ table: node__field_date
+ plugin_id: datetime_month
+ display_plugin: embed
+ id: embed_1
+ display_title: ''
+ position: null
+ embed_2:
+ display_options:
+ defaults:
+ arguments: false
+ arguments:
+ field_date_value_day:
+ field: field_date_value_day
+ id: field_date_value
+ table: node__field_date
+ plugin_id: datetime_day
+ display_plugin: embed
+ id: embed_2
+ display_title: ''
+ position: null
+ embed_3:
+ display_options:
+ defaults:
+ arguments: false
+ arguments:
+ field_date_value_year:
+ field: field_date_value_year
+ id: field_date_value
+ table: node__field_date
+ plugin_id: datetime_year
+ field_date_value_month:
+ field: field_date_value_month
+ id: field_date_value
+ table: node__field_date
+ plugin_id: datetime_month
+ field_date_value_day:
+ field: field_date_value_day
+ id: field_date_value
+ table: node__field_date
+ plugin_id: datetime_day
+ display_plugin: embed
+ id: embed_2
+ display_title: ''
+ position: null
diff --git a/core/modules/datetime/tests/modules/datetime_test/test_views/views.view.test_filter_datetime.yml b/core/modules/datetime/tests/modules/datetime_test/test_views/views.view.test_filter_datetime.yml
new file mode 100644
index 000000000..c9b23ded6
--- /dev/null
+++ b/core/modules/datetime/tests/modules/datetime_test/test_views/views.view.test_filter_datetime.yml
@@ -0,0 +1,56 @@
+langcode: und
+status: true
+dependencies:
+ module:
+ - node
+id: test_filter_datetime
+label: ''
+module: views
+description: ''
+tag: ''
+base_table: node_field_data
+base_field: nid
+core: '8'
+display:
+ default:
+ display_options:
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ fields:
+ nid:
+ field: nid
+ id: nid
+ table: node
+ plugin_id: node
+ filters:
+ field_date_value:
+ id: field_date_value
+ table: node__field_date
+ field: field_date_value
+ plugin_id: datetime
+ sorts:
+ id:
+ field: nid
+ id: nid
+ order: ASC
+ relationship: none
+ table: node
+ plugin_id: numeric
+ pager:
+ type: full
+ query:
+ options:
+ query_comment: ''
+ type: views_query
+ style:
+ type: default
+ row:
+ type: fields
+ display_plugin: default
+ display_title: Master
+ id: default
+ position: 0
diff --git a/core/modules/datetime/tests/modules/datetime_test/test_views/views.view.test_sort_datetime.yml b/core/modules/datetime/tests/modules/datetime_test/test_views/views.view.test_sort_datetime.yml
new file mode 100644
index 000000000..8b8d052ae
--- /dev/null
+++ b/core/modules/datetime/tests/modules/datetime_test/test_views/views.view.test_sort_datetime.yml
@@ -0,0 +1,57 @@
+langcode: und
+status: true
+dependencies:
+ module:
+ - node
+id: test_sort_datetime
+label: ''
+module: views
+description: ''
+tag: ''
+base_table: node_field_data
+base_field: nid
+core: '8'
+display:
+ default:
+ display_options:
+ access:
+ type: none
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ fields:
+ nid:
+ field: nid
+ id: nid
+ table: node
+ plugin_id: node
+ sorts:
+ field_date_value:
+ field: field_date_value
+ id: field_date_value
+ relationship: none
+ table: node__field_date
+ order: DESC
+ plugin_id: datetime
+ id:
+ field: nid
+ id: nid
+ order: ASC
+ relationship: none
+ table: node
+ plugin_id: numeric
+ pager:
+ type: full
+ query:
+ options:
+ query_comment: ''
+ type: views_query
+ style:
+ type: default
+ row:
+ type: fields
+ display_plugin: default
+ display_title: Master
+ id: default
+ position: 0
diff --git a/core/modules/dblog/dblog.module b/core/modules/dblog/dblog.module
index 01c4d2395..91c6038e6 100644
--- a/core/modules/dblog/dblog.module
+++ b/core/modules/dblog/dblog.module
@@ -11,6 +11,7 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\StringTranslation\TranslationWrapper;
/**
* Implements hook_help().
@@ -41,9 +42,9 @@ function dblog_help($route_name, RouteMatchInterface $route_match) {
function dblog_menu_links_discovered_alter(&$links) {
if (\Drupal::moduleHandler()->moduleExists('search')) {
$links['dblog.search'] = array(
- 'title' => 'Top search phrases',
+ 'title' => new TranslationWrapper('Top search phrases'),
'route_name' => 'dblog.search',
- 'description' => 'View most popular search phrases.',
+ 'description' => new TranslationWrapper('View most popular search phrases.'),
'parent' => 'system.admin_reports',
);
}
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_dblog_settings.yml b/core/modules/dblog/migration_templates/d6_dblog_settings.yml
similarity index 81%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_dblog_settings.yml
rename to core/modules/dblog/migration_templates/d6_dblog_settings.yml
index fb387f57e..6c94b1e2e 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_dblog_settings.yml
+++ b/core/modules/dblog/migration_templates/d6_dblog_settings.yml
@@ -11,7 +11,3 @@ process:
destination:
plugin: config
config_name: dblog.settings
-dependencies:
- module:
- - dblog
- - migrate_drupal
diff --git a/core/modules/dblog/migration_templates/d7_dblog_settings.yml b/core/modules/dblog/migration_templates/d7_dblog_settings.yml
new file mode 100644
index 000000000..8e681fdbc
--- /dev/null
+++ b/core/modules/dblog/migration_templates/d7_dblog_settings.yml
@@ -0,0 +1,13 @@
+id: d7_dblog_settings
+label: Drupal 7 database logging configuration
+migration_tags:
+ - Drupal 7
+source:
+ plugin: variable
+ variables:
+ - dblog_row_limit
+process:
+ row_limit: dblog_row_limit
+destination:
+ plugin: config
+ config_name: dblog.settings
diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php
index 2e2eccf3a..3982da677 100644
--- a/core/modules/dblog/src/Controller/DbLogController.php
+++ b/core/modules/dblog/src/Controller/DbLogController.php
@@ -207,7 +207,7 @@ class DbLogController extends ControllerBase {
$this->dateFormatter->format($dblog->timestamp, 'short'),
$message,
array('data' => $username),
- Xss::filter($dblog->link),
+ SafeMarkup::xssFilter($dblog->link),
),
// Attributes for table row.
'class' => array(Html::getClass('dblog-' . $dblog->type), $classes[$dblog->severity]),
@@ -285,7 +285,7 @@ class DbLogController extends ControllerBase {
),
array(
array('data' => $this->t('Operations'), 'header' => TRUE),
- SafeMarkup::checkAdminXss($dblog->link),
+ array('data' => array('#markup' => $dblog->link)),
),
);
$build['dblog_table'] = array(
diff --git a/core/modules/dblog/src/Tests/DbLogFormInjectionTest.php b/core/modules/dblog/src/Tests/DbLogFormInjectionTest.php
index 6957f517d..62fa23455 100644
--- a/core/modules/dblog/src/Tests/DbLogFormInjectionTest.php
+++ b/core/modules/dblog/src/Tests/DbLogFormInjectionTest.php
@@ -99,6 +99,9 @@ class DbLogFormInjectionTest extends KernelTestBase implements FormInterface {
*/
public function testLoggerSerialization() {
$form_state = new FormState();
+
+ // Forms are only serialized during POST requests.
+ $form_state->setRequestMethod('POST');
$form_state->setCached();
$form_builder = $this->container->get('form_builder');
$form_id = $form_builder->getFormId($this, $form_state);
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateDblogConfigsTest.php b/core/modules/dblog/src/Tests/Migrate/d6/MigrateDblogConfigsTest.php
similarity index 61%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateDblogConfigsTest.php
rename to core/modules/dblog/src/Tests/Migrate/d6/MigrateDblogConfigsTest.php
index 33cca917f..f3dcfa163 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateDblogConfigsTest.php
+++ b/core/modules/dblog/src/Tests/Migrate/d6/MigrateDblogConfigsTest.php
@@ -2,20 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateDblogConfigsTest.
+ * Contains \Drupal\dblog\Tests\Migrate\d6\MigrateDblogConfigsTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\dblog\Tests\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
-use Drupal\migrate\MigrateMessage;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to dblog.settings.yml.
*
- * @group migrate_drupal
+ * @group dblog
*/
class MigrateDblogConfigsTest extends MigrateDrupal6TestBase {
@@ -33,13 +31,8 @@ class MigrateDblogConfigsTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
- $migration = entity_load('migration', 'd6_dblog_settings');
- $dumps = array(
- $this->getDumpDirectory() . '/Variable.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, new MigrateMessage());
- $executable->import();
+ $this->loadDumps(['Variable.php']);
+ $this->executeMigration('d6_dblog_settings');
}
/**
diff --git a/core/modules/dblog/src/Tests/Migrate/d7/MigrateDblogConfigsTest.php b/core/modules/dblog/src/Tests/Migrate/d7/MigrateDblogConfigsTest.php
new file mode 100644
index 000000000..7d1f28a15
--- /dev/null
+++ b/core/modules/dblog/src/Tests/Migrate/d7/MigrateDblogConfigsTest.php
@@ -0,0 +1,44 @@
+installConfig(static::$modules);
+ $this->loadDumps(['Variable.php']);
+ $this->executeMigration('d7_dblog_settings');
+ }
+
+ /**
+ * Tests migration of dblog variables to dblog.settings.yml.
+ */
+ public function testDblogSettings() {
+ $config = $this->config('dblog.settings');
+ $this->assertIdentical(1000, $config->get('row_limit'));
+ }
+
+}
diff --git a/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php b/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php
index c7f37975d..063ce2550 100644
--- a/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php
+++ b/core/modules/dblog/src/Tests/Views/ViewsIntegrationTest.php
@@ -34,7 +34,7 @@ class ViewsIntegrationTest extends ViewUnitTestBase {
*
* @var array
*/
- public static $modules = array('dblog', 'dblog_test_views');
+ public static $modules = array('dblog', 'dblog_test_views', 'user');
/**
* {@inheritdoc}
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 7fa3c05e8..470f26938 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -11,7 +11,7 @@ use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\StringTranslation\TranslationWrapper;
use Drupal\Core\Entity\EntityInterface;
use Drupal\filter\FilterFormatInterface;
use Drupal\filter\Plugin\FilterInterface;
@@ -47,8 +47,8 @@ function editor_help($route_name, RouteMatchInterface $route_match) {
* of text editors.
*/
function editor_menu_links_discovered_alter(array &$links) {
- $links['filter.admin_overview']['title'] = 'Text formats and editors';
- $links['filter.admin_overview']['description'] = 'Configure how user-contributed content is filtered and formatted, as well as the text editor user interface (WYSIWYGs or toolbars).';
+ $links['filter.admin_overview']['title'] = new TranslationWrapper('Text formats and editors');
+ $links['filter.admin_overview']['description'] = new TranslationWrapper('Configure how user-contributed content is filtered and formatted, as well as the text editor user interface (WYSIWYGs or toolbars).');
}
/**
diff --git a/core/modules/editor/src/EditorController.php b/core/modules/editor/src/EditorController.php
index 44feda30f..192f3411e 100644
--- a/core/modules/editor/src/EditorController.php
+++ b/core/modules/editor/src/EditorController.php
@@ -48,7 +48,7 @@ class EditorController extends ControllerBase {
// Direct text editing is only supported for single-valued fields.
$field = $entity->getTranslation($langcode)->$field_name;
$editable_text = check_markup($field->value, $field->format, $langcode, array(FilterInterface::TYPE_TRANSFORM_REVERSIBLE, FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE));
- $response->addCommand(new GetUntransformedTextCommand($editable_text));
+ $response->addCommand(new GetUntransformedTextCommand((string) $editable_text));
return $response;
}
diff --git a/core/modules/editor/src/Form/EditorImageDialog.php b/core/modules/editor/src/Form/EditorImageDialog.php
index 8b37f723f..9c2feb5a3 100644
--- a/core/modules/editor/src/Form/EditorImageDialog.php
+++ b/core/modules/editor/src/Form/EditorImageDialog.php
@@ -15,12 +15,40 @@ use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\editor\Ajax\EditorDialogSave;
use Drupal\Core\Ajax\CloseModalDialogCommand;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
/**
* Provides an image dialog for text editors.
*/
class EditorImageDialog extends FormBase {
+ /**
+ * The file storage service.
+ *
+ * @var \Drupal\Core\Entity\EntityStorageInterface
+ */
+ protected $fileStorage;
+
+ /**
+ * Constructs a form object for image dialog.
+ *
+ * @param \Drupal\Core\Entity\EntityStorageInterface $file_storage
+ * The file storage service.
+ */
+ public function __construct(EntityStorageInterface $file_storage) {
+ $this->fileStorage = $file_storage;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('entity.manager')->getStorage('file')
+ );
+ }
+
/**
* {@inheritdoc}
*/
@@ -208,7 +236,7 @@ class EditorImageDialog extends FormBase {
// attributes and set data-entity-type to 'file'.
$fid = $form_state->getValue(array('fid', 0));
if (!empty($fid)) {
- $file = file_load($fid);
+ $file = $this->fileStorage->load($fid);
$file_url = file_create_url($file->getFileUri());
// Transform absolute image URLs to relative image URLs: prevent problems
// on multisite set-ups and prevent mixed content errors.
diff --git a/core/modules/entity_reference/src/Plugin/views/display/EntityReference.php b/core/modules/entity_reference/src/Plugin/views/display/EntityReference.php
index 83f998f60..e55481c0a 100644
--- a/core/modules/entity_reference/src/Plugin/views/display/EntityReference.php
+++ b/core/modules/entity_reference/src/Plugin/views/display/EntityReference.php
@@ -137,10 +137,11 @@ class EntityReference extends DisplayPluginBase {
$conditions = db_or();
// Build the condition using the selected search fields.
- foreach ($style_options['options']['search_fields'] as $field_alias) {
- if (!empty($field_alias)) {
+ foreach ($style_options['options']['search_fields'] as $field_id) {
+ if (!empty($field_id)) {
// Get the table and field names for the checked field.
- $field = $this->view->query->fields[$this->view->field[$field_alias]->field_alias];
+ $field_alias = $this->view->query->addField($this->view->field[$field_id]->table, $field_id);
+ $field = $this->view->query->fields[$field_alias];
// Add an OR condition for the field.
$conditions->condition($field['table'] . '.' . $field['field'], $value, 'LIKE');
}
diff --git a/core/modules/entity_reference/src/Plugin/views/style/EntityReference.php b/core/modules/entity_reference/src/Plugin/views/style/EntityReference.php
index e3aeb34a8..328d96e15 100644
--- a/core/modules/entity_reference/src/Plugin/views/style/EntityReference.php
+++ b/core/modules/entity_reference/src/Plugin/views/style/EntityReference.php
@@ -47,7 +47,7 @@ class EntityReference extends StylePluginBase {
*/
protected function defineOptions() {
$options = parent::defineOptions();
- $options['search_fields'] = array('default' => NULL);
+ $options['search_fields'] = array('default' => array());
return $options;
}
diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php
index 6c72d49bf..eb8b85fb7 100644
--- a/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php
+++ b/core/modules/entity_reference/src/Tests/EntityReferenceAdminTest.php
@@ -7,6 +7,7 @@
namespace Drupal\entity_reference\Tests;
+use Drupal\Core\Entity\Entity;
use Drupal\field_ui\Tests\FieldUiTestTrait;
use Drupal\simpletest\WebTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
@@ -45,14 +46,21 @@ class EntityReferenceAdminTest extends WebTestBase {
parent::setUp();
$this->drupalPlaceBlock('system_breadcrumb_block');
- // Create test user.
- $admin_user = $this->drupalCreateUser(array('access content', 'administer node fields', 'administer node display', 'administer views'));
- $this->drupalLogin($admin_user);
-
// Create a content type, with underscores.
$type_name = strtolower($this->randomMachineName(8)) . '_test';
$type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
$this->type = $type->id();
+
+ // Create test user.
+ $admin_user = $this->drupalCreateUser(array(
+ 'access content',
+ 'administer node fields',
+ 'administer node display',
+ 'administer views',
+ 'create ' . $type_name . ' content',
+ 'edit own ' . $type_name . ' content',
+ ));
+ $this->drupalLogin($admin_user);
}
/**
@@ -191,10 +199,66 @@ class EntityReferenceAdminTest extends WebTestBase {
);
$this->drupalPostAjaxForm($bundle_path . '/fields/' . $field_name, $edit, 'settings[handler]');
$edit = array(
+ 'required' => FALSE,
'settings[handler_settings][view][view_and_display]' => 'test_entity_reference_entity_test:entity_reference_1',
);
$this->drupalPostForm(NULL, $edit, t('Save settings'));
$this->assertResponse(200);
+
+ // Create a new view and display it as a entity reference.
+ $edit = array(
+ 'id' => 'node_test_view',
+ 'label' => 'Node Test View',
+ 'show[wizard_key]' => 'node',
+ 'page[create]' => 1,
+ 'page[title]' => 'Test Node View',
+ 'page[path]' => 'test/node/view',
+ 'page[style][style_plugin]' => 'default',
+ 'page[style][row_plugin]' => 'fields',
+ );
+ $this->drupalPostForm('admin/structure/views/add', $edit, t('Save and edit'));
+ $this->drupalPostForm(NULL, array(), t('Duplicate as Entity Reference'));
+ $this->clickLink(t('Settings'));
+ $edit = array(
+ 'style_options[search_fields][title]' => 'title',
+ );
+ $this->drupalPostForm(NULL, $edit, t('Apply'));
+ $this->drupalPostForm('admin/structure/views/view/node_test_view/edit/entity_reference_1', array(), t('Save'));
+ $this->clickLink(t('Settings'));
+
+ // Create a test entity reference field.
+ $field_name = 'test_entity_ref_field';
+ $edit = array(
+ 'new_storage_type' => 'field_ui:entity_reference:node',
+ 'label' => 'Test Entity Reference Field',
+ 'field_name' => $field_name,
+ );
+ $this->drupalPostForm($bundle_path . '/fields/add-field', $edit, t('Save and continue'));
+ $this->drupalPostForm(NULL, array(), t('Save field settings'));
+
+ // Add the view to the test field.
+ $edit = array(
+ 'settings[handler]' => 'views',
+ );
+ $this->drupalPostAjaxForm(NULL, $edit, 'settings[handler]');
+ $edit = array(
+ 'required' => FALSE,
+ 'settings[handler_settings][view][view_and_display]' => 'node_test_view:entity_reference_1',
+ );
+ $this->drupalPostForm(NULL, $edit, t('Save settings'));
+
+ // Create a node.
+ $edit = array(
+ 'title[0][value]' => 'Foo Node',
+ );
+ $this->drupalPostForm('node/add/' . $this->type, $edit, t('Save'));
+
+ // Try to add a new node and fill the entity reference field.
+ $this->drupalGet('node/add/' . $this->type);
+ $result = $this->xpath('//input[@name="field_test_entity_ref_field[0][target_id]" and contains(@data-autocomplete-path, "/entity_reference_autocomplete/node/views/")]');
+ $target_url = $this->getAbsoluteUrl($result[0]['data-autocomplete-path']);
+ $this->drupalGet($target_url, array('query' => array('q' => 'Foo')));
+ $this->assertRaw('Foo');
}
/**
diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceFieldTranslatedReferenceViewTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceFieldTranslatedReferenceViewTest.php
index 648920b7e..f72a4de4f 100644
--- a/core/modules/entity_reference/src/Tests/EntityReferenceFieldTranslatedReferenceViewTest.php
+++ b/core/modules/entity_reference/src/Tests/EntityReferenceFieldTranslatedReferenceViewTest.php
@@ -188,7 +188,7 @@ class EntityReferenceFieldTranslatedReferenceViewTest extends WebTestBase {
'entity_type' => $this->testEntityTypeName,
'type' => 'entity_reference',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
- 'translatable' => FALSE,
+ 'translatable' => TRUE,
'settings' => array(
'allowed_values' => array(
array(
@@ -292,7 +292,7 @@ class EntityReferenceFieldTranslatedReferenceViewTest extends WebTestBase {
'langcode' => $this->baseLangcode,
));
$node->save();
- $node->addTranslation($this->translateToLangcode, array());
+ $node->addTranslation($this->translateToLangcode, $node->toArray());
$node->save();
return $node;
diff --git a/core/modules/field/src/Entity/FieldConfig.php b/core/modules/field/src/Entity/FieldConfig.php
index 500fa426c..6054d0221 100644
--- a/core/modules/field/src/Entity/FieldConfig.php
+++ b/core/modules/field/src/Entity/FieldConfig.php
@@ -110,12 +110,12 @@ class FieldConfig extends FieldConfigBase implements FieldConfigInterface {
throw new FieldException('Attempt to create a field without a field_name.');
}
if (empty($values['entity_type'])) {
- throw new FieldException(SafeMarkup::format('Attempt to create a field @field_name without an entity_type.', array('@field_name' => $values['field_name'])));
+ throw new FieldException("Attempt to create a field '{$values['field_name']}' without an entity_type.");
}
}
// 'bundle' is required in either case.
if (empty($values['bundle'])) {
- throw new FieldException(SafeMarkup::format('Attempt to create a field @field_name without a bundle.', array('@field_name' => $values['field_name'])));
+ throw new FieldException("Attempt to create a field '{$values['field_name']}' without a bundle.");
}
parent::__construct($values, $entity_type);
@@ -288,9 +288,10 @@ class FieldConfig extends FieldConfigBase implements FieldConfigInterface {
if (!$this->fieldStorage) {
$fields = $this->entityManager()->getFieldStorageDefinitions($this->entity_type);
if (!isset($fields[$this->field_name])) {
- throw new FieldException(SafeMarkup::format('Attempt to create a field @field_name that does not exist on entity type @entity_type.', array('@field_name' => $this->field_name, '@entity_type' => $this->entity_type))); }
+ throw new FieldException('Attempt to create a field {$this->field_name} that does not exist on entity type {$this->entity_type}.');
+ }
if (!$fields[$this->field_name] instanceof FieldStorageConfigInterface) {
- throw new FieldException(SafeMarkup::format('Attempt to create a configurable field of non-configurable field storage @field_name.', array('@field_name' => $this->field_name, '@entity_type' => $this->entity_type)));
+ throw new FieldException("Attempt to create a configurable field of non-configurable field storage {$this->field_name}.");
}
$this->fieldStorage = $fields[$this->field_name];
}
diff --git a/core/modules/field/src/Entity/FieldStorageConfig.php b/core/modules/field/src/Entity/FieldStorageConfig.php
index 66b6fff4d..5fff1aa59 100644
--- a/core/modules/field/src/Entity/FieldStorageConfig.php
+++ b/core/modules/field/src/Entity/FieldStorageConfig.php
@@ -7,7 +7,6 @@
namespace Drupal\field\Entity;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
@@ -242,13 +241,13 @@ class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigI
throw new FieldException('Attempt to create a field storage without a field name.');
}
if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['field_name'])) {
- throw new FieldException(SafeMarkup::format('Attempt to create a field storage @field_name with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character', array('@field_name' => $values['field_name'])));
+ throw new FieldException("Attempt to create a field storage {$values['field_name']} with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character");
}
if (empty($values['type'])) {
- throw new FieldException(SafeMarkup::format('Attempt to create a field storage @field_name with no type.', array('@field_name' => $values['field_name'])));
+ throw new FieldException("Attempt to create a field storage {$values['field_name']} with no type.");
}
if (empty($values['entity_type'])) {
- throw new FieldException(SafeMarkup::format('Attempt to create a field storage @field_name with no entity_type.', array('@field_name' => $values['field_name'])));
+ throw new FieldException("Attempt to create a field storage {$values['field_name']} with no entity_type.");
}
parent::__construct($values, $entity_type);
@@ -309,24 +308,19 @@ class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigI
// We use Unicode::strlen() because the DB layer assumes that column widths
// are given in characters rather than bytes.
if (Unicode::strlen($this->getName()) > static::NAME_MAX_LENGTH) {
- throw new FieldException(SafeMarkup::format(
- 'Attempt to create a field storage with an name longer than @max characters: %name', array(
- '@max' => static::NAME_MAX_LENGTH,
- '%name' => $this->getName(),
- )
- ));
+ throw new FieldException('Attempt to create a field storage with an name longer than ' . static::NAME_MAX_LENGTH . ' characters: ' . $this->getName());
}
// Disallow reserved field names.
$disallowed_field_names = array_keys($entity_manager->getBaseFieldDefinitions($this->getTargetEntityTypeId()));
if (in_array($this->getName(), $disallowed_field_names)) {
- throw new FieldException(SafeMarkup::format('Attempt to create field storage %name which is reserved by entity type %type.', array('%name' => $this->getName(), '%type' => $this->getTargetEntityTypeId())));
+ throw new FieldException("Attempt to create field storage {$this->getName()} which is reserved by entity type {$this->getTargetEntityTypeId()}.");
}
// Check that the field type is known.
$field_type = $field_type_manager->getDefinition($this->getType(), FALSE);
if (!$field_type) {
- throw new FieldException(SafeMarkup::format('Attempt to create a field storage of unknown type %type.', array('%type' => $this->getType())));
+ throw new FieldException("Attempt to create a field storage of unknown type {$this->getType()}.");
}
$this->module = $field_type['provider'];
diff --git a/core/modules/field/src/ProxyClass/FieldUninstallValidator.php b/core/modules/field/src/ProxyClass/FieldUninstallValidator.php
new file mode 100644
index 000000000..89667bff1
--- /dev/null
+++ b/core/modules/field/src/ProxyClass/FieldUninstallValidator.php
@@ -0,0 +1,92 @@
+container = $container;
+ $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
+ }
+
+ /**
+ * Lazy loads the real service from the container.
+ *
+ * @return object
+ * Returns the constructed real service.
+ */
+ protected function lazyLoadItself()
+ {
+ if (!isset($this->service)) {
+ $this->service = $this->container->get($this->drupalProxyOriginalServiceId);
+ }
+
+ return $this->service;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($module)
+ {
+ return $this->lazyLoadItself()->validate($module);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setStringTranslation(\Drupal\Core\StringTranslation\TranslationInterface $translation)
+ {
+ return $this->lazyLoadItself()->setStringTranslation($translation);
+ }
+
+ }
+
+}
diff --git a/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php b/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php
index 6f04ba2bf..3c7b9d6c5 100644
--- a/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php
+++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceFormatterTest.php
@@ -200,11 +200,8 @@ class EntityReferenceFormatterTest extends EntityUnitTestBase {
';
$renderer->renderRoot($build[0]);
$this->assertEqual($build[0]['#markup'], 'default | ' . $this->referencedEntity->label() . $expected_rendered_name_field_1 . $expected_rendered_body_field_1, sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
- $expected_cache_tags = Cache::mergeTags(
- \Drupal::entityManager()->getViewBuilder($this->entityType)->getCacheTags(),
- $this->referencedEntity->getCacheTags(),
- FilterFormat::load('full_html')->getCacheTags()
- );
+ $expected_cache_tags = Cache::mergeTags(\Drupal::entityManager()->getViewBuilder($this->entityType)->getCacheTags(), $this->referencedEntity->getCacheTags());
+ $expected_cache_tags = Cache::mergeTags($expected_cache_tags, FilterFormat::load('full_html')->getCacheTags());
$this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter)));
// Test the second field item.
diff --git a/core/modules/field/src/Tests/FieldDataCountTest.php b/core/modules/field/src/Tests/FieldDataCountTest.php
index ebf0591de..aef5e7e82 100644
--- a/core/modules/field/src/Tests/FieldDataCountTest.php
+++ b/core/modules/field/src/Tests/FieldDataCountTest.php
@@ -29,6 +29,11 @@ class FieldDataCountTest extends FieldUnitTestBase {
*/
protected $storageRev;
+ /**
+ * @var \Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface
+ */
+ protected $storageUser;
+
/**
* {@inheritdoc}
*/
@@ -37,6 +42,7 @@ class FieldDataCountTest extends FieldUnitTestBase {
$this->installEntitySchema('entity_test_rev');
$this->storage = \Drupal::entityManager()->getStorage('entity_test');
$this->storageRev = \Drupal::entityManager()->getStorage('entity_test_rev');
+ $this->storageUser = \Drupal::entityManager()->getStorage('user');
}
/**
@@ -136,4 +142,22 @@ class FieldDataCountTest extends FieldUnitTestBase {
$this->assertEqual(count($entity->{$this->fieldTestData->field_name_2}), $cardinality, format_string('Revision %revision_id: expected number of values.', array('%revision_id' => $first_revision)));
}
+ /**
+ * Verify that we can count a table that contains an entry with index 0.
+ */
+ public function testCountWithIndex0() {
+ // Create an entry for the anonymous user, who has user ID 0.
+ $user = $this->storageUser
+ ->create(array(
+ 'uid' => 0,
+ 'name' => 'anonymous',
+ 'mail' => NULL,
+ 'status' => FALSE,
+ ));
+ $user->save();
+
+ $storage = $user->getFieldDefinition('name')->getFieldStorageDefinition();
+ $this->assertIdentical(TRUE, $this->storageUser->countFieldData($storage, TRUE));
+ }
+
}
diff --git a/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml b/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml
index 48f5dcf37..8e06db17b 100644
--- a/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml
+++ b/core/modules/field/tests/modules/field_test/config/schema/field_test.schema.yml
@@ -65,6 +65,9 @@ field.storage_settings.test_field:
config_data_from_storage_setting:
type: boolean
label: 'Test FieldItemInterface::storageSettingsToConfigData()'
+ translatable_storage_setting:
+ type: label
+ label: 'Translatable storage setting'
field.storage_settings.test_field_with_dependencies:
type: field.storage_settings.test_field
@@ -88,6 +91,9 @@ field.field_settings.test_field:
config_data_from_field_setting:
type: boolean
label: 'Test FieldItemInterface::fieldSettingsToConfigData()'
+ translatable_field_setting:
+ type: label
+ label: 'Translatable field setting'
field.field_settings.test_field_with_dependencies:
type: field.field_settings.test_field
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
index 9886b0307..42eb11e8d 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItem.php
@@ -33,6 +33,7 @@ class TestItem extends FieldItemBase {
'test_field_storage_setting' => 'dummy test string',
'changeable' => 'a changeable field storage setting',
'unchangeable' => 'an unchangeable field storage setting',
+ 'translatable_storage_setting' => 'a translatable field storage setting',
) + parent::defaultStorageSettings();
}
@@ -42,6 +43,7 @@ class TestItem extends FieldItemBase {
public static function defaultFieldSettings() {
return array(
'test_field_setting' => 'dummy test string',
+ 'translatable_field_setting' => 'a translatable field setting',
) + parent::defaultFieldSettings();
}
diff --git a/core/modules/field/tests/src/Unit/FieldConfigEntityUnitTest.php b/core/modules/field/tests/src/Unit/FieldConfigEntityUnitTest.php
index 2bce97d08..f249c6c15 100644
--- a/core/modules/field/tests/src/Unit/FieldConfigEntityUnitTest.php
+++ b/core/modules/field/tests/src/Unit/FieldConfigEntityUnitTest.php
@@ -178,7 +178,7 @@ class FieldConfigEntityUnitTest extends UnitTestCase {
* Test that invalid bundles are handled.
*
* @expectedException \LogicException
- * @expectedExceptionMessage Missing bundle entity, entity type bundle_entity_type , entity id test_bundle_not_exists .
+ * @expectedExceptionMessage Missing bundle entity, entity type bundle_entity_type, entity id test_bundle_not_exists.
*/
public function testCalculateDependenciesIncorrectBundle() {
$storage = $this->getMock('\Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
diff --git a/core/modules/field_ui/field_ui.js b/core/modules/field_ui/field_ui.js
index f73df41d5..c15e4a01c 100644
--- a/core/modules/field_ui/field_ui.js
+++ b/core/modules/field_ui/field_ui.js
@@ -9,6 +9,9 @@
/**
* @type {Drupal~behavior}
+ *
+ * @prop {Drupal~behaviorAttach} attach
+ * Adds behaviors to the field storage add form.
*/
Drupal.behaviors.fieldUIFieldStorageAddForm = {
attach: function (context) {
@@ -56,8 +59,14 @@
};
/**
+ * Attaches the fieldUIOverview behavior.
*
* @type {Drupal~behavior}
+ *
+ * @prop {Drupal~behaviorAttach} attach
+ * Attaches the fieldUIOverview behavior.
+ *
+ * @see Drupal.fieldUIOverview.attach
*/
Drupal.behaviors.fieldUIDisplayOverview = {
attach: function (context, settings) {
@@ -68,6 +77,8 @@
};
/**
+ * Namespace for the field UI overview.
+ *
* @namespace
*/
Drupal.fieldUIOverview = {
@@ -76,8 +87,11 @@
* Attaches the fieldUIOverview behavior.
*
* @param {HTMLTableElement} table
+ * The table element for the overview.
* @param {object} rowsData
+ * The data of the rows in the table.
* @param {object} rowHandlers
+ * Handlers to be added to the rows.
*/
attach: function (table, rowsData, rowHandlers) {
var tableDrag = Drupal.tableDrag[table.id];
@@ -243,6 +257,7 @@
* Additional data to be populated in the constructed object.
*
* @return {Drupal.fieldUIDisplayOverview.field}
+ * The field row handler constructed.
*/
Drupal.fieldUIDisplayOverview.field = function (row, data) {
this.row = row;
@@ -263,6 +278,7 @@
* Returns the region corresponding to the current form values of the row.
*
* @return {string}
+ * Either 'hidden' or 'content'.
*/
getRegion: function () {
return (this.$pluginSelect.val() === 'hidden') ? 'hidden' : 'content';
diff --git a/core/modules/field_ui/src/FieldUI.php b/core/modules/field_ui/src/FieldUI.php
index 598381770..42107d330 100644
--- a/core/modules/field_ui/src/FieldUI.php
+++ b/core/modules/field_ui/src/FieldUI.php
@@ -40,10 +40,15 @@ class FieldUI {
* @param array $destinations
* An array of destinations to redirect to.
*
- * @return \Drupal\Core\Url
+ * @return \Drupal\Core\Url|null
* The next destination to redirect to.
*/
public static function getNextDestination(array $destinations) {
+ // If there are no valid destinations left, return here.
+ if (empty($destinations)) {
+ return NULL;
+ }
+
$next_destination = array_shift($destinations);
if (is_array($next_destination)) {
$next_destination['options']['query']['destinations'] = $destinations;
@@ -59,7 +64,7 @@ class FieldUI {
}
// Redirect to any given path within the same domain.
// @todo Revisit this in https://www.drupal.org/node/2418219.
- $next_destination = Url::fromUserInput('/' . $options['path']);
+ $next_destination = Url::fromUserInput('/' . $options['path'], $options);
}
return $next_destination;
}
diff --git a/core/modules/field_ui/src/Form/EntityDisplayFormBase.php b/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
index d675cef26..3ba0258a3 100644
--- a/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
+++ b/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
@@ -567,7 +567,7 @@ abstract class EntityDisplayFormBase extends EntityForm {
if ($form_state->get('plugin_settings_update') === $field_name) {
// Only store settings actually used by the selected plugin.
$default_settings = $this->pluginManager->getDefaultSettings($options['type']);
- $options['settings'] = array_intersect_key($values['settings_edit_form']['settings'], $default_settings);
+ $options['settings'] = isset($values['settings_edit_form']['settings']) ? array_intersect_key($values['settings_edit_form']['settings'], $default_settings) : [];
$options['third_party_settings'] = isset($values['settings_edit_form']['third_party_settings']) ? $values['settings_edit_form']['third_party_settings'] : [];
$form_state->set('plugin_settings_update', NULL);
}
diff --git a/core/modules/field_ui/src/Form/EntityDisplayModeAddForm.php b/core/modules/field_ui/src/Form/EntityDisplayModeAddForm.php
index d6d801b75..350a2e99b 100644
--- a/core/modules/field_ui/src/Form/EntityDisplayModeAddForm.php
+++ b/core/modules/field_ui/src/Form/EntityDisplayModeAddForm.php
@@ -38,8 +38,8 @@ class EntityDisplayModeAddForm extends EntityDisplayModeFormBase {
/**
* {@inheritdoc}
*/
- public function validate(array $form, FormStateInterface $form_state) {
- parent::validate($form, $form_state);
+ public function validateForm(array &$form, FormStateInterface $form_state) {
+ parent::validateForm($form, $form_state);
$form_state->setValueForElement($form['id'], $this->targetEntityTypeId . '.' . $form_state->getValue('id'));
}
diff --git a/core/modules/field_ui/src/Form/FieldConfigEditForm.php b/core/modules/field_ui/src/Form/FieldConfigEditForm.php
index 866ff9a48..52ace9581 100644
--- a/core/modules/field_ui/src/Form/FieldConfigEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldConfigEditForm.php
@@ -150,8 +150,8 @@ class FieldConfigEditForm extends EntityForm {
/**
* {@inheritdoc}
*/
- public function validate(array $form, FormStateInterface $form_state) {
- parent::validate($form, $form_state);
+ public function validateForm(array &$form, FormStateInterface $form_state) {
+ parent::validateForm($form, $form_state);
if (isset($form['default_value'])) {
$item = $form['#entity']->get($this->entity->getName());
diff --git a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
index 58c4ea903..adafcc790 100644
--- a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
@@ -146,8 +146,8 @@ class FieldStorageConfigEditForm extends EntityForm {
/**
* {@inheritdoc}
*/
- public function validate(array $form, FormStateInterface $form_state) {
- parent::validate($form, $form_state);
+ public function validateForm(array &$form, FormStateInterface $form_state) {
+ parent::validateForm($form, $form_state);
// Validate field cardinality.
if ($form_state->getValue('cardinality') === 'number' && !$form_state->getValue('cardinality_number')) {
diff --git a/core/modules/field_ui/src/Tests/ManageDisplayTest.php b/core/modules/field_ui/src/Tests/ManageDisplayTest.php
index 04a353fcb..9ed319f26 100644
--- a/core/modules/field_ui/src/Tests/ManageDisplayTest.php
+++ b/core/modules/field_ui/src/Tests/ManageDisplayTest.php
@@ -163,6 +163,11 @@ class ManageDisplayTest extends WebTestBase {
$edit = array('fields[field_test][type]' => 'field_no_settings', 'refresh_rows' => 'field_test');
$this->drupalPostAjaxForm(NULL, $edit, array('op' => t('Refresh')));
$this->assertFieldByName('field_test_settings_edit');
+
+ // Make sure we can save the third party settings when there are no settings available
+ $this->drupalPostAjaxForm(NULL, array(), "field_test_settings_edit");
+ $this->drupalPostAjaxForm(NULL, $edit, "field_test_plugin_settings_update");
+
// Uninstall the module providing third party settings and ensure the button
// is no longer there.
\Drupal::service('module_installer')->uninstall(array('field_third_party_test'));
diff --git a/core/modules/field_ui/src/Tests/ManageFieldsTest.php b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
index 3b90bffc5..3b2ac3eba 100644
--- a/core/modules/field_ui/src/Tests/ManageFieldsTest.php
+++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
@@ -616,6 +616,20 @@ class ManageFieldsTest extends WebTestBase {
$this->assertUrl($url, array(), 'Stayed on the same page.');
}
+ /**
+ * Tests that external URLs in the 'destinations' query parameter are blocked.
+ */
+ public function testExternalDestinations() {
+ $options = [
+ 'query' => ['destinations' => ['http://example.com']],
+ ];
+ $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.body/storage', [], 'Save field settings', $options);
+ // The external redirect should not fire.
+ $this->assertUrl('admin/structure/types/manage/article/fields/node.article.body/storage', $options);
+ $this->assertResponse(200);
+ $this->assertRaw('Attempt to update field Body failed: The internal path component 'http://example.com' is external. You are not allowed to specify an external URL together with internal:/. .');
+ }
+
/**
* Tests that deletion removes field storages and fields as expected for a term.
*/
diff --git a/core/modules/field_ui/tests/src/Unit/FieldUiTest.php b/core/modules/field_ui/tests/src/Unit/FieldUiTest.php
new file mode 100644
index 000000000..fc055bf12
--- /dev/null
+++ b/core/modules/field_ui/tests/src/Unit/FieldUiTest.php
@@ -0,0 +1,77 @@
+pathValidator = $this->getMock('Drupal\Core\Path\PathValidatorInterface');
+ $container = new ContainerBuilder();
+ $container->set('path.validator', $this->pathValidator);
+ \Drupal::setContainer($container);
+ }
+
+ /**
+ * @covers ::getNextDestination
+ */
+ public function testGetNextDestination() {
+ $destinations = ['admin', 'admin/content'];
+ $expected_uri = 'base:admin';
+ $expected_query = [
+ 'destinations' => ['admin/content'],
+ ];
+ $actual = FieldUI::getNextDestination($destinations);
+ $this->assertSame($expected_uri, $actual->getUri());
+ $this->assertSame($expected_query, $actual->getOption('query'));
+ }
+
+ /**
+ * @covers ::getNextDestination
+ */
+ public function testGetNextDestinationEmpty() {
+ $destinations = [];
+ $actual = FieldUI::getNextDestination($destinations);
+ $this->assertNull($actual);
+ }
+
+ /**
+ * @covers ::getNextDestination
+ */
+ public function testGetNextDestinationRouteName() {
+ $destinations = [['route_name' => 'system.admin'], ['route_name' => 'system.admin_content']];
+ $expected_route_name = 'system.admin';
+ $expected_query = [
+ 'destinations' => [['route_name' => 'system.admin_content']],
+ ];
+ $actual = FieldUI::getNextDestination($destinations);
+ $this->assertSame($expected_route_name, $actual->getRouteName());
+ $this->assertSame($expected_query, $actual->getOption('query'));
+ }
+
+}
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 01b4ddb71..a99591678 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -6,8 +6,10 @@
*/
use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
@@ -631,6 +633,7 @@ function file_file_download($uri) {
*/
function file_cron() {
$age = \Drupal::config('system.file')->get('temporary_maximum_age');
+ $file_storage = \Drupal::entityManager()->getStorage('file');
// Only delete temporary files if older than $age. Note that automatic cleanup
// is disabled if $age set to 0.
@@ -640,7 +643,7 @@ function file_cron() {
->condition('changed', REQUEST_TIME - $age, '<')
->range(0, 100)
->execute();
- $files = file_load_multiple($fids);
+ $files = $file_storage->loadMultiple($fids);
foreach ($files as $file) {
$references = \Drupal::service('file.usage')->listUsage($file);
if (empty($references)) {
@@ -937,7 +940,7 @@ function file_file_predelete(File $file) {
/**
* Implements hook_tokens().
*/
-function file_tokens($type, $tokens, array $data = array(), array $options = array()) {
+function file_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
$token_service = \Drupal::token();
$url_options = array('absolute' => TRUE);
@@ -986,30 +989,36 @@ function file_tokens($type, $tokens, array $data = array(), array $options = arr
// These tokens are default variations on the chained tokens handled below.
case 'created':
+ $date_format = DateFormat::load('medium');
+ $bubbleable_metadata->addCacheableDependency($date_format);
$replacements[$original] = format_date($file->getCreatedTime(), 'medium', '', NULL, $langcode);
break;
case 'changed':
+ $date_format = DateFormat::load('medium');
+ $bubbleable_metadata = $bubbleable_metadata->addCacheableDependency($date_format);
$replacements[$original] = format_date($file->getChangedTime(), 'medium', '', NULL, $langcode);
break;
case 'owner':
- $name = $file->getOwner()->label();
+ $owner = $file->getOwner();
+ $bubbleable_metadata->addCacheableDependency($owner);
+ $name = $owner->label();
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($name) : $name;
break;
}
}
if ($date_tokens = $token_service->findWithPrefix($tokens, 'created')) {
- $replacements += $token_service->generate('date', $date_tokens, array('date' => $file->getCreatedTime()), $options);
+ $replacements += $token_service->generate('date', $date_tokens, array('date' => $file->getCreatedTime()), $options, $bubbleable_metadata);
}
if ($date_tokens = $token_service->findWithPrefix($tokens, 'changed')) {
- $replacements += $token_service->generate('date', $date_tokens, array('date' => $file->getChangedTime()), $options);
+ $replacements += $token_service->generate('date', $date_tokens, array('date' => $file->getChangedTime()), $options, $bubbleable_metadata);
}
if (($owner_tokens = $token_service->findWithPrefix($tokens, 'owner')) && $file->getOwner()) {
- $replacements += $token_service->generate('user', $owner_tokens, array('user' => $file->getOwner()), $options);
+ $replacements += $token_service->generate('user', $owner_tokens, array('user' => $file->getOwner()), $options, $bubbleable_metadata);
}
}
@@ -1225,7 +1234,7 @@ function template_preprocess_file_link(&$variables) {
$file = $variables['file'];
$options = array();
- $file_entity = ($file instanceof File) ? $file : file_load($file->fid);
+ $file_entity = ($file instanceof File) ? $file : File::load($file->fid);
$url = file_create_url($file_entity->getFileUri());
$mime_type = $file->getMimeType();
diff --git a/core/modules/file/src/Controller/FileWidgetAjaxController.php b/core/modules/file/src/Controller/FileWidgetAjaxController.php
index 1c1b7e3a7..d05a4dd7b 100644
--- a/core/modules/file/src/Controller/FileWidgetAjaxController.php
+++ b/core/modules/file/src/Controller/FileWidgetAjaxController.php
@@ -7,13 +7,12 @@
namespace Drupal\file\Controller;
-use Drupal\system\Controller\FormAjaxController;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Defines a controller to respond to file widget AJAX requests.
*/
-class FileWidgetAjaxController extends FormAjaxController {
+class FileWidgetAjaxController {
/**
* Returns the progress status for a file upload process.
diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileFieldItemList.php b/core/modules/file/src/Plugin/Field/FieldType/FileFieldItemList.php
index f8ee03c84..90dcaa913 100644
--- a/core/modules/file/src/Plugin/Field/FieldType/FileFieldItemList.php
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileFieldItemList.php
@@ -23,59 +23,54 @@ class FileFieldItemList extends EntityReferenceFieldItemList {
/**
* {@inheritdoc}
*/
- public function insert() {
- parent::insert();
+ public function postSave($update) {
$entity = $this->getEntity();
- // Add a new usage for newly uploaded files.
- foreach ($this->referencedEntities() as $file) {
- \Drupal::service('file.usage')->add($file, 'file', $entity->getEntityTypeId(), $entity->id());
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function update() {
- parent::update();
- $entity = $this->getEntity();
-
- // Get current target file entities and file IDs.
- $files = $this->referencedEntities();
- $fids = array();
-
- foreach ($files as $file) {
- $fids[] = $file->id();
- }
-
- // On new revisions, all files are considered to be a new usage and no
- // deletion of previous file usages are necessary.
- if (!empty($entity->original) && $entity->getRevisionId() != $entity->original->getRevisionId()) {
- foreach ($files as $file) {
+ if (!$update) {
+ // Add a new usage for newly uploaded files.
+ foreach ($this->referencedEntities() as $file) {
\Drupal::service('file.usage')->add($file, 'file', $entity->getEntityTypeId(), $entity->id());
}
- return;
}
+ else {
+ // Get current target file entities and file IDs.
+ $files = $this->referencedEntities();
+ $ids = array();
- // Get the file IDs attached to the field before this update.
- $field_name = $this->getFieldDefinition()->getName();
- $original_fids = array();
- $original_items = $entity->original->getTranslation($this->getLangcode())->$field_name;
- foreach ($original_items as $item) {
- $original_fids[] = $item->target_id;
- }
+ /** @var \Drupal\file\FileInterface $file */
+ foreach ($files as $file) {
+ $ids[] = $file->id();
+ }
- // Decrement file usage by 1 for files that were removed from the field.
- $removed_fids = array_filter(array_diff($original_fids, $fids));
- $removed_files = \Drupal::entityManager()->getStorage('file')->loadMultiple($removed_fids);
- foreach ($removed_files as $file) {
- \Drupal::service('file.usage')->delete($file, 'file', $entity->getEntityTypeId(), $entity->id());
- }
+ // On new revisions, all files are considered to be a new usage and no
+ // deletion of previous file usages are necessary.
+ if (!empty($entity->original) && $entity->getRevisionId() != $entity->original->getRevisionId()) {
+ foreach ($files as $file) {
+ \Drupal::service('file.usage')->add($file, 'file', $entity->getEntityTypeId(), $entity->id());
+ }
+ return;
+ }
- // Add new usage entries for newly added files.
- foreach ($files as $file) {
- if (!in_array($file->id(), $original_fids)) {
- \Drupal::service('file.usage')->add($file, 'file', $entity->getEntityTypeId(), $entity->id());
+ // Get the file IDs attached to the field before this update.
+ $field_name = $this->getFieldDefinition()->getName();
+ $original_ids = array();
+ $original_items = $entity->original->getTranslation($this->getLangcode())->$field_name;
+ foreach ($original_items as $item) {
+ $original_ids[] = $item->target_id;
+ }
+
+ // Decrement file usage by 1 for files that were removed from the field.
+ $removed_ids = array_filter(array_diff($original_ids, $ids));
+ $removed_files = \Drupal::entityManager()->getStorage('file')->loadMultiple($removed_ids);
+ foreach ($removed_files as $file) {
+ \Drupal::service('file.usage')->delete($file, 'file', $entity->getEntityTypeId(), $entity->id());
+ }
+
+ // Add new usage entries for newly added files.
+ foreach ($files as $file) {
+ if (!in_array($file->id(), $original_ids)) {
+ \Drupal::service('file.usage')->add($file, 'file', $entity->getEntityTypeId(), $entity->id());
+ }
}
}
}
diff --git a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
index fbc30d89e..a2d1fefc6 100644
--- a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
+++ b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
@@ -20,6 +20,7 @@ use Drupal\Core\Render\ElementInfoManagerInterface;
use Drupal\Core\Url;
use Drupal\file\Element\ManagedFile;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\file\Entity\File;
/**
* Plugin implementation of the 'file_generic' widget.
@@ -346,10 +347,10 @@ class FileWidget extends WidgetBase implements ContainerFactoryPluginInterface {
$removed_files = array_slice($values['fids'], $keep);
$removed_names = array();
foreach ($removed_files as $fid) {
- $file = file_load($fid);
+ $file = File::load($fid);
$removed_names[] = $file->getFilename();
}
- $args = array('%field' => $field_storage->getFieldName(), '@max' => $field_storage->getCardinality(), '@count' => $keep, '%list' => implode(', ', $removed_names));
+ $args = array('%field' => $field_storage->getName(), '@max' => $field_storage->getCardinality(), '@count' => $uploaded, '%list' => implode(', ', $removed_names));
$message = t('Field %field can only hold @max values but there were @count uploaded. The following files have been omitted as a result: %list.', $args);
drupal_set_message($message, 'warning');
$values['fids'] = array_slice($values['fids'], 0, $keep);
diff --git a/core/modules/file/src/Tests/CopyTest.php b/core/modules/file/src/Tests/CopyTest.php
index 4b9444901..68c589bef 100644
--- a/core/modules/file/src/Tests/CopyTest.php
+++ b/core/modules/file/src/Tests/CopyTest.php
@@ -7,6 +7,8 @@
namespace Drupal\file\Tests;
+use Drupal\file\Entity\File;
+
/**
* Tests the file copy function.
*
@@ -39,7 +41,7 @@ class CopyTest extends FileManagedUnitTestBase {
// Reload the file from the database and check that the changes were
// actually saved.
- $this->assertFileUnchanged($result, file_load($result->id(), TRUE));
+ $this->assertFileUnchanged($result, File::load($result->id()));
}
/**
@@ -66,9 +68,9 @@ class CopyTest extends FileManagedUnitTestBase {
// Load all the affected files to check the changes that actually made it
// to the database.
- $loaded_source = file_load($source->id(), TRUE);
- $loaded_target = file_load($target->id(), TRUE);
- $loaded_result = file_load($result->id(), TRUE);
+ $loaded_source = File::load($source->id());
+ $loaded_target = File::load($target->id());
+ $loaded_result = File::load($result->id());
// Verify that the source file wasn't changed.
$this->assertFileUnchanged($source, $loaded_source);
@@ -106,9 +108,9 @@ class CopyTest extends FileManagedUnitTestBase {
// Load all the affected files to check the changes that actually made it
// to the database.
- $loaded_source = file_load($source->id(), TRUE);
- $loaded_target = file_load($target->id(), TRUE);
- $loaded_result = file_load($result->id(), TRUE);
+ $loaded_source = File::load($source->id());
+ $loaded_target = File::load($target->id());
+ $loaded_result = File::load($result->id());
// Verify that the source file wasn't changed.
$this->assertFileUnchanged($source, $loaded_source);
@@ -141,7 +143,7 @@ class CopyTest extends FileManagedUnitTestBase {
// Check that the correct hooks were called.
$this->assertFileHooksCalled(array());
- $this->assertFileUnchanged($source, file_load($source->id(), TRUE));
- $this->assertFileUnchanged($target, file_load($target->id(), TRUE));
+ $this->assertFileUnchanged($source, File::load($source->id()));
+ $this->assertFileUnchanged($target, File::load($target->id()));
}
}
diff --git a/core/modules/file/src/Tests/DeleteTest.php b/core/modules/file/src/Tests/DeleteTest.php
index f89dfbff8..1ec037669 100644
--- a/core/modules/file/src/Tests/DeleteTest.php
+++ b/core/modules/file/src/Tests/DeleteTest.php
@@ -7,6 +7,8 @@
namespace Drupal\file\Tests;
+use Drupal\file\Entity\File;
+
/**
* Tests the file delete function.
*
@@ -24,7 +26,7 @@ class DeleteTest extends FileManagedUnitTestBase {
$file->delete();
$this->assertFileHooksCalled(array('delete'));
$this->assertFalse(file_exists($file->getFileUri()), 'Test file has actually been deleted.');
- $this->assertFalse(file_load($file->id()), 'File was removed from the database.');
+ $this->assertFalse(File::load($file->id()), 'File was removed from the database.');
}
/**
@@ -40,7 +42,7 @@ class DeleteTest extends FileManagedUnitTestBase {
$usage = $file_usage->listUsage($file);
$this->assertEqual($usage['testing']['test'], array(1 => 1), 'Test file is still in use.');
$this->assertTrue(file_exists($file->getFileUri()), 'File still exists on the disk.');
- $this->assertTrue(file_load($file->id()), 'File still exists in the database.');
+ $this->assertTrue(File::load($file->id()), 'File still exists in the database.');
// Clear out the call to hook_file_load().
file_test_reset();
@@ -50,7 +52,7 @@ class DeleteTest extends FileManagedUnitTestBase {
$this->assertFileHooksCalled(array('load', 'update'));
$this->assertTrue(empty($usage), 'File usage data was removed.');
$this->assertTrue(file_exists($file->getFileUri()), 'File still exists on the disk.');
- $file = file_load($file->id());
+ $file = File::load($file->id());
$this->assertTrue($file, 'File still exists in the database.');
$this->assertTrue($file->isTemporary(), 'File is temporary.');
file_test_reset();
@@ -69,6 +71,6 @@ class DeleteTest extends FileManagedUnitTestBase {
// file_cron() loads
$this->assertFileHooksCalled(array('delete'));
$this->assertFalse(file_exists($file->getFileUri()), 'File has been deleted after its last usage was removed.');
- $this->assertFalse(file_load($file->id()), 'File was removed from the database.');
+ $this->assertFalse(File::load($file->id()), 'File was removed from the database.');
}
}
diff --git a/core/modules/file/src/Tests/DownloadTest.php b/core/modules/file/src/Tests/DownloadTest.php
index 25aab9b68..d2df0d266 100644
--- a/core/modules/file/src/Tests/DownloadTest.php
+++ b/core/modules/file/src/Tests/DownloadTest.php
@@ -30,7 +30,7 @@ class DownloadTest extends FileManagedTestBase {
$url = file_create_url($file->getFileUri());
// URLs can't contain characters outside the ASCII set so $filename has to be
// encoded.
- $filename = $GLOBALS['base_url'] . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . rawurlencode($file->getFilename());
+ $filename = $GLOBALS['base_url'] . '/' . \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath() . '/' . rawurlencode($file->getFilename());
$this->assertEqual($filename, $url, 'Correctly generated a URL for a created file.');
$this->drupalHead($url);
$this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the created file.');
@@ -117,7 +117,7 @@ class DownloadTest extends FileManagedTestBase {
$clean_urls = $clean_url_setting == 'clean';
$request = $this->prepareRequestForGenerator($clean_urls);
$base_path = $request->getSchemeAndHttpHost() . $request->getBasePath();
- $this->checkUrl('public', '', $basename, $base_path . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded);
+ $this->checkUrl('public', '', $basename, $base_path . '/' . \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath() . '/' . $basename_encoded);
$this->checkUrl('private', '', $basename, $base_path . '/' . $script_path . 'system/files/' . $basename_encoded);
}
$this->assertEqual(file_create_url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==', t('Generated URL matches expected URL.'));
diff --git a/core/modules/file/src/Tests/FileFieldDisplayTest.php b/core/modules/file/src/Tests/FileFieldDisplayTest.php
index f4db76525..5cbb9707d 100644
--- a/core/modules/file/src/Tests/FileFieldDisplayTest.php
+++ b/core/modules/file/src/Tests/FileFieldDisplayTest.php
@@ -8,6 +8,7 @@
namespace Drupal\file\Tests;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\file\Entity\File;
/**
* Tests the display of file fields in node and views.
@@ -57,7 +58,7 @@ class FileFieldDisplayTest extends FileFieldTestBase {
$node_storage = $this->container->get('entity.manager')->getStorage('node');
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$file_link = array(
'#theme' => 'file_link',
'#file' => $node_file,
diff --git a/core/modules/file/src/Tests/FileFieldPathTest.php b/core/modules/file/src/Tests/FileFieldPathTest.php
index d7e034607..346fc7a27 100644
--- a/core/modules/file/src/Tests/FileFieldPathTest.php
+++ b/core/modules/file/src/Tests/FileFieldPathTest.php
@@ -7,6 +7,8 @@
namespace Drupal\file\Tests;
+use Drupal\file\Entity\File;
+
/**
* Tests that files are uploaded to proper locations.
*
@@ -29,7 +31,7 @@ class FileFieldPathTest extends FileFieldTestBase {
// Check that the file was uploaded to the file root.
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertPathMatch('public://' . $test_file->getFilename(), $node_file->getFileUri(), format_string('The file %file was uploaded to the correct path.', array('%file' => $node_file->getFileUri())));
// Change the path to contain multiple subdirectories.
@@ -41,7 +43,7 @@ class FileFieldPathTest extends FileFieldTestBase {
// Check that the file was uploaded into the subdirectory.
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id, TRUE);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertPathMatch('public://foo/bar/baz/' . $test_file->getFilename(), $node_file->getFileUri(), format_string('The file %file was uploaded to the correct path.', array('%file' => $node_file->getFileUri())));
// Check the path when used with tokens.
@@ -54,7 +56,7 @@ class FileFieldPathTest extends FileFieldTestBase {
// Check that the file was uploaded into the subdirectory.
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
// Do token replacement using the same user which uploaded the file, not
// the user running the test case.
$data = array('user' => $this->adminUser);
diff --git a/core/modules/file/src/Tests/FileFieldRSSContentTest.php b/core/modules/file/src/Tests/FileFieldRSSContentTest.php
index 7455301c6..5314a7bbb 100644
--- a/core/modules/file/src/Tests/FileFieldRSSContentTest.php
+++ b/core/modules/file/src/Tests/FileFieldRSSContentTest.php
@@ -8,6 +8,7 @@
namespace Drupal\file\Tests;
use Drupal\node\Entity\Node;
+use Drupal\file\Entity\File;
/**
* Ensure that files added to nodes appear correctly in RSS feeds.
@@ -63,7 +64,7 @@ class FileFieldRSSContentTest extends FileFieldTestBase {
// Get the uploaded file from the node.
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
// Check that the RSS enclosure appears in the RSS feed.
$this->drupalGet('rss.xml');
diff --git a/core/modules/file/src/Tests/FileFieldRevisionTest.php b/core/modules/file/src/Tests/FileFieldRevisionTest.php
index bb3cf8a50..7da2a9072 100644
--- a/core/modules/file/src/Tests/FileFieldRevisionTest.php
+++ b/core/modules/file/src/Tests/FileFieldRevisionTest.php
@@ -7,6 +7,8 @@
namespace Drupal\file\Tests;
+use Drupal\file\Entity\File;
+
/**
* Tests creating and deleting revisions with files attached.
*
@@ -40,7 +42,7 @@ class FileFieldRevisionTest extends FileFieldTestBase {
// Check that the file exists on disk and in the database.
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file_r1 = file_load($node->{$field_name}->target_id);
+ $node_file_r1 = File::load($node->{$field_name}->target_id);
$node_vid_r1 = $node->getRevisionId();
$this->assertFileExists($node_file_r1, 'New file saved to disk on node creation.');
$this->assertFileEntryExists($node_file_r1, 'File entry exists in database on node creation.');
@@ -50,7 +52,7 @@ class FileFieldRevisionTest extends FileFieldTestBase {
$this->replaceNodeFile($test_file, $field_name, $nid);
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file_r2 = file_load($node->{$field_name}->target_id);
+ $node_file_r2 = File::load($node->{$field_name}->target_id);
$node_vid_r2 = $node->getRevisionId();
$this->assertFileExists($node_file_r2, 'Replacement file exists on disk after creating new revision.');
$this->assertFileEntryExists($node_file_r2, 'Replacement file entry exists in database after creating new revision.');
@@ -58,7 +60,7 @@ class FileFieldRevisionTest extends FileFieldTestBase {
// Check that the original file is still in place on the first revision.
$node = node_revision_load($node_vid_r1);
- $current_file = file_load($node->{$field_name}->target_id);
+ $current_file = File::load($node->{$field_name}->target_id);
$this->assertEqual($node_file_r1->id(), $current_file->id(), 'Original file still in place after replacing file in new revision.');
$this->assertFileExists($node_file_r1, 'Original file still in place after replacing file in new revision.');
$this->assertFileEntryExists($node_file_r1, 'Original file entry still in place after replacing file in new revision');
@@ -69,7 +71,7 @@ class FileFieldRevisionTest extends FileFieldTestBase {
$this->drupalPostForm('node/' . $nid . '/edit', array('revision' => '1'), t('Save and keep published'));
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file_r3 = file_load($node->{$field_name}->target_id);
+ $node_file_r3 = File::load($node->{$field_name}->target_id);
$node_vid_r3 = $node->getRevisionId();
$this->assertEqual($node_file_r2->id(), $node_file_r3->id(), 'Previous revision file still in place after creating a new revision without a new file.');
$this->assertFileIsPermanent($node_file_r3, 'New revision file is permanent.');
@@ -78,7 +80,7 @@ class FileFieldRevisionTest extends FileFieldTestBase {
$this->drupalPostForm('node/' . $nid . '/revisions/' . $node_vid_r1 . '/revert', array(), t('Revert'));
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file_r4 = file_load($node->{$field_name}->target_id);
+ $node_file_r4 = File::load($node->{$field_name}->target_id);
$this->assertEqual($node_file_r1->id(), $node_file_r4->id(), 'Original revision file still in place after reverting to the original revision.');
$this->assertFileIsPermanent($node_file_r4, 'Original revision file still permanent after reverting to the original revision.');
diff --git a/core/modules/file/src/Tests/FileFieldTestBase.php b/core/modules/file/src/Tests/FileFieldTestBase.php
index 4f8d9c8d8..5874cf5db 100644
--- a/core/modules/file/src/Tests/FileFieldTestBase.php
+++ b/core/modules/file/src/Tests/FileFieldTestBase.php
@@ -11,6 +11,7 @@ use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
use Drupal\file\FileInterface;
use Drupal\simpletest\WebTestBase;
+use Drupal\file\Entity\File;
/**
* Provides methods specifically for testing File module's field handling.
@@ -45,7 +46,8 @@ abstract class FileFieldTestBase extends WebTestBase {
// Get a file to upload.
$file = current($this->drupalGetTestFiles($type_name, $size));
- // Add a filesize property to files as would be read by file_load().
+ // Add a filesize property to files as would be read by
+ // \Drupal\file\Entity\File::load().
$file->filesize = filesize($file->uri);
return entity_create('file', (array) $file);
@@ -145,18 +147,57 @@ abstract class FileFieldTestBase extends WebTestBase {
/**
* Uploads a file to a node.
+ *
+ * @param \Drupal\file\FileInterface $file
+ * The File to be uploaded.
+ * @param string $field_name
+ * The name of the field on which the files should be saved.
+ * @param $nid_or_type
+ * A numeric node id to upload files to an existing node, or a string
+ * indicating the desired bundle for a new node.
+ * @param bool $new_revision
+ * The revision number.
+ * @param array $extras
+ * Additional values when a new node is created.
+ *
+ * @return int
+ * The node id.
*/
- function uploadNodeFile($file, $field_name, $nid_or_type, $new_revision = TRUE, $extras = array()) {
+ function uploadNodeFile(FileInterface $file, $field_name, $nid_or_type, $new_revision = TRUE, array $extras = array()) {
+ return $this->uploadNodeFiles([$file], $field_name, $nid_or_type, $new_revision, $extras);
+ }
+
+ /**
+ * Uploads multiple files to a node.
+ *
+ * @param \Drupal\file\FileInterface[] $files
+ * The files to be uploaded.
+ * @param string $field_name
+ * The name of the field on which the files should be saved.
+ * @param $nid_or_type
+ * A numeric node id to upload files to an existing node, or a string
+ * indicating the desired bundle for a new node.
+ * @param bool $new_revision
+ * The revision number.
+ * @param array $extras
+ * Additional values when a new node is created.
+ *
+ * @return int
+ * The node id.
+ */
+ function uploadNodeFiles(array $files, $field_name, $nid_or_type, $new_revision = TRUE, array $extras = array()) {
$edit = array(
'title[0][value]' => $this->randomMachineName(),
'revision' => (string) (int) $new_revision,
);
+ $node_storage = $this->container->get('entity.manager')->getStorage('node');
if (is_numeric($nid_or_type)) {
$nid = $nid_or_type;
+ $node_storage->resetCache(array($nid));
+ $node = $node_storage->load($nid);
}
else {
- $node_storage = $this->container->get('entity.manager')->getStorage('node');
// Add a new node.
$extras['type'] = $nid_or_type;
$node = $this->drupalCreateNode($extras);
@@ -169,13 +210,23 @@ abstract class FileFieldTestBase extends WebTestBase {
$this->assertNotEqual($nid, $node->getRevisionId(), 'Node revision exists.');
}
- // Attach a file to the node.
+ // Attach files to the node.
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
- $name = 'files[' . $field_name . '_0]';
+ // File input name depends on number of files already uploaded.
+ $field_num = count($node->{$field_name});
+ $name = 'files[' . $field_name . "_$field_num]";
if ($field_storage->getCardinality() != 1) {
$name .= '[]';
}
- $edit[$name] = drupal_realpath($file->getFileUri());
+ foreach ($files as $file) {
+ $file_path = $this->container->get('file_system')->realpath($file->getFileUri());
+ if (count($files) == 1) {
+ $edit[$name] = $file_path;
+ }
+ else {
+ $edit[$name][] = $file_path;
+ }
+ }
$this->drupalPostForm("node/$nid/edit", $edit, t('Save and keep published'));
return $nid;
@@ -221,7 +272,7 @@ abstract class FileFieldTestBase extends WebTestBase {
*/
function assertFileEntryExists($file, $message = NULL) {
$this->container->get('entity.manager')->getStorage('file')->resetCache();
- $db_file = file_load($file->id());
+ $db_file = File::load($file->id());
$message = isset($message) ? $message : format_string('File %file exists in database at the correct path.', array('%file' => $file->getFileUri()));
$this->assertEqual($db_file->getFileUri(), $file->getFileUri(), $message);
}
@@ -240,7 +291,7 @@ abstract class FileFieldTestBase extends WebTestBase {
function assertFileEntryNotExists($file, $message) {
$this->container->get('entity.manager')->getStorage('file')->resetCache();
$message = isset($message) ? $message : format_string('File %file exists in database at the correct path.', array('%file' => $file->getFileUri()));
- $this->assertFalse(file_load($file->id()), $message);
+ $this->assertFalse(File::load($file->id()), $message);
}
/**
diff --git a/core/modules/file/src/Tests/FileFieldValidateTest.php b/core/modules/file/src/Tests/FileFieldValidateTest.php
index d98f6cd6c..46e34ee27 100644
--- a/core/modules/file/src/Tests/FileFieldValidateTest.php
+++ b/core/modules/file/src/Tests/FileFieldValidateTest.php
@@ -9,6 +9,7 @@ namespace Drupal\file\Tests;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
+use Drupal\file\Entity\File;
/**
* Tests validation functions such as file type, max file size, max size per
@@ -44,7 +45,7 @@ class FileFieldValidateTest extends FileFieldTestBase {
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertFileExists($node_file, 'File exists after uploading to the required field.');
$this->assertFileEntryExists($node_file, 'File entry exists after uploading to the required field.');
@@ -63,7 +64,7 @@ class FileFieldValidateTest extends FileFieldTestBase {
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertFileExists($node_file, 'File exists after uploading to the required multiple value field.');
$this->assertFileEntryExists($node_file, 'File entry exists after uploading to the required multiple value field.');
}
@@ -95,7 +96,7 @@ class FileFieldValidateTest extends FileFieldTestBase {
$nid = $this->uploadNodeFile($small_file, $field_name, $type_name);
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertFileExists($node_file, format_string('File exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->getSize()), '%maxsize' => $max_filesize)));
$this->assertFileEntryExists($node_file, format_string('File entry exists after uploading a file (%filesize) under the max limit (%maxsize).', array('%filesize' => format_size($small_file->getSize()), '%maxsize' => $max_filesize)));
@@ -112,7 +113,7 @@ class FileFieldValidateTest extends FileFieldTestBase {
$nid = $this->uploadNodeFile($large_file, $field_name, $type_name);
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertFileExists($node_file, format_string('File exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->getSize()))));
$this->assertFileEntryExists($node_file, format_string('File entry exists after uploading a file (%filesize) with no max limit.', array('%filesize' => format_size($large_file->getSize()))));
}
@@ -136,7 +137,7 @@ class FileFieldValidateTest extends FileFieldTestBase {
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertFileExists($node_file, 'File exists after uploading a file with no extension checking.');
$this->assertFileEntryExists($node_file, 'File entry exists after uploading a file with no extension checking.');
@@ -155,7 +156,7 @@ class FileFieldValidateTest extends FileFieldTestBase {
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertFileExists($node_file, 'File exists after uploading a file with extension checking.');
$this->assertFileEntryExists($node_file, 'File entry exists after uploading a file with extension checking.');
}
diff --git a/core/modules/file/src/Tests/FileFieldWidgetTest.php b/core/modules/file/src/Tests/FileFieldWidgetTest.php
index c03e118cc..7c24aabff 100644
--- a/core/modules/file/src/Tests/FileFieldWidgetTest.php
+++ b/core/modules/file/src/Tests/FileFieldWidgetTest.php
@@ -12,6 +12,7 @@ use Drupal\comment\Tests\CommentTestTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\field_ui\Tests\FieldUiTestTrait;
use Drupal\user\RoleInterface;
+use Drupal\file\Entity\File;
/**
* Tests the file field widget, single and multi-valued, with and without AJAX,
@@ -58,7 +59,7 @@ class FileFieldWidgetTest extends FileFieldTestBase {
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertFileExists($node_file, 'New file saved to disk on node creation.');
// Ensure the file can be downloaded.
@@ -111,8 +112,9 @@ class FileFieldWidgetTest extends FileFieldTestBase {
// names).
$field_name = 'test_file_field_1';
$field_name2 = 'test_file_field_2';
- $this->createFileField($field_name, 'node', $type_name, array('cardinality' => 3));
- $this->createFileField($field_name2, 'node', $type_name, array('cardinality' => 3));
+ $cardinality = 3;
+ $this->createFileField($field_name, 'node', $type_name, array('cardinality' => $cardinality));
+ $this->createFileField($field_name2, 'node', $type_name, array('cardinality' => $cardinality));
$test_file = $this->getTestFile('text');
@@ -214,6 +216,49 @@ class FileFieldWidgetTest extends FileFieldTestBase {
$node = $node_storage->load($nid);
$this->assertTrue(empty($node->{$field_name}->target_id), 'Node was successfully saved without any files.');
}
+
+ $upload_files = array($test_file, $test_file);
+ // Try to upload multiple files, but fewer than the maximum.
+ $nid = $this->uploadNodeFiles($upload_files, $field_name, $type_name);
+ $node_storage->resetCache(array($nid));
+ $node = $node_storage->load($nid);
+ $this->assertEqual(count($node->{$field_name}), count($upload_files), 'Node was successfully saved with mulitple files.');
+
+ // Try to upload more files than allowed on revision.
+ $this->uploadNodeFiles($upload_files, $field_name, $nid, 1);
+ $args = array(
+ '%field' => $field_name,
+ '@count' => $cardinality
+ );
+ $this->assertRaw(t('%field: this field cannot hold more than @count values.', $args));
+ $node_storage->resetCache(array($nid));
+ $node = $node_storage->load($nid);
+ $this->assertEqual(count($node->{$field_name}), count($upload_files), 'More files than allowed could not be saved to node.');
+
+ // Try to upload exactly the allowed number of files on revision.
+ $this->uploadNodeFile($test_file, $field_name, $nid, 1);
+ $node_storage->resetCache(array($nid));
+ $node = $node_storage->load($nid);
+ $this->assertEqual(count($node->{$field_name}), $cardinality, 'Node was successfully revised to maximum number of files.');
+
+ // Try to upload exactly the allowed number of files, new node.
+ $upload_files[] = $test_file;
+ $nid = $this->uploadNodeFiles($upload_files, $field_name, $type_name);
+ $node_storage->resetCache(array($nid));
+ $node = $node_storage->load($nid);
+ $this->assertEqual(count($node->{$field_name}), $cardinality, 'Node was successfully saved with maximum number of files.');
+
+ // Try to upload more files than allowed, new node.
+ $upload_files[] = $test_file;
+ $this->uploadNodeFiles($upload_files, $field_name, $type_name);
+
+ $args = [
+ '%field' => $field_name,
+ '@max' => $cardinality,
+ '@count' => count($upload_files),
+ '%list' => $test_file->getFileName(),
+ ];
+ $this->assertRaw(t('Field %field can only hold @max values but there were @count uploaded. The following files have been omitted as a result: %list.', $args));
}
/**
@@ -238,7 +283,7 @@ class FileFieldWidgetTest extends FileFieldTestBase {
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $node_file = file_load($node->{$field_name}->target_id);
+ $node_file = File::load($node->{$field_name}->target_id);
$this->assertFileExists($node_file, 'New file saved to disk on node creation.');
// Ensure the private file is available to the user who uploaded it.
diff --git a/core/modules/file/src/Tests/FileListingTest.php b/core/modules/file/src/Tests/FileListingTest.php
index b752c3ffd..72b2378fc 100644
--- a/core/modules/file/src/Tests/FileListingTest.php
+++ b/core/modules/file/src/Tests/FileListingTest.php
@@ -8,6 +8,7 @@
namespace Drupal\file\Tests;
use Drupal\node\Entity\Node;
+use Drupal\file\Entity\File;
/**
* Tests file listing page functionality.
@@ -103,7 +104,7 @@ class FileListingTest extends FileFieldTestBase {
$this->drupalGet('admin/content/files');
foreach ($nodes as $node) {
- $file = entity_load('file', $node->file->target_id);
+ $file = File::load($node->file->target_id);
$this->assertText($file->getFilename());
$this->assertLinkByHref(file_create_url($file->getFileUri()));
$this->assertLinkByHref('admin/content/files/usage/' . $file->id());
@@ -117,11 +118,11 @@ class FileListingTest extends FileFieldTestBase {
$nodes[1]->save();
$this->drupalGet('admin/content/files');
- $file = entity_load('file', $orphaned_file);
+ $file = File::load($orphaned_file);
$usage = $this->sumUsages($file_usage->listUsage($file));
$this->assertRaw('admin/content/files/usage/' . $file->id() . '">' . $usage);
- $file = entity_load('file', $used_file);
+ $file = File::load($used_file);
$usage = $this->sumUsages($file_usage->listUsage($file));
$this->assertRaw('admin/content/files/usage/' . $file->id() . '">' . $usage);
@@ -130,7 +131,7 @@ class FileListingTest extends FileFieldTestBase {
// Test file usage page.
foreach ($nodes as $node) {
- $file = entity_load('file', $node->file->target_id);
+ $file = File::load($node->file->target_id);
$usage = $file_usage->listUsage($file);
$this->drupalGet('admin/content/files/usage/' . $file->id());
$this->assertResponse(200);
diff --git a/core/modules/file/src/Tests/FileTokenReplaceTest.php b/core/modules/file/src/Tests/FileTokenReplaceTest.php
index 1f0d733f6..2d289c4f3 100644
--- a/core/modules/file/src/Tests/FileTokenReplaceTest.php
+++ b/core/modules/file/src/Tests/FileTokenReplaceTest.php
@@ -8,6 +8,8 @@
namespace Drupal\file\Tests;
use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\file\Entity\File;
/**
* Generates text using placeholders for dummy content to check file token
@@ -40,7 +42,7 @@ class FileTokenReplaceTest extends FileFieldTestBase {
// Load the node and the file.
$node_storage->resetCache(array($nid));
$node = $node_storage->load($nid);
- $file = file_load($node->{$field_name}->target_id);
+ $file = File::load($node->{$field_name}->target_id);
// Generate and test sanitized tokens.
$tests = array();
@@ -57,12 +59,31 @@ class FileTokenReplaceTest extends FileFieldTestBase {
$tests['[file:owner]'] = SafeMarkup::checkPlain(user_format_name($this->adminUser));
$tests['[file:owner:uid]'] = $file->getOwnerId();
+ $base_bubbleable_metadata = BubbleableMetadata::createFromObject($file);
+ $metadata_tests = [];
+ $metadata_tests['[file:fid]'] = $base_bubbleable_metadata;
+ $metadata_tests['[file:name]'] = $base_bubbleable_metadata;
+ $metadata_tests['[file:path]'] = $base_bubbleable_metadata;
+ $metadata_tests['[file:mime]'] = $base_bubbleable_metadata;
+ $metadata_tests['[file:size]'] = $base_bubbleable_metadata;
+ $metadata_tests['[file:url]'] = $base_bubbleable_metadata;
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $metadata_tests['[file:created]'] = $bubbleable_metadata->addCacheTags(['rendered']);
+ $metadata_tests['[file:created:short]'] = $bubbleable_metadata;
+ $metadata_tests['[file:changed]'] = $bubbleable_metadata;
+ $metadata_tests['[file:changed:short]'] = $bubbleable_metadata;
+ $bubbleable_metadata = clone $base_bubbleable_metadata;
+ $metadata_tests['[file:owner]'] = $bubbleable_metadata->addCacheTags(['user:2']);
+ $metadata_tests['[file:owner:uid]'] = $bubbleable_metadata;
+
// Test to make sure that we generated something for each token.
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
foreach ($tests as $input => $expected) {
- $output = $token_service->replace($input, array('file' => $file), array('langcode' => $language_interface->getId()));
+ $bubbleable_metadata = new BubbleableMetadata();
+ $output = $token_service->replace($input, array('file' => $file), array('langcode' => $language_interface->getId()), $bubbleable_metadata);
$this->assertEqual($output, $expected, format_string('Sanitized file token %token replaced.', array('%token' => $input)));
+ $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]);
}
// Generate and test unsanitized tokens.
diff --git a/core/modules/file/src/Tests/LoadTest.php b/core/modules/file/src/Tests/LoadTest.php
index a9cf13187..7a0fde9ce 100644
--- a/core/modules/file/src/Tests/LoadTest.php
+++ b/core/modules/file/src/Tests/LoadTest.php
@@ -7,8 +7,10 @@
namespace Drupal\file\Tests;
+use Drupal\file\Entity\File;
+
/**
- * Tests the file_load() function.
+ * Tests \Drupal\file\Entity\File::load().
*
* @group file
*/
@@ -17,7 +19,7 @@ class LoadTest extends FileManagedUnitTestBase {
* Try to load a non-existent file by fid.
*/
function testLoadMissingFid() {
- $this->assertFalse(file_load(-1), 'Try to load an invalid fid fails.');
+ $this->assertFalse(File::load(-1), 'Try to load an invalid fid fails.');
$this->assertFileHooksCalled(array());
}
@@ -45,10 +47,9 @@ class LoadTest extends FileManagedUnitTestBase {
function testSingleValues() {
// Create a new file entity from scratch so we know the values.
$file = $this->createFile('druplicon.txt', NULL, 'public');
-
- $by_fid_file = file_load($file->id());
+ $by_fid_file = File::load($file->id());
$this->assertFileHookCalled('load');
- $this->assertTrue(is_object($by_fid_file), 'file_load() returned an object.');
+ $this->assertTrue(is_object($by_fid_file), '\Drupal\file\Entity\File::load() returned an object.');
$this->assertEqual($by_fid_file->id(), $file->id(), 'Loading by fid got the same fid.', 'File');
$this->assertEqual($by_fid_file->getFileUri(), $file->getFileUri(), 'Loading by fid got the correct filepath.', 'File');
$this->assertEqual($by_fid_file->getFilename(), $file->getFilename(), 'Loading by fid got the correct filename.', 'File');
@@ -68,16 +69,16 @@ class LoadTest extends FileManagedUnitTestBase {
file_test_reset();
$by_path_files = entity_load_multiple_by_properties('file', array('uri' => $file->getFileUri()));
$this->assertFileHookCalled('load');
- $this->assertEqual(1, count($by_path_files), 'file_load_multiple() returned an array of the correct size.');
+ $this->assertEqual(1, count($by_path_files), 'entity_load_multiple_by_properties() returned an array of the correct size.');
$by_path_file = reset($by_path_files);
$this->assertTrue($by_path_file->file_test['loaded'], 'file_test_file_load() was able to modify the file during load.');
$this->assertEqual($by_path_file->id(), $file->id(), 'Loading by filepath got the correct fid.', 'File');
// Load by fid.
file_test_reset();
- $by_fid_files = file_load_multiple(array($file->id()));
+ $by_fid_files = File::loadMultiple(array($file->id()));
$this->assertFileHooksCalled(array());
- $this->assertEqual(1, count($by_fid_files), 'file_load_multiple() returned an array of the correct size.');
+ $this->assertEqual(1, count($by_fid_files), '\Drupal\file\Entity\File::loadMultiple() returned an array of the correct size.');
$by_fid_file = reset($by_fid_files);
$this->assertTrue($by_fid_file->file_test['loaded'], 'file_test_file_load() was able to modify the file during load.');
$this->assertEqual($by_fid_file->getFileUri(), $file->getFileUri(), 'Loading by fid got the correct filepath.', 'File');
diff --git a/core/modules/file/src/Tests/MoveTest.php b/core/modules/file/src/Tests/MoveTest.php
index f3380db06..817799293 100644
--- a/core/modules/file/src/Tests/MoveTest.php
+++ b/core/modules/file/src/Tests/MoveTest.php
@@ -7,6 +7,8 @@
namespace Drupal\file\Tests;
+use Drupal\file\Entity\File;
+
/**
* Tests the file move function.
*
@@ -38,7 +40,7 @@ class MoveTest extends FileManagedUnitTestBase {
// Reload the file from the database and check that the changes were
// actually saved.
- $loaded_file = file_load($result->id(), TRUE);
+ $loaded_file = File::load($result->id());
$this->assertTrue($loaded_file, 'File can be loaded from the database.');
$this->assertFileUnchanged($result, $loaded_file);
}
@@ -66,14 +68,14 @@ class MoveTest extends FileManagedUnitTestBase {
$this->assertFileHooksCalled(array('move', 'load', 'update'));
// Compare the returned value to what made it into the database.
- $this->assertFileUnchanged($result, file_load($result->id(), TRUE));
+ $this->assertFileUnchanged($result, File::load($result->id()));
// The target file should not have been altered.
- $this->assertFileUnchanged($target, file_load($target->id(), TRUE));
+ $this->assertFileUnchanged($target, File::load($target->id()));
// Make sure we end up with two distinct files afterwards.
$this->assertDifferentFile($target, $result);
// Compare the source and results.
- $loaded_source = file_load($source->id(), TRUE);
+ $loaded_source = File::load($source->id());
$this->assertEqual($loaded_source->id(), $result->id(), "Returned file's id matches the source.");
$this->assertNotEqual($loaded_source->getFileUri(), $source->getFileUri(), 'Returned file path has changed from the original.');
}
@@ -102,7 +104,7 @@ class MoveTest extends FileManagedUnitTestBase {
// Reload the file from the database and check that the changes were
// actually saved.
- $loaded_result = file_load($result->id(), TRUE);
+ $loaded_result = File::load($result->id());
$this->assertFileUnchanged($result, $loaded_result);
// Check that target was re-used.
$this->assertSameFile($target, $loaded_result);
@@ -129,7 +131,7 @@ class MoveTest extends FileManagedUnitTestBase {
// Load the file from the database and make sure it is identical to what
// was returned.
- $this->assertFileUnchanged($source, file_load($source->id(), TRUE));
+ $this->assertFileUnchanged($source, File::load($source->id()));
}
/**
@@ -156,7 +158,7 @@ class MoveTest extends FileManagedUnitTestBase {
// Load the file from the database and make sure it is identical to what
// was returned.
- $this->assertFileUnchanged($source, file_load($source->id(), TRUE));
- $this->assertFileUnchanged($target, file_load($target->id(), TRUE));
+ $this->assertFileUnchanged($source, File::load($source->id()));
+ $this->assertFileUnchanged($target, File::load($target->id()));
}
}
diff --git a/core/modules/file/src/Tests/SaveDataTest.php b/core/modules/file/src/Tests/SaveDataTest.php
index 24d8fb5a1..4ca49ad89 100644
--- a/core/modules/file/src/Tests/SaveDataTest.php
+++ b/core/modules/file/src/Tests/SaveDataTest.php
@@ -7,6 +7,8 @@
namespace Drupal\file\Tests;
+use Drupal\file\Entity\File;
+
/**
* Tests the file_save_data() function.
*
@@ -32,7 +34,7 @@ class SaveDataTest extends FileManagedUnitTestBase {
$this->assertFileHooksCalled(array('insert'));
// Verify that what was returned is what's in the database.
- $this->assertFileUnchanged($result, file_load($result->id(), TRUE));
+ $this->assertFileUnchanged($result, File::load($result->id()));
}
/**
@@ -57,7 +59,7 @@ class SaveDataTest extends FileManagedUnitTestBase {
$this->assertFileHooksCalled(array('insert'));
// Verify that what was returned is what's in the database.
- $this->assertFileUnchanged($result, file_load($result->id(), TRUE));
+ $this->assertFileUnchanged($result, File::load($result->id()));
}
/**
@@ -82,10 +84,10 @@ class SaveDataTest extends FileManagedUnitTestBase {
// Ensure that the existing file wasn't overwritten.
$this->assertDifferentFile($existing, $result);
- $this->assertFileUnchanged($existing, file_load($existing->id(), TRUE));
+ $this->assertFileUnchanged($existing, File::load($existing->id()));
// Verify that was returned is what's in the database.
- $this->assertFileUnchanged($result, file_load($result->id(), TRUE));
+ $this->assertFileUnchanged($result, File::load($result->id()));
}
/**
@@ -112,7 +114,7 @@ class SaveDataTest extends FileManagedUnitTestBase {
$this->assertSameFile($existing, $result);
// Verify that what was returned is what's in the database.
- $this->assertFileUnchanged($result, file_load($result->id(), TRUE));
+ $this->assertFileUnchanged($result, File::load($result->id()));
}
/**
@@ -131,6 +133,6 @@ class SaveDataTest extends FileManagedUnitTestBase {
$this->assertFileHooksCalled(array());
// Ensure that the existing file wasn't overwritten.
- $this->assertFileUnchanged($existing, file_load($existing->id(), TRUE));
+ $this->assertFileUnchanged($existing, File::load($existing->id()));
}
}
diff --git a/core/modules/file/src/Tests/SaveTest.php b/core/modules/file/src/Tests/SaveTest.php
index 254917747..97d4882e6 100644
--- a/core/modules/file/src/Tests/SaveTest.php
+++ b/core/modules/file/src/Tests/SaveTest.php
@@ -36,7 +36,7 @@ class SaveTest extends FileManagedUnitTestBase {
$this->assertFileHooksCalled(array('insert'));
$this->assertTrue($file->id() > 0, 'A new file ID is set when saving a new file to the database.', 'File');
- $loaded_file = file_load($file->id());
+ $loaded_file = File::load($file->id());
$this->assertNotNull($loaded_file, 'Record exists in the database.');
$this->assertEqual($loaded_file->isPermanent(), $file->isPermanent(), 'Status was saved correctly.');
$this->assertEqual($file->getSize(), filesize($file->getFileUri()), 'File size was set correctly.', 'File');
@@ -53,7 +53,7 @@ class SaveTest extends FileManagedUnitTestBase {
$this->assertEqual($file->id(), $file->id(), 'The file ID of an existing file is not changed when updating the database.', 'File');
$this->assertTrue($file->getChangedTime() >= $file->getChangedTime(), "Timestamp didn't go backwards.", 'File');
- $loaded_file = file_load($file->id());
+ $loaded_file = File::load($file->id());
$this->assertNotNull($loaded_file, 'Record still exists in the database.', 'File');
$this->assertEqual($loaded_file->isPermanent(), $file->isPermanent(), 'Status was saved correctly.');
$this->assertEqual($loaded_file->langcode->value, 'en', 'Langcode was saved correctly.');
diff --git a/core/modules/file/src/Tests/SaveUploadTest.php b/core/modules/file/src/Tests/SaveUploadTest.php
index 155a67afa..baeb443bc 100644
--- a/core/modules/file/src/Tests/SaveUploadTest.php
+++ b/core/modules/file/src/Tests/SaveUploadTest.php
@@ -7,6 +7,8 @@
namespace Drupal\file\Tests;
+use Drupal\file\Entity\File;
+
/**
* Tests the file_save_upload() function.
*
@@ -81,7 +83,7 @@ class SaveUploadTest extends FileManagedTestBase {
function testNormal() {
$max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
$this->assertTrue($max_fid_after > $this->maxFidBefore, 'A new file was created.');
- $file1 = file_load($max_fid_after);
+ $file1 = File::load($max_fid_after);
$this->assertTrue($file1, 'Loaded the file.');
// MIME type of the uploaded image may be either image/jpeg or image/png.
$this->assertEqual(substr($file1->getMimeType(), 0, 5), 'image', 'A MIME type was set.');
@@ -100,13 +102,13 @@ class SaveUploadTest extends FileManagedTestBase {
// Check that the correct hooks were called.
$this->assertFileHooksCalled(array('validate', 'insert'));
- $file2 = file_load($max_fid_after);
+ $file2 = File::load($max_fid_after);
$this->assertTrue($file2, 'Loaded the file');
// MIME type of the uploaded image may be either image/jpeg or image/png.
$this->assertEqual(substr($file2->getMimeType(), 0, 5), 'image', 'A MIME type was set.');
- // Load both files using file_load_multiple().
- $files = file_load_multiple(array($file1->id(), $file2->id()));
+ // Load both files using File::loadMultiple().
+ $files = File::loadMultiple(array($file1->id(), $file2->id()));
$this->assertTrue(isset($files[$file1->id()]), 'File was loaded successfully');
$this->assertTrue(isset($files[$file2->id()]), 'File was loaded successfully');
diff --git a/core/modules/file/src/Tests/Views/ExtensionViewsFieldTest.php b/core/modules/file/src/Tests/Views/ExtensionViewsFieldTest.php
index 75623f4e5..f7d2b3fc0 100644
--- a/core/modules/file/src/Tests/Views/ExtensionViewsFieldTest.php
+++ b/core/modules/file/src/Tests/Views/ExtensionViewsFieldTest.php
@@ -7,6 +7,7 @@
namespace Drupal\file\Tests\Views;
+use Drupal\Core\Render\RenderContext;
use Drupal\file\Entity\File;
use Drupal\views\Views;
use Drupal\views\Tests\ViewUnitTestBase;
@@ -69,17 +70,22 @@ class ExtensionViewsFieldTest extends ViewUnitTestBase {
* Tests file extension views field handler extension_detect_tar option.
*/
public function testFileExtensionTarOption() {
+ /** @var \Drupal\Core\Render\RendererInterface $renderer */
+ $renderer = \Drupal::service('renderer');
+
$view = Views::getView('file_extension_view');
$view->setDisplay();
$this->executeView($view);
// Test without the tar option.
- $this->assertEqual($view->field['extension']->advancedRender($view->result[0]), 'png');
- $this->assertEqual($view->field['extension']->advancedRender($view->result[1]), 'tar');
- $this->assertEqual($view->field['extension']->advancedRender($view->result[2]), 'gz');
- $this->assertEqual($view->field['extension']->advancedRender($view->result[3]), '');
- // Test with the tar option.
+ $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+ $this->assertEqual($view->field['extension']->advancedRender($view->result[0]), 'png');
+ $this->assertEqual($view->field['extension']->advancedRender($view->result[1]), 'tar');
+ $this->assertEqual($view->field['extension']->advancedRender($view->result[2]), 'gz');
+ $this->assertEqual($view->field['extension']->advancedRender($view->result[3]), '');
+ });
+ // Test with the tar option.
$view = Views::getView('file_extension_view');
$view->setDisplay();
$view->initHandlers();
@@ -87,10 +93,12 @@ class ExtensionViewsFieldTest extends ViewUnitTestBase {
$view->field['extension']->options['settings']['extension_detect_tar'] = TRUE;
$this->executeView($view);
- $this->assertEqual($view->field['extension']->advancedRender($view->result[0]), 'png');
- $this->assertEqual($view->field['extension']->advancedRender($view->result[1]), 'tar');
- $this->assertEqual($view->field['extension']->advancedRender($view->result[2]), 'tar.gz');
- $this->assertEqual($view->field['extension']->advancedRender($view->result[3]), '');
+ $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
+ $this->assertEqual($view->field['extension']->advancedRender($view->result[0]), 'png');
+ $this->assertEqual($view->field['extension']->advancedRender($view->result[1]), 'tar');
+ $this->assertEqual($view->field['extension']->advancedRender($view->result[2]), 'tar.gz');
+ $this->assertEqual($view->field['extension']->advancedRender($view->result[3]), '');
+ });
}
}
diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module
index e7ec56cb3..7b25af456 100644
--- a/core/modules/file/tests/file_test/file_test.module
+++ b/core/modules/file/tests/file_test/file_test.module
@@ -222,7 +222,7 @@ function file_test_file_url_alter(&$uri) {
}
// Public created files.
else {
- $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
+ $wrapper = \Drupal::service('stream_wrapper_manager')->getViaScheme($scheme);
$path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
}
@@ -252,7 +252,7 @@ function file_test_file_url_alter(&$uri) {
}
// Public created files.
else {
- $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
+ $wrapper = \Drupal::service('stream_wrapper_manager')->getViaScheme($scheme);
$path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
}
@@ -275,7 +275,7 @@ function file_test_file_url_alter(&$uri) {
}
// Public created files.
else {
- $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
+ $wrapper = \Drupal::service('stream_wrapper_manager')->getViaScheme($scheme);
$path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
}
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 400d6cc0a..2287d7aed 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -288,7 +288,7 @@ function filter_fallback_format() {
* FilterInterface::TYPE_HTML_RESTRICTOR is the only type that cannot be
* skipped.
*
- * @return string
+ * @return \Drupal\Component\Utility\SafeStringInterface
* The filtered text.
*
* @see filter_process_text()
@@ -353,7 +353,10 @@ function _filter_tips($format_id, $long = FALSE) {
if ($filter->status) {
$tip = $filter->tips($long);
if (isset($tip)) {
- $tips[$format->label()][$name] = array('tip' => $tip, 'id' => $name);
+ $tips[$format->label()][$name] = array(
+ 'tip' => array('#markup' => $tip),
+ 'id' => $name,
+ );
}
}
}
@@ -430,7 +433,6 @@ function template_preprocess_filter_tips(&$variables) {
foreach ($variables['tips'] as $name => $tiplist) {
foreach ($tiplist as $tip_key => $tip) {
$tiplist[$tip_key]['attributes'] = new Attribute();
- $tiplist[$tip_key]['tip'] = Xss::filterAdmin($tiplist[$tip_key]['tip']);
}
$variables['tips'][$name] = array(
@@ -499,7 +501,7 @@ function _filter_url($text, $filter) {
// we cannot cleanly differ between protocols here without hard-coding MAILTO,
// so '//' is optional for all protocols.
// @see \Drupal\Component\Utility\UrlHelper::filterBadProtocol()
- $protocols = \Drupal::config('system.filter')->get('protocols');
+ $protocols = \Drupal::getContainer()->getParameter('filter_protocols') ?: ['http', 'https'];
$protocols = implode(':(?://)?|', $protocols) . ':(?://)?';
$valid_url_path_characters = "[\p{L}\p{M}\p{N}!\*\';:=\+,\.\$\/%#\[\]\-_~@&]";
@@ -535,7 +537,7 @@ function _filter_url($text, $filter) {
$tasks['_filter_url_parse_full_links'] = $pattern;
// Match email addresses.
- $url_pattern = "[\p{L}\p{M}\p{N}._-]{1,254}@(?:$domain)";
+ $url_pattern = "[\p{L}\p{M}\p{N}._+-]{1,254}@(?:$domain)";
$pattern = "`($url_pattern)`u";
$tasks['_filter_url_parse_email_links'] = $pattern;
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_filter_format.yml b/core/modules/filter/migration_templates/d6_filter_format.yml
similarity index 93%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_filter_format.yml
rename to core/modules/filter/migration_templates/d6_filter_format.yml
index 9efaf7a6a..16982d39a 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_filter_format.yml
+++ b/core/modules/filter/migration_templates/d6_filter_format.yml
@@ -43,7 +43,3 @@ process:
destination:
plugin: entity:filter_format
no_stub: true
-dependencies:
- module:
- - filter
- - migrate_drupal
diff --git a/core/modules/filter/src/Element/TextFormat.php b/core/modules/filter/src/Element/TextFormat.php
index aaff8b427..9247dbadb 100644
--- a/core/modules/filter/src/Element/TextFormat.php
+++ b/core/modules/filter/src/Element/TextFormat.php
@@ -15,6 +15,27 @@ use Drupal\Core\Url;
/**
* Provides a text format render element.
*
+ * Properties:
+ * - #base_type: The form element #type to use for the 'value' element.
+ * 'textarea' by default.
+ * - #format: (optional) The text format ID to preselect. If omitted, the
+ * default format for the current user will be used.
+ * - #allowed_formats: (optional) An array of text format IDs that are available
+ * for this element. If omitted, all text formats that the current user has
+ * access to will be allowed.
+ *
+ * Usage Example:
+ * @code
+ * $form['body'] = array(
+ * '#type' => 'text_format',
+ * '#title' => 'Body',
+ * '#format' => 'full_html',
+ * '#default_value' => 'The quick brown fox jumped over the lazy dog.
',
+ * );
+ * @endcode
+ *
+ * @see \Drupal\Core\Render\Element\Textarea
+ *
* @RenderElement("text_format")
*/
class TextFormat extends RenderElement {
@@ -54,14 +75,7 @@ class TextFormat extends RenderElement {
* @endcode
*
* @param array $element
- * The form element to process. Properties used:
- * - #base_type: The form element #type to use for the 'value' element.
- * 'textarea' by default.
- * - #format: (optional) The text format ID to preselect. If omitted, the
- * default format for the current user will be used.
- * - #allowed_formats: (optional) An array of text format IDs that are
- * available for this element. If omitted, all text formats that the
- * current user has access to will be allowed.
+ * The form element to process. See main class documentation for properties.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
@@ -178,12 +192,17 @@ class TextFormat extends RenderElement {
'#parents' => array_merge($element['#parents'], array('format')),
);
- $element['format']['help'] = array(
+ $element['format']['help'] = [
'#type' => 'container',
- '#attributes' => array('class' => array('filter-help')),
- '#markup' => \Drupal::l(t('About text formats'), new Url('filter.tips_all', array(), array('attributes' => array('target' => '_blank')))),
+ 'about' => [
+ '#type' => 'link',
+ '#title' => t('About text formats'),
+ '#url' => new Url('filter.tips_all'),
+ '#attributes' => ['target' => '_blank'],
+ ],
+ '#attributes' => ['class' => ['filter-help']],
'#weight' => 0,
- );
+ ];
$all_formats = filter_formats();
$format_exists = isset($all_formats[$element['#format']]);
diff --git a/core/modules/filter/src/FilterFormatFormBase.php b/core/modules/filter/src/FilterFormatFormBase.php
index ff140c3b0..77b543359 100644
--- a/core/modules/filter/src/FilterFormatFormBase.php
+++ b/core/modules/filter/src/FilterFormatFormBase.php
@@ -204,8 +204,8 @@ abstract class FilterFormatFormBase extends EntityForm {
/**
* {@inheritdoc}
*/
- public function validate(array $form, FormStateInterface $form_state) {
- parent::validate($form, $form_state);
+ public function validateForm(array &$form, FormStateInterface $form_state) {
+ parent::validateForm($form, $form_state);
// @todo Move trimming upstream.
$format_format = trim($form_state->getValue('format'));
diff --git a/core/modules/filter/src/Plugin/Filter/FilterCaption.php b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
index 6f90565eb..ed241d88e 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterCaption.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
@@ -45,7 +45,7 @@ class FilterCaption extends FilterBase {
// Sanitize caption: decode HTML encoding, limit allowed HTML tags; only
// allow inline tags that are allowed by default, plus .
$caption = Html::decodeEntities($caption);
- $caption = Xss::filter($caption, array('a', 'em', 'strong', 'cite', 'code', 'br'));
+ $caption = SafeMarkup::xssFilter($caption, array('a', 'em', 'strong', 'cite', 'code', 'br'));
// The caption must be non-empty.
if (Unicode::strlen($caption) === 0) {
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/FilterFormat.php b/core/modules/filter/src/Plugin/migrate/source/d6/FilterFormat.php
similarity index 95%
rename from core/modules/migrate_drupal/src/Plugin/migrate/source/d6/FilterFormat.php
rename to core/modules/filter/src/Plugin/migrate/source/d6/FilterFormat.php
index f832179c2..6d99e05ec 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/FilterFormat.php
+++ b/core/modules/filter/src/Plugin/migrate/source/d6/FilterFormat.php
@@ -2,10 +2,10 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Plugin\migrate\source\d6\FilterFormat.
+ * Contains \Drupal\filter\Plugin\migrate\source\d6\FilterFormat.
*/
-namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
+namespace Drupal\filter\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
use Drupal\migrate\Row;
diff --git a/core/modules/filter/src/ProxyClass/FilterUninstallValidator.php b/core/modules/filter/src/ProxyClass/FilterUninstallValidator.php
new file mode 100644
index 000000000..63fa96ca1
--- /dev/null
+++ b/core/modules/filter/src/ProxyClass/FilterUninstallValidator.php
@@ -0,0 +1,92 @@
+container = $container;
+ $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
+ }
+
+ /**
+ * Lazy loads the real service from the container.
+ *
+ * @return object
+ * Returns the constructed real service.
+ */
+ protected function lazyLoadItself()
+ {
+ if (!isset($this->service)) {
+ $this->service = $this->container->get($this->drupalProxyOriginalServiceId);
+ }
+
+ return $this->service;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($module)
+ {
+ return $this->lazyLoadItself()->validate($module);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setStringTranslation(\Drupal\Core\StringTranslation\TranslationInterface $translation)
+ {
+ return $this->lazyLoadItself()->setStringTranslation($translation);
+ }
+
+ }
+
+}
diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php
index ff23d39bb..263b31ee4 100644
--- a/core/modules/filter/src/Tests/FilterAPITest.php
+++ b/core/modules/filter/src/Tests/FilterAPITest.php
@@ -60,7 +60,7 @@ class FilterAPITest extends EntityUnitTestBase {
$text = "Llamas are awesome!
";
$expected_filtered_text = "<p>Llamas are awesome!</p>";
- $this->assertIdentical(check_markup($text, 'crazy'), $expected_filtered_text, 'Filters applied in correct order.');
+ $this->assertEqual(check_markup($text, 'crazy'), $expected_filtered_text, 'Filters applied in correct order.');
}
/**
@@ -73,14 +73,14 @@ class FilterAPITest extends EntityUnitTestBase {
$actual_filtered_text = check_markup($text, 'filtered_html', '', array());
$this->verbose("Actual:$actual_filtered_text Expected:$expected_filtered_text ");
- $this->assertIdentical(
+ $this->assertEqual(
$actual_filtered_text,
$expected_filtered_text,
'Expected filter result.'
);
$actual_filtered_text_without_html_generators = check_markup($text, 'filtered_html', '', array(FilterInterface::TYPE_MARKUP_LANGUAGE));
$this->verbose("Actual:$actual_filtered_text_without_html_generators Expected:$expected_filter_text_without_html_generators ");
- $this->assertIdentical(
+ $this->assertEqual(
$actual_filtered_text_without_html_generators,
$expected_filter_text_without_html_generators,
'Expected filter result when skipping FilterInterface::TYPE_MARKUP_LANGUAGE filters.'
@@ -91,7 +91,7 @@ class FilterAPITest extends EntityUnitTestBase {
// most extensive test possible.
$actual_filtered_text_without_html_generators = check_markup($text, 'filtered_html', '', array(FilterInterface::TYPE_HTML_RESTRICTOR, FilterInterface::TYPE_MARKUP_LANGUAGE));
$this->verbose("Actual:$actual_filtered_text_without_html_generators Expected:$expected_filter_text_without_html_generators ");
- $this->assertIdentical(
+ $this->assertEqual(
$actual_filtered_text_without_html_generators,
$expected_filter_text_without_html_generators,
'Expected filter result when skipping FilterInterface::TYPE_MARKUP_LANGUAGE filters, even when trying to disable filters of the FilterInterface::TYPE_HTML_RESTRICTOR type.'
@@ -221,6 +221,10 @@ class FilterAPITest extends EntityUnitTestBase {
'weight' => 0,
'status' => TRUE,
),
+ 'filter_test_cache_merge' => array(
+ 'weight' => 0,
+ 'status' => TRUE,
+ ),
'filter_test_placeholders' => array(
'weight' => 1,
'status' => TRUE,
@@ -257,6 +261,8 @@ class FilterAPITest extends EntityUnitTestBase {
// The cache tags set by the filter_test_cache_tags filter.
'foo:bar',
'foo:baz',
+ // The cache tags set by the filter_test_cache_merge filter.
+ 'merge:tag',
);
$this->assertEqual($expected_cache_tags, $build['#cache']['tags'], 'Expected cache tags present.');
$expected_cache_contexts = [
@@ -265,6 +271,8 @@ class FilterAPITest extends EntityUnitTestBase {
// The default cache contexts for Renderer.
'languages:' . LanguageInterface::TYPE_INTERFACE,
'theme',
+ // The cache tags set by the filter_test_cache_merge filter.
+ 'user.permissions',
];
$this->assertEqual($expected_cache_contexts, $build['#cache']['contexts'], 'Expected cache contexts present.');
$expected_markup = 'Hello, world!
This is a dynamic llama.
';
diff --git a/core/modules/filter/src/Tests/FilterUnitTest.php b/core/modules/filter/src/Tests/FilterUnitTest.php
index 7fdf8f9d5..fe7664c31 100644
--- a/core/modules/filter/src/Tests/FilterUnitTest.php
+++ b/core/modules/filter/src/Tests/FilterUnitTest.php
@@ -9,6 +9,7 @@ namespace Drupal\filter\Tests;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Render\RenderContext;
use Drupal\editor\EditorXssFilter\Standard;
use Drupal\filter\Entity\FilterFormat;
use Drupal\filter\FilterPluginCollection;
@@ -101,10 +102,14 @@ class FilterUnitTest extends KernelTestBase {
* Tests the caption filter.
*/
function testCaptionFilter() {
+ /** @var \Drupal\Core\Render\RendererInterface $renderer */
+ $renderer = \Drupal::service('renderer');
$filter = $this->filters['filter_caption'];
- $test = function($input) use ($filter) {
- return $filter->process($input, 'und');
+ $test = function($input) use ($filter, $renderer) {
+ return $renderer->executeInRenderContext(new RenderContext(), function () use ($input, $filter) {
+ return $filter->process($input, 'und');
+ });
};
$attached_library = array(
@@ -190,12 +195,14 @@ class FilterUnitTest extends KernelTestBase {
'filter_html_nofollow' => 0,
)
));
- $test_with_html_filter = function ($input) use ($filter, $html_filter) {
- // 1. Apply HTML filter's processing step.
- $output = $html_filter->process($input, 'und');
- // 2. Apply caption filter's processing step.
- $output = $filter->process($output, 'und');
- return $output->getProcessedText();
+ $test_with_html_filter = function ($input) use ($filter, $html_filter, $renderer) {
+ return $renderer->executeInRenderContext(new RenderContext(), function () use ($input, $filter, $html_filter) {
+ // 1. Apply HTML filter's processing step.
+ $output = $html_filter->process($input, 'und');
+ // 2. Apply caption filter's processing step.
+ $output = $filter->process($output, 'und');
+ return $output->getProcessedText();
+ });
};
// Editor XSS filter.
$test_editor_xss_filter = function ($input) {
@@ -252,11 +259,15 @@ class FilterUnitTest extends KernelTestBase {
* Tests the combination of the align and caption filters.
*/
function testAlignAndCaptionFilters() {
+ /** @var \Drupal\Core\Render\RendererInterface $renderer */
+ $renderer = \Drupal::service('renderer');
$align_filter = $this->filters['filter_align'];
$caption_filter = $this->filters['filter_caption'];
- $test = function($input) use ($align_filter, $caption_filter) {
- return $caption_filter->process($align_filter->process($input, 'und'), 'und');
+ $test = function($input) use ($align_filter, $caption_filter, $renderer) {
+ return $renderer->executeInRenderContext(new RenderContext(), function () use ($input, $align_filter, $caption_filter) {
+ return $caption_filter->process($align_filter->process($input, 'und'), 'und');
+ });
};
$attached_library = array(
@@ -504,7 +515,7 @@ class FilterUnitTest extends KernelTestBase {
// Create a email that is too long.
$long_email = str_repeat('a', 254) . '@example.com';
$too_long_email = str_repeat('b', 255) . '@example.com';
-
+ $email_with_plus_sign = 'one+two@example.com';
// Filter selection/pattern matching.
$tests = array(
@@ -517,12 +528,13 @@ http://example.com or www.example.com
),
// MAILTO URLs.
'
-person@example.com or mailto:person2@example.com or ' . $long_email . ' but not ' . $too_long_email . '
+person@example.com or mailto:person2@example.com or ' . $email_with_plus_sign . ' or ' . $long_email . ' but not ' . $too_long_email . '
' => array(
'person@example.com ' => TRUE,
'mailto:person2@example.com ' => TRUE,
'' . $long_email . ' ' => TRUE,
'' . $too_long_email . ' ' => FALSE,
+ '' . $email_with_plus_sign . ' ' => TRUE,
),
// URI parts and special characters.
'
@@ -582,7 +594,7 @@ me@me.tv
),
// Absolute URL protocols.
// The list to test is found in the beginning of _filter_url() at
- // $protocols = $this->config('system.filter')->get('protocols').
+ // $protocols = \Drupal::getContainer()->getParameter('filter_protocols').
'
https://example.com,
ftp://ftp.example.com,
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateFilterFormatTest.php b/core/modules/filter/src/Tests/Migrate/d6/MigrateFilterFormatTest.php
similarity index 74%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateFilterFormatTest.php
rename to core/modules/filter/src/Tests/Migrate/d6/MigrateFilterFormatTest.php
index 1c11f83da..62f374b6d 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateFilterFormatTest.php
+++ b/core/modules/filter/src/Tests/Migrate/d6/MigrateFilterFormatTest.php
@@ -2,18 +2,17 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateFilterFormatTest.
+ * Contains \Drupal\filter\Tests\Migrate\d6\MigrateFilterFormatTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\filter\Tests\Migrate\d6;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to filter.formats.*.yml.
*
- * @group migrate_drupal
+ * @group filter
*/
class MigrateFilterFormatTest extends MigrateDrupal6TestBase {
@@ -27,15 +26,8 @@ class MigrateFilterFormatTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
- $migration = entity_load('migration', 'd6_filter_format');
- $dumps = array(
- $this->getDumpDirectory() . '/Filters.php',
- $this->getDumpDirectory() . '/FilterFormats.php',
- $this->getDumpDirectory() . '/Variable.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
+ $this->loadDumps(['Filters.php', 'FilterFormats.php', 'Variable.php']);
+ $this->executeMigration('d6_filter_format');
}
/**
diff --git a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheMerge.php b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheMerge.php
new file mode 100644
index 000000000..9e56c9bf9
--- /dev/null
+++ b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheMerge.php
@@ -0,0 +1,41 @@
+addCacheTags(['merge:tag']);
+ $metadata->addCacheContexts(['user.permissions']);
+ $result = $result->merge($metadata);
+
+ return $result;
+ }
+
+}
diff --git a/core/modules/migrate_drupal/tests/src/Unit/source/d6/FilterFormatTest.php b/core/modules/filter/tests/src/Unit/Plugin/migrate/source/d6/FilterFormatTest.php
similarity index 91%
rename from core/modules/migrate_drupal/tests/src/Unit/source/d6/FilterFormatTest.php
rename to core/modules/filter/tests/src/Unit/Plugin/migrate/source/d6/FilterFormatTest.php
index 49ec31a50..ebdb776bb 100644
--- a/core/modules/migrate_drupal/tests/src/Unit/source/d6/FilterFormatTest.php
+++ b/core/modules/filter/tests/src/Unit/Plugin/migrate/source/d6/FilterFormatTest.php
@@ -2,23 +2,23 @@
/**
* @file
- * Contains \Drupal\Tests\migrate_drupal\Unit\source\d6\FilterFormatTest.
+ * Contains \Drupal\Tests\filter\Unit\Plugin\migrate\source\d6\FilterFormatTest.
*/
-namespace Drupal\Tests\migrate_drupal\Unit\source\d6;
+namespace Drupal\Tests\filter\Unit\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase;
/**
* Tests D6 filter_formats table source plugin.
*
- * @group migrate_drupal
+ * @group filter
*/
class FilterFormatTest extends MigrateSqlSourceTestCase {
// The plugin system is not working during unit testing so the source plugin
// class needs to be manually specified.
- const PLUGIN_CLASS = 'Drupal\migrate_drupal\Plugin\migrate\source\d6\FilterFormat';
+ const PLUGIN_CLASS = 'Drupal\filter\Plugin\migrate\source\d6\FilterFormat';
// The fake Migration configuration entity.
protected $migrationConfiguration = array(
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 8ce12ba2e..90086a6a8 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -540,7 +540,7 @@ function template_preprocess_forum_list(&$variables) {
$row = 0;
// Sanitize each forum so that the template can safely print the data.
foreach ($variables['forums'] as $id => $forum) {
- $variables['forums'][$id]->description = Xss::filterAdmin($forum->description->value);
+ $variables['forums'][$id]->description = array('#markup' => $forum->description->value);
$variables['forums'][$id]->link = forum_uri($forum);
$variables['forums'][$id]->name = SafeMarkup::checkPlain($forum->label());
$variables['forums'][$id]->is_container = !empty($forum->forum_container->value);
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_forum_settings.yml b/core/modules/forum/migration_templates/d6_forum_settings.yml
similarity index 85%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_forum_settings.yml
rename to core/modules/forum/migration_templates/d6_forum_settings.yml
index 72e4814f3..2df6496d7 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_forum_settings.yml
+++ b/core/modules/forum/migration_templates/d6_forum_settings.yml
@@ -27,9 +27,3 @@ destination:
migration_dependencies:
required:
- d6_taxonomy_vocabulary
-dependencies:
- config:
- - migrate.migration.d6_taxonomy_vocabulary
- module:
- - forum
- - migrate_drupal
diff --git a/core/modules/forum/src/Form/Overview.php b/core/modules/forum/src/Form/Overview.php
index 24f5142aa..dae7fb54c 100644
--- a/core/modules/forum/src/Form/Overview.php
+++ b/core/modules/forum/src/Form/Overview.php
@@ -86,9 +86,7 @@ class Overview extends OverviewTerms {
// Remove the alphabetical reset.
unset($form['actions']['reset_alphabetical']);
- // The form needs to have submit and validate handlers set explicitly.
// Use the existing taxonomy overview submit handler.
- $form['#submit'] = array('::submitForm');
$form['terms']['#empty'] = $this->t('No containers or forums available. Add container or Add forum .', array(
'@container' => $this->url('forum.add_container'),
'@forum' => $this->url('forum.add_forum')
diff --git a/core/modules/forum/src/ProxyClass/ForumUninstallValidator.php b/core/modules/forum/src/ProxyClass/ForumUninstallValidator.php
new file mode 100644
index 000000000..59a747ebb
--- /dev/null
+++ b/core/modules/forum/src/ProxyClass/ForumUninstallValidator.php
@@ -0,0 +1,92 @@
+container = $container;
+ $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
+ }
+
+ /**
+ * Lazy loads the real service from the container.
+ *
+ * @return object
+ * Returns the constructed real service.
+ */
+ protected function lazyLoadItself()
+ {
+ if (!isset($this->service)) {
+ $this->service = $this->container->get($this->drupalProxyOriginalServiceId);
+ }
+
+ return $this->service;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($module)
+ {
+ return $this->lazyLoadItself()->validate($module);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setStringTranslation(\Drupal\Core\StringTranslation\TranslationInterface $translation)
+ {
+ return $this->lazyLoadItself()->setStringTranslation($translation);
+ }
+
+ }
+
+}
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateForumConfigsTest.php b/core/modules/forum/src/Tests/Migrate/d6/MigrateForumConfigsTest.php
similarity index 76%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateForumConfigsTest.php
rename to core/modules/forum/src/Tests/Migrate/d6/MigrateForumConfigsTest.php
index f91dade33..803de4b07 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateForumConfigsTest.php
+++ b/core/modules/forum/src/Tests/Migrate/d6/MigrateForumConfigsTest.php
@@ -2,19 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateForumConfigsTest.
+ * Contains \Drupal\forum\Tests\Migrate\d6\MigrateForumConfigsTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\forum\Tests\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to forum.settings.yml.
*
- * @group migrate_drupal
+ * @group forum
*/
class MigrateForumConfigsTest extends MigrateDrupal6TestBase {
@@ -37,13 +36,8 @@ class MigrateForumConfigsTest extends MigrateDrupal6TestBase {
array(array(1), array('vocabulary_1_i_0_')),
)
));
- $migration = entity_load('migration', 'd6_forum_settings');
- $dumps = array(
- $this->getDumpDirectory() . '/Variable.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
+ $this->loadDumps(['Variable.php']);
+ $this->executeMigration('d6_forum_settings');
}
/**
diff --git a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php
index f993024f6..52ee04b76 100644
--- a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php
+++ b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php
@@ -64,7 +64,7 @@ class FileEntityNormalizer extends ContentEntityNormalizer {
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = NULL, array $context = array()) {
- $file_data = $this->httpClient->get($data['uri'][0]['value'])->getBody(TRUE);
+ $file_data = (string) $this->httpClient->get($data['uri'][0]['value'])->getBody();
$path = 'temporary://' . drupal_basename($data['uri'][0]['value']);
$data['uri'] = file_unmanaged_save_data($file_data, $path);
diff --git a/core/modules/help/tests/modules/help_test/src/SupernovaGenerator.php b/core/modules/help/tests/modules/help_test/src/SupernovaGenerator.php
index 3b6adec23..635ecaa65 100644
--- a/core/modules/help/tests/modules/help_test/src/SupernovaGenerator.php
+++ b/core/modules/help/tests/modules/help_test/src/SupernovaGenerator.php
@@ -39,7 +39,7 @@ class SupernovaGenerator implements UrlGeneratorInterface {
/**
* {@inheritdoc}
*/
- public function generateFromPath($path = NULL, $options = array(), $collect_cacheability_metadata = FALSE) {
+ public function generateFromPath($path = NULL, $options = array(), $collect_bubbleable_metadata = FALSE) {
throw new \Exception();
}
@@ -53,7 +53,7 @@ class SupernovaGenerator implements UrlGeneratorInterface {
/**
* {@inheritdoc}
*/
- public function generateFromRoute($name, $parameters = array(), $options = array(), $collect_cacheability_metadata = FALSE) {
+ public function generateFromRoute($name, $parameters = array(), $options = array(), $collect_bubbleable_metadata = FALSE) {
throw new \Exception();
}
diff --git a/core/modules/image/image.services.yml b/core/modules/image/image.services.yml
index 88770c67f..2f17bb5c8 100644
--- a/core/modules/image/image.services.yml
+++ b/core/modules/image/image.services.yml
@@ -1,6 +1,7 @@
services:
path_processor.image_styles:
class: Drupal\image\PathProcessor\PathProcessorImageStyles
+ arguments: ['@stream_wrapper_manager']
tags:
- { name: path_processor_inbound, priority: 300 }
plugin.manager.image.effect:
diff --git a/core/modules/image/src/Entity/ImageStyle.php b/core/modules/image/src/Entity/ImageStyle.php
index a05d3a881..d956d881f 100644
--- a/core/modules/image/src/Entity/ImageStyle.php
+++ b/core/modules/image/src/Entity/ImageStyle.php
@@ -226,7 +226,7 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
// to the actual file path, this avoids bootstrapping PHP once the files are
// built.
if ($clean_urls === FALSE && file_uri_scheme($uri) == 'public' && !file_exists($uri)) {
- $directory_path = file_stream_wrapper_get_instance_by_uri($uri)->getDirectoryPath();
+ $directory_path = \Drupal::service('stream_wrapper_manager')->getViaUri($uri)->getDirectoryPath();
return Url::fromUri('base:' . $directory_path . '/' . file_uri_target($uri), array('absolute' => TRUE, 'query' => $token_query))->toString();
}
@@ -267,7 +267,7 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
// Clear caches so that formatters may be added for this style.
drupal_theme_rebuild();
- Cache::invalidateTags($this->getCacheTags());
+ Cache::invalidateTags($this->getCacheTagsToInvalidate());
return $this;
}
diff --git a/core/modules/image/src/Form/ImageEffectFormBase.php b/core/modules/image/src/Form/ImageEffectFormBase.php
index 7b7f25b61..66edd95ba 100644
--- a/core/modules/image/src/Form/ImageEffectFormBase.php
+++ b/core/modules/image/src/Form/ImageEffectFormBase.php
@@ -13,7 +13,6 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\image\ConfigurableImageEffectInterface;
use Drupal\image\ImageStyleInterface;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
-use Drupal\Component\Utility\SafeMarkup;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
@@ -61,7 +60,7 @@ abstract class ImageEffectFormBase extends FormBase {
$this->imageEffect = $this->prepareImageEffect($image_effect);
}
catch (PluginNotFoundException $e) {
- throw new NotFoundHttpException(SafeMarkup::format("Invalid effect id: '@id'.", array('@id' => $image_effect)));
+ throw new NotFoundHttpException("Invalid effect id: '$image_effect'.");
}
$request = $this->getRequest();
diff --git a/core/modules/image/src/PathProcessor/PathProcessorImageStyles.php b/core/modules/image/src/PathProcessor/PathProcessorImageStyles.php
index c723860a0..f3948d869 100644
--- a/core/modules/image/src/PathProcessor/PathProcessorImageStyles.php
+++ b/core/modules/image/src/PathProcessor/PathProcessorImageStyles.php
@@ -8,6 +8,7 @@
namespace Drupal\image\PathProcessor;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
@@ -27,11 +28,28 @@ use Symfony\Component\HttpFoundation\Request;
*/
class PathProcessorImageStyles implements InboundPathProcessorInterface {
+ /**
+ * The stream wrapper manager service.
+ *
+ * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
+ */
+ protected $streamWrapperManager;
+
+ /**
+ * Constructs a new PathProcessorImageStyles object.
+ *
+ * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
+ * The stream wrapper manager service.
+ */
+ public function __construct(StreamWrapperManagerInterface $stream_wrapper_manager) {
+ $this->streamWrapperManager = $stream_wrapper_manager;
+ }
+
/**
* {@inheritdoc}
*/
public function processInbound($path, Request $request) {
- $directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath();
+ $directory_path = $this->streamWrapperManager->getViaScheme('public')->getDirectoryPath();
if (strpos($path, '/' . $directory_path . '/styles/') === 0) {
$path_prefix = '/' . $directory_path . '/styles/';
}
@@ -43,7 +61,7 @@ class PathProcessorImageStyles implements InboundPathProcessorInterface {
}
// Strip out path prefix.
- $rest = preg_replace('|^' . $path_prefix . '|', '', $path);
+ $rest = preg_replace('|^' . preg_quote($path_prefix, '|') . '|', '', $path);
// Get the image style, scheme and path.
if (substr_count($rest, '/') >= 2) {
diff --git a/core/modules/image/src/Routing/ImageStyleRoutes.php b/core/modules/image/src/Routing/ImageStyleRoutes.php
index 32e8f2b26..44829ed26 100644
--- a/core/modules/image/src/Routing/ImageStyleRoutes.php
+++ b/core/modules/image/src/Routing/ImageStyleRoutes.php
@@ -7,12 +7,41 @@
namespace Drupal\image\Routing;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Route;
/**
* Defines a route subscriber to register a url for serving image styles.
*/
-class ImageStyleRoutes {
+class ImageStyleRoutes implements ContainerInjectionInterface {
+
+ /**
+ * The stream wrapper manager service.
+ *
+ * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
+ */
+ protected $streamWrapperManager;
+
+ /**
+ * Constructs a new PathProcessorImageStyles object.
+ *
+ * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
+ * The stream wrapper manager service.
+ */
+ public function __construct(StreamWrapperManagerInterface $stream_wrapper_manager) {
+ $this->streamWrapperManager = $stream_wrapper_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('stream_wrapper_manager')
+ );
+ }
/**
* Returns an array of route objects.
@@ -26,7 +55,7 @@ class ImageStyleRoutes {
// disabled image derivatives will always be served through the menu system.
// If clean URLs are enabled and the image derivative already exists, PHP
// will be bypassed.
- $directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath();
+ $directory_path = $this->streamWrapperManager->getViaScheme('public')->getDirectoryPath();
$routes['image.style_public'] = new Route(
'/' . $directory_path . '/styles/{image_style}/{scheme}',
diff --git a/core/modules/image/src/Tests/ImageAdminStylesTest.php b/core/modules/image/src/Tests/ImageAdminStylesTest.php
index 5501d8024..194c016ce 100644
--- a/core/modules/image/src/Tests/ImageAdminStylesTest.php
+++ b/core/modules/image/src/Tests/ImageAdminStylesTest.php
@@ -11,6 +11,7 @@ use Drupal\Component\Utility\SafeMarkup;
use Drupal\image\Entity\ImageStyle;
use Drupal\image\ImageStyleInterface;
use Drupal\node\Entity\Node;
+use Drupal\file\Entity\File;
/**
* Tests creation, deletion, and editing of image styles and effects.
@@ -302,7 +303,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
// Get node field original image URI.
$fid = $node->get($field_name)->target_id;
- $original_uri = file_load($fid)->getFileUri();
+ $original_uri = File::load($fid)->getFileUri();
// Test that image is displayed using newly created style.
$this->drupalGet('node/' . $nid);
@@ -436,7 +437,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
// Get node field original image URI.
$fid = $node->get($field_name)->target_id;
- $original_uri = file_load($fid)->getFileUri();
+ $original_uri = File::load($fid)->getFileUri();
// Test that image is displayed using newly created style.
$this->drupalGet('node/' . $nid);
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 342e7302d..29c93d2cf 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -282,42 +282,6 @@ function language_get_default_langcode($entity_type, $bundle) {
return $configuration->getDefaultLangcode();
}
-/**
- * Implements hook_language_types_info().
- *
- * Defines the three core language types:
- * - Interface language is the only configurable language type in core. It is
- * used by t() as the default language if none is specified.
- * - Content language is by default non-configurable and inherits the interface
- * language negotiated value. It is used by the Field API to determine the
- * display language for fields if no explicit value is specified.
- * - URL language is by default non-configurable and is determined through the
- * URL language negotiation method or the URL fallback language negotiation
- * method if no language can be detected. It is used by l() as the default
- * language if none is specified.
- */
-function language_language_types_info() {
- return array(
- LanguageInterface::TYPE_INTERFACE => array(
- 'name' => t('Interface text'),
- 'description' => t('Order of language detection methods for interface text. If a translation of interface text is available in the detected language, it will be displayed.'),
- 'locked' => TRUE,
- ),
- LanguageInterface::TYPE_CONTENT => array(
- 'name' => t('Content'),
- 'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'),
- 'fixed' => array(LanguageNegotiationUI::METHOD_ID),
- 'locked' => TRUE,
- ),
- LanguageInterface::TYPE_URL => array(
- 'name' => t('URL'),
- 'description' => t('Order of language detection methods for URLs. The detected language will be used as the default when generating URLs for internal links on the site.'),
- 'fixed' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationUrlFallback::METHOD_ID),
- 'locked' => TRUE,
- ),
- );
-}
-
/**
* Reads language prefixes and uses the langcode if no prefix is set.
*/
@@ -540,3 +504,16 @@ function language_tour_tips_alter(array &$tour_tips, EntityInterface $entity) {
}
}
}
+
+/**
+ * Implements hook_language_types_info_alter().
+ *
+ * We can't set the fixed properties in \Drupal\Core\Language\LanguageManager,
+ * where the rest of the properties for the default language types are defined.
+ * The LanguageNegation classes are only loaded when the language module is
+ * enabled and we can't be sure of that in the LanguageManager.
+ */
+function language_language_types_info_alter(array &$language_types) {
+ $language_types[LanguageInterface::TYPE_CONTENT]['fixed'] = [LanguageNegotiationUI::METHOD_ID];
+ $language_types[LanguageInterface::TYPE_URL]['fixed'] = [LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationUrlFallback::METHOD_ID];
+}
diff --git a/core/modules/language/language.services.yml b/core/modules/language/language.services.yml
index b9de79a0b..fc9bba1cf 100644
--- a/core/modules/language/language.services.yml
+++ b/core/modules/language/language.services.yml
@@ -9,7 +9,7 @@ services:
- [initLanguageManager]
language.config_subscriber:
class: Drupal\language\EventSubscriber\ConfigSubscriber
- arguments: ['@language_manager', '@language.default']
+ arguments: ['@language_manager', '@language.default', '@config.factory']
tags:
- { name: event_subscriber }
language.config_factory_override:
diff --git a/core/modules/language/src/Config/LanguageConfigCollectionNameTrait.php b/core/modules/language/src/Config/LanguageConfigCollectionNameTrait.php
index 651209ae0..00cd1b9f1 100644
--- a/core/modules/language/src/Config/LanguageConfigCollectionNameTrait.php
+++ b/core/modules/language/src/Config/LanguageConfigCollectionNameTrait.php
@@ -7,8 +7,6 @@
namespace Drupal\language\Config;
-use Drupal\Component\Utility\SafeMarkup;
-
/**
* Provides a common trait for working with language override collection names.
*/
@@ -45,7 +43,7 @@ trait LanguageConfigCollectionNameTrait {
protected function getLangcodeFromCollectionName($collection) {
preg_match('/^language\.(.*)$/', $collection, $matches);
if (!isset($matches[1])) {
- throw new \InvalidArgumentException(SafeMarkup::format('!collection is not a valid language override collection', array('!collection' => $collection)));
+ throw new \InvalidArgumentException("'$collection' is not a valid language override collection");
}
return $matches[1];
}
diff --git a/core/modules/language/src/Config/LanguageConfigFactoryOverride.php b/core/modules/language/src/Config/LanguageConfigFactoryOverride.php
index 3d0a67ba8..e547d1e13 100644
--- a/core/modules/language/src/Config/LanguageConfigFactoryOverride.php
+++ b/core/modules/language/src/Config/LanguageConfigFactoryOverride.php
@@ -7,6 +7,7 @@
namespace Drupal\language\Config;
+use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigCollectionInfo;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigFactoryOverrideBase;
@@ -222,4 +223,15 @@ class LanguageConfigFactoryOverride extends ConfigFactoryOverrideBase implements
}
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheableMetadata($name) {
+ $metadata = new CacheableMetadata();
+ if ($this->language) {
+ $metadata->setCacheContexts(['languages:language_interface']);
+ }
+ return $metadata;
+ }
+
}
diff --git a/core/modules/language/src/ConfigurableLanguageManager.php b/core/modules/language/src/ConfigurableLanguageManager.php
index a4ca11b6a..d51692f1a 100644
--- a/core/modules/language/src/ConfigurableLanguageManager.php
+++ b/core/modules/language/src/ConfigurableLanguageManager.php
@@ -8,7 +8,6 @@
namespace Drupal\language;
use Drupal\Core\Language\LanguageInterface;
-use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\Language;
@@ -105,7 +104,7 @@ class ConfigurableLanguageManager extends LanguageManager implements Configurabl
* {@inheritdoc}
*/
public static function rebuildServices() {
- PhpStorageFactory::get('service_container')->deleteAll();
+ \Drupal::service('kernel')->invalidateContainer();
}
/**
@@ -183,10 +182,14 @@ class ConfigurableLanguageManager extends LanguageManager implements Configurabl
*/
public function getDefinedLanguageTypesInfo() {
if (!isset($this->languageTypesInfo)) {
+ $defaults = parent::getDefinedLanguageTypesInfo();
+
$info = $this->moduleHandler->invokeAll('language_types_info');
+ $language_info = $info + $defaults;
+
// Let other modules alter the list of language types.
- $this->moduleHandler->alter('language_types_info', $info);
- $this->languageTypesInfo = $info;
+ $this->moduleHandler->alter('language_types_info', $language_info);
+ $this->languageTypesInfo = $language_info;
}
return $this->languageTypesInfo;
}
diff --git a/core/modules/language/src/Entity/ContentLanguageSettings.php b/core/modules/language/src/Entity/ContentLanguageSettings.php
index 76a8a2573..da69686fe 100644
--- a/core/modules/language/src/Entity/ContentLanguageSettings.php
+++ b/core/modules/language/src/Entity/ContentLanguageSettings.php
@@ -7,7 +7,6 @@
namespace Drupal\language\Entity;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Language\LanguageInterface;
@@ -200,7 +199,7 @@ class ContentLanguageSettings extends ConfigEntityBase implements ContentLanguag
// If the target entity type uses entities to manage its bundles then
// depend on the bundle entity.
if (!$bundle_entity = $this->entityManager()->getStorage($bundle_entity_type_id)->load($this->target_bundle)) {
- throw new \LogicException(SafeMarkup::format('Missing bundle entity, entity type %type, entity id %bundle.', array('%type' => $bundle_entity_type_id, '%bundle' => $this->target_bundle)));
+ throw new \LogicException("Missing bundle entity, entity type $bundle_entity_type_id, entity id {$this->target_bundle}.");
}
$this->addDependency('config', $bundle_entity->getConfigDependencyName());
}
diff --git a/core/modules/language/src/EventSubscriber/ConfigSubscriber.php b/core/modules/language/src/EventSubscriber/ConfigSubscriber.php
index ef3f65d5e..e1875ce7d 100644
--- a/core/modules/language/src/EventSubscriber/ConfigSubscriber.php
+++ b/core/modules/language/src/EventSubscriber/ConfigSubscriber.php
@@ -7,11 +7,13 @@
namespace Drupal\language\EventSubscriber;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageDefault;
use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents;
+use Drupal\language\ConfigurableLanguageManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
@@ -33,6 +35,13 @@ class ConfigSubscriber implements EventSubscriberInterface {
*/
protected $languageDefault;
+ /**
+ * The configuration factory.
+ *
+ * @var \Drupal\Core\Config\ConfigFactoryInterface
+ */
+ protected $configFactory;
+
/**
* Constructs a new class object.
*
@@ -40,31 +49,58 @@ class ConfigSubscriber implements EventSubscriberInterface {
* The language manager.
* @param \Drupal\Core\Language\LanguageDefault $language_default
* The default language.
+ * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+ * The configuration factory.
*/
- public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default) {
+ public function __construct(LanguageManagerInterface $language_manager, LanguageDefault $language_default, ConfigFactoryInterface $config_factory) {
$this->languageManager = $language_manager;
$this->languageDefault = $language_default;
+ $this->configFactory = $config_factory;
}
/**
* Causes the container to be rebuilt on the next request.
*
+ * This event subscriber assumes that the new default langcode and old default
+ * langcode are valid langcodes. If the schema definition of either
+ * system.site:default_langcode or language.negotiation::url.prefixes changes
+ * then this event must be changed to work with both the old and new schema
+ * definition so this event is update safe.
+ *
* @param ConfigCrudEvent $event
* The configuration event.
*/
public function onConfigSave(ConfigCrudEvent $event) {
$saved_config = $event->getConfig();
if ($saved_config->getName() == 'system.site' && $event->isChanged('default_langcode')) {
- $language = $this->languageManager->getLanguage($saved_config->get('default_langcode'));
+ $new_default_langcode = $saved_config->get('default_langcode');
+ $default_language = $this->configFactory->get('language.entity.' . $new_default_langcode);
// During an import the language might not exist yet.
- if ($language) {
- $this->languageDefault->set($language);
+ if (!$default_language->isNew()) {
+ $this->languageDefault->set(new Language($default_language->get()));
$this->languageManager->reset();
- language_negotiation_url_prefixes_update();
+
+ // Directly update language negotiation settings instead of calling
+ // language_negotiation_url_prefixes_update() to ensure that the code
+ // obeys the hook_update_N() restrictions.
+ $negotiation_config = $this->configFactory->getEditable('language.negotiation');
+ $negotiation_changed = FALSE;
+ $url_prefixes = $negotiation_config->get('url.prefixes');
+ $old_default_langcode = $saved_config->getOriginal('default_langcode');
+ if (empty($url_prefixes[$old_default_langcode])) {
+ $negotiation_config->set('url.prefixes.' . $old_default_langcode, $old_default_langcode);
+ $negotiation_changed = TRUE;
+ }
+ if (empty($url_prefixes[$new_default_langcode])) {
+ $negotiation_config->set('url.prefixes.' . $new_default_langcode, '');
+ $negotiation_changed = TRUE;
+ }
+ if ($negotiation_changed) {
+ $negotiation_config->save(TRUE);
+ }
}
- // Trigger a container rebuild on the next request by deleting compiled
- // from PHP storage.
- PhpStorageFactory::get('service_container')->deleteAll();
+ // Trigger a container rebuild on the next request by invalidating it.
+ ConfigurableLanguageManager::rebuildServices();
}
}
diff --git a/core/modules/language/src/Form/NegotiationConfigureForm.php b/core/modules/language/src/Form/NegotiationConfigureForm.php
index d33138b24..938564e11 100644
--- a/core/modules/language/src/Form/NegotiationConfigureForm.php
+++ b/core/modules/language/src/Form/NegotiationConfigureForm.php
@@ -301,7 +301,7 @@ class NegotiationConfigureForm extends ConfigFormBase {
$table_form['enabled'][$method_id]['#attributes'] = array('disabled' => 'disabled');
}
- $table_form['description'][$method_id] = array('#markup' => Xss::filterAdmin($method['description']));
+ $table_form['description'][$method_id] = array('#markup' => $method['description']);
$config_op = array();
if (isset($method['config_route_name'])) {
diff --git a/core/modules/language/src/HttpKernel/PathProcessorLanguage.php b/core/modules/language/src/HttpKernel/PathProcessorLanguage.php
index a69330994..e25e2b5e0 100644
--- a/core/modules/language/src/HttpKernel/PathProcessorLanguage.php
+++ b/core/modules/language/src/HttpKernel/PathProcessorLanguage.php
@@ -8,10 +8,10 @@
namespace Drupal\language\HttpKernel;
use Drupal\Component\Utility\Unicode;
-use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\Core\Render\BubbleableMetadata;
use Drupal\language\ConfigurableLanguageManagerInterface;
use Drupal\language\LanguageNegotiatorInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -95,7 +95,7 @@ class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPa
/**
* {@inheritdoc}
*/
- public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) {
+ public function processOutbound($path, &$options = array(), Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
if (!isset($this->multilingual)) {
$this->multilingual = $this->languageManager->isMultilingual();
}
@@ -106,7 +106,7 @@ class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPa
$this->initProcessors($scope);
}
foreach ($this->processors[$scope] as $instance) {
- $path = $instance->processOutbound($path, $options, $request, $cacheable_metadata);
+ $path = $instance->processOutbound($path, $options, $request, $bubbleable_metadata);
}
// No language dependent path allowed in this mode.
if (empty($this->processors[$scope])) {
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
index fe9500a4a..9f4710956 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
@@ -7,9 +7,9 @@
namespace Drupal\language\Plugin\LanguageNegotiation;
-use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Url;
use Drupal\language\LanguageNegotiationMethodBase;
use Drupal\language\LanguageSwitcherInterface;
@@ -86,7 +86,7 @@ class LanguageNegotiationSession extends LanguageNegotiationMethodBase implement
/**
* {@inheritdoc}
*/
- public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) {
+ public function processOutbound($path, &$options = array(), Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
if ($request) {
// The following values are not supposed to change during a single page
// request processing.
@@ -115,10 +115,10 @@ class LanguageNegotiationSession extends LanguageNegotiationMethodBase implement
if (!isset($options['query'][$this->queryParam])) {
$options['query'][$this->queryParam] = $this->queryValue;
}
- if ($cacheable_metadata) {
+ if ($bubbleable_metadata) {
// Cached URLs that have been processed by this outbound path
// processor must be:
- $cacheable_metadata
+ $bubbleable_metadata
// - invalidated when the language negotiation config changes, since
// another query parameter may be used to determine the language.
->addCacheTags($this->config->get('language.negotiation')->getCacheTags())
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
index 0c57d8b84..ef6891c9b 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
@@ -7,10 +7,10 @@
namespace Drupal\language\Plugin\LanguageNegotiation;
-use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Url;
use Drupal\language\LanguageNegotiationMethodBase;
use Drupal\language\LanguageSwitcherInterface;
@@ -123,7 +123,7 @@ class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements In
/**
* Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processOutbound().
*/
- public function processOutbound($path, &$options = array(), Request $request = NULL, CacheableMetadata $cacheable_metadata = NULL) {
+ public function processOutbound($path, &$options = array(), Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
$url_scheme = 'http';
$port = 80;
if ($request) {
@@ -144,8 +144,8 @@ class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements In
if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
if (is_object($options['language']) && !empty($config['prefixes'][$options['language']->getId()])) {
$options['prefix'] = $config['prefixes'][$options['language']->getId()] . '/';
- if ($cacheable_metadata) {
- $cacheable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL]);
+ if ($bubbleable_metadata) {
+ $bubbleable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL]);
}
}
}
@@ -184,8 +184,8 @@ class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements In
// Add Drupal's subfolder from the base_path if there is one.
$options['base_url'] .= rtrim(base_path(), '/');
- if ($cacheable_metadata) {
- $cacheable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL, 'url.site']);
+ if ($bubbleable_metadata) {
+ $bubbleable_metadata->addCacheContexts(['languages:' . LanguageInterface::TYPE_URL, 'url.site']);
}
}
}
diff --git a/core/modules/language/src/ProxyClass/LanguageConverter.php b/core/modules/language/src/ProxyClass/LanguageConverter.php
new file mode 100644
index 000000000..95a14e119
--- /dev/null
+++ b/core/modules/language/src/ProxyClass/LanguageConverter.php
@@ -0,0 +1,92 @@
+container = $container;
+ $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
+ }
+
+ /**
+ * Lazy loads the real service from the container.
+ *
+ * @return object
+ * Returns the constructed real service.
+ */
+ protected function lazyLoadItself()
+ {
+ if (!isset($this->service)) {
+ $this->service = $this->container->get($this->drupalProxyOriginalServiceId);
+ }
+
+ return $this->service;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function convert($value, $definition, $name, array $defaults)
+ {
+ return $this->lazyLoadItself()->convert($value, $definition, $name, $defaults);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function applies($definition, $name, \Symfony\Component\Routing\Route $route)
+ {
+ return $this->lazyLoadItself()->applies($definition, $name, $route);
+ }
+
+ }
+
+}
diff --git a/core/modules/language/src/Tests/LanguageListModuleInstallTest.php b/core/modules/language/src/Tests/LanguageListModuleInstallTest.php
index eae3f1a90..00a76777a 100644
--- a/core/modules/language/src/Tests/LanguageListModuleInstallTest.php
+++ b/core/modules/language/src/Tests/LanguageListModuleInstallTest.php
@@ -34,7 +34,7 @@ class LanguageListModuleInstallTest extends WebTestBase {
$this->drupalLogin($admin_user);
$edit = array();
$edit['modules[Multilingual][language][enable]'] = 'language';
- $this->drupalPostForm('admin/modules', $edit, t('Save configuration'));
+ $this->drupalPostForm('admin/modules', $edit, t('Install'));
$this->assertEqual(\Drupal::state()->get('language_test.language_count_preinstall', 0), 1, 'Using LanguageManager::getLanguages() returns 1 language during Language installation.');
diff --git a/core/modules/language/src/Tests/LanguageUrlRewritingTest.php b/core/modules/language/src/Tests/LanguageUrlRewritingTest.php
index 81602d1f4..f590861b4 100644
--- a/core/modules/language/src/Tests/LanguageUrlRewritingTest.php
+++ b/core/modules/language/src/Tests/LanguageUrlRewritingTest.php
@@ -139,7 +139,7 @@ class LanguageUrlRewritingTest extends WebTestBase {
// Create an absolute French link.
$language = \Drupal::languageManager()->getLanguage('fr');
- $url = Url::fromRoute('', [], [
+ $url = Url::fromRoute('', [], [
'absolute' => TRUE,
'language' => $language,
])->toString();
@@ -149,7 +149,7 @@ class LanguageUrlRewritingTest extends WebTestBase {
$this->assertEqual($url, $expected, 'The right port is used.');
// If we set the port explicitly, it should not be overridden.
- $url = Url::fromRoute('', [], [
+ $url = Url::fromRoute('', [], [
'absolute' => TRUE,
'language' => $language,
'base_url' => $request->getBaseUrl() . ':90',
diff --git a/core/modules/language/tests/src/Unit/LanguageNegotiationUrlTest.php b/core/modules/language/tests/src/Unit/LanguageNegotiationUrlTest.php
index 76d72b552..34abb6105 100644
--- a/core/modules/language/tests/src/Unit/LanguageNegotiationUrlTest.php
+++ b/core/modules/language/tests/src/Unit/LanguageNegotiationUrlTest.php
@@ -8,8 +8,8 @@
namespace Drupal\Tests\language\Unit {
use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Session\UserSession;
use Drupal\Tests\UnitTestCase;
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
@@ -91,10 +91,10 @@ class LanguageNegotiationUrlTest extends UnitTestCase {
$method->setCurrentUser($this->user);
$this->assertEquals($expected_langcode, $method->getLangcode($request));
- $cacheability = new CacheableMetadata();
+ $cacheability = new BubbleableMetadata();
$options = [];
$method->processOutbound('foo', $options, $request, $cacheability);
- $expected_cacheability = new CacheableMetadata();
+ $expected_cacheability = new BubbleableMetadata();
if ($expected_langcode) {
$this->assertSame($prefix . '/', $options['prefix']);
$expected_cacheability->setCacheContexts(['languages:' . LanguageInterface::TYPE_URL]);
@@ -180,10 +180,10 @@ class LanguageNegotiationUrlTest extends UnitTestCase {
$method->setCurrentUser($this->user);
$this->assertEquals($expected_langcode, $method->getLangcode($request));
- $cacheability = new CacheableMetadata();
+ $cacheability = new BubbleableMetadata();
$options = [];
$this->assertSame('foo', $method->processOutbound('foo', $options, $request, $cacheability));
- $expected_cacheability = new CacheableMetadata();
+ $expected_cacheability = new BubbleableMetadata();
if ($expected_langcode !== FALSE && count($domains) > 1) {
$expected_cacheability->setCacheMaxAge(Cache::PERMANENT)->setCacheContexts(['languages:' . LanguageInterface::TYPE_URL, 'url.site']);
}
diff --git a/core/modules/locale/locale.batch.inc b/core/modules/locale/locale.batch.inc
index d06178f11..4b67ddb2a 100644
--- a/core/modules/locale/locale.batch.inc
+++ b/core/modules/locale/locale.batch.inc
@@ -6,6 +6,9 @@
*/
use GuzzleHttp\Exception\RequestException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
/**
* Load the common translation API.
@@ -234,15 +237,20 @@ function locale_translation_batch_fetch_finished($success, $results) {
function locale_translation_http_check($uri) {
$logger = \Drupal::logger('locale');
try {
- $response = \Drupal::httpClient()->head($uri);
+ $actual_uri = NULL;
+ $response = \Drupal::service('http_client_factory')->fromOptions(['allow_redirects' => [
+ 'on_redirect' => function(RequestInterface $request, ResponseInterface $response, UriInterface $request_uri) use (&$actual_uri) {
+ $actual_uri = (string) $request_uri;
+ }
+ ]])->head($uri);
$result = array();
// Return the effective URL if it differs from the requested.
- if ($response->getEffectiveUrl() != $uri) {
- $result['location'] = $response->getEffectiveUrl();
+ if ($actual_uri && $actual_uri !== $uri) {
+ $result['location'] = $actual_uri;
}
- $result['last_modified'] = $response->hasHeader('Last-Modified') ? strtotime($response->getHeader('Last-Modified')) : 0;
+ $result['last_modified'] = $response->hasHeader('Last-Modified') ? strtotime($response->getHeaderLine('Last-Modified')) : 0;
return $result;
}
catch (RequestException $e) {
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_locale_settings.yml b/core/modules/locale/migration_templates/d6_locale_settings.yml
similarity index 85%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_locale_settings.yml
rename to core/modules/locale/migration_templates/d6_locale_settings.yml
index a38bd9221..e9440cc84 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_locale_settings.yml
+++ b/core/modules/locale/migration_templates/d6_locale_settings.yml
@@ -13,7 +13,3 @@ process:
destination:
plugin: config
config_name: locale.settings
-dependencies:
- module:
- - locale
- - migrate_drupal
diff --git a/core/modules/locale/src/Form/TranslateEditForm.php b/core/modules/locale/src/Form/TranslateEditForm.php
index 44e86e72b..bac65c7ee 100644
--- a/core/modules/locale/src/Form/TranslateEditForm.php
+++ b/core/modules/locale/src/Form/TranslateEditForm.php
@@ -123,6 +123,7 @@ class TranslateEditForm extends TranslateFormBase {
for ($i = 0; $i < $plurals; $i++) {
$form['strings'][$string->lid]['translations'][$i] = array(
'#type' => 'textarea',
+ // @todo Should use better labels https://www.drupal.org/node/2499639
'#title' => ($i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form')),
'#rows' => $rows,
'#default_value' => isset($translation_array[$i]) ? $translation_array[$i] : '',
diff --git a/core/modules/locale/src/Form/TranslationStatusForm.php b/core/modules/locale/src/Form/TranslationStatusForm.php
index 6fc338333..d2571399a 100644
--- a/core/modules/locale/src/Form/TranslationStatusForm.php
+++ b/core/modules/locale/src/Form/TranslationStatusForm.php
@@ -99,7 +99,7 @@ class TranslationStatusForm extends FormBase {
),
'status' => array(
'class' => array('description', 'priority-low'),
- 'data' => drupal_render($locale_translation_update_info),
+ 'data' => $locale_translation_update_info,
),
);
if (!empty($update['not_found'])) {
diff --git a/core/modules/locale/src/LocaleLookup.php b/core/modules/locale/src/LocaleLookup.php
index f5b4fc164..d46ae4b2b 100644
--- a/core/modules/locale/src/LocaleLookup.php
+++ b/core/modules/locale/src/LocaleLookup.php
@@ -117,7 +117,7 @@ class LocaleLookup extends CacheCollector {
// for example, strings for admin menu items and settings forms are not
// cached for anonymous users.
$user = \Drupal::currentUser();
- $rids = $user ? implode(':', array_keys($user->getRoles())) : '0';
+ $rids = $user ? implode(':', $user->getRoles()) : '';
$this->cid = "locale:{$this->langcode}:{$this->context}:$rids";
// Getting the roles from the current user might have resulted in t()
diff --git a/core/modules/locale/src/StringBase.php b/core/modules/locale/src/StringBase.php
index eecd50500..d0340ef01 100644
--- a/core/modules/locale/src/StringBase.php
+++ b/core/modules/locale/src/StringBase.php
@@ -190,9 +190,7 @@ abstract class StringBase implements StringInterface {
$storage->save($this);
}
else {
- throw new StringStorageException(SafeMarkup::format('The string cannot be saved because its not bound to a storage: @string', array(
- '@string' => $this->getString(),
- )));
+ throw new StringStorageException('The string cannot be saved because its not bound to a storage: ' . $this->getString());
}
return $this;
}
@@ -206,9 +204,7 @@ abstract class StringBase implements StringInterface {
$storage->delete($this);
}
else {
- throw new StringStorageException(SafeMarkup::format('The string cannot be deleted because its not bound to a storage: @string', array(
- '@string' => $this->getString(),
- )));
+ throw new StringStorageException('The string cannot be deleted because its not bound to a storage: ' . $this->getString());
}
}
return $this;
diff --git a/core/modules/locale/src/StringDatabaseStorage.php b/core/modules/locale/src/StringDatabaseStorage.php
index 06f9f97dd..de624849d 100644
--- a/core/modules/locale/src/StringDatabaseStorage.php
+++ b/core/modules/locale/src/StringDatabaseStorage.php
@@ -199,9 +199,7 @@ class StringDatabaseStorage implements StringStorageInterface {
}
}
else {
- throw new StringStorageException(format_string('The string cannot be deleted because it lacks some key fields: @string', array(
- '@string' => $string->getString(),
- )));
+ throw new StringStorageException('The string cannot be deleted because it lacks some key fields: ' . $string->getString());
}
return $this;
}
@@ -483,9 +481,7 @@ class StringDatabaseStorage implements StringStorageInterface {
->execute();
}
else {
- throw new StringStorageException(format_string('The string cannot be saved: @string', array(
- '@string' => $string->getString(),
- )));
+ throw new StringStorageException('The string cannot be saved: ' . $string->getString());
}
}
@@ -516,9 +512,7 @@ class StringDatabaseStorage implements StringStorageInterface {
->execute();
}
else {
- throw new StringStorageException(format_string('The string cannot be updated: @string', array(
- '@string' => $string->getString(),
- )));
+ throw new StringStorageException('The string cannot be updated: ' . $string->getString());
}
}
diff --git a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
index 64ea79738..39fcccd80 100644
--- a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
+++ b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
@@ -125,7 +125,7 @@ class LocaleConfigTranslationTest extends WebTestBase {
$this->assertFalse($string, 'Configuration strings have been created upon installation.');
// Enable the image module.
- $this->drupalPostForm('admin/modules', array('modules[Field types][image][enable]' => "1"), t('Save configuration'));
+ $this->drupalPostForm('admin/modules', array('modules[Field types][image][enable]' => "1"), t('Install'));
$this->rebuildContainer();
$string = $this->storage->findString(array('source' => 'Medium (220×220)', 'context' => '', 'type' => 'configuration'));
@@ -204,12 +204,12 @@ class LocaleConfigTranslationTest extends WebTestBase {
public function testOptionalConfiguration() {
$this->assertNodeConfig(FALSE, FALSE);
// Enable the node module.
- $this->drupalPostForm('admin/modules', ['modules[Core][node][enable]' => "1"], t('Save configuration'));
+ $this->drupalPostForm('admin/modules', ['modules[Core][node][enable]' => "1"], t('Install'));
$this->drupalPostForm(NULL, [], t('Continue'));
$this->rebuildContainer();
$this->assertNodeConfig(TRUE, FALSE);
// Enable the views module (which node provides some optional config for).
- $this->drupalPostForm('admin/modules', ['modules[Core][views][enable]' => "1"], t('Save configuration'));
+ $this->drupalPostForm('admin/modules', ['modules[Core][views][enable]' => "1"], t('Install'));
$this->rebuildContainer();
$this->assertNodeConfig(TRUE, TRUE);
}
diff --git a/core/modules/locale/src/Tests/LocaleLocaleLookupTest.php b/core/modules/locale/src/Tests/LocaleLocaleLookupTest.php
index ef53b8c89..3870cb0f4 100644
--- a/core/modules/locale/src/Tests/LocaleLocaleLookupTest.php
+++ b/core/modules/locale/src/Tests/LocaleLocaleLookupTest.php
@@ -42,7 +42,7 @@ class LocaleLocaleLookupTest extends WebTestBase {
*/
public function testCircularDependency() {
// Ensure that we can enable early_translation_test on a non-english site.
- $this->drupalPostForm('admin/modules', array('modules[Testing][early_translation_test][enable]' => TRUE), t('Save configuration'));
+ $this->drupalPostForm('admin/modules', array('modules[Testing][early_translation_test][enable]' => TRUE), t('Install'));
$this->assertResponse(200);
}
diff --git a/core/modules/locale/src/Tests/LocaleUpdateTest.php b/core/modules/locale/src/Tests/LocaleUpdateTest.php
index c4f4fe8f1..494c43349 100644
--- a/core/modules/locale/src/Tests/LocaleUpdateTest.php
+++ b/core/modules/locale/src/Tests/LocaleUpdateTest.php
@@ -315,7 +315,7 @@ class LocaleUpdateTest extends LocaleUpdateBase {
$edit = array(
'modules[Testing][locale_test_translate][enable]' => 'locale_test_translate',
);
- $this->drupalPostForm('admin/modules', $edit, t('Save configuration'));
+ $this->drupalPostForm('admin/modules', $edit, t('Install'));
// Check if translations have been imported.
$this->assertRaw(t('One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.',
@@ -350,7 +350,7 @@ class LocaleUpdateTest extends LocaleUpdateBase {
$edit = array(
'modules[Testing][locale_test_translate][enable]' => 'locale_test_translate',
);
- $this->drupalPostForm('admin/modules', $edit, t('Save configuration'));
+ $this->drupalPostForm('admin/modules', $edit, t('Install'));
// Check if there is no Dutch translation yet.
$this->assertTranslation('Extraday', '', 'nl');
@@ -394,7 +394,7 @@ class LocaleUpdateTest extends LocaleUpdateBase {
$edit = array(
'modules[Testing][locale_test_translate][enable]' => 'locale_test_translate',
);
- $this->drupalPostForm('admin/modules', $edit, t('Save configuration'));
+ $this->drupalPostForm('admin/modules', $edit, t('Install'));
// Create a custom language with language code 'xx' and a random
// name.
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateLocaleConfigsTest.php b/core/modules/locale/src/Tests/Migrate/d6/MigrateLocaleConfigsTest.php
similarity index 67%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateLocaleConfigsTest.php
rename to core/modules/locale/src/Tests/Migrate/d6/MigrateLocaleConfigsTest.php
index 721745dc4..7e42bb050 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateLocaleConfigsTest.php
+++ b/core/modules/locale/src/Tests/Migrate/d6/MigrateLocaleConfigsTest.php
@@ -2,19 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateLocaleConfigsTest.
+ * Contains \Drupal\locale\Tests\Migrate\d6\MigrateLocaleConfigsTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\locale\Tests\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to locale.settings.yml.
*
- * @group migrate_drupal
+ * @group locale
*/
class MigrateLocaleConfigsTest extends MigrateDrupal6TestBase {
@@ -32,13 +31,8 @@ class MigrateLocaleConfigsTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
- $migration = entity_load('migration', 'd6_locale_settings');
- $dumps = array(
- $this->getDumpDirectory() . '/Variable.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
+ $this->loadDumps(['Variable.php']);
+ $this->executeMigration('d6_locale_settings');
}
/**
diff --git a/core/modules/locale/tests/src/Unit/LocaleLookupTest.php b/core/modules/locale/tests/src/Unit/LocaleLookupTest.php
index fafcb458c..a65084019 100644
--- a/core/modules/locale/tests/src/Unit/LocaleLookupTest.php
+++ b/core/modules/locale/tests/src/Unit/LocaleLookupTest.php
@@ -111,7 +111,7 @@ class LocaleLookupTest extends UnitTestCase {
$this->cache->expects($this->once())
->method('get')
- ->with('locale:en:irrelevant:0', FALSE);
+ ->with('locale:en:irrelevant:anonymous', FALSE);
$this->storage->expects($this->once())
->method('findTranslation')
@@ -183,7 +183,7 @@ class LocaleLookupTest extends UnitTestCase {
$this->cache->expects($this->once())
->method('get')
- ->with('locale:' . $langcode . ':' . $context . ':0', FALSE);
+ ->with('locale:' . $langcode . ':' . $context . ':anonymous', FALSE);
$locale_lookup = new LocaleLookup($langcode, $context, $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager, $this->requestStack);
$this->assertSame($expected, $locale_lookup->get($string));
diff --git a/core/modules/menu_link_content/config/schema/menu_link_content.schema.yml b/core/modules/menu_link_content/config/schema/menu_link_content.schema.yml
new file mode 100644
index 000000000..ed5f71eed
--- /dev/null
+++ b/core/modules/menu_link_content/config/schema/menu_link_content.schema.yml
@@ -0,0 +1,7 @@
+migrate.source.d6_menu_link:
+ type: migrate_source_sql
+ label: 'Drupal 6 menu link'
+ mapping:
+ constants:
+ type: migrate_entity_constant
+ label: 'Constants'
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_menu_links.yml b/core/modules/menu_link_content/migration_templates/d6_menu_links.yml
similarity index 88%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_menu_links.yml
rename to core/modules/menu_link_content/migration_templates/d6_menu_links.yml
index c98a3a721..67531604e 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_menu_links.yml
+++ b/core/modules/menu_link_content/migration_templates/d6_menu_links.yml
@@ -45,9 +45,3 @@ destination:
migration_dependencies:
required:
- d6_menu
-dependencies:
- config:
- - migrate.migration.d6_menu
- module:
- - menu_link_content
- - migrate_drupal
diff --git a/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php b/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php
index e75d075f2..ba8eb768b 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php
@@ -19,7 +19,10 @@ class MenuLinkContentDeleteForm extends ContentEntityDeleteForm {
* {@inheritdoc}
*/
public function getCancelUrl() {
- return new Url('entity.menu.edit_form', array('menu' => $this->entity->getMenuName()));
+ if ($this->moduleHandler->moduleExists('menu_ui')) {
+ return new Url('entity.menu.edit_form', array('menu' => $this->entity->getMenuName()));
+ }
+ return $this->entity->urlInfo();
}
/**
diff --git a/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php b/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php
index f120b6193..a9a7d2c6a 100644
--- a/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php
+++ b/core/modules/menu_link_content/src/Plugin/Menu/MenuLinkContent.php
@@ -8,7 +8,6 @@
namespace Drupal\menu_link_content\Plugin\Menu;
use Drupal\Component\Plugin\Exception\PluginException;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Menu\MenuLinkBase;
@@ -138,7 +137,7 @@ class MenuLinkContent extends MenuLinkBase implements ContainerFactoryPluginInte
$entity = reset($loaded_entities);
}
if (!$entity) {
- throw new PluginException(SafeMarkup::format('Entity not found through the menu link plugin definition and could not fallback on UUID @uuid', array('@uuid' => $uuid)));
+ throw new PluginException("Entity not found through the menu link plugin definition and could not fallback on UUID '$uuid'");
}
// Clone the entity object to avoid tampering with the static cache.
$this->entity = clone $entity;
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/MenuLink.php b/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLink.php
similarity index 96%
rename from core/modules/migrate_drupal/src/Plugin/migrate/source/d6/MenuLink.php
rename to core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLink.php
index 4711477d3..8e285ad62 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/MenuLink.php
+++ b/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLink.php
@@ -2,10 +2,10 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Plugin\migrate\source\d6\MenuLink.
+ * Contains \Drupal\menu_link_content\Plugin\migrate\source\d6\MenuLink.
*/
-namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
+namespace Drupal\menu_link_content\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
use Drupal\migrate\Row;
diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php
index 7980c7720..825f49388 100644
--- a/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php
+++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php
@@ -8,9 +8,9 @@
namespace Drupal\menu_link_content\Tests;
use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Menu\MenuTreeParameters;
+use Drupal\Core\Render\BubbleableMetadata;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\Entity\User;
@@ -19,7 +19,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
- * Ensures that rendered menu links bubble the necessary cacheability metadata
+ * Ensures that rendered menu links bubble the necessary bubbleable metadata
* for outbound path/route processing.
*
* @group menu_link_content
@@ -47,7 +47,7 @@ class MenuLinkContentCacheabilityBubblingTest extends KernelTestBase {
}
/**
- * Tests bubbling of menu links' outbound route/path processing cacheability.
+ * Tests bubbleable metadata of menu links' outbound route/path processing.
*/
public function testOutboundPathAndRouteProcessing() {
\Drupal::service('router.builder')->rebuild();
@@ -66,10 +66,10 @@ class MenuLinkContentCacheabilityBubblingTest extends KernelTestBase {
$renderer = \Drupal::service('renderer');
- $default_menu_cacheability = (new CacheableMetadata())
+ $default_menu_cacheability = (new BubbleableMetadata())
->setCacheMaxAge(Cache::PERMANENT)
->setCacheTags(['config:system.menu.tools'])
- ->setCacheContexts(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme']);
+ ->setCacheContexts(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions']);
User::create(['uid' => 1, 'name' => $this->randomString()])->save();
User::create(['uid' => 2, 'name' => $this->randomString()])->save();
@@ -87,26 +87,26 @@ class MenuLinkContentCacheabilityBubblingTest extends KernelTestBase {
// \Drupal\Core\RouteProcessor\RouteProcessorCurrent: 'route' cache context.
[
'uri' => 'route:',
- 'cacheability' => (new CacheableMetadata())->setCacheContexts(['route']),
+ 'cacheability' => (new BubbleableMetadata())->setCacheContexts(['route']),
],
- // \Drupal\Core\Access\RouteProcessorCsrf: max-age = 0.
+ // \Drupal\Core\Access\RouteProcessorCsrf: placeholder.
[
'uri' => 'route:outbound_processing_test.route.csrf',
- 'cacheability' => (new CacheableMetadata())->setCacheMaxAge(0),
+ 'cacheability' => (new BubbleableMetadata())->setCacheContexts(['session'])->setAttachments(['placeholders' => []]),
],
// \Drupal\Core\PathProcessor\PathProcessorFront: permanently cacheable.
[
'uri' => 'internal:/',
- 'cacheability' => (new CacheableMetadata()),
+ 'cacheability' => (new BubbleableMetadata()),
],
// \Drupal\url_alter_test\PathProcessorTest: user entity's cache tags.
[
'uri' => 'internal:/user/1',
- 'cacheability' => (new CacheableMetadata())->setCacheTags(User::load(1)->getCacheTags()),
+ 'cacheability' => (new BubbleableMetadata())->setCacheTags(User::load(1)->getCacheTags()),
],
[
'uri' => 'internal:/user/2',
- 'cacheability' => (new CacheableMetadata())->setCacheTags(User::load(2)->getCacheTags()),
+ 'cacheability' => (new BubbleableMetadata())->setCacheTags(User::load(2)->getCacheTags()),
],
];
@@ -122,7 +122,7 @@ class MenuLinkContentCacheabilityBubblingTest extends KernelTestBase {
$renderer->renderRoot($build);
$expected_cacheability = $default_menu_cacheability->merge($expectation['cacheability']);
- $this->assertEqual($expected_cacheability, CacheableMetadata::createFromRenderArray($build));
+ $this->assertEqual($expected_cacheability, BubbleableMetadata::createFromRenderArray($build));
$menu_link_content->delete();
}
@@ -130,7 +130,7 @@ class MenuLinkContentCacheabilityBubblingTest extends KernelTestBase {
// Now test them all together in one menu: the rendered menu's cacheability
// metadata should be the combination of the cacheability of all links, and
// thus of all tested outbound path & route processors.
- $expected_cacheability = new CacheableMetadata();
+ $expected_cacheability = new BubbleableMetadata();
foreach ($test_cases as $expectation) {
$menu_link_content = MenuLinkContent::create([
'link' => ['uri' => $expectation['uri']],
@@ -143,7 +143,7 @@ class MenuLinkContentCacheabilityBubblingTest extends KernelTestBase {
$build = $menu_tree->build($tree);
$renderer->renderRoot($build);
$expected_cacheability = $expected_cacheability->merge($default_menu_cacheability);
- $this->assertEqual($expected_cacheability, CacheableMetadata::createFromRenderArray($build));
+ $this->assertEqual($expected_cacheability, BubbleableMetadata::createFromRenderArray($build));
}
}
diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentDeleteFormTest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeleteFormTest.php
new file mode 100644
index 000000000..c411f8310
--- /dev/null
+++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeleteFormTest.php
@@ -0,0 +1,67 @@
+drupalCreateUser(['administer menu']);
+ $this->drupalLogin($web_user);
+ }
+
+ /**
+ * Tests the MenuLinkContentDeleteForm class.
+ */
+ public function testMenuLinkContentDeleteForm() {
+ // Add new menu item.
+ $this->drupalPostForm(
+ 'admin/structure/menu/manage/admin/add',
+ [
+ 'title[0][value]' => t('Front page'),
+ 'link[0][uri]' => '',
+ ],
+ t('Save')
+ );
+ $this->assertText(t('The menu link has been saved.'));
+
+ $menu_link = MenuLinkContent::load(1);
+ $this->drupalGet($menu_link->urlInfo('delete-form'));
+ $this->assertRaw(t('Are you sure you want to delete the custom menu link %name?', ['%name' => $menu_link->label()]));
+ $this->assertLink(t('Cancel'));
+ // Make sure cancel link points to link edit
+ $this->assertLinkByHref($menu_link->url('edit-form'));
+
+ \Drupal::service('module_installer')->install(['menu_ui']);
+ // Make sure cancel URL points to menu_ui route now.
+ $this->drupalGet($menu_link->urlInfo('delete-form'));
+ $menu = Menu::load($menu_link->getMenuName());
+ $this->assertLinkByHref($menu->url('edit-form'));
+ $this->drupalPostForm(NULL, [], t('Delete'));
+ $this->assertRaw(t('The menu link %title has been deleted.', ['%title' => $menu_link->label()]));
+ }
+
+}
diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php
index c7a1ae1fc..b7238f8cb 100644
--- a/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php
+++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php
@@ -7,7 +7,9 @@
namespace Drupal\menu_link_content\Tests;
+use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Menu\MenuTreeParameters;
+use Drupal\Core\StringTranslation\TranslationWrapper;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\simpletest\KernelTestBase;
use Symfony\Component\Routing\Route;
@@ -45,7 +47,7 @@ class MenuLinkContentDeriverTest extends KernelTestBase {
// Set up a custom menu link pointing to a specific path.
MenuLinkContent::create([
- 'title' => 'Example',
+ 'title' => '',
'link' => [['uri' => 'internal:/example-path']],
'menu_name' => 'tools',
])->save();
@@ -67,6 +69,10 @@ class MenuLinkContentDeriverTest extends KernelTestBase {
/** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
$tree_element = reset($menu_tree);
$this->assertEqual('route_name_2', $tree_element->link->getRouteName());
+ $title = $tree_element->link->getTitle();
+ $this->assertFalse($title instanceof TranslationWrapper);
+ $this->assertIdentical('', $title);
+ $this->assertFalse(SafeMarkup::isSafe($title));
}
}
diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentFormTest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentFormTest.php
index 1c9d10ee4..9395d662c 100644
--- a/core/modules/menu_link_content/src/Tests/MenuLinkContentFormTest.php
+++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentFormTest.php
@@ -22,7 +22,6 @@ class MenuLinkContentFormTest extends WebTestBase {
*/
public static $modules = array(
'menu_link_content',
- 'menu_ui',
);
/**
diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
index b0688081d..615da6bdb 100644
--- a/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
+++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
@@ -17,6 +17,11 @@ use Drupal\menu_link_content\Entity\MenuLinkContent;
*/
class MenuLinkContentTranslationUITest extends ContentTranslationUITestBase {
+ /**
+ * {inheritdoc}
+ */
+ protected $defaultCacheContexts = ['languages:language_interface', 'theme', 'user.permissions', 'user.roles:authenticated'];
+
/**
* Modules to enable.
*
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateMenuLinkTest.php b/core/modules/menu_link_content/src/Tests/Migrate/d6/MigrateMenuLinkTest.php
similarity index 86%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateMenuLinkTest.php
rename to core/modules/menu_link_content/src/Tests/Migrate/d6/MigrateMenuLinkTest.php
index 18aea259d..527c35488 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateMenuLinkTest.php
+++ b/core/modules/menu_link_content/src/Tests/Migrate/d6/MigrateMenuLinkTest.php
@@ -2,18 +2,17 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateMenuLinkTest.
+ * Contains \Drupal\menu_link_content\Tests\Migrate\d6\MigrateMenuLinkTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\menu_link_content\Tests\Migrate\d6;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Menu link migration.
*
- * @group migrate_drupal
+ * @group menu_link_content
*/
class MigrateMenuLinkTest extends MigrateDrupal6TestBase {
@@ -43,13 +42,8 @@ class MigrateMenuLinkTest extends MigrateDrupal6TestBase {
),
));
- $migration = entity_load('migration', 'd6_menu_links');
- $dumps = array(
- $this->getDumpDirectory() . '/MenuLinks.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
+ $this->loadDumps(['MenuLinks.php']);
+ $this->executeMigration('d6_menu_links');
}
public function testMenuLinks() {
diff --git a/core/modules/migrate_drupal/tests/src/Unit/source/d6/MenuLinkSourceTest.php b/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/d6/MenuLinkSourceTest.php
similarity index 91%
rename from core/modules/migrate_drupal/tests/src/Unit/source/d6/MenuLinkSourceTest.php
rename to core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/d6/MenuLinkSourceTest.php
index fb9e0e736..e641c09e6 100644
--- a/core/modules/migrate_drupal/tests/src/Unit/source/d6/MenuLinkSourceTest.php
+++ b/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/d6/MenuLinkSourceTest.php
@@ -2,23 +2,23 @@
/**
* @file
- * Contains \Drupal\Tests\migrate_drupal\Unit\source\d6\MenuLinkSourceTest.
+ * Contains \Drupal\Tests\menu_link_content\Unit\Plugin\migrate\source\d6\MenuLinkSourceTest.
*/
-namespace Drupal\Tests\migrate_drupal\Unit\source\d6;
+namespace Drupal\Tests\menu_link_content\Unit\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase;
/**
* Tests D6 menu link source plugin.
*
- * @group migrate_drupal
+ * @group menu_link_content
*/
class MenuLinkSourceTest extends MigrateSqlSourceTestCase {
// The plugin system is not working during unit testing so the source plugin
// class needs to be manually specified.
- const PLUGIN_CLASS = 'Drupal\migrate_drupal\Plugin\migrate\source\d6\MenuLink';
+ const PLUGIN_CLASS = 'Drupal\menu_link_content\Plugin\migrate\source\d6\MenuLink';
// The fake Migration configuration entity.
protected $migrationConfiguration = array(
diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module
index 0c097ba36..f26dade62 100644
--- a/core/modules/menu_ui/menu_ui.module
+++ b/core/modules/menu_ui/menu_ui.module
@@ -415,7 +415,7 @@ function menu_ui_form_node_type_form_alter(&$form, FormStateInterface $form_stat
);
$options_cacheability->applyTo($form['menu']['menu_parent']);
- $form['actions']['submit']['#validate'][] = 'menu_ui_form_node_type_form_validate';
+ $form['#validate'][] = 'menu_ui_form_node_type_form_validate';
$form['#entity_builders'][] = 'menu_ui_form_node_type_form_builder';
}
diff --git a/core/modules/menu_ui/menu_ui.permissions.yml b/core/modules/menu_ui/menu_ui.permissions.yml
deleted file mode 100644
index 9bc104f53..000000000
--- a/core/modules/menu_ui/menu_ui.permissions.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-administer menu:
- title: 'Administer menus and menu items'
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_menu_settings.yml b/core/modules/menu_ui/migration_templates/d6_menu_settings.yml
similarity index 88%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_menu_settings.yml
rename to core/modules/menu_ui/migration_templates/d6_menu_settings.yml
index 8ec039d84..9f332fb67 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_menu_settings.yml
+++ b/core/modules/menu_ui/migration_templates/d6_menu_settings.yml
@@ -15,7 +15,3 @@ process:
destination:
plugin: config
config_name: menu_ui.settings
-dependencies:
- module:
- - menu_ui
- - migrate_drupal
diff --git a/core/modules/menu_ui/src/Controller/MenuController.php b/core/modules/menu_ui/src/Controller/MenuController.php
index 72870547f..560bb1843 100644
--- a/core/modules/menu_ui/src/Controller/MenuController.php
+++ b/core/modules/menu_ui/src/Controller/MenuController.php
@@ -7,7 +7,7 @@
namespace Drupal\menu_ui\Controller;
-use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Menu\MenuParentFormSelectorInterface;
use Drupal\system\MenuInterface;
@@ -77,7 +77,7 @@ class MenuController extends ControllerBase {
* The menu label.
*/
public function menuTitle(MenuInterface $menu) {
- return Xss::filter($menu->label());
+ return SafeMarkup::xssFilter($menu->label());
}
}
diff --git a/core/modules/menu_ui/src/MenuListBuilder.php b/core/modules/menu_ui/src/MenuListBuilder.php
index d93f94516..95d029c4f 100644
--- a/core/modules/menu_ui/src/MenuListBuilder.php
+++ b/core/modules/menu_ui/src/MenuListBuilder.php
@@ -39,7 +39,7 @@ class MenuListBuilder extends ConfigEntityListBuilder {
'data' => $this->getLabel($entity),
'class' => array('menu-label'),
);
- $row['description'] = Xss::filterAdmin($entity->getDescription());
+ $row['description']['data'] = ['#markup' => $entity->getDescription()];
return $row + parent::buildRow($entity);
}
diff --git a/core/modules/menu_ui/src/Plugin/Menu/LocalAction/MenuLinkAdd.php b/core/modules/menu_ui/src/Plugin/Menu/LocalAction/MenuLinkAdd.php
index 81152c25d..606dbaaf5 100644
--- a/core/modules/menu_ui/src/Plugin/Menu/LocalAction/MenuLinkAdd.php
+++ b/core/modules/menu_ui/src/Plugin/Menu/LocalAction/MenuLinkAdd.php
@@ -8,7 +8,7 @@
namespace Drupal\menu_ui\Plugin\Menu\LocalAction;
use Drupal\Core\Menu\LocalActionDefault;
-use Drupal\Core\Routing\RedirectDestination;
+use Drupal\Core\Routing\RedirectDestinationInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -21,7 +21,7 @@ class MenuLinkAdd extends LocalActionDefault {
/**
* The redirect destination.
*
- * @var \Drupal\Core\Routing\RedirectDestination
+ * @var \Drupal\Core\Routing\RedirectDestinationInterface
*/
private $redirectDestination;
@@ -36,10 +36,10 @@ class MenuLinkAdd extends LocalActionDefault {
* The plugin implementation definition.
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
* The route provider to load routes by name.
- * @param \Drupal\Core\Routing\RedirectDestination $redirect_destination
+ * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
* The redirect destination.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, RedirectDestination $redirect_destination) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, RedirectDestinationInterface $redirect_destination) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider);
$this->redirectDestination = $redirect_destination;
diff --git a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
index adff9a033..520627ed7 100644
--- a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
@@ -110,7 +110,7 @@ class MenuCacheTagsTest extends PageCacheTagsTestBase {
$this->verifyPageCache($url, 'MISS');
// Verify a cache hit.
- $this->verifyPageCache($url, 'HIT', ['config:block_list', 'rendered']);
+ $this->verifyPageCache($url, 'HIT', ['config:block_list', 'config:user.role.anonymous', 'rendered']);
}
}
diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php
index 3a1bac557..34dacc5f6 100644
--- a/core/modules/menu_ui/src/Tests/MenuTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuTest.php
@@ -219,6 +219,7 @@ class MenuTest extends MenuWebTestBase {
// Confirm that the custom menu block is available.
$this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default'));
+ $this->clickLinkPartialName('Place block');
$this->assertText($label);
// Enable the block.
@@ -532,6 +533,7 @@ class MenuTest extends MenuWebTestBase {
// Make sure menu shows up with new name in block addition.
$default_theme = $this->config('system.theme')->get('default');
$this->drupalget('admin/structure/block/list/' . $default_theme);
+ $this->clickLinkPartialName('Place block');
$this->assertText($edit['label']);
}
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateMenuConfigsTest.php b/core/modules/menu_ui/src/Tests/Migrate/d6/MigrateMenuConfigsTest.php
similarity index 64%
rename from core/modules/migrate_drupal/src/Tests/d6/MigrateMenuConfigsTest.php
rename to core/modules/menu_ui/src/Tests/Migrate/d6/MigrateMenuConfigsTest.php
index d12a9e6b7..f6182fe51 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateMenuConfigsTest.php
+++ b/core/modules/menu_ui/src/Tests/Migrate/d6/MigrateMenuConfigsTest.php
@@ -2,19 +2,18 @@
/**
* @file
- * Contains \Drupal\migrate_drupal\Tests\d6\MigrateMenuConfigsTest.
+ * Contains \Drupal\menu_ui\Tests\Migrate\d6\MigrateMenuConfigsTest.
*/
-namespace Drupal\migrate_drupal\Tests\d6;
+namespace Drupal\menu_ui\Tests\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
-use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to menu_ui.settings.yml.
*
- * @group migrate_drupal
+ * @group menu_ui
*/
class MigrateMenuConfigsTest extends MigrateDrupal6TestBase {
@@ -32,13 +31,8 @@ class MigrateMenuConfigsTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
- $migration = entity_load('migration', 'd6_menu_settings');
- $dumps = array(
- $this->getDumpDirectory() . '/Variable.php',
- );
- $this->prepare($migration, $dumps);
- $executable = new MigrateExecutable($migration, $this);
- $executable->import();
+ $this->loadDumps(['Variable.php']);
+ $this->executeMigration('d6_menu_settings');
}
/**
diff --git a/core/modules/migrate/migrate.services.yml b/core/modules/migrate/migrate.services.yml
index 3f2d32d56..c4e52e0e5 100644
--- a/core/modules/migrate/migrate.services.yml
+++ b/core/modules/migrate/migrate.services.yml
@@ -5,6 +5,9 @@ services:
- { name: cache.bin }
factory: cache_factory:get
arguments: [migrate]
+ migrate.template_storage:
+ class: Drupal\migrate\MigrateTemplateStorage
+ arguments: ['@module_handler']
plugin.manager.migrate.source:
class: Drupal\migrate\Plugin\MigratePluginManager
arguments: [source, '@container.namespaces', '@cache.discovery', '@module_handler', 'Drupal\migrate\Annotation\MigrateSource']
diff --git a/core/modules/migrate/src/Entity/Migration.php b/core/modules/migrate/src/Entity/Migration.php
index e3f920cb5..c92f97542 100644
--- a/core/modules/migrate/src/Entity/Migration.php
+++ b/core/modules/migrate/src/Entity/Migration.php
@@ -7,7 +7,6 @@
namespace Drupal\migrate\Entity;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\MigrateException;
@@ -381,7 +380,7 @@ class Migration extends ConfigEntityBase implements MigrationInterface, Requirem
}
}
if ($missing_migrations) {
- throw new RequirementsException(SafeMarkup::format('Missing migrations @requirements.', ['@requirements' => implode(', ', $missing_migrations)]), ['requirements' => $missing_migrations]);
+ throw new RequirementsException('Missing migrations ' . implode(', ', $missing_migrations) . '.', ['requirements' => $missing_migrations]);
}
}
diff --git a/core/modules/migrate/src/MigrateTemplateStorage.php b/core/modules/migrate/src/MigrateTemplateStorage.php
new file mode 100644
index 000000000..d08f1aa8b
--- /dev/null
+++ b/core/modules/migrate/src/MigrateTemplateStorage.php
@@ -0,0 +1,102 @@
+moduleHandler = $module_handler;
+ $this->directory = $directory;
+ }
+
+ /**
+ * Find all migration templates with the specified tag.
+ *
+ * @param $tag
+ * The tag to match.
+ *
+ * @return array
+ * Any templates (parsed YAML config) that matched, keyed by the ID.
+ */
+ public function findTemplatesByTag($tag) {
+ $templates = $this->getAllTemplates();
+ $matched_templates = [];
+ foreach ($templates as $template_name => $template) {
+ if (!empty($template['migration_tags'])) {
+ if (in_array($tag, $template['migration_tags'])) {
+ $matched_templates[$template_name] = $template;
+ }
+ }
+ }
+ return $matched_templates;
+ }
+
+ /**
+ * Retrieve a template given a specific name.
+ *
+ * @param string $name
+ * A migration template name.
+ *
+ * @return NULL|array
+ * A parsed migration template, or NULL if it doesn't exist.
+ */
+ public function getTemplateByName($name) {
+ $templates = $this->getAllTemplates();
+ return isset($templates[$name]) ? $templates[$name] : NULL;
+ }
+
+ /**
+ * Retrieves all migration templates belonging to enabled extensions.
+ *
+ * @return array
+ * Array of parsed templates, keyed by the fully-qualified id.
+ */
+ public function getAllTemplates() {
+ $templates = [];
+ foreach ($this->moduleHandler->getModuleDirectories() as $directory) {
+ $full_directory = $directory . '/' . $this->directory;
+ if (file_exists($full_directory)) {
+ $files = scandir($full_directory);
+ foreach ($files as $file) {
+ if ($file[0] !== '.' && fnmatch('*.yml', $file)) {
+ $templates[basename($file, '.yml')] = Yaml::decode(file_get_contents("$full_directory/$file"));
+ }
+ }
+ }
+ }
+
+ return $templates;
+ }
+
+}
diff --git a/core/modules/migrate/src/Plugin/MigratePluginManager.php b/core/modules/migrate/src/Plugin/MigratePluginManager.php
index f6eee47fa..84f227843 100644
--- a/core/modules/migrate/src/Plugin/MigratePluginManager.php
+++ b/core/modules/migrate/src/Plugin/MigratePluginManager.php
@@ -70,20 +70,4 @@ class MigratePluginManager extends DefaultPluginManager {
return $plugin;
}
- /**
- * Helper for the plugin type to interface map.
- *
- * @return array
- * An array map from plugin type to interface.
- */
- protected function getPluginInterfaceMap() {
- return [
- 'destination' => 'Drupal\migrate\Plugin\MigrateDestinationInterface',
- 'process' => 'Drupal\migrate\Plugin\MigrateProcessInterface',
- 'source' => 'Drupal\migrate\Plugin\MigrateSourceInterface',
- 'id_map' => 'Drupal\migrate\Plugin\MigrateIdMapInterface',
- 'entity_field' => 'Drupal\migrate\Plugin\MigrateEntityDestinationFieldInterface',
- ];
- }
-
}
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityFile.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityFile.php
index f945164f6..57d690925 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityFile.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityFile.php
@@ -7,7 +7,6 @@
namespace Drupal\migrate\Plugin\migrate\destination;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\File\FileSystemInterface;
@@ -83,7 +82,7 @@ class EntityFile extends EntityContentBase {
// Ensure the source file exists, if it's a local URI or path.
if ($this->isLocalUri($source) && !file_exists($source)) {
- throw new MigrateException(SafeMarkup::format('File @source does not exist.', ['@source' => $source]));
+ throw new MigrateException("File '$source' does not exist.");
}
// If the start and end file is exactly the same, there is nothing to do.
@@ -99,7 +98,7 @@ class EntityFile extends EntityContentBase {
$success = $this->writeFile($source, $destination, $replace);
}
else {
- throw new MigrateException(SafeMarkup::format('Could not create directory @dir', ['@dir' => $dir]));
+ throw new MigrateException("Could not create directory '$dir'");
}
}
@@ -107,7 +106,7 @@ class EntityFile extends EntityContentBase {
return parent::import($row, $old_destination_id_values);
}
else {
- throw new MigrateException(SafeMarkup::format('File %source could not be copied to %destination.', ['%source' => $source, '%destination' => $destination]));
+ throw new MigrateException("File $source could not be copied to $destination.");
}
}
@@ -121,7 +120,7 @@ class EntityFile extends EntityContentBase {
* @param integer $replace
* FILE_EXISTS_REPLACE (default) or FILE_EXISTS_RENAME.
*
- * @return boolean
+ * @return bool
* TRUE on success, FALSE on failure.
*/
protected function writeFile($source, $destination, $replace = FILE_EXISTS_REPLACE) {
@@ -164,7 +163,7 @@ class EntityFile extends EntityContentBase {
* @param string $uri
* The URI or path.
*
- * @return boolean|string
+ * @return string|false
* The directory component of the path or URI, or FALSE if it could not
* be determined.
*/
@@ -187,7 +186,7 @@ class EntityFile extends EntityContentBase {
* @param string $destination
* The destination URI.
*
- * @return boolean
+ * @return bool
* TRUE if the source and destination URIs refer to the same physical path,
* otherwise FALSE.
*/
@@ -210,7 +209,7 @@ class EntityFile extends EntityContentBase {
* @param string $uri
* The URI or path to test.
*
- * @return boolean
+ * @return bool
*/
protected function isLocalUri($uri) {
$scheme = $this->fileSystem->uriScheme($uri);
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/Null.php b/core/modules/migrate/src/Plugin/migrate/destination/NullDestination.php
similarity index 82%
rename from core/modules/migrate/src/Plugin/migrate/destination/Null.php
rename to core/modules/migrate/src/Plugin/migrate/destination/NullDestination.php
index d5e6ba2db..1bffa9ea4 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/Null.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/NullDestination.php
@@ -2,7 +2,7 @@
/**
* @file
- * Contains \Drupal\migrate\Plugin\migrate\destination\Null.
+ * Contains \Drupal\migrate\Plugin\migrate\destination\NullDestination.
*/
namespace Drupal\migrate\Plugin\migrate\destination;
@@ -16,7 +16,7 @@ use Drupal\migrate\Row;
* requirements_met = false
* )
*/
-class Null extends DestinationBase {
+class NullDestination extends DestinationBase {
/**
* {@inheritdoc}
diff --git a/core/modules/migrate/src/Plugin/migrate/process/Concat.php b/core/modules/migrate/src/Plugin/migrate/process/Concat.php
index 6e45e5d02..174818d80 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/Concat.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/Concat.php
@@ -7,7 +7,6 @@
namespace Drupal\migrate\Plugin\migrate\process;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\migrate\MigrateException;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
@@ -34,7 +33,7 @@ class Concat extends ProcessPluginBase {
return implode($delimiter, $value);
}
else {
- throw new MigrateException(sprintf('%s is not an array', SafeMarkup::checkPlain(var_export($value, TRUE))));
+ throw new MigrateException(sprintf('%s is not an array', var_export($value, TRUE)));
}
}
diff --git a/core/modules/migrate/src/Plugin/migrate/source/DummyQueryTrait.php b/core/modules/migrate/src/Plugin/migrate/source/DummyQueryTrait.php
new file mode 100644
index 000000000..42a9943bb
--- /dev/null
+++ b/core/modules/migrate/src/Plugin/migrate/source/DummyQueryTrait.php
@@ -0,0 +1,29 @@
+select(uniqid(), 's')
+ ->range(0, 1);
+ $query->addExpression('1');
+ return $query;
+ }
+
+}
diff --git a/core/modules/migrate/src/Tests/EntityFileTest.php b/core/modules/migrate/src/Tests/EntityFileTest.php
index 509e98b6f..718e0a750 100644
--- a/core/modules/migrate/src/Tests/EntityFileTest.php
+++ b/core/modules/migrate/src/Tests/EntityFileTest.php
@@ -94,7 +94,7 @@ class EntityFileTest extends KernelTestBase {
$this->fail('Expected Drupal\migrate\MigrateException when importing ' . $destination);
}
catch (MigrateException $e) {
- $this->assertIdentical($e->getMessage(), 'File ' . $destination . ' does not exist.');
+ $this->assertIdentical($e->getMessage(), "File '$destination' does not exist.");
}
}
diff --git a/core/modules/migrate/src/Tests/MigrateTestBase.php b/core/modules/migrate/src/Tests/MigrateTestBase.php
index 967f7726c..538db5596 100644
--- a/core/modules/migrate/src/Tests/MigrateTestBase.php
+++ b/core/modules/migrate/src/Tests/MigrateTestBase.php
@@ -8,7 +8,9 @@
namespace Drupal\migrate\Tests;
use Drupal\Core\Database\Database;
+use Drupal\migrate\Entity\Migration;
use Drupal\migrate\Entity\MigrationInterface;
+use Drupal\migrate\MigrateExecutable;
use Drupal\migrate\MigrateMessageInterface;
use Drupal\migrate\Row;
use Drupal\simpletest\KernelTestBase;
@@ -43,6 +45,13 @@ abstract class MigrateTestBase extends KernelTestBase implements MigrateMessageI
*/
protected $migrateMessages;
+ /**
+ * The primary migration being tested.
+ *
+ * @var \Drupal\migrate\Entity\MigrationInterface
+ */
+ protected $migration;
+
public static $modules = array('migrate');
/**
@@ -73,21 +82,6 @@ abstract class MigrateTestBase extends KernelTestBase implements MigrateMessageI
parent::tearDown();
}
- /**
- * Prepare the migration.
- *
- * @param \Drupal\migrate\Entity\MigrationInterface $migration
- * The migration object.
- * @param array $files
- * An array of files.
- */
- protected function prepare(MigrationInterface $migration, array $files = array()) {
- $this->loadDumps($files);
- if ($this instanceof MigrateDumpAlterInterface) {
- static::migrateDumpAlter($this);
- }
- }
-
/**
* Load Drupal 6 database dumps to be used.
*
@@ -96,7 +90,7 @@ abstract class MigrateTestBase extends KernelTestBase implements MigrateMessageI
* @param string $method
* The name of the method in the dump class to use. Defaults to load.
*/
- protected function loadDumps($files, $method = 'load') {
+ protected function loadDumps(array $files, $method = 'load') {
// Load the database from the portable PHP dump.
// The files may be gzipped.
foreach ($files as $file) {
@@ -137,6 +131,25 @@ abstract class MigrateTestBase extends KernelTestBase implements MigrateMessageI
}
}
+ /**
+ * Executes a single migration.
+ *
+ * @param string|\Drupal\migrate\Entity\MigrationInterface $migration
+ * The migration to execute, or its ID.
+ */
+ protected function executeMigration($migration) {
+ if (is_string($migration)) {
+ $this->migration = Migration::load($migration);
+ }
+ else {
+ $this->migration = $migration;
+ }
+ if ($this instanceof MigrateDumpAlterInterface) {
+ static::migrateDumpAlter($this);
+ }
+ (new MigrateExecutable($this->migration, $this))->import();
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/core/modules/migrate/src/Tests/TemplateTest.php b/core/modules/migrate/src/Tests/TemplateTest.php
new file mode 100644
index 000000000..15c230557
--- /dev/null
+++ b/core/modules/migrate/src/Tests/TemplateTest.php
@@ -0,0 +1,78 @@
+findTemplatesByTag("Template Test");
+ $expected_url = [
+ 'id' => 'url_template',
+ 'label' => 'Template test - url',
+ 'migration_tags' => ['Template Test'],
+ 'source' => ['plugin' => 'empty'],
+ 'process' => ['src' => 'foobar'],
+ 'destination' => ['plugin' => 'url_alias'],
+ ];
+ $expected_node = [
+ 'id' => 'node_template',
+ 'label' => 'Template test - node',
+ 'migration_tags' => ['Template Test'],
+ 'source' => ['plugin' => 'empty'],
+ 'process' => ['src' => 'barfoo'],
+ 'destination' => ['plugin' => 'entity:node'],
+ ];
+ $this->assertIdentical($migration_templates['migrate.migration.url_template'], $expected_url);
+ $this->assertIdentical($migration_templates['migrate.migration.node_template'], $expected_node);
+ $this->assertFalse(isset($migration_templates['migrate.migration.other_template']));
+ }
+
+ /**
+ * Tests retrieving a template by name.
+ */
+ public function testGetTemplateByName() {
+ /** @var \Drupal\migrate\MigrateTemplateStorage $template_storage */
+ $template_storage = \Drupal::service('migrate.template_storage');
+
+ $expected_url = [
+ 'id' => 'url_template',
+ 'label' => 'Template test - url',
+ 'migration_tags' => ['Template Test'],
+ 'source' => ['plugin' => 'empty'],
+ 'process' => ['src' => 'foobar'],
+ 'destination' => ['plugin' => 'url_alias'],
+ ];
+ $expected_node = [
+ 'id' => 'node_template',
+ 'label' => 'Template test - node',
+ 'migration_tags' => ['Template Test'],
+ 'source' => ['plugin' => 'empty'],
+ 'process' => ['src' => 'barfoo'],
+ 'destination' => ['plugin' => 'entity:node'],
+ ];
+ $this->assertIdentical($template_storage->getTemplateByName('migrate.migration.url_template'), $expected_url);
+ $this->assertIdentical($template_storage->getTemplateByName('migrate.migration.node_template'), $expected_node);
+ $this->assertNull($template_storage->getTemplateByName('migrate.migration.dne'));
+ }
+
+}
diff --git a/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.node_template.yml b/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.node_template.yml
new file mode 100644
index 000000000..6eead8b70
--- /dev/null
+++ b/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.node_template.yml
@@ -0,0 +1,10 @@
+id: node_template
+label: Template test - node
+migration_tags:
+ - Template Test
+source:
+ plugin: empty
+process:
+ src: barfoo
+destination:
+ plugin: entity:node
diff --git a/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.other_template.yml b/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.other_template.yml
new file mode 100644
index 000000000..3d974f007
--- /dev/null
+++ b/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.other_template.yml
@@ -0,0 +1,10 @@
+id: other_template
+label: Template with a different tag
+migration_tags:
+ - Different Template Test
+source:
+ plugin: empty
+process:
+ src: raboof
+destination:
+ plugin: entity:user
diff --git a/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.url_template.yml b/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.url_template.yml
new file mode 100644
index 000000000..fd8211945
--- /dev/null
+++ b/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.url_template.yml
@@ -0,0 +1,10 @@
+id: url_template
+label: Template test - url
+migration_tags:
+ - Template Test
+source:
+ plugin: empty
+process:
+ src: foobar
+destination:
+ plugin: url_alias
diff --git a/core/modules/migrate/tests/modules/template_test/template_test.info.yml b/core/modules/migrate/tests/modules/template_test/template_test.info.yml
new file mode 100644
index 000000000..d396c63d2
--- /dev/null
+++ b/core/modules/migrate/tests/modules/template_test/template_test.info.yml
@@ -0,0 +1,5 @@
+name: 'Migration template test'
+type: module
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php b/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php
index 98180d51a..e644a0460 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php
@@ -58,6 +58,13 @@ abstract class MigrateSqlSourceTestCase extends MigrateTestCase {
*/
protected $expectedResults = array();
+ /**
+ * Expected count of source rows.
+ *
+ * @var int
+ */
+ protected $expectedCount = 0;
+
/**
* The source plugin instance under test.
*
@@ -97,6 +104,7 @@ abstract class MigrateSqlSourceTestCase extends MigrateTestCase {
->method('getSourcePlugin')
->will($this->returnValue($plugin));
$this->source = $plugin;
+ $this->expectedCount = count($this->expectedResults);
}
/**
@@ -106,6 +114,13 @@ abstract class MigrateSqlSourceTestCase extends MigrateTestCase {
$this->queryResultTest($this->source, $this->expectedResults);
}
+ /**
+ * Test the source returns the row count expected.
+ */
+ public function testSourceCount() {
+ $this->assertEquals($this->source->count(), $this->expectedCount);
+ }
+
/**
* @param \Drupal\migrate\Row $row
* @param string $key
diff --git a/core/modules/migrate/tests/src/Unit/TestSqlIdMap.php b/core/modules/migrate/tests/src/Unit/TestSqlIdMap.php
index 203bd62de..68d80774a 100644
--- a/core/modules/migrate/tests/src/Unit/TestSqlIdMap.php
+++ b/core/modules/migrate/tests/src/Unit/TestSqlIdMap.php
@@ -7,7 +7,6 @@
namespace Drupal\Tests\migrate\Unit;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Database\Connection;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\MigrateException;
@@ -61,7 +60,7 @@ class TestSqlIdMap extends Sql implements \Iterator {
'not null' => FALSE,
);
default:
- throw new MigrateException(SafeMarkup::format('@type not supported', array('@type' => $id_definition['type'])));
+ throw new MigrateException($id_definition['type'] . ' not supported');
}
}
}
diff --git a/core/modules/migrate/tests/src/Unit/process/StaticMapTest.php b/core/modules/migrate/tests/src/Unit/process/StaticMapTest.php
index de883ba35..536d58777 100644
--- a/core/modules/migrate/tests/src/Unit/process/StaticMapTest.php
+++ b/core/modules/migrate/tests/src/Unit/process/StaticMapTest.php
@@ -19,12 +19,6 @@ class StaticMapTest extends MigrateProcessTestCase {
* {@inheritdoc}
*/
protected function setUp() {
- $this->row = $this->getMockBuilder('Drupal\migrate\Row')
- ->disableOriginalConstructor()
- ->getMock();
- $this->migrateExecutable = $this->getMockBuilder('Drupal\migrate\MigrateExecutable')
- ->disableOriginalConstructor()
- ->getMock();
$configuration['map']['foo']['bar'] = 'baz';
$this->plugin = new StaticMap($configuration, 'map', array());
parent::setUp();
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_system_filter.yml b/core/modules/migrate_drupal/config/optional/migrate.migration.d6_system_filter.yml
deleted file mode 100644
index 8e36db7cc..000000000
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_system_filter.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-id: d6_system_filter
-label: Drupal 6 filter configuration
-migration_tags:
- - Drupal 6
-source:
- plugin: variable
- variables:
- - filter_allowed_protocols
-process:
- protocols: filter_allowed_protocols
-destination:
- plugin: config
- config_name: system.filter
-dependencies:
- module:
- - migrate_drupal
- - system
diff --git a/core/modules/migrate_drupal/config/schema/migrate_drupal.source.schema.yml b/core/modules/migrate_drupal/config/schema/migrate_drupal.source.schema.yml
index 9fdf6d861..fdd6b9414 100644
--- a/core/modules/migrate_drupal/config/schema/migrate_drupal.source.schema.yml
+++ b/core/modules/migrate_drupal/config/schema/migrate_drupal.source.schema.yml
@@ -67,34 +67,6 @@ migrate.source.d6_comment_entity_form_display_subject:
type: migrate_entity_constant
label: 'Constants'
-migrate.source.d6_menu_link:
- type: migrate_source_sql
- label: 'Drupal 6 menu link'
- mapping:
- constants:
- type: migrate_entity_constant
- label: 'Constants'
-
-migrate.source.d6_box:
- type: migrate_source_sql
- label: 'Drupal 6 box'
- mapping:
- constants:
- type: mapping
- label: 'Constants'
- mapping:
- type:
- type: string
- label: 'Type'
-
-migrate.source.d6_taxonomy_vocabulary:
- type: migrate_source_sql
- label: 'Drupal 6 field instance form display'
- mapping:
- constants:
- type: migrate_entity_constant
- label: 'Constants'
-
migrate.source.d6_field_instance_per_form_display:
type: migrate_source_sql
label: 'Drupal 6 field instance form display'
@@ -103,22 +75,6 @@ migrate.source.d6_field_instance_per_form_display:
type: migrate_entity_constant
label: 'Constants'
-migrate.source.d6_taxonomy_vocabulary_per_type:
- type: migrate_source_sql
- label: 'Drupal 6 field instance form display'
- mapping:
- constants:
- type: migrate_entity_constant
- label: 'Constants'
-
-migrate.source.d6_taxonomy_vocabulary_per_type:
- type: migrate_source_sql
- label: 'Drupal 6 field instance form display'
- mapping:
- constants:
- type: migrate_entity_constant
- label: 'Constants'
-
migrate.source.d6_field:
type: migrate_source_sql
label: 'Drupal 6 field'
@@ -146,32 +102,6 @@ migrate.source.d6_comment_variable:
type: migrate_entity_constant
label: 'Constants'
-migrate.source.d6_contact_settings:
- type: migrate_source_sql
- label: 'Drupal 6 contact settings'
- mapping:
- variables:
- type: sequence
- label: 'Variables'
- sequence:
- type: string
- label: 'Variable'
-
-migrate.source.d6_view_mode:
- type: migrate_source_sql
- label: 'Drupal 6 view mode'
- mapping:
- constants:
- type: mapping
- label: 'Constants'
- mapping:
- targetEntityType:
- type: string
- label: 'Target entity type'
- status:
- type: boolean
- label: 'Status'
-
migrate.source.d6_profile_field:
type: migrate_source_sql
label: 'Drupal 6 profile field'
@@ -188,34 +118,6 @@ migrate.source.d6_field_formatter_settings:
type: migrate_entity_constant
label: 'Constants'
-migrate.source.d6_node_type:
- type: migrate_source_sql
- label: 'Drupal 6 node type'
- mapping:
- constants:
- type: migrate_entity_constant
- label: 'Constants'
-
-migrate.source.d6_node:
- type: migrate_source_sql
- label: 'Drupal 6 node'
- mapping:
- node_type:
- # The node type can be specified both as a string ID of a node type or an
- # array of node type IDs, so there is no way to consistently parse this.
- type: ignore
- label: 'Node type'
-
-migrate.source.d6_node_revision:
- type: migrate_source_sql
- label: 'Drupal 6 node'
- mapping:
- node_type:
- # The node type can be specified both as a string ID of a node type or an
- # array of node type IDs, so there is no way to consistently parse this.
- type: ignore
- label: 'Node type'
-
migrate.source.d6_upload_instance:
type: migrate_source_sql
label: 'Drupal 6 upload form display'
diff --git a/core/modules/migrate_drupal/migrate_drupal.services.yml b/core/modules/migrate_drupal/migrate_drupal.services.yml
index 301ced83c..684ecb8b0 100644
--- a/core/modules/migrate_drupal/migrate_drupal.services.yml
+++ b/core/modules/migrate_drupal/migrate_drupal.services.yml
@@ -1,7 +1,7 @@
services:
plugin.manager.migrate.load:
- class: Drupal\migrate_drupal\Plugin\MigratePluginManager
+ class: Drupal\migrate\Plugin\MigratePluginManager
arguments: [load, '@container.namespaces', '@cache.discovery', '@module_handler']
plugin.manager.migrate.cckfield:
- class: Drupal\migrate_drupal\Plugin\MigratePluginManager
+ class: Drupal\migrate\Plugin\MigratePluginManager
arguments: [cckfield, '@container.namespaces', '@cache.discovery', '@module_handler']
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_cck_field_revision.yml b/core/modules/migrate_drupal/migration_templates/d6_cck_field_revision.yml
similarity index 69%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_cck_field_revision.yml
rename to core/modules/migrate_drupal/migration_templates/d6_cck_field_revision.yml
index b3203f913..fa159894c 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_cck_field_revision.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_cck_field_revision.yml
@@ -15,10 +15,3 @@ migration_dependencies:
required:
- d6_cck_field_values
- d6_node_revision
-dependencies:
- config:
- - migrate.migration.d6_cck_field_values
- - migrate.migration.d6_node_revision
- module:
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_cck_field_values.yml b/core/modules/migrate_drupal/migration_templates/d6_cck_field_values.yml
similarity index 66%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_cck_field_values.yml
rename to core/modules/migrate_drupal/migration_templates/d6_cck_field_values.yml
index 0457ecfe7..27027baf7 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_cck_field_values.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_cck_field_values.yml
@@ -19,11 +19,3 @@ migration_dependencies:
- d6_node
- d6_field_formatter_settings
- d6_field_instance_widget_settings
-dependencies:
- config:
- - migrate.migration.d6_field_formatter_settings
- - migrate.migration.d6_field_instance_widget_settings
- - migrate.migration.d6_node
- module:
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment.yml b/core/modules/migrate_drupal/migration_templates/d6_comment.yml
similarity index 75%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment.yml
rename to core/modules/migrate_drupal/migration_templates/d6_comment.yml
index 94fb020f0..1a41ec7ae 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_comment.yml
@@ -43,15 +43,3 @@ migration_dependencies:
- d6_comment_entity_display
- d6_comment_entity_form_display
- d6_filter_format
-dependencies:
- config:
- - migrate.migration.d6_comment_entity_display
- - migrate.migration.d6_comment_entity_form_display
- - migrate.migration.d6_comment_type
- - migrate.migration.d6_filter_format
- - migrate.migration.d6_node
- - migrate.migration.d6_user
- module:
- - comment
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_entity_display.yml b/core/modules/migrate_drupal/migration_templates/d6_comment_entity_display.yml
similarity index 83%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_entity_display.yml
rename to core/modules/migrate_drupal/migration_templates/d6_comment_entity_display.yml
index 99e373b40..e06c08fcc 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_entity_display.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_comment_entity_display.yml
@@ -24,9 +24,3 @@ destination:
migration_dependencies:
required:
- d6_comment_field_instance
-dependencies:
- config:
- - migrate.migration.d6_comment_field_instance
- module:
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_entity_form_display.yml b/core/modules/migrate_drupal/migration_templates/d6_comment_entity_form_display.yml
similarity index 83%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_entity_form_display.yml
rename to core/modules/migrate_drupal/migration_templates/d6_comment_entity_form_display.yml
index 059517f20..a05a11bae 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_entity_form_display.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_comment_entity_form_display.yml
@@ -23,9 +23,3 @@ destination:
migration_dependencies:
required:
- d6_comment_field_instance
-dependencies:
- config:
- - migrate.migration.d6_comment_field_instance
- module:
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_entity_form_display_subject.yml b/core/modules/migrate_drupal/migration_templates/d6_comment_entity_form_display_subject.yml
similarity index 87%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_entity_form_display_subject.yml
rename to core/modules/migrate_drupal/migration_templates/d6_comment_entity_form_display_subject.yml
index 8a3627289..a7e3e6b24 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_entity_form_display_subject.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_comment_entity_form_display_subject.yml
@@ -30,9 +30,3 @@ destination:
migration_dependencies:
required:
- d6_comment_type
-dependencies:
- config:
- - migrate.migration.d6_comment_type
- module:
- - comment
- - migrate_drupal
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_field.yml b/core/modules/migrate_drupal/migration_templates/d6_comment_field.yml
similarity index 77%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_field.yml
rename to core/modules/migrate_drupal/migration_templates/d6_comment_field.yml
index ab22f23cf..aca8783c1 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_field.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_comment_field.yml
@@ -17,11 +17,3 @@ destination:
migration_dependencies:
required:
- d6_comment_type
-dependencies:
- config:
- - migrate.migration.d6_comment_type
- module:
- - comment
- - field
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_field_instance.yml b/core/modules/migrate_drupal/migration_templates/d6_comment_field_instance.yml
similarity index 84%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_field_instance.yml
rename to core/modules/migrate_drupal/migration_templates/d6_comment_field_instance.yml
index 669ea9437..f00b5278b 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_field_instance.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_comment_field_instance.yml
@@ -32,11 +32,3 @@ migration_dependencies:
required:
- d6_comment_field
- d6_node_type
-dependencies:
- config:
- - migrate.migration.d6_comment_field
- - migrate.migration.d6_node_type
- module:
- - field
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_type.yml b/core/modules/migrate_drupal/migration_templates/d6_comment_type.yml
similarity index 82%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_type.yml
rename to core/modules/migrate_drupal/migration_templates/d6_comment_type.yml
index 35dfde0ad..54ffec188 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_comment_type.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_comment_type.yml
@@ -13,8 +13,3 @@ process:
description: description
destination:
plugin: entity:comment_type
-dependencies:
- module:
- - comment
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field.yml b/core/modules/migrate_drupal/migration_templates/d6_field.yml
similarity index 98%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_field.yml
rename to core/modules/migrate_drupal/migration_templates/d6_field.yml
index 191c538f2..9ff9e2444 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_field.yml
@@ -131,8 +131,3 @@ process:
destination:
plugin: md_entity:field_storage_config
-dependencies:
- module:
- - field
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_formatter_settings.yml b/core/modules/migrate_drupal/migration_templates/d6_field_formatter_settings.yml
similarity index 92%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_formatter_settings.yml
rename to core/modules/migrate_drupal/migration_templates/d6_field_formatter_settings.yml
index 883e811c8..425e7d4f0 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_formatter_settings.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_field_formatter_settings.yml
@@ -52,10 +52,6 @@ process:
- type
- 'display_settings/format'
map:
- text:
- default: text_default
- trimmed: text_trimmed
- plain: basic_string
number_integer:
default: number_integer
us_0: number_integer
@@ -91,13 +87,6 @@ process:
spamspan: email_mailto
contact: email_mailto
plain: basic_string
- filefield:
- default: file_default
- url_plain: file_url_plain
- path_plain: file_url_plain
- image_plain: image
- image_nodelink: image
- image_imagelink: image
fr_phone:
default: basic_string
be_phone:
@@ -261,10 +250,3 @@ migration_dependencies:
required:
- d6_field_instance
- d6_view_modes
-dependencies:
- config:
- - migrate.migration.d6_field_instance
- - migrate.migration.d6_view_modes
- module:
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_instance.yml b/core/modules/migrate_drupal/migration_templates/d6_field_instance.yml
similarity index 87%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_instance.yml
rename to core/modules/migrate_drupal/migration_templates/d6_field_instance.yml
index 571043329..f876dfd1f 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_instance.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_field_instance.yml
@@ -50,11 +50,3 @@ migration_dependencies:
required:
- d6_node_type
- d6_field
-dependencies:
- config:
- - migrate.migration.d6_field
- - migrate.migration.d6_node_type
- module:
- - field
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_instance_widget_settings.yml b/core/modules/migrate_drupal/migration_templates/d6_field_instance_widget_settings.yml
similarity index 89%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_instance_widget_settings.yml
rename to core/modules/migrate_drupal/migration_templates/d6_field_instance_widget_settings.yml
index a343abedb..3299d5c86 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_field_instance_widget_settings.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_field_instance_widget_settings.yml
@@ -36,12 +36,10 @@ process:
bypass: true
source: widget_type
map:
- text_textfield: text_textfield
number: number
email_textfield: email_default
date_select: datetime_default
date_text: datetime_default
- filefield_widget: file_generic
imagefield_widget: image_image
phone_textfield: telephone_default
optionwidgets_onoff: boolean_checkbox
@@ -60,9 +58,3 @@ destination:
migration_dependencies:
required:
- d6_field_instance
-dependencies:
- config:
- - migrate.migration.d6_field_instance
- module:
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_file.yml b/core/modules/migrate_drupal/migration_templates/d6_file.yml
similarity index 90%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_file.yml
rename to core/modules/migrate_drupal/migration_templates/d6_file.yml
index 508d0bba6..e8ead684b 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_file.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_file.yml
@@ -23,7 +23,3 @@ process:
uid: uid
destination:
plugin: entity:file
-dependencies:
- module:
- - file
- - migrate_drupal
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_file_settings.yml b/core/modules/migrate_drupal/migration_templates/d6_file_settings.yml
similarity index 87%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_file_settings.yml
rename to core/modules/migrate_drupal/migration_templates/d6_file_settings.yml
index f2ca1707d..f08d92e64 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_file_settings.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_file_settings.yml
@@ -15,7 +15,3 @@ process:
destination:
plugin: config
config_name: file.settings
-dependencies:
- module:
- - file
- - migrate_drupal
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_profile_values.yml b/core/modules/migrate_drupal/migration_templates/d6_profile_values.yml
similarity index 58%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_profile_values.yml
rename to core/modules/migrate_drupal/migration_templates/d6_profile_values.yml
index 047de4d04..d2b81a5bf 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_profile_values.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_profile_values.yml
@@ -16,12 +16,3 @@ migration_dependencies:
- d6_user_profile_field_instance
- d6_user_profile_entity_display
- d6_user_profile_entity_form_display
-dependencies:
- config:
- - migrate.migration.d6_user
- - migrate.migration.d6_user_profile_entity_display
- - migrate.migration.d6_user_profile_entity_form_display
- - migrate.migration.d6_user_profile_field_instance
- module:
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload.yml b/core/modules/migrate_drupal/migration_templates/d6_upload.yml
similarity index 77%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload.yml
rename to core/modules/migrate_drupal/migration_templates/d6_upload.yml
index fedda76ed..2d04241af 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_upload.yml
@@ -25,10 +25,3 @@ migration_dependencies:
required:
- d6_file
- d6_node
-dependencies:
- config:
- - migrate.migration.d6_file
- - migrate.migration.d6_node
- module:
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_entity_display.yml b/core/modules/migrate_drupal/migration_templates/d6_upload_entity_display.yml
similarity index 84%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_entity_display.yml
rename to core/modules/migrate_drupal/migration_templates/d6_upload_entity_display.yml
index 8daf13f1c..28e154b0a 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_entity_display.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_upload_entity_display.yml
@@ -25,9 +25,3 @@ destination:
migration_dependencies:
required:
- d6_upload_field_instance
-dependencies:
- config:
- - migrate.migration.d6_upload_field_instance
- module:
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_entity_form_display.yml b/core/modules/migrate_drupal/migration_templates/d6_upload_entity_form_display.yml
similarity index 85%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_entity_form_display.yml
rename to core/modules/migrate_drupal/migration_templates/d6_upload_entity_form_display.yml
index f248d1d25..7725659cb 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_entity_form_display.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_upload_entity_form_display.yml
@@ -26,9 +26,3 @@ destination:
migration_dependencies:
required:
- d6_upload_field_instance
-dependencies:
- config:
- - migrate.migration.d6_upload_field_instance
- module:
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_field.yml b/core/modules/migrate_drupal/migration_templates/d6_upload_field.yml
similarity index 88%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_field.yml
rename to core/modules/migrate_drupal/migration_templates/d6_upload_field.yml
index 15df33af5..e92b8019d 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_field.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_upload_field.yml
@@ -21,9 +21,3 @@ process:
'settings/display_field': 'constants/display_field'
destination:
plugin: md_entity:field_storage_config
-dependencies:
- module:
- - field
- - file
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_field_instance.yml b/core/modules/migrate_drupal/migration_templates/d6_upload_field_instance.yml
similarity index 78%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_field_instance.yml
rename to core/modules/migrate_drupal/migration_templates/d6_upload_field_instance.yml
index 701351263..4410ec011 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_upload_field_instance.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_upload_field_instance.yml
@@ -22,11 +22,3 @@ migration_dependencies:
required:
- d6_upload_field
- d6_node_type
-dependencies:
- config:
- - migrate.migration.d6_node_type
- - migrate.migration.d6_upload_field
- module:
- - field
- - migrate_drupal
- - node
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user.yml b/core/modules/migrate_drupal/migration_templates/d6_user.yml
similarity index 87%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user.yml
index b55e2ec03..05ff4a00e 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user.yml
@@ -35,9 +35,3 @@ migration_dependencies:
- d6_user_picture_file
- d6_user_picture_entity_display
- d6_user_picture_entity_form_display
-dependencies:
- config:
- - migrate.migration.d6_user_role
- module:
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_contact_settings.yml b/core/modules/migrate_drupal/migration_templates/d6_user_contact_settings.yml
similarity index 80%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_contact_settings.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_contact_settings.yml
index 7da9e52ce..04291550c 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_contact_settings.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_contact_settings.yml
@@ -21,9 +21,3 @@ destination:
migration_dependencies:
required:
- d6_user
-dependencies:
- config:
- - migrate.migration.d6_user
- module:
- - contact
- - migrate_drupal
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_mail.yml b/core/modules/migrate_drupal/migration_templates/d6_user_mail.yml
similarity index 96%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_mail.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_mail.yml
index 15ea0d41c..bc3badf6b 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_mail.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_mail.yml
@@ -37,7 +37,3 @@ process:
destination:
plugin: config
config_name: user.mail
-dependencies:
- module:
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_entity_display.yml b/core/modules/migrate_drupal/migration_templates/d6_user_picture_entity_display.yml
similarity index 85%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_entity_display.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_picture_entity_display.yml
index dd6be7989..f38bc3606 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_entity_display.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_picture_entity_display.yml
@@ -28,9 +28,3 @@ destination:
migration_dependencies:
required:
- d6_user_picture_field_instance
-dependencies:
- config:
- - migrate.migration.d6_user_picture_field_instance
- module:
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_entity_form_display.yml b/core/modules/migrate_drupal/migration_templates/d6_user_picture_entity_form_display.yml
similarity index 85%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_entity_form_display.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_picture_entity_form_display.yml
index 9d2182b99..4e422287e 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_entity_form_display.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_picture_entity_form_display.yml
@@ -27,9 +27,3 @@ destination:
migration_dependencies:
required:
- d6_user_picture_field_instance
-dependencies:
- config:
- - migrate.migration.d6_user_picture_field_instance
- module:
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_field.yml b/core/modules/migrate_drupal/migration_templates/d6_user_picture_field.yml
similarity index 86%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_field.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_picture_field.yml
index eefb62d92..27a39a23c 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_field.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_picture_field.yml
@@ -18,9 +18,3 @@ process:
cardinality: 'constants/cardinality'
destination:
plugin: md_entity:field_storage_config
-dependencies:
- module:
- - field
- - image
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_field_instance.yml b/core/modules/migrate_drupal/migration_templates/d6_user_picture_field_instance.yml
similarity index 86%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_field_instance.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_picture_field_instance.yml
index 14dfc0e91..7dc83b19b 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_field_instance.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_picture_field_instance.yml
@@ -28,10 +28,3 @@ destination:
migration_dependencies:
required:
- d6_user_picture_field
-dependencies:
- config:
- - migrate.migration.d6_user_picture_field
- module:
- - field
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_file.yml b/core/modules/migrate_drupal/migration_templates/d6_user_picture_file.yml
similarity index 91%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_file.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_picture_file.yml
index 9be01218c..0bc5a0b3c 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_picture_file.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_picture_file.yml
@@ -24,7 +24,3 @@ migration_dependencies:
# migration as an optional dependency to ensure it runs first.
optional:
- d6_file
-dependencies:
- module:
- - file
- - migrate_drupal
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_entity_display.yml b/core/modules/migrate_drupal/migration_templates/d6_user_profile_entity_display.yml
similarity index 94%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_entity_display.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_profile_entity_display.yml
index 7da0e7ad5..3e7b46778 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_entity_display.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_profile_entity_display.yml
@@ -38,7 +38,3 @@ process:
4: true # PROFILE_HIDDEN
destination:
plugin: component_entity_display
-dependencies:
- module:
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_entity_form_display.yml b/core/modules/migrate_drupal/migration_templates/d6_user_profile_entity_form_display.yml
similarity index 95%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_entity_form_display.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_profile_entity_form_display.yml
index 18fab1bbe..d1cde0b65 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_entity_form_display.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_profile_entity_form_display.yml
@@ -47,7 +47,3 @@ process:
4: true # PROFILE_HIDDEN
destination:
plugin: component_entity_form_display
-dependencies:
- module:
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_field.yml b/core/modules/migrate_drupal/migration_templates/d6_user_profile_field.yml
similarity index 91%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_field.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_profile_field.yml
index 1f84573d7..99176739a 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_field.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_profile_field.yml
@@ -32,8 +32,3 @@ process:
list: -1
destination:
plugin: md_entity:field_storage_config
-dependencies:
- module:
- - field
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_field_instance.yml b/core/modules/migrate_drupal/migration_templates/d6_user_profile_field_instance.yml
similarity index 79%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_field_instance.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_profile_field_instance.yml
index 30c593849..ec0e52667 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_profile_field_instance.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_profile_field_instance.yml
@@ -19,10 +19,3 @@ destination:
migration_dependencies:
required:
- d6_user_profile_field
-dependencies:
- config:
- - migrate.migration.d6_user_profile_field
- module:
- - field
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_role.yml b/core/modules/migrate_drupal/migration_templates/d6_user_role.yml
similarity index 92%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_role.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_role.yml
index 46edcc807..e15058bb6 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_role.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_role.yml
@@ -44,9 +44,3 @@ destination:
migration_dependencies:
required:
- d6_filter_format
-dependencies:
- config:
- - migrate.migration.d6_filter_format
- module:
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_settings.yml b/core/modules/migrate_drupal/migration_templates/d6_user_settings.yml
similarity index 92%
rename from core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_settings.yml
rename to core/modules/migrate_drupal/migration_templates/d6_user_settings.yml
index 09e2eb778..5984d51af 100644
--- a/core/modules/migrate_drupal/config/optional/migrate.migration.d6_user_settings.yml
+++ b/core/modules/migrate_drupal/migration_templates/d6_user_settings.yml
@@ -26,7 +26,3 @@ process:
destination:
plugin: config
config_name: user.settings
-dependencies:
- module:
- - migrate_drupal
- - user
diff --git a/core/modules/migrate_drupal/src/Entity/Migration.php b/core/modules/migrate_drupal/src/Entity/Migration.php
index 7875ddf63..4a34c1671 100644
--- a/core/modules/migrate_drupal/src/Entity/Migration.php
+++ b/core/modules/migrate_drupal/src/Entity/Migration.php
@@ -16,7 +16,7 @@ class Migration extends BaseMigration implements MigrationInterface {
*
* @var array
*/
- public $load = array();
+ protected $load = array();
/**
* The load plugin.
diff --git a/core/modules/migrate_drupal/src/MigrationStorage.php b/core/modules/migrate_drupal/src/MigrationStorage.php
index 84c69175b..9a33b3607 100644
--- a/core/modules/migrate_drupal/src/MigrationStorage.php
+++ b/core/modules/migrate_drupal/src/MigrationStorage.php
@@ -7,7 +7,6 @@
namespace Drupal\migrate_drupal;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityInterface;
@@ -17,7 +16,7 @@ use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
use Drupal\migrate_drupal\Plugin\CckFieldMigrateSourceInterface;
use Drupal\migrate\MigrationStorage as BaseMigrationStorage;
-use Drupal\migrate_drupal\Plugin\MigratePluginManager;
+use Drupal\migrate\Plugin\MigratePluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -164,7 +163,7 @@ class MigrationStorage extends BaseMigrationStorage {
*/
public function save(EntityInterface $entity) {
if (strpos($entity->id(), ':') !== FALSE) {
- throw new EntityStorageException(SafeMarkup::format("Dynamic migration %id can't be saved", array('$%id' => $entity->id())));
+ throw new EntityStorageException("Dynamic migration '{$entity->id()}' can't be saved");
}
return parent::save($entity);
}
diff --git a/core/modules/migrate_drupal/src/Plugin/MigratePluginManager.php b/core/modules/migrate_drupal/src/Plugin/MigratePluginManager.php
deleted file mode 100644
index 4a15e77ae..000000000
--- a/core/modules/migrate_drupal/src/Plugin/MigratePluginManager.php
+++ /dev/null
@@ -1,31 +0,0 @@
- 'Drupal\migrate_drupal\Plugin\MigrateLoadInterface',
- 'cckfield' => 'Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface',
- ];
- }
-
-}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/FileField.php b/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/FileField.php
new file mode 100644
index 000000000..d91bd9972
--- /dev/null
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/FileField.php
@@ -0,0 +1,55 @@
+ 'file_generic',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFieldFormatterMap() {
+ return [
+ 'default' => 'file_default',
+ 'url_plain' => 'file_url_plain',
+ 'path_plain' => 'file_url_plain',
+ 'image_plain' => 'image',
+ 'image_nodelink' => 'image',
+ 'image_imagelink' => 'image',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) {
+ $process = [
+ 'plugin' => 'd6_cck_file',
+ 'source' => [
+ $field_name,
+ $field_name . '_list',
+ $field_name . '_data',
+ ],
+ ];
+ $migration->mergeProcessOfProperty($field_name, $process);
+ }
+
+}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/TextField.php b/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/TextField.php
new file mode 100644
index 000000000..268ea606c
--- /dev/null
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/TextField.php
@@ -0,0 +1,71 @@
+ 'text_textfield',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFieldFormatterMap() {
+ return [
+ 'default' => 'text_default',
+ 'trimmed' => 'text_trimmed',
+ 'plain' => 'basic_string',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function processCckFieldValues(MigrationInterface $migration, $field_name, $data) {
+ // The data is stored differently depending on whether we're using
+ // db storage.
+ $value_key = $data['db_storage'] ? $field_name : "$field_name/value";
+ $format_key = $data['db_storage'] ? $field_name . '_format' : "$field_name/format" ;
+
+ $migration->setProcessOfProperty("$field_name/value", $value_key);
+
+ // See \Drupal\migrate_drupal\Plugin\migrate\source\d6\User::baseFields(),
+ // signature_format for an example of the YAML that represents this
+ // process array.
+ $process = [
+ [
+ 'plugin' => 'static_map',
+ 'bypass' => TRUE,
+ 'source' => $format_key,
+ 'map' => [0 => NULL]
+ ],
+ [
+ 'plugin' => 'skip_on_empty',
+ 'method' => 'process',
+ ],
+ [
+ 'plugin' => 'migration',
+ 'migration' => 'd6_filter_format',
+ 'source' => $format_key,
+ ],
+ ];
+ $migration->mergeProcessOfProperty("$field_name/format", $process);
+ }
+
+}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/load/LoadEntity.php b/core/modules/migrate_drupal/src/Plugin/migrate/load/LoadEntity.php
index c3b574c51..375fe7bf3 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/load/LoadEntity.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/load/LoadEntity.php
@@ -7,7 +7,6 @@
namespace Drupal\migrate_drupal\Plugin\migrate\load;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\migrate\Entity\MigrationInterface;
@@ -44,7 +43,7 @@ class LoadEntity extends PluginBase implements MigrateLoadInterface {
throw new MigrateException('Migrations with a load plugin using LoadEntity should have an entity as source.');
}
if ($source_plugin->bundleMigrationRequired() && empty($configuration['bundle_migration'])) {
- throw new MigrateException(SafeMarkup::format('Source plugin @plugin requires the bundle_migration key to be set.', array('@plugin' => $source_plugin->getPluginId())));
+ throw new MigrateException("Source plugin '{$source_plugin->getPluginId()}' requires the bundle_migration key to be set.");
}
}
@@ -87,16 +86,7 @@ class LoadEntity extends PluginBase implements MigrateLoadInterface {
if ($source_plugin instanceof CckFieldMigrateSourceInterface) {
foreach ($source_plugin->fieldData() as $field_name => $data) {
- switch ($data['type']) {
- case 'filefield':
- $this->processFileField($field_name, $data, $migration);
- break;
- case 'text':
- $this->processTextField($field_name, $data, $migration);
- break;
- default:
- $migration->setProcessOfProperty($field_name, $field_name);
- }
+ $migration->setProcessOfProperty($field_name, $field_name);
}
}
else {
@@ -113,67 +103,4 @@ class LoadEntity extends PluginBase implements MigrateLoadInterface {
return $migrations;
}
- /**
- * Manipulate text fields with any per field type processing.
- *
- * @param string $field_name
- * The field we're processing.
- * @param array $field_data
- * The an array of field type data from the source.
- * @param \Drupal\migrate\Entity\MigrationInterface $migration
- * The migration entity.
- */
- protected function processTextField($field_name, $field_data, MigrationInterface $migration) {
- // The data is stored differently depending on whether we're using
- // db storage.
- $value_key = $field_data['db_storage'] ? $field_name : "$field_name/value";
- $format_key = $field_data['db_storage'] ? $field_name . '_format' : "$field_name/format" ;
-
- $migration->setProcessOfProperty("$field_name/value", $value_key);
-
- // See \Drupal\migrate_drupal\Plugin\migrate\source\d6\User::baseFields(),
- // signature_format for an example of the YAML that represents this
- // process array.
- $process = [
- [
- 'plugin' => 'static_map',
- 'bypass' => TRUE,
- 'source' => $format_key,
- 'map' => [0 => NULL]
- ],
- [
- 'plugin' => 'skip_on_empty',
- 'method' => 'process',
- ],
- [
- 'plugin' => 'migration',
- 'migration' => 'd6_filter_format',
- 'source' => $format_key,
- ],
- ];
- $migration->mergeProcessOfProperty("$field_name/format", $process);
- }
-
- /**
- * Manipulate file fields with any per field type processing.
- *
- * @param string $field_name
- * The field we're processing.
- * @param array $field_data
- * The an array of field type data from the source.
- * @param \Drupal\migrate\Entity\MigrationInterface $migration
- * The migration entity.
- */
- protected function processFileField($field_name, $field_data, MigrationInterface $migration) {
- $process = [
- 'plugin' => 'd6_cck_file',
- 'source' => [
- $field_name,
- $field_name . '_list',
- $field_name . '_data',
- ],
- ];
- $migration->mergeProcessOfProperty($field_name, $process);
- }
-
}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php
index 5257d9401..deb7adfde 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php
@@ -8,7 +8,6 @@
namespace Drupal\migrate_drupal\Plugin\migrate\source;
use Drupal\Component\Plugin\DependentPluginInterface;
-use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\DependencyTrait;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -102,11 +101,11 @@ abstract class DrupalSqlBase extends SqlBase implements ContainerFactoryPluginIn
if (isset($this->pluginDefinition['source_provider'])) {
if ($this->moduleExists($this->pluginDefinition['source_provider'])) {
if (isset($this->pluginDefinition['minimum_schema_version']) && !$this->getModuleSchemaVersion($this->pluginDefinition['source_provider']) < $this->pluginDefinition['minimum_schema_version']) {
- throw new RequirementsException(SafeMarkup::format('Required minimum schema version @minimum_schema_version', ['@minimum_schema_version' => $this->pluginDefinition['minimum_schema_version']]), ['minimum_schema_version' => $this->pluginDefinition['minimum_schema_version']]);
+ throw new RequirementsException('Required minimum schema version ' . $this->pluginDefinition['minimum_schema_version'], ['minimum_schema_version' => $this->pluginDefinition['minimum_schema_version']]);
}
}
else {
- throw new RequirementsException(SafeMarkup::format('Missing source provider @provider', ['@provider' => $this->pluginDefinition['source_provider']]), ['source_provider' => $this->pluginDefinition['source_provider']]);
+ throw new RequirementsException('Missing source provider ' . $this->pluginDefinition['source_provider'], ['source_provider' => $this->pluginDefinition['source_provider']]);
}
}
}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/CommentVariable.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/CommentVariable.php
index 8dfb5a8ff..e01d28d00 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/CommentVariable.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/CommentVariable.php
@@ -8,6 +8,7 @@
namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+use Drupal\migrate\Plugin\migrate\source\DummyQueryTrait;
/**
* @MigrateSource(
@@ -16,6 +17,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
*/
class CommentVariable extends DrupalSqlBase {
+ use DummyQueryTrait;
+
/**
* {@inheritdoc}
*/
@@ -92,13 +95,6 @@ class CommentVariable extends DrupalSqlBase {
);
}
- /**
- * {@inheritdoc}
- */
- public function query() {
- // Nothing to do here.
- }
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/FieldInstancePerViewMode.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/FieldInstancePerViewMode.php
index a0cf124ca..b3dd8a050 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/FieldInstancePerViewMode.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/FieldInstancePerViewMode.php
@@ -7,6 +7,8 @@
namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
+use Drupal\node\Plugin\migrate\source\d6\ViewModeBase;
+
/**
* The field instance per view mode source class.
*
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UploadInstance.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UploadInstance.php
index 6d5f891f2..3b24f5b5e 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UploadInstance.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UploadInstance.php
@@ -8,6 +8,7 @@
namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+use Drupal\migrate\Plugin\migrate\source\DummyQueryTrait;
/**
* Drupal 6 upload instance source from database.
@@ -19,6 +20,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
*/
class UploadInstance extends DrupalSqlBase {
+ use DummyQueryTrait;
+
/**
* {@inheritdoc}
*/
@@ -60,13 +63,6 @@ class UploadInstance extends DrupalSqlBase {
);
}
- /**
- * {@inheritdoc}
- */
- public function query() {
- // Nothing needed here.
- }
-
/**
* {@inheritdoc}
*/
@@ -78,4 +74,11 @@ class UploadInstance extends DrupalSqlBase {
);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function count() {
+ return count($this->initializeIterator());
+ }
+
}
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UserPictureInstance.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UserPictureInstance.php
index 667a5518e..f3a9654fb 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UserPictureInstance.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/UserPictureInstance.php
@@ -8,6 +8,7 @@
namespace Drupal\migrate_drupal\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+use Drupal\migrate\Plugin\migrate\source\DummyQueryTrait;
/**
* Drupal 6 user picture field instance source.
@@ -20,6 +21,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
*/
class UserPictureInstance extends DrupalSqlBase {
+ use DummyQueryTrait;
+
/**
* {@inheritdoc}
*/
@@ -33,6 +36,15 @@ class UserPictureInstance extends DrupalSqlBase {
)));
}
+ /**
+ * {@inheritdoc}
+ */
+ public function count() {
+ // This source provides a single row, corresponding to a single picture
+ // field to be added to the user entity.
+ return 1;
+ }
+
/**
* {@inheritdoc}
*/
@@ -52,11 +64,4 @@ class UserPictureInstance extends DrupalSqlBase {
return $ids;
}
- /**
- * {@inheritdoc}
- */
- public function query() {
- // Nothing to do here.
- }
-
}
diff --git a/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php b/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php
index b8a436fb4..9d4e1a2e1 100644
--- a/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php
+++ b/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php
@@ -8,6 +8,10 @@
namespace Drupal\migrate_drupal\Tests;
use Drupal\migrate\Tests\MigrateTestBase;
+use Drupal\migrate\Entity\Migration;
+use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\migrate\Plugin\migrate\source\SqlBase;
+use Drupal\Core\Database\Query\SelectInterface;
/**
* Base class for Drupal migration tests.
@@ -26,7 +30,7 @@ abstract class MigrateDrupalTestBase extends MigrateTestBase {
*/
protected function setUp() {
parent::setUp();
- $this->loadDumps([$this->getDumpDirectory() . '/System.php']);
+ $this->loadDumps(['System.php']);
$this->installEntitySchema('user');
$this->installConfig(['migrate_drupal', 'system']);
@@ -42,4 +46,51 @@ abstract class MigrateDrupalTestBase extends MigrateTestBase {
return __DIR__ . '/Table';
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function loadDumps(array $files, $method = 'load') {
+ $files = array_map(function($file) { return $this->getDumpDirectory() . '/' . $file; }, $files);
+ parent::loadDumps($files, $method);
+ }
+
+ /**
+ * Turn all the migration templates for the specified drupal version into
+ * real migration entities so we can test them.
+ *
+ * @param string $version
+ * Drupal version as provided in migration_tags - e.g., 'Drupal 6'.
+ */
+ protected function installMigrations($version) {
+ $migration_templates = \Drupal::service('migrate.template_storage')->findTemplatesByTag($version);
+ foreach ($migration_templates as $template) {
+ try {
+ $migration = Migration::create($template);
+ $migration->save();
+ }
+ catch (PluginNotFoundException $e) {
+ // Migrations requiring modules not enabled will throw an exception.
+ // Ignoring this exception is equivalent to placing config in the
+ // optional subdirectory - the migrations we require for the test will
+ // be successfully saved.
+ }
+ }
+ }
+
+ /**
+ * Test that the source plugin provides a valid query and a valid count.
+ */
+ public function testSourcePlugin() {
+ if (isset($this->migration)) {
+ $source = $this->migration->getSourcePlugin();
+ // Make sure a SqlBase source has a valid query.
+ if ($source instanceof SqlBase) {
+ /** @var SqlBase $source */
+ $this->assertTrue($source->query() instanceof SelectInterface, 'SQL source plugin has valid query');
+ }
+ // Validate that any source returns a valid count.
+ $this->assertTrue(is_numeric($source->count()), 'Source plugin returns valid count');
+ }
+ }
+
}
diff --git a/core/modules/migrate_drupal/src/Tests/MigrateFullDrupalTestBase.php b/core/modules/migrate_drupal/src/Tests/MigrateFullDrupalTestBase.php
index 6b01174b3..98289c024 100644
--- a/core/modules/migrate_drupal/src/Tests/MigrateFullDrupalTestBase.php
+++ b/core/modules/migrate_drupal/src/Tests/MigrateFullDrupalTestBase.php
@@ -7,10 +7,6 @@
namespace Drupal\migrate_drupal\Tests;
-use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Core\Config\ExtensionInstallStorage;
-use Drupal\Core\Config\InstallStorage;
-use Drupal\Core\Config\StorageInterface;
use Drupal\migrate\MigrateExecutable;
use Drupal\simpletest\TestBase;
@@ -19,6 +15,19 @@ use Drupal\simpletest\TestBase;
*/
abstract class MigrateFullDrupalTestBase extends MigrateDrupalTestBase {
+ /**
+ * The test class which discovered migration tests must extend in order to be
+ * included in this test run.
+ */
+ const BASE_TEST_CLASS = 'Drupal\migrate_drupal\Tests\MigrateDrupalTestBase';
+
+ /**
+ * A list of fully-qualified test classes which should be ignored.
+ *
+ * @var string[]
+ */
+ protected static $blacklist = [];
+
/**
* Get the dump classes required for this migration test.
*
@@ -31,9 +40,24 @@ abstract class MigrateFullDrupalTestBase extends MigrateDrupalTestBase {
* Get the test classes that needs to be run for this test.
*
* @return array
- * The list of test fully-classified class names.
+ * The list of fully-classified test class names.
*/
- protected abstract function getTestClassesList();
+ protected function getTestClassesList() {
+ $classes = [];
+
+ $discovery = \Drupal::getContainer()->get('test_discovery');
+ foreach (static::$modules as $module) {
+ foreach ($discovery->getTestClasses($module) as $group) {
+ foreach (array_keys($group) as $class) {
+ if (is_subclass_of($class, static::BASE_TEST_CLASS)) {
+ $classes[] = $class;
+ }
+ }
+ }
+ }
+ // Exclude blacklisted classes.
+ return array_diff($classes, static::$blacklist);
+ }
/**
* {@inheritdoc}
@@ -58,7 +82,6 @@ abstract class MigrateFullDrupalTestBase extends MigrateDrupalTestBase {
$this->loadDumps($dumps);
$classes = $this->getTestClassesList();
- $extension_install_storage = new ExtensionInstallStorage(\Drupal::service('config.storage'), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE);
foreach ($classes as $class) {
if (is_subclass_of($class, '\Drupal\migrate\Tests\MigrateDumpAlterInterface')) {
$class::migrateDumpAlter($this);
@@ -68,16 +91,6 @@ abstract class MigrateFullDrupalTestBase extends MigrateDrupalTestBase {
// Run every migration in the order specified by the storage controller.
foreach (entity_load_multiple('migration', static::$migrations) as $migration) {
(new MigrateExecutable($migration, $this))->import();
-
- // Ensure that the default migration has the correct dependencies.
- list($base_name, ) = explode(':', $migration->id(), 2);
- $default_configuration = $extension_install_storage->read('migrate.migration.' . $base_name);
- $default_dependencies = isset($default_configuration['dependencies']) ? $default_configuration['dependencies'] : [];
- $this->assertEqual($default_dependencies, $migration->getDependencies(), SafeMarkup::format('Dependencies in @id match after installing. Default configuration @first is equal to active configuration @second.', array(
- '@id' => $migration->id(),
- '@first' => var_export($default_dependencies, TRUE),
- '@second' => var_export($migration->getDependencies(), TRUE)
- )));
}
foreach ($classes as $class) {
$test_object = new $class($this->testId);
diff --git a/core/modules/migrate_drupal/src/Tests/Table/d7/Variable.php b/core/modules/migrate_drupal/src/Tests/Table/d7/Variable.php
index f6faf11fa..8f4c3a8ef 100644
--- a/core/modules/migrate_drupal/src/Tests/Table/d7/Variable.php
+++ b/core/modules/migrate_drupal/src/Tests/Table/d7/Variable.php
@@ -49,6 +49,27 @@ class Variable extends DrupalDumpBase {
))->values(array(
'name' => 'admin_theme',
'value' => 's:5:"seven";',
+ ))->values(array(
+ 'name' => 'aggregator_allowed_html_tags',
+ 'value' => 's:13:"