' . t('The Place Blocks module allows you to place blocks from every page. For more information, see the online documentation for the Place Blocks module.', [':blocks-documentation' => 'https://www.drupal.org/documentation/modules/block_place/']) . '
';
$output .= '
' . t('Uses') . '
';
- $output .= '
' . t('Block placement is specific to each theme on your site. This module allows you to place blocks in the context of your content pages') . '
';
+ $output .= '
' . t('Block placement is specific to each theme on your site. This module allows you to place blocks in the context of your content pages.') . '
';
return $output;
}
}
diff --git a/core/modules/book/src/Plugin/Block/BookNavigationBlock.php b/core/modules/book/src/Plugin/Block/BookNavigationBlock.php
index b232fbf41..6967077f3 100644
--- a/core/modules/book/src/Plugin/Block/BookNavigationBlock.php
+++ b/core/modules/book/src/Plugin/Block/BookNavigationBlock.php
@@ -156,9 +156,12 @@ class BookNavigationBlock extends BlockBase implements ContainerFactoryPluginInt
}
}
elseif ($current_bid) {
- // Only display this block when the user is browsing a book.
- $query = \Drupal::entityQuery('node');
- $nid = $query->condition('nid', $node->book['bid'], '=')->execute();
+ // Only display this block when the user is browsing a book and do
+ // not show unpublished books.
+ $nid = \Drupal::entityQuery('node')
+ ->condition('nid', $node->book['bid'], '=')
+ ->condition('status', NODE_PUBLISHED)
+ ->execute();
// Only show the block if the user has view access for the top-level node.
if ($nid) {
diff --git a/core/modules/book/src/Tests/BookTest.php b/core/modules/book/src/Tests/BookTest.php
index 015899295..cdd61c60a 100644
--- a/core/modules/book/src/Tests/BookTest.php
+++ b/core/modules/book/src/Tests/BookTest.php
@@ -570,34 +570,6 @@ class BookTest extends WebTestBase {
$this->assertEqual($child->id(), $second->book['bid'], '3rd-level child node is now second level when top-level node is deleted.');
}
- /**
- * Tests re-ordering of books.
- */
- public function testBookOrdering() {
- // Create new book.
- $this->createBook();
- $book = $this->book;
-
- $this->drupalLogin($this->adminUser);
- $node1 = $this->createBookNode($book->id());
- $node2 = $this->createBookNode($book->id());
- $pid = $node1->book['nid'];
-
- // Head to admin screen and attempt to re-order.
- $this->drupalGet('admin/structure/book/' . $book->id());
- $edit = array(
- "table[book-admin-{$node1->id()}][weight]" => 1,
- "table[book-admin-{$node2->id()}][weight]" => 2,
- // Put node 2 under node 1.
- "table[book-admin-{$node2->id()}][pid]" => $pid,
- );
- $this->drupalPostForm(NULL, $edit, t('Save book pages'));
- // Verify weight was updated.
- $this->assertFieldByName("table[book-admin-{$node1->id()}][weight]", 1);
- $this->assertFieldByName("table[book-admin-{$node2->id()}][weight]", 2);
- $this->assertFieldByName("table[book-admin-{$node2->id()}][pid]", $pid);
- }
-
/**
* Tests outline of a book.
*/
@@ -752,4 +724,29 @@ class BookTest extends WebTestBase {
$this->assertEqual($book_node->book['bid'], $this->book->id());
}
+ /**
+ * Tests the book navigation block when book is unpublished.
+ *
+ * There was a fatal error with "Show block only on book pages" block mode.
+ */
+ public function testBookNavigationBlockOnUnpublishedBook() {
+ // Create a new book.
+ $this->createBook();
+
+ // Create administrator user.
+ $administratorUser = $this->drupalCreateUser(['administer blocks', 'administer nodes', 'bypass node access']);
+ $this->drupalLogin($administratorUser);
+
+ // Enable the block with "Show block only on book pages" mode.
+ $this->drupalPlaceBlock('book_navigation', ['block_mode' => 'book pages']);
+
+ // Unpublish book node.
+ $edit = [];
+ $this->drupalPostForm('node/' . $this->book->id() . '/edit', $edit, t('Save and unpublish'));
+
+ // Test node page.
+ $this->drupalGet('node/' . $this->book->id());
+ $this->assertText($this->book->label(), 'Unpublished book with "Show block only on book pages" book navigation settings.');
+ }
+
}
diff --git a/core/modules/book/tests/src/FunctionalJavascript/BookJavascriptTest.php b/core/modules/book/tests/src/FunctionalJavascript/BookJavascriptTest.php
new file mode 100644
index 000000000..e895b6824
--- /dev/null
+++ b/core/modules/book/tests/src/FunctionalJavascript/BookJavascriptTest.php
@@ -0,0 +1,160 @@
+ 'book',
+ 'title' => 'Book',
+ 'book' => ['bid' => 'new'],
+ ]);
+ $book->save();
+ $page1 = Node::create([
+ 'type' => 'book',
+ 'title' => '1st page',
+ 'book' => ['bid' => $book->id(), 'pid' => $book->id(), 'weight' => 0],
+ ]);
+ $page1->save();
+ $page2 = Node::create([
+ 'type' => 'book',
+ 'title' => '2nd page',
+ 'book' => ['bid' => $book->id(), 'pid' => $book->id(), 'weight' => 1],
+ ]);
+ $page2->save();
+
+ // Head to admin screen and attempt to re-order.
+ $this->drupalLogin($this->drupalCreateUser(['administer book outlines']));
+ $this->drupalGet('admin/structure/book/' . $book->id());
+
+ $page = $this->getSession()->getPage();
+
+ $weight_select1 = $page->findField("table[book-admin-{$page1->id()}][weight]");
+ $weight_select2 = $page->findField("table[book-admin-{$page2->id()}][weight]");
+
+ // Check that rows weight selects are hidden.
+ $this->assertFalse($weight_select1->isVisible());
+ $this->assertFalse($weight_select2->isVisible());
+
+ // Check that '2nd page' row is heavier than '1st page' row.
+ $this->assertGreaterThan($weight_select1->getValue(), $weight_select2->getValue());
+
+ // Check that '1st page' precedes the '2nd page'.
+ $this->assertOrderInPage(['1st page', '2nd page']);
+
+ // Check that the 'unsaved changes' text is not present in the message area.
+ $this->assertSession()->pageTextNotContains('You have unsaved changes.');
+
+ // Drag and drop the '1st page' row over the '2nd page' row.
+ // @todo: Test also the reverse, '2nd page' over '1st page', when
+ // https://www.drupal.org/node/2769825 is fixed.
+ // @see https://www.drupal.org/node/2769825
+ $dragged = $this->xpath("//tr[@data-drupal-selector='edit-table-book-admin-{$page1->id()}']//a[@class='tabledrag-handle']")[0];
+ $target = $this->xpath("//tr[@data-drupal-selector='edit-table-book-admin-{$page2->id()}']//a[@class='tabledrag-handle']")[0];
+ $dragged->dragTo($target);
+
+ // Give javascript some time to manipulate the DOM.
+ $this->getSession()->wait(1000, 'jQuery(".tabledrag-changed-warning").is(":visible")');
+
+ // Check that the 'unsaved changes' text appeared in the message area.
+ $this->assertSession()->pageTextContains('You have unsaved changes.');
+
+ // Check that '2nd page' page precedes the '1st page'.
+ $this->assertOrderInPage(['2nd page', '1st page']);
+
+ $this->submitForm([], 'Save book pages');
+ $this->assertSession()->pageTextContains(new FormattableMarkup('Updated book @book.', ['@book' => $book->getTitle()]));
+
+ // Check that page reordering was done in the backend for drag-n-drop.
+ $page1 = Node::load($page1->id());
+ $page2 = Node::load($page2->id());
+ $this->assertGreaterThan($page2->book['weight'], $page1->book['weight']);
+
+ // Check again that '2nd page' is on top after form submit in the UI.
+ $this->assertOrderInPage(['2nd page', '1st page']);
+
+ // Toggle row weight selects as visible.
+ $page->findButton('Show row weights')->click();
+
+ // Check that rows weight selects are visible.
+ $this->assertTrue($weight_select1->isVisible());
+ $this->assertTrue($weight_select2->isVisible());
+
+ // Check that '1st page' row became heavier than '2nd page' row.
+ $this->assertGreaterThan($weight_select2->getValue(), $weight_select1->getValue());
+
+ // Reverse again using the weight fields. Use the current values so the test
+ // doesn't rely on knowing the values in the select boxes.
+ $value1 = $weight_select1->getValue();
+ $value2 = $weight_select2->getValue();
+ $weight_select1->setValue($value2);
+ $weight_select2->setValue($value1);
+
+ // Toggle row weight selects back to hidden.
+ $page->findButton('Hide row weights')->click();
+
+ // Check that rows weight selects are hidden again.
+ $this->assertFalse($weight_select1->isVisible());
+ $this->assertFalse($weight_select2->isVisible());
+
+ $this->submitForm([], 'Save book pages');
+ $this->assertSession()->pageTextContains(new FormattableMarkup('Updated book @book.', ['@book' => $book->getTitle()]));
+
+ // Check that the '1st page' is first again.
+ $this->assertOrderInPage(['1st page', '2nd page']);
+
+ // Check that page reordering was done in the backend for manual weight
+ // field usage.
+ $page1 = Node::load($page1->id());
+ $page2 = Node::load($page2->id());
+ $this->assertGreaterThan($page2->book['weight'], $page1->book['weight']);
+ }
+
+ /**
+ * Asserts that several pieces of markup are in a given order in the page.
+ *
+ * @param string[] $items
+ * An ordered list of strings.
+ *
+ * @throws \Behat\Mink\Exception\ExpectationException
+ * When any of the given string is not found.
+ *
+ * @todo Remove this once https://www.drupal.org/node/2817657 is committed.
+ */
+ protected function assertOrderInPage(array $items) {
+ $session = $this->getSession();
+ $text = $session->getPage()->getHtml();
+ $strings = [];
+ foreach ($items as $item) {
+ if (($pos = strpos($text, $item)) === FALSE) {
+ throw new ExpectationException("Cannot find '$item' in the page", $session->getDriver());
+ }
+ $strings[$pos] = $item;
+ }
+ ksort($strings);
+ $ordered = implode(', ', array_map(function ($item) {
+ return "'$item'";
+ }, $items));
+ $this->assertSame($items, array_values($strings), "Found strings, ordered as: $ordered.");
+ }
+
+}
diff --git a/core/modules/book/tests/src/Kernel/Plugin/migrate/source/d6/BookTest.php b/core/modules/book/tests/src/Kernel/Plugin/migrate/source/d6/BookTest.php
new file mode 100644
index 000000000..feece1f7b
--- /dev/null
+++ b/core/modules/book/tests/src/Kernel/Plugin/migrate/source/d6/BookTest.php
@@ -0,0 +1,84 @@
+ '1',
+ 'nid' => '4',
+ 'bid' => '4',
+ ],
+ ];
+ $tests[0]['source_data']['menu_links'] = [
+ [
+ 'menu_name' => 'book-toc-1',
+ 'mlid' => '1',
+ 'plid' => '0',
+ 'link_path' => 'node/4',
+ 'router_path' => 'node/%',
+ 'link_title' => 'Test top book title',
+ 'options' => 'a:0:{}',
+ 'module' => 'book',
+ 'hidden' => '0',
+ 'external' => '0',
+ 'has_children' => '1',
+ 'expanded' => '0',
+ 'weight' => '-10',
+ 'depth' => '1',
+ 'customized' => '0',
+ 'p1' => '1',
+ 'p2' => '0',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'nid' => '4',
+ 'bid' => '4',
+ 'mlid' => '1',
+ 'plid' => '0',
+ 'weight' => '-10',
+ 'p1' => '1',
+ 'p2' => '0',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ ],
+ ];
+ return $tests;
+ }
+
+}
diff --git a/core/modules/book/tests/src/Unit/Plugin/migrate/source/d6/BookTest.php b/core/modules/book/tests/src/Unit/Plugin/migrate/source/d6/BookTest.php
deleted file mode 100644
index 6433cadcd..000000000
--- a/core/modules/book/tests/src/Unit/Plugin/migrate/source/d6/BookTest.php
+++ /dev/null
@@ -1,85 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd6_book',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'nid' => '4',
- 'bid' => '4',
- 'mlid' => '1',
- 'plid' => '0',
- 'weight' => '-10',
- 'p1' => '1',
- 'p2' => '0',
- 'p3' => '0',
- 'p4' => '0',
- 'p5' => '0',
- 'p6' => '0',
- 'p7' => '0',
- 'p8' => '0',
- 'p9' => '0',
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['book'] = array(
- array(
- 'mlid' => '1',
- 'nid' => '4',
- 'bid' => '4',
- ),
- );
- $this->databaseContents['menu_links'] = array(
- array(
- 'menu_name' => 'book-toc-1',
- 'mlid' => '1',
- 'plid' => '0',
- 'link_path' => 'node/4',
- 'router_path' => 'node/%',
- 'link_title' => 'Test top book title',
- 'options' => 'a:0:{}',
- 'module' => 'book',
- 'hidden' => '0',
- 'external' => '0',
- 'has_children' => '1',
- 'expanded' => '0',
- 'weight' => '-10',
- 'depth' => '1',
- 'customized' => '0',
- 'p1' => '1',
- 'p2' => '0',
- 'p3' => '0',
- 'p4' => '0',
- 'p5' => '0',
- 'p6' => '0',
- 'p7' => '0',
- 'p8' => '0',
- 'p9' => '0',
- 'updated' => '0',
- ),
- );
- parent::setUp();
- }
-
-}
diff --git a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php
index 3728790ca..605dc56a7 100644
--- a/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php
+++ b/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php
@@ -5,6 +5,7 @@ namespace Drupal\ckeditor\Plugin\CKEditorPlugin;
use Drupal\ckeditor\CKEditorPluginBase;
use Drupal\ckeditor\CKEditorPluginContextualInterface;
use Drupal\ckeditor\CKEditorPluginManager;
+use Drupal\Component\Utility\Html;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -369,7 +370,7 @@ class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInter
foreach ($possible_format_tags as $tag) {
$input = '<' . $tag . '>TEST' . $tag . '>';
$output = trim(check_markup($input, $editor->id()));
- if ($input == $output) {
+ if (Html::load($output)->getElementsByTagName($tag)->length !== 0) {
$format_tags[] = $tag;
}
}
diff --git a/core/modules/ckeditor/tests/modules/config/schema/ckeditor_test.schema.yml b/core/modules/ckeditor/tests/modules/config/schema/ckeditor_test.schema.yml
index e6734a7c5..e7305a291 100644
--- a/core/modules/ckeditor/tests/modules/config/schema/ckeditor_test.schema.yml
+++ b/core/modules/ckeditor/tests/modules/config/schema/ckeditor_test.schema.yml
@@ -5,3 +5,12 @@ ckeditor.plugin.llama_contextual_and_button:
ultra_llama_mode:
type: boolean
label: 'Ultra llama mode'
+
+filter_settings.test_attribute_filter:
+ type: filter
+ label: 'Test Attribute Filter'
+ mapping:
+ tags:
+ type: sequence
+ sequence:
+ type: string
diff --git a/core/modules/ckeditor/tests/modules/src/Plugin/Filter/TestAttributeFilter.php b/core/modules/ckeditor/tests/modules/src/Plugin/Filter/TestAttributeFilter.php
new file mode 100644
index 000000000..885877b4b
--- /dev/null
+++ b/core/modules/ckeditor/tests/modules/src/Plugin/Filter/TestAttributeFilter.php
@@ -0,0 +1,38 @@
+settings['tags'] as $tag) {
+ $tag_elements = $document->getElementsByTagName($tag);
+ foreach ($tag_elements as $tag_element) {
+ $tag_element->setAttribute('test_attribute', 'test attribute value');
+ }
+ }
+ return new FilterProcessResult(Html::serialize($document));
+ }
+
+}
diff --git a/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php b/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php
new file mode 100644
index 000000000..aecc22802
--- /dev/null
+++ b/core/modules/ckeditor/tests/src/Kernel/Plugin/CKEditorPlugin/InternalTest.php
@@ -0,0 +1,140 @@
+installEntitySchema('editor');
+ $this->installEntitySchema('filter_format');
+
+ $this->format = FilterFormat::create([
+ 'format' => 'test_format',
+ 'name' => $this->randomMachineName(),
+ ]);
+ $this->format->save();
+
+ $this->editor = Editor::create([
+ 'editor' => 'ckeditor',
+ 'format' => 'test_format',
+ 'settings' => [
+ 'toolbar' => [
+ 'rows' => [
+ [
+ [
+ 'name' => 'Enabled Buttons',
+ 'items' => [
+ 'Format',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ]);
+ $this->editor->save();
+
+ $this->ckeditorPluginManager = $this->container->get('plugin.manager.ckeditor.plugin');
+ }
+
+ /**
+ * Test the format tags settings.
+ *
+ * @dataProvider formatTagsSettingsTestCases
+ */
+ public function testFormatTagsSettings($filter_plugins, $expected_format_tags) {
+ foreach ($filter_plugins as $filter_plugin_id => $filter_plugin_settings) {
+ $this->format->setFilterConfig($filter_plugin_id, $filter_plugin_settings);
+ }
+ $this->format->save();
+
+ $internal_plugin = $this->ckeditorPluginManager->createInstance('internal', []);
+ $plugin_config = $internal_plugin->getConfig($this->editor);
+ $this->assertEquals($expected_format_tags, explode(';', $plugin_config['format_tags']));
+ }
+
+ /**
+ * A data provider for testFormatTagsSettings.
+ */
+ public function formatTagsSettingsTestCases() {
+ $all_tags = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre'];
+
+ return [
+ 'No filter plugins enabled (all tags allowed)' => [
+ [],
+ $all_tags,
+ ],
+ 'HTML filter plugin enabled (some tags filtered out)' => [
+ [
+ 'filter_html' => [
+ 'status' => 1,
+ 'settings' => [
+ 'allowed_html' => '
'
weight: 6
diff --git a/core/modules/language/migration_templates/default_language.yml b/core/modules/language/migration_templates/default_language.yml
index d7e52adf1..6d7604dee 100644
--- a/core/modules/language/migration_templates/default_language.yml
+++ b/core/modules/language/migration_templates/default_language.yml
@@ -10,9 +10,22 @@ source:
process:
default_langcode:
-
- plugin: callback
- callable: get_object_vars
+ plugin: default_value
source: language_default
+ default_value:
+ 'language': 'en'
+ # Encode and decode to turn the default_language variable, which is
+ # an stdClass, into an array so it can be passed to extract.
+ -
+ plugin: callback
+ callable:
+ - '\Drupal\Component\Serialization\Json'
+ - 'encode'
+ -
+ plugin: callback
+ callable:
+ - '\Drupal\Component\Serialization\Json'
+ - 'decode'
-
plugin: extract
index:
diff --git a/core/modules/language/src/Form/NegotiationUrlForm.php b/core/modules/language/src/Form/NegotiationUrlForm.php
index 13fe368c3..d5ad8639e 100644
--- a/core/modules/language/src/Form/NegotiationUrlForm.php
+++ b/core/modules/language/src/Form/NegotiationUrlForm.php
@@ -23,7 +23,7 @@ class NegotiationUrlForm extends ConfigFormBase {
protected $languageManager;
/**
- * Constructs a new LanguageDeleteForm object.
+ * Constructs a new NegotiationUrlForm object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
diff --git a/core/modules/language/tests/src/Kernel/Migrate/MigrateDefaultLanguageTrait.php b/core/modules/language/tests/src/Kernel/Migrate/MigrateDefaultLanguageTrait.php
index deb60206e..94bb02d33 100644
--- a/core/modules/language/tests/src/Kernel/Migrate/MigrateDefaultLanguageTrait.php
+++ b/core/modules/language/tests/src/Kernel/Migrate/MigrateDefaultLanguageTrait.php
@@ -38,7 +38,7 @@ trait MigrateDefaultLanguageTrait {
// default_langcode config should be set.
$default_language = ConfigurableLanguage::load($langcode);
$this->assertNotNull($default_language);
- $this->assertIdentical($langcode, $this->config('system.site')->get('default_langcode'));
+ $this->assertSame($langcode, $this->config('system.site')->get('default_langcode'));
}
else {
// Otherwise, the migration log should contain an error message.
@@ -46,11 +46,31 @@ trait MigrateDefaultLanguageTrait {
$count = 0;
foreach ($messages as $message) {
$count++;
- $this->assertEqual($message->message, "The language '$langcode' does not exist on this site.");
- $this->assertEqual($message->level, MigrationInterface::MESSAGE_ERROR);
+ $this->assertSame($message->message, "The language '$langcode' does not exist on this site.");
+ $this->assertSame((int) $message->level, MigrationInterface::MESSAGE_ERROR);
}
- $this->assertEqual($count, 1);
+ $this->assertSame($count, 1);
}
}
+ /**
+ * Helper method to test migrating the default language when no default language is set.
+ */
+ protected function doTestMigrationWithUnsetVariable() {
+ // Delete the language_default variable.
+ $this->sourceDatabase->delete('variable')
+ ->condition('name', 'language_default' )
+ ->execute();
+
+ $this->startCollectingMessages();
+ $this->executeMigrations(['language', 'default_language']);
+ $messages = $this->migration->getIdMap()->getMessageIterator()->fetchAll();
+
+ // Make sure there's no migration exceptions.
+ $this->assertEmpty($messages);
+
+ // Make sure the default langcode is 'en', since it was the default on D6 & D7.
+ $this->assertSame('en', $this->config('system.site')->get('default_langcode'));
+ }
+
}
diff --git a/core/modules/language/tests/src/Kernel/Migrate/d6/MigrateDefaultLanguageTest.php b/core/modules/language/tests/src/Kernel/Migrate/d6/MigrateDefaultLanguageTest.php
index 20beb96bf..645af29fe 100644
--- a/core/modules/language/tests/src/Kernel/Migrate/d6/MigrateDefaultLanguageTest.php
+++ b/core/modules/language/tests/src/Kernel/Migrate/d6/MigrateDefaultLanguageTest.php
@@ -33,4 +33,11 @@ class MigrateDefaultLanguageTest extends MigrateDrupal6TestBase {
$this->doTestMigration('tv', FALSE);
}
+ /**
+ * Tests language_default migration with unset variable.
+ */
+ public function testMigrationWithUnsetVariable() {
+ $this->doTestMigrationWithUnsetVariable();
+ }
+
}
diff --git a/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateDefaultLanguageTest.php b/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateDefaultLanguageTest.php
index 0e584e53b..79769bc00 100644
--- a/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateDefaultLanguageTest.php
+++ b/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateDefaultLanguageTest.php
@@ -33,4 +33,11 @@ class MigrateDefaultLanguageTest extends MigrateDrupal7TestBase {
$this->doTestMigration('tv', FALSE);
}
+ /**
+ * Tests language_default migration with unset variable.
+ */
+ public function testMigrationWithUnsetVariable() {
+ $this->doTestMigrationWithUnsetVariable();
+ }
+
}
diff --git a/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php b/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php
index 3d657baa5..5de568dab 100644
--- a/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php
+++ b/core/modules/link/src/Plugin/migrate/cckfield/d7/LinkField.php
@@ -3,6 +3,7 @@
namespace Drupal\link\Plugin\migrate\cckfield\d7;
use Drupal\link\Plugin\migrate\cckfield\LinkField as D6LinkField;
+use Drupal\migrate\Plugin\MigrationInterface;
/**
* @MigrateCckField(
@@ -27,4 +28,21 @@ class LinkField extends D6LinkField {
return ['link_field' => 'link_default'];
}
+ /**
+ * {@inheritdoc}
+ */
+ public function processFieldInstance(MigrationInterface $migration) {
+ $process = [
+ 'plugin' => 'static_map',
+ 'source' => 'instance_settings/title',
+ 'bypass' => TRUE,
+ 'map' => [
+ 'disabled' => DRUPAL_DISABLED,
+ 'optional' => DRUPAL_OPTIONAL,
+ 'required' => DRUPAL_REQUIRED,
+ ],
+ ];
+ $migration->mergeProcessOfProperty('settings/title', $process);
+ }
+
}
diff --git a/core/modules/locale/config/optional/tour.tour.locale.yml b/core/modules/locale/config/optional/tour.tour.locale.yml
index 041a62352..b6b376cf6 100644
--- a/core/modules/locale/config/optional/tour.tour.locale.yml
+++ b/core/modules/locale/config/optional/tour.tour.locale.yml
@@ -13,7 +13,7 @@ tips:
id: locale-overview
plugin: text
label: 'User interface translation'
- body: 'This page allows you to translate the user interface or modify existing translations. If you have installed your site initially in English, you must first add another language on the Languages page, in order to use this page.'
+ body: 'This page allows you to translate the user interface or modify existing translations. If you have installed your site initially in English, you must first add another language on the Languages page, in order to use this page.'
weight: 1
locale-language:
id: locale-language
@@ -67,5 +67,5 @@ tips:
id: locale-continue
plugin: text
label: 'Continuing on'
- body: 'The translations you have made here will be used on your site''s user interface. If you want to use them on another site or modify them on an external translation editor, you can export them to a .po file and import them later.'
+ body: 'The translations you have made here will be used on your site''s user interface. If you want to use them on another site or modify them on an external translation editor, you can export them to a .po file and import them later.'
weight: 8
diff --git a/core/modules/menu_link_content/migration_templates/menu_links.yml b/core/modules/menu_link_content/migration_templates/d6_menu_links.yml
similarity index 90%
rename from core/modules/menu_link_content/migration_templates/menu_links.yml
rename to core/modules/menu_link_content/migration_templates/d6_menu_links.yml
index b6bcdaf74..c418bfebd 100644
--- a/core/modules/menu_link_content/migration_templates/menu_links.yml
+++ b/core/modules/menu_link_content/migration_templates/d6_menu_links.yml
@@ -1,8 +1,7 @@
-id: menu_links
+id: d6_menu_links
label: Menu links
migration_tags:
- Drupal 6
- - Drupal 7
source:
plugin: menu_link
process:
@@ -13,8 +12,11 @@ process:
-
plugin: migration
# The menu migration is in the system module.
- migration: menu
+ migration: d6_menu
source: menu_name
+ -
+ plugin: skip_on_empty
+ method: row
-
plugin: static_map
map:
@@ -51,4 +53,4 @@ destination:
no_stub: true
migration_dependencies:
required:
- - menu
+ - d6_menu
diff --git a/core/modules/menu_link_content/migration_templates/d7_menu_links.yml b/core/modules/menu_link_content/migration_templates/d7_menu_links.yml
new file mode 100644
index 000000000..8581e2c07
--- /dev/null
+++ b/core/modules/menu_link_content/migration_templates/d7_menu_links.yml
@@ -0,0 +1,52 @@
+id: d7_menu_links
+label: Menu links
+migration_tags:
+ - Drupal 7
+source:
+ plugin: menu_link
+ constants:
+ bundle: menu_link_content
+process:
+ id: mlid
+ bundle: 'constants/bundle'
+ title: link_title
+ description: description
+ menu_name:
+ -
+ plugin: migration
+ migration: d7_menu
+ source: menu_name
+ -
+ plugin: skip_on_empty
+ method: row
+ 'link/uri':
+ plugin: d7_internal_uri
+ source:
+ - link_path
+ 'link/options': options
+ route:
+ plugin: route
+ source:
+ - link_path
+ - options
+ route_name: '@route/route_name'
+ route_parameters: '@route/route_parameters'
+ url: '@route/url'
+ options: '@route/options'
+ external: external
+ weight: weight
+ expanded: expanded
+ enabled: enabled
+ parent:
+ plugin: menu_link_parent
+ source:
+ - plid
+ - '@menu_name'
+ - parent_link_path
+ changed: updated
+destination:
+ plugin: entity:menu_link_content
+ no_stub: true
+migration_dependencies:
+ required:
+ - d7_menu
diff --git a/core/modules/menu_link_content/src/Plugin/migrate/process/d7/InternalUri.php b/core/modules/menu_link_content/src/Plugin/migrate/process/d7/InternalUri.php
new file mode 100644
index 000000000..9d373568f
--- /dev/null
+++ b/core/modules/menu_link_content/src/Plugin/migrate/process/d7/InternalUri.php
@@ -0,0 +1,43 @@
+') {
+ return 'internal:/';
+ }
+ else {
+ return 'internal:/' . $path;
+ }
+ }
+ return $path;
+ }
+
+}
diff --git a/core/modules/menu_link_content/tests/src/Kernel/Migrate/d6/MigrateMenuLinkTest.php b/core/modules/menu_link_content/tests/src/Kernel/Migrate/d6/MigrateMenuLinkTest.php
index 58171e361..eea87dbab 100644
--- a/core/modules/menu_link_content/tests/src/Kernel/Migrate/d6/MigrateMenuLinkTest.php
+++ b/core/modules/menu_link_content/tests/src/Kernel/Migrate/d6/MigrateMenuLinkTest.php
@@ -23,7 +23,7 @@ class MigrateMenuLinkTest extends MigrateDrupal6TestBase {
protected function setUp() {
parent::setUp();
$this->installEntitySchema('menu_link_content');
- $this->executeMigrations(['menu', 'menu_links']);
+ $this->executeMigrations(['d6_menu', 'd6_menu_links']);
}
/**
diff --git a/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php b/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php
index 484c5a49d..0c915d423 100644
--- a/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php
+++ b/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\menu_link_content\Kernel\Migrate\d7;
-use Drupal\Core\Database\Database;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\menu_link_content\MenuLinkContentInterface;
@@ -27,7 +26,7 @@ class MigrateMenuLinkTest extends MigrateDrupal7TestBase {
protected function setUp() {
parent::setUp();
$this->installEntitySchema('menu_link_content');
- $this->executeMigration('menu');
+ $this->executeMigrations(['d7_menu', 'd7_menu_links']);
\Drupal::service('router.builder')->rebuild();
}
@@ -52,21 +51,24 @@ class MigrateMenuLinkTest extends MigrateDrupal7TestBase {
* The expected URI of the link.
* @param int $weight
* The expected weight of the link.
+ *
+ * @return \Drupal\menu_link_content\MenuLinkContentInterface
+ * The menu link content.
*/
protected function assertEntity($id, $title, $menu, $description, $enabled, $expanded, array $attributes, $uri, $weight) {
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $menu_link */
$menu_link = MenuLinkContent::load($id);
$this->assertTrue($menu_link instanceof MenuLinkContentInterface);
- $this->assertIdentical($title, $menu_link->getTitle());
- $this->assertIdentical($menu, $menu_link->getMenuName());
+ $this->assertSame($title, $menu_link->getTitle());
+ $this->assertSame($menu, $menu_link->getMenuName());
// The migration sets the description of the link to the value of the
// 'title' attribute. Bit strange, but there you go.
- $this->assertIdentical($description, $menu_link->getDescription());
- $this->assertIdentical($enabled, $menu_link->isEnabled());
- $this->assertIdentical($expanded, $menu_link->isExpanded());
- $this->assertIdentical($attributes, $menu_link->link->options);
- $this->assertIdentical($uri, $menu_link->link->uri);
- $this->assertIdentical($weight, $menu_link->getWeight());
+ $this->assertSame($description, $menu_link->getDescription());
+ $this->assertSame($enabled, $menu_link->isEnabled());
+ $this->assertSame($expanded, $menu_link->isExpanded());
+ $this->assertSame($attributes, $menu_link->link->options);
+ $this->assertSame($uri, $menu_link->link->uri);
+ $this->assertSame($weight, $menu_link->getWeight());
return $menu_link;
}
@@ -74,31 +76,36 @@ class MigrateMenuLinkTest extends MigrateDrupal7TestBase {
* Tests migration of menu links.
*/
public function testMenuLinks() {
- $this->executeMigration('menu_links');
$this->assertEntity(469, 'Bing', static::MENU_NAME, 'Bing', TRUE, FALSE, ['attributes' => ['title' => 'Bing']], 'http://bing.com', 0);
$this->assertEntity(467, 'Google', static::MENU_NAME, 'Google', TRUE, FALSE, ['attributes' => ['title' => 'Google']], 'http://google.com', 0);
$this->assertEntity(468, 'Yahoo', static::MENU_NAME, 'Yahoo', TRUE, FALSE, ['attributes' => ['title' => 'Yahoo']], 'http://yahoo.com', 0);
+ // Tests migrating an external link with an undefined title attribute.
+ $this->assertEntity(470, 'Ask', static::MENU_NAME, NULL, TRUE, FALSE, [], 'http://ask.com', 0);
+ $this->assertEntity(245, 'Home', 'main', NULL, TRUE, FALSE, [], 'internal:/', 0);
+ $this->assertEntity(478, 'custom link test', 'admin', NULL, TRUE, FALSE, ['attributes' => ['title' => '']], 'internal:/admin/content/book', 0);
+ $this->assertEntity(479, 'node link test', 'tools', 'node 3', TRUE, FALSE, ['attributes' => ['title' => 'node 3']], 'entity:node/3', 3);
+
$menu_link_tree_service = \Drupal::service('menu.link_tree');
$parameters = new MenuTreeParameters();
$tree = $menu_link_tree_service->load(static::MENU_NAME, $parameters);
- $this->assertEqual(2, count($tree));
+ $this->assertCount(2, $tree);
$children = 0;
$google_found = FALSE;
foreach ($tree as $menu_link_tree_element) {
$children += $menu_link_tree_element->hasChildren;
if ($menu_link_tree_element->link->getUrlObject()->toString() == 'http://bing.com') {
- $this->assertEqual(reset($menu_link_tree_element->subtree)->link->getUrlObject()->toString(), 'http://google.com');
+ $this->assertEquals(reset($menu_link_tree_element->subtree)->link->getUrlObject()->toString(), 'http://google.com');
$google_found = TRUE;
}
}
- $this->assertEqual(1, $children);
+ $this->assertEquals(1, $children);
$this->assertTrue($google_found);
// Now find the custom link under a system link.
$parameters->root = 'system.admin_structure';
$tree = $menu_link_tree_service->load(static::MENU_NAME, $parameters);
$found = FALSE;
foreach ($tree as $menu_link_tree_element) {
- $this->pass($menu_link_tree_element->link->getUrlObject()->toString());
+ $this->assertTrue($menu_link_tree_element->link->getUrlObject()->toString());
if ($menu_link_tree_element->link->getTitle() == 'custom link test') {
$found = TRUE;
break;
@@ -107,20 +114,4 @@ class MigrateMenuLinkTest extends MigrateDrupal7TestBase {
$this->assertTrue($found);
}
- /**
- * Tests migrating a link with an undefined title attribute.
- */
- public function testUndefinedLinkTitle() {
- Database::getConnection('default', 'migrate')
- ->update('menu_links')
- ->fields(array(
- 'options' => 'a:0:{}',
- ))
- ->condition('mlid', 467)
- ->execute();
-
- $this->executeMigration('menu_links');
- $this->assertEntity(467, 'Google', static::MENU_NAME, NULL, TRUE, FALSE, [], 'http://google.com', 0);
- }
-
}
diff --git a/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/MenuLinkTest.php b/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/MenuLinkTest.php
new file mode 100644
index 000000000..db6c5472c
--- /dev/null
+++ b/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/MenuLinkTest.php
@@ -0,0 +1,225 @@
+ 'menu-test-menu',
+ 'mlid' => 140,
+ 'plid' => 0,
+ 'link_path' => 'admin/config/system/cron',
+ 'router_path' => 'admin/config/system/cron',
+ 'link_title' => 'Cron',
+ 'options' => array(),
+ 'module' => 'system',
+ 'hidden' => 0,
+ 'external' => 0,
+ 'has_children' => 0,
+ 'expanded' => 0,
+ 'weight' => 0,
+ 'depth' => 0,
+ 'customized' => 1,
+ 'p1' => '0',
+ 'p2' => '0',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+ 'description' => '',
+ ],
+ [
+ // D6 customized menu link, provided by menu module.
+ 'menu_name' => 'menu-test-menu',
+ 'mlid' => 141,
+ 'plid' => 0,
+ 'link_path' => 'node/141',
+ 'router_path' => 'node/%',
+ 'link_title' => 'Node 141',
+ 'options' => array(),
+ 'module' => 'menu',
+ 'hidden' => 0,
+ 'external' => 0,
+ 'has_children' => 0,
+ 'expanded' => 0,
+ 'weight' => 0,
+ 'depth' => 0,
+ 'customized' => 1,
+ 'p1' => '0',
+ 'p2' => '0',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+ 'description' => '',
+ ],
+ [
+ // D6 non-customized menu link, provided by menu module.
+ 'menu_name' => 'menu-test-menu',
+ 'mlid' => 142,
+ 'plid' => 0,
+ 'link_path' => 'node/142',
+ 'router_path' => 'node/%',
+ 'link_title' => 'Node 142',
+ 'options' => array(),
+ 'module' => 'menu',
+ 'hidden' => 0,
+ 'external' => 0,
+ 'has_children' => 0,
+ 'expanded' => 0,
+ 'weight' => 0,
+ 'depth' => 0,
+ 'customized' => 0,
+ 'p1' => '0',
+ 'p2' => '0',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+ 'description' => '',
+ ],
+ [
+ 'menu_name' => 'menu-test-menu',
+ 'mlid' => 138,
+ 'plid' => 0,
+ 'link_path' => 'admin',
+ 'router_path' => 'admin',
+ 'link_title' => 'Test 1',
+ 'options' => array('attributes' => array('title' => 'Test menu link 1')),
+ 'module' => 'menu',
+ 'hidden' => 0,
+ 'external' => 0,
+ 'has_children' => 1,
+ 'expanded' => 0,
+ 'weight' => 15,
+ 'depth' => 1,
+ 'customized' => 1,
+ 'p1' => '138',
+ 'p2' => '0',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+ 'description' => 'Test menu link 1',
+ ],
+ [
+ 'menu_name' => 'menu-test-menu',
+ 'mlid' => 139,
+ 'plid' => 138,
+ 'link_path' => 'admin/modules',
+ 'router_path' => 'admin/modules',
+ 'link_title' => 'Test 2',
+ 'options' => array('attributes' => array('title' => 'Test menu link 2')),
+ 'module' => 'menu',
+ 'hidden' => 0,
+ 'external' => 0,
+ 'has_children' => 0,
+ 'expanded' => 0,
+ 'weight' => 12,
+ 'depth' => 2,
+ 'customized' => 1,
+ 'p1' => '138',
+ 'p2' => '139',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+ 'description' => 'Test menu link 2',
+ ],
+ [
+ 'menu_name' => 'menu-user',
+ 'mlid' => 143,
+ 'plid' => 0,
+ 'link_path' => 'admin/build/menu-customize/navigation',
+ 'router_path' => 'admin/build/menu-customize/%',
+ 'link_title' => 'Navigation',
+ 'options' => array(),
+ 'module' => 'menu',
+ 'hidden' => 0,
+ 'external' => 0,
+ 'has_children' => 0,
+ 'expanded' => 0,
+ 'weight' => 0,
+ 'depth' => 0,
+ 'customized' => 0,
+ 'p1' => '0',
+ 'p2' => '0',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+ 'description' => '',
+ ],
+ ];
+
+ // Add long link title attributes to source data.
+ $title = $this->getRandomGenerator()->string('500');
+ $tests[0]['source_data']['menu_links'][0]['options']['attributes']['title'] = $title;
+
+ // Build the expected results.
+ $expected = $tests[0]['source_data']['menu_links'];
+
+ // Add long link title attributes to expected results.
+ $expected[0]['description'] = Unicode::truncate($title, 255);
+
+ // Don't expect D6 menu link to a custom menu, provided by menu module.
+ unset($expected[5]);
+
+ array_walk($tests[0]['source_data']['menu_links'], function (&$row) {
+ $row['options'] = serialize($row['options']);
+ });
+
+ $tests[0]['expected_data'] = $expected;
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/process/d7/InternalUriTest.php b/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/process/d7/InternalUriTest.php
new file mode 100644
index 000000000..9292718ae
--- /dev/null
+++ b/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/process/d7/InternalUriTest.php
@@ -0,0 +1,73 @@
+processPlugin = new InternalUri([], 'd7_internal_uri', []);
+ }
+
+ /**
+ * Tests InternalUri::transform().
+ *
+ * @param array $value
+ * The value to pass to InternalUri::transform().
+ * @param string $expected
+ * The expected return value of InternalUri::transform().
+ *
+ * @dataProvider providerTestTransform
+ *
+ * @covers ::transform
+ */
+ public function testTransform(array $value, $expected) {
+ $migrate_executable = $this->prophesize(MigrateExecutableInterface::class);
+ $row = $this->prophesize(Row::class);
+
+ $actual = $this->processPlugin->transform($value, $migrate_executable->reveal(), $row->reveal(), 'link/uri');
+ $this->assertEquals($expected, $actual);
+ }
+
+ /**
+ * Provides test cases for InternalUriTest::testTransform().
+ *
+ * @return array
+ * An array of test cases, each which the following values:
+ * - The value array to pass to InternalUri::transform().
+ * - The expected path returned by InternalUri::transform().
+ */
+ public function providerTestTransform() {
+ $tests = [];
+ $tests['with_scheme'] = [['http://example.com'], 'http://example.com'];
+ $tests['leading_slash'] = [['/test'], 'internal:/test'];
+ $tests['without_scheme'] = [['test'], 'internal:/test'];
+ $tests['front'] = [[''], 'internal:/'];
+ $tests['node'] = [['node/27'], 'entity:node/27'];
+ return $tests;
+ }
+
+}
diff --git a/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/MenuLinkSourceTest.php b/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/MenuLinkSourceTest.php
deleted file mode 100644
index 3c087dbeb..000000000
--- a/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/MenuLinkSourceTest.php
+++ /dev/null
@@ -1,217 +0,0 @@
- 'mlid',
- 'source' => array(
- 'plugin' => 'menu_link',
- ),
- );
-
- protected $expectedResults = array(
- array(
- // Customized menu link, provided by system module.
- 'menu_name' => 'menu-test-menu',
- 'mlid' => 140,
- 'plid' => 0,
- 'link_path' => 'admin/config/system/cron',
- 'router_path' => 'admin/config/system/cron',
- 'link_title' => 'Cron',
- 'options' => array(),
- 'module' => 'system',
- 'hidden' => 0,
- 'external' => 0,
- 'has_children' => 0,
- 'expanded' => 0,
- 'weight' => 0,
- 'depth' => 0,
- 'customized' => 1,
- 'p1' => '0',
- 'p2' => '0',
- 'p3' => '0',
- 'p4' => '0',
- 'p5' => '0',
- 'p6' => '0',
- 'p7' => '0',
- 'p8' => '0',
- 'p9' => '0',
- 'updated' => '0',
- ),
- array(
- // D6 customized menu link, provided by menu module.
- 'menu_name' => 'menu-test-menu',
- 'mlid' => 141,
- 'plid' => 0,
- 'link_path' => 'node/141',
- 'router_path' => 'node/%',
- 'link_title' => 'Node 141',
- 'options' => array(),
- 'module' => 'menu',
- 'hidden' => 0,
- 'external' => 0,
- 'has_children' => 0,
- 'expanded' => 0,
- 'weight' => 0,
- 'depth' => 0,
- 'customized' => 1,
- 'p1' => '0',
- 'p2' => '0',
- 'p3' => '0',
- 'p4' => '0',
- 'p5' => '0',
- 'p6' => '0',
- 'p7' => '0',
- 'p8' => '0',
- 'p9' => '0',
- 'updated' => '0',
- 'description' => '',
- ),
- array(
- // D6 non-customized menu link, provided by menu module.
- 'menu_name' => 'menu-test-menu',
- 'mlid' => 142,
- 'plid' => 0,
- 'link_path' => 'node/142',
- 'router_path' => 'node/%',
- 'link_title' => 'Node 142',
- 'options' => array(),
- 'module' => 'menu',
- 'hidden' => 0,
- 'external' => 0,
- 'has_children' => 0,
- 'expanded' => 0,
- 'weight' => 0,
- 'depth' => 0,
- 'customized' => 0,
- 'p1' => '0',
- 'p2' => '0',
- 'p3' => '0',
- 'p4' => '0',
- 'p5' => '0',
- 'p6' => '0',
- 'p7' => '0',
- 'p8' => '0',
- 'p9' => '0',
- 'updated' => '0',
- 'description' => '',
- ),
- array(
- 'menu_name' => 'menu-test-menu',
- 'mlid' => 138,
- 'plid' => 0,
- 'link_path' => 'admin',
- 'router_path' => 'admin',
- 'link_title' => 'Test 1',
- 'options' => array('attributes' => array('title' => 'Test menu link 1')),
- 'module' => 'menu',
- 'hidden' => 0,
- 'external' => 0,
- 'has_children' => 1,
- 'expanded' => 0,
- 'weight' => 15,
- 'depth' => 1,
- 'customized' => 1,
- 'p1' => '138',
- 'p2' => '0',
- 'p3' => '0',
- 'p4' => '0',
- 'p5' => '0',
- 'p6' => '0',
- 'p7' => '0',
- 'p8' => '0',
- 'p9' => '0',
- 'updated' => '0',
- 'description' => 'Test menu link 1',
- ),
- array(
- 'menu_name' => 'menu-test-menu',
- 'mlid' => 139,
- 'plid' => 138,
- 'link_path' => 'admin/modules',
- 'router_path' => 'admin/modules',
- 'link_title' => 'Test 2',
- 'options' => array('attributes' => array('title' => 'Test menu link 2')),
- 'module' => 'menu',
- 'hidden' => 0,
- 'external' => 0,
- 'has_children' => 0,
- 'expanded' => 0,
- 'weight' => 12,
- 'depth' => 2,
- 'customized' => 1,
- 'p1' => '138',
- 'p2' => '139',
- 'p3' => '0',
- 'p4' => '0',
- 'p5' => '0',
- 'p6' => '0',
- 'p7' => '0',
- 'p8' => '0',
- 'p9' => '0',
- 'updated' => '0',
- 'description' => 'Test menu link 2',
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['menu_links'] = $this->expectedResults;
-
- // Add long link title attributes.
- $title = $this->getRandomGenerator()->string('500');
- $this->databaseContents['menu_links'][0]['options']['attributes']['title'] = $title;
- $this->expectedResults[0]['description'] = Unicode::truncate($title, 255);
-
- // D6 menu link to a custom menu, provided by menu module.
- $this->databaseContents['menu_links'][] = [
- 'menu_name' => 'menu-user',
- 'mlid' => 143,
- 'plid' => 0,
- 'link_path' => 'admin/build/menu-customize/navigation',
- 'router_path' => 'admin/build/menu-customize/%',
- 'link_title' => 'Navigation',
- 'options' => array(),
- 'module' => 'menu',
- 'hidden' => 0,
- 'external' => 0,
- 'has_children' => 0,
- 'expanded' => 0,
- 'weight' => 0,
- 'depth' => 0,
- 'customized' => 0,
- 'p1' => '0',
- 'p2' => '0',
- 'p3' => '0',
- 'p4' => '0',
- 'p5' => '0',
- 'p6' => '0',
- 'p7' => '0',
- 'p8' => '0',
- 'p9' => '0',
- 'updated' => '0',
- 'description' => '',
- ];
-
- array_walk($this->databaseContents['menu_links'], function (&$row) {
- $row['options'] = serialize($row['options']);
- });
-
- parent::setUp();
- }
-
-}
diff --git a/core/modules/migrate/src/MigrateExecutable.php b/core/modules/migrate/src/MigrateExecutable.php
index c51b278ad..0488d9665 100644
--- a/core/modules/migrate/src/MigrateExecutable.php
+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -66,13 +66,6 @@ class MigrateExecutable implements MigrateExecutableInterface {
*/
protected $counts = array();
- /**
- * The object currently being constructed.
- *
- * @var \stdClass
- */
- protected $destinationValues;
-
/**
* The source.
*
@@ -80,13 +73,6 @@ class MigrateExecutable implements MigrateExecutableInterface {
*/
protected $source;
- /**
- * The current data row retrieved from the source.
- *
- * @var \stdClass
- */
- protected $sourceValues;
-
/**
* The event dispatcher.
*
@@ -94,6 +80,15 @@ class MigrateExecutable implements MigrateExecutableInterface {
*/
protected $eventDispatcher;
+ /**
+ * Migration message service.
+ *
+ * @todo https://www.drupal.org/node/2822663 Make this protected.
+ *
+ * @var \Drupal\migrate\MigrateMessageInterface
+ */
+ public $message;
+
/**
* Constructs a MigrateExecutable and verifies and sets the memory limit.
*
@@ -229,7 +224,12 @@ class MigrateExecutable implements MigrateExecutableInterface {
$save = FALSE;
}
catch (MigrateSkipRowException $e) {
- $id_map->saveIdMapping($row, array(), MigrateIdMapInterface::STATUS_IGNORED);
+ if ($e->getSaveToMap()) {
+ $id_map->saveIdMapping($row, [], MigrateIdMapInterface::STATUS_IGNORED);
+ }
+ if ($message = trim($e->getMessage())) {
+ $this->saveMessage($message, MigrationInterface::MESSAGE_INFORMATIONAL);
+ }
$save = FALSE;
}
@@ -263,8 +263,6 @@ class MigrateExecutable implements MigrateExecutableInterface {
}
}
- // Reset row properties.
- unset($sourceValues, $destinationValues);
$this->sourceRowStatus = MigrateIdMapInterface::STATUS_IMPORTED;
// Check for memory exhaustion.
diff --git a/core/modules/migrate/src/Plugin/MigrateDestinationInterface.php b/core/modules/migrate/src/Plugin/MigrateDestinationInterface.php
index 0f69308f9..b6e86a09f 100644
--- a/core/modules/migrate/src/Plugin/MigrateDestinationInterface.php
+++ b/core/modules/migrate/src/Plugin/MigrateDestinationInterface.php
@@ -21,15 +21,59 @@ use Drupal\migrate\Row;
interface MigrateDestinationInterface extends PluginInspectionInterface {
/**
- * Get the destination IDs.
+ * Gets the destination IDs.
*
* To support MigrateIdMap maps, derived destination classes should return
- * schema field definition(s) corresponding to the primary key of the
- * destination being implemented. These are used to construct the destination
- * key fields of the map table for a migration using this destination.
+ * field definition(s) corresponding to the primary key of the destination
+ * being implemented. These are used to construct the destination key fields
+ * of the map table for a migration using this destination.
*
- * @return array
- * An array of IDs.
+ * @return array[]
+ * An associative array of field definitions keyed by field ID. Values are
+ * associative arrays with a structure that contains the field type ('type'
+ * key). The other keys are the field storage settings as they are returned
+ * by FieldStorageDefinitionInterface::getSettings(). As an example, for a
+ * composite destination primary key that is defined by an integer and a
+ * string, the returned value might look like:
+ * @code
+ * return [
+ * 'id' => [
+ * 'type' => 'integer',
+ * 'unsigned' => FALSE,
+ * 'size' => 'big',
+ * ],
+ * 'version' => [
+ * 'type' => 'string',
+ * 'max_length' => 64,
+ * 'is_ascii' => TRUE,
+ * ],
+ * ];
+ * @endcode
+ * If 'type' points to a field plugin with multiple columns and needs to
+ * refer to a column different than 'value', the key of that column will be
+ * appended as a suffix to the plugin name, separated by dot ('.'). Example:
+ * @code
+ * return [
+ * 'format' => [
+ * 'type' => 'text.format',
+ * ],
+ * ];
+ * @endcode
+ * Additional custom keys/values, that are not part of field storage
+ * definition, can be passed in definitions:
+ * @code
+ * return [
+ * 'nid' => [
+ * 'type' => 'integer',
+ * 'custom_setting' => 'some_value',
+ * ],
+ * ];
+ * @endcode
+ *
+ * @see \Drupal\Core\Field\FieldStorageDefinitionInterface::getSettings()
+ * @see \Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem
+ * @see \Drupal\Core\Field\Plugin\Field\FieldType\StringItem
+ * @see \Drupal\text\Plugin\Field\FieldType\TextItem
*/
public function getIds();
diff --git a/core/modules/migrate/src/Plugin/MigratePluginManager.php b/core/modules/migrate/src/Plugin/MigratePluginManager.php
index 8fd1644e5..94c59859e 100644
--- a/core/modules/migrate/src/Plugin/MigratePluginManager.php
+++ b/core/modules/migrate/src/Plugin/MigratePluginManager.php
@@ -41,8 +41,7 @@ class MigratePluginManager extends DefaultPluginManager implements MigratePlugin
* 'Drupal\Component\Annotation\PluginID'.
*/
public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, $annotation = 'Drupal\Component\Annotation\PluginID') {
- $plugin_interface = isset($plugin_interface_map[$type]) ? $plugin_interface_map[$type] : NULL;
- parent::__construct("Plugin/migrate/$type", $namespaces, $module_handler, $plugin_interface, $annotation);
+ parent::__construct("Plugin/migrate/$type", $namespaces, $module_handler, NULL, $annotation);
$this->alterInfo('migrate_' . $type . '_info');
$this->setCacheBackend($cache_backend, 'migrate_plugins_' . $type);
}
diff --git a/core/modules/migrate/src/Plugin/MigrateSourceInterface.php b/core/modules/migrate/src/Plugin/MigrateSourceInterface.php
index 32d2b983b..ef1785e6f 100644
--- a/core/modules/migrate/src/Plugin/MigrateSourceInterface.php
+++ b/core/modules/migrate/src/Plugin/MigrateSourceInterface.php
@@ -49,9 +49,54 @@ interface MigrateSourceInterface extends \Countable, \Iterator, PluginInspection
* prepareRow() or hook_migrate_prepare_row() to rewrite NULL values to
* appropriate empty values (such as '' or 0).
*
- * @return array
- * Array keyed by source field name, with values being a schema array
- * describing the field (such as ['type' => 'string]).
+ * @return array[]
+ * An associative array of field definitions keyed by field ID. Values are
+ * associative arrays with a structure that contains the field type ('type'
+ * key). The other keys are the field storage settings as they are returned
+ * by FieldStorageDefinitionInterface::getSettings(). As an example, for a
+ * composite source primary key that is defined by an integer and a
+ * string, the returned value might look like:
+ * @code
+ * return [
+ * 'id' => [
+ * 'type' => 'integer',
+ * 'unsigned' => FALSE,
+ * 'size' => 'big',
+ * ],
+ * 'version' => [
+ * 'type' => 'string',
+ * 'max_length' => 64,
+ * 'is_ascii' => TRUE,
+ * ],
+ * ];
+ * @endcode
+ * If 'type' points to a field plugin with multiple columns and needs to
+ * refer to a column different than 'value', the key of that column will be
+ * appended as a suffix to the plugin name, separated by dot ('.'). Example:
+ * @code
+ * return [
+ * 'format' => [
+ * 'type' => 'text.format',
+ * ],
+ * ];
+ * @endcode
+ * Additional custom keys/values, that are not part of field storage
+ * definition, can be passed in definitions. The most common setting, passed
+ * along the ID definition, is 'alias' used by SqlBase source plugin:
+ * @code
+ * return [
+ * 'nid' => [
+ * 'type' => 'integer',
+ * 'alias' => 'n',
+ * ],
+ * ];
+ * @endcode
+ *
+ * @see \Drupal\Core\Field\FieldStorageDefinitionInterface::getSettings()
+ * @see \Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem
+ * @see \Drupal\Core\Field\Plugin\Field\FieldType\StringItem
+ * @see \Drupal\text\Plugin\Field\FieldType\TextItem
+ * @see \Drupal\migrate\Plugin\migrate\source\SqlBase
*/
public function getIds();
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
index d3adce883..9da3de6f9 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
@@ -125,15 +125,13 @@ class EntityContentBase extends Entity {
*/
public function getIds() {
$id_key = $this->getKey('id');
- $ids[$id_key]['type'] = 'integer';
+ $ids[$id_key] = $this->getDefinitionFromEntity($id_key);
if ($this->isTranslationDestination()) {
- if ($key = $this->getKey('langcode')) {
- $ids[$key]['type'] = 'string';
- }
- else {
+ if (!$langcode_key = $this->getKey('langcode')) {
throw new MigrateException('This entity type does not support translation.');
}
+ $ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key);
}
return $ids;
@@ -263,4 +261,32 @@ class EntityContentBase extends Entity {
}
}
+ /**
+ * Gets the field definition from a specific entity base field.
+ *
+ * The method takes the field ID as an argument and returns the field storage
+ * definition to be used in getIds() by querying the destination entity base
+ * field definition.
+ *
+ * @param string $key
+ * The field ID key.
+ *
+ * @return array
+ * An associative array with a structure that contains the field type, keyed
+ * as 'type', together with field storage settings as they are returned by
+ * FieldStorageDefinitionInterface::getSettings().
+ *
+ * @see \Drupal\Core\Field\FieldStorageDefinitionInterface::getSettings()
+ */
+ protected function getDefinitionFromEntity($key) {
+ $entity_type_id = static::getEntityTypeId($this->getPluginId());
+ /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface[] $definitions */
+ $definitions = $this->entityManager->getBaseFieldDefinitions($entity_type_id);
+ $field_definition = $definitions[$key];
+
+ return [
+ 'type' => $field_definition->getType(),
+ ] + $field_definition->getSettings();
+ }
+
}
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
index 62469e730..381974291 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
@@ -73,8 +73,7 @@ class EntityRevision extends EntityContentBase {
*/
public function getIds() {
if ($key = $this->getKey('revision')) {
- $ids[$key]['type'] = 'integer';
- return $ids;
+ return [$key => $this->getDefinitionFromEntity($key)];
}
throw new MigrateException('This entity type does not support revisions.');
}
diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
index cbf4a9f46..962e05b23 100644
--- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
+++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
@@ -441,20 +441,40 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP
* Creates schema from an ID definition.
*
* @param array $id_definition
- * A field schema definition. Can be SQL schema or a type data
- * based schema. In the latter case, the value of type needs to be
- * $typed_data_type.$column.
+ * The definition of the field having the structure as the items returned by
+ * MigrateSourceInterface or MigrateDestinationInterface::getIds().
*
* @return array
- * The schema definition.
+ * The database schema definition.
+ *
+ * @see \Drupal\migrate\Plugin\MigrateSourceInterface::getIds()
+ * @see \Drupal\migrate\Plugin\MigrateDestinationInterface::getIds()
*/
protected function getFieldSchema(array $id_definition) {
$type_parts = explode('.', $id_definition['type']);
if (count($type_parts) == 1) {
$type_parts[] = 'value';
}
- $schema = BaseFieldDefinition::create($type_parts[0])->getColumns();
- return $schema[$type_parts[1]];
+ unset($id_definition['type']);
+
+ // Get the field storage definition.
+ $definition = BaseFieldDefinition::create($type_parts[0]);
+
+ // Get a list of setting keys belonging strictly to the field definition.
+ $default_field_settings = $definition->getSettings();
+ // Separate field definition settings from custom settings. Custom settings
+ // are settings passed in $id_definition that are not part of field storage
+ // definition settings.
+ $field_settings = array_intersect_key($id_definition, $default_field_settings);
+ $custom_settings = array_diff_key($id_definition, $default_field_settings);
+
+ // Resolve schema from field storage definition settings.
+ $schema = $definition
+ ->setSettings($field_settings)
+ ->getColumns()[$type_parts[1]];
+
+ // Merge back custom settings.
+ return $schema + $custom_settings;
}
/**
diff --git a/core/modules/migrate/src/Plugin/migrate/process/Download.php b/core/modules/migrate/src/Plugin/migrate/process/Download.php
new file mode 100644
index 000000000..08c9b57b1
--- /dev/null
+++ b/core/modules/migrate/src/Plugin/migrate/process/Download.php
@@ -0,0 +1,117 @@
+ FALSE,
+ 'guzzle_options' => [],
+ ];
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
+ $this->fileSystem = $file_system;
+ $this->httpClient = $http_client;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $configuration,
+ $plugin_id,
+ $plugin_definition,
+ $container->get('file_system'),
+ $container->get('http_client')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+ // If we're stubbing a file entity, return a uri of NULL so it will get
+ // stubbed by the general process.
+ if ($row->isStub()) {
+ return NULL;
+ }
+ list($source, $destination) = $value;
+
+ // Modify the destination filename if necessary.
+ $replace = !empty($this->configuration['rename']) ?
+ FILE_EXISTS_RENAME :
+ FILE_EXISTS_REPLACE;
+ $final_destination = file_destination($destination, $replace);
+
+ // Try opening the file first, to avoid calling file_prepare_directory()
+ // unnecessarily. We're suppressing fopen() errors because we want to try
+ // to prepare the directory before we give up and fail.
+ $destination_stream = @fopen($final_destination, 'w');
+ if (!$destination_stream) {
+ // If fopen didn't work, make sure there's a writable directory in place.
+ $dir = $this->fileSystem->dirname($final_destination);
+ if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
+ throw new MigrateException("Could not create or write to directory '$dir'");
+ }
+ // Let's try that fopen again.
+ $destination_stream = @fopen($final_destination, 'w');
+ if (!$destination_stream) {
+ throw new MigrateException("Could not write to file '$final_destination'");
+ }
+ }
+
+ // Stream the request body directly to the final destination stream.
+ $this->configuration['guzzle_options']['sink'] = $destination_stream;
+
+ // Make the request. Guzzle throws an exception for anything other than 200.
+ $this->httpClient->get($source, $this->configuration['guzzle_options']);
+
+ return $final_destination;
+ }
+
+}
diff --git a/core/modules/migrate/src/Plugin/migrate/process/Extract.php b/core/modules/migrate/src/Plugin/migrate/process/Extract.php
index 81671d879..1ac88ca5b 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/Extract.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/Extract.php
@@ -14,7 +14,8 @@ use Drupal\migrate\Row;
* @link https://www.drupal.org/node/2152731 Online handbook documentation for extract process plugin @endlink
*
* @MigrateProcessPlugin(
- * id = "extract"
+ * id = "extract",
+ * handle_multiples = TRUE
* )
*/
class Extract extends ProcessPluginBase {
diff --git a/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php b/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php
index 7c49715d6..c68fe344e 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/FileCopy.php
@@ -8,6 +8,7 @@ use Drupal\Core\StreamWrapper\LocalStream;
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
use Drupal\migrate\MigrateException;
use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\Plugin\MigrateProcessInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -35,6 +36,13 @@ class FileCopy extends ProcessPluginBase implements ContainerFactoryPluginInterf
*/
protected $fileSystem;
+ /**
+ * An instance of the download process plugin.
+ *
+ * @var \Drupal\migrate\Plugin\MigrateProcessInterface
+ */
+ protected $downloadPlugin;
+
/**
* Constructs a file_copy process plugin.
*
@@ -48,8 +56,10 @@ class FileCopy extends ProcessPluginBase implements ContainerFactoryPluginInterf
* The stream wrapper manager service.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
+ * @param \Drupal\migrate\Plugin\MigrateProcessInterface $download_plugin
+ * An instance of the download plugin for handling remote URIs.
*/
- public function __construct(array $configuration, $plugin_id, array $plugin_definition, StreamWrapperManagerInterface $stream_wrappers, FileSystemInterface $file_system) {
+ public function __construct(array $configuration, $plugin_id, array $plugin_definition, StreamWrapperManagerInterface $stream_wrappers, FileSystemInterface $file_system, MigrateProcessInterface $download_plugin) {
$configuration += array(
'move' => FALSE,
'rename' => FALSE,
@@ -58,6 +68,7 @@ class FileCopy extends ProcessPluginBase implements ContainerFactoryPluginInterf
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->streamWrapperManager = $stream_wrappers;
$this->fileSystem = $file_system;
+ $this->downloadPlugin = $download_plugin;
}
/**
@@ -69,7 +80,8 @@ class FileCopy extends ProcessPluginBase implements ContainerFactoryPluginInterf
$plugin_id,
$plugin_definition,
$container->get('stream_wrapper_manager'),
- $container->get('file_system')
+ $container->get('file_system'),
+ $container->get('plugin.manager.migrate.process')->createInstance('download')
);
}
@@ -84,8 +96,14 @@ class FileCopy extends ProcessPluginBase implements ContainerFactoryPluginInterf
}
list($source, $destination) = $value;
+ // If the source path or URI represents a remote resource, delegate to the
+ // download plugin.
+ if (!$this->isLocalUri($source)) {
+ return $this->downloadPlugin->transform($value, $migrate_executable, $row, $destination_property);
+ }
+
// Ensure the source file exists, if it's a local URI or path.
- if ($this->isLocalUri($source) && !file_exists($source)) {
+ if (!file_exists($source)) {
throw new MigrateException("File '$source' does not exist");
}
@@ -128,20 +146,14 @@ class FileCopy extends ProcessPluginBase implements ContainerFactoryPluginInterf
* File destination on success, FALSE on failure.
*/
protected function writeFile($source, $destination, $replace = FILE_EXISTS_REPLACE) {
- if ($this->configuration['move']) {
- return file_unmanaged_move($source, $destination, $replace);
- }
// Check if there is a destination available for copying. If there isn't,
// it already exists at the destination and the replace flag tells us to not
// replace it. In that case, return the original destination.
if (!($final_destination = file_destination($destination, $replace))) {
return $destination;
}
- // We can't use file_unmanaged_copy because it will break with remote Urls.
- if (@copy($source, $final_destination)) {
- return $final_destination;
- }
- return FALSE;
+ $function = 'file_unmanaged_' . ($this->configuration['move'] ? 'move' : 'copy');
+ return $function($source, $destination, $replace);
}
/**
@@ -187,8 +199,6 @@ class FileCopy extends ProcessPluginBase implements ContainerFactoryPluginInterf
/**
* Determines if the source and destination URIs represent identical paths.
*
- * If either URI is a remote stream, will return FALSE.
- *
* @param string $source
* The source URI.
* @param string $destination
@@ -199,10 +209,7 @@ class FileCopy extends ProcessPluginBase implements ContainerFactoryPluginInterf
* otherwise FALSE.
*/
protected function isLocationUnchanged($source, $destination) {
- if ($this->isLocalUri($source) && $this->isLocalUri($destination)) {
- return $this->fileSystem->realpath($source) === $this->fileSystem->realpath($destination);
- }
- return FALSE;
+ return $this->fileSystem->realpath($source) === $this->fileSystem->realpath($destination);
}
/**
@@ -219,6 +226,13 @@ class FileCopy extends ProcessPluginBase implements ContainerFactoryPluginInterf
*/
protected function isLocalUri($uri) {
$scheme = $this->fileSystem->uriScheme($uri);
+
+ // The vfs scheme is vfsStream, which is used in testing. vfsStream is a
+ // simulated file system that exists only in memory, but should be treated
+ // as a local resource.
+ if ($scheme == 'vfs') {
+ $scheme = FALSE;
+ }
return $scheme === FALSE || $this->streamWrapperManager->getViaScheme($scheme) instanceof LocalStream;
}
diff --git a/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php b/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php
index 45e2be5e0..6eebdeac0 100644
--- a/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php
@@ -207,6 +207,9 @@ abstract class SourcePluginBase extends PluginBase implements MigrateSourceInter
catch (MigrateSkipRowException $e) {
$skip = TRUE;
$save_to_map = $e->getSaveToMap();
+ if ($message = trim($e->getMessage())) {
+ $this->idMap->saveMessage($row->getSourceIdValues(), $message, MigrationInterface::MESSAGE_INFORMATIONAL);
+ }
}
// We're explicitly skipping this row - keep track in the map table.
diff --git a/core/modules/migrate/src/Row.php b/core/modules/migrate/src/Row.php
index 94cb8b2bd..ea70acdf0 100644
--- a/core/modules/migrate/src/Row.php
+++ b/core/modules/migrate/src/Row.php
@@ -91,7 +91,7 @@ class Row {
* @throws \InvalidArgumentException
* Thrown when a source ID property does not exist.
*/
- public function __construct(array $values, array $source_ids, $is_stub = FALSE) {
+ public function __construct(array $values = [], array $source_ids = [], $is_stub = FALSE) {
$this->source = $values;
$this->sourceIds = $source_ids;
$this->isStub = $is_stub;
diff --git a/core/modules/migrate/tests/modules/migrate_entity_test/migrate_entity_test.info.yml b/core/modules/migrate/tests/modules/migrate_entity_test/migrate_entity_test.info.yml
new file mode 100644
index 000000000..8f2da388c
--- /dev/null
+++ b/core/modules/migrate/tests/modules/migrate_entity_test/migrate_entity_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Migrate entity test'
+type: module
+description: 'Support module for entity destination test.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/migrate/tests/modules/migrate_entity_test/src/Entity/StringIdEntityTest.php b/core/modules/migrate/tests/modules/migrate_entity_test/src/Entity/StringIdEntityTest.php
new file mode 100644
index 000000000..060e355f8
--- /dev/null
+++ b/core/modules/migrate/tests/modules/migrate_entity_test/src/Entity/StringIdEntityTest.php
@@ -0,0 +1,36 @@
+ BaseFieldDefinition::create('integer')
+ ->setSetting('size', 'big')
+ ->setLabel('ID'),
+ 'version' => BaseFieldDefinition::create('string')
+ ->setLabel('Version'),
+ ];
+ }
+
+}
diff --git a/core/modules/migrate/tests/modules/migrate_prepare_row_test/migrate_prepare_row_test.module b/core/modules/migrate/tests/modules/migrate_prepare_row_test/migrate_prepare_row_test.module
index 66f1bd937..1da25f5a6 100644
--- a/core/modules/migrate/tests/modules/migrate_prepare_row_test/migrate_prepare_row_test.module
+++ b/core/modules/migrate/tests/modules/migrate_prepare_row_test/migrate_prepare_row_test.module
@@ -18,9 +18,11 @@ function migrate_prepare_row_test_migrate_prepare_row(Row $row, MigrateSourceInt
// Test both options for save_to_map.
$data = $row->getSourceProperty('data');
if ($data == 'skip_and_record') {
+ // Record mapping but don't record a message.
throw new MigrateSkipRowException('', TRUE);
}
elseif ($data == 'skip_and_dont_record') {
- throw new MigrateSkipRowException('', FALSE);
+ // Don't record mapping but record a message.
+ throw new MigrateSkipRowException('skip_and_dont_record message', FALSE);
}
}
diff --git a/core/modules/migrate/tests/modules/migrate_prepare_row_test/src/Plugin/migrate/process/TestSkipRowProcess.php b/core/modules/migrate/tests/modules/migrate_prepare_row_test/src/Plugin/migrate/process/TestSkipRowProcess.php
new file mode 100644
index 000000000..4822113b3
--- /dev/null
+++ b/core/modules/migrate/tests/modules/migrate_prepare_row_test/src/Plugin/migrate/process/TestSkipRowProcess.php
@@ -0,0 +1,34 @@
+getSourceProperty('data');
+ if ($data == 'skip_and_record (use plugin)') {
+ throw new MigrateSkipRowException('', TRUE);
+ }
+ elseif ($data == 'skip_and_dont_record (use plugin)') {
+ throw new MigrateSkipRowException('', FALSE);
+ }
+ return $value;
+ }
+
+}
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateEntityContentBaseTest.php b/core/modules/migrate/tests/src/Kernel/MigrateEntityContentBaseTest.php
index 352b6606c..0ebc719ec 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateEntityContentBaseTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateEntityContentBaseTest.php
@@ -4,6 +4,8 @@ namespace Drupal\Tests\migrate\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\migrate\MigrateExecutable;
+use Drupal\migrate\MigrateMessage;
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Plugin\MigrationInterface;
@@ -123,7 +125,7 @@ class MigrateEntityContentBaseTest extends KernelTestBase {
// Import some rows.
foreach ($destination_rows as $idx => $destination_row) {
- $row = new Row([], []);
+ $row = new Row();
foreach ($destination_row as $key => $value) {
$row->setDestinationProperty($key, $value);
}
@@ -156,4 +158,50 @@ class MigrateEntityContentBaseTest extends KernelTestBase {
$this->assertTranslations(4, 'fr');
}
+ /**
+ * Tests creation of ID columns table with definitions taken from entity type.
+ */
+ public function testEntityWithStringId() {
+ $this->enableModules(['migrate_entity_test']);
+ $this->installEntitySchema('migrate_string_id_entity_test');
+
+ $definition = [
+ 'source' => [
+ 'plugin' => 'embedded_data',
+ 'data_rows' => [
+ ['id' => 123, 'version' => 'foo'],
+ // This integer needs an 'int' schema with 'big' size. If 'destid1'
+ // is not correctly taking the definition from the destination entity
+ // type, the import will fail with a SQL exception.
+ ['id' => 123456789012, 'version' => 'bar'],
+ ],
+ 'ids' => [
+ 'id' => ['type' => 'integer', 'size' => 'big'],
+ 'version' => ['type' => 'string'],
+ ],
+ ],
+ 'process' => [
+ 'id' => 'id',
+ 'version' => 'version',
+ ],
+ 'destination' => [
+ 'plugin' => 'entity:migrate_string_id_entity_test',
+ ],
+ ];
+
+ $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
+ $executable = new MigrateExecutable($migration, new MigrateMessage());
+ $result = $executable->import();
+ $this->assertEquals(MigrationInterface::RESULT_COMPLETED, $result);
+
+ /** @var \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map_plugin */
+ $id_map_plugin = $migration->getIdMap();
+
+ // Check that the destination has been stored.
+ $map_row = $id_map_plugin->getRowBySource(['id' => 123, 'version' => 'foo']);
+ $this->assertEquals(123, $map_row['destid1']);
+ $map_row = $id_map_plugin->getRowBySource(['id' => 123456789012, 'version' => 'bar']);
+ $this->assertEquals(123456789012, $map_row['destid1']);
+ }
+
}
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php b/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php
index acad26d25..3b0091d92 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php
@@ -54,14 +54,49 @@ class MigrateSkipRowTest extends KernelTestBase {
$result = $executable->import();
$this->assertEqual($result, MigrationInterface::RESULT_COMPLETED);
+ /** @var \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map_plugin */
$id_map_plugin = $migration->getIdMap();
// The first row is recorded in the map as ignored.
$map_row = $id_map_plugin->getRowBySource(['id' => 1]);
$this->assertEqual(MigrateIdMapInterface::STATUS_IGNORED, $map_row['source_row_status']);
+ // Check that no message has been logged for the first exception.
+ $messages = $id_map_plugin->getMessageIterator(['id' => 1])->fetchAll();
+ $this->assertEmpty($messages);
+
// The second row is not recorded in the map.
$map_row = $id_map_plugin->getRowBySource(['id' => 2]);
$this->assertFalse($map_row);
+ // Check that the correct message has been logged for the second exception.
+ $messages = $id_map_plugin->getMessageIterator(['id' => 2])->fetchAll();
+ $this->assertCount(1, $messages);
+ $message = reset($messages);
+ $this->assertEquals('skip_and_dont_record message', $message->message);
+ $this->assertEquals(MigrationInterface::MESSAGE_INFORMATIONAL, $message->level);
+ // Insert a custom processor in the process flow.
+ $definition['process']['value'] = [
+ 'source' => 'data',
+ 'plugin' => 'test_skip_row_process',
+ ];
+ // Change data to avoid triggering again hook_migrate_prepare_row().
+ $definition['source']['data_rows'] = [
+ ['id' => '1', 'data' => 'skip_and_record (use plugin)'],
+ ['id' => '2', 'data' => 'skip_and_dont_record (use plugin)'],
+ ];
+ $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
+
+ $executable = new MigrateExecutable($migration, new MigrateMessage());
+ $result = $executable->import();
+ $this->assertEquals($result, MigrationInterface::RESULT_COMPLETED);
+
+ $id_map_plugin = $migration->getIdMap();
+
+ // The first row is recorded in the map as ignored.
+ $map_row = $id_map_plugin->getRowBySource(['id' => 1]);
+ $this->assertEquals(MigrateIdMapInterface::STATUS_IGNORED, $map_row['source_row_status']);
+ // The second row is not recorded in the map.
+ $map_row = $id_map_plugin->getRowBySource(['id' => 2]);
+ $this->assertFalse($map_row);
}
}
diff --git a/core/modules/migrate/tests/src/Kernel/process/CopyFileTest.php b/core/modules/migrate/tests/src/Kernel/process/CopyFileTest.php
index 53ada1d77..427d413bc 100644
--- a/core/modules/migrate/tests/src/Kernel/process/CopyFileTest.php
+++ b/core/modules/migrate/tests/src/Kernel/process/CopyFileTest.php
@@ -18,7 +18,7 @@ class CopyFileTest extends FileTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['system'];
+ public static $modules = ['migrate', 'system'];
/**
* The file system service.
@@ -172,7 +172,7 @@ class CopyFileTest extends FileTestBase {
protected function doImport($source_path, $destination_path, $configuration = []) {
$plugin = FileCopy::create($this->container, $configuration, 'file_copy', []);
$executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
- $row = new Row([], []);
+ $row = new Row();
$result = $plugin->transform([$source_path, $destination_path], $executable, $row, 'foobaz');
diff --git a/core/modules/migrate/tests/src/Kernel/process/DownloadTest.php b/core/modules/migrate/tests/src/Kernel/process/DownloadTest.php
new file mode 100644
index 000000000..69e3aa002
--- /dev/null
+++ b/core/modules/migrate/tests/src/Kernel/process/DownloadTest.php
@@ -0,0 +1,128 @@
+container->get('stream_wrapper_manager')->registerWrapper('temporary', 'Drupal\Core\StreamWrapper\TemporaryStream', StreamWrapperInterface::LOCAL_NORMAL);
+ }
+
+ /**
+ * Tests a download that overwrites an existing local file.
+ */
+ public function testOverwritingDownload() {
+ // Create a pre-existing file at the destination, to test overwrite behavior.
+ $destination_uri = $this->createUri('existing_file.txt');
+
+ // Test destructive download.
+ $actual_destination = $this->doTransform($destination_uri);
+ $this->assertSame($destination_uri, $actual_destination, 'Import returned a destination that was not renamed');
+ $this->assertFileNotExists('public://existing_file_0.txt', 'Import did not rename the file');
+ }
+
+ /**
+ * Tests a download that renames the downloaded file if there's a collision.
+ */
+ public function testNonDestructiveDownload() {
+ // Create a pre-existing file at the destination, to test overwrite behavior.
+ $destination_uri = $this->createUri('another_existing_file.txt');
+
+ // Test non-destructive download.
+ $actual_destination = $this->doTransform($destination_uri, ['rename' => TRUE]);
+ $this->assertSame('public://another_existing_file_0.txt', $actual_destination, 'Import returned a renamed destination');
+ $this->assertFileExists($actual_destination, 'Downloaded file was created');
+ }
+
+ /**
+ * Tests that an exception is thrown if the destination URI is not writable.
+ */
+ public function testWriteProectedDestination() {
+ // Create a pre-existing file at the destination, to test overwrite behavior.
+ $destination_uri = $this->createUri('not-writable.txt');
+
+ // Make the destination non-writable.
+ $this->container
+ ->get('file_system')
+ ->chmod($destination_uri, 0444);
+
+ // Pass or fail, we'll need to make the file writable again so the test
+ // can clean up after itself.
+ $fix_permissions = function () use ($destination_uri) {
+ $this->container
+ ->get('file_system')
+ ->chmod($destination_uri, 0755);
+ };
+
+ try {
+ $this->doTransform($destination_uri);
+ $fix_permissions();
+ $this->fail('MigrateException was not thrown for non-writable destination URI.');
+ }
+ catch (MigrateException $e) {
+ $this->assertTrue(TRUE, 'MigrateException was thrown for non-writable destination URI.');
+ $fix_permissions();
+ }
+ }
+
+ /**
+ * Runs an input value through the download plugin.
+ *
+ * @param string $destination_uri
+ * The destination URI to download to.
+ * @param array $configuration
+ * (optional) Configuration for the download plugin.
+ *
+ * @return string
+ * The local URI of the downloaded file.
+ */
+ protected function doTransform($destination_uri, $configuration = []) {
+ // The HTTP client will return a file with contents 'It worked!'
+ $body = fopen('data://text/plain;base64,SXQgd29ya2VkIQ==', 'r');
+
+ // Prepare a mock HTTP client.
+ $this->container->set('http_client', $this->getMock(Client::class));
+ $this->container->get('http_client')
+ ->method('get')
+ ->willReturn(new Response(200, [], $body));
+
+ // Instantiate the plugin statically so it can pull dependencies out of
+ // the container.
+ $plugin = Download::create($this->container, $configuration, 'download', []);
+
+ // Execute the transformation.
+ $executable = $this->getMock(MigrateExecutableInterface::class);
+ $row = new Row([], []);
+
+ // Return the downloaded file's local URI.
+ $value = [
+ 'http://drupal.org/favicon.ico',
+ $destination_uri,
+ ];
+ return $plugin->transform($value, $executable, $row, 'foobaz');
+ }
+
+}
diff --git a/core/modules/migrate/tests/src/Kernel/process/ExtractTest.php b/core/modules/migrate/tests/src/Kernel/process/ExtractTest.php
new file mode 100644
index 000000000..8c747abad
--- /dev/null
+++ b/core/modules/migrate/tests/src/Kernel/process/ExtractTest.php
@@ -0,0 +1,111 @@
+ [
+ 'plugin' => 'embedded_data',
+ 'data_rows' => [],
+ 'ids' => [
+ 'id' => ['type' => 'string'],
+ ],
+ ],
+ 'process' => [
+ 'first' => [
+ 'plugin' => 'extract',
+ 'index' => [0],
+ 'source' => 'simple_array',
+ ],
+ 'second' => [
+ 'plugin' => 'extract',
+ 'index' => [1],
+ 'source' => 'complex_array',
+ ],
+ ],
+ 'destination' => [
+ 'plugin' => 'config',
+ 'config_name' => 'migrate_test.settings',
+ ],
+ ];
+ }
+
+ /**
+ * Tests multiple value handling.
+ *
+ * @dataProvider multipleValueProviderSource
+ *
+ * @param array $source_data
+ * @param array $expected_data
+ */
+ public function testMultipleValueExplode(array $source_data, array $expected_data) {
+ $definition = $this->getDefinition();
+ $definition['source']['data_rows'] = [$source_data];
+
+ $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
+
+ $executable = new MigrateExecutable($migration, new MigrateMessage());
+ $result = $executable->import();
+
+ // Migration needs to succeed before further assertions are made.
+ $this->assertSame(MigrationInterface::RESULT_COMPLETED, $result);
+
+ // Compare with expected data.
+ $this->assertEquals($expected_data, \Drupal::config('migrate_test.settings')->get());
+ }
+
+ /**
+ * Provides multiple source data for "extract" process plugin test.
+ */
+ public function multipleValueProviderSource() {
+ $tests = [
+ [
+ 'source_data' => [
+ 'id' => '1',
+ 'simple_array' => ['alpha', 'beta'],
+ 'complex_array' => [['alpha', 'beta'], ['psi', 'omega']],
+ ],
+ 'expected_data' => [
+ 'first' => 'alpha',
+ 'second' => ['psi', 'omega'],
+ ],
+ ],
+ [
+ 'source_data' => [
+ 'id' => '2',
+ 'simple_array' => ['one'],
+ 'complex_array' => [0, 1],
+ ],
+ 'expected_data' => [
+ 'first' => 'one',
+ 'second' => 1,
+ ],
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/migrate/tests/src/Kernel/process/FileCopyTest.php b/core/modules/migrate/tests/src/Kernel/process/FileCopyTest.php
new file mode 100644
index 000000000..ee75e4545
--- /dev/null
+++ b/core/modules/migrate/tests/src/Kernel/process/FileCopyTest.php
@@ -0,0 +1,180 @@
+fileSystem = $this->container->get('file_system');
+ $this->container->get('stream_wrapper_manager')->registerWrapper('temporary', 'Drupal\Core\StreamWrapper\TemporaryStream', StreamWrapperInterface::LOCAL_NORMAL);
+ }
+
+ /**
+ * Test successful imports/copies.
+ */
+ public function testSuccessfulCopies() {
+ $file = $this->createUri(NULL, NULL, 'temporary');
+ $file_absolute = $this->fileSystem->realpath($file);
+ $data_sets = [
+ // Test a local to local copy.
+ [
+ $this->root . '/core/modules/simpletest/files/image-test.jpg',
+ 'public://file1.jpg'
+ ],
+ // Test a temporary file using an absolute path.
+ [
+ $file_absolute,
+ 'temporary://test.jpg'
+ ],
+ // Test a temporary file using a relative path.
+ [
+ $file_absolute,
+ 'temporary://core/modules/simpletest/files/test.jpg'
+ ],
+ ];
+ foreach ($data_sets as $data) {
+ list($source_path, $destination_path) = $data;
+ $actual_destination = $this->doTransform($source_path, $destination_path);
+ $message = sprintf('File %s exists', $destination_path);
+ $this->assertFileExists($destination_path, $message);
+ // Make sure we didn't accidentally do a move.
+ $this->assertFileExists($source_path, $message);
+ $this->assertSame($actual_destination, $destination_path, 'The import returned the copied filename.');
+ }
+ }
+
+ /**
+ * Test successful moves.
+ */
+ public function testSuccessfulMoves() {
+ $file_1 = $this->createUri(NULL, NULL, 'temporary');
+ $file_1_absolute = $this->fileSystem->realpath($file_1);
+ $file_2 = $this->createUri(NULL, NULL, 'temporary');
+ $file_2_absolute = $this->fileSystem->realpath($file_2);
+ $local_file = $this->createUri(NULL, NULL, 'public');
+ $data_sets = [
+ // Test a local to local copy.
+ [
+ $local_file,
+ 'public://file1.jpg'
+ ],
+ // Test a temporary file using an absolute path.
+ [
+ $file_1_absolute,
+ 'temporary://test.jpg'
+ ],
+ // Test a temporary file using a relative path.
+ [
+ $file_2_absolute,
+ 'temporary://core/modules/simpletest/files/test.jpg'
+ ],
+ ];
+ foreach ($data_sets as $data) {
+ list($source_path, $destination_path) = $data;
+ $actual_destination = $this->doTransform($source_path, $destination_path, ['move' => TRUE]);
+ $message = sprintf('File %s exists', $destination_path);
+ $this->assertFileExists($destination_path, $message);
+ $message = sprintf('File %s does not exist', $source_path);
+ $this->assertFileNotExists($source_path, $message);
+ $this->assertSame($actual_destination, $destination_path, 'The importer returned the moved filename.');
+ }
+ }
+
+ /**
+ * Test that non-existent files throw an exception.
+ *
+ * @expectedException \Drupal\migrate\MigrateException
+ *
+ * @expectedExceptionMessage File '/non/existent/file' does not exist
+ */
+ public function testNonExistentSourceFile() {
+ $source = '/non/existent/file';
+ $this->doTransform($source, 'public://wontmatter.jpg');
+ }
+
+ /**
+ * Test the 'rename' overwrite mode.
+ */
+ public function testRenameFile() {
+ $source = $this->createUri(NULL, NULL, 'temporary');
+ $destination = $this->createUri('foo.txt', NULL, 'public');
+ $expected_destination = 'public://foo_0.txt';
+ $actual_destination = $this->doTransform($source, $destination, ['rename' => TRUE]);
+ $this->assertFileExists($expected_destination, 'File was renamed on import');
+ $this->assertSame($actual_destination, $expected_destination, 'The importer returned the renamed filename.');
+ }
+
+ /**
+ * Tests that remote URIs are delegated to the download plugin.
+ */
+ public function testDownloadRemoteUri() {
+ $download_plugin = $this->getMock(MigrateProcessInterface::class);
+ $download_plugin->expects($this->once())->method('transform');
+
+ $plugin = new FileCopy(
+ [],
+ $this->randomMachineName(),
+ [],
+ $this->container->get('stream_wrapper_manager'),
+ $this->container->get('file_system'),
+ $download_plugin
+ );
+
+ $plugin->transform(
+ ['http://drupal.org/favicon.ico', '/destination/path'],
+ $this->getMock(MigrateExecutableInterface::class),
+ new Row([], []),
+ $this->randomMachineName()
+ );
+ }
+
+ /**
+ * Do an import using the destination.
+ *
+ * @param string $source_path
+ * Source path to copy from.
+ * @param string $destination_path
+ * The destination path to copy to.
+ * @param array $configuration
+ * Process plugin configuration settings.
+ *
+ * @return string
+ * The URI of the copied file.
+ */
+ protected function doTransform($source_path, $destination_path, $configuration = []) {
+ $plugin = FileCopy::create($this->container, $configuration, 'file_copy', []);
+ $executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
+ $row = new Row([], []);
+
+ return $plugin->transform([$source_path, $destination_path], $executable, $row, 'foobaz');
+ }
+
+}
diff --git a/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php b/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php
index 84c97f2a2..60809a90d 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php
@@ -398,7 +398,7 @@ class MigrateExecutableTest extends MigrateTestCase {
->method('getProcessPlugins')
->with(NULL)
->will($this->returnValue($plugins));
- $row = new Row(array(), array());
+ $row = new Row();
$this->executable->processRow($row);
foreach ($expected as $key => $value) {
$this->assertSame($row->getDestinationProperty($key), $value);
@@ -414,7 +414,7 @@ class MigrateExecutableTest extends MigrateTestCase {
->method('getProcessPlugins')
->with(NULL)
->will($this->returnValue(array('test' => array())));
- $row = new Row(array(), array());
+ $row = new Row();
$this->executable->processRow($row);
$this->assertSame($row->getDestination(), array());
}
diff --git a/core/modules/migrate/tests/src/Unit/MigrateSourceTest.php b/core/modules/migrate/tests/src/Unit/MigrateSourceTest.php
index db8313e70..4a462e985 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateSourceTest.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateSourceTest.php
@@ -286,7 +286,7 @@ class MigrateSourceTest extends MigrateTestCase {
// Get a new migration with an id.
$migration = $this->getMigration();
$source = new StubSourcePlugin([], '', [], $migration);
- $row = new Row([], []);
+ $row = new Row();
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
$module_handler->invokeAll('migrate_prepare_row', [$row, $source, $migration])
@@ -328,7 +328,7 @@ class MigrateSourceTest extends MigrateTestCase {
$migration = $this->getMigration();
$source = new StubSourcePlugin([], '', [], $migration);
- $row = new Row([], []);
+ $row = new Row();
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
// Return a failure from a prepare row hook.
@@ -357,7 +357,7 @@ class MigrateSourceTest extends MigrateTestCase {
$migration = $this->getMigration();
$source = new StubSourcePlugin([], '', [], $migration);
- $row = new Row([], []);
+ $row = new Row();
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
// Return a failure from a prepare row hook.
@@ -386,7 +386,7 @@ class MigrateSourceTest extends MigrateTestCase {
$migration = $this->getMigration();
$source = new StubSourcePlugin([], '', [], $migration);
- $row = new Row([], []);
+ $row = new Row();
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
// Return a failure from a prepare row hook.
diff --git a/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php b/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php
index d07bb3005..962a75dca 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateSqlSourceTestCase.php
@@ -147,7 +147,7 @@ abstract class MigrateSqlSourceTestCase extends MigrateTestCase {
public function testSourceCount() {
$count = $this->source->count();
$this->assertTrue(is_numeric($count));
- $this->assertEquals($count, $this->expectedCount);
+ $this->assertEquals($this->expectedCount, $count);
}
/**
diff --git a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
index 33aec586a..e5d63c476 100644
--- a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
+++ b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
@@ -11,6 +11,7 @@ use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\ContentEntityType;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
@@ -74,7 +75,7 @@ class EntityContentBaseTest extends UnitTestCase {
->willReturn(5);
$destination->setEntity($entity->reveal());
// Ensure the id is saved entity id is returned from import.
- $this->assertEquals([5], $destination->import(new Row([], [])));
+ $this->assertEquals([5], $destination->import(new Row()));
// Assert that import set the rollback action.
$this->assertEquals(MigrateIdMapInterface::ROLLBACK_DELETE, $destination->rollbackAction());
}
@@ -95,7 +96,7 @@ class EntityContentBaseTest extends UnitTestCase {
$this->entityManager->reveal(),
$this->prophesize(FieldTypePluginManagerInterface::class)->reveal());
$destination->setEntity(FALSE);
- $destination->import(new Row([], []));
+ $destination->import(new Row());
}
/**
@@ -109,6 +110,8 @@ class EntityContentBaseTest extends UnitTestCase {
$entity_type = $this->prophesize(ContentEntityType::class);
$entity_type->getKey('langcode')->willReturn('');
$entity_type->getKey('id')->willReturn('id');
+ $this->entityManager->getBaseFieldDefinitions('foo')
+ ->willReturn(['id' => BaseFieldDefinitionTest::create('integer')]);
$this->storage->getEntityType()->willReturn($entity_type->reveal());
@@ -144,4 +147,27 @@ class EntityTestDestination extends EntityContentBase {
return $this->entity;
}
+ public static function getEntityTypeId($plugin_id) {
+ return 'foo';
+ }
+
+}
+
+/**
+ * Stub class for BaseFieldDefinition.
+ */
+class BaseFieldDefinitionTest extends BaseFieldDefinition {
+
+ public static function create($type) {
+ return new static([]);
+ }
+
+ public function getSettings() {
+ return [];
+ }
+
+ public function getType() {
+ return 'integer';
+ }
+
}
diff --git a/core/modules/migrate/tests/src/Unit/RowTest.php b/core/modules/migrate/tests/src/Unit/RowTest.php
index 96c27eab1..ec04f826b 100644
--- a/core/modules/migrate/tests/src/Unit/RowTest.php
+++ b/core/modules/migrate/tests/src/Unit/RowTest.php
@@ -49,7 +49,7 @@ class RowTest extends UnitTestCase {
* Tests object creation: empty.
*/
public function testRowWithoutData() {
- $row = new Row(array(), array());
+ $row = new Row();
$this->assertSame(array(), $row->getSource(), 'Empty row');
}
diff --git a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
index f1e288e57..1886914b5 100644
--- a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
+++ b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
@@ -67,7 +67,7 @@ class EntityRevisionTest extends UnitTestCase {
$this->storage->loadRevision(12)
->shouldBeCalled()
->willReturn($entity->reveal());
- $row = new Row([], []);
+ $row = new Row();
$this->assertEquals($entity->reveal(), $destination->getEntity($row, [12, 13]));
}
diff --git a/core/modules/migrate/tests/src/Unit/destination/PerComponentEntityDisplayTest.php b/core/modules/migrate/tests/src/Unit/destination/PerComponentEntityDisplayTest.php
index 1be14bc56..4782dc0f4 100644
--- a/core/modules/migrate/tests/src/Unit/destination/PerComponentEntityDisplayTest.php
+++ b/core/modules/migrate/tests/src/Unit/destination/PerComponentEntityDisplayTest.php
@@ -29,7 +29,7 @@ class PerComponentEntityDisplayTest extends MigrateTestCase {
'field_name' => 'field_name_test',
'options' => array('test setting'),
);
- $row = new Row(array(), array());
+ $row = new Row();
foreach ($values as $key => $value) {
$row->setDestinationProperty($key, $value);
}
diff --git a/core/modules/migrate/tests/src/Unit/destination/PerComponentEntityFormDisplayTest.php b/core/modules/migrate/tests/src/Unit/destination/PerComponentEntityFormDisplayTest.php
index 18df3621a..50cdbf6dc 100644
--- a/core/modules/migrate/tests/src/Unit/destination/PerComponentEntityFormDisplayTest.php
+++ b/core/modules/migrate/tests/src/Unit/destination/PerComponentEntityFormDisplayTest.php
@@ -29,7 +29,7 @@ class PerComponentEntityFormDisplayTest extends MigrateTestCase {
'field_name' => 'field_name_test',
'options' => array('test setting'),
);
- $row = new Row(array(), array());
+ $row = new Row();
foreach ($values as $key => $value) {
$row->setDestinationProperty($key, $value);
}
diff --git a/core/modules/migrate/tests/src/Unit/process/IteratorTest.php b/core/modules/migrate/tests/src/Unit/process/IteratorTest.php
index 7c731a2e0..cdffac07a 100644
--- a/core/modules/migrate/tests/src/Unit/process/IteratorTest.php
+++ b/core/modules/migrate/tests/src/Unit/process/IteratorTest.php
@@ -67,7 +67,7 @@ class IteratorTest extends MigrateTestCase {
),
);
// This is not used but the interface requires it, so create an empty row.
- $row = new Row(array(), array());
+ $row = new Row();
// After transformation, check to make sure that source_foo and source_id's
// values ended up in the proper destinations, and that the value of the
diff --git a/core/modules/migrate/tests/src/Unit/process/UrlEncodeTest.php b/core/modules/migrate/tests/src/Unit/process/UrlEncodeTest.php
index 539858f61..78d89a16e 100644
--- a/core/modules/migrate/tests/src/Unit/process/UrlEncodeTest.php
+++ b/core/modules/migrate/tests/src/Unit/process/UrlEncodeTest.php
@@ -57,7 +57,7 @@ class UrlEncodeTest extends MigrateTestCase {
*/
protected function doTransform($value) {
$executable = new MigrateExecutable($this->getMigration(), new MigrateMessage());
- $row = new Row([], []);
+ $row = new Row();
return (new UrlEncode([], 'urlencode', []))
->transform($value, $executable, $row, 'foobaz');
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/Config.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/Config.php
new file mode 100644
index 000000000..df3311f5f
--- /dev/null
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/Config.php
@@ -0,0 +1,59 @@
+select('config', 'c')
+ ->fields('c', array('collection', 'name', 'data'));
+ if (!empty($this->configuration['collections'])) {
+ $query->condition('collection', (array) $this->configuration['collections'], 'IN');
+ }
+ if (!empty($this->configuration['names'])) {
+ $query->condition('name', (array) $this->configuration['names'], 'IN');
+ }
+ return $query;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prepareRow(Row $row) {
+ $row->setSourceProperty('data', unserialize($row->getSourceProperty('data')));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fields() {
+ return [
+ 'collection' => $this->t('The config object collection.'),
+ 'name' => $this->t('The config object name.'),
+ 'data' => $this->t('Serialized configuration object data.'),
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIds() {
+ $ids['collection']['type'] = 'string';
+ $ids['name']['type'] = 'string';
+ return $ids;
+ }
+
+}
diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal6.php b/core/modules/migrate_drupal/tests/fixtures/drupal6.php
index 347b2f272..9debae008 100644
--- a/core/modules/migrate_drupal/tests/fixtures/drupal6.php
+++ b/core/modules/migrate_drupal/tests/fixtures/drupal6.php
@@ -42136,13 +42136,13 @@ $connection->insert('permission')
->values(array(
'pid' => '1',
'rid' => '1',
- 'perm' => 'migrate test anonymous permission',
+ 'perm' => 'access content, migrate test anonymous permission',
'tid' => '0',
))
->values(array(
'pid' => '2',
'rid' => '2',
- 'perm' => 'migrate test authenticated permission',
+ 'perm' => 'access comments, access content, post comments, post comments without approval, migrate test authenticated permission',
'tid' => '0',
))
->values(array(
@@ -42157,18 +42157,6 @@ $connection->insert('permission')
'perm' => 'migrate test role 2 test permission, use PHP for settings, administer contact forms, skip comment approval, edit own blog content, edit any blog content, delete own blog content, delete any blog content, create forum content, delete any forum content, delete own forum content, edit any forum content, edit own forum content, administer nodes',
'tid' => '0',
))
-->values(array(
- 'pid' => '5',
- 'rid' => '1',
- 'perm' => 'access content',
- 'tid' => '0',
-))
-->values(array(
- 'pid' => '6',
- 'rid' => '2',
- 'perm' => 'access comments, access content, post comments, post comments without approval',
- 'tid' => '0',
-))
->execute();
$connection->schema()->createTable('profile_fields', array(
diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php
index e8c006b2d..98e56382f 100644
--- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php
+++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php
@@ -3726,7 +3726,7 @@ $connection->insert('field_config_instance')
'field_name' => 'field_link',
'entity_type' => 'node',
'bundle' => 'article',
- 'data' => 'a:7:{s:5:"label";s:4:"Link";s:6:"widget";a:5:{s:6:"weight";s:2:"10";s:4:"type";s:10:"link_field";s:6:"module";s:4:"link";s:6:"active";i:0;s:8:"settings";a:0:{}}s:8:"settings";a:12:{s:12:"absolute_url";i:1;s:12:"validate_url";i:1;s:3:"url";i:0;s:5:"title";s:8:"optional";s:11:"title_value";s:19:"Unused Static Title";s:27:"title_label_use_field_label";i:0;s:15:"title_maxlength";s:3:"128";s:7:"display";a:1:{s:10:"url_cutoff";s:2:"81";}s:10:"attributes";a:6:{s:6:"target";s:6:"_blank";s:3:"rel";s:8:"nofollow";s:18:"configurable_class";i:0;s:5:"class";s:7:"classes";s:18:"configurable_title";i:1;s:5:"title";s:0:"";}s:10:"rel_remove";s:19:"rel_remove_external";s:13:"enable_tokens";i:1;s:18:"user_register_form";b:0;}s:7:"display";a:1:{s:7:"default";a:5:{s:5:"label";s:5:"above";s:4:"type";s:12:"link_default";s:6:"weight";s:1:"9";s:8:"settings";a:0:{}s:6:"module";s:4:"link";}}s:8:"required";i:0;s:11:"description";s:0:"";s:13:"default_value";N;}',
+ 'data' => 'a:7:{s:5:"label";s:4:"Link";s:6:"widget";a:5:{s:6:"weight";s:2:"10";s:4:"type";s:10:"link_field";s:6:"module";s:4:"link";s:6:"active";i:0;s:8:"settings";a:0:{}}s:8:"settings";a:12:{s:12:"absolute_url";i:1;s:12:"validate_url";i:1;s:3:"url";i:0;s:5:"title";s:8:"disabled";s:11:"title_value";s:19:"Unused Static Title";s:27:"title_label_use_field_label";i:0;s:15:"title_maxlength";s:3:"128";s:7:"display";a:1:{s:10:"url_cutoff";s:2:"81";}s:10:"attributes";a:6:{s:6:"target";s:6:"_blank";s:3:"rel";s:8:"nofollow";s:18:"configurable_class";i:0;s:5:"class";s:7:"classes";s:18:"configurable_title";i:1;s:5:"title";s:0:"";}s:10:"rel_remove";s:19:"rel_remove_external";s:13:"enable_tokens";i:1;s:18:"user_register_form";b:0;}s:7:"display";a:1:{s:7:"default";a:5:{s:5:"label";s:5:"above";s:4:"type";s:12:"link_default";s:6:"weight";s:1:"9";s:8:"settings";a:0:{}s:6:"module";s:4:"link";}}s:8:"required";i:0;s:11:"description";s:0:"";s:13:"default_value";N;}',
'deleted' => '0',
))
->values(array(
@@ -3747,6 +3747,15 @@ $connection->insert('field_config_instance')
'data' => 'a:7:{s:5:"label";s:7:"Integer";s:6:"widget";a:5:{s:6:"weight";s:1:"9";s:4:"type";s:6:"number";s:6:"module";s:6:"number";s:6:"active";i:0;s:8:"settings";a:0:{}}s:8:"settings";a:5:{s:3:"min";s:1:"1";s:3:"max";s:2:"25";s:6:"prefix";s:3:"abc";s:6:"suffix";s:3:"xyz";s:18:"user_register_form";b:0;}s:7:"display";a:1:{s:7:"default";a:5:{s:5:"label";s:5:"above";s:4:"type";s:14:"number_integer";s:6:"weight";s:1:"8";s:8:"settings";a:4:{s:18:"thousand_separator";s:1:" ";s:17:"decimal_separator";s:1:".";s:5:"scale";i:0;s:13:"prefix_suffix";b:1;}s:6:"module";s:6:"number";}}s:8:"required";i:1;s:11:"description";s:0:"";s:13:"default_value";N;}',
'deleted' => '0',
))
+->values(array(
+ 'id' => '37',
+ 'field_id' => '15',
+ 'field_name' => 'field_link',
+ 'entity_type' => 'node',
+ 'bundle' => 'blog',
+ 'data' => 'a:7:{s:5:"label";s:4:"Link";s:6:"widget";a:5:{s:6:"weight";s:2:"10";s:4:"type";s:10:"link_field";s:6:"module";s:4:"link";s:6:"active";i:0;s:8:"settings";a:0:{}}s:8:"settings";a:12:{s:12:"absolute_url";i:1;s:12:"validate_url";i:1;s:3:"url";i:0;s:5:"title";s:8:"required";s:11:"title_value";s:19:"Unused Static Title";s:27:"title_label_use_field_label";i:0;s:15:"title_maxlength";s:3:"128";s:7:"display";a:1:{s:10:"url_cutoff";s:2:"81";}s:10:"attributes";a:6:{s:6:"target";s:6:"_blank";s:3:"rel";s:8:"nofollow";s:18:"configurable_class";i:0;s:5:"class";s:7:"classes";s:18:"configurable_title";i:1;s:5:"title";s:0:"";}s:10:"rel_remove";s:19:"rel_remove_external";s:13:"enable_tokens";i:1;s:18:"user_register_form";b:0;}s:7:"display";a:1:{s:7:"default";a:5:{s:5:"label";s:5:"above";s:4:"type";s:12:"link_default";s:6:"weight";s:1:"9";s:8:"settings";a:0:{}s:6:"module";s:4:"link";}}s:8:"required";i:0;s:11:"description";s:0:"";s:13:"default_value";N;}',
+ 'deleted' => '0',
+))
->execute();
$connection->schema()->createTable('field_data_body', array(
@@ -19190,6 +19199,33 @@ $connection->insert('menu_links')
'p9' => '0',
'updated' => '0',
))
+->values(array(
+ 'menu_name' => 'menu-test-menu',
+ 'mlid' => '470',
+ 'plid' => '469',
+ 'link_path' => 'http://ask.com',
+ 'router_path' => '',
+ 'link_title' => 'Ask',
+ 'options' => 'a:0:{}',
+ 'module' => 'menu',
+ 'hidden' => '0',
+ 'external' => '1',
+ 'has_children' => '0',
+ 'expanded' => '0',
+ 'weight' => '0',
+ 'depth' => '2',
+ 'customized' => '1',
+ 'p1' => '469',
+ 'p2' => '470',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+))
->values(array(
'menu_name' => 'shortcut-set-2',
'mlid' => '472',
@@ -19325,6 +19361,33 @@ $connection->insert('menu_links')
'p9' => '0',
'updated' => '0',
))
+->values(array(
+ 'menu_name' => 'navigation',
+ 'mlid' => '479',
+ 'plid' => '0',
+ 'link_path' => 'node/3',
+ 'router_path' => 'node/3',
+ 'link_title' => 'node link test',
+ 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:6:"node 3";}}',
+ 'module' => 'menu',
+ 'hidden' => '0',
+ 'external' => '0',
+ 'has_children' => '0',
+ 'expanded' => '0',
+ 'weight' => '3',
+ 'depth' => '1',
+ 'customized' => '1',
+ 'p1' => '479',
+ 'p2' => '0',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+))
->execute();
$connection->schema()->createTable('menu_router', array(
@@ -41044,11 +41107,29 @@ $connection->insert('users')
'login' => '0',
'status' => '1',
'timezone' => 'America/Chicago',
- 'language' => 'en',
+ 'language' => 'is',
'picture' => '0',
'init' => 'odo@local.host',
'data' => 'a:1:{s:7:"contact";i:1;}',
))
+->values(array(
+ 'uid' => '3',
+ 'name' => 'Bob',
+ 'pass' => '$S$DGFZUE.FhrXbe4y52eC7p0ZVRGD/gOPtVctDlmC89qkujnBokAlJ',
+ 'mail' => 'bob@local.host',
+ 'theme' => '',
+ 'signature' => '',
+ 'signature_format' => 'filtered_html',
+ 'created' => '1440532218',
+ 'access' => '0',
+ 'login' => '0',
+ 'status' => '1',
+ 'timezone' => 'America/New_York',
+ 'language' => 'fr',
+ 'picture' => '0',
+ 'init' => 'bob@local.host',
+ 'data' => 'a:1:{s:7:"contact";i:1;}',
+))
->execute();
$connection->schema()->createTable('users_roles', array(
@@ -41084,6 +41165,14 @@ $connection->insert('users_roles')
'uid' => '1',
'rid' => '3',
))
+->values(array(
+ 'uid' => '2',
+ 'rid' => '3',
+))
+->values(array(
+ 'uid' => '3',
+ 'rid' => '3',
+))
->execute();
$connection->schema()->createTable('variable', array(
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/d8/ConfigTest.php b/core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/d8/ConfigTest.php
new file mode 100644
index 000000000..20b80957d
--- /dev/null
+++ b/core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/d8/ConfigTest.php
@@ -0,0 +1,115 @@
+ [
+ [
+ 'collection' => 'language.af',
+ 'name' => 'user.settings',
+ 'data' => 'a:1:{s:9:"anonymous";s:14:"af - Anonymous";}',
+ ],
+ [
+ 'collection' => '',
+ 'name' => 'user.settings',
+ 'data' => 'a:1:{s:9:"anonymous";s:9:"Anonymous";}',
+ ],
+ [
+ 'collection' => 'language.de',
+ 'name' => 'user.settings',
+ 'data' => 'a:1:{s:9:"anonymous";s:14:"de - Anonymous";}',
+ ],
+ [
+ 'collection' => 'language.af',
+ 'name' => 'bar',
+ 'data' => 'b:0;',
+ ],
+ ],
+ ];
+
+ // The expected results.
+ $data[0]['expected_results'] = [
+ [
+ 'collection' => 'language.af',
+ 'name' => 'user.settings',
+ 'data' => [
+ 'anonymous' => 'af - Anonymous',
+ ],
+ ],
+ [
+ 'collection' => 'language.af',
+ 'name' => 'bar',
+ 'data' => FALSE,
+ ],
+ ];
+ $data[0]['expected_count'] = NULL;
+ $data[0]['configuration'] = [
+ 'names' => [
+ 'user.settings',
+ 'bar',
+ ],
+ 'collections' => [
+ 'language.af',
+ ]
+ ];
+
+ // Test with name and no collection in configuration.
+ $data[1]['source_data'] = $data[0]['source_data'];
+ $data[1]['expected_results'] = [
+ [
+ 'collection' => 'language.af',
+ 'name' => 'bar',
+ 'data' => FALSE,
+ ],
+ ];
+ $data[1]['expected_count'] = NULL;
+ $data[1]['configuration'] = [
+ 'names' => [
+ 'bar',
+ ],
+ ];
+
+ // Test with collection and no name in configuration.
+ $data[2]['source_data'] = $data[0]['source_data'];
+ $data[2]['expected_results'] = [
+ [
+ 'collection' => 'language.de',
+ 'name' => 'user.settings',
+ 'data' => [
+ 'anonymous' => 'de - Anonymous',
+ ],
+ ],
+ ];
+ $data[2]['expected_count'] = NULL;
+ $data[2]['configuration'] = [
+ 'collections' => [
+ 'language.de',
+ ],
+ ];
+
+ return $data;
+ }
+
+}
diff --git a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
index b53069e7a..2d60c4694 100644
--- a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
@@ -290,7 +290,7 @@ class MigrateUpgradeForm extends ConfirmFormBase {
'source_module' => 'locale',
'destination_module' => 'locale',
],
- 'menu_links' => [
+ 'd6_menu_links' => [
'source_module' => 'menu',
'destination_module' => 'menu_link_content',
],
@@ -298,13 +298,17 @@ class MigrateUpgradeForm extends ConfirmFormBase {
'source_module' => 'menu',
'destination_module' => 'menu_ui',
],
+ 'd7_menu_links' => [
+ 'source_module' => 'menu',
+ 'destination_module' => 'menu_link_content',
+ ],
'd6_node' => [
'source_module' => 'node',
'destination_module' => 'node',
],
'd6_node_translation' => [
- 'source_module' => 'node',
- 'destination_module' => 'node',
+ 'source_module' => 'translation',
+ 'destination_module' => 'content_translation',
],
'd6_node_revision' => [
'source_module' => 'node',
@@ -474,7 +478,11 @@ class MigrateUpgradeForm extends ConfirmFormBase {
'source_module' => 'system',
'destination_module' => 'system',
],
- 'menu' => [
+ 'd6_menu' => [
+ 'source_module' => 'menu',
+ 'destination_module' => 'system',
+ ],
+ 'd7_menu' => [
'source_module' => 'menu',
'destination_module' => 'system',
],
@@ -690,7 +698,7 @@ class MigrateUpgradeForm extends ConfirmFormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
- $step = $form_state->getValue('step', 'overview');
+ $step = $form_state->get('step') ?: 'overview';
switch ($step) {
case 'overview':
return $this->buildOverviewForm($form, $form_state);
@@ -788,7 +796,7 @@ class MigrateUpgradeForm extends ConfirmFormBase {
* The current state of the form.
*/
public function submitOverviewForm(array &$form, FormStateInterface $form_state) {
- $form_state->setValue('step', 'credentials');
+ $form_state->set('step', 'credentials');
$form_state->setRebuild();
}
@@ -977,7 +985,7 @@ class MigrateUpgradeForm extends ConfirmFormBase {
*/
public function submitCredentialForm(array &$form, FormStateInterface $form_state) {
// Indicate the next step is confirmation.
- $form_state->setValue('step', 'confirm');
+ $form_state->set('step', 'confirm');
$form_state->setRebuild();
}
diff --git a/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php
index bac70e538..74d4237ca 100644
--- a/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php
+++ b/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php
@@ -43,7 +43,7 @@ class MigrateUpgrade7Test extends MigrateUpgradeTestBase {
'configurable_language' => 4,
'contact_form' => 3,
'editor' => 2,
- 'field_config' => 44,
+ 'field_config' => 45,
'field_storage_config' => 33,
'file' => 1,
'filter_format' => 7,
@@ -56,14 +56,14 @@ class MigrateUpgrade7Test extends MigrateUpgradeTestBase {
'search_page' => 2,
'shortcut' => 6,
'shortcut_set' => 2,
- 'action' => 18,
- 'menu' => 10,
+ 'action' => 16,
+ 'menu' => 6,
'taxonomy_term' => 18,
'taxonomy_vocabulary' => 3,
'tour' => 4,
- 'user' => 3,
- 'user_role' => 4,
- 'menu_link_content' => 9,
+ 'user' => 4,
+ 'user_role' => 3,
+ 'menu_link_content' => 7,
'view' => 12,
'date_format' => 11,
'entity_form_display' => 16,
diff --git a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeByNodeTypeTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeByNodeTypeTest.php
new file mode 100644
index 000000000..d5a8db09d
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeByNodeTypeTest.php
@@ -0,0 +1,185 @@
+ 1,
+ 'vid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 1',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 1,
+ 'translate' => 0,
+ ],
+ [
+ 'nid' => 2,
+ 'vid' => 2,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 2',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 2,
+ 'translate' => 0,
+ ],
+ // Add another row with an article node and make sure it is not migrated.
+ [
+ 'nid' => 5,
+ 'vid' => 5,
+ 'type' => 'article',
+ 'language' => 'en',
+ 'title' => 'node title 5',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 0,
+ 'translate' => 0,
+ ],
+ ];
+
+ $tests[0]['source_data']['node_revisions'] = [
+ [
+ 'nid' => 1,
+ 'vid' => 1,
+ 'title' => 'node title 1',
+ 'uid' => 2,
+ 'timestamp' => 1279051598,
+ 'body' => 'body for node 1',
+ 'teaser' => 'teaser for node 1',
+ 'format' => 1,
+ 'log' => 'log message 1',
+ ],
+ [
+ 'nid' => 2,
+ 'vid' => 2,
+ 'title' => 'node title 2',
+ 'uid' => 2,
+ 'timestamp' => 1279290908,
+ 'body' => 'body for node 2',
+ 'teaser' => 'teaser for node 2',
+ 'format' => 1,
+ 'log' => 'log message 2',
+ ],
+ // Add another row with an article node and make sure it is not migrated.
+ [
+ 'nid' => 5,
+ 'vid' => 5,
+ 'title' => 'node title 5',
+ 'uid' => 2,
+ 'timestamp' => 1279290908,
+ 'body' => 'body for node 5',
+ 'teaser' => 'body for node 5',
+ 'format' => 1,
+ 'log' => 'log message 3',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ // Node fields.
+ 'nid' => 1,
+ 'vid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 1',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'timestamp' => 1279051598,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 1,
+ 'translate' => 0,
+ // Node revision fields.
+ 'body' => 'body for node 1',
+ 'teaser' => 'teaser for node 1',
+ 'format' => 1,
+ 'log' => 'log message 1',
+ ],
+ [
+ // Node fields.
+ 'nid' => 2,
+ 'vid' => 2,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 2',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'timestamp' => 1279290908,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 2,
+ 'translate' => 0,
+ // Node revision fields.
+ 'body' => 'body for node 2',
+ 'teaser' => 'teaser for node 2',
+ 'format' => 1,
+ 'log' => 'log message 2',
+ ],
+ ];
+
+ // Do an automatic count.
+ $tests[0]['expected_count'] = NULL;
+
+ // Set up source plugin configuration.
+ $tests[0]['configuration'] = [
+ 'node_type' => 'page',
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php
similarity index 50%
rename from core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php
rename to core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php
index d548ff3d4..27e17cc41 100644
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php
@@ -1,40 +1,31 @@
'test',
- // The fake configuration for the source.
- 'source' => [
- 'plugin' => 'd6_node_revision',
- 'node_type' => 'page',
- ],
- 'sourceIds' => [
- 'vid' => [
- 'alias' => 'v',
- ],
- ],
- 'destinationIds' => [
- 'vid' => [
- // This is where the field schema would go.
- ],
- ],
- ];
+ /**
+ * {@inheritdoc}
+ */
+ public function providerSource() {
+ $tests = [];
- protected $databaseContents = [
- 'node' => [
+ // The source data.
+ $tests[0]['source_data']['node'] = [
[
'nid' => 1,
'type' => 'page',
@@ -69,8 +60,8 @@ class NodeRevisionByNodeTypeTest extends MigrateSqlSourceTestCase {
'uid' => 1,
'title' => 'title for revision 2 (node 2)',
],
- ],
- 'node_revisions' => [
+ ];
+ $tests[0]['source_data']['node_revisions'] = [
[
'nid' => 1,
'vid' => 1,
@@ -115,58 +106,69 @@ class NodeRevisionByNodeTypeTest extends MigrateSqlSourceTestCase {
'format' => 1,
'timestamp' => 1279308993,
],
- ],
- ];
+ ];
- // There are three revisions of nid 1; vid 4 is the current one. The
- // NodeRevision plugin should capture every revision EXCEPT that one.
- // nid 2 will be ignored because $this->migrationConfiguration specifies
- // a particular node type.
- protected $expectedResults = [
- [
- 'nid' => 1,
- 'type' => 'page',
- 'language' => 'en',
- 'status' => 1,
- 'created' => 1279051598,
- 'changed' => 1279051598,
- 'comment' => 2,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 1,
- 'translate' => 0,
- 'vid' => 1,
- 'node_uid' => 1,
- 'revision_uid' => 1,
- 'title' => 'title for revision 1 (node 1)',
- 'body' => 'body for revision 1 (node 1)',
- 'teaser' => 'teaser for revision 1 (node 1)',
- 'log' => 'log for revision 1 (node 1)',
- 'format' => 1,
- ],
- [
- 'nid' => 1,
- 'type' => 'page',
- 'language' => 'en',
- 'status' => 1,
- 'created' => 1279051598,
- 'changed' => 1279051598,
- 'comment' => 2,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 1,
- 'translate' => 0,
- 'vid' => 3,
- 'node_uid' => 1,
- 'revision_uid' => 1,
- 'title' => 'title for revision 3 (node 1)',
- 'body' => 'body for revision 3 (node 1)',
- 'teaser' => 'teaser for revision 3 (node 1)',
- 'log' => 'log for revision 3 (node 1)',
- 'format' => 1,
- ],
- ];
+ // The expected results.
+ // There are three revisions of nid 1; vid 4 is the current one. The
+ // NodeRevision plugin should capture every revision EXCEPT that one.
+ // nid 2 will be ignored because source plugin configuration specifies
+ // a particular node type.
+ $tests[0]['expected_data'] = [
+ [
+ 'nid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'status' => 1,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 1,
+ 'translate' => 0,
+ 'vid' => 1,
+ 'node_uid' => 1,
+ 'revision_uid' => 1,
+ 'title' => 'title for revision 1 (node 1)',
+ 'body' => 'body for revision 1 (node 1)',
+ 'teaser' => 'teaser for revision 1 (node 1)',
+ 'log' => 'log for revision 1 (node 1)',
+ 'format' => 1,
+ ],
+ [
+ 'nid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'status' => 1,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 1,
+ 'translate' => 0,
+ 'vid' => 3,
+ 'node_uid' => 1,
+ 'revision_uid' => 1,
+ 'title' => 'title for revision 3 (node 1)',
+ 'body' => 'body for revision 3 (node 1)',
+ 'teaser' => 'teaser for revision 3 (node 1)',
+ 'log' => 'log for revision 3 (node 1)',
+ 'format' => 1,
+ ],
+ ];
+
+ // Do an automatic count.
+ $tests[0]['expected_count'] = NULL;
+
+ // Set up source plugin configuration.
+ $tests[0]['configuration'] = [
+ 'node_type' => 'page',
+ ];
+
+ return $tests;
+ }
}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeRevisionTest.php
similarity index 50%
rename from core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionTest.php
rename to core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeRevisionTest.php
index 752ed9bfc..fbc28c5ce 100644
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionTest.php
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeRevisionTest.php
@@ -1,39 +1,31 @@
'test',
- // The fake configuration for the source.
- 'source' => [
- 'plugin' => 'd6_node_revision',
- ],
- 'sourceIds' => [
- 'vid' => [
- 'alias' => 'v',
- ],
- ],
- 'destinationIds' => [
- 'vid' => [
- // This is where the field schema would go.
- ],
- ],
- ];
+ /**
+ * {@inheritdoc}
+ */
+ public function providerSource() {
+ $tests = [];
- protected $databaseContents = [
- 'node' => [
+ // The source data.
+ $tests[0]['source_data']['node'] = [
[
'nid' => 1,
'type' => 'page',
@@ -68,8 +60,8 @@ class NodeRevisionTest extends MigrateSqlSourceTestCase {
'uid' => 1,
'title' => 'title for revision 2 (node 2)',
],
- ],
- 'node_revisions' => [
+ ];
+ $tests[0]['source_data']['node_revisions'] = [
[
'nid' => 1,
'vid' => 1,
@@ -114,61 +106,64 @@ class NodeRevisionTest extends MigrateSqlSourceTestCase {
'format' => 1,
'timestamp' => 1279308993,
],
- ],
- ];
+ ];
- // There are three revisions of nid 1, but the NodeRevision source ignores
- // the current revision. So only two revisions will be returned here. nid 2
- // is ignored because it only has one revision (the current one).
- protected $expectedResults = [
- [
- // Node fields.
- 'nid' => 1,
- 'type' => 'page',
- 'language' => 'en',
- 'status' => 1,
- 'created' => 1279051598,
- 'changed' => 1279051598,
- 'comment' => 2,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 1,
- 'translate' => 0,
- // Node revision fields.
- 'vid' => 1,
- 'node_uid' => 1,
- 'revision_uid' => 1,
- 'title' => 'title for revision 1 (node 1)',
- 'body' => 'body for revision 1 (node 1)',
- 'teaser' => 'teaser for revision 1 (node 1)',
- 'log' => 'log for revision 1 (node 1)',
- 'format' => 1,
- ],
- [
- // Node fields.
- 'nid' => 1,
- 'type' => 'page',
- 'language' => 'en',
- 'status' => 1,
- 'created' => 1279051598,
- 'changed' => 1279051598,
- 'comment' => 2,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 1,
- 'translate' => 0,
- // Node revision fields.
- 'vid' => 3,
- 'node_uid' => 1,
- 'revision_uid' => 1,
- 'title' => 'title for revision 3 (node 1)',
- 'body' => 'body for revision 3 (node 1)',
- 'teaser' => 'teaser for revision 3 (node 1)',
- 'log' => 'log for revision 3 (node 1)',
- 'format' => 1,
- ],
- ];
+ // The expected results.
+ // There are three revisions of nid 1, but the NodeRevision source ignores
+ // the current revision. So only two revisions will be returned here. nid 2
+ // is ignored because it only has one revision (the current one).
+ $tests[0]['expected_data'] = [
+ [
+ // Node fields.
+ 'nid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'status' => 1,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 1,
+ 'translate' => 0,
+ // Node revision fields.
+ 'vid' => 1,
+ 'node_uid' => 1,
+ 'revision_uid' => 1,
+ 'title' => 'title for revision 1 (node 1)',
+ 'body' => 'body for revision 1 (node 1)',
+ 'teaser' => 'teaser for revision 1 (node 1)',
+ 'log' => 'log for revision 1 (node 1)',
+ 'format' => 1,
+ ],
+ [
+ // Node fields.
+ 'nid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'status' => 1,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 1,
+ 'translate' => 0,
+ // Node revision fields.
+ 'vid' => 3,
+ 'node_uid' => 1,
+ 'revision_uid' => 1,
+ 'title' => 'title for revision 3 (node 1)',
+ 'body' => 'body for revision 3 (node 1)',
+ 'teaser' => 'teaser for revision 3 (node 1)',
+ 'log' => 'log for revision 3 (node 1)',
+ 'format' => 1,
+ ],
+ ];
+
+ return $tests;
+ }
}
diff --git a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTest.php
new file mode 100644
index 000000000..4bc1be0af
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTest.php
@@ -0,0 +1,328 @@
+ 'field_test_four',
+ 'type' => 'number_float',
+ 'global_settings' => 'a:0:{}',
+ 'required' => '0',
+ 'multiple' => '0',
+ 'db_storage' => '1',
+ 'module' => 'number',
+ 'db_columns' => 'a:1:{s:5:"value";a:3:{s:4:"type";s:5:"float";s:8:"not null";b:0;s:8:"sortable";b:1;}}',
+ 'active' => '1',
+ 'locked' => '0',
+ ],
+ ];
+ $tests[0]['source_data']['content_node_field_instance'] = [
+ [
+ 'field_name' => 'field_test_four',
+ 'type_name' => 'story',
+ 'weight' => '3',
+ 'label' => 'Float Field',
+ 'widget_type' => 'number',
+ 'widget_settings' => 'a:0:{}',
+ 'display_settings' => 'a:0:{}',
+ 'description' => 'An example float field.',
+ 'widget_module' => 'number',
+ 'widget_active' => '1',
+ ],
+ ];
+ $tests[0]['source_data']['content_type_story'] = [
+ [
+ 'nid' => 5,
+ 'vid' => 5,
+ 'uid' => 5,
+ 'field_test_four_value' => '3.14159',
+ ],
+ ];
+ $tests[0]['source_data']['system'] = [
+ [
+ 'type' => 'module',
+ 'name' => 'content',
+ 'schema_version' => 6001,
+ 'status' => TRUE,
+ ],
+ ];
+ $tests[0]['source_data']['node'] = [
+ [
+ 'nid' => 1,
+ 'vid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 1',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'translate' => 0,
+ 'tnid' => 0,
+ ],
+ [
+ 'nid' => 2,
+ 'vid' => 2,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 2',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'translate' => 0,
+ 'tnid' => 0,
+ ],
+ [
+ 'nid' => 5,
+ 'vid' => 5,
+ 'type' => 'story',
+ 'language' => 'en',
+ 'title' => 'node title 5',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'translate' => 0,
+ 'tnid' => 0,
+ ],
+ [
+ 'nid' => 6,
+ 'vid' => 6,
+ 'type' => 'story',
+ 'language' => 'en',
+ 'title' => 'node title 6',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279290909,
+ 'changed' => 1279308994,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'translate' => 0,
+ 'tnid' => 6,
+ ],
+ [
+ 'nid' => 7,
+ 'vid' => 7,
+ 'type' => 'story',
+ 'language' => 'fr',
+ 'title' => 'node title 7',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279290910,
+ 'changed' => 1279308995,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'translate' => 0,
+ 'tnid' => 6,
+ ],
+ ];
+ $tests[0]['source_data']['node_revisions'] = [
+ [
+ 'nid' => 1,
+ 'vid' => 1,
+ 'uid' => 2,
+ 'title' => 'node title 1',
+ 'body' => 'body for node 1',
+ 'teaser' => 'teaser for node 1',
+ 'log' => '',
+ 'format' => 1,
+ 'timestamp' => 1279051598,
+ ],
+ [
+ 'nid' => 2,
+ 'vid' => 2,
+ 'uid' => 2,
+ 'title' => 'node title 2',
+ 'body' => 'body for node 2',
+ 'teaser' => 'teaser for node 2',
+ 'log' => '',
+ 'format' => 1,
+ 'timestamp' => 1279308993,
+ ],
+ [
+ 'nid' => 5,
+ 'vid' => 5,
+ 'uid' => 2,
+ 'title' => 'node title 5',
+ 'body' => 'body for node 5',
+ 'teaser' => 'body for node 5',
+ 'log' => '',
+ 'format' => 1,
+ 'timestamp' => 1279308993,
+ ],
+ [
+ 'nid' => 6,
+ 'vid' => 6,
+ 'uid' => 2,
+ 'title' => 'node title 6',
+ 'body' => 'body for node 6',
+ 'teaser' => 'body for node 6',
+ 'log' => '',
+ 'format' => 1,
+ 'timestamp' => 1279308994,
+ ],
+ [
+ 'nid' => 7,
+ 'vid' => 7,
+ 'uid' => 2,
+ 'title' => 'node title 7',
+ 'body' => 'body for node 7',
+ 'teaser' => 'body for node 7',
+ 'log' => '',
+ 'format' => 1,
+ 'timestamp' => 1279308995,
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ // Node fields.
+ 'nid' => 1,
+ 'vid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 1',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 1,
+ 'translate' => 0,
+ // Node revision fields.
+ 'body' => 'body for node 1',
+ 'teaser' => 'teaser for node 1',
+ 'log' => '',
+ 'timestamp' => 1279051598,
+ 'format' => 1,
+ ],
+ [
+ // Node fields.
+ 'nid' => 2,
+ 'vid' => 2,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 2',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 2,
+ 'translate' => 0,
+ // Node revision fields.
+ 'body' => 'body for node 2',
+ 'teaser' => 'teaser for node 2',
+ 'log' => '',
+ 'timestamp' => 1279308993,
+ 'format' => 1,
+ ],
+ [
+ 'nid' => 5,
+ 'vid' => 5,
+ 'type' => 'story',
+ 'language' => 'en',
+ 'title' => 'node title 5',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 5,
+ 'translate' => 0,
+ // Node revision fields.
+ 'body' => 'body for node 5',
+ 'teaser' => 'body for node 5',
+ 'log' => '',
+ 'timestamp' => 1279308993,
+ 'format' => 1,
+ 'field_test_four' => [
+ [
+ 'value' => '3.14159',
+ 'delta' => 0,
+ ],
+ ],
+ ],
+ [
+ 'nid' => 6,
+ 'vid' => 6,
+ 'type' => 'story',
+ 'language' => 'en',
+ 'title' => 'node title 6',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'created' => 1279290909,
+ 'changed' => 1279308994,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 6,
+ 'translate' => 0,
+ // Node revision fields.
+ 'body' => 'body for node 6',
+ 'teaser' => 'body for node 6',
+ 'log' => '',
+ 'timestamp' => 1279308994,
+ 'format' => 1,
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTranslationTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTranslationTest.php
new file mode 100644
index 000000000..e7de62b07
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTranslationTest.php
@@ -0,0 +1,65 @@
+ 7,
+ 'vid' => 7,
+ 'type' => 'story',
+ 'language' => 'fr',
+ 'title' => 'node title 7',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'created' => 1279290910,
+ 'changed' => 1279308995,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'moderate' => 0,
+ 'sticky' => 0,
+ 'tnid' => 6,
+ 'translate' => 0,
+ // Node revision fields.
+ 'body' => 'body for node 7',
+ 'teaser' => 'body for node 7',
+ 'log' => '',
+ 'timestamp' => 1279308995,
+ 'format' => 1,
+ ],
+ ];
+
+ // Do an automatic count.
+ $tests[0]['expected_count'] = NULL;
+
+ // Set up source plugin configuration.
+ $tests[0]['configuration'] = [
+ 'translations' => TRUE,
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTypeTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTypeTest.php
new file mode 100644
index 000000000..38af680a1
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTypeTest.php
@@ -0,0 +1,67 @@
+ 'page',
+ 'name' => 'Page',
+ 'module' => 'node',
+ 'description' => 'A page, similar in form to a story, is a simple method for creating and displaying information that rarely changes, such as an "About us" section of a website. By default, a page entry does not allow visitor comments and is not featured on the site\'s initial home page.',
+ 'help' => '',
+ 'title_label' => 'Title',
+ 'has_body' => 1,
+ 'body_label' => 'Body',
+ 'min_word_count' => 0,
+ 'custom' => 1,
+ 'modified' => 0,
+ 'locked' => 0,
+ 'orig_type' => 'page',
+ ],
+ [
+ 'type' => 'story',
+ 'name' => 'Story',
+ 'module' => 'node',
+ 'description' => 'A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site\'s initial home page, and provides the ability to post comments.',
+ 'help' => '',
+ 'title_label' => 'Title',
+ 'has_body' => 1,
+ 'body_label' => 'Body',
+ 'min_word_count' => 0,
+ 'custom' => 1,
+ 'modified' => 0,
+ 'locked' => 0,
+ 'orig_type' => 'story',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['node_type'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/ViewModeTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/ViewModeTest.php
new file mode 100644
index 000000000..18d941c8a
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/ViewModeTest.php
@@ -0,0 +1,71 @@
+ serialize([
+ 'weight' => '31',
+ 'parent' => '',
+ 'label' => [
+ 'format' => 'above',
+ ],
+ 'teaser' => [
+ 'format' => 'default',
+ 'exclude' => 0,
+ ],
+ 'full' => [
+ 'format' => 'default',
+ 'exclude' => 0,
+ ],
+ 4 => [
+ 'format' => 'default',
+ 'exclude' => 0,
+ ],
+ ]),
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'entity_type' => 'node',
+ 'view_mode' => '4',
+ ],
+ [
+ 'entity_type' => 'node',
+ 'view_mode' => 'teaser',
+ ],
+ [
+ 'entity_type' => 'node',
+ 'view_mode' => 'full',
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTest.php
new file mode 100644
index 000000000..6b19b9580
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTest.php
@@ -0,0 +1,232 @@
+ 1,
+ 'vid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 1',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'sticky' => 0,
+ 'tnid' => 0,
+ 'translate' => 0,
+ ],
+ [
+ // Node fields.
+ 'nid' => 2,
+ 'vid' => 2,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 2',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'sticky' => 0,
+ 'tnid' => 0,
+ 'translate' => 0,
+ ],
+ [
+ // Node fields.
+ 'nid' => 5,
+ 'vid' => 5,
+ 'type' => 'article',
+ 'language' => 'en',
+ 'title' => 'node title 5',
+ 'uid' => 1,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'sticky' => 0,
+ 'tnid' => 0,
+ 'translate' => 0,
+ ],
+
+ ];
+ $tests[0]['source_data']['node_revision'] = [
+ [
+ // Node fields.
+ 'nid' => 1,
+ 'vid' => 1,
+ 'uid' => 2,
+ 'title' => 'node title 1',
+ 'log' => '',
+ 'timestamp' => 1279051598,
+ 'status' => 1,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'sticky' => 0,
+ ],
+ [
+ // Node fields.
+ 'nid' => 2,
+ 'vid' => 2,
+ 'uid' => 2,
+ 'title' => 'node title 2',
+ 'log' => '',
+ 'timestamp' => 1279308993,
+ 'status' => 1,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'sticky' => 0,
+ ],
+ [
+ // Node fields.
+ 'nid' => 5,
+ 'vid' => 5,
+ 'uid' => 2,
+ 'title' => 'node title 5',
+ 'log' => '',
+ 'timestamp' => 1279308993,
+ 'status' => 1,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'sticky' => 0,
+ ],
+ ];
+ $tests[0]['source_data']['field_config_instance'] = [
+ [
+ 'id' => '2',
+ 'field_id' => '2',
+ 'field_name' => 'body',
+ 'entity_type' => 'node',
+ 'bundle' => 'page',
+ 'data' => 'a:0:{}',
+ 'deleted' => '0',
+ ],
+ [
+ 'id' => '2',
+ 'field_id' => '2',
+ 'field_name' => 'body',
+ 'entity_type' => 'node',
+ 'bundle' => 'article',
+ 'data' => 'a:0:{}',
+ 'deleted' => '0',
+ ],
+ ];
+ $tests[0]['source_data']['field_revision_body'] = [
+ [
+ 'entity_type' => 'node',
+ 'bundle' => 'page',
+ 'deleted' => '0',
+ 'entity_id' => '1',
+ 'revision_id' => '1',
+ 'language' => 'en',
+ 'delta' => '0',
+ 'body_value' => 'Foobaz',
+ 'body_summary' => '',
+ 'body_format' => 'filtered_html',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ // Node fields.
+ 'nid' => 1,
+ 'vid' => 1,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 1',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'created' => 1279051598,
+ 'changed' => 1279051598,
+ 'comment' => 2,
+ 'promote' => 1,
+ 'sticky' => 0,
+ 'tnid' => 0,
+ 'translate' => 0,
+ 'log' => '',
+ 'timestamp' => 1279051598,
+ 'body' => [
+ [
+ 'value' => 'Foobaz',
+ 'summary' => '',
+ 'format' => 'filtered_html',
+ ],
+ ],
+ ],
+ [
+ // Node fields.
+ 'nid' => 2,
+ 'vid' => 2,
+ 'type' => 'page',
+ 'language' => 'en',
+ 'title' => 'node title 2',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'sticky' => 0,
+ 'tnid' => 0,
+ 'translate' => 0,
+ 'log' => '',
+ 'timestamp' => 1279308993,
+ ],
+ [
+ // Node fields.
+ 'nid' => 5,
+ 'vid' => 5,
+ 'type' => 'article',
+ 'language' => 'en',
+ 'title' => 'node title 5',
+ 'node_uid' => 1,
+ 'revision_uid' => 2,
+ 'status' => 1,
+ 'created' => 1279290908,
+ 'changed' => 1279308993,
+ 'comment' => 0,
+ 'promote' => 1,
+ 'sticky' => 0,
+ 'tnid' => 0,
+ 'translate' => 0,
+ 'log' => '',
+ 'timestamp' => 1279308993,
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTypeTest.php b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTypeTest.php
new file mode 100644
index 000000000..1c738d1a8
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTypeTest.php
@@ -0,0 +1,87 @@
+ 'page',
+ 'name' => 'Page',
+ 'base' => 'node',
+ 'description' => 'A page, similar in form to a story, is a simple method for creating and displaying information that rarely changes, such as an "About us" section of a website. By default, a page entry does not allow visitor comments and is not featured on the site\'s initial home page.',
+ 'help' => '',
+ 'title_label' => 'Title',
+ 'custom' => 1,
+ 'modified' => 0,
+ 'locked' => 0,
+ 'disabled' => 0,
+ 'orig_type' => 'page',
+ ],
+ [
+ 'type' => 'story',
+ 'name' => 'Story',
+ 'base' => 'node',
+ 'description' => 'A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site\'s initial home page, and provides the ability to post comments.',
+ 'help' => '',
+ 'title_label' => 'Title',
+ 'custom' => 1,
+ 'modified' => 0,
+ 'locked' => 0,
+ 'disabled' => 0,
+ 'orig_type' => 'story',
+ ],
+ ];
+ $tests[0]['source_data']['variable'] = [
+ [
+ 'name' => 'node_options_page',
+ 'value' => 'a:1:{i:0;s:6:"status";}',
+ ],
+ [
+ 'name' => 'node_options_story',
+ 'value' => 'a:1:{i:0;s:6:"status";}',
+ ],
+ ];
+ $tests[0]['source_data']['field_config_instance'] = [
+ [
+ 'entity_type' => 'node',
+ 'bundle' => 'page',
+ 'field_name' => 'body',
+ 'data' => 'a:1:{s:5:"label";s:4:"Body";}',
+ ],
+ [
+ 'entity_type' => 'node',
+ 'bundle' => 'story',
+ 'field_name' => 'body',
+ 'data' => 'a:1:{s:5:"label";s:4:"Body";}',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['node_type'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeByNodeTypeTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeByNodeTypeTest.php
deleted file mode 100644
index 6a308ffff..000000000
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeByNodeTypeTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
- 'test',
- // The fake configuration for the source.
- 'source' => array(
- 'plugin' => 'd6_node',
- 'node_type' => 'page',
- ),
- );
-
- protected $expectedResults = array(
- array(
- // Node fields.
- 'nid' => 1,
- 'vid' => 1,
- 'type' => 'page',
- 'language' => 'en',
- 'title' => 'node title 1',
- 'uid' => 1,
- 'status' => 1,
- 'timestamp' => 1279051598,
- 'created' => 1279051598,
- 'changed' => 1279051598,
- 'comment' => 2,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 1,
- 'translate' => 0,
- // Node revision fields.
- 'body' => 'body for node 1',
- 'teaser' => 'teaser for node 1',
- 'format' => 1,
- 'log' => 'log message 1',
- ),
- array(
- // Node fields.
- 'nid' => 2,
- 'vid' => 2,
- 'type' => 'page',
- 'language' => 'en',
- 'title' => 'node title 2',
- 'uid' => 1,
- 'status' => 1,
- 'timestamp' => 1279290908,
- 'created' => 1279290908,
- 'changed' => 1279308993,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 2,
- 'translate' => 0,
- // Node revision fields.
- 'body' => 'body for node 2',
- 'teaser' => 'teaser for node 2',
- 'format' => 1,
- 'log' => 'log message 2',
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $database_contents = $this->expectedResults;
- array_walk($this->expectedResults, function (&$row) {
- $row['node_uid'] = $row['uid'];
- $row['revision_uid'] = $row['uid'] + 1;
- unset($row['uid']);
- });
-
- $database_contents[] = array(
- // Node fields.
- 'nid' => 5,
- 'vid' => 5,
- 'type' => 'article',
- 'language' => 'en',
- 'title' => 'node title 5',
- 'uid' => 1,
- 'status' => 1,
- 'timestamp' => 1279290908,
- 'created' => 1279290908,
- 'changed' => 1279308993,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 0,
- 'translate' => 0,
- // Node revision fields.
- 'body' => 'body for node 5',
- 'teaser' => 'body for node 5',
- 'format' => 1,
- 'log' => 'log message 3',
- );
-
- // Add another row with an article node and make sure it is not migrated.
-
- foreach ($database_contents as $k => $row) {
- foreach (array('nid', 'vid', 'title', 'uid', 'body', 'teaser', 'format', 'timestamp', 'log') as $field) {
- $this->databaseContents['node_revisions'][$k][$field] = $row[$field];
- switch ($field) {
- case 'nid': case 'vid':
- break;
- case 'uid':
- $this->databaseContents['node_revisions'][$k]['uid']++;
- break;
- default:
- unset($row[$field]);
- break;
- }
- }
- $this->databaseContents['node'][$k] = $row;
- }
-
- parent::setUp();
- }
-
-}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTest.php
deleted file mode 100644
index b6ce22e2e..000000000
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTest.php
+++ /dev/null
@@ -1,122 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd6_node',
- ),
- );
-
- protected $expectedResults = array(
- array(
- // Node fields.
- 'nid' => 1,
- 'vid' => 1,
- 'type' => 'page',
- 'language' => 'en',
- 'title' => 'node title 1',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279051598,
- 'changed' => 1279051598,
- 'comment' => 2,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 1,
- 'translate' => 0,
- // Node revision fields.
- 'body' => 'body for node 1',
- 'teaser' => 'teaser for node 1',
- 'log' => '',
- 'timestamp' => 1279051598,
- 'format' => 1,
- ),
- array(
- // Node fields.
- 'nid' => 2,
- 'vid' => 2,
- 'type' => 'page',
- 'language' => 'en',
- 'title' => 'node title 2',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290908,
- 'changed' => 1279308993,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 2,
- 'translate' => 0,
- // Node revision fields.
- 'body' => 'body for node 2',
- 'teaser' => 'teaser for node 2',
- 'log' => '',
- 'timestamp' => 1279308993,
- 'format' => 1,
- ),
- array(
- 'nid' => 5,
- 'vid' => 5,
- 'type' => 'story',
- 'language' => 'en',
- 'title' => 'node title 5',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290908,
- 'changed' => 1279308993,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 5,
- 'translate' => 0,
- // Node revision fields.
- 'body' => 'body for node 5',
- 'teaser' => 'body for node 5',
- 'log' => '',
- 'timestamp' => 1279308993,
- 'format' => 1,
- 'field_test_four' => array(
- array(
- 'value' => '3.14159',
- 'delta' => 0,
- ),
- ),
- ),
- array(
- 'nid' => 6,
- 'vid' => 6,
- 'type' => 'story',
- 'language' => 'en',
- 'title' => 'node title 6',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290909,
- 'changed' => 1279308994,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 6,
- 'translate' => 0,
- // Node revision fields.
- 'body' => 'body for node 6',
- 'teaser' => 'body for node 6',
- 'log' => '',
- 'timestamp' => 1279308994,
- 'format' => 1,
- ),
- );
-
-}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTestBase.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTestBase.php
deleted file mode 100644
index 8850fc461..000000000
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTestBase.php
+++ /dev/null
@@ -1,179 +0,0 @@
-databaseContents['content_node_field'] = array(
- array(
- 'field_name' => 'field_test_four',
- 'type' => 'number_float',
- 'global_settings' => 'a:0:{}',
- 'required' => '0',
- 'multiple' => '0',
- 'db_storage' => '1',
- 'module' => 'number',
- 'db_columns' => 'a:1:{s:5:"value";a:3:{s:4:"type";s:5:"float";s:8:"not null";b:0;s:8:"sortable";b:1;}}',
- 'active' => '1',
- 'locked' => '0',
- ),
- );
- $this->databaseContents['content_node_field_instance'] = array(
- array(
- 'field_name' => 'field_test_four',
- 'type_name' => 'story',
- 'weight' => '3',
- 'label' => 'Float Field',
- 'widget_type' => 'number',
- 'widget_settings' => 'a:0:{}',
- 'display_settings' => 'a:0:{}',
- 'description' => 'An example float field.',
- 'widget_module' => 'number',
- 'widget_active' => '1',
- ),
- );
- $this->databaseContents['content_type_story'] = array(
- array(
- 'nid' => 5,
- 'vid' => 5,
- 'uid' => 5,
- 'field_test_four_value' => '3.14159',
- ),
- );
- $this->databaseContents['system'] = array(
- array(
- 'type' => 'module',
- 'name' => 'content',
- 'schema_version' => 6001,
- 'status' => TRUE,
- ),
- );
- $this->databaseContents['node'] = [
- [
- 'nid' => 1,
- 'vid' => 1,
- 'type' => 'page',
- 'language' => 'en',
- 'title' => 'node title 1',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279051598,
- 'changed' => 1279051598,
- 'comment' => 2,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'translate' => 0,
- 'tnid' => 0,
- ],
- [
- 'nid' => 2,
- 'vid' => 2,
- 'type' => 'page',
- 'language' => 'en',
- 'title' => 'node title 2',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290908,
- 'changed' => 1279308993,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'translate' => 0,
- 'tnid' => 0,
- ],
- [
- 'nid' => 5,
- 'vid' => 5,
- 'type' => 'story',
- 'language' => 'en',
- 'title' => 'node title 5',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290908,
- 'changed' => 1279308993,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'translate' => 0,
- 'tnid' => 0,
- ],
- [
- 'nid' => 6,
- 'vid' => 6,
- 'type' => 'story',
- 'language' => 'en',
- 'title' => 'node title 6',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290909,
- 'changed' => 1279308994,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'translate' => 0,
- 'tnid' => 6,
- ],
- [
- 'nid' => 7,
- 'vid' => 7,
- 'type' => 'story',
- 'language' => 'fr',
- 'title' => 'node title 7',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290910,
- 'changed' => 1279308995,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'translate' => 0,
- 'tnid' => 6,
- ],
- ];
-
- foreach ($this->databaseContents['node'] as $k => $row) {
- // Find the equivalent row from expected results.
- $result_row = NULL;
- foreach ($this->expectedResults as $result) {
- if (in_array($result['nid'], [$row['nid'], $row['tnid']]) && $result['language'] == $row['language']) {
- $result_row = $result;
- break;
- }
- }
-
- // Populate node_revisions.
- foreach (array('nid', 'vid', 'title', 'uid', 'body', 'teaser', 'format', 'timestamp', 'log') as $field) {
- $value = isset($row[$field]) ? $row[$field] : $result_row[$field];
- $this->databaseContents['node_revisions'][$k][$field] = $value;
- if ($field == 'uid') {
- $this->databaseContents['node_revisions'][$k]['uid']++;
- }
- }
- }
-
- array_walk($this->expectedResults, function (&$row) {
- $row['node_uid'] = $row['uid'];
- $row['revision_uid'] = $row['uid'] + 1;
- unset($row['uid']);
- });
-
- parent::setUp();
- }
-
-}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTranslationTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTranslationTest.php
deleted file mode 100644
index da0b167ed..000000000
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTranslationTest.php
+++ /dev/null
@@ -1,46 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd6_node',
- 'translations' => TRUE,
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'nid' => 7,
- 'vid' => 7,
- 'type' => 'story',
- 'language' => 'fr',
- 'title' => 'node title 7',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290910,
- 'changed' => 1279308995,
- 'comment' => 0,
- 'promote' => 1,
- 'moderate' => 0,
- 'sticky' => 0,
- 'tnid' => 6,
- 'translate' => 0,
- // Node revision fields.
- 'body' => 'body for node 7',
- 'teaser' => 'body for node 7',
- 'log' => '',
- 'timestamp' => 1279308995,
- 'format' => 1,
- ),
- );
-
-}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTypeTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTypeTest.php
deleted file mode 100644
index 591bcd9bc..000000000
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTypeTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
- 'test_nodetypes',
- 'source' => array(
- 'plugin' => 'd6_nodetype',
- ),
- );
-
- // We need to set up the database contents; it's easier to do that below.
- // These are sample result queries.
- protected $expectedResults = array(
- array(
- 'type' => 'page',
- 'name' => 'Page',
- 'module' => 'node',
- 'description' => 'A page, similar in form to a story, is a simple method for creating and displaying information that rarely changes, such as an "About us" section of a website. By default, a page entry does not allow visitor comments and is not featured on the site\'s initial home page.',
- 'help' => '',
- 'title_label' => 'Title',
- 'has_body' => 1,
- 'body_label' => 'Body',
- 'min_word_count' => 0,
- 'custom' => 1,
- 'modified' => 0,
- 'locked' => 0,
- 'orig_type' => 'page',
- ),
- array(
- 'type' => 'story',
- 'name' => 'Story',
- 'module' => 'node',
- 'description' => 'A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site\'s initial home page, and provides the ability to post comments.',
- 'help' => '',
- 'title_label' => 'Title',
- 'has_body' => 1,
- 'body_label' => 'Body',
- 'min_word_count' => 0,
- 'custom' => 1,
- 'modified' => 0,
- 'locked' => 0,
- 'orig_type' => 'story',
- ),
- );
-
- /**
- * Prepopulate contents with results.
- */
- protected function setUp() {
- $this->databaseContents['node_type'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/ViewModeTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/ViewModeTest.php
deleted file mode 100644
index 1df544390..000000000
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/ViewModeTest.php
+++ /dev/null
@@ -1,73 +0,0 @@
- 'view_mode_test',
- 'source' => array(
- 'plugin' => 'd6_field_instance_view_mode',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'entity_type' => 'node',
- 'view_mode' => '4',
- ),
- array(
- 'entity_type' => 'node',
- 'view_mode' => 'teaser',
- ),
- array(
- 'entity_type' => 'node',
- 'view_mode' => 'full',
- ),
- );
-
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
-
- $this->databaseContents['content_node_field_instance'][] = array(
- 'display_settings' => serialize(array(
- 'weight' => '31',
- 'parent' => '',
- 'label' => array(
- 'format' => 'above',
- ),
- 'teaser' => array(
- 'format' => 'default',
- 'exclude' => 0,
- ),
- 'full' => array(
- 'format' => 'default',
- 'exclude' => 0,
- ),
- 4 => array(
- 'format' => 'default',
- 'exclude' => 0,
- ),
- )),
- );
-
- parent::setUp();
- }
-
-}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d7/NodeTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d7/NodeTest.php
deleted file mode 100644
index b0489bfad..000000000
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d7/NodeTest.php
+++ /dev/null
@@ -1,147 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd7_node',
- ),
- );
-
- protected $expectedResults = array(
- array(
- // Node fields.
- 'nid' => 1,
- 'vid' => 1,
- 'type' => 'page',
- 'language' => 'en',
- 'title' => 'node title 1',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279051598,
- 'changed' => 1279051598,
- 'comment' => 2,
- 'promote' => 1,
- 'sticky' => 0,
- 'tnid' => 0,
- 'translate' => 0,
- 'log' => '',
- 'timestamp' => 1279051598,
- ),
- array(
- // Node fields.
- 'nid' => 2,
- 'vid' => 2,
- 'type' => 'page',
- 'language' => 'en',
- 'title' => 'node title 2',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290908,
- 'changed' => 1279308993,
- 'comment' => 0,
- 'promote' => 1,
- 'sticky' => 0,
- 'tnid' => 0,
- 'translate' => 0,
- 'log' => '',
- 'timestamp' => 1279308993,
- ),
- array(
- // Node fields.
- 'nid' => 5,
- 'vid' => 5,
- 'type' => 'article',
- 'language' => 'en',
- 'title' => 'node title 5',
- 'uid' => 1,
- 'status' => 1,
- 'created' => 1279290908,
- 'changed' => 1279308993,
- 'comment' => 0,
- 'promote' => 1,
- 'sticky' => 0,
- 'tnid' => 0,
- 'translate' => 0,
- 'log' => '',
- 'timestamp' => 1279308993,
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- foreach ($this->expectedResults as $k => $row) {
- foreach (array('nid', 'vid', 'title', 'uid', 'timestamp', 'log') as $field) {
- $this->databaseContents['node_revision'][$k][$field] = $row[$field];
- switch ($field) {
- case 'nid': case 'vid':
- break;
- case 'uid':
- $this->databaseContents['node_revision'][$k]['uid']++;
- break;
- default:
- unset($row[$field]);
- break;
- }
- }
- $this->databaseContents['node'][$k] = $row;
- }
- array_walk($this->expectedResults, function (&$row) {
- $row['node_uid'] = $row['uid'];
- $row['revision_uid'] = $row['uid'] + 1;
- unset($row['uid']);
- });
-
- $this->databaseContents['field_config_instance'] = array(
- array(
- 'id' => '2',
- 'field_id' => '2',
- 'field_name' => 'body',
- 'entity_type' => 'node',
- 'bundle' => 'page',
- 'data' => 'a:0:{}',
- 'deleted' => '0',
- ),
- array(
- 'id' => '2',
- 'field_id' => '2',
- 'field_name' => 'body',
- 'entity_type' => 'node',
- 'bundle' => 'article',
- 'data' => 'a:0:{}',
- 'deleted' => '0',
- ),
- );
- $this->databaseContents['field_revision_body'] = array(
- array(
- 'entity_type' => 'node',
- 'bundle' => 'page',
- 'deleted' => '0',
- 'entity_id' => '1',
- 'revision_id' => '1',
- 'language' => 'en',
- 'delta' => '0',
- 'body_value' => 'Foobaz',
- 'body_summary' => '',
- 'body_format' => 'filtered_html',
- ),
- );
-
- parent::setUp();
- }
-
-}
diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d7/NodeTypeTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d7/NodeTypeTest.php
deleted file mode 100644
index 19e1491af..000000000
--- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d7/NodeTypeTest.php
+++ /dev/null
@@ -1,84 +0,0 @@
- 'test_nodetypes',
- 'source' => array(
- 'plugin' => 'd7_node_type',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'type' => 'page',
- 'name' => 'Page',
- 'base' => 'node',
- 'description' => 'A page, similar in form to a story, is a simple method for creating and displaying information that rarely changes, such as an "About us" section of a website. By default, a page entry does not allow visitor comments and is not featured on the site\'s initial home page.',
- 'help' => '',
- 'title_label' => 'Title',
- 'custom' => 1,
- 'modified' => 0,
- 'locked' => 0,
- 'disabled' => 0,
- 'orig_type' => 'page',
- ),
- array(
- 'type' => 'story',
- 'name' => 'Story',
- 'base' => 'node',
- 'description' => 'A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site\'s initial home page, and provides the ability to post comments.',
- 'help' => '',
- 'title_label' => 'Title',
- 'custom' => 1,
- 'modified' => 0,
- 'locked' => 0,
- 'disabled' => 0,
- 'orig_type' => 'story',
- ),
- );
-
- /**
- * Prepopulate contents with results.
- */
- protected function setUp() {
- $this->databaseContents['node_type'] = $this->expectedResults;
- $this->databaseContents['variable'] = array(
- array(
- 'name' => 'node_options_page',
- 'value' => 'a:1:{i:0;s:6:"status";}',
- ),
- array(
- 'name' => 'node_options_story',
- 'value' => 'a:1:{i:0;s:6:"status";}',
- ),
- );
- $this->databaseContents['field_config_instance'] = array(
- array(
- 'entity_type' => 'node',
- 'bundle' => 'page',
- 'field_name' => 'body',
- 'data' => 'a:1:{s:5:"label";s:4:"Body";}',
- ),
- array(
- 'entity_type' => 'node',
- 'bundle' => 'story',
- 'field_name' => 'body',
- 'data' => 'a:1:{s:5:"label";s:4:"Body";}',
- )
- );
- parent::setUp();
- }
-
-}
diff --git a/core/modules/options/options.api.php b/core/modules/options/options.api.php
index a4ac605d4..28ac77114 100644
--- a/core/modules/options/options.api.php
+++ b/core/modules/options/options.api.php
@@ -20,7 +20,7 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface;
* properties.
* @param array $context
* An associative array containing:
- * - field_definition: The field definition
+ * - fieldDefinition: The field definition
* (\Drupal\Core\Field\FieldDefinitionInterface).
* - entity: The entity object the field is attached to
* (\Drupal\Core\Entity\EntityInterface).
@@ -30,7 +30,7 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface;
*/
function hook_options_list_alter(array &$options, array $context) {
// Check if this is the field we want to change.
- if ($context['field']->id() == 'field_option') {
+ if ($context['fieldDefinition']->id() == 'field_option') {
// Change the label of the empty option.
$options['_none'] = t('== Empty ==');
}
diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php b/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
index aaea32f9b..4527ca361 100644
--- a/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
+++ b/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php
@@ -168,7 +168,7 @@ abstract class ListItemBase extends FieldItemBase implements OptionsProviderInte
* @return array|null
* The array of extracted key/value pairs, or NULL if the string is invalid.
*
- * @see \Drupal\options\Plugin\Field\FieldType\ListTextItem::allowedValuesString()
+ * @see \Drupal\options\Plugin\Field\FieldType\ListItemBase::allowedValuesString()
*/
protected static function extractAllowedValues($string, $has_data) {
$values = array();
@@ -277,7 +277,7 @@ abstract class ListItemBase extends FieldItemBase implements OptionsProviderInte
* Allowed values were the array key is the 'value' value, the value is
* the 'label' value.
*
- * @see Drupal\options\Plugin\Field\FieldType\ListItemBase::structureAllowedValues()
+ * @see \Drupal\options\Plugin\Field\FieldType\ListItemBase::structureAllowedValues()
*/
protected static function simplifyAllowedValues(array $structured_values) {
$values = array();
@@ -302,7 +302,7 @@ abstract class ListItemBase extends FieldItemBase implements OptionsProviderInte
* Array of items with a 'value' and 'label' key each for the allowed
* values.
*
- * @see Drupal\options\Plugin\Field\FieldType\ListItemBase::simplifyAllowedValues()
+ * @see \Drupal\options\Plugin\Field\FieldType\ListItemBase::simplifyAllowedValues()
*/
protected static function structureAllowedValues(array $values) {
$structured_values = array();
diff --git a/core/modules/outside_in/js/outside_in.js b/core/modules/outside_in/js/outside_in.js
index a4f94aa90..d079664cd 100644
--- a/core/modules/outside_in/js/outside_in.js
+++ b/core/modules/outside_in/js/outside_in.js
@@ -7,7 +7,7 @@
'use strict';
- var blockConfigureSelector = '[data-dialog-renderer="offcanvas"]';
+ var blockConfigureSelector = '[data-outside-in-edit]';
var toggleEditSelector = '[data-drupal-outsidein="toggle"]';
var itemsToToggleSelector = '#main-canvas, #toolbar-bar, [data-drupal-outsidein="editable"] a, [data-drupal-outsidein="editable"] button';
var contextualItemsSelector = '[data-contextual-id] a, [data-contextual-id] button';
@@ -186,7 +186,11 @@
// @todo Move logic for data-dialog-renderer attribute into ajax.js
// https://www.drupal.org/node/2784443
instance.options.url = instance.options.url.replace(search, replace);
- instance.options.data.dialogOptions = {outsideInActiveEditableId: $(instance.element).parents('.outside-in-editable').attr('id')};
+ // Check to make sure existing dialogOptions aren't overridden.
+ if (!('dialogOptions' in instance.options.data)) {
+ instance.options.data.dialogOptions = {};
+ }
+ instance.options.data.dialogOptions.outsideInActiveEditableId = $(instance.element).parents('.outside-in-editable').attr('id');
instance.progress = {type: 'fullscreen'};
});
}
diff --git a/core/modules/outside_in/outside_in.module b/core/modules/outside_in/outside_in.module
index 298b994c9..a7fab7bd2 100644
--- a/core/modules/outside_in/outside_in.module
+++ b/core/modules/outside_in/outside_in.module
@@ -38,6 +38,7 @@ function outside_in_contextual_links_view_alter(&$element, $items) {
'class' => ['use-ajax'],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'offcanvas',
+ 'data-outside-in-edit' => TRUE,
];
$element['#attached']['library'][] = 'outside_in/drupal.off_canvas';
diff --git a/core/modules/outside_in/src/Ajax/OpenOffCanvasDialogCommand.php b/core/modules/outside_in/src/Ajax/OpenOffCanvasDialogCommand.php
index f6663deff..d78dc3539 100644
--- a/core/modules/outside_in/src/Ajax/OpenOffCanvasDialogCommand.php
+++ b/core/modules/outside_in/src/Ajax/OpenOffCanvasDialogCommand.php
@@ -14,8 +14,10 @@ class OpenOffCanvasDialogCommand extends OpenDialogCommand {
/**
* Constructs an OpenOffCanvasDialogCommand object.
*
- * Drupal provides a built-in offcanvas tray for this purpose, so no selector
- * needs to be provided.
+ * The off-canvas dialog differs from the normal modal provided by
+ * OpenDialogCommand in that a off-canvas has built in positioning and
+ * behaviours. Drupal provides a built-in off-canvas tray for this purpose,
+ * so the selector is hard-coded in the call to the parent constructor.
*
* @param string $title
* The title of the dialog.
@@ -46,15 +48,10 @@ class OpenOffCanvasDialogCommand extends OpenDialogCommand {
* {@inheritdoc}
*/
public function render() {
- return [
- 'command' => 'openDialog',
- 'selector' => '#drupal-offcanvas',
- 'settings' => $this->settings,
- 'data' => $this->getRenderedContent(),
- 'dialogOptions' => $this->dialogOptions,
- 'effect' => 'fade',
- 'speed' => 1000,
- ];
+ $build = parent::render();
+ $build['effect'] = 'fade';
+ $build['speed'] = 1000;
+ return $build;
}
}
diff --git a/core/modules/outside_in/src/Tests/Ajax/OffCanvasDialogTest.php b/core/modules/outside_in/src/Tests/Ajax/OffCanvasDialogTest.php
index 1e5b0d195..f3510427b 100644
--- a/core/modules/outside_in/src/Tests/Ajax/OffCanvasDialogTest.php
+++ b/core/modules/outside_in/src/Tests/Ajax/OffCanvasDialogTest.php
@@ -39,7 +39,7 @@ class OffCanvasDialogTest extends AjaxTestBase {
'data' => $dialog_contents,
'dialogOptions' =>
[
- 'title' => 'AJAX Dialog contents',
+ 'title' => 'AJAX Dialog & contents',
'modal' => FALSE,
'autoResize' => FALSE,
'resizable' => 'w',
diff --git a/core/modules/outside_in/templates/outside-in-page-wrapper.html.twig b/core/modules/outside_in/templates/outside-in-page-wrapper.html.twig
index 1d1c50fc7..588918334 100644
--- a/core/modules/outside_in/templates/outside-in-page-wrapper.html.twig
+++ b/core/modules/outside_in/templates/outside-in-page-wrapper.html.twig
@@ -12,12 +12,10 @@
*/
#}
{% if children %}
- {% spaceless %}
-
-
- {{ children }}
-
+
+
+ {{ children }}
-
- {% endspaceless %}
+
+
{% endif %}
diff --git a/core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php b/core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php
index e711fc3f5..9ee8e1768 100644
--- a/core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php
+++ b/core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php
@@ -2,6 +2,7 @@
namespace Drupal\offcanvas_test\Controller;
+use Drupal\Component\Serialization\Json;
use Drupal\Core\Url;
/**
@@ -66,6 +67,9 @@ class TestController {
'class' => ['use-ajax'],
'data-dialog-type' => 'dialog',
'data-dialog-renderer' => 'offcanvas',
+ 'data-dialog-options' => Json::encode([
+ 'width' => 555,
+ ]),
],
'#attached' => [
'library' => [
diff --git a/core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php b/core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php
index a2a9514af..8e04fd8b5 100644
--- a/core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php
+++ b/core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php
@@ -52,10 +52,13 @@ class OffCanvasTest extends OutsideInJavascriptTestBase {
$tray_text = $offcanvas_tray->findById('drupal-offcanvas')->getText();
$this->assertEquals("Thing $link_index says hello", $tray_text);
- // Check no title behavior.
if ($link_index == '2') {
+ // Check no title behavior.
$web_assert->elementExists('css', '.ui-dialog-empty-title');
$this->assertEquals('', $header_text);
+
+ $style = $page->find('css', '.ui-dialog-offcanvas')->getAttribute('style');
+ self::assertTrue(strstr($style, 'width: 555px;') !== FALSE, 'Dialog width respected.');
}
else {
// Check that header is correct.
diff --git a/core/modules/page_cache/src/Tests/PageCacheTest.php b/core/modules/page_cache/src/Tests/PageCacheTest.php
index 22b8b51a1..1ec9282eb 100644
--- a/core/modules/page_cache/src/Tests/PageCacheTest.php
+++ b/core/modules/page_cache/src/Tests/PageCacheTest.php
@@ -510,4 +510,31 @@ class PageCacheTest extends WebTestBase {
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Drupal page cache header not found.');
}
+ /**
+ * Tests that HEAD requests are treated the same as GET requests.
+ */
+ public function testHead() {
+ // GET, then HEAD.
+ $url_a = $this->buildUrl('system-test/set-header', ['query' => ['name' => 'Foo', 'value' => 'bar']]);
+ $response_body = $this->curlExec([CURLOPT_HTTPGET => TRUE, CURLOPT_URL => $url_a, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_NOBODY => FALSE]);
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
+ $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
+ $this->assertEqual('The following header was set: Foo: bar', $response_body);
+ $response_body = $this->curlExec([CURLOPT_HTTPGET => FALSE, CURLOPT_URL => $url_a, CURLOPT_CUSTOMREQUEST => 'HEAD', CURLOPT_NOBODY => FALSE]);
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+ $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
+ $this->assertEqual('', $response_body);
+
+ // HEAD, then GET.
+ $url_b = $this->buildUrl('system-test/set-header', ['query' => ['name' => 'Foo', 'value' => 'baz']]);
+ $response_body = $this->curlExec([CURLOPT_HTTPGET => FALSE, CURLOPT_URL => $url_b, CURLOPT_CUSTOMREQUEST => 'HEAD', CURLOPT_NOBODY => FALSE]);
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
+ $this->assertEqual($this->drupalGetHeader('Foo'), 'baz', 'Custom header was sent.');
+ $this->assertEqual('', $response_body);
+ $response_body = $this->curlExec([CURLOPT_HTTPGET => TRUE, CURLOPT_URL => $url_b, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_NOBODY => FALSE]);
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+ $this->assertEqual($this->drupalGetHeader('Foo'), 'baz', 'Custom header was sent.');
+ $this->assertEqual('The following header was set: Foo: baz', $response_body);
+ }
+
}
diff --git a/core/modules/path/tests/src/Kernel/Plugin/migrate/source/d6/UrlAliasTest.php b/core/modules/path/tests/src/Kernel/Plugin/migrate/source/d6/UrlAliasTest.php
new file mode 100644
index 000000000..1057dffbe
--- /dev/null
+++ b/core/modules/path/tests/src/Kernel/Plugin/migrate/source/d6/UrlAliasTest.php
@@ -0,0 +1,48 @@
+ 1,
+ 'src' => 'node/1',
+ 'dst' => 'test-article',
+ 'language' => 'en',
+ ],
+ [
+ 'pid' => 2,
+ 'src' => 'node/2',
+ 'dst' => 'another-alias',
+ 'language' => 'en',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['url_alias'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/path/tests/src/Kernel/Plugin/migrate/source/d7/UrlAliasTest.php b/core/modules/path/tests/src/Kernel/Plugin/migrate/source/d7/UrlAliasTest.php
new file mode 100644
index 000000000..2fdfd361d
--- /dev/null
+++ b/core/modules/path/tests/src/Kernel/Plugin/migrate/source/d7/UrlAliasTest.php
@@ -0,0 +1,48 @@
+ 1,
+ 'source' => 'node/1',
+ 'alias' => 'test-article',
+ 'language' => 'en',
+ ],
+ [
+ 'pid' => 2,
+ 'source' => 'node/2',
+ 'alias' => 'another-alias',
+ 'language' => 'en',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['url_alias'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/path/tests/src/Unit/Migrate/UrlAliasTestBase.php b/core/modules/path/tests/src/Unit/Migrate/UrlAliasTestBase.php
deleted file mode 100644
index c929192cb..000000000
--- a/core/modules/path/tests/src/Unit/Migrate/UrlAliasTestBase.php
+++ /dev/null
@@ -1,20 +0,0 @@
-databaseContents['url_alias'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/path/tests/src/Unit/Migrate/d6/UrlAliasTest.php b/core/modules/path/tests/src/Unit/Migrate/d6/UrlAliasTest.php
deleted file mode 100644
index fa00d60c9..000000000
--- a/core/modules/path/tests/src/Unit/Migrate/d6/UrlAliasTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd6_url_alias',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'pid' => 1,
- 'src' => 'node/1',
- 'dst' => 'test-article',
- 'language' => 'en',
- ),
- array(
- 'pid' => 2,
- 'src' => 'node/2',
- 'dst' => 'another-alias',
- 'language' => 'en',
- ),
- );
-
-}
diff --git a/core/modules/path/tests/src/Unit/Migrate/d7/UrlAliasTest.php b/core/modules/path/tests/src/Unit/Migrate/d7/UrlAliasTest.php
deleted file mode 100644
index d2fce564d..000000000
--- a/core/modules/path/tests/src/Unit/Migrate/d7/UrlAliasTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd7_url_alias',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'pid' => 1,
- 'source' => 'node/1',
- 'alias' => 'test-article',
- 'language' => 'en',
- ),
- array(
- 'pid' => 2,
- 'source' => 'node/2',
- 'alias' => 'another-alias',
- 'language' => 'en',
- ),
- );
-
-}
diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module
index 89ac37f49..15ec5375a 100644
--- a/core/modules/responsive_image/responsive_image.module
+++ b/core/modules/responsive_image/responsive_image.module
@@ -182,7 +182,7 @@ function template_preprocess_responsive_image(&$variables) {
if (isset($variables['sources']) && count($variables['sources']) === 1 && !isset($variables['sources'][0]['media'])) {
// There is only one source tag with an empty media attribute. This means
- // we can output an image tag with the srcset attribute in stead of a
+ // we can output an image tag with the srcset attribute instead of a
// picture tag.
$variables['output_image_tag'] = TRUE;
foreach ($variables['sources'][0] as $attribute => $value) {
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 7e4b90717..91ce482ec 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -268,7 +268,7 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
$entity->delete();
$this->logger->notice('Deleted entity %type with ID %id.', array('%type' => $entity->getEntityTypeId(), '%id' => $entity->id()));
- // Delete responses have an empty body.
+ // DELETE responses have an empty body.
return new ModifiedResourceResponse(NULL, 204);
}
catch (EntityStorageException $e) {
diff --git a/core/modules/rest/src/Tests/CreateTest.php b/core/modules/rest/src/Tests/CreateTest.php
index 8c7db07ba..2454d2a0c 100644
--- a/core/modules/rest/src/Tests/CreateTest.php
+++ b/core/modules/rest/src/Tests/CreateTest.php
@@ -366,10 +366,16 @@ class CreateTest extends RESTTestBase {
// Note: this will fail with PHP 5.6 when always_populate_raw_post_data is
// set to something other than -1. See https://www.drupal.org/node/2456025.
// Try first without the CSRF token, which should fail.
- $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType, TRUE);
- $this->assertResponse(403, 'X-CSRF-Token request header is missing');
+ $url = Url::fromUri('internal:/entity/' . $entity_type)->setOption('query', ['_format' => $this->defaultFormat]);
+ $this->httpRequest($url, 'POST', $serialized, $this->defaultMimeType, FALSE);
+ $this->assertResponse(403);
+ $this->assertRaw('X-CSRF-Token request header is missing');
+ // Then try with an invalid CSRF token.
+ $this->httpRequest($url, 'POST', $serialized, $this->defaultMimeType, 'invalid-csrf-token');
+ $this->assertResponse(403);
+ $this->assertRaw('X-CSRF-Token request header is invalid');
// Then try with the CSRF token.
- $response = $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType);
+ $response = $this->httpRequest($url, 'POST', $serialized, $this->defaultMimeType);
$this->assertResponse(201);
// Make sure that the response includes an entity in the body and check the
diff --git a/core/modules/rest/src/Tests/DeleteTest.php b/core/modules/rest/src/Tests/DeleteTest.php
index a1443422a..88db0fd9d 100644
--- a/core/modules/rest/src/Tests/DeleteTest.php
+++ b/core/modules/rest/src/Tests/DeleteTest.php
@@ -38,10 +38,17 @@ class DeleteTest extends RESTTestBase {
$entity = $this->entityCreate($entity_type);
$entity->save();
// Try first to delete over REST API without the CSRF token.
- $this->httpRequest($entity->urlInfo(), 'DELETE', NULL, NULL, TRUE);
- $this->assertResponse(403, 'X-CSRF-Token request header is missing');
+ $url = $entity->toUrl()->setRouteParameter('_format', $this->defaultFormat);
+ $this->httpRequest($url, 'DELETE', NULL, 'application/hal+json', FALSE);
+ $this->assertResponse(403);
+ $this->assertRaw('X-CSRF-Token request header is missing');
+ // Then try with an invalid CSRF token.
+ $this->httpRequest($url, 'DELETE', NULL, 'application/hal+json', 'invalid-csrf-token');
+ $this->assertResponse(403);
+ $this->assertRaw('X-CSRF-Token request header is invalid');
// Delete it over the REST API.
- $response = $this->httpRequest($entity->urlInfo(), 'DELETE');
+ $response = $this->httpRequest($url, 'DELETE');
+ $this->assertResponse(204);
// Clear the static cache with entity_load(), otherwise we won't see the
// update.
$storage = $this->container->get('entity_type.manager')
diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php
index 2a752274b..7cb0f1bde 100644
--- a/core/modules/rest/src/Tests/RESTTestBase.php
+++ b/core/modules/rest/src/Tests/RESTTestBase.php
@@ -85,19 +85,22 @@ abstract class RESTTestBase extends WebTestBase {
* The body for POST and PUT.
* @param string $mime_type
* The MIME type of the transmitted content.
- * @param bool $forget_xcsrf_token
- * If TRUE, the CSRF token won't be included in request.
+ * @param bool $csrf_token
+ * If NULL, a CSRF token will be retrieved and used. If FALSE, omit the
+ * X-CSRF-Token request header (to simulate developer error). Otherwise, the
+ * passed in value will be used as the value for the X-CSRF-Token request
+ * header (to simulate developer error, by sending an invalid CSRF token).
*
* @return string
* The content returned from the request.
*/
- protected function httpRequest($url, $method, $body = NULL, $mime_type = NULL, $forget_xcsrf_token = FALSE) {
+ protected function httpRequest($url, $method, $body = NULL, $mime_type = NULL, $csrf_token = NULL) {
if (!isset($mime_type)) {
$mime_type = $this->defaultMimeType;
}
if (!in_array($method, array('GET', 'HEAD', 'OPTIONS', 'TRACE'))) {
// GET the CSRF token first for writing requests.
- $token = $this->drupalGet('session/token');
+ $requested_token = $this->drupalGet('session/token');
}
$url = $this->buildUrl($url);
@@ -132,9 +135,9 @@ abstract class RESTTestBase extends WebTestBase {
CURLOPT_POSTFIELDS => $body,
CURLOPT_URL => $url,
CURLOPT_NOBODY => FALSE,
- CURLOPT_HTTPHEADER => !$forget_xcsrf_token ? array(
+ CURLOPT_HTTPHEADER => $csrf_token !== FALSE ? array(
'Content-Type: ' . $mime_type,
- 'X-CSRF-Token: ' . $token,
+ 'X-CSRF-Token: ' . ($csrf_token === NULL ? $requested_token : $csrf_token),
) : array(
'Content-Type: ' . $mime_type,
),
@@ -148,9 +151,9 @@ abstract class RESTTestBase extends WebTestBase {
CURLOPT_POSTFIELDS => $body,
CURLOPT_URL => $url,
CURLOPT_NOBODY => FALSE,
- CURLOPT_HTTPHEADER => !$forget_xcsrf_token ? array(
+ CURLOPT_HTTPHEADER => $csrf_token !== FALSE ? array(
'Content-Type: ' . $mime_type,
- 'X-CSRF-Token: ' . $token,
+ 'X-CSRF-Token: ' . ($csrf_token === NULL ? $requested_token : $csrf_token),
) : array(
'Content-Type: ' . $mime_type,
),
@@ -164,9 +167,9 @@ abstract class RESTTestBase extends WebTestBase {
CURLOPT_POSTFIELDS => $body,
CURLOPT_URL => $url,
CURLOPT_NOBODY => FALSE,
- CURLOPT_HTTPHEADER => !$forget_xcsrf_token ? array(
+ CURLOPT_HTTPHEADER => $csrf_token !== FALSE ? array(
'Content-Type: ' . $mime_type,
- 'X-CSRF-Token: ' . $token,
+ 'X-CSRF-Token: ' . ($csrf_token === NULL ? $requested_token : $csrf_token),
) : array(
'Content-Type: ' . $mime_type,
),
@@ -179,7 +182,9 @@ abstract class RESTTestBase extends WebTestBase {
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_URL => $url,
CURLOPT_NOBODY => FALSE,
- CURLOPT_HTTPHEADER => !$forget_xcsrf_token ? array('X-CSRF-Token: ' . $token) : array(),
+ CURLOPT_HTTPHEADER => $csrf_token !== FALSE ? array(
+ 'X-CSRF-Token: ' . ($csrf_token === NULL ? $requested_token : $csrf_token),
+ ) : array(),
);
break;
}
@@ -197,6 +202,8 @@ abstract class RESTTestBase extends WebTestBase {
$this->verbose($method . ' request to: ' . $url .
'Code: ' . curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE) .
+ (isset($curl_options[CURLOPT_HTTPHEADER]) ? 'Request headers: ' . nl2br(print_r($curl_options[CURLOPT_HTTPHEADER], TRUE)) : '' ) .
+ (isset($curl_options[CURLOPT_POSTFIELDS]) ? 'Request body: ' . nl2br(print_r($curl_options[CURLOPT_POSTFIELDS], TRUE)) : '' ) .
'Response headers: ' . nl2br(print_r($headers, TRUE)) .
'Response body: ' . $this->responseBody);
diff --git a/core/modules/rest/src/Tests/UpdateTest.php b/core/modules/rest/src/Tests/UpdateTest.php
index cdef82c34..d3784ed66 100644
--- a/core/modules/rest/src/Tests/UpdateTest.php
+++ b/core/modules/rest/src/Tests/UpdateTest.php
@@ -381,9 +381,13 @@ class UpdateTest extends RESTTestBase {
$serialized = $serializer->serialize($normalized, $format, $context);
// Try first without CSRF token which should fail.
- $this->httpRequest($url, 'PATCH', $serialized, $mime_type, TRUE);
- $this->assertResponse(403, 'X-CSRF-Token request header is missing');
-
+ $this->httpRequest($url, 'PATCH', $serialized, $mime_type, FALSE);
+ $this->assertResponse(403);
+ $this->assertRaw('X-CSRF-Token request header is missing');
+ // Then try with an invalid CSRF token.
+ $this->httpRequest($url, 'PATCH', $serialized, $mime_type, 'invalid-csrf-token');
+ $this->assertResponse(403);
+ $this->assertRaw('X-CSRF-Token request header is invalid');
// Then try with CSRF token.
$this->httpRequest($url, 'PATCH', $serialized, $mime_type);
$this->assertResponse(200);
diff --git a/core/modules/search/migration_templates/d6_search_settings.yml b/core/modules/search/migration_templates/d6_search_settings.yml
index 8efb9fdb1..28f860060 100644
--- a/core/modules/search/migration_templates/d6_search_settings.yml
+++ b/core/modules/search/migration_templates/d6_search_settings.yml
@@ -10,8 +10,6 @@ source:
- minimum_word_size
- overlap_cjk
- search_cron_limit
- - search_tag_weights
- - search_and_or_limit
process:
'index/minimum_word_size': minimum_word_size
'index/overlap_cjk': overlap_cjk
diff --git a/core/modules/serialization/src/Normalizer/ListNormalizer.php b/core/modules/serialization/src/Normalizer/ListNormalizer.php
index 94aca1134..1729cfce8 100644
--- a/core/modules/serialization/src/Normalizer/ListNormalizer.php
+++ b/core/modules/serialization/src/Normalizer/ListNormalizer.php
@@ -26,7 +26,7 @@ class ListNormalizer extends NormalizerBase {
public function normalize($object, $format = NULL, array $context = array()) {
$attributes = array();
foreach ($object as $fieldItem) {
- $attributes[] = $this->serializer->normalize($fieldItem, $format);
+ $attributes[] = $this->serializer->normalize($fieldItem, $format, $context);
}
return $attributes;
}
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/ListNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/ListNormalizerTest.php
index 170fd3ecd..bbc7c1103 100644
--- a/core/modules/serialization/tests/src/Unit/Normalizer/ListNormalizerTest.php
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/ListNormalizerTest.php
@@ -7,6 +7,7 @@ use Drupal\Core\TypedData\TypedDataManagerInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\serialization\Normalizer\ListNormalizer;
use Drupal\Core\TypedData\Plugin\DataType\ItemList;
+use Symfony\Component\Serializer\Serializer;
/**
* @coversDefaultClass \Drupal\serialization\Normalizer\ListNormalizer
@@ -35,13 +36,20 @@ class ListNormalizerTest extends UnitTestCase {
*/
protected $expectedListValues = array('test', 'test', 'test');
+ /**
+ * The mocked typed data.
+ *
+ * @var \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\TypedData\TypedDataInterface
+ */
+ protected $typedData;
+
protected function setUp() {
// Mock the TypedDataManager to return a TypedDataInterface mock.
- $typed_data = $this->getMock('Drupal\Core\TypedData\TypedDataInterface');
+ $this->typedData = $this->getMock('Drupal\Core\TypedData\TypedDataInterface');
$typed_data_manager = $this->getMock(TypedDataManagerInterface::class);
$typed_data_manager->expects($this->any())
->method('getPropertyInstance')
- ->will($this->returnValue($typed_data));
+ ->will($this->returnValue($this->typedData));
// Set up a mock container as ItemList() will call for the 'typed_data_manager'
// service.
@@ -73,16 +81,14 @@ class ListNormalizerTest extends UnitTestCase {
* Tests the normalize() method.
*/
public function testNormalize() {
- $serializer = $this->getMockBuilder('Symfony\Component\Serializer\Serializer')
- ->setMethods(array('normalize'))
- ->getMock();
- $serializer->expects($this->exactly(3))
- ->method('normalize')
- ->will($this->returnValue('test'));
+ $serializer = $this->prophesize(Serializer::class);
+ $serializer->normalize($this->typedData, 'json', ['mu' => 'nu'])
+ ->shouldBeCalledTimes(3)
+ ->willReturn('test');
- $this->normalizer->setSerializer($serializer);
+ $this->normalizer->setSerializer($serializer->reveal());
- $normalized = $this->normalizer->normalize($this->list);
+ $normalized = $this->normalizer->normalize($this->list, 'json', ['mu' => 'nu']);
$this->assertEquals($this->expectedListValues, $normalized);
}
diff --git a/core/modules/shortcut/migration_templates/d7_shortcut.yml b/core/modules/shortcut/migration_templates/d7_shortcut.yml
index ba986eb85..da3d89494 100644
--- a/core/modules/shortcut/migration_templates/d7_shortcut.yml
+++ b/core/modules/shortcut/migration_templates/d7_shortcut.yml
@@ -23,4 +23,4 @@ destination:
migration_dependencies:
required:
- d7_shortcut_set
- - menu_links
+ - d7_menu_links
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 066afcc95..b7294ad65 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -311,7 +311,8 @@ function shortcut_preprocess_page_title(&$variables) {
// Replicate template_preprocess_html()'s processing to get the title in
// string form, so we can set the default name for the shortcut.
- $name = render($variables['title']);
+ // Strip HTML tags from the title.
+ $name = trim(strip_tags(render($variables['title'])));
$query = array(
'link' => $link,
'name' => $name,
diff --git a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
index c6efe4134..679f89bbe 100644
--- a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
@@ -2,7 +2,9 @@
namespace Drupal\shortcut\Tests;
+use Drupal\block_content\Entity\BlockContentType;
use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Url;
use Drupal\shortcut\Entity\Shortcut;
use Drupal\shortcut\Entity\ShortcutSet;
@@ -161,18 +163,12 @@ class ShortcutLinksTest extends ShortcutTestBase {
// Test the "Add to shortcuts" link for a page generated by views.
$this->clickLink('Add to Default shortcuts');
$this->assertText('Added a shortcut for People.');
- // Due to the structure of the markup in the link ::assertLink() doesn't
- // works here.
- $link = $this->xpath('//a[normalize-space()=:label]', array(':label' => 'Remove from Default shortcuts'));
- $this->assertTrue(!empty($link), 'Link Remove from Default shortcuts found.');
+ $this->assertShortcutQuickLink('Remove from Default shortcuts');
// Test the "Remove from shortcuts" link for a page generated by views.
$this->clickLink('Remove from Default shortcuts');
$this->assertText('The shortcut People has been deleted.');
- // Due to the structure of the markup in the link ::assertLink() doesn't
- // works here.
- $link = $this->xpath('//a[normalize-space()=:label]', array(':label' => 'Add to Default shortcuts'));
- $this->assertTrue(!empty($link), 'Link Add to Default shortcuts found.');
+ $this->assertShortcutQuickLink('Add to Default shortcuts');
// Test two pages which use same route name but different route parameters.
$this->drupalGet('node/add/page');
@@ -186,6 +182,37 @@ class ShortcutLinksTest extends ShortcutTestBase {
// Add Shortcut for Article.
$this->clickLink('Add to Default shortcuts');
$this->assertText('Added a shortcut for Create Article.');
+
+ $this->config('system.theme')->set('default', 'seven')->save();
+ $this->drupalGet('node/' . $this->node->id());
+ $title = $this->node->getTitle();
+
+ // Test the "Add to shortcuts" link for node view route.
+ $this->clickLink('Add to Default shortcuts');
+ $this->assertText(new FormattableMarkup('Added a shortcut for @title.', ['@title' => $title]));
+ $this->assertShortcutQuickLink('Remove from Default shortcuts');
+
+ // Test the "Remove from shortcuts" link for node view route.
+ $this->clickLink('Remove from Default shortcuts');
+ $this->assertText(new FormattableMarkup('The shortcut @title has been deleted.', ['@title' => $title]));
+ $this->assertShortcutQuickLink('Add to Default shortcuts');
+
+ \Drupal::service('module_installer')->install(['block_content']);
+ BlockContentType::create(array(
+ 'id' => 'basic',
+ 'label' => 'Basic block',
+ 'revision' => FALSE,
+ ))->save();
+ // Test page with HTML tags in title.
+ $this->drupalGet('admin/structure/block/block-content/manage/basic');
+ $page_title = new FormattableMarkup('Edit %label custom block type', ['%label' => 'Basic block']);
+ $this->assertRaw($page_title);
+ // Add shortcut to this page.
+ $this->clickLink('Add to Default shortcuts');
+ $this->assertRaw(new FormattableMarkup('Added a shortcut for %title.', [
+ '%title' => trim(strip_tags($page_title)),
+ ]));
+
}
/**
@@ -404,4 +431,32 @@ class ShortcutLinksTest extends ShortcutTestBase {
$this->assertNoBlockAppears($block);
}
+ /**
+ * Passes if a shortcut quick link with the specified label is found.
+ *
+ * An optional link index may be passed.
+ *
+ * @param string $label
+ * Text between the anchor tags.
+ * @param int $index
+ * Link position counting from zero.
+ * @param string $message
+ * (optional) A message to display with the assertion. Do not translate
+ * messages: use format_string() to embed variables in the message text, not
+ * t(). If left blank, a default message will be displayed.
+ * @param string $group
+ * (optional) The group this message is in, which is displayed in a column
+ * in test output. Use 'Debug' to indicate this is debugging output. Do not
+ * translate this string. Defaults to 'Other'; most tests do not override
+ * this default.
+ *
+ * @return bool
+ * TRUE if the assertion succeeded, FALSE otherwise.
+ */
+ protected function assertShortcutQuickLink($label, $index = 0, $message = '', $group = 'Other') {
+ $links = $this->xpath('//a[normalize-space()=:label]', array(':label' => $label));
+ $message = ($message ? $message : SafeMarkup::format('Shortcut quick link with label %label found.', array('%label' => $label)));
+ return $this->assert(isset($links[$index]), $message, $group);
+ }
+
}
diff --git a/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetTest.php b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetTest.php
index 316c53f24..865828641 100644
--- a/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetTest.php
+++ b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetTest.php
@@ -34,8 +34,8 @@ class MigrateShortcutSetTest extends MigrateDrupal7TestBase {
$this->installEntitySchema('menu_link_content');
\Drupal::service('router.builder')->rebuild();
$this->executeMigration('d7_shortcut_set');
- $this->executeMigration('menu');
- $this->executeMigration('menu_links');
+ $this->executeMigration('d7_menu');
+ $this->executeMigration('d7_menu_links');
$this->executeMigration('d7_shortcut');
}
diff --git a/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetUsersTest.php b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetUsersTest.php
index 6a9ebe346..1b15dcdef 100644
--- a/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetUsersTest.php
+++ b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetUsersTest.php
@@ -36,8 +36,8 @@ class MigrateShortcutSetUsersTest extends MigrateDrupal7TestBase {
$this->executeMigration('d7_user_role');
$this->executeMigration('d7_user');
$this->executeMigration('d7_shortcut_set');
- $this->executeMigration('menu');
- $this->executeMigration('menu_links');
+ $this->executeMigration('d7_menu');
+ $this->executeMigration('d7_menu_links');
$this->executeMigration('d7_shortcut');
$this->executeMigration('d7_shortcut_set_users');
}
diff --git a/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutTest.php b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutTest.php
index 791deb219..18f55170a 100644
--- a/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutTest.php
+++ b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutTest.php
@@ -34,8 +34,8 @@ class MigrateShortcutTest extends MigrateDrupal7TestBase {
$this->installEntitySchema('menu_link_content');
\Drupal::service('router.builder')->rebuild();
$this->executeMigration('d7_shortcut_set');
- $this->executeMigration('menu');
- $this->executeMigration('menu_links');
+ $this->executeMigration('d7_menu');
+ $this->executeMigration('d7_menu_links');
$this->executeMigration('d7_shortcut');
}
diff --git a/core/modules/shortcut/tests/src/Kernel/Plugin/migrate/source/d7/ShortcutSetTest.php b/core/modules/shortcut/tests/src/Kernel/Plugin/migrate/source/d7/ShortcutSetTest.php
new file mode 100644
index 000000000..e1982de64
--- /dev/null
+++ b/core/modules/shortcut/tests/src/Kernel/Plugin/migrate/source/d7/ShortcutSetTest.php
@@ -0,0 +1,41 @@
+ 'shortcut-set-2',
+ 'title' => 'Alternative shortcut set',
+ ],
+ ];
+
+ // The expected results are identical to the source data.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['shortcut_set'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/shortcut/tests/src/Kernel/Plugin/migrate/source/d7/ShortcutSetUsersTest.php b/core/modules/shortcut/tests/src/Kernel/Plugin/migrate/source/d7/ShortcutSetUsersTest.php
new file mode 100644
index 000000000..035aec31c
--- /dev/null
+++ b/core/modules/shortcut/tests/src/Kernel/Plugin/migrate/source/d7/ShortcutSetUsersTest.php
@@ -0,0 +1,41 @@
+ '2',
+ 'set_name' => 'shortcut-set-2',
+ ],
+ ];
+
+ // The expected results are identical to the source data.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['shortcut_set_users'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/shortcut/tests/src/Kernel/Plugin/migrate/source/d7/ShortcutTest.php b/core/modules/shortcut/tests/src/Kernel/Plugin/migrate/source/d7/ShortcutTest.php
new file mode 100644
index 000000000..9f8fd3ae6
--- /dev/null
+++ b/core/modules/shortcut/tests/src/Kernel/Plugin/migrate/source/d7/ShortcutTest.php
@@ -0,0 +1,72 @@
+ 'shortcut-set-2',
+ 'mlid' => '473',
+ 'plid' => '0',
+ 'link_path' => 'admin/people',
+ 'router_path' => 'admin/people',
+ 'link_title' => 'People',
+ 'options' => 'a:0:{}',
+ 'module' => 'menu',
+ 'hidden' => '0',
+ 'external' => '0',
+ 'has_children' => '0',
+ 'expanded' => '0',
+ 'weight' => '-50',
+ 'depth' => '1',
+ 'customized' => '0',
+ 'p1' => '473',
+ 'p2' => '0',
+ 'p3' => '0',
+ 'p4' => '0',
+ 'p5' => '0',
+ 'p6' => '0',
+ 'p7' => '0',
+ 'p8' => '0',
+ 'p9' => '0',
+ 'updated' => '0',
+ ]
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'mlid' => '473',
+ 'menu_name' => 'shortcut-set-2',
+ 'link_path' => 'admin/people',
+ 'link_title' => 'People',
+ 'weight' => '-50',
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/shortcut/tests/src/Unit/Plugin/migrate/source/d7/ShortcutSetTest.php b/core/modules/shortcut/tests/src/Unit/Plugin/migrate/source/d7/ShortcutSetTest.php
deleted file mode 100644
index f2f221f09..000000000
--- a/core/modules/shortcut/tests/src/Unit/Plugin/migrate/source/d7/ShortcutSetTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
- 'test',
- 'source' => [
- 'plugin' => 'd7_shortcut_set',
- ],
- ];
-
- protected $expectedResults = [
- [
- 'set_name' => 'shortcut-set-2',
- 'title' => 'Alternative shortcut set',
- ],
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['shortcut_set'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/shortcut/tests/src/Unit/Plugin/migrate/source/d7/ShortcutSetUsersTest.php b/core/modules/shortcut/tests/src/Unit/Plugin/migrate/source/d7/ShortcutSetUsersTest.php
deleted file mode 100644
index 5681f6dfc..000000000
--- a/core/modules/shortcut/tests/src/Unit/Plugin/migrate/source/d7/ShortcutSetUsersTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
- 'test',
- 'source' => [
- 'plugin' => 'd7_shortcut_set_users',
- ],
- ];
-
- protected $expectedResults = [
- [
- 'uid' => '2',
- 'set_name' => 'shortcut-set-2',
- ],
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['shortcut_set_users'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/shortcut/tests/src/Unit/Plugin/migrate/source/d7/ShortcutTest.php b/core/modules/shortcut/tests/src/Unit/Plugin/migrate/source/d7/ShortcutTest.php
deleted file mode 100644
index 9fde228bc..000000000
--- a/core/modules/shortcut/tests/src/Unit/Plugin/migrate/source/d7/ShortcutTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
- 'test',
- 'source' => [
- 'plugin' => 'd7_shortcut',
- ],
- ];
-
- protected $expectedResults = [
- [
- 'mlid' => '473',
- 'menu_name' => 'shortcut-set-2',
- 'link_path' => 'admin/people',
- 'link_title' => 'People',
- 'weight' => '-50',
- ],
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['menu_links'][] = [
- 'menu_name' => 'shortcut-set-2',
- 'mlid' => '473',
- 'plid' => '0',
- 'link_path' => 'admin/people',
- 'router_path' => 'admin/people',
- 'link_title' => 'People',
- 'options' => 'a:0:{}',
- 'module' => 'menu',
- 'hidden' => '0',
- 'external' => '0',
- 'has_children' => '0',
- 'expanded' => '0',
- 'weight' => '-50',
- 'depth' => '1',
- 'customized' => '0',
- 'p1' => '473',
- 'p2' => '0',
- 'p3' => '0',
- 'p4' => '0',
- 'p5' => '0',
- 'p6' => '0',
- 'p7' => '0',
- 'p8' => '0',
- 'p9' => '0',
- 'updated' => '0',
- ];
-
- parent::setUp();
- }
-
-}
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 0752e4251..f72ca5c6a 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -13,6 +13,7 @@ use Drupal\simpletest\TestBase;
use Drupal\Core\Test\TestDatabase;
use Drupal\simpletest\TestDiscovery;
use Symfony\Component\Process\PhpExecutableFinder;
+use Drupal\Core\Test\TestStatus;
/**
* Implements hook_help().
@@ -183,16 +184,15 @@ function simpletest_run_phpunit_tests($test_id, array $unescaped_test_classnames
$phpunit_file = simpletest_phpunit_xml_filepath($test_id);
simpletest_phpunit_run_command($unescaped_test_classnames, $phpunit_file, $status, $output);
- $rows = simpletest_phpunit_xml_to_rows($test_id, $phpunit_file);
- // A $status of 0 = passed test, 1 = failed test, > 1 indicates segfault
- // timeout, or other type of failure.
- if ($status > 1) {
- // Something broke during the execution of phpunit.
- // Return an error record of all failed classes.
+ $rows = [];
+ if ($status == TestStatus::PASS) {
+ $rows = simpletest_phpunit_xml_to_rows($test_id, $phpunit_file);
+ }
+ else {
$rows[] = [
'test_id' => $test_id,
'test_class' => implode(",", $unescaped_test_classnames),
- 'status' => 'fail',
+ 'status' => TestStatus::label($status),
'message' => 'PHPunit Test failed to complete; Error: ' . implode("\n", $output),
'message_group' => 'Other',
'function' => implode(",", $unescaped_test_classnames),
@@ -200,20 +200,6 @@ function simpletest_run_phpunit_tests($test_id, array $unescaped_test_classnames
'file' => $phpunit_file,
];
}
-
- if ($status === 1) {
- $rows[] = [
- 'test_id' => $test_id,
- 'test_class' => implode(",", $unescaped_test_classnames),
- 'status' => 'fail',
- 'message' => 'PHPunit Test failed to complete; Error: ' . implode("\n", $output),
- 'message_group' => 'Other',
- 'function' => implode(",", $unescaped_test_classnames),
- 'line' => '0',
- 'file' => $phpunit_file,
- ];
- }
-
return $rows;
}
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index fe25f9510..f4a23e7c1 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -1532,7 +1532,11 @@ abstract class TestBase {
* need to get deleted too.
*/
public static function filePreDeleteCallback($path) {
- chmod($path, 0700);
+ // When the webserver runs with the same system user as the test runner, we
+ // can make read-only files writable again. If not, chmod will fail while
+ // the file deletion still works if file permissions have been configured
+ // correctly. Thus, we ignore any problems while running chmod.
+ @chmod($path, 0700);
}
/**
diff --git a/core/modules/simpletest/src/Tests/SimpleTestBrowserTest.php b/core/modules/simpletest/src/Tests/SimpleTestBrowserTest.php
index c4528a53a..3b2061ec4 100644
--- a/core/modules/simpletest/src/Tests/SimpleTestBrowserTest.php
+++ b/core/modules/simpletest/src/Tests/SimpleTestBrowserTest.php
@@ -4,6 +4,7 @@ namespace Drupal\simpletest\Tests;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\simpletest\Functional\ThroughUITest;
/**
* Tests the Simpletest UI internal browser.
@@ -131,7 +132,7 @@ class SimpleTestBrowserTest extends WebTestBase {
// A PHPUnit unit test.
'Drupal\Tests\action\Unit\Menu\ActionLocalTasksTest',
// A PHPUnit functional test.
- 'Drupal\FunctionalTests\BrowserTestBaseTest',
+ ThroughUITest::class,
);
foreach ($tests as $test) {
diff --git a/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php b/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php
index 6fb61d523..a4ffc7ae4 100644
--- a/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php
+++ b/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php
@@ -2,22 +2,27 @@
namespace Drupal\Tests\simpletest\Unit;
-use Drupal\Tests\UnitTestCase;
-
/**
* This test crashes PHP.
*
* To avoid accidentally running, it is not in a normal PSR-4 directory, the
* file name does not adhere to PSR-4 and an environment variable also needs to
* be set for the crash to happen.
+ *
+ * @see \Drupal\Tests\simpletest\Unit\SimpletestPhpunitRunCommandTest::testSimpletestPhpUnitRunCommand()
*/
-class SimpletestPhpunitRunCommandTestWillDie extends UnitTestCase {
+class SimpletestPhpunitRunCommandTestWillDie extends \PHPUnit_Framework_TestCase {
+ /**
+ * Performs the status specified by SimpletestPhpunitRunCommandTestWillDie.
+ */
public function testWillDie() {
- if (getenv('SimpletestPhpunitRunCommandTestWillDie') === 'fail') {
- exit(2);
+ $status = (int) getenv('SimpletestPhpunitRunCommandTestWillDie');
+ if ($status == 0) {
+ $this->assertTrue(TRUE, 'Assertion to ensure test pass');
+ return;
}
- $this->assertTrue(TRUE, 'Assertion to ensure test pass');
+ exit($status);
}
}
diff --git a/core/modules/simpletest/tests/src/Functional/ThroughUITest.php b/core/modules/simpletest/tests/src/Functional/ThroughUITest.php
new file mode 100644
index 000000000..48737433f
--- /dev/null
+++ b/core/modules/simpletest/tests/src/Functional/ThroughUITest.php
@@ -0,0 +1,23 @@
+pass('Success!');
+ }
+
+}
diff --git a/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php b/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php
index 2afea1c7f..a96db963c 100644
--- a/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php
+++ b/core/modules/simpletest/tests/src/Unit/SimpletestPhpunitRunCommandTest.php
@@ -3,7 +3,7 @@
namespace Drupal\Tests\simpletest\Unit;
use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Tests\UnitTestCase;
+use Drupal\Core\File\FileSystemInterface;
/**
* Tests simpletest_run_phpunit_tests() handles PHPunit fatals correctly.
@@ -11,27 +11,92 @@ use Drupal\Tests\UnitTestCase;
* @group simpletest
*
* @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
*/
-class SimpletestPhpunitRunCommandTest extends UnitTestCase {
+class SimpletestPhpunitRunCommandTest extends \PHPUnit_Framework_TestCase {
- function testSimpletestPhpUnitRunCommand() {
- include_once __DIR__ . '/../../fixtures/simpletest_phpunit_run_command_test.php';
- $app_root = __DIR__ . '/../../../../../..';
- include_once "$app_root/core/modules/simpletest/simpletest.module";
+ /**
+ * Path to the app root.
+ *
+ * @var string
+ */
+ protected static $root;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function setUpBeforeClass() {
+ parent::setUpBeforeClass();
+ // Figure out our app root.
+ self::$root = dirname(dirname(dirname(dirname(dirname(dirname(__DIR__))))));
+ // Include the files we need for tests. The stub test we will run is
+ // SimpletestPhpunitRunCommandTestWillDie which is located in
+ // simpletest_phpunit_run_command_test.php.
+ include_once self::$root . '/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php';
+ // Since we're testing simpletest_run_phpunit_tests(), we need to include
+ // simpletest.module.
+ include_once self::$root . '/core/modules/simpletest/simpletest.module';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ // Organize our mock container.
$container = new ContainerBuilder();
- $container->set('app.root', $app_root);
- $file_system = $this->prophesize('Drupal\Core\File\FileSystemInterface');
+ $container->set('app.root', self::$root);
+ $file_system = $this->prophesize(FileSystemInterface::class);
+ // The simpletest directory wrapper will always point to /tmp.
$file_system->realpath('public://simpletest')->willReturn(sys_get_temp_dir());
$container->set('file_system', $file_system->reveal());
\Drupal::setContainer($container);
- $test_id = basename(tempnam(sys_get_temp_dir(), 'xxx'));
- foreach (['pass', 'fail'] as $status) {
- putenv('SimpletestPhpunitRunCommandTestWillDie=' . $status);
- $ret = simpletest_run_phpunit_tests($test_id, ['Drupal\Tests\simpletest\Unit\SimpletestPhpunitRunCommandTestWillDie']);
- $this->assertSame($ret[0]['status'], $status);
+ }
+
+ /**
+ * Data provider for testSimpletestPhpUnitRunCommand().
+ *
+ * @return array
+ * Arrays of status codes and the label they're expected to have.
+ */
+ public function provideStatusCodes() {
+ $data = [
+ [0, 'pass'],
+ [1, 'fail'],
+ [2, 'exception'],
+ ];
+ // All status codes 3 and above should be labeled 'error'.
+ // @todo: The valid values here would be 3 to 127. But since the test
+ // touches the file system a lot, we only have 3, 4, and 127 for speed.
+ foreach ([3, 4, 127] as $status) {
+ $data[] = [$status, 'error'];
}
+ return $data;
+ }
+
+ /**
+ * Test the round trip for PHPUnit execution status codes.
+ *
+ * @covers ::simpletest_run_phpunit_tests
+ *
+ * @dataProvider provideStatusCodes
+ */
+ public function testSimpletestPhpUnitRunCommand($status, $label) {
+ $test_id = basename(tempnam(sys_get_temp_dir(), 'xxx'));
+ putenv('SimpletestPhpunitRunCommandTestWillDie=' . $status);
+ $ret = simpletest_run_phpunit_tests($test_id, [SimpletestPhpunitRunCommandTestWillDie::class]);
+ $this->assertSame($ret[0]['status'], $label);
+ putenv('SimpletestPhpunitRunCommandTestWillDie');
unlink(simpletest_phpunit_xml_filepath($test_id));
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function tearDown() {
+ // We unset the $base_url global, since the test code sets it as a
+ // side-effect.
+ unset($GLOBALS['base_url']);
+ parent::tearDown();
+ }
+
}
diff --git a/core/modules/statistics/migration_templates/d6_statistics_settings.yml b/core/modules/statistics/migration_templates/statistics_settings.yml
similarity index 92%
rename from core/modules/statistics/migration_templates/d6_statistics_settings.yml
rename to core/modules/statistics/migration_templates/statistics_settings.yml
index 348ad38e4..62c2c0612 100644
--- a/core/modules/statistics/migration_templates/d6_statistics_settings.yml
+++ b/core/modules/statistics/migration_templates/statistics_settings.yml
@@ -1,7 +1,8 @@
-id: d6_statistics_settings
+id: statistics_settings
label: Statistics configuration
migration_tags:
- Drupal 6
+ - Drupal 7
source:
plugin: variable
variables:
diff --git a/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php b/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php
index aa9760a52..401a5e82b 100644
--- a/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php
+++ b/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php
@@ -24,7 +24,7 @@ class MigrateStatisticsConfigsTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
- $this->executeMigration('d6_statistics_settings');
+ $this->executeMigration('statistics_settings');
}
/**
diff --git a/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateStatisticsConfigsTest.php b/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateStatisticsConfigsTest.php
new file mode 100644
index 000000000..e5d3310ad
--- /dev/null
+++ b/core/modules/statistics/tests/src/Kernel/Migrate/d7/MigrateStatisticsConfigsTest.php
@@ -0,0 +1,41 @@
+executeMigration('statistics_settings');
+ }
+
+ /**
+ * Tests migration of statistics variables to statistics.settings.yml.
+ */
+ public function testStatisticsSettings() {
+ $config = $this->config('statistics.settings');
+ $this->assertIdentical(TRUE, $config->get('access_log.enabled'));
+ $this->assertIdentical(3600, $config->get('access_log.max_lifetime'));
+ $this->assertIdentical(1, $config->get('count_content_views'));
+ $this->assertConfigSchema(\Drupal::service('config.typed'), 'statistics.settings', $config->get());
+ }
+
+}
diff --git a/core/modules/system/migration_templates/menu.yml b/core/modules/system/migration_templates/d6_menu.yml
similarity index 91%
rename from core/modules/system/migration_templates/menu.yml
rename to core/modules/system/migration_templates/d6_menu.yml
index 974a2d68a..25ad0af9b 100644
--- a/core/modules/system/migration_templates/menu.yml
+++ b/core/modules/system/migration_templates/d6_menu.yml
@@ -1,9 +1,8 @@
# The menu_settings migration is in the menu_ui module.
-id: menu
+id: d6_menu
label: Menus
migration_tags:
- Drupal 6
- - Drupal 7
source:
plugin: menu
process:
diff --git a/core/modules/system/migration_templates/d7_menu.yml b/core/modules/system/migration_templates/d7_menu.yml
new file mode 100644
index 000000000..da47b3656
--- /dev/null
+++ b/core/modules/system/migration_templates/d7_menu.yml
@@ -0,0 +1,20 @@
+id: d7_menu
+label: Menus
+migration_tags:
+ - Drupal 7
+source:
+ plugin: menu
+process:
+ id:
+ plugin: static_map
+ bypass: true
+ source: menu_name
+ map:
+ main-menu: main
+ management: admin
+ navigation: tools
+ user-menu: account
+ label: title
+ description: description
+destination:
+ plugin: entity:menu
diff --git a/core/modules/system/src/Tests/Ajax/DialogTest.php b/core/modules/system/src/Tests/Ajax/DialogTest.php
index 80b465067..254f01d33 100644
--- a/core/modules/system/src/Tests/Ajax/DialogTest.php
+++ b/core/modules/system/src/Tests/Ajax/DialogTest.php
@@ -39,7 +39,7 @@ class DialogTest extends AjaxTestBase {
'data' => $dialog_contents,
'dialogOptions' => array(
'modal' => TRUE,
- 'title' => 'AJAX Dialog contents',
+ 'title' => 'AJAX Dialog & contents',
),
);
$form_expected_response = array(
@@ -67,7 +67,7 @@ class DialogTest extends AjaxTestBase {
'data' => $dialog_contents,
'dialogOptions' => array(
'modal' => FALSE,
- 'title' => 'AJAX Dialog contents',
+ 'title' => 'AJAX Dialog & contents',
),
);
$no_target_expected_response = array(
@@ -77,7 +77,7 @@ class DialogTest extends AjaxTestBase {
'data' => $dialog_contents,
'dialogOptions' => array(
'modal' => FALSE,
- 'title' => 'AJAX Dialog contents',
+ 'title' => 'AJAX Dialog & contents',
),
);
$close_expected_response = array(
@@ -97,6 +97,9 @@ class DialogTest extends AjaxTestBase {
// Emulate going to the JS version of the page and check the JSON response.
$ajax_result = $this->drupalGetAjax('ajax-test/dialog-contents', array('query' => array(MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_modal')));
$this->assertEqual($modal_expected_response, $ajax_result[3], 'Modal dialog JSON response matches.');
+ // Test the HTML escaping of & character.
+ $this->assertEqual($ajax_result[3]['dialogOptions']['title'], 'AJAX Dialog & contents');
+ $this->assertNotEqual($ajax_result[3]['dialogOptions']['title'], 'AJAX Dialog & contents');
// Check that requesting a "normal" dialog without JS goes to a page.
$this->drupalGet('ajax-test/dialog-contents');
@@ -152,6 +155,8 @@ class DialogTest extends AjaxTestBase {
// Check that the response matches the expected value.
$this->assertEqual($modal_expected_response, $ajax_result[4], 'POST request modal dialog JSON response matches.');
+ // Test the HTML escaping of & character.
+ $this->assertNotEqual($ajax_result[4]['dialogOptions']['title'], 'AJAX Dialog & contents');
// Abbreviated test for "normal" dialogs, testing only the difference.
$ajax_result = $this->drupalPostAjaxForm('ajax-test/dialog', array(), 'button2');
diff --git a/core/modules/system/src/Tests/Common/FormatDateTest.php b/core/modules/system/src/Tests/Common/FormatDateTest.php
index 36589ef46..cddf0a3fa 100644
--- a/core/modules/system/src/Tests/Common/FormatDateTest.php
+++ b/core/modules/system/src/Tests/Common/FormatDateTest.php
@@ -77,46 +77,4 @@ class FormatDateTest extends WebTestBase {
$this->assertIdentical(format_date($timestamp, 'undefined_style'), format_date($timestamp, 'fallback'), 'Test format_date() defaulting to `fallback` when $type not found.');
}
- /**
- * Tests the format_date() function.
- */
- function testFormatDate() {
- $timestamp = strtotime('2007-03-26T00:00:00+00:00');
- $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', 'Test all parameters.');
- $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'domingo, 25-Mar-07 17:00:00 PDT', 'Test translated format.');
- $this->assertIdentical(format_date($timestamp, 'custom', '\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'l, 25-Mar-07 17:00:00 PDT', 'Test an escaped format string.');
- $this->assertIdentical(format_date($timestamp, 'custom', '\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\domingo, 25-Mar-07 17:00:00 PDT', 'Test format containing backslash character.');
- $this->assertIdentical(format_date($timestamp, 'custom', '\\\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\l, 25-Mar-07 17:00:00 PDT', 'Test format containing backslash followed by escaped format string.');
- $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London', 'en'), 'Monday, 26-Mar-07 01:00:00 BST', 'Test a different time zone.');
-
- // Change the default language and timezone.
- $this->config('system.site')->set('default_langcode', static::LANGCODE)->save();
- date_default_timezone_set('America/Los_Angeles');
-
- // Reset the language manager so new negotiations attempts will fall back on
- // on the new language.
- $this->container->get('language_manager')->reset();
-
- $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', 'Test a different language.');
- $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London'), 'Monday, 26-Mar-07 01:00:00 BST', 'Test a different time zone.');
- $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T'), 'domingo, 25-Mar-07 17:00:00 PDT', 'Test custom date format.');
- $this->assertIdentical(format_date($timestamp, 'long'), 'domingo, 25. marzo 2007 - 17:00', 'Test long date format.');
- $this->assertIdentical(format_date($timestamp, 'medium'), '25. marzo 2007 - 17:00', 'Test medium date format.');
- $this->assertIdentical(format_date($timestamp, 'short'), '2007 Mar 25 - 5:00pm', 'Test short date format.');
- $this->assertIdentical(format_date($timestamp), '25. marzo 2007 - 17:00', 'Test default date format.');
- // Test HTML time element formats.
- $this->assertIdentical(format_date($timestamp, 'html_datetime'), '2007-03-25T17:00:00-0700', 'Test html_datetime date format.');
- $this->assertIdentical(format_date($timestamp, 'html_date'), '2007-03-25', 'Test html_date date format.');
- $this->assertIdentical(format_date($timestamp, 'html_time'), '17:00:00', 'Test html_time date format.');
- $this->assertIdentical(format_date($timestamp, 'html_yearless_date'), '03-25', 'Test html_yearless_date date format.');
- $this->assertIdentical(format_date($timestamp, 'html_week'), '2007-W12', 'Test html_week date format.');
- $this->assertIdentical(format_date($timestamp, 'html_month'), '2007-03', 'Test html_month date format.');
- $this->assertIdentical(format_date($timestamp, 'html_year'), '2007', 'Test html_year date format.');
-
- // HTML is not escaped by the date formatter, it must be escaped later.
- $formatter = \Drupal::service('date.formatter');
- $this->assertIdentical($formatter->format($timestamp, 'custom', '\<\s\c\r\i\p\t\>\a\l\e\r\t\(\'Y\'\)\;\<\/\s\c\r\i\p\t\>'), "", 'Script tags not removed from dates.');
- $this->assertIdentical($formatter->format($timestamp, 'custom', '\<\e\m\>Y\<\/\e\m\>'), '2007', 'Em tags are not removed from dates.');
- }
-
}
diff --git a/core/modules/system/src/Tests/Render/AjaxPageStateTest.php b/core/modules/system/src/Tests/Render/AjaxPageStateTest.php
index 08cd2768e..19e623775 100644
--- a/core/modules/system/src/Tests/Render/AjaxPageStateTest.php
+++ b/core/modules/system/src/Tests/Render/AjaxPageStateTest.php
@@ -11,6 +11,13 @@ use Drupal\simpletest\WebTestBase;
*/
class AjaxPageStateTest extends WebTestBase {
+ /**
+ * Modules to install.
+ *
+ * @var array
+ */
+ public static $modules = ['node', 'views'];
+
/**
* User account with all available permissions
*
@@ -77,22 +84,16 @@ class AjaxPageStateTest extends WebTestBase {
}
/**
- * Test if multiple libaries can be excluded.
+ * Test if multiple libraries can be excluded.
*
- * ajax_page_state[libraries] should be able to support multiple libraries
+ * The ajax_page_state[libraries] should be able to support multiple libraries
* comma separated.
*/
public function testMultipleLibrariesAreNotLoaded() {
$this->drupalGet('node',
- array(
- "query" =>
- array(
- 'ajax_page_state' => array(
- 'libraries' => 'core/html5shiv,core/drupalSettings'
- )
- )
- )
+ ['query' => ['ajax_page_state' => ['libraries' => 'core/html5shiv,core/drupalSettings']]]
);
+ $this->assertResponse(200);
$this->assertNoRaw(
'/core/assets/vendor/html5shiv/html5shiv.min.js',
'The html5shiv library from core should be excluded from loading.'
@@ -102,6 +103,17 @@ class AjaxPageStateTest extends WebTestBase {
'/core/misc/drupalSettingsLoader.js',
'The drupalSettings library from core should be excluded from loading.'
);
+
+ $this->drupalGet('node');
+ $this->assertRaw(
+ '/core/assets/vendor/html5shiv/html5shiv.min.js',
+ 'The html5shiv library from core should be included in loading.'
+ );
+
+ $this->assertRaw(
+ '/core/misc/drupalSettingsLoader.js',
+ 'The drupalSettings library from core should be included in loading.'
+ );
}
}
diff --git a/core/modules/system/src/Tests/Theme/EngineTwigTest.php b/core/modules/system/src/Tests/Theme/EngineTwigTest.php
index ae08fc835..51d092d44 100644
--- a/core/modules/system/src/Tests/Theme/EngineTwigTest.php
+++ b/core/modules/system/src/Tests/Theme/EngineTwigTest.php
@@ -2,6 +2,7 @@
namespace Drupal\system\Tests\Theme;
+use Drupal\Core\Render\Markup;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
@@ -75,12 +76,16 @@ class EngineTwigTest extends WebTestBase {
/** @var \Drupal\Core\Utility\LinkGenerator $link_generator */
$link_generator = $this->container->get('link_generator');
+
+ $generated_url = Url::fromRoute('user.register', [], ['absolute' => TRUE])->toString(TRUE)->getGeneratedUrl();
$expected = [
'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['absolute' => TRUE])),
'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['absolute' => TRUE, 'attributes' => ['foo' => 'bar']])),
'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['foo' => 'bar', 'id' => 'kitten']])),
'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['id' => 'kitten']])),
'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['class' => ['llama', 'kitten', 'panda']]])),
+ 'link via the linkgenerator: ' . $link_generator->generate(Markup::create('register'), new Url('user.register', [], ['absolute' => TRUE])),
+ 'link via the linkgenerator: register',
];
// Verify that link() has the ability to bubble cacheability metadata:
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 3ef24a5a1..cbdafbc9f 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -276,7 +276,7 @@ function system_requirements($phase) {
if (empty($drivers)) {
$database_ok = FALSE;
$pdo_message = t('Your web server does not appear to support any common PDO database extensions. Check with your hosting provider to see if they support PDO (PHP Data Objects) and offer any databases that Drupal supports.', array(
- ':drupal-databases' => 'https://www.drupal.org/node/270#database',
+ ':drupal-databases' => 'https://www.drupal.org/requirements/database',
));
}
// Make sure the native PDO extension is available, not the older PEAR
diff --git a/core/modules/system/system.libraries.yml b/core/modules/system/system.libraries.yml
index dd9d2ec43..206ae4fc8 100644
--- a/core/modules/system/system.libraries.yml
+++ b/core/modules/system/system.libraries.yml
@@ -59,6 +59,7 @@ drupal.system.modules:
- core/drupal
- core/drupal.debounce
- core/jquery.once
+ - core/drupal.announce
diff:
version: VERSION
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 2f627bef5..01c8c5e1e 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -623,7 +623,7 @@ function system_page_attachments(array &$page) {
// Handle setting the "active" class on links by:
// - loading the active-link library if the current user is authenticated;
// - applying a response filter if the current user is anonymous.
- // @see l()
+ // @see \Drupal\Core\Link
// @see \Drupal\Core\Utility\LinkGenerator::generate()
// @see template_preprocess_links()
// @see \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter
@@ -655,15 +655,6 @@ function system_js_settings_build(&$settings, AttachedAssetsInterface $assets) {
// @see \Drupal\Core\Theme\AjaxBasePageNegotiator
$theme_key = \Drupal::theme()->getActiveTheme()->getName();
$settings['ajaxPageState']['theme'] = $theme_key;
-
- // Provide the page with information about the individual asset libraries
- // used, information not otherwise available when aggregation is enabled.
- $minimal_libraries = $library_dependency_resolver->getMinimalRepresentativeSubset(array_merge(
- $assets->getLibraries(),
- $assets->getAlreadyLoadedLibraries()
- ));
- sort($minimal_libraries);
- $settings['ajaxPageState']['libraries'] = implode(',', $minimal_libraries);
}
}
@@ -715,7 +706,7 @@ function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) {
$settings['pluralDelimiter'] = LOCALE_PLURAL_DELIMITER;
}
// Add the theme token to ajaxPageState, ensuring the database is available
- // before doing so.
+ // before doing so. Also add the loaded libraries to ajaxPageState.
/** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */
$library_dependency_resolver = \Drupal::service('library.dependency_resolver');
if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) {
@@ -729,6 +720,14 @@ function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) {
->get($active_theme_key);
}
}
+ // Provide the page with information about the individual asset libraries
+ // used, information not otherwise available when aggregation is enabled.
+ $minimal_libraries = $library_dependency_resolver->getMinimalRepresentativeSubset(array_merge(
+ $assets->getLibraries(),
+ $assets->getAlreadyLoadedLibraries()
+ ));
+ sort($minimal_libraries);
+ $settings['ajaxPageState']['libraries'] = implode(',', $minimal_libraries);
}
}
diff --git a/core/modules/system/templates/html.html.twig b/core/modules/system/templates/html.html.twig
index 195d82e77..107c56b0a 100644
--- a/core/modules/system/templates/html.html.twig
+++ b/core/modules/system/templates/html.html.twig
@@ -8,7 +8,7 @@
* - root_path: The root path of the current page (e.g., node, admin, user).
* - node_type: The content type for the current node, if the page is a node.
* - head_title: List of text elements that make up the head_title variable.
- * May contain or more of the following:
+ * May contain one or more of the following:
* - title: The title of the page.
* - name: The name of the site.
* - slogan: The slogan of the site.
diff --git a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
index b618f0af7..63cc0abf1 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
@@ -23,7 +23,7 @@ class AjaxTestController {
public static function dialogContents() {
// This is a regular render array; the keys do not have special meaning.
$content = array(
- '#title' => 'AJAX Dialog contents',
+ '#title' => 'AJAX Dialog & contents',
'content' => array(
'#markup' => 'Example message',
),
diff --git a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
index 304e2bbaf..a97ed9525 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
@@ -93,7 +93,7 @@ class AjaxTestDialogForm extends FormBase {
protected function dialog($is_modal = FALSE) {
$content = AjaxTestController::dialogContents();
$response = new AjaxResponse();
- $title = $this->t('AJAX Dialog contents');
+ $title = $this->t('AJAX Dialog & contents');
// Attach the library necessary for using the Open(Modal)DialogCommand and
// set the attachments for this Ajax response.
diff --git a/core/modules/system/tests/modules/condition_test/src/Plugin/Condition/ConditionTestNoExistingType.php b/core/modules/system/tests/modules/condition_test/src/Plugin/Condition/ConditionTestNoExistingType.php
new file mode 100644
index 000000000..ef00053fe
--- /dev/null
+++ b/core/modules/system/tests/modules/condition_test/src/Plugin/Condition/ConditionTestNoExistingType.php
@@ -0,0 +1,34 @@
+t('Condition that requires a non-existent context.');
+ }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 453bdf114..b0c8cbf4d 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -674,14 +674,25 @@ function _entity_test_record_hooks($hook, $data) {
* Implements hook_entity_prepare_view().
*/
function entity_test_entity_prepare_view($entity_type, array $entities, array $displays) {
- // Add a dummy field item attribute on field_test_text if it exists.
if ($entity_type == 'entity_test') {
foreach ($entities as $entity) {
+ /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+
+ // Add a dummy field item attribute on field_test_text if it exists.
if ($entity->hasField('field_test_text') && $displays[$entity->bundle()]->getComponent('field_test_text')) {
foreach ($entity->get('field_test_text') as $item) {
$item->_attributes += array('data-field-item-attr' => 'foobar');
}
}
+
+ // Add a dummy field item attribute on daterange fields if they exist.
+ $fields = $entity->getFieldDefinitions();
+ foreach ($fields as $field) {
+ if ($field->getType() === 'daterange') {
+ $item = $entity->get($field->getName());
+ $item->_attributes += array('data-field-item-attr' => 'foobar');
+ }
+ }
}
}
}
diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml
index 790b9ce21..2250a0bb2 100644
--- a/core/modules/system/tests/modules/form_test/form_test.routing.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml
@@ -358,6 +358,14 @@ form_test.label:
requirements:
_access: 'TRUE'
+form_test.machine_name:
+ path: '/form-test/machine-name'
+ defaults:
+ _form: '\Drupal\form_test\Form\FormTestMachineNameForm'
+ _title: 'Machine name fields'
+ requirements:
+ _access: 'TRUE'
+
form_test.state_persistence:
path: '/form-test/state-persist'
defaults:
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameForm.php
new file mode 100644
index 000000000..542edc8a7
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestMachineNameForm.php
@@ -0,0 +1,63 @@
+ 'textfield',
+ '#title' => 'Machine name 1 label',
+ ];
+ $form['machine_name_1'] = [
+ '#type' => 'machine_name',
+ '#title' => 'Machine name 1',
+ '#description' => 'A machine name.',
+ '#machine_name' => [
+ 'source' => ['machine_name_1_label']
+ ],
+ ];
+ $form['machine_name_2_label'] = [
+ '#type' => 'textfield',
+ '#title' => 'Machine name 2 label',
+ ];
+ $form['machine_name_2'] = [
+ '#type' => 'machine_name',
+ '#title' => 'Machine name 2',
+ '#description' => 'Another machine name.',
+ '#machine_name' => [
+ 'source' => ['machine_name_2_label']
+ ],
+ ];
+ $form['submit'] = [
+ '#type' => 'submit',
+ '#value' => 'Submit',
+ ];
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+ $form_state->setResponse(new JsonResponse($form_state->getValues()));
+ }
+
+}
diff --git a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.link_generator.html.twig b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.link_generator.html.twig
index 8925705be..a836aec93 100644
--- a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.link_generator.html.twig
+++ b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.link_generator.html.twig
@@ -3,3 +3,7 @@
link via the linkgenerator: {{ link('register', test_url_attribute, {'id': 'kitten'}) }}
link via the linkgenerator: {{ link('register', 'route:user.register', {'id': 'kitten'}) }}
link via the linkgenerator: {{ link('register', 'route:user.register', attributes) }}
+{% set title %}register{% endset %}
+
link via the linkgenerator: {{ link(title, test_url) }}
+{% set title %}register{% endset %}
+
link via the linkgenerator: {{ link(title, test_url) }}
diff --git a/core/modules/system/tests/src/Kernel/Form/FormElementLabelTest.php b/core/modules/system/tests/src/Kernel/Form/FormElementLabelTest.php
new file mode 100644
index 000000000..f93d461bc
--- /dev/null
+++ b/core/modules/system/tests/src/Kernel/Form/FormElementLabelTest.php
@@ -0,0 +1,36 @@
+ 'label',
+ '#attributes' => ['class' => ['kitten']],
+ '#title' => 'Kittens',
+ '#title_display' => 'above',
+ ];
+ $css_selector_converter = new CssSelectorConverter();
+ $this->render($render_array);
+ $elements = $this->xpath($css_selector_converter->toXPath('.kitten'));
+ $this->assertCount(1, $elements);
+ }
+
+}
diff --git a/core/modules/system/tests/src/Kernel/Installer/InstallerMissingDependenciesTest.php b/core/modules/system/tests/src/Kernel/Installer/InstallerMissingDependenciesTest.php
new file mode 100644
index 000000000..052c0afb8
--- /dev/null
+++ b/core/modules/system/tests/src/Kernel/Installer/InstallerMissingDependenciesTest.php
@@ -0,0 +1,39 @@
+ ['profile' => 'testing_missing_dependencies'],
+ 'profile_info' => install_profile_info('testing_missing_dependencies'),
+ ]);
+
+ $message = $info['required_modules']['description']->render();
+ $this->assertContains('Missing_module1', $message);
+ $this->assertContains('Missing_module2', $message);
+ }
+
+}
diff --git a/core/modules/system/tests/src/Kernel/Migrate/MigrateMenuTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateMenuTest.php
similarity index 75%
rename from core/modules/system/tests/src/Kernel/Migrate/MigrateMenuTest.php
rename to core/modules/system/tests/src/Kernel/Migrate/d6/MigrateMenuTest.php
index c66ca02c7..e6ac30c88 100644
--- a/core/modules/system/tests/src/Kernel/Migrate/MigrateMenuTest.php
+++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateMenuTest.php
@@ -1,6 +1,6 @@
executeMigration('menu');
+ $this->executeMigration('d6_menu');
}
/**
@@ -26,12 +26,12 @@ class MigrateMenuTest extends MigrateDrupal6TestBase {
*/
public function testMenu() {
$navigation_menu = Menu::load('navigation');
- $this->assertIdentical('navigation', $navigation_menu->id());
- $this->assertIdentical('Navigation', $navigation_menu->label());
+ $this->assertSame('navigation', $navigation_menu->id());
+ $this->assertSame('Navigation', $navigation_menu->label());
$expected = <<assertIdentical($expected, $navigation_menu->getDescription());
+ $this->assertSame($expected, $navigation_menu->getDescription());
// Test that we can re-import using the ConfigEntityBase destination.
Database::getConnection('default', 'migrate')
@@ -40,14 +40,14 @@ EOT;
->condition('menu_name', 'navigation')
->execute();
- $migration = $this->getMigration('menu');
+ $migration = $this->getMigration('d6_menu');
\Drupal::database()
->truncate($migration->getIdMap()->mapTableName())
->execute();
$this->executeMigration($migration);
$navigation_menu = Menu::load('navigation');
- $this->assertIdentical('Home Navigation', $navigation_menu->label());
+ $this->assertSame('Home Navigation', $navigation_menu->label());
}
}
diff --git a/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTest.php b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTest.php
new file mode 100644
index 000000000..987084539
--- /dev/null
+++ b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateMenuTest.php
@@ -0,0 +1,68 @@
+executeMigration('d7_menu');
+ }
+
+ /**
+ * Asserts various aspects of a menu.
+ *
+ * @param $id
+ * The menu ID.
+ * @param $label
+ * The menu label.
+ * @param $description
+ * The menu description.
+ */
+ protected function assertEntity($id, $label, $description) {
+ $navigation_menu = Menu::load($id);
+ $this->assertSame($id, $navigation_menu->id());
+ $this->assertSame($label, $navigation_menu->label());
+ $this->assertSame($description, $navigation_menu->getDescription());
+ }
+
+ /**
+ * Tests the Drupal 7 menu to Drupal 8 migration.
+ */
+ public function testMenu() {
+ $this->assertEntity('main', 'Main menu', 'The Main menu is used on many sites to show the major sections of the site, often in a top navigation bar.');
+ $this->assertEntity('admin', 'Management', 'The Management menu contains links for administrative tasks.');
+ $this->assertEntity('menu-test-menu', 'Test Menu', 'Test menu description.');
+ $this->assertEntity('tools', 'Navigation', 'The Navigation menu contains links intended for site visitors. Links are added to the Navigation menu automatically by some modules.');
+ $this->assertEntity('account', 'User menu', 'The User menu contains links related to the user\'s account, as well as the \'Log out\' link.');
+
+ // Test that we can re-import using the ConfigEntityBase destination.
+ Database::getConnection('default', 'migrate')
+ ->update('menu_custom')
+ ->fields(array('title' => 'Home Navigation'))
+ ->condition('menu_name', 'navigation')
+ ->execute();
+
+ $migration = $this->getMigration('d7_menu');
+ \Drupal::database()
+ ->truncate($migration->getIdMap()->mapTableName())
+ ->execute();
+ $this->executeMigration($migration);
+
+ $navigation_menu = Menu::load('tools');
+ $this->assertSame('Home Navigation', $navigation_menu->label());
+ }
+
+}
diff --git a/core/modules/system/tests/src/Kernel/Plugin/migrate/source/MenuTest.php b/core/modules/system/tests/src/Kernel/Plugin/migrate/source/MenuTest.php
new file mode 100644
index 000000000..0feb9a2b0
--- /dev/null
+++ b/core/modules/system/tests/src/Kernel/Plugin/migrate/source/MenuTest.php
@@ -0,0 +1,47 @@
+ 'menu-name-1',
+ 'title' => 'menu custom value 1',
+ 'description' => 'menu custom description value 1',
+ ],
+ [
+ 'menu_name' => 'menu-name-2',
+ 'title' => 'menu custom value 2',
+ 'description' => 'menu custom description value 2',
+ ],
+ ];
+
+ // The expected results are identical to the source data.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['menu_custom'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/system/tests/src/Unit/Plugin/migrate/source/MenuTest.php b/core/modules/system/tests/src/Unit/Plugin/migrate/source/MenuTest.php
deleted file mode 100644
index ca38892d3..000000000
--- a/core/modules/system/tests/src/Unit/Plugin/migrate/source/MenuTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'menu',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'menu_name' => 'menu-name-1',
- 'title' => 'menu custom value 1',
- 'description' => 'menu custom description value 1',
- ),
- array(
- 'menu_name' => 'menu-name-2',
- 'title' => 'menu custom value 2',
- 'description' => 'menu custom description value 2',
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['menu_custom'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/taxonomy/src/Entity/Vocabulary.php b/core/modules/taxonomy/src/Entity/Vocabulary.php
index fa6cc18c9..a2d7eef8a 100644
--- a/core/modules/taxonomy/src/Entity/Vocabulary.php
+++ b/core/modules/taxonomy/src/Entity/Vocabulary.php
@@ -73,13 +73,13 @@ class Vocabulary extends ConfigEntityBundleBase implements VocabularyInterface {
* The type of hierarchy allowed within the vocabulary.
*
* Possible values:
- * - TAXONOMY_HIERARCHY_DISABLED: No parents.
- * - TAXONOMY_HIERARCHY_SINGLE: Single parent.
- * - TAXONOMY_HIERARCHY_MULTIPLE: Multiple parents.
+ * - VocabularyInterface::HIERARCHY_DISABLED: No parents.
+ * - VocabularyInterface::HIERARCHY_SINGLE: Single parent.
+ * - VocabularyInterface::HIERARCHY_MULTIPL: Multiple parents.
*
* @var int
*/
- protected $hierarchy = TAXONOMY_HIERARCHY_DISABLED;
+ protected $hierarchy = VocabularyInterface::HIERARCHY_DISABLED;
/**
* The weight of this vocabulary in relation to other vocabularies.
diff --git a/core/modules/taxonomy/src/Form/OverviewTerms.php b/core/modules/taxonomy/src/Form/OverviewTerms.php
index cdc661956..4c8cee3fb 100644
--- a/core/modules/taxonomy/src/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/src/Form/OverviewTerms.php
@@ -223,7 +223,7 @@ class OverviewTerms extends FormBase {
'#title' => $term->getName(),
'#url' => $term->urlInfo(),
);
- if ($taxonomy_vocabulary->getHierarchy() != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
+ if ($taxonomy_vocabulary->getHierarchy() != VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) {
$parent_fields = TRUE;
$form['terms'][$key]['term']['tid'] = array(
'#type' => 'hidden',
@@ -340,7 +340,7 @@ class OverviewTerms extends FormBase {
'group' => 'term-weight',
);
- if ($taxonomy_vocabulary->getHierarchy() != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
+ if ($taxonomy_vocabulary->getHierarchy() != VocabularyInterface::HIERARCHY_MULTIPLE && count($tree) > 1) {
$form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
$form['actions']['submit'] = array(
'#type' => 'submit',
@@ -382,7 +382,7 @@ class OverviewTerms extends FormBase {
$vocabulary = $form_state->get(['taxonomy', 'vocabulary']);
// Update the current hierarchy type as we go.
- $hierarchy = TAXONOMY_HIERARCHY_DISABLED;
+ $hierarchy = VocabularyInterface::HIERARCHY_DISABLED;
$changed_terms = array();
$tree = $this->storageController->loadTree($vocabulary->id(), 0, NULL, TRUE);
@@ -400,7 +400,7 @@ class OverviewTerms extends FormBase {
$changed_terms[$term->id()] = $term;
}
$weight++;
- $hierarchy = $term->parents[0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
+ $hierarchy = $term->parents[0] != 0 ? VocabularyInterface::HIERARCHY_SINGLE : $hierarchy;
$term = $tree[$weight];
}
@@ -427,7 +427,7 @@ class OverviewTerms extends FormBase {
$term->parent->target_id = $values['term']['parent'];
$changed_terms[$term->id()] = $term;
}
- $hierarchy = $term->parents[0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
+ $hierarchy = $term->parents[0] != 0 ? VocabularyInterface::HIERARCHY_SINGLE : $hierarchy;
$weight++;
}
}
@@ -440,7 +440,7 @@ class OverviewTerms extends FormBase {
$term->setWeight($weight);
$changed_terms[$term->id()] = $term;
}
- $hierarchy = $term->parents[0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
+ $hierarchy = $term->parents[0] != 0 ? VocabularyInterface::HIERARCHY_SINGLE : $hierarchy;
}
// Save all updated terms.
diff --git a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
index 3b1c2368d..7dd4aa2d6 100644
--- a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
+++ b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php
@@ -49,6 +49,7 @@ class TermSelection extends DefaultSelection {
*/
public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
if ($match || $limit) {
+ $this->configuration['handler_settings']['sort'] = ['field' => 'name', 'direction' => 'asc'];
return parent::getReferenceableEntities($match, $match_operator, $limit);
}
diff --git a/core/modules/taxonomy/src/TermForm.php b/core/modules/taxonomy/src/TermForm.php
index cf05932e5..1f5635132 100644
--- a/core/modules/taxonomy/src/TermForm.php
+++ b/core/modules/taxonomy/src/TermForm.php
@@ -26,7 +26,7 @@ class TermForm extends ContentEntityForm {
$form['relations'] = array(
'#type' => 'details',
'#title' => $this->t('Relations'),
- '#open' => $vocabulary->getHierarchy() == TAXONOMY_HIERARCHY_MULTIPLE,
+ '#open' => $vocabulary->getHierarchy() == VocabularyInterface::HIERARCHY_MULTIPLE,
'#weight' => 10,
);
@@ -152,8 +152,8 @@ class TermForm extends ContentEntityForm {
}
// If we've increased the number of parents and this is a single or flat
// hierarchy, update the vocabulary immediately.
- elseif ($current_parent_count > $previous_parent_count && $vocabulary->getHierarchy() != TAXONOMY_HIERARCHY_MULTIPLE) {
- $vocabulary->setHierarchy($current_parent_count == 1 ? TAXONOMY_HIERARCHY_SINGLE : TAXONOMY_HIERARCHY_MULTIPLE);
+ elseif ($current_parent_count > $previous_parent_count && $vocabulary->getHierarchy() != VocabularyInterface::HIERARCHY_MULTIPLE) {
+ $vocabulary->setHierarchy($current_parent_count == 1 ? VocabularyInterface::HIERARCHY_SINGLE : VocabularyInterface::HIERARCHY_MULTIPLE);
$vocabulary->save();
}
diff --git a/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php b/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php
new file mode 100644
index 000000000..7ff519df9
--- /dev/null
+++ b/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php
@@ -0,0 +1,204 @@
+vocabulary = $this->createVocabulary();
+
+ // Create 11 terms, which have some sub-string in common, in a
+ // non-alphabetical order, so that we will have more than 10 matches later
+ // when we test the correct number of results is returned, and we can test
+ // the order of the results. The location of the sub-string to match varies
+ // also, since it should not be necessary to start with the sub-string to
+ // match it. Save term IDs to reuse later.
+ $termNames = [
+ 'aaa 20 bbb',
+ 'aaa 70 bbb',
+ 'aaa 10 bbb',
+ 'aaa 12 bbb',
+ 'aaa 40 bbb',
+ 'aaa 11 bbb',
+ 'aaa 30 bbb',
+ 'aaa 50 bbb',
+ 'aaa 80',
+ 'aaa 90',
+ 'bbb 60 aaa',
+ ];
+ foreach ($termNames as $termName) {
+ $term = $this->createTerm($this->vocabulary, ['name' => $termName]);
+ $this->termIds[$termName] = $term->id();
+ }
+
+ // Create a taxonomy_term_reference field on the article Content Type that
+ // uses a taxonomy_autocomplete widget.
+ $this->fieldName = Unicode::strtolower($this->randomMachineName());
+ FieldStorageConfig::create([
+ 'field_name' => $this->fieldName,
+ 'entity_type' => 'node',
+ 'type' => 'entity_reference',
+ 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+ 'settings' => [
+ 'target_type' => 'taxonomy_term',
+ ],
+ ])->save();
+ FieldConfig::create([
+ 'field_name' => $this->fieldName,
+ 'bundle' => 'article',
+ 'entity_type' => 'node',
+ 'settings' => [
+ 'handler' => 'default',
+ 'handler_settings' => [
+ // Restrict selection of terms to a single vocabulary.
+ 'target_bundles' => [
+ $this->vocabulary->id() => $this->vocabulary->id(),
+ ],
+ ],
+ ],
+ ])->save();
+ EntityFormDisplay::load('node.article.default')
+ ->setComponent($this->fieldName, [
+ 'type' => 'entity_reference_autocomplete',
+ ])
+ ->save();
+ EntityViewDisplay::load('node.article.default')
+ ->setComponent($this->fieldName, [
+ 'type' => 'entity_reference_label',
+ ])
+ ->save();
+
+ // Create a user and then login.
+ $this->adminUser = $this->drupalCreateUser(['create article content']);
+ $this->drupalLogin($this->adminUser);
+
+ // Retrieve the autocomplete url.
+ $this->drupalGet('node/add/article');
+ $result = $this->xpath('//input[@name="' . $this->fieldName . '[0][target_id]"]');
+ $this->autocompleteUrl = $this->getAbsoluteUrl($result[0]['data-autocomplete-path']);
+ }
+
+ /**
+ * Tests that the autocomplete method returns the good number of results.
+ *
+ * @see \Drupal\taxonomy\Controller\TermAutocompleteController::autocomplete()
+ */
+ public function testAutocompleteCountResults() {
+ // Test that no matching term found.
+ $data = $this->drupalGetJSON(
+ $this->autocompleteUrl,
+ ['query' => ['q' => 'zzz']]
+ );
+ $this->assertTrue(empty($data), 'Autocomplete returned no results');
+
+ // Test that only one matching term found, when only one matches.
+ $data = $this->drupalGetJSON(
+ $this->autocompleteUrl,
+ ['query' => ['q' => 'aaa 10']]
+ );
+ $this->assertEqual(1, count($data), 'Autocomplete returned 1 result');
+
+ // Test the correct number of matches when multiple are partial matches.
+ $data = $this->drupalGetJSON(
+ $this->autocompleteUrl,
+ ['query' => ['q' => 'aaa 1']]
+ );
+ $this->assertEqual(3, count($data), 'Autocomplete returned 3 results');
+
+ // Tests that only 10 results are returned, even if there are more than 10
+ // matches.
+ $data = $this->drupalGetJSON(
+ $this->autocompleteUrl,
+ ['query' => ['q' => 'aaa']]
+ );
+ $this->assertEqual(10, count($data), 'Autocomplete returned only 10 results (for over 10 matches)');
+ }
+
+ /**
+ * Tests that the autocomplete method returns properly ordered results.
+ *
+ * @see \Drupal\taxonomy\Controller\TermAutocompleteController::autocomplete()
+ */
+ public function testAutocompleteOrderedResults() {
+ $expectedResults = [
+ 'aaa 10 bbb',
+ 'aaa 11 bbb',
+ 'aaa 12 bbb',
+ 'aaa 20 bbb',
+ 'aaa 30 bbb',
+ 'aaa 40 bbb',
+ 'aaa 50 bbb',
+ 'aaa 70 bbb',
+ 'bbb 60 aaa',
+ ];
+ // Build $expected to match the autocomplete results.
+ $expected = [];
+ foreach ($expectedResults as $termName) {
+ $expected[] = [
+ 'value' => $termName . ' (' . $this->termIds[$termName] . ')',
+ 'label' => $termName
+ ];
+ }
+
+ $data = $this->drupalGetJSON(
+ $this->autocompleteUrl,
+ ['query' => ['q' => 'bbb']]
+ );
+
+ $this->assertIdentical($expected, $data);
+ }
+
+}
diff --git a/core/modules/taxonomy/src/VocabularyInterface.php b/core/modules/taxonomy/src/VocabularyInterface.php
index ff52bec82..cb54e2018 100644
--- a/core/modules/taxonomy/src/VocabularyInterface.php
+++ b/core/modules/taxonomy/src/VocabularyInterface.php
@@ -9,6 +9,21 @@ use Drupal\Core\Config\Entity\ConfigEntityInterface;
*/
interface VocabularyInterface extends ConfigEntityInterface {
+ /**
+ * Denotes that no term in the vocabulary has a parent.
+ */
+ const HIERARCHY_DISABLED = 0;
+
+ /**
+ * Denotes that one or more terms in the vocabulary has a single parent.
+ */
+ const HIERARCHY_SINGLE = 1;
+
+ /**
+ * Denotes that one or more terms in the vocabulary have multiple parents.
+ */
+ const HIERARCHY_MULTIPLE = 2;
+
/**
* Returns the vocabulary hierarchy.
*
@@ -23,9 +38,9 @@ interface VocabularyInterface extends ConfigEntityInterface {
* @param int $hierarchy
* The hierarchy type of vocabulary.
* Possible values:
- * - TAXONOMY_HIERARCHY_DISABLED: No parents.
- * - TAXONOMY_HIERARCHY_SINGLE: Single parent.
- * - TAXONOMY_HIERARCHY_MULTIPLE: Multiple parents.
+ * - VocabularyInterface::HIERARCHY_DISABLED: No parents.
+ * - VocabularyInterface::HIERARCHY_SINGLE: Single parent.
+ * - VocabularyInterface::HIERARCHY_MULTIPLE: Multiple parents.
*
* @return $this
*/
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 32df678b0..7a1ca6521 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -19,16 +19,25 @@ use Drupal\taxonomy\VocabularyInterface;
/**
* Denotes that no term in the vocabulary has a parent.
+ *
+ * @deprecated in Drupal 8.2.x and will be removed before 9.0.0. Use
+ * \Drupal\taxonomy\VocabularyInterface::HIERARCHY_DISABLED instead.
*/
const TAXONOMY_HIERARCHY_DISABLED = 0;
/**
* Denotes that one or more terms in the vocabulary has a single parent.
+ *
+ * @deprecated in Drupal 8.2.x and will be removed before 9.0.0. Use
+ * \Drupal\taxonomy\VocabularyInterface::HIERARCHY_SINGLE instead.
*/
const TAXONOMY_HIERARCHY_SINGLE = 1;
/**
* Denotes that one or more terms in the vocabulary have multiple parents.
+ *
+ * @deprecated in Drupal 8.2.x and will be removed before 9.0.0. Use
+ * \Drupal\taxonomy\VocabularyInterface::HIERARCHY_MULTIPLE instead.
*/
const TAXONOMY_HIERARCHY_MULTIPLE = 2;
@@ -67,11 +76,11 @@ function taxonomy_help($route_name, RouteMatchInterface $route_match) {
case 'entity.taxonomy_vocabulary.overview_form':
$vocabulary = $route_match->getParameter('taxonomy_vocabulary');
switch ($vocabulary->getHierarchy()) {
- case TAXONOMY_HIERARCHY_DISABLED:
+ case VocabularyInterface::HIERARCHY_DISABLED:
return '
' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', array('%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label())) . '
';
- case TAXONOMY_HIERARCHY_SINGLE:
+ case VocabularyInterface::HIERARCHY_SINGLE:
return '
' . t('%capital_name contains terms grouped under parent terms. You can reorganize the terms in %capital_name using their drag-and-drop handles.', array('%capital_name' => Unicode::ucfirst($vocabulary->label()), '%name' => $vocabulary->label())) . '
';
- case TAXONOMY_HIERARCHY_MULTIPLE:
+ case VocabularyInterface::HIERARCHY_MULTIPLE:
return '
' . t('%capital_name contains terms with multiple parents. Drag and drop of terms with multiple parents is not supported, but you can re-enable drag-and-drop support by editing each term to include only a single parent.', array('%capital_name' => Unicode::ucfirst($vocabulary->label()))) . '
';
}
}
@@ -134,10 +143,11 @@ function taxonomy_theme() {
* Checks the current parents of all terms in a vocabulary and updates the
* vocabulary's hierarchy setting to the lowest possible level. If no term
* has parent terms then the vocabulary will be given a hierarchy of
- * TAXONOMY_HIERARCHY_DISABLED. If any term has a single parent then the
- * vocabulary will be given a hierarchy of TAXONOMY_HIERARCHY_SINGLE. If any
- * term has multiple parents then the vocabulary will be given a hierarchy of
- * TAXONOMY_HIERARCHY_MULTIPLE.
+ * VocabularyInterface::HIERARCHY_DISABLED. If any term has a single parent then
+ * the vocabulary will be given a hierarchy of
+ * VocabularyInterface::HIERARCHY_SINGLE. If any term has multiple parents then
+ * the vocabulary will be given a hierarchy of
+ * VocabularyInterface::HIERARCHY_MULTIPLE.
*
* @param \Drupal\taxonomy\VocabularyInterface $vocabulary
* A taxonomy vocabulary entity.
@@ -149,7 +159,7 @@ function taxonomy_theme() {
*/
function taxonomy_check_vocabulary_hierarchy(VocabularyInterface $vocabulary, $changed_term) {
$tree = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vocabulary->id());
- $hierarchy = TAXONOMY_HIERARCHY_DISABLED;
+ $hierarchy = VocabularyInterface::HIERARCHY_DISABLED;
foreach ($tree as $term) {
// Update the changed term with the new parent value before comparison.
if ($term->tid == $changed_term['tid']) {
@@ -158,11 +168,11 @@ function taxonomy_check_vocabulary_hierarchy(VocabularyInterface $vocabulary, $c
}
// Check this term's parent count.
if (count($term->parents) > 1) {
- $hierarchy = TAXONOMY_HIERARCHY_MULTIPLE;
+ $hierarchy = VocabularyInterface::HIERARCHY_MULTIPLE;
break;
}
elseif (count($term->parents) == 1 && !isset($term->parents[0])) {
- $hierarchy = TAXONOMY_HIERARCHY_SINGLE;
+ $hierarchy = VocabularyInterface::HIERARCHY_SINGLE;
}
}
if ($hierarchy != $vocabulary->getHierarchy()) {
diff --git a/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/src/VocabularyResponse.php b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/src/VocabularyResponse.php
new file mode 100644
index 000000000..39941b4c6
--- /dev/null
+++ b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/src/VocabularyResponse.php
@@ -0,0 +1,19 @@
+vocabulary = $vocabulary;
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/src/VocabularySerializationTestController.php b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/src/VocabularySerializationTestController.php
new file mode 100644
index 000000000..829edfbe0
--- /dev/null
+++ b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/src/VocabularySerializationTestController.php
@@ -0,0 +1,15 @@
+setVocabulary($taxonomy_vocabulary);
+ return $response;
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.info.yml b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.info.yml
new file mode 100644
index 000000000..be28f20a2
--- /dev/null
+++ b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.info.yml
@@ -0,0 +1,7 @@
+name: 'Vocabulary serialization test'
+type: module
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+ - taxonomy
diff --git a/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.routing.yml b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.routing.yml
new file mode 100644
index 000000000..82f2b2222
--- /dev/null
+++ b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.routing.yml
@@ -0,0 +1,6 @@
+vocabulary_serialization_test:
+ path: '/vocabulary_serialization_test/{taxonomy_vocabulary}'
+ defaults:
+ _controller: 'Drupal\vocabulary_serialization_test\VocabularySerializationTestController::vocabularyResponse'
+ requirements:
+ _access: 'TRUE'
diff --git a/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php b/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php
new file mode 100644
index 000000000..b9aba8a79
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php
@@ -0,0 +1,47 @@
+ 'test'])->save();
+ }
+
+ public function testSerialization() {
+ $this->drupalGet('/vocabulary_serialization_test/test');
+ $this->assertSession()->statusCodeEquals(200);
+ $this->assertSame('this is the output', $this->getSession()->getPage()->getContent());
+ $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'MISS');
+
+ $this->drupalGet('/vocabulary_serialization_test/test');
+ $this->assertSession()->statusCodeEquals(200);
+ $this->assertSame('this is the output', $this->getSession()->getPage()->getContent());
+ $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'HIT');
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyVocabularyTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyVocabularyTest.php
index f94d79197..3f29fbf77 100644
--- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyVocabularyTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyVocabularyTest.php
@@ -54,9 +54,9 @@ class MigrateTaxonomyVocabularyTest extends MigrateDrupal7TestBase {
* Tests the Drupal 7 taxonomy vocabularies to Drupal 8 migration.
*/
public function testTaxonomyVocabulary() {
- $this->assertEntity('tags', 'Tags', 'Use tags to group articles on similar topics into categories.', TAXONOMY_HIERARCHY_DISABLED, 0);
- $this->assertEntity('forums', 'Forums', 'Forum navigation vocabulary', TAXONOMY_HIERARCHY_SINGLE, -10);
- $this->assertEntity('test_vocabulary', 'Test Vocabulary', 'This is the vocabulary description', TAXONOMY_HIERARCHY_SINGLE, 0);
+ $this->assertEntity('tags', 'Tags', 'Use tags to group articles on similar topics into categories.', VocabularyInterface::HIERARCHY_DISABLED, 0);
+ $this->assertEntity('forums', 'Forums', 'Forum navigation vocabulary', VocabularyInterface::HIERARCHY_SINGLE, -10);
+ $this->assertEntity('test_vocabulary', 'Test Vocabulary', 'This is the vocabulary description', VocabularyInterface::HIERARCHY_SINGLE, 0);
}
}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/TermSourceWithVocabularyFilterTest.php b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/TermSourceWithVocabularyFilterTest.php
new file mode 100644
index 000000000..4ac2766d7
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/TermSourceWithVocabularyFilterTest.php
@@ -0,0 +1,56 @@
+ 1,
+ 'vid' => 5,
+ 'name' => 'name value 1',
+ 'description' => 'description value 1',
+ 'weight' => 0,
+ 'parent' => [0],
+ ],
+ [
+ 'tid' => 4,
+ 'vid' => 5,
+ 'name' => 'name value 4',
+ 'description' => 'description value 4',
+ 'weight' => 1,
+ 'parent' => [1],
+ ],
+ ];
+
+ // We know there are two rows with vid == 5.
+ $tests[0]['expected_count'] = 2;
+
+ // Set up source plugin configuration.
+ $tests[0]['configuration'] = [
+ 'vocabulary' => [5],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/TermTest.php b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/TermTest.php
new file mode 100644
index 000000000..bdffcf81c
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/TermTest.php
@@ -0,0 +1,157 @@
+ 1,
+ 'vid' => 5,
+ 'name' => 'name value 1',
+ 'description' => 'description value 1',
+ 'weight' => 0,
+ ],
+ [
+ 'tid' => 2,
+ 'vid' => 6,
+ 'name' => 'name value 2',
+ 'description' => 'description value 2',
+ 'weight' => 0,
+ ],
+ [
+ 'tid' => 3,
+ 'vid' => 6,
+ 'name' => 'name value 3',
+ 'description' => 'description value 3',
+ 'weight' => 0,
+ ],
+ [
+ 'tid' => 4,
+ 'vid' => 5,
+ 'name' => 'name value 4',
+ 'description' => 'description value 4',
+ 'weight' => 1,
+ ],
+ [
+ 'tid' => 5,
+ 'vid' => 6,
+ 'name' => 'name value 5',
+ 'description' => 'description value 5',
+ 'weight' => 1,
+ ],
+ [
+ 'tid' => 6,
+ 'vid' => 6,
+ 'name' => 'name value 6',
+ 'description' => 'description value 6',
+ 'weight' => 0,
+ ],
+ ];
+ $tests[0]['source_data']['term_hierarchy'] = [
+ [
+ 'tid' => 1,
+ 'parent' => 0,
+ ],
+ [
+ 'tid' => 2,
+ 'parent' => 0,
+ ],
+ [
+ 'tid' => 3,
+ 'parent' => 0,
+ ],
+ [
+ 'tid' => 4,
+ 'parent' => 1,
+ ],
+ [
+ 'tid' => 5,
+ 'parent' => 2,
+ ],
+ [
+ 'tid' => 6,
+ 'parent' => 3,
+ ],
+ [
+ 'tid' => 6,
+ 'parent' => 2,
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'tid' => 1,
+ 'vid' => 5,
+ 'name' => 'name value 1',
+ 'description' => 'description value 1',
+ 'weight' => 0,
+ 'parent' => [0],
+ ],
+ [
+ 'tid' => 2,
+ 'vid' => 6,
+ 'name' => 'name value 2',
+ 'description' => 'description value 2',
+ 'weight' => 0,
+ 'parent' => [0],
+ ],
+ [
+ 'tid' => 3,
+ 'vid' => 6,
+ 'name' => 'name value 3',
+ 'description' => 'description value 3',
+ 'weight' => 0,
+ 'parent' => [0],
+ ],
+ [
+ 'tid' => 4,
+ 'vid' => 5,
+ 'name' => 'name value 4',
+ 'description' => 'description value 4',
+ 'weight' => 1,
+ 'parent' => [1],
+ ],
+ [
+ 'tid' => 5,
+ 'vid' => 6,
+ 'name' => 'name value 5',
+ 'description' => 'description value 5',
+ 'weight' => 1,
+ 'parent' => [2],
+ ],
+ [
+ 'tid' => 6,
+ 'vid' => 6,
+ 'name' => 'name value 6',
+ 'description' => 'description value 6',
+ 'weight' => 0,
+ 'parent' => [3, 2],
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Unit/Migrate/d6/TermNodeTest.php b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d6/TermNodeTest.php
similarity index 54%
rename from core/modules/taxonomy/tests/src/Unit/Migrate/d6/TermNodeTest.php
rename to core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d6/TermNodeTest.php
index 6ef0bd79f..3084acffa 100644
--- a/core/modules/taxonomy/tests/src/Unit/Migrate/d6/TermNodeTest.php
+++ b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d6/TermNodeTest.php
@@ -1,59 +1,48 @@
'test',
- 'source' => array(
- 'plugin' => 'd6_term_node',
- 'vid' => 3,
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'nid' => 1,
- 'vid' => 1,
- 'type' => 'story',
- 'tid' => array(1, 4, 5),
- ),
- );
+class TermNodeTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
- protected function setUp() {
- $this->databaseContents['term_node'] = array(
- array(
+ public static $modules = ['taxonomy', 'migrate_drupal'];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function providerSource() {
+ $tests = [];
+
+ // The source data.
+ $tests[0]['source_data']['term_node'] = [
+ [
'nid' => '1',
'vid' => '1',
'tid' => '1',
- ),
- array(
+ ],
+ [
'nid' => '1',
'vid' => '1',
'tid' => '4',
- ),
- array(
+ ],
+ [
'nid' => '1',
'vid' => '1',
'tid' => '5',
- ),
- );
- $this->databaseContents['node'] = array(
- array(
+ ],
+ ];
+ $tests[0]['source_data']['node'] = [
+ [
'nid' => '1',
'vid' => '1',
'type' => 'story',
@@ -69,32 +58,51 @@ class TermNodeTest extends MigrateSqlSourceTestCase {
'sticky' => '0',
'tnid' => '0',
'translate' => '0',
- ),
- );
- $this->databaseContents['term_data'] = array(
- array(
+ ],
+ ];
+ $tests[0]['source_data']['term_data'] = [
+ [
'tid' => '1',
'vid' => '3',
'name' => 'term 1 of vocabulary 3',
'description' => 'description of term 1 of vocabulary 3',
'weight' => '0',
- ),
- array(
+ ],
+ [
'tid' => '4',
'vid' => '3',
'name' => 'term 4 of vocabulary 3',
'description' => 'description of term 4 of vocabulary 3',
'weight' => '6',
- ),
- array(
+ ],
+ [
'tid' => '5',
'vid' => '3',
'name' => 'term 5 of vocabulary 3',
'description' => 'description of term 5 of vocabulary 3',
'weight' => '7',
- ),
- );
- parent::setUp();
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'nid' => 1,
+ 'vid' => 1,
+ 'type' => 'story',
+ 'tid' => [1, 4, 5],
+ ],
+ ];
+
+ // Set default value for expected count.
+ $tests[0]['expected_count'] = NULL;
+
+ // Set plugin configuration.
+ $tests[0]['configuration'] = [
+ 'vid' => 3,
+ ];
+
+ return $tests;
}
}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d6/VocabularyTest.php b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d6/VocabularyTest.php
new file mode 100644
index 000000000..a98b2985e
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d6/VocabularyTest.php
@@ -0,0 +1,108 @@
+ 1,
+ 'name' => 'Tags',
+ 'description' => 'Tags description.',
+ 'help' => 1,
+ 'relations' => 0,
+ 'hierarchy' => 0,
+ 'multiple' => 0,
+ 'required' => 0,
+ 'tags' => 1,
+ 'module' => 'taxonomy',
+ 'weight' => 0,
+ ],
+ [
+ 'vid' => 2,
+ 'name' => 'Categories',
+ 'description' => 'Categories description.',
+ 'help' => 1,
+ 'relations' => 1,
+ 'hierarchy' => 1,
+ 'multiple' => 0,
+ 'required' => 1,
+ 'tags' => 0,
+ 'module' => 'taxonomy',
+ 'weight' => 0,
+ ],
+ ];
+ $tests[0]['source_data']['vocabulary_node_types'] = [
+ [
+ 'vid' => 1,
+ 'type' => 'page',
+ ],
+ [
+ 'vid' => 1,
+ 'type' => 'article',
+ ],
+ [
+ 'vid' => 2,
+ 'type' => 'article',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'vid' => 1,
+ 'name' => 'Tags',
+ 'description' => 'Tags description.',
+ 'help' => 1,
+ 'relations' => 0,
+ 'hierarchy' => 0,
+ 'multiple' => 0,
+ 'required' => 0,
+ 'tags' => 1,
+ 'module' => 'taxonomy',
+ 'weight' => 0,
+ 'node_types' => ['page', 'article'],
+ 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+ ],
+ [
+ 'vid' => 2,
+ 'name' => 'Categories',
+ 'description' => 'Categories description.',
+ 'help' => 1,
+ 'relations' => 1,
+ 'hierarchy' => 1,
+ 'multiple' => 0,
+ 'required' => 1,
+ 'tags' => 0,
+ 'module' => 'taxonomy',
+ 'weight' => 0,
+ 'node_types' => ['article'],
+ 'cardinality' => 1,
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/VocabularyTest.php b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/VocabularyTest.php
new file mode 100644
index 000000000..4d1975ceb
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/VocabularyTest.php
@@ -0,0 +1,54 @@
+ 1,
+ 'name' => 'Tags',
+ 'description' => 'Tags description.',
+ 'hierarchy' => 0,
+ 'module' => 'taxonomy',
+ 'weight' => 0,
+ 'machine_name' => 'tags',
+ ],
+ [
+ 'vid' => 2,
+ 'name' => 'Categories',
+ 'description' => 'Categories description.',
+ 'hierarchy' => 1,
+ 'module' => 'taxonomy',
+ 'weight' => 0,
+ 'machine_name' => 'categories',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['taxonomy_vocabulary'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Unit/Migrate/TermSourceWithVocabularyFilterTest.php b/core/modules/taxonomy/tests/src/Unit/Migrate/TermSourceWithVocabularyFilterTest.php
deleted file mode 100644
index 67c46cb5f..000000000
--- a/core/modules/taxonomy/tests/src/Unit/Migrate/TermSourceWithVocabularyFilterTest.php
+++ /dev/null
@@ -1,25 +0,0 @@
-migrationConfiguration['source']['vocabulary'] = array(5);
- parent::setUp();
- $this->expectedResults = array_values(array_filter($this->expectedResults, function($result) {
- return $result['vid'] == 5;
- }));
- // We know there are two rows with vid == 5.
- $this->expectedCount = 2;
- }
-
-}
diff --git a/core/modules/taxonomy/tests/src/Unit/Migrate/TermTest.php b/core/modules/taxonomy/tests/src/Unit/Migrate/TermTest.php
deleted file mode 100644
index ad2706bb7..000000000
--- a/core/modules/taxonomy/tests/src/Unit/Migrate/TermTest.php
+++ /dev/null
@@ -1,12 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd6_taxonomy_term',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'tid' => 1,
- 'vid' => 5,
- 'name' => 'name value 1',
- 'description' => 'description value 1',
- 'weight' => 0,
- 'parent' => array(0),
- ),
- array(
- 'tid' => 2,
- 'vid' => 6,
- 'name' => 'name value 2',
- 'description' => 'description value 2',
- 'weight' => 0,
- 'parent' => array(0),
- ),
- array(
- 'tid' => 3,
- 'vid' => 6,
- 'name' => 'name value 3',
- 'description' => 'description value 3',
- 'weight' => 0,
- 'parent' => array(0),
- ),
- array(
- 'tid' => 4,
- 'vid' => 5,
- 'name' => 'name value 4',
- 'description' => 'description value 4',
- 'weight' => 1,
- 'parent' => array(1),
- ),
- array(
- 'tid' => 5,
- 'vid' => 6,
- 'name' => 'name value 5',
- 'description' => 'description value 5',
- 'weight' => 1,
- 'parent' => array(2),
- ),
- array(
- 'tid' => 6,
- 'vid' => 6,
- 'name' => 'name value 6',
- 'description' => 'description value 6',
- 'weight' => 0,
- 'parent' => array(3, 2),
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- foreach ($this->expectedResults as $k => $row) {
- foreach ($row['parent'] as $parent) {
- $this->databaseContents['term_hierarchy'][] = array(
- 'tid' => $row['tid'],
- 'parent' => $parent,
- );
- }
- unset($row['parent']);
- $this->databaseContents['term_data'][$k] = $row;
- }
- parent::setUp();
- }
-
-}
diff --git a/core/modules/taxonomy/tests/src/Unit/Migrate/d6/VocabularyTest.php b/core/modules/taxonomy/tests/src/Unit/Migrate/d6/VocabularyTest.php
deleted file mode 100644
index 9227b80cc..000000000
--- a/core/modules/taxonomy/tests/src/Unit/Migrate/d6/VocabularyTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
- 'test',
- 'source' => [
- 'plugin' => 'd6_vocabulary',
- ],
- ];
-
- protected $expectedResults = [
- [
- 'vid' => 1,
- 'name' => 'Tags',
- 'description' => 'Tags description.',
- 'help' => 1,
- 'relations' => 0,
- 'hierarchy' => 0,
- 'multiple' => 0,
- 'required' => 0,
- 'tags' => 1,
- 'module' => 'taxonomy',
- 'weight' => 0,
- 'node_types' => ['page', 'article'],
- 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
- ],
- [
- 'vid' => 2,
- 'name' => 'Categories',
- 'description' => 'Categories description.',
- 'help' => 1,
- 'relations' => 1,
- 'hierarchy' => 1,
- 'multiple' => 0,
- 'required' => 1,
- 'tags' => 0,
- 'module' => 'taxonomy',
- 'weight' => 0,
- 'node_types' => ['article'],
- 'cardinality' => 1,
- ],
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- foreach ($this->expectedResults as &$row) {
- foreach ($row['node_types'] as $type) {
- $this->databaseContents['vocabulary_node_types'][] = [
- 'type' => $type,
- 'vid' => $row['vid'],
- ];
- }
- unset($row['node_types']);
- }
- $this->databaseContents['vocabulary'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/taxonomy/tests/src/Unit/Migrate/d7/VocabularyTest.php b/core/modules/taxonomy/tests/src/Unit/Migrate/d7/VocabularyTest.php
deleted file mode 100644
index 88cd14467..000000000
--- a/core/modules/taxonomy/tests/src/Unit/Migrate/d7/VocabularyTest.php
+++ /dev/null
@@ -1,52 +0,0 @@
- 'test',
- 'source' => [
- 'plugin' => 'd7_vocabulary',
- ],
- ];
-
- protected $expectedResults = [
- [
- 'vid' => 1,
- 'name' => 'Tags',
- 'description' => 'Tags description.',
- 'hierarchy' => 0,
- 'module' => 'taxonomy',
- 'weight' => 0,
- 'machine_name' => 'tags',
- ],
- [
- 'vid' => 2,
- 'name' => 'Categories',
- 'description' => 'Categories description.',
- 'hierarchy' => 1,
- 'module' => 'taxonomy',
- 'weight' => 0,
- 'machine_name' => 'categories',
- ],
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['taxonomy_vocabulary'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerNodeTest.php b/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerNodeTest.php
new file mode 100644
index 000000000..0318b8e7b
--- /dev/null
+++ b/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerNodeTest.php
@@ -0,0 +1,42 @@
+ '2',
+ 'published' => '1',
+ 'changed' => '1421727536',
+ ]
+ ];
+
+ // The expected results are identical to the source data.
+ $tests[0]['expected_results'] = $tests[0]['database']['tracker_node'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerUserTest.php b/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerUserTest.php
new file mode 100644
index 000000000..ffcee93b5
--- /dev/null
+++ b/core/modules/tracker/tests/src/Kernel/Plugin/migrate/source/d7/TrackerUserTest.php
@@ -0,0 +1,43 @@
+ '1',
+ 'uid' => '2',
+ 'published' => '1',
+ 'changed' => '1421727536',
+ ]
+ ];
+
+ // The expected results are identical to the source data.
+ $tests[0]['expected_results'] = $tests[0]['database']['tracker_user'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/tracker/tests/src/Unit/Plugin/migrate/source/d7/TrackerNodeTest.php b/core/modules/tracker/tests/src/Unit/Plugin/migrate/source/d7/TrackerNodeTest.php
deleted file mode 100644
index 49f42400a..000000000
--- a/core/modules/tracker/tests/src/Unit/Plugin/migrate/source/d7/TrackerNodeTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
- 'test',
- 'source' => [
- 'plugin' => 'd7_tracker_node',
- ],
- ];
-
- protected $expectedResults = [
- [
- 'nid' => '2',
- 'published' => '1',
- 'changed' => '1421727536',
- ]
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['tracker_node'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/tracker/tests/src/Unit/Plugin/migrate/source/d7/TrackerUserTest.php b/core/modules/tracker/tests/src/Unit/Plugin/migrate/source/d7/TrackerUserTest.php
deleted file mode 100644
index 847869e2f..000000000
--- a/core/modules/tracker/tests/src/Unit/Plugin/migrate/source/d7/TrackerUserTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
- 'test',
- 'source' => [
- 'plugin' => 'd7_tracker_user',
- ],
- ];
-
- protected $expectedResults = [
- [
- 'nid' => '1',
- 'uid' => '2',
- 'published' => '1',
- 'changed' => '1421727536',
- ]
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['tracker_user'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/user/migration_templates/d6_user.yml b/core/modules/user/migration_templates/d6_user.yml
index bf6ec2ade..fa6326e93 100644
--- a/core/modules/user/migration_templates/d6_user.yml
+++ b/core/modules/user/migration_templates/d6_user.yml
@@ -16,7 +16,18 @@ process:
timezone:
plugin: user_update_7002
source: timezone
- preferred_langcode: language
+ langcode:
+ plugin: user_langcode
+ source: language
+ fallback_to_site_default: false
+ preferred_langcode:
+ plugin: user_langcode
+ source: language
+ fallback_to_site_default: true
+ preferred_admin_langcode:
+ plugin: user_langcode
+ source: language
+ fallback_to_site_default: true
init: init
roles:
plugin: migration
@@ -34,6 +45,8 @@ migration_dependencies:
required:
- d6_user_role
optional:
+ - language
+ - default_language
- d6_user_picture_file
- user_picture_entity_display
- user_picture_entity_form_display
diff --git a/core/modules/user/migration_templates/d6_user_role.yml b/core/modules/user/migration_templates/d6_user_role.yml
index a97d5194f..1d224f489 100644
--- a/core/modules/user/migration_templates/d6_user_role.yml
+++ b/core/modules/user/migration_templates/d6_user_role.yml
@@ -9,11 +9,6 @@ process:
-
plugin: machine_name
source: name
- -
- plugin: dedupe_entity
- entity_type: user_role
- field: id
- length: 32
-
plugin: user_update_8002
label: name
@@ -26,15 +21,15 @@ process:
'use PHP for block visibility': 'use PHP for settings'
'administer site-wide contact form': 'administer contact forms'
'post comments without approval': 'skip comment approval'
- 'edit own blog entries' : 'edit own blog content'
- 'edit any blog entry' : 'edit any blog content'
- 'delete own blog entries' : 'delete own blog content'
- 'delete any blog entry' : 'delete any blog content'
- 'create forum topics' : 'create forum content'
- 'delete any forum topic' : 'delete any forum content'
- 'delete own forum topics' : 'delete own forum content'
- 'edit any forum topic' : 'edit any forum content'
- 'edit own forum topics' : 'edit own forum content'
+ 'edit own blog entries': 'edit own blog content'
+ 'edit any blog entry': 'edit any blog content'
+ 'delete own blog entries': 'delete own blog content'
+ 'delete any blog entry': 'delete any blog content'
+ 'create forum topics': 'create forum content'
+ 'delete any forum topic': 'delete any forum content'
+ 'delete own forum topics': 'delete own forum content'
+ 'edit any forum topic': 'edit any forum content'
+ 'edit own forum topics': 'edit own forum content'
- plugin: system_update_7000
- plugin: node_update_7008
- plugin: flatten
diff --git a/core/modules/user/migration_templates/d7_user.yml b/core/modules/user/migration_templates/d7_user.yml
index d68fddff4..b72e8c728 100644
--- a/core/modules/user/migration_templates/d7_user.yml
+++ b/core/modules/user/migration_templates/d7_user.yml
@@ -15,9 +15,18 @@ process:
login: login
status: status
timezone: timezone
- langcode: language
- preferred_langcode: language
- preferred_admin_langcode: language
+ langcode:
+ plugin: user_langcode
+ source: language
+ fallback_to_site_default: false
+ preferred_langcode:
+ plugin: user_langcode
+ source: language
+ fallback_to_site_default: true
+ preferred_admin_langcode:
+ plugin: user_langcode
+ source: language
+ fallback_to_site_default: true
init: init
roles:
plugin: migration
@@ -38,6 +47,8 @@ migration_dependencies:
- d7_user_role
optional:
- d7_file
+ - language
+ - default_language
- user_picture_field_instance
- user_picture_entity_display
- user_picture_entity_form_display
diff --git a/core/modules/user/migration_templates/d7_user_role.yml b/core/modules/user/migration_templates/d7_user_role.yml
index 68c0d1168..5f887574e 100644
--- a/core/modules/user/migration_templates/d7_user_role.yml
+++ b/core/modules/user/migration_templates/d7_user_role.yml
@@ -9,11 +9,6 @@ process:
-
plugin: machine_name
source: name
- -
- plugin: dedupe_entity
- entity_type: user_role
- field: id
- length: 32
-
plugin: user_update_8002
label: name
@@ -26,15 +21,15 @@ process:
'use PHP for block visibility': 'use PHP for settings'
'administer site-wide contact form': 'administer contact forms'
'post comments without approval': 'skip comment approval'
- 'edit own blog entries' : 'edit own blog content'
- 'edit any blog entry' : 'edit any blog content'
- 'delete own blog entries' : 'delete own blog content'
- 'delete any blog entry' : 'delete any blog content'
- 'create forum topics' : 'create forum content'
- 'delete any forum topic' : 'delete any forum content'
- 'delete own forum topics' : 'delete own forum content'
- 'edit any forum topic' : 'edit any forum content'
- 'edit own forum topics' : 'edit own forum content'
+ 'edit own blog entries': 'edit own blog content'
+ 'edit any blog entry': 'edit any blog content'
+ 'delete own blog entries': 'delete own blog content'
+ 'delete any blog entry': 'delete any blog content'
+ 'create forum topics': 'create forum content'
+ 'delete any forum topic': 'delete any forum content'
+ 'delete own forum topics': 'delete own forum content'
+ 'edit any forum topic': 'edit any forum content'
+ 'edit own forum topics': 'edit own forum content'
- plugin: flatten
weight: weight
destination:
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 030b9a933..6a3d6b161 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -416,6 +416,9 @@ class User extends ContentEntityBase implements UserInterface {
static::$anonymousUser = new $class([
'uid' => [LanguageInterface::LANGCODE_DEFAULT => 0],
'name' => [LanguageInterface::LANGCODE_DEFAULT => ''],
+ // Explicitly set the langcode to ensure that field definitions do not
+ // need to be fetched to figure out a default.
+ 'langcode' => [LanguageInterface::LANGCODE_DEFAULT => LanguageInterface::LANGCODE_NOT_SPECIFIED]
], $entity_type->id());
}
return clone static::$anonymousUser;
diff --git a/core/modules/user/src/Plugin/migrate/process/UserLangcode.php b/core/modules/user/src/Plugin/migrate/process/UserLangcode.php
new file mode 100644
index 000000000..80f20783b
--- /dev/null
+++ b/core/modules/user/src/Plugin/migrate/process/UserLangcode.php
@@ -0,0 +1,86 @@
+languageManager = $language_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $configuration,
+ $plugin_id,
+ $plugin_definition,
+ $container->get('language_manager')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+ if (!isset($this->configuration['fallback_to_site_default'])) {
+ $this->configuration['fallback_to_site_default'] = TRUE;
+ }
+
+ // If the user's language is empty, it means the locale module was not
+ // installed, so the user's langcode should be English and the user's
+ // preferred_langcode and preferred_admin_langcode should fallback to the
+ // default language.
+ if (empty($value)) {
+ if ($this->configuration['fallback_to_site_default']) {
+ return $this->languageManager->getDefaultLanguage()->getId();
+ }
+ else {
+ return 'en';
+ }
+ }
+ // If the user's language does not exists, use the default language.
+ elseif ($this->languageManager->getLanguage($value) === NULL) {
+ return $this->languageManager->getDefaultLanguage()->getId();
+ }
+
+ // If the langcode is a valid one, just return it.
+ return $value;
+ }
+
+}
diff --git a/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php b/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php
index 0970b8737..0ccbbeb51 100644
--- a/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php
+++ b/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php
@@ -21,7 +21,7 @@ class ProfileFieldValues extends DrupalSqlBase {
public function query() {
$query = $this->select('profile_values', 'pv')
->distinct()
- ->fields('pv', array('fid', 'uid'));
+ ->fields('pv', array('uid'));
return $query;
}
diff --git a/core/modules/user/src/Plugin/migrate/source/d6/Role.php b/core/modules/user/src/Plugin/migrate/source/d6/Role.php
index f7bac99c2..2357c5c86 100644
--- a/core/modules/user/src/Plugin/migrate/source/d6/Role.php
+++ b/core/modules/user/src/Plugin/migrate/source/d6/Role.php
@@ -69,7 +69,15 @@ class Role extends DrupalSqlBase {
->condition('rid', $rid)
->execute()
->fetchField();
- $row->setSourceProperty('permissions', explode(', ', $permissions));
+
+ // If a role has no permissions then set to an empty array. The role will
+ // be migrated and given the default D8 permissions.
+ if ($permissions) {
+ $row->setSourceProperty('permissions', explode(', ', $permissions));
+ }
+ else {
+ $row->setSourceProperty('permissions', []);
+ }
if (isset($this->filterPermissions[$rid])) {
$row->setSourceProperty("filter_permissions:$rid", $this->filterPermissions[$rid]);
}
diff --git a/core/modules/user/src/Plugin/migrate/source/d6/User.php b/core/modules/user/src/Plugin/migrate/source/d6/User.php
index 1260ca75e..56f68bf8c 100644
--- a/core/modules/user/src/Plugin/migrate/source/d6/User.php
+++ b/core/modules/user/src/Plugin/migrate/source/d6/User.php
@@ -100,6 +100,7 @@ class User extends DrupalSqlBase {
'name' => $this->t('Username'),
'pass' => $this->t('Password'),
'mail' => $this->t('Email address'),
+ 'theme' => $this->t('Theme'),
'signature' => $this->t('Signature'),
'signature_format' => $this->t('Signature format'),
'created' => $this->t('Registered timestamp'),
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php
index 978c0bf45..e734517a5 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php
@@ -12,6 +12,11 @@ use Drupal\user\Entity\User;
*/
class MigrateUserProfileValuesTest extends MigrateDrupal6TestBase {
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['language'];
+
/**
* {@inheritdoc}
*/
@@ -19,6 +24,7 @@ class MigrateUserProfileValuesTest extends MigrateDrupal6TestBase {
parent::setUp();
$this->executeMigrations([
+ 'language',
'user_profile_field',
'user_profile_field_instance',
'user_profile_entity_display',
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php
index d3f49ccfc..a52b6a771 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php
@@ -3,7 +3,9 @@
namespace Drupal\Tests\user\Kernel\Migrate\d6;
use Drupal\user\Entity\Role;
+use Drupal\user\RoleInterface;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
+use Drupal\migrate\Plugin\MigrateIdMapInterface;
/**
* Upgrade user roles to user.role.*.yml.
@@ -21,29 +23,69 @@ class MigrateUserRoleTest extends MigrateDrupal6TestBase {
}
/**
- * Tests user role migration.
+ * Helper function to perform assertions on a user role.
+ *
+ * @param string $id
+ * The role ID.
+ * @param string[] $permissions
+ * An array of user permissions.
+ * @param int $lookupId
+ * The original numeric ID of the role in the source database.
+ * @param \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map
+ * The map table plugin.
*/
- public function testUserRole() {
- /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
- $id_map = $this->getMigration('d6_user_role')->getIdMap();
- $rid = 'anonymous';
- $anonymous = Role::load($rid);
- $this->assertSame($rid, $anonymous->id());
- $this->assertSame(array('migrate test anonymous permission', 'use text format filtered_html'), $anonymous->getPermissions());
- $this->assertSame(array($rid), $id_map->lookupDestinationId(array(1)));
- $rid = 'authenticated';
- $authenticated = Role::load($rid);
- $this->assertSame($rid, $authenticated->id());
- $this->assertSame(array('migrate test authenticated permission', 'use text format filtered_html'), $authenticated->getPermissions());
- $this->assertSame(array($rid), $id_map->lookupDestinationId(array(2)));
- $rid = 'migrate_test_role_1';
- $migrate_test_role_1 = Role::load($rid);
- $this->assertSame($rid, $migrate_test_role_1->id());
- $this->assertSame(array('migrate test role 1 test permission', 'use text format full_html', 'use text format php_code'), $migrate_test_role_1->getPermissions());
- $this->assertSame(array($rid), $id_map->lookupDestinationId(array(3)));
- $rid = 'migrate_test_role_2';
- $migrate_test_role_2 = Role::load($rid);
- $this->assertSame(array(
+ protected function assertRole($id, array $permissions, $lookupId, MigrateIdMapInterface $id_map) {
+ /** @var \Drupal\user\RoleInterface $role */
+ $role = Role::load($id);
+ $this->assertInstanceOf(RoleInterface::class, $role);
+ $this->assertSame($permissions, $role->getPermissions());
+ $this->assertSame([[$id]], $id_map->lookupDestinationIds(['rid' => $lookupId]));
+ }
+
+ /**
+ * Helper function to test the migration of the user roles. The user roles
+ * will be re-imported and the tests here will be repeated.
+ *
+ * @param \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map
+ * The map table plugin.
+ */
+ protected function assertRoles(MigrateIdMapInterface $id_map) {
+
+ // The permissions for each role are found in the two tables in the Drupal 6
+ // source database. One is the permission table and the other is the
+ // filter_format table.
+ $permissions = [
+ // From permission table.
+ 'access content',
+ 'migrate test anonymous permission',
+ // From filter_format tables.
+ 'use text format filtered_html'
+ ];
+ $this->assertRole('anonymous', $permissions, 1, $id_map);
+
+ $permissions = [
+ // From permission table.
+ 'access comments',
+ 'access content',
+ 'post comments',
+ 'skip comment approval',
+ 'migrate test authenticated permission',
+ // From filter_format.
+ 'use text format filtered_html',
+ ];
+ $this->assertRole('authenticated', $permissions, 2, $id_map);
+
+ $permissions = [
+ // From permission table.
+ 'migrate test role 1 test permission',
+ // From filter format.
+ 'use text format full_html',
+ 'use text format php_code'
+ ];
+ $this->assertRole('migrate_test_role_1', $permissions, 3, $id_map);
+
+ $permissions = [
+ // From permission table.
'migrate test role 2 test permission',
'use PHP for settings',
'administer contact forms',
@@ -59,14 +101,66 @@ class MigrateUserRoleTest extends MigrateDrupal6TestBase {
'edit own forum content',
'administer nodes',
'access content overview',
+ // From filter format.
'use text format php_code',
- ), $migrate_test_role_2->getPermissions());
- $this->assertSame($rid, $migrate_test_role_2->id());
- $this->assertSame(array($rid), $id_map->lookupDestinationId(array(4)));
- $rid = 'migrate_test_role_3_that_is_long';
- $migrate_test_role_3 = Role::load($rid);
- $this->assertSame($rid, $migrate_test_role_3->id());
- $this->assertSame(array($rid), $id_map->lookupDestinationId(array(5)));
+ ];
+ $this->assertRole('migrate_test_role_2', $permissions, 4, $id_map);
+
+ // The only permission for this role is a filter format.
+ $permissions = ['use text format php_code'];
+ $this->assertRole('migrate_test_role_3_that_is_longer_than_thirty_two_characters', $permissions, 5, $id_map);
+ }
+
+ /**
+ * Tests user role migration.
+ */
+ public function testUserRole() {
+ $id_map = $this->getMigration('d6_user_role')->getIdMap();
+ $this->assertRoles($id_map);
+
+ // Test there are no duplicated roles.
+ $roles = [
+ 'anonymous1',
+ 'authenticated1',
+ 'administrator1',
+ 'migrate_test_role_11',
+ 'migrate_test_role_21',
+ 'migrate_test_role_3_that_is_longer_than_thirty_two_characters1'
+ ];
+ $this->assertEmpty(Role::loadMultiple($roles));
+
+ // Remove the map row for the migrate_test_role_1 role and rerun the
+ // migration. This will re-import the migrate_test_role_1 role migration
+ // again.
+ $this->sourceDatabase->insert('role')
+ ->fields([
+ 'rid' => 6,
+ 'name' => 'migrate test role 4',
+ ])
+ ->execute();
+ $this->sourceDatabase->insert('permission')
+ ->fields([
+ 'pid' => 7,
+ 'rid' => 6,
+ 'perm' => 'access content',
+ 'tid' => 0,
+ ])
+ ->execute();
+
+ $id_map->delete(['rid' => 3]);
+
+ $this->executeMigration('d6_user_role');
+
+ // Test there are no duplicated roles.
+ $roles[] = 'migrate_test_role_41';
+ $this->assertEmpty(Role::loadMultiple($roles));
+
+ // Test that the existing roles have not changed.
+ $this->assertRoles($id_map);
+
+ // Test the migration of the new role, migrate_test_role_4.
+ $permissions = ['access content'];
+ $this->assertRole('migrate_test_role_4', $permissions, 6, $id_map);
}
}
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php
index 931783406..33952d177 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php
@@ -19,6 +19,11 @@ class MigrateUserTest extends MigrateDrupal6TestBase {
use FileMigrationTestTrait;
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = ['language'];
+
/**
* {@inheritdoc}
*/
@@ -60,6 +65,7 @@ class MigrateUserTest extends MigrateDrupal6TestBase {
file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-2.jpg'));
$file->save();
+ $this->executeMigration('language');
$this->migrateUsers();
}
@@ -91,28 +97,47 @@ class MigrateUserTest extends MigrateDrupal6TestBase {
/** @var \Drupal\user\UserInterface $user */
$user = User::load($source->uid);
- $this->assertIdentical($source->uid, $user->id());
- $this->assertIdentical($source->name, $user->label());
- $this->assertIdentical($source->mail, $user->getEmail());
- $this->assertIdentical($source->created, $user->getCreatedTime());
- $this->assertIdentical($source->access, $user->getLastAccessedTime());
- $this->assertIdentical($source->login, $user->getLastLoginTime());
+ $this->assertSame($source->uid, $user->id());
+ $this->assertSame($source->name, $user->label());
+ $this->assertSame($source->mail, $user->getEmail());
+ $this->assertSame($source->created, $user->getCreatedTime());
+ $this->assertSame($source->access, $user->getLastAccessedTime());
+ $this->assertSame($source->login, $user->getLastLoginTime());
$is_blocked = $source->status == 0;
- $this->assertIdentical($is_blocked, $user->isBlocked());
+ $this->assertSame($is_blocked, $user->isBlocked());
+ $expected_timezone_name = $source->timezone_name ?: $this->config('system.date')->get('timezone.default');
+ $this->assertSame($expected_timezone_name, $user->getTimeZone());
+ $this->assertSame($source->init, $user->getInitialEmail());
+ $this->assertSame($roles, $user->getRoles());
+
+ // Ensure the user's langcode, preferred_langcode and
+ // preferred_admin_langcode are valid.
// $user->getPreferredLangcode() might fallback to default language if the
// user preferred language is not configured on the site. We just want to
// test if the value was imported correctly.
- $this->assertIdentical($source->language, $user->preferred_langcode->value);
- $expected_timezone_name = $source->timezone_name ?: $this->config('system.date')->get('timezone.default');
- $this->assertIdentical($expected_timezone_name, $user->getTimeZone());
- $this->assertIdentical($source->init, $user->getInitialEmail());
- $this->assertIdentical($roles, $user->getRoles());
+ $language_manager = $this->container->get('language_manager');
+ $default_langcode = $language_manager->getDefaultLanguage()->getId();
+ if (empty($source->language)) {
+ $this->assertSame('en', $user->langcode->value);
+ $this->assertSame($default_langcode, $user->preferred_langcode->value);
+ $this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
+ }
+ elseif ($language_manager->getLanguage($source->language) === NULL) {
+ $this->assertSame($default_langcode, $user->langcode->value);
+ $this->assertSame($default_langcode, $user->preferred_langcode->value);
+ $this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
+ }
+ else {
+ $this->assertSame($source->language, $user->langcode->value);
+ $this->assertSame($source->language, $user->preferred_langcode->value);
+ $this->assertSame($source->language, $user->preferred_admin_langcode->value);
+ }
// We have one empty picture in the data so don't try load that.
if (!empty($source->picture)) {
// Test the user picture.
$file = File::load($user->user_picture->target_id);
- $this->assertIdentical(basename($source->picture), $file->getFilename());
+ $this->assertSame(basename($source->picture), $file->getFilename());
}
else {
// Ensure the user does not have a picture.
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserRoleTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserRoleTest.php
index de0d0b96a..edd5e3c03 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserRoleTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserRoleTest.php
@@ -56,6 +56,50 @@ class MigrateUserRoleTest extends MigrateDrupal7TestBase {
$this->assertEntity('anonymous', 'anonymous user', 1);
$this->assertEntity('authenticated', 'authenticated user', 2);
$this->assertEntity('administrator', 'administrator', 3);
+ // Test there are no duplicated roles.
+ $roles = [
+ 'anonymous1',
+ 'authenticated1',
+ 'administrator1',
+ ];
+ $this->assertEmpty(Role::loadMultiple($roles));
+
+ // Remove the map row for the administrator role and rerun the migration.
+ // This will re-import the administrator role again.
+ $id_map = $this->getMigration('d7_user_role')->getIdMap();
+ $id_map->delete(['rid' => 3]);
+
+ $this->sourceDatabase->insert('role')
+ ->fields([
+ 'rid' => 4,
+ 'name' => 'test role',
+ 'weight' => 10,
+ ])
+ ->execute();
+ $this->sourceDatabase->insert('role_permission')
+ ->fields([
+ 'rid' => 4,
+ 'permission' => 'access content',
+ 'module' => 'node',
+ ])
+ ->execute();
+ $this->executeMigration('d7_user_role');
+
+ // Test there are no duplicated roles.
+ $roles = [
+ 'anonymous1',
+ 'authenticated1',
+ 'administrator1',
+ ];
+ $this->assertEmpty(Role::loadMultiple($roles));
+
+ // Test that the existing roles have not changed.
+ $this->assertEntity('administrator', 'administrator', 3);
+ $this->assertEntity('anonymous', 'anonymous user', 1);
+ $this->assertEntity('authenticated', 'authenticated user', 2);
+
+ // Test the migration of the new role, test role.
+ $this->assertEntity('test_role', 'test role', 4);
}
}
diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php
index 33bc5fe8b..c21856e5b 100644
--- a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php
+++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php
@@ -3,6 +3,7 @@
namespace Drupal\Tests\user\Kernel\Migrate\d7;
use Drupal\comment\Entity\CommentType;
+use Drupal\Core\Database\Database;
use Drupal\node\Entity\NodeType;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
@@ -25,6 +26,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
'datetime',
'file',
'image',
+ 'language',
'link',
'node',
'system',
@@ -49,6 +51,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
$this->createType('test_content_type');
Vocabulary::create(['vid' => 'test_vocabulary'])->save();
$this->executeMigrations([
+ 'language',
'user_picture_field',
'user_picture_field_instance',
'd7_user_role',
@@ -88,6 +91,8 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
* The user's email address.
* @param string $password
* The password for this user.
+ * @param int $created
+ * The user's creation time.
* @param int $access
* The last access time.
* @param int $login
@@ -96,37 +101,59 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
* Whether or not the account is blocked.
* @param string $langcode
* The user account's language code.
+ * @param string $timezone
+ * The user account's timezone name.
* @param string $init
* The user's initial email address.
* @param string[] $roles
* Role IDs the user account is expected to have.
- * @param bool $has_picture
- * Whether the user is expected to have a picture attached.
* @param int $field_integer
* The value of the integer field.
+ * @param bool $has_picture
+ * Whether the user is expected to have a picture attached.
*/
- protected function assertEntity($id, $label, $mail, $password, $access, $login, $blocked, $langcode, $init, array $roles = [RoleInterface::AUTHENTICATED_ID], $has_picture = FALSE, $field_integer = NULL) {
+ protected function assertEntity($id, $label, $mail, $password, $created, $access, $login, $blocked, $langcode, $timezone, $init, $roles, $field_integer, $has_picture = FALSE) {
/** @var \Drupal\user\UserInterface $user */
$user = User::load($id);
$this->assertTrue($user instanceof UserInterface);
- $this->assertIdentical($label, $user->label());
- $this->assertIdentical($mail, $user->getEmail());
- $this->assertIdentical($access, $user->getLastAccessedTime());
- $this->assertIdentical($login, $user->getLastLoginTime());
- $this->assertIdentical($blocked, $user->isBlocked());
+ $this->assertSame($label, $user->label());
+ $this->assertSame($mail, $user->getEmail());
+ $this->assertSame($password, $user->getPassword());
+ $this->assertSame($created, $user->getCreatedTime());
+ $this->assertSame($access, $user->getLastAccessedTime());
+ $this->assertSame($login, $user->getLastLoginTime());
+ $this->assertNotSame($blocked, $user->isBlocked());
+
+ // Ensure the user's langcode, preferred_langcode and
+ // preferred_admin_langcode are valid.
// $user->getPreferredLangcode() might fallback to default language if the
// user preferred language is not configured on the site. We just want to
// test if the value was imported correctly.
- $this->assertIdentical($langcode, $user->langcode->value);
- $this->assertIdentical($langcode, $user->preferred_langcode->value);
- $this->assertIdentical($langcode, $user->preferred_admin_langcode->value);
- $this->assertIdentical($init, $user->getInitialEmail());
- $this->assertIdentical($roles, $user->getRoles());
- $this->assertIdentical($has_picture, !$user->user_picture->isEmpty());
- $this->assertIdentical($password, $user->getPassword());
+ $language_manager = $this->container->get('language_manager');
+ $default_langcode = $language_manager->getDefaultLanguage()->getId();
+ if ($langcode == '') {
+ $this->assertSame('en', $user->langcode->value);
+ $this->assertSame($default_langcode, $user->preferred_langcode->value);
+ $this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
+ }
+ elseif ($language_manager->getLanguage($langcode) === NULL) {
+ $this->assertSame($default_langcode, $user->langcode->value);
+ $this->assertSame($default_langcode, $user->preferred_langcode->value);
+ $this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
+ }
+ else {
+ $this->assertSame($langcode, $user->langcode->value);
+ $this->assertSame($langcode, $user->preferred_langcode->value);
+ $this->assertSame($langcode, $user->preferred_admin_langcode->value);
+ }
+
+ $this->assertSame($timezone, $user->getTimeZone());
+ $this->assertSame($init, $user->getInitialEmail());
+ $this->assertSame($roles, $user->getRoles());
+ $this->assertSame($has_picture, !$user->user_picture->isEmpty());
if (!is_null($field_integer)) {
$this->assertTrue($user->hasField('field_integer'));
- $this->assertEquals($field_integer, $user->field_integer->value);
+ $this->assertEquals($field_integer[0], $user->field_integer->value);
}
}
@@ -134,22 +161,65 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
* Tests the Drupal 7 user to Drupal 8 migration.
*/
public function testUser() {
- $password = '$S$DGFZUE.FhrXbe4y52eC7p0ZVRGD/gOPtVctDlmC89qkujnBokAlJ';
- $this->assertEntity(2, 'Odo', 'odo@local.host', $password, '0', '0', FALSE, 'en', 'odo@local.host', [RoleInterface::AUTHENTICATED_ID], FALSE, 99);
+ $users = Database::getConnection('default', 'migrate')
+ ->select('users', 'u')
+ ->fields('u')
+ ->condition('uid', 1, '>')
+ ->execute()
+ ->fetchAll();
- // Ensure that the user can authenticate.
- $this->assertEquals(2, \Drupal::service('user.auth')->authenticate('Odo', 'a password'));
- // After authenticating the password will be rehashed because the password
- // stretching iteration count has changed from 15 in Drupal 7 to 16 in
- // Drupal 8.
- $user = User::load(2);
- $rehash = $user->getPassword();
- $this->assertNotEquals($password, $rehash);
+ foreach ($users as $source) {
+ $rids = Database::getConnection('default', 'migrate')
+ ->select('users_roles', 'ur')
+ ->fields('ur', array('rid'))
+ ->condition('ur.uid', $source->uid)
+ ->execute()
+ ->fetchCol();
+ $roles = array(RoleInterface::AUTHENTICATED_ID);
+ $id_map = $this->getMigration('d7_user_role')->getIdMap();
+ foreach ($rids as $rid) {
+ $role = $id_map->lookupDestinationId(array($rid));
+ $roles[] = reset($role);
+ }
- // Authenticate again and there should be no re-hash.
- $this->assertEquals(2, \Drupal::service('user.auth')->authenticate('Odo', 'a password'));
- $user = User::load(2);
- $this->assertEquals($rehash, $user->getPassword());
+ $field_integer = Database::getConnection('default', 'migrate')
+ ->select('field_data_field_integer', 'fi')
+ ->fields('fi', array('field_integer_value'))
+ ->condition('fi.entity_id', $source->uid)
+ ->execute()
+ ->fetchCol();
+ $field_integer = !empty($field_integer) ? $field_integer : NULL;
+
+ $this->assertEntity(
+ $source->uid,
+ $source->name,
+ $source->mail,
+ $source->pass,
+ $source->created,
+ $source->access,
+ $source->login,
+ $source->status,
+ $source->language,
+ $source->timezone,
+ $source->init,
+ $roles,
+ $field_integer
+ );
+
+ // Ensure that the user can authenticate.
+ $this->assertEquals($source->uid, $this->container->get('user.auth')->authenticate($source->name, 'a password'));
+ // After authenticating the password will be rehashed because the password
+ // stretching iteration count has changed from 15 in Drupal 7 to 16 in
+ // Drupal 8.
+ $user = User::load($source->uid);
+ $rehash = $user->getPassword();
+ $this->assertNotEquals($source->pass, $rehash);
+
+ // Authenticate again and there should be no re-hash.
+ $this->assertEquals($source->uid, $this->container->get('user.auth')->authenticate($source->name, 'a password'));
+ $user = User::load($source->uid);
+ $this->assertEquals($rehash, $user->getPassword());
+ }
}
}
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/ProfileFieldTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/ProfileFieldTest.php
new file mode 100644
index 000000000..c32118571
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/ProfileFieldTest.php
@@ -0,0 +1,129 @@
+ [],
+ 'expected_data' => [],
+ ],
+ ];
+
+ $profile_fields = [
+ [
+ 'fid' => 1,
+ 'title' => 'First name',
+ 'name' => 'profile_first_name',
+ 'explanation' => 'First name user',
+ 'category' => 'profile',
+ 'page' => '',
+ 'type' => 'textfield',
+ 'weight' => 0,
+ 'required' => 1,
+ 'register' => 0,
+ 'visibility' => 2,
+ 'autocomplete' => 0,
+ 'options' => '',
+ ],
+ [
+ 'fid' => 2,
+ 'title' => 'Last name',
+ 'name' => 'profile_last_name',
+ 'explanation' => 'Last name user',
+ 'category' => 'profile',
+ 'page' => '',
+ 'type' => 'textfield',
+ 'weight' => 0,
+ 'required' => 0,
+ 'register' => 0,
+ 'visibility' => 2,
+ 'autocomplete' => 0,
+ 'options' => '',
+ ],
+ [
+ 'fid' => 3,
+ 'title' => 'Policy',
+ 'name' => 'profile_policy',
+ 'explanation' => 'A checkbox that say if you accept policy of website',
+ 'category' => 'profile',
+ 'page' => '',
+ 'type' => 'checkbox',
+ 'weight' => 0,
+ 'required' => 1,
+ 'register' => 1,
+ 'visibility' => 2,
+ 'autocomplete' => 0,
+ 'options' => '',
+ ],
+ [
+ 'fid' => 4,
+ 'title' => 'Color',
+ 'name' => 'profile_color',
+ 'explanation' => 'A selection that allows user to select a color',
+ 'category' => 'profile',
+ 'page' => '',
+ 'type' => 'selection',
+ 'weight' => 0,
+ 'required' => 0,
+ 'register' => 0,
+ 'visibility' => 2,
+ 'autocomplete' => 0,
+ 'options' => "red\nblue\ngreen",
+ ],
+ ];
+
+ $tests[0]['source_data']['profile_fields'] = $profile_fields;
+
+ // Profile values are merged with pre-set options of a "selection" field.
+ $tests[0]['source_data']['profile_values'] = [
+ [
+ 'fid' => 4,
+ 'uid' => 1,
+ 'value' => 'yellow',
+ ]
+ ];
+
+ // Expected options are:
+ // for "checkbox" fields - array with NULL options
+ // for "selection" fields - options in both keys and values
+ $expected_field_options = [
+ '',
+ '',
+ [NULL, NULL],
+ [
+ 'red' => 'red',
+ 'blue' => 'blue',
+ 'green' => 'green',
+ 'yellow' => 'yellow',
+ ]
+ ];
+
+ $tests[0]['expected_data'] = $profile_fields;
+
+ foreach ($tests[0]['expected_data'] as $delta => $row) {
+ $tests[0]['expected_data'][$delta]['options'] = $expected_field_options[$delta];
+ }
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/UserPictureInstanceTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/UserPictureInstanceTest.php
new file mode 100644
index 000000000..d9e77edde
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/UserPictureInstanceTest.php
@@ -0,0 +1,55 @@
+ 'file_directory',
+ 'value' => serialize(NULL),
+ ],
+ [
+ 'name' => 'user_picture_file_size',
+ 'value' => serialize(128),
+ ],
+ [
+ 'name' => 'user_picture_dimensions',
+ 'value' => serialize('128x128'),
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'id' => '',
+ 'file_directory' => 'pictures',
+ 'max_filesize' => '128KB',
+ 'max_resolution' => '128x128',
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/user/tests/src/Unit/Migrate/d6/ProfileFieldValuesTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/ProfileFieldValuesTest.php
similarity index 55%
rename from core/modules/user/tests/src/Unit/Migrate/d6/ProfileFieldValuesTest.php
rename to core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/ProfileFieldValuesTest.php
index ce20e669c..26c2ed55f 100644
--- a/core/modules/user/tests/src/Unit/Migrate/d6/ProfileFieldValuesTest.php
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/ProfileFieldValuesTest.php
@@ -1,57 +1,44 @@
'test',
- 'source' => array(
- 'plugin' => 'd6_profile_field_values',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'fid' => '8',
- 'profile_color' => array('red'),
- 'uid' => '2',
- ),
- array(
- 'fid' => '9',
- 'profile_biography' => array('Lorem ipsum dolor sit amet...'),
- 'uid' => '2',
- ),
- );
+class ProfileFieldValuesTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
- protected function setUp() {
- $this->databaseContents['profile_values'] = array(
- array(
+ public static $modules = ['user', 'migrate_drupal'];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function providerSource() {
+ $tests = [];
+
+ // The source data.
+ $tests[0]['source_data']['profile_values'] = [
+ [
'fid' => '8',
'uid' => '2',
'value' => 'red',
- ),
- array(
+ ],
+ [
'fid' => '9',
'uid' => '2',
'value' => 'Lorem ipsum dolor sit amet...',
- ),
- );
- $this->databaseContents['profile_fields'] = array(
- array(
+ ],
+ ];
+
+ $tests[0]['source_data']['profile_fields'] = [
+ [
'fid' => '8',
'title' => 'Favorite color',
'name' => 'profile_color',
@@ -65,8 +52,8 @@ class ProfileFieldValuesTest extends MigrateSqlSourceTestCase {
'visibility' => '2',
'autocomplete' => '1',
'options' => '',
- ),
- array(
+ ],
+ [
'fid' => '9',
'title' => 'Biography',
'name' => 'profile_biography',
@@ -80,9 +67,19 @@ class ProfileFieldValuesTest extends MigrateSqlSourceTestCase {
'visibility' => '2',
'autocomplete' => '0',
'options' => '',
- ),
- );
- parent::setUp();
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'profile_color' => ['red'],
+ 'profile_biography' => ['Lorem ipsum dolor sit amet...'],
+ 'uid' => '2',
+ ],
+ ];
+
+ return $tests;
}
}
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/RoleTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/RoleTest.php
new file mode 100644
index 000000000..1a841d32d
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/RoleTest.php
@@ -0,0 +1,87 @@
+ [],
+ 'expected_data' => [],
+ ],
+ ];
+
+ $roles = [
+ [
+ 'rid' => 1,
+ 'name' => 'anonymous user',
+ 'permissions' => [
+ 'access content',
+ ],
+ ],
+ [
+ 'rid' => 2,
+ 'name' => 'authenticated user',
+ 'permissions' => [
+ 'access comments',
+ 'access content',
+ 'post comments',
+ 'post comments without approval',
+ ],
+ ],
+ [
+ 'rid' => 3,
+ 'name' => 'administrator',
+ 'permissions' => [
+ 'access comments',
+ 'administer comments',
+ 'post comments',
+ 'post comments without approval',
+ 'access content',
+ 'administer content types',
+ 'administer nodes',
+ ],
+ ],
+ ];
+
+ // The source data.
+ foreach ($roles as $role) {
+ $tests[0]['source_data']['permission'][] = [
+ 'rid' => $role['rid'],
+ 'perm' => implode(', ', $role['permissions']),
+ ];
+ unset($role['permissions']);
+ $tests[0]['source_data']['role'][] = $role;
+ }
+
+ $tests[0]['source_data']['filter_formats'] = [
+ [
+ 'format' => 1,
+ 'roles' => '',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = $roles;
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/UserPictureFileTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/UserPictureFileTest.php
new file mode 100644
index 000000000..2724d35f5
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/UserPictureFileTest.php
@@ -0,0 +1,49 @@
+ '2',
+ 'picture' => 'core/modules/simpletest/files/image-test.jpg',
+ ],
+ [
+ 'uid' => '15',
+ 'picture' => '',
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'uid' => '2',
+ 'picture' => 'core/modules/simpletest/files/image-test.jpg',
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/UserPictureTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/UserPictureTest.php
new file mode 100644
index 000000000..aea6afa7c
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/UserPictureTest.php
@@ -0,0 +1,46 @@
+ 1,
+ 'access' => 1382835435,
+ 'picture' => 'sites/default/files/pictures/picture-1.jpg',
+ ],
+ [
+ 'uid' => 2,
+ 'access' => 1382835436,
+ 'picture' => 'sites/default/files/pictures/picture-2.jpg',
+ ],
+ ];
+
+ // User picture data model is identical in source input and output.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['users'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/UserTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/UserTest.php
new file mode 100644
index 000000000..1dc34a7cd
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d6/UserTest.php
@@ -0,0 +1,83 @@
+ 2,
+ 'name' => 'admin',
+ 'pass' => '1234',
+ 'mail' => 'admin@example.com',
+ 'theme' => '',
+ 'signature' => '',
+ 'signature_format' => 0,
+ 'created' => 1279402616,
+ 'access' => 1322981278,
+ 'login' => 1322699994,
+ 'status' => 0,
+ 'timezone' => 'America/Lima',
+ 'language' => 'en',
+ // @todo Add the file when needed.
+ 'picture' => 'sites/default/files/pictures/picture-1.jpg',
+ 'init' => 'admin@example.com',
+ 'data' => NULL,
+ ],
+ [
+ 'uid' => 4,
+ 'name' => 'alice',
+ // @todo d6 hash?
+ 'pass' => '1234',
+ 'mail' => 'alice@example.com',
+ 'theme' => '',
+ 'signature' => '',
+ 'signature_format' => 0,
+ 'created' => 1322981368,
+ 'access' => 1322982419,
+ 'login' => 132298140,
+ 'status' => 0,
+ 'timezone' => 'America/Lima',
+ 'language' => 'en',
+ 'picture' => '',
+ 'init' => 'alice@example.com',
+ 'data' => NULL,
+ ],
+ ];
+
+ // getDatabase() will not create empty tables, so we need to insert data
+ // even if it's irrelevant to the test.
+ $tests[0]['source_data']['users_roles'] = [
+ [
+ 'uid' => 99,
+ 'rid' => 99,
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = $tests[0]['source_data']['users'];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserTest.php
new file mode 100644
index 000000000..8e14d63c9
--- /dev/null
+++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserTest.php
@@ -0,0 +1,120 @@
+ '33',
+ 'field_id' => '11',
+ 'field_name' => 'field_file',
+ 'entity_type' => 'user',
+ 'bundle' => 'user',
+ 'data' => 'a:0:{}',
+ 'deleted' => '0',
+ ],
+ ];
+ $tests[0]['source_data']['field_data_field_file'] = [
+ [
+ 'entity_type' => 'user',
+ 'bundle' => 'user',
+ 'deleted' => 0,
+ 'entity_id' => 2,
+ 'revision_id' => NULL,
+ 'language' => 'und',
+ 'delta' => 0,
+ 'field_file_fid' => 99,
+ 'field_file_display' => 1,
+ 'field_file_description' => 'None',
+ ],
+ ];
+ $tests[0]['source_data']['role'] = [
+ [
+ 'rid' => 2,
+ 'name' => 'authenticated user',
+ 'weight' => 0,
+ ],
+ ];
+ $tests[0]['source_data']['users'] = [
+ [
+ 'uid' => '2',
+ 'name' => 'Odo',
+ 'pass' => '$S$DVpvPItXvnsmF3giVEe7Jy2lG.SCoEs8uKwpHsyPvdeNAaNZYxZ8',
+ 'mail' => 'odo@local.host',
+ 'theme' => '',
+ 'signature' => '',
+ 'signature_format' => 'filtered_html',
+ 'created' => '1432750741',
+ 'access' => '0',
+ 'login' => '0',
+ 'status' => '1',
+ 'timezone' => 'America/Chicago',
+ 'language' => '',
+ 'picture' => '0',
+ 'init' => 'odo@local.host',
+ 'data' => 'a:1:{s:7:"contact";i:1;}',
+ ],
+ ];
+ $tests[0]['source_data']['users_roles'] = [
+ [
+ 'uid' => 2,
+ 'rid' => 2,
+ ],
+ ];
+
+ // The expected results.
+ $tests[0]['expected_data'] = [
+ [
+ 'uid' => '2',
+ 'name' => 'Odo',
+ 'pass' => '$S$DVpvPItXvnsmF3giVEe7Jy2lG.SCoEs8uKwpHsyPvdeNAaNZYxZ8',
+ 'mail' => 'odo@local.host',
+ 'signature' => '',
+ 'signature_format' => 'filtered_html',
+ 'created' => '1432750741',
+ 'access' => '0',
+ 'login' => '0',
+ 'status' => '1',
+ 'timezone' => 'America/Chicago',
+ 'language' => '',
+ 'picture' => '0',
+ 'init' => 'odo@local.host',
+ 'roles' => [2],
+ 'data' => [
+ 'contact' => 1,
+ ],
+ 'field_file' => [
+ [
+ 'fid' => 99,
+ 'display' => 1,
+ 'description' => 'None',
+ ],
+ ],
+ ],
+ ];
+
+ return $tests;
+ }
+
+}
diff --git a/core/modules/user/tests/src/Unit/Migrate/ProfileFieldTest.php b/core/modules/user/tests/src/Unit/Migrate/ProfileFieldTest.php
deleted file mode 100644
index a639bf418..000000000
--- a/core/modules/user/tests/src/Unit/Migrate/ProfileFieldTest.php
+++ /dev/null
@@ -1,82 +0,0 @@
- 'test_profile_fields',
- 'source' => [
- 'plugin' => 'd6_profile_field',
- ],
- ];
-
- protected $expectedResults = [
- [
- 'fid' => 1,
- 'title' => 'First name',
- 'name' => 'profile_first_name',
- 'explanation' => 'First name user',
- 'category' => 'profile',
- 'page' => '',
- 'type' => 'textfield',
- 'weight' => 0,
- 'required' => 1,
- 'register' => 0,
- 'visibility' => 2,
- 'autocomplete' => 0,
- 'options' => [],
- ],
- [
- 'fid' => 2,
- 'title' => 'Last name',
- 'name' => 'profile_last_name',
- 'explanation' => 'Last name user',
- 'category' => 'profile',
- 'page' => '',
- 'type' => 'textfield',
- 'weight' => 0,
- 'required' => 0,
- 'register' => 0,
- 'visibility' => 2,
- 'autocomplete' => 0,
- 'options' => [],
- ],
- [
- 'fid' => 3,
- 'title' => 'Policy',
- 'name' => 'profile_policy',
- 'explanation' => 'A checkbox that say if you accept policy of website',
- 'category' => 'profile',
- 'page' => '',
- 'type' => 'checkbox',
- 'weight' => 0,
- 'required' => 1,
- 'register' => 1,
- 'visibility' => 2,
- 'autocomplete' => 0,
- 'options' => [],
- ],
- ];
-
- /**
- * Prepopulate contents with results.
- */
- protected function setUp() {
- $this->databaseContents['profile_fields'] = $this->expectedResults;
- foreach ($this->databaseContents['profile_fields'] as &$row) {
- $row['options'] = serialize([]);
- }
- parent::setUp();
- }
-
-}
diff --git a/core/modules/user/tests/src/Unit/Migrate/UserPictureInstanceTest.php b/core/modules/user/tests/src/Unit/Migrate/UserPictureInstanceTest.php
deleted file mode 100644
index a650704d4..000000000
--- a/core/modules/user/tests/src/Unit/Migrate/UserPictureInstanceTest.php
+++ /dev/null
@@ -1,54 +0,0 @@
- 'test',
- 'source' => [
- 'plugin' => 'user_picture_instance',
- ],
- ];
-
- protected $expectedResults = array(
- array(
- 'id' => '',
- 'file_directory' => 'pictures',
- 'max_filesize' => '128KB',
- 'max_resolution' => '128x128',
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['variable'] = array(
- array(
- 'name' => 'file_directory',
- 'value' => serialize(NULL),
- ),
- array(
- 'name' => 'user_picture_file_size',
- 'value' => serialize(128),
- ),
- array(
- 'name' => 'user_picture_dimensions',
- 'value' => serialize('128x128'),
- ),
- );
- parent::setUp();
- }
-
-}
diff --git a/core/modules/user/tests/src/Unit/Migrate/d6/RoleTest.php b/core/modules/user/tests/src/Unit/Migrate/d6/RoleTest.php
deleted file mode 100644
index 9d21b56c1..000000000
--- a/core/modules/user/tests/src/Unit/Migrate/d6/RoleTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd6_user_role',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'rid' => 1,
- 'name' => 'anonymous user',
- 'permissions' => array(
- 'access content',
- ),
- ),
- array(
- 'rid' => 2,
- 'name' => 'authenticated user',
- 'permissions' => array(
- 'access comments',
- 'access content',
- 'post comments',
- 'post comments without approval',
- ),
- ),
- array(
- 'rid' => 3,
- 'name' => 'administrator',
- 'permissions' => array(
- 'access comments',
- 'administer comments',
- 'post comments',
- 'post comments without approval',
- 'access content',
- 'administer content types',
- 'administer nodes',
- ),
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- foreach ($this->expectedResults as $row) {
- $this->databaseContents['permission'][] = array(
- 'perm' => implode(', ', $row['permissions']),
- 'rid' => $row['rid'],
- );
- unset($row['permissions']);
- $this->databaseContents['role'][] = $row;
- }
- $this->databaseContents['filter_formats'][] = array(
- 'format' => 1,
- 'roles' => '',
- );
- parent::setUp();
- }
-
-}
diff --git a/core/modules/user/tests/src/Unit/Migrate/d6/UserPictureFileTest.php b/core/modules/user/tests/src/Unit/Migrate/d6/UserPictureFileTest.php
deleted file mode 100644
index 6c045eaf3..000000000
--- a/core/modules/user/tests/src/Unit/Migrate/d6/UserPictureFileTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd6_user_picture_file',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'uid' => '2',
- 'picture' => 'core/modules/simpletest/files/image-test.jpg',
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['users'] = array(
- array(
- 'uid' => '2',
- 'picture' => 'core/modules/simpletest/files/image-test.jpg',
- ),
- array(
- 'uid' => '15',
- 'picture' => '',
- ),
- );
- parent::setUp();
- }
-
-}
diff --git a/core/modules/user/tests/src/Unit/Migrate/d6/UserPictureTest.php b/core/modules/user/tests/src/Unit/Migrate/d6/UserPictureTest.php
deleted file mode 100644
index 1962b1732..000000000
--- a/core/modules/user/tests/src/Unit/Migrate/d6/UserPictureTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
- 'test_user_picture',
- 'source' => array(
- 'plugin' => 'd6_user_picture',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'uid' => 1,
- 'access' => 1382835435,
- 'picture' => 'sites/default/files/pictures/picture-1.jpg',
- ),
- array(
- 'uid' => 2,
- 'access' => 1382835436,
- 'picture' => 'sites/default/files/pictures/picture-2.jpg',
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['users'] = $this->expectedResults;
- parent::setUp();
- }
-
-}
diff --git a/core/modules/user/tests/src/Unit/Migrate/d6/UserTest.php b/core/modules/user/tests/src/Unit/Migrate/d6/UserTest.php
deleted file mode 100644
index d087451d0..000000000
--- a/core/modules/user/tests/src/Unit/Migrate/d6/UserTest.php
+++ /dev/null
@@ -1,83 +0,0 @@
- 'test',
- 'source' => array(
- 'plugin' => 'd6_user',
- ),
- );
-
- protected $expectedResults = array(
- array(
- 'uid' => 2,
- 'name' => 'admin',
- // @todo d6 hash?
- 'pass' => '1234',
- 'mail' => 'admin@example.com',
- 'theme' => '',
- 'signature' => '',
- 'signature_format' => 0,
- 'created' => 1279402616,
- 'access' => 1322981278,
- 'login' => 1322699994,
- 'status' => 0,
- 'timezone' => 'America/Lima',
- 'language' => 'en',
- // @todo Add the file when needed.
- 'picture' => 'sites/default/files/pictures/picture-1.jpg',
- 'init' => 'admin@example.com',
- 'data' => NULL,
- ),
- array(
- 'uid' => 4,
- 'name' => 'alice',
- // @todo d6 hash?
- 'pass' => '1234',
- 'mail' => 'alice@example.com',
- 'theme' => '',
- 'signature' => '',
- 'signature_format' => 0,
- 'created' => 1322981368,
- 'access' => 1322982419,
- 'login' => 132298140,
- 'status' => 0,
- 'timezone' => 'America/Lima',
- 'language' => 'en',
- 'picture' => '',
- 'init' => 'alice@example.com',
- 'data' => NULL,
- ),
- );
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- foreach ($this->expectedResults as $k => $row) {
- $this->databaseContents['users'][$k] = $row;
- }
- // getDatabase() will not create empty tables, so we need to insert data
- // even if it's irrelevant to the test.
- $this->databaseContents['users_roles'] = array(
- array(
- 'uid' => 99,
- 'rid' => 99,
- ),
- );
- parent::setUp();
- }
-
-}
diff --git a/core/modules/user/tests/src/Unit/Plugin/migrate/source/d7/UserTest.php b/core/modules/user/tests/src/Unit/Plugin/migrate/source/d7/UserTest.php
deleted file mode 100644
index f2c862bc6..000000000
--- a/core/modules/user/tests/src/Unit/Plugin/migrate/source/d7/UserTest.php
+++ /dev/null
@@ -1,112 +0,0 @@
- 'test',
- 'source' => [
- 'plugin' => 'd7_user',
- ],
- ];
-
- protected $expectedResults = [
- [
- 'uid' => '2',
- 'name' => 'Odo',
- 'pass' => '$S$DVpvPItXvnsmF3giVEe7Jy2lG.SCoEs8uKwpHsyPvdeNAaNZYxZ8',
- 'mail' => 'odo@local.host',
- 'signature' => '',
- 'signature_format' => 'filtered_html',
- 'created' => '1432750741',
- 'access' => '0',
- 'login' => '0',
- 'status' => '1',
- 'timezone' => 'America/Chicago',
- 'language' => '',
- 'picture' => '0',
- 'init' => 'odo@local.host',
- 'roles' => [2],
- 'data' => [
- 'contact' => 1,
- ],
- 'field_file' => [
- [
- 'fid' => 99,
- 'display' => 1,
- 'description' => 'None',
- ],
- ],
- ],
- ];
-
- /**
- * {@inheritdoc}
- */
- protected function setUp() {
- $this->databaseContents['users'][] = [
- 'uid' => '2',
- 'name' => 'Odo',
- 'pass' => '$S$DVpvPItXvnsmF3giVEe7Jy2lG.SCoEs8uKwpHsyPvdeNAaNZYxZ8',
- 'mail' => 'odo@local.host',
- 'theme' => '',
- 'signature' => '',
- 'signature_format' => 'filtered_html',
- 'created' => '1432750741',
- 'access' => '0',
- 'login' => '0',
- 'status' => '1',
- 'timezone' => 'America/Chicago',
- 'language' => '',
- 'picture' => '0',
- 'init' => 'odo@local.host',
- 'data' => 'a:1:{s:7:"contact";i:1;}',
- ];
- $this->databaseContents['users_roles'][] = [
- 'uid' => 2,
- 'rid' => 2,
- ];
- $this->databaseContents['role'][] = [
- 'rid' => 2,
- 'name' => 'authenticated user',
- 'weight' => 0,
- ];
- $this->databaseContents['field_config_instance'] = [
- [
- 'id' => '33',
- 'field_id' => '11',
- 'field_name' => 'field_file',
- 'entity_type' => 'user',
- 'bundle' => 'user',
- 'data' => 'a:0:{}',
- 'deleted' => '0',
- ],
- ];
- $this->databaseContents['field_data_field_file'] = [
- [
- 'entity_type' => 'user',
- 'bundle' => 'user',
- 'deleted' => 0,
- 'entity_id' => 2,
- 'revision_id' => NULL,
- 'language' => 'und',
- 'delta' => 0,
- 'field_file_fid' => 99,
- 'field_file_display' => 1,
- 'field_file_description' => 'None',
- ],
- ];
- parent::setUp();
- }
-
-}
diff --git a/core/modules/views/js/base.js b/core/modules/views/js/base.js
index 7c7622db4..cca3be4dd 100644
--- a/core/modules/views/js/base.js
+++ b/core/modules/views/js/base.js
@@ -53,9 +53,11 @@
Drupal.Views.parseViewArgs = function (href, viewPath) {
var returnObj = {};
var path = Drupal.Views.getPath(href);
+ // Get viewPath url without baseUrl portion.
+ var viewHref = Drupal.url(viewPath).substring(drupalSettings.path.baseUrl.length);
// Ensure we have a correct path.
- if (viewPath && path.substring(0, viewPath.length + 1) === viewPath + '/') {
- returnObj.view_args = decodeURIComponent(path.substring(viewPath.length + 1, path.length));
+ if (viewHref && path.substring(0, viewHref.length + 1) === viewHref + '/') {
+ returnObj.view_args = decodeURIComponent(path.substring(viewHref.length + 1, path.length));
returnObj.view_path = path;
}
return returnObj;
diff --git a/core/modules/views/src/ManyToOneHelper.php b/core/modules/views/src/ManyToOneHelper.php
index 09accee5a..a3c4834ac 100644
--- a/core/modules/views/src/ManyToOneHelper.php
+++ b/core/modules/views/src/ManyToOneHelper.php
@@ -298,7 +298,7 @@ class ManyToOneHelper {
}
$placeholders = array(
$placeholder => $value,
- ) + $this->placeholders;
+ );
$this->handler->query->addWhereExpression($options['group'], "$field $operator", $placeholders);
}
else {
diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
index cde5ecb35..103dd9dee 100644
--- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
@@ -1271,7 +1271,20 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
// @todo Views should expect and store a leading /. See
// https://www.drupal.org/node/2423913.
- $more_link = ' ' . $this->linkGenerator()->generate($more_link_text, CoreUrl::fromUserInput('/' . $more_link_path, array('attributes' => array('class' => array('views-more-link')))));
+ $options = array(
+ 'attributes' => array(
+ 'class' => array(
+ 'views-more-link',
+ ),
+ ),
+ );
+ if (UrlHelper::isExternal($more_link_path)) {
+ $more_link_url = CoreUrl::fromUri($more_link_path, $options);
+ }
+ else {
+ $more_link_url = CoreUrl::fromUserInput('/' . $more_link_path, $options);
+ }
+ $more_link = ' ' . $this->linkGenerator()->generate($more_link_text, $more_link_url);
}
}
diff --git a/core/modules/views/src/Plugin/views/filter/Combine.php b/core/modules/views/src/Plugin/views/filter/Combine.php
index e13df51ec..1b4ab9c1c 100644
--- a/core/modules/views/src/Plugin/views/filter/Combine.php
+++ b/core/modules/views/src/Plugin/views/filter/Combine.php
@@ -3,6 +3,7 @@
namespace Drupal\views\Plugin\views\filter;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Database\Database;
/**
* Filter handler which allows to search on multiple fields.
@@ -138,6 +139,40 @@ class Combine extends StringFilter {
$this->query->addWhereExpression($this->options['group'], "$expression LIKE $placeholder", array($placeholder => '%' . db_like($this->value) . '%'));
}
+ /**
+ * Filters by one or more words.
+ *
+ * By default opContainsWord uses add_where, that doesn't support complex
+ * expressions.
+ *
+ * @param string $expression
+ */
+ protected function opContainsWord($expression) {
+ $placeholder = $this->placeholder();
+
+ // Don't filter on empty strings.
+ if (empty($this->value)) {
+ return;
+ }
+
+ // Match all words separated by spaces or sentences encapsulated by double
+ // quotes.
+ preg_match_all(static::WORDS_PATTERN, ' ' . $this->value, $matches, PREG_SET_ORDER);
+
+ // Switch between the 'word' and 'allwords' operator.
+ $type = $this->operator == 'word' ? 'OR' : 'AND';
+ $group = $this->query->setWhereGroup($type);
+ $operator = Database::getConnection()->mapConditionOperator('LIKE');
+ $operator = isset($operator['operator']) ? $operator['operator'] : 'LIKE';
+
+ foreach ($matches as $match_key => $match) {
+ $temp_placeholder = $placeholder . '_' . $match_key;
+ // Clean up the user input and remove the sentence delimiters.
+ $word = trim($match[2], ',?!();:-"');
+ $this->query->addWhereExpression($group, "$expression $operator $temp_placeholder", array($temp_placeholder => '%' . Database::getConnection()->escapeLike($word) . '%'));
+ }
+ }
+
protected function opStartsWith($expression) {
$placeholder = $this->placeholder();
$this->query->addWhereExpression($this->options['group'], "$expression LIKE $placeholder", array($placeholder => db_like($this->value) . '%'));
diff --git a/core/modules/views/src/Plugin/views/filter/StringFilter.php b/core/modules/views/src/Plugin/views/filter/StringFilter.php
index 10c407017..618b58458 100644
--- a/core/modules/views/src/Plugin/views/filter/StringFilter.php
+++ b/core/modules/views/src/Plugin/views/filter/StringFilter.php
@@ -14,6 +14,11 @@ use Drupal\Core\Form\FormStateInterface;
*/
class StringFilter extends FilterPluginBase {
+ /**
+ * All words separated by spaces or sentences encapsulated by double quotes.
+ */
+ const WORDS_PATTERN = '/ (-?)("[^"]+"|[^" ]+)/i';
+
// exposed filter options
protected $alwaysMultiple = TRUE;
@@ -267,7 +272,7 @@ class StringFilter extends FilterPluginBase {
return;
}
- preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->value, $matches, PREG_SET_ORDER);
+ preg_match_all(static::WORDS_PATTERN, ' ' . $this->value, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$phrase = FALSE;
// Strip off phrase quotes
diff --git a/core/modules/views/src/Plugin/views/pager/Mini.php b/core/modules/views/src/Plugin/views/pager/Mini.php
index 5f1750ea3..c6520f4fb 100644
--- a/core/modules/views/src/Plugin/views/pager/Mini.php
+++ b/core/modules/views/src/Plugin/views/pager/Mini.php
@@ -47,14 +47,17 @@ class Mini extends SqlBase {
public function query() {
parent::query();
- // Don't query for the next page if we have a pager that has a limited
- // amount of pages.
- if ($this->getItemsPerPage() > 0 && (empty($this->options['total_pages']) || ($this->getCurrentPage() < $this->options['total_pages']))) {
- // Increase the items in the query in order to be able to find out whether
- // there is another page.
- $limit = $this->view->query->getLimit();
- $limit += 1;
- $this->view->query->setLimit($limit);
+ // Only modify the query if we don't want to do a total row count
+ if (!$this->view->get_total_rows) {
+ // Don't query for the next page if we have a pager that has a limited
+ // amount of pages.
+ if ($this->getItemsPerPage() > 0 && (empty($this->options['total_pages']) || ($this->getCurrentPage() < $this->options['total_pages']))) {
+ // Increase the items in the query in order to be able to find out
+ // whether there is another page.
+ $limit = $this->view->query->getLimit();
+ $limit += 1;
+ $this->view->query->setLimit($limit);
+ }
}
}
@@ -69,19 +72,16 @@ class Mini extends SqlBase {
* {@inheritdoc}
*/
public function postExecute(&$result) {
- // In query() one more item might have been retrieved than necessary. If so,
- // the next link needs to be displayed and the item removed.
- if ($this->getItemsPerPage() > 0 && count($result) > $this->getItemsPerPage()) {
- array_pop($result);
- // Make sure the pager shows the next link by setting the total items to
- // the biggest possible number but prevent failing calculations like
- // ceil(PHP_INT_MAX) we take PHP_INT_MAX / 2.
- $total = PHP_INT_MAX / 2;
+ // Only modify the result if we didn't do a total row count
+ if (!$this->view->get_total_rows) {
+ $this->total_items = $this->getCurrentPage() * $this->getItemsPerPage() + count($result);
+ // query() checks if we need a next link by setting limit 1 record past
+ // this page If we got the extra record we need to remove it before we
+ // render the result.
+ if ($this->getItemsPerPage() > 0 && count($result) > $this->getItemsPerPage()) {
+ array_pop($result);
+ }
}
- else {
- $total = $this->getCurrentPage() * $this->getItemsPerPage() + count($result);
- }
- $this->total_items = $total;
}
/**
diff --git a/core/modules/views/src/Tests/Plugin/MiniPagerTest.php b/core/modules/views/src/Tests/Plugin/MiniPagerTest.php
index f39a9d65f..1a73b6980 100644
--- a/core/modules/views/src/Tests/Plugin/MiniPagerTest.php
+++ b/core/modules/views/src/Tests/Plugin/MiniPagerTest.php
@@ -70,6 +70,18 @@ class MiniPagerTest extends PluginTestBase {
$this->assertText($this->nodes[18]->label());
$this->assertText($this->nodes[19]->label());
+ // Test @total value in result summary
+ $view = Views::getView('test_mini_pager');
+ $view->setDisplay('page_4');
+ $this->executeView($view);
+ $this->assertIdentical($view->get_total_rows, TRUE, 'The query was set to calculate the total number of rows.');
+ $this->assertEqual(count($this->nodes), $view->total_rows, 'The total row count is equal to the number of nodes.');
+
+ $this->drupalGet('test_mini_pager_total', array('query' => array('page' => 1)));
+ $this->assertText('of ' . count($this->nodes), 'The first page shows the total row count.');
+ $this->drupalGet('test_mini_pager_total', array('query' => array('page' => 6)));
+ $this->assertText('of ' . count($this->nodes), 'The last page shows the total row count.');
+
// Test a mini pager with just one item per page.
$this->drupalGet('test_mini_pager_one');
$this->assertText('››');
diff --git a/core/modules/views/src/Tests/TaxonomyGlossaryTest.php b/core/modules/views/src/Tests/TaxonomyGlossaryTest.php
new file mode 100644
index 000000000..ccda8877d
--- /dev/null
+++ b/core/modules/views/src/Tests/TaxonomyGlossaryTest.php
@@ -0,0 +1,58 @@
+enableViewsTestModule();
+
+ /** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
+ $vocabulary = $this->createVocabulary();
+ for ($i = 0; $i < 10; $i++) {
+ $this->taxonomyTerms[] = $this->createTerm($vocabulary);
+ }
+ }
+
+ /**
+ * Tests a taxonomy glossary view.
+ */
+ public function testTaxonomyGlossaryView() {
+ // Go the taxonomy glossary page for the first term.
+ $this->drupalGet('test_taxonomy_glossary/' . substr($this->taxonomyTerms[0]->getName(), 0, 1));
+ $this->assertText($this->taxonomyTerms[0]->getName());
+ }
+
+}
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml
index bf44f36e6..e2e143eaf 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml
@@ -5,56 +5,450 @@ dependencies:
- node
- user
id: test_glossary
-label: test_glossary
-module: views
-description: ''
+label: Test glossary
+module: node
+description: 'All content, by letter.'
tag: default
base_table: node_field_data
base_field: nid
core: '8'
display:
default:
+ id: default
+ display_title: Master
+ display_plugin: default
+ position: 0
display_options:
- access:
- type: perm
- arguments:
- title:
- default_argument_type: fixed
- field: title
- glossary: true
- id: title
- limit: 1
- summary:
- format: default_summary
- number_of_records: 0
- summary_options:
- items_per_page: 25
- table: node_field_data
- plugin_id: string
- entity_type: node
- entity_field: title
- cache:
- type: tag
- exposed_form:
- type: basic
- fields:
- title:
- field: title
- id: title
- label: ''
- table: node_field_data
- plugin_id: field
- entity_type: node
- entity_field: title
- pager:
- type: full
query:
type: views_query
+ options:
+ query_comment: ''
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_tags: { }
+ use_ajax: true
+ access:
+ type: perm
+ options:
+ perm: 'access content'
+ cache:
+ type: tag
+ options: { }
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ pager:
+ type: mini
+ options:
+ items_per_page: 36
+ offset: 0
+ id: 0
+ total_pages: 0
+ expose:
+ items_per_page: false
+ items_per_page_label: 'Items per page'
+ items_per_page_options: '5, 10, 25, 50'
+ items_per_page_options_all: false
+ items_per_page_options_all_label: '- All -'
+ offset: false
+ offset_label: Offset
+ tags:
+ previous: ‹‹
+ next: ››
+ fields:
+ title:
+ id: title
+ table: node_field_data
+ field: title
+ plugin_id: field
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: Title
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ entity_type: node
+ entity_field: title
+ name:
+ id: name
+ table: users_field_data
+ field: name
+ label: Author
+ relationship: uid
+ plugin_id: field
+ type: user_name
+ group_type: group
+ admin_label: ''
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ entity_type: user
+ entity_field: name
+ changed:
+ id: changed
+ table: node_field_data
+ field: changed
+ label: 'Last update'
+ type: timestamp
+ settings:
+ date_format: long
+ custom_date_format: ''
+ timezone: ''
+ plugin_id: field
+ relationship: none
+ group_type: group
+ admin_label: ''
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ entity_type: node
+ entity_field: changed
+ arguments:
+ title:
+ id: title
+ table: node_field_data
+ field: title
+ default_action: default
+ exception:
+ title_enable: true
+ default_argument_type: fixed
+ default_argument_options:
+ argument: a
+ summary:
+ format: default_summary
+ specify_validation: true
+ glossary: true
+ limit: 1
+ case: upper
+ path_case: lower
+ transform_dash: false
+ plugin_id: string
+ relationship: none
+ group_type: group
+ admin_label: ''
+ title_enable: false
+ title: ''
+ default_argument_skip_url: false
+ summary_options: { }
+ validate:
+ type: none
+ fail: 'not found'
+ validate_options: { }
+ break_phrase: false
+ entity_type: node
+ entity_field: title
+ relationships:
+ uid:
+ id: uid
+ table: node_field_data
+ field: uid
+ plugin_id: standard
+ relationship: none
+ group_type: group
+ admin_label: author
+ required: false
style:
- type: default
+ type: table
+ options:
+ columns:
+ title: title
+ name: name
+ changed: changed
+ default: title
+ info:
+ title:
+ sortable: true
+ separator: ''
+ name:
+ sortable: true
+ separator: ''
+ changed:
+ sortable: true
+ separator: ''
+ override: true
+ sticky: false
+ grouping: { }
+ row_class: ''
+ default_row_class: true
+ uses_fields: false
+ order: asc
+ summary: ''
+ empty_table: false
row:
type: fields
- display_plugin: default
- display_title: Master
- id: default
- position: 0
+ options:
+ inline: { }
+ separator: ''
+ hide_empty: false
+ default_field_elements: true
+ header: { }
+ footer: { }
+ empty: { }
+ sorts: { }
+ filters:
+ langcode:
+ id: langcode
+ table: node_field_data
+ field: langcode
+ relationship: none
+ group_type: group
+ admin_label: ''
+ operator: in
+ value:
+ '***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
+ group: 1
+ exposed: false
+ expose:
+ operator_id: ''
+ label: ''
+ description: ''
+ use_operator: false
+ operator: ''
+ identifier: ''
+ required: false
+ remember: false
+ multiple: false
+ remember_roles:
+ authenticated: authenticated
+ reduce: false
+ is_grouped: false
+ group_info:
+ label: ''
+ description: ''
+ identifier: ''
+ optional: true
+ widget: select
+ multiple: false
+ remember: false
+ default_group: All
+ default_group_multiple: { }
+ group_items: { }
+ plugin_id: language
+ entity_type: node
+ entity_field: langcode
+ display_extenders: { }
+ cache_metadata:
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url
+ - url.query_args
+ - 'user.node_grants:view'
+ - user.permissions
+ max-age: 0
+ tags: { }
+ attachment_1:
+ id: attachment_1
+ display_title: Attachment
+ display_plugin: attachment
+ position: 2
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ pager:
+ type: none
+ options:
+ offset: 0
+ items_per_page: 0
+ defaults:
+ arguments: false
+ arguments:
+ title:
+ id: title
+ table: node_field_data
+ field: title
+ default_action: summary
+ exception:
+ title_enable: true
+ default_argument_type: fixed
+ default_argument_options:
+ argument: a
+ summary:
+ format: unformatted_summary
+ summary_options:
+ items_per_page: 25
+ inline: true
+ separator: ' | '
+ specify_validation: true
+ glossary: true
+ limit: 1
+ case: upper
+ path_case: lower
+ transform_dash: false
+ plugin_id: string
+ relationship: none
+ group_type: group
+ admin_label: ''
+ title_enable: false
+ title: ''
+ default_argument_skip_url: false
+ validate:
+ type: none
+ fail: 'not found'
+ validate_options: { }
+ break_phrase: false
+ entity_type: node
+ entity_field: title
+ displays:
+ default: default
+ page_1: page_1
+ inherit_arguments: false
+ display_extenders: { }
+ cache_metadata:
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url
+ - url.query_args
+ - 'user.node_grants:view'
+ - user.permissions
+ max-age: 0
+ tags: { }
+ page_1:
+ id: page_1
+ display_title: Page
+ display_plugin: page
+ position: 1
+ display_options:
+ query:
+ type: views_query
+ options: { }
+ path: test-glossary
+ menu:
+ type: normal
+ title: Glossary
+ weight: 0
+ menu_name: main
+ parent: ''
+ display_extenders: { }
+ cache_metadata:
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url
+ - url.query_args
+ - 'user.node_grants:view'
+ - user.permissions
+ max-age: 0
+ tags: { }
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_invalid_tokens.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_invalid_tokens.yml
new file mode 100644
index 000000000..b57593a80
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_invalid_tokens.yml
@@ -0,0 +1,53 @@
+langcode: en
+status: true
+dependencies: { }
+id: test_invalid_tokens
+label: 'Test invalid tokens'
+module: views
+description: 'Test view to invalid token replacement tests.'
+tag: ''
+base_table: views_test_data
+base_field: id
+core: 8.x
+display:
+ default:
+ display_options:
+ title: 'Test token default'
+ defaults:
+ fields: false
+ pager: false
+ sorts: false
+ fields:
+ age:
+ field: age
+ id: age
+ relationship: none
+ table: views_test_data
+ plugin_id: numeric
+ id:
+ field: id
+ id: id
+ relationship: none
+ table: views_test_data
+ plugin_id: numeric
+ name:
+ field: name
+ id: name
+ relationship: none
+ table: views_test_data
+ plugin_id: string
+ pager:
+ type: full
+ options:
+ items_per_page: 10
+ display_plugin: default
+ display_title: Master
+ id: default
+ position: 0
+ block_1:
+ display_plugin: block
+ id: block_1
+ display_title: Block
+ position: 1
+ display_options:
+ display_extenders: { }
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_mini_pager.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_mini_pager.yml
index e65ea4a4e..289f83db9 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_mini_pager.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_mini_pager.yml
@@ -118,3 +118,26 @@ display:
type: mini
options:
items_per_page: 0
+ page_4:
+ display_plugin: page
+ id: page_4
+ display_title: 'Page 4'
+ position: 4
+ display_options:
+ display_extenders: { }
+ path: test_mini_pager_total
+ empty: { }
+ defaults:
+ empty: false
+ header: false
+ header:
+ result:
+ id: result
+ table: views
+ field: result
+ relationship: none
+ group_type: group
+ admin_label: ''
+ empty: false
+ content: 'Displaying @start - @end of @total'
+ plugin_id: result
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_taxonomy_glossary.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_taxonomy_glossary.yml
new file mode 100644
index 000000000..8e4182dc1
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_taxonomy_glossary.yml
@@ -0,0 +1,191 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - taxonomy
+id: test_taxonomy_glossary
+label: test_taxonomy_glossary
+module: views
+description: ''
+tag: ''
+base_table: taxonomy_term_field_data
+base_field: tid
+core: 8.x
+display:
+ default:
+ display_plugin: default
+ id: default
+ display_title: Master
+ position: 0
+ display_options:
+ access:
+ type: none
+ options: { }
+ cache:
+ type: tag
+ options: { }
+ query:
+ type: views_query
+ options:
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_comment: ''
+ query_tags: { }
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ pager:
+ type: mini
+ options:
+ items_per_page: 10
+ offset: 0
+ id: 0
+ total_pages: null
+ expose:
+ items_per_page: false
+ items_per_page_label: 'Items per page'
+ items_per_page_options: '5, 10, 25, 50'
+ items_per_page_options_all: false
+ items_per_page_options_all_label: '- All -'
+ offset: false
+ offset_label: Offset
+ tags:
+ previous: ‹‹
+ next: ››
+ style:
+ type: default
+ row:
+ type: fields
+ fields:
+ name:
+ id: name
+ table: taxonomy_term_field_data
+ field: name
+ entity_type: taxonomy_term
+ entity_field: name
+ label: ''
+ alter:
+ alter_text: false
+ make_link: false
+ absolute: false
+ trim: false
+ word_boundary: false
+ ellipsis: false
+ strip_tags: false
+ html: false
+ hide_empty: false
+ empty_zero: false
+ type: string
+ settings:
+ link_to_entity: true
+ plugin_id: term_name
+ relationship: none
+ group_type: group
+ admin_label: ''
+ exclude: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_alter_empty: true
+ click_sort_column: value
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ convert_spaces: false
+ filters: { }
+ sorts: { }
+ title: test_taxonomy_glossary
+ header: { }
+ footer: { }
+ empty: { }
+ relationships: { }
+ arguments:
+ name:
+ id: name
+ table: taxonomy_term_field_data
+ field: name
+ relationship: none
+ group_type: group
+ admin_label: ''
+ default_action: ignore
+ exception:
+ value: all
+ title_enable: false
+ title: All
+ title_enable: false
+ title: ''
+ default_argument_type: fixed
+ default_argument_options:
+ argument: ''
+ default_argument_skip_url: false
+ summary_options:
+ base_path: ''
+ count: true
+ items_per_page: 25
+ override: false
+ summary:
+ sort_order: asc
+ number_of_records: 0
+ format: default_summary
+ specify_validation: false
+ validate:
+ type: none
+ fail: 'not found'
+ validate_options: { }
+ glossary: true
+ limit: 1
+ case: none
+ path_case: none
+ transform_dash: false
+ break_phrase: false
+ add_table: false
+ require_value: false
+ entity_type: taxonomy_term
+ entity_field: name
+ plugin_id: string
+ display_extenders: { }
+ cache_metadata:
+ max-age: -1
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url
+ - url.query_args
+ tags: { }
+ page_1:
+ display_plugin: page
+ id: page_1
+ display_title: Page
+ position: 1
+ display_options:
+ display_extenders: { }
+ path: test_taxonomy_glossary
+ cache_metadata:
+ max-age: -1
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - url
+ - url.query_args
+ tags: { }
diff --git a/core/modules/views/tests/src/FunctionalJavascript/GlossaryViewTest.php b/core/modules/views/tests/src/FunctionalJavascript/GlossaryViewTest.php
new file mode 100644
index 000000000..42da7b659
--- /dev/null
+++ b/core/modules/views/tests/src/FunctionalJavascript/GlossaryViewTest.php
@@ -0,0 +1,129 @@
+createContentType(['type' => 'page']);
+
+ $titles = [
+ 'Page One',
+ 'Page Two',
+ 'Another page',
+ ];
+ foreach ($titles as $title) {
+ $this->createNode([
+ 'title' => $title,
+ 'language' => 'en',
+ ]);
+ $this->createNode([
+ 'title' => $title,
+ 'language' => 'nl',
+ ]);
+ }
+
+ // Create a user privileged enough to use exposed filters and view content.
+ $user = $this->drupalCreateUser([
+ 'administer site configuration',
+ 'access content',
+ 'access content overview',
+ ]);
+ $this->drupalLogin($user);
+ }
+
+ /**
+ * Tests the AJAX callbacks for the glossary view.
+ */
+ public function testGlossaryDefault() {
+ // Visit the default Glossary page.
+ $url = Url::fromRoute('view.test_glossary.page_1');
+ $this->drupalGet($url);
+
+ $session = $this->getSession();
+ $web_assert = $this->assertSession();
+
+ $page = $session->getPage();
+ $rows = $page->findAll('css', '.view-test-glossary tr');
+ // We expect 2 rows plus the header row.
+ $this->assertCount(3, $rows);
+ // Click on the P link, this should show 4 rows plus the header row.
+ $page->clickLink('P');
+ $web_assert->assertWaitOnAjaxRequest();
+ $rows = $page->findAll('css', '.view-test-glossary tr');
+ $this->assertCount(5, $rows);
+ }
+
+ /**
+ * Test that the glossary also works on a language prefixed URL.
+ */
+ public function testGlossaryLanguagePrefix() {
+ $this->language = ConfigurableLanguage::createFromLangcode('nl')->save();
+
+ $config = $this->config('language.negotiation');
+ $config->set('url.prefixes', ['en' => 'en', 'nl' => 'nl'])
+ ->save();
+
+ \Drupal::service('kernel')->rebuildContainer();
+
+ $url = Url::fromRoute('view.test_glossary.page_1');
+ $this->drupalGet($url);
+
+ $session = $this->getSession();
+ $web_assert = $this->assertSession();
+
+ $page = $session->getPage();
+
+ $rows = $page->findAll('css', '.view-test-glossary tr');
+ // We expect 2 rows plus the header row.
+ $this->assertCount(3, $rows);
+ // Click on the P link, this should show 4 rows plus the header row.
+ $page->clickLink('P');
+ $web_assert->assertWaitOnAjaxRequest();
+
+ $rows = $page->findAll('css', '.view-test-glossary tr');
+ $this->assertCount(5, $rows);
+ }
+
+}
diff --git a/core/modules/views/tests/src/FunctionalJavascript/Plugin/views/Handler/ContextualFilterTest.php b/core/modules/views/tests/src/FunctionalJavascript/Plugin/views/Handler/ContextualFilterTest.php
new file mode 100644
index 000000000..8cca9c360
--- /dev/null
+++ b/core/modules/views/tests/src/FunctionalJavascript/Plugin/views/Handler/ContextualFilterTest.php
@@ -0,0 +1,75 @@
+getEditable('views.settings')->set('ui.show.advanced_column', TRUE)->save();
+
+ $account = $this->drupalCreateUser(['administer views']);
+ $this->drupalLogin($account);
+ }
+
+ /**
+ * Test adding a contextual filter handler through the UI.
+ */
+ public function testAddContextualFilterUI() {
+ $web_assert = $this->assertSession();
+
+ $this->drupalGet('/admin/structure/views/view/test_field_body');
+ $web_assert->assertWaitOnAjaxRequest();
+
+ $page = $this->getSession()->getPage();
+
+ $page->clickLink('views-add-argument');
+ $web_assert->assertWaitOnAjaxRequest();
+
+ $page->checkField('name[node_field_data.nid]');
+ $add_button = $page->find('css', '.ui-dialog-buttonset .button--primary');
+ $add_button->click();
+ $web_assert->assertWaitOnAjaxRequest();
+
+ $page->fillField('options[default_action]', 'default');
+ $page->selectFieldOption('options[default_argument_type]', 'node');
+ $add_button = $page->find('css', '.ui-dialog-buttonset .button--primary');
+ $add_button->click();
+ $web_assert->assertWaitOnAjaxRequest();
+ $page->pressButton('edit-actions-submit');
+ $web_assert->assertWaitOnAjaxRequest();
+ $page->clickLink('Content: ID');
+ // Check that the dialog opens.
+ $web_assert->assertWaitOnAjaxRequest();
+ $page->pressButton('Close');
+ }
+
+}
diff --git a/core/modules/views/tests/src/Kernel/Handler/FilterCombineTest.php b/core/modules/views/tests/src/Kernel/Handler/FilterCombineTest.php
index 775015bbe..042426383 100644
--- a/core/modules/views/tests/src/Kernel/Handler/FilterCombineTest.php
+++ b/core/modules/views/tests/src/Kernel/Handler/FilterCombineTest.php
@@ -95,6 +95,101 @@ class FilterCombineTest extends ViewsKernelTestBase {
$this->assertIdenticalResultset($view, $resultset, $this->columnMap);
}
+ /**
+ * Tests the Combine field filter with the 'word' operator.
+ */
+ public function testFilterCombineWord() {
+ $view = Views::getView('test_view');
+ $view->setDisplay();
+
+ $fields = $view->displayHandlers->get('default')->getOption('fields');
+ $view->displayHandlers->get('default')->overrideOption('fields', $fields + array(
+ 'job' => array(
+ 'id' => 'job',
+ 'table' => 'views_test_data',
+ 'field' => 'job',
+ 'relationship' => 'none',
+ ),
+ ));
+
+ // Change the filtering.
+ $view->displayHandlers->get('default')->overrideOption('filters', array(
+ 'age' => array(
+ 'id' => 'combine',
+ 'table' => 'views',
+ 'field' => 'combine',
+ 'relationship' => 'none',
+ 'operator' => 'word',
+ 'fields' => array(
+ 'name',
+ 'job',
+ ),
+ 'value' => 'singer ringo',
+ ),
+ ));
+
+ $this->executeView($view);
+ $resultset = array(
+ array(
+ 'name' => 'John',
+ 'job' => 'Singer',
+ ),
+ array(
+ 'name' => 'George',
+ 'job' => 'Singer',
+ ),
+ array(
+ 'name' => 'Ringo',
+ 'job' => 'Drummer',
+ ),
+ );
+ $this->assertIdenticalResultset($view, $resultset, $this->columnMap);
+ }
+
+ /**
+ * Tests the Combine field filter with the 'allwords' operator.
+ */
+ public function testFilterCombineAllWords() {
+ $view = Views::getView('test_view');
+ $view->setDisplay();
+
+ $fields = $view->displayHandlers->get('default')->getOption('fields');
+ $view->displayHandlers->get('default')->overrideOption('fields', $fields + array(
+ 'job' => array(
+ 'id' => 'job',
+ 'table' => 'views_test_data',
+ 'field' => 'job',
+ 'relationship' => 'none',
+ ),
+ ));
+
+ // Set the filtering to allwords and simulate searching for a phrase.
+ $view->displayHandlers->get('default')->overrideOption('filters', array(
+ 'age' => array(
+ 'id' => 'combine',
+ 'table' => 'views',
+ 'field' => 'combine',
+ 'relationship' => 'none',
+ 'operator' => 'allwords',
+ 'fields' => array(
+ 'name',
+ 'job',
+ 'age',
+ ),
+ 'value' => '25 "john singer"',
+ ),
+ ));
+
+ $this->executeView($view);
+ $resultset = array(
+ array(
+ 'name' => 'John',
+ 'job' => 'Singer',
+ ),
+ );
+ $this->assertIdenticalResultset($view, $resultset, $this->columnMap);
+ }
+
/**
* Tests if the filter can handle removed fields.
*
diff --git a/core/modules/views/tests/src/Kernel/TokenReplaceTest.php b/core/modules/views/tests/src/Kernel/TokenReplaceTest.php
index 9377ff409..5cf40ad58 100644
--- a/core/modules/views/tests/src/Kernel/TokenReplaceTest.php
+++ b/core/modules/views/tests/src/Kernel/TokenReplaceTest.php
@@ -19,7 +19,7 @@ class TokenReplaceTest extends ViewsKernelTestBase {
*
* @var array
*/
- public static $testViews = array('test_tokens');
+ public static $testViews = array('test_tokens', 'test_invalid_tokens');
protected function setUp($import_test_views = TRUE) {
parent::setUp();
@@ -90,4 +90,23 @@ class TokenReplaceTest extends ViewsKernelTestBase {
}
}
+ /**
+ * Tests path token replacements generated from a view without a path.
+ */
+ function testTokenReplacementNoPath() {
+ $token_handler = \Drupal::token();
+ $view = Views::getView('test_invalid_tokens');
+ $view->setDisplay('block_1');
+ $this->executeView($view);
+
+ $expected = array(
+ '[view:url]' => '',
+ );
+
+ foreach ($expected as $token => $expected_output) {
+ $output = $token_handler->replace($token, array('view' => $view));
+ $this->assertIdentical($output, $expected_output, format_string('Token %token replaced correctly.', array('%token' => $token)));
+ }
+ }
+
}
diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
index 5135c4f00..0b33a5891 100644
--- a/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldPluginBaseTest.php
@@ -227,11 +227,17 @@ class FieldPluginBaseTest extends UnitTestCase {
}
/**
- * Test rendering as a link without a path.
+ * Test rendering with a more link.
*
+ * @param string $path
+ * An internal or external path.
+ * @param string $url
+ * The final url used by the more link.
+ *
+ * @dataProvider providerTestRenderTrimmedWithMoreLinkAndPath
* @covers ::renderText
*/
- public function testRenderTrimmedWithMoreLink() {
+ public function testRenderTrimmedWithMoreLinkAndPath($path, $url) {
$alter = [
'trim' => TRUE,
'max_length' => 7,
@@ -239,6 +245,7 @@ class FieldPluginBaseTest extends UnitTestCase {
// Don't invoke translation.
'ellipsis' => FALSE,
'more_link_text' => 'more link',
+ 'more_link_path' => $path,
];
$this->display->expects($this->any())
@@ -253,11 +260,38 @@ class FieldPluginBaseTest extends UnitTestCase {
$field->field_alias = 'key';
$row = new ResultRow(['key' => 'a long value']);
- $expected_result = 'a long more link';
+ $expected_result = 'a long more link';
$result = $field->advancedRender($row);
$this->assertEquals($expected_result, $result);
}
+ /**
+ * Data provider for ::testRenderTrimmedWithMoreLinkAndPath().
+ *
+ * @return array
+ * Test data.
+ */
+ public function providerTestRenderTrimmedWithMoreLinkAndPath() {
+ $data = [];
+ // Simple path with default options.
+ $data[] = ['test-path', '/test-path'];
+ // Add a fragment.
+ $data[] = ['test-path#test', '/test-path#test'];
+ // Query specified as part of the path.
+ $data[] = ['test-path?foo=bar', '/test-path?foo=bar'];
+ // Empty path.
+ $data[] = ['', '/%3Cfront%3E'];
+ // Front page path.
+ $data[] = ['', '/%3Cfront%3E'];
+
+ // External URL.
+ $data[] = ['https://www.drupal.org', 'https://www.drupal.org'];
+ $data[] = ['http://www.drupal.org', 'http://www.drupal.org'];
+ $data[] = ['www.drupal.org', '/www.drupal.org'];
+
+ return $data;
+ }
+
/**
* Tests the "No results text" rendering.
*
diff --git a/core/modules/views/views.tokens.inc b/core/modules/views/views.tokens.inc
index 59faec39b..a0929552d 100644
--- a/core/modules/views/views.tokens.inc
+++ b/core/modules/views/views.tokens.inc
@@ -101,9 +101,15 @@ function views_tokens($type, $tokens, array $data, array $options, BubbleableMet
break;
case 'url':
- if ($url = $view->getUrl()) {
- $replacements[$original] = $url->setOptions($url_options)
- ->toString();
+ try {
+ if ($url = $view->getUrl()) {
+ $replacements[$original] = $url->setOptions($url_options)
+ ->toString();
+ }
+ }
+ catch (\InvalidArgumentException $e) {
+ // The view has no URL so we leave the value empty.
+ $replacements[$original] = '';
}
break;
case 'base-table':
diff --git a/core/modules/views_ui/src/Tests/DisplayCRUDTest.php b/core/modules/views_ui/src/Tests/DisplayCRUDTest.php
index 20f57d9ef..1e060dcb7 100644
--- a/core/modules/views_ui/src/Tests/DisplayCRUDTest.php
+++ b/core/modules/views_ui/src/Tests/DisplayCRUDTest.php
@@ -81,6 +81,16 @@ class DisplayCRUDTest extends UITestBase {
$this->drupalPostForm(NULL, array(), t('Save'));
$this->assertNoLinkByHref($path_prefix . '/page_1', 'Make sure there is no display tab for the deleted display.');
+
+ // Test deleting a display that has a modified machine name.
+ $view = $this->randomView();
+ $machine_name = 'new_machine_name';
+ $path_prefix = 'admin/structure/views/view/' . $view['id'] . '/edit';
+ $this->drupalPostForm("admin/structure/views/nojs/display/{$view['id']}/page_1/display_id", array('display_id' => $machine_name), 'Apply');
+ $this->drupalPostForm(NULL, array(), 'Delete Page');
+ $this->drupalPostForm(NULL, array(), t('Save'));
+ $this->assertResponse(200);
+ $this->assertNoLinkByHref($path_prefix . '/new_machine_name', 'Make sure there is no display tab for the deleted display.');
}
/**
diff --git a/core/modules/views_ui/src/Tests/FieldUITest.php b/core/modules/views_ui/src/Tests/FieldUITest.php
index 5b79da9b4..8c486d8b3 100644
--- a/core/modules/views_ui/src/Tests/FieldUITest.php
+++ b/core/modules/views_ui/src/Tests/FieldUITest.php
@@ -2,6 +2,7 @@
namespace Drupal\views_ui\Tests;
+use Drupal\Component\Serialization\Json;
use Drupal\views\Views;
/**
@@ -55,6 +56,23 @@ class FieldUITest extends UITestBase {
$result = $this->xpath('//details[@id="edit-options-more"]');
$this->assertEqual(empty($result), TRUE, "Container 'more' is empty and should not be displayed.");
+
+ // Ensure that dialog titles are not escaped.
+ $edit_groupby_url = 'admin/structure/views/nojs/handler/test_view/default/field/name';
+ $this->assertNoLinkByHref($edit_groupby_url, 0, 'No aggregation link found.');
+
+ // Enable aggregation on the view.
+ $edit = array(
+ 'group_by' => TRUE,
+ );
+ $this->drupalPostForm('/admin/structure/views/nojs/display/test_view/default/group_by', $edit, t('Apply'));
+
+ $this->assertLinkByHref($edit_groupby_url, 0, 'Aggregation link found.');
+
+ $edit_handler_url = '/admin/structure/views/ajax/handler-group/test_view/default/field/name';
+ $this->drupalGet($edit_handler_url);
+ $data = Json::decode($this->getRawContent());
+ $this->assertEqual($data[3]['dialogOptions']['title'], 'Configure aggregation settings for field Views test: Name');
}
/**
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index 5f8c095ad..530a47f9b 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -266,7 +266,7 @@ class ViewEditForm extends ViewFormBase {
// Rename display ids if needed.
foreach ($executable->displayHandlers as $id => $display) {
- if (!empty($display->display['new_id'])) {
+ if (!empty($display->display['new_id']) && empty($display->display['deleted'])) {
$new_id = $display->display['new_id'];
$display->display['id'] = $new_id;
unset($display->display['new_id']);
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
index 9658f2ce0..ef1ae3b6f 100644
--- a/core/phpunit.xml.dist
+++ b/core/phpunit.xml.dist
@@ -15,8 +15,8 @@
result printer that links to the html output results for functional tests.
Unfortunately, this breaks the output of PHPStorm's PHPUnit runner. However, if
using the command line you can add
- - -printerClass="\Drupal\Tests\Listeners\HtmlOutputPrinter" to use it (note
- there should be no spaces between the hyphens).
+ - -printer="\Drupal\Tests\Listeners\HtmlOutputPrinter" to use it (note there
+ should be no spaces between the hyphens).
-->
diff --git a/core/profiles/testing_missing_dependencies/testing_missing_dependencies.info.yml b/core/profiles/testing_missing_dependencies/testing_missing_dependencies.info.yml
new file mode 100644
index 000000000..442e93421
--- /dev/null
+++ b/core/profiles/testing_missing_dependencies/testing_missing_dependencies.info.yml
@@ -0,0 +1,10 @@
+name: 'Testing missing dependencies'
+type: profile
+description: 'Minimal profile for running a test when dependencies are listed but missing.'
+version: VERSION
+core: 8.x
+hidden: true
+dependencies:
+ - missing_module1
+ - missing_module2
+keep_english: true
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php
new file mode 100644
index 000000000..e05940537
--- /dev/null
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php
@@ -0,0 +1,85 @@
+install(['stable', 'seven']);
+ $theme_config = \Drupal::configFactory()->getEditable('system.theme');
+ $theme_config->set('admin', 'seven');
+ $theme_config->set('default', 'stable');
+ $theme_config->save();
+
+ $account = $this->drupalCreateUser(['view the administration theme']);
+ $this->drupalLogin($account);
+
+ // First visit the site directly via the URL. This should render it in the
+ // admin theme.
+ $this->drupalGet('admin/ajax-test/theme');
+ $assert = $this->assertSession();
+ $assert->pageTextContains('Current theme: seven');
+
+ // Now click the modal, which should also use the admin theme.
+ $this->drupalGet('ajax-test/dialog');
+ $assert->pageTextNotContains('Current theme: stable');
+ $this->clickLink('Link 8 (ajax)');
+ $assert->assertWaitOnAjaxRequest();
+
+ $assert->pageTextContains('Current theme: stable');
+ $assert->pageTextNotContains('Current theme: seven');
+ }
+
+ /**
+ * Test that AJAX loaded libraries are not retained between requests.
+ *
+ * @see https://www.drupal.org/node/2647916
+ */
+ public function testDrupalSettingsCachingRegression() {
+ $this->drupalGet('ajax-test/dialog');
+ $assert = $this->assertSession();
+ $session = $this->getSession();
+
+ // Insert a fake library into the already loaded library settings.
+ $fake_library = 'fakeLibrary/fakeLibrary';
+ $session->evaluateScript("drupalSettings.ajaxPageState.libraries = drupalSettings.ajaxPageState.libraries + ',$fake_library';");
+
+ $libraries = $session->evaluateScript('drupalSettings.ajaxPageState.libraries');
+ // Test that the fake library is set.
+ $this->assertContains($fake_library, $libraries);
+
+ // Click on the AJAX link.
+ $this->clickLink('Link 8 (ajax)');
+ $assert->assertWaitOnAjaxRequest();
+
+ // Test that the fake library is still set after the AJAX call.
+ $libraries = $session->evaluateScript('drupalSettings.ajaxPageState.libraries');
+ $this->assertContains($fake_library, $libraries);
+
+ // Reload the page, this should reset the loaded libraries and remove the
+ // fake library.
+ $this->drupalGet('ajax-test/dialog');
+ $libraries = $session->evaluateScript('drupalSettings.ajaxPageState.libraries');
+ $this->assertNotContains($fake_library, $libraries);
+
+ // Click on the AJAX link again, and the libraries should still not contain
+ // the fake library.
+ $this->clickLink('Link 8 (ajax)');
+ $assert->assertWaitOnAjaxRequest();
+ $libraries = $session->evaluateScript('drupalSettings.ajaxPageState.libraries');
+ $this->assertNotContains($fake_library, $libraries);
+ }
+
+}
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxThemeTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxThemeTest.php
deleted file mode 100644
index 929a943ad..000000000
--- a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxThemeTest.php
+++ /dev/null
@@ -1,45 +0,0 @@
-install(['stable', 'seven']);
- $theme_config = \Drupal::configFactory()->getEditable('system.theme');
- $theme_config->set('admin', 'seven');
- $theme_config->set('default', 'stable');
- $theme_config->save();
-
- $account = $this->drupalCreateUser(['view the administration theme']);
- $this->drupalLogin($account);
-
- // First visit the site directly via the URL. This should render it in the
- // admin theme.
- $this->drupalGet('admin/ajax-test/theme');
- $assert = $this->assertSession();
- $assert->pageTextContains('Current theme: seven');
-
- // Now click the modal, which should also use the admin theme.
- $this->drupalGet('ajax-test/dialog');
- $assert->pageTextNotContains('Current theme: stable');
- $this->clickLink('Link 8 (ajax)');
- $assert->assertWaitOnAjaxRequest();
-
- $assert->pageTextContains('Current theme: stable');
- $assert->pageTextNotContains('Current theme: seven');
- }
-
-}
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Core/MachineNameTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Core/MachineNameTest.php
new file mode 100644
index 000000000..23c2f0bb9
--- /dev/null
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/MachineNameTest.php
@@ -0,0 +1,116 @@
+drupalCreateUser(array(
+ 'access content',
+ ));
+ $this->drupalLogin($account);
+ }
+
+ /**
+ * Tests that machine name field functions.
+ *
+ * Makes sure that the machine name field automatically provides a valid
+ * machine name and that the manual editing mode functions.
+ */
+ public function testMachineName() {
+ // Visit the machine name test page which contains two machine name fields.
+ $this->drupalGet('form-test/machine-name');
+
+ // Test values for conversion.
+ $test_values = [
+ [
+ 'input' => 'Test value !0-9@',
+ 'message' => 'A title that should be transliterated must be equal to the php generated machine name',
+ 'expected' => 'test_value_0_9_',
+ ],
+ [
+ 'input' => 'Test value',
+ 'message' => 'A title that should not be transliterated must be equal to the php generated machine name',
+ 'expected' => 'test_value',
+ ],
+ ];
+
+ // Get page and session.
+ $page = $this->getSession()->getPage();
+ $assert_session = $this->assertSession();
+
+ // Get elements from the page.
+ $title_1 = $page->findField('machine_name_1_label');
+ $machine_name_1_field = $page->findField('machine_name_1');
+ $machine_name_2_field = $page->findField('machine_name_2');
+ $machine_name_1_wrapper = $machine_name_1_field->getParent();
+ $machine_name_2_wrapper = $machine_name_2_field->getParent();
+ $machine_name_1_value = $page->find('css', '#edit-machine-name-1-label-machine-name-suffix .machine-name-value');
+ $machine_name_2_value = $page->find('css', '#edit-machine-name-2-label-machine-name-suffix .machine-name-value');
+ $button_1 = $page->find('css', '#edit-machine-name-1-label-machine-name-suffix button.link');
+
+ // Assert both fields are initialized correctly.
+ $this->assertNotEmpty($machine_name_1_value, 'Machine name field 1 must be initialized');
+ $this->assertNotEmpty($machine_name_2_value, 'Machine name field 2 must be initialized');
+
+ // Field must be present for the rest of the test to work.
+ if (empty($machine_name_1_value)) {
+ $this->fail('Cannot finish test, missing machine name field');
+ }
+
+ // Test each value for conversion to a machine name.
+ foreach ($test_values as $test_info) {
+ // Set the value for the field, triggering the machine name update.
+ $title_1->setValue($test_info['input']);
+
+ // Wait the set timeout for fetching the machine name.
+ $this->getSession()->wait(1000, 'jQuery("#edit-machine-name-1-label-machine-name-suffix .machine-name-value").html() == "' . $test_info['expected'] . '"');
+
+ // Validate the generated machine name.
+ $this->assertEquals($test_info['expected'], $machine_name_1_value->getHtml(), $test_info['message']);
+
+ // Validate the second machine name field is empty.
+ $this->assertEmpty($machine_name_2_value->getHtml(), 'The second machine name field should still be empty');
+ }
+
+ // Validate the machine name field is hidden. Elements are visually hidden
+ // using positioning, isVisible() will therefore not work.
+ $this->assertEquals(TRUE, $machine_name_1_wrapper->hasClass('visually-hidden'), 'The ID field must not be visible');
+ $this->assertEquals(TRUE, $machine_name_2_wrapper->hasClass('visually-hidden'), 'The ID field must not be visible');
+
+ // Test switching back to the manual editing mode by clicking the edit link.
+ $button_1->click();
+
+ // Validate the visibility of the machine name field.
+ $this->assertEquals(FALSE, $machine_name_1_wrapper->hasClass('visually-hidden'), 'The ID field must now be visible');
+
+ // Validate the visibility of the second machine name field.
+ $this->assertEquals(TRUE, $machine_name_2_wrapper->hasClass('visually-hidden'), 'The ID field must not be visible');
+
+ // Validate if the element contains the correct value.
+ $this->assertEquals($test_values[1]['expected'], $machine_name_1_field->getValue(), 'The ID field value must be equal to the php generated machine name');
+ }
+
+}
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php
new file mode 100644
index 000000000..a116c6fde
--- /dev/null
+++ b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceAutocompleteWidgetTest.php
@@ -0,0 +1,102 @@
+createContentType(['type' => 'page']);
+ $this->createNode(['title' => 'Test page']);
+ $this->createNode(['title' => 'Page test']);
+
+ $user = $this->drupalCreateUser([
+ 'access content',
+ 'create page content',
+ ]);
+ $this->drupalLogin($user);
+ }
+
+ /**
+ * Tests that the default autocomplete widget return the correct results.
+ */
+ public function testEntityReferenceAutocompleteWidget() {
+ // Create an entity reference field and use the default 'CONTAINS' match
+ // operator.
+ $field_name = 'field_test';
+ $this->createEntityReferenceField('node', 'page', $field_name, $field_name, 'node', 'default', ['target_bundles' => ['page']]);
+ entity_get_form_display('node', 'page', 'default')
+ ->setComponent($field_name, [
+ 'type' => 'entity_reference_autocomplete',
+ 'settings' => array(
+ 'match_operator' => 'CONTAINS',
+ ),
+ ])
+ ->save();
+
+ // Visit the node add page.
+ $this->drupalGet('node/add/page');
+ $page = $this->getSession()->getPage();
+ $assert_session = $this->assertSession();
+
+ $autocomplete_field = $page->findField($field_name . '[0][target_id]');
+ $autocomplete_field->setValue('Test');
+ $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), ' ');
+ $assert_session->waitOnAutocomplete();
+
+ $results = $page->findAll('css', '.ui-autocomplete li');
+
+ $this->assertCount(2, $results);
+ $assert_session->pageTextContains('Test page');
+ $assert_session->pageTextContains('Page test');
+
+ // Now switch the autocomplete widget to the 'STARTS_WITH' match operator.
+ entity_get_form_display('node', 'page', 'default')
+ ->setComponent($field_name, [
+ 'type' => 'entity_reference_autocomplete',
+ 'settings' => array(
+ 'match_operator' => 'STARTS_WITH',
+ ),
+ ])
+ ->save();
+
+ $this->drupalGet('node/add/page');
+ $page = $this->getSession()->getPage();
+
+ $autocomplete_field = $page->findField($field_name . '[0][target_id]');
+ $autocomplete_field->setValue('Test');
+ $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), ' ');
+ $assert_session->waitOnAutocomplete();
+
+ $results = $page->findAll('css', '.ui-autocomplete li');
+
+ $this->assertCount(1, $results);
+ $assert_session->pageTextContains('Test page');
+ $assert_session->pageTextNotContains('Page test');
+ }
+
+}
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php b/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php
index 8d3ff479f..46f6017ed 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php
@@ -28,4 +28,15 @@ class JSWebAssert extends WebAssert {
}
}
+ /**
+ * Waits for the jQuery autocomplete delay duration.
+ *
+ * @see https://api.jqueryui.com/autocomplete/#option-delay
+ */
+ public function waitOnAutocomplete() {
+ // Drupal is using the default delay value of 300 milliseconds.
+ $this->session->wait(300);
+ $this->assertWaitOnAjaxRequest();
+ }
+
}
diff --git a/core/tests/Drupal/KernelTests/Core/Database/FetchTest.php b/core/tests/Drupal/KernelTests/Core/Database/FetchTest.php
index 9b5db1a6c..e4f4d6704 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/FetchTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/FetchTest.php
@@ -113,6 +113,19 @@ class FetchTest extends DatabaseTestBase {
$this->assertIdentical(count($records), 1, 'There is only one record.');
}
+ /**
+ * Confirms that we can fetch all records into an array explicitly.
+ */
+ public function testQueryFetchAllColumn() {
+ $query = db_select('test');
+ $query->addField('test', 'name');
+ $query->orderBy('name');
+ $query_result = $query->execute()->fetchAll(\PDO::FETCH_COLUMN);
+
+ $expected_result = ['George', 'John', 'Paul', 'Ringo'];
+ $this->assertEqual($query_result, $expected_result, 'Returned the correct result.');
+ }
+
/**
* Confirms that we can fetch an entire column of a result set at once.
*/
diff --git a/core/tests/Drupal/KernelTests/Core/Datetime/FormatDateTest.php b/core/tests/Drupal/KernelTests/Core/Datetime/FormatDateTest.php
new file mode 100644
index 000000000..5fbb4dff0
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Datetime/FormatDateTest.php
@@ -0,0 +1,92 @@
+installConfig(['system']);
+
+ $this->setSetting('locale_custom_strings_' . self::LANGCODE, [
+ '' => ['Sunday' => 'domingo'],
+ 'Long month name' => ['March' => 'marzo'],
+ ]);
+
+ $formats = $this->container->get('entity.manager')
+ ->getStorage('date_format')
+ ->loadMultiple(['long', 'medium', 'short']);
+ $formats['long']->setPattern('l, j. F Y - G:i')->save();
+ $formats['medium']->setPattern('j. F Y - G:i')->save();
+ $formats['short']->setPattern('Y M j - g:ia')->save();
+
+ ConfigurableLanguage::createFromLangcode(static::LANGCODE)->save();
+ }
+
+ /**
+ * Tests the format_date() function.
+ */
+ public function testFormatDate() {
+ /** @var \Drupal\Core\Datetime\DateFormatterInterface $formatter */
+ $formatter = $this->container->get('date.formatter');
+
+ $timestamp = strtotime('2007-03-26T00:00:00+00:00');
+ $this->assertSame('Sunday, 25-Mar-07 17:00:00 PDT', $formatter->format($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Test all parameters.');
+ $this->assertSame('domingo, 25-Mar-07 17:00:00 PDT', $formatter->format($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'Test translated format.');
+ $this->assertSame('l, 25-Mar-07 17:00:00 PDT', $formatter->format($timestamp, 'custom', '\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'Test an escaped format string.');
+ $this->assertSame('\\domingo, 25-Mar-07 17:00:00 PDT', $formatter->format($timestamp, 'custom', '\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'Test format containing backslash character.');
+ $this->assertSame('\\l, 25-Mar-07 17:00:00 PDT', $formatter->format($timestamp, 'custom', '\\\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'Test format containing backslash followed by escaped format string.');
+ $this->assertSame('Monday, 26-Mar-07 01:00:00 BST', $formatter->format($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London', 'en'), 'Test a different time zone.');
+
+ // Change the default language and timezone.
+ $this->config('system.site')->set('default_langcode', static::LANGCODE)->save();
+ date_default_timezone_set('America/Los_Angeles');
+
+ // Reset the language manager so new negotiations attempts will fall back on
+ // on the new language.
+ $this->container->get('language_manager')->reset();
+
+ $this->assertSame('Sunday, 25-Mar-07 17:00:00 PDT', $formatter->format($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Test a different language.');
+ $this->assertSame('Monday, 26-Mar-07 01:00:00 BST', $formatter->format($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London'), 'Test a different time zone.');
+ $this->assertSame('domingo, 25-Mar-07 17:00:00 PDT', $formatter->format($timestamp, 'custom', 'l, d-M-y H:i:s T'), 'Test custom date format.');
+ $this->assertSame('domingo, 25. marzo 2007 - 17:00', $formatter->format($timestamp, 'long'), 'Test long date format.');
+ $this->assertSame('25. marzo 2007 - 17:00', $formatter->format($timestamp, 'medium'), 'Test medium date format.');
+ $this->assertSame('2007 Mar 25 - 5:00pm', $formatter->format($timestamp, 'short'), 'Test short date format.');
+ $this->assertSame('25. marzo 2007 - 17:00', $formatter->format($timestamp), 'Test default date format.');
+ // Test HTML time element formats.
+ $this->assertSame('2007-03-25T17:00:00-0700', $formatter->format($timestamp, 'html_datetime'), 'Test html_datetime date format.');
+ $this->assertSame('2007-03-25', $formatter->format($timestamp, 'html_date'), 'Test html_date date format.');
+ $this->assertSame('17:00:00', $formatter->format($timestamp, 'html_time'), 'Test html_time date format.');
+ $this->assertSame('03-25', $formatter->format($timestamp, 'html_yearless_date'), 'Test html_yearless_date date format.');
+ $this->assertSame('2007-W12', $formatter->format($timestamp, 'html_week'), 'Test html_week date format.');
+ $this->assertSame('2007-03', $formatter->format($timestamp, 'html_month'), 'Test html_month date format.');
+ $this->assertSame('2007', $formatter->format($timestamp, 'html_year'), 'Test html_year date format.');
+
+ // HTML is not escaped by the date formatter, it must be escaped later.
+ $this->assertSame("", $formatter->format($timestamp, 'custom', '\<\s\c\r\i\p\t\>\a\l\e\r\t\(\'Y\'\)\;\<\/\s\c\r\i\p\t\>'), 'Script tags not removed from dates.');
+ $this->assertSame('2007', $formatter->format($timestamp, 'custom', '\<\e\m\>Y\<\/\e\m\>'), 'Em tags are not removed from dates.');
+ }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php b/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php
index 774bd7c0e..d7e7c382f 100644
--- a/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php
+++ b/core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php
@@ -161,4 +161,17 @@ class DirectoryTest extends FileTestBase {
$this->assertEqual($config->get('path.temporary'), $tmp_directory);
}
+ /**
+ * Tests directory creation.
+ */
+ public function testDirectoryCreation() {
+ /** @var \Drupal\Core\File\FileSystemInterface $file_system */
+ $file_system = $this->container->get('file_system');
+
+ // mkdir() recursion should work with or without a trailing slash.
+ $dir = $this->siteDirectory . '/files';
+ $this->assertTrue($file_system->mkdir($dir . '/foo/bar', 0775, TRUE));
+ $this->assertTrue($file_system->mkdir($dir . '/foo/baz/', 0775, TRUE));
+ }
+
}
diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php
index bc3ef4252..8d0cd39a0 100644
--- a/core/tests/Drupal/Tests/BrowserTestBase.php
+++ b/core/tests/Drupal/Tests/BrowserTestBase.php
@@ -528,10 +528,11 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
* The file path.
*/
public static function filePreDeleteCallback($path) {
- $success = @chmod($path, 0700);
- if (!$success) {
- trigger_error("Can not make $path writable whilst cleaning up test directory. The webserver and phpunit are probably not being run by the same user.");
- }
+ // When the webserver runs with the same system user as phpunit, we can
+ // make read-only files writable again. If not, chmod will fail while the
+ // file deletion still works if file permissions have been configured
+ // correctly. Thus, we ignore any problems while running chmod.
+ @chmod($path, 0700);
}
/**
diff --git a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php
index 43d17fab1..121aebc95 100644
--- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php
+++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php
@@ -86,7 +86,7 @@ class LibraryDiscoveryParserTest extends UnitTestCase {
$path = substr($path, strlen($this->root) + 1);
$this->libraryDiscoveryParser->setPaths('module', 'example_module', $path);
- $libraries = $this->libraryDiscoveryParser->buildByExtension('example_module', 'example');
+ $libraries = $this->libraryDiscoveryParser->buildByExtension('example_module');
$library = $libraries['example'];
$this->assertCount(0, $library['js']);
diff --git a/core/tests/Drupal/Tests/Core/Cache/Context/SessionCacheContextTest.php b/core/tests/Drupal/Tests/Core/Cache/Context/SessionCacheContextTest.php
new file mode 100644
index 000000000..b621b2dac
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Cache/Context/SessionCacheContextTest.php
@@ -0,0 +1,85 @@
+requestStack = new RequestStack();
+ $this->requestStack->push($request);
+
+ $this->session = $this->getMock('\Symfony\Component\HttpFoundation\Session\SessionInterface');
+ $request->setSession($this->session);
+
+ $this->cacheContext = new SessionCacheContext($this->requestStack);
+ }
+
+ /**
+ * @covers ::getContext
+ */
+ public function testSameContextForSameSession() {
+ $session_id = 'aSebeZ52bbM6SvADurQP89SFnEpxY6j8';
+ $this->session->expects($this->exactly(2))
+ ->method('getId')
+ ->will($this->returnValue($session_id));
+
+ $context1 = $this->cacheContext->getContext();
+ $context2 = $this->cacheContext->getContext();
+ $this->assertSame($context1, $context2);
+ $this->assertSame(FALSE, strpos($context1, $session_id), 'Session ID not contained in cache context');
+ }
+
+ /**
+ * @covers ::getContext
+ */
+ public function testDifferentContextForDifferentSession() {
+ $session1_id = 'pjH_8aSoofyCDQiuVYXJcbfyr-CPtkUY';
+ $this->session->expects($this->at(0))
+ ->method('getId')
+ ->will($this->returnValue($session1_id));
+
+ $session2_id = 'aSebeZ52bbM6SvADurQP89SFnEpxY6j8';
+ $this->session->expects($this->at(1))
+ ->method('getId')
+ ->will($this->returnValue($session2_id));
+
+ $context1 = $this->cacheContext->getContext();
+ $context2 = $this->cacheContext->getContext();
+ $this->assertNotEquals($context1, $context2);
+
+ $this->assertSame(FALSE, strpos($context1, $session1_id), 'Session ID not contained in cache context');
+ $this->assertSame(FALSE, strpos($context2, $session2_id), 'Session ID not contained in cache context');
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php
index bacab58af..6e164708b 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php
@@ -416,6 +416,27 @@ class EntityResolverManagerTest extends UnitTestCase {
$this->assertEquals($expect, $parameters);
}
+ /**
+ * Tests setRouteOptions() with an _entity_form route for an add form.
+ *
+ * @covers ::setRouteOptions
+ * @covers ::getControllerClass
+ * @covers ::getEntityTypes
+ * @covers ::setParametersFromReflection
+ * @covers ::setParametersFromEntityInformation
+ */
+ public function testSetRouteOptionsWithEntityAddFormRoute() {
+ $this->setupEntityTypes();
+ $route = new Route('/example/add', array(
+ '_entity_form' => 'entity_test.add',
+ ));
+
+ $defaults = $route->getDefaults();
+ $this->entityResolverManager->setRouteOptions($route);
+ $this->assertEquals($defaults, $route->getDefaults());
+ $this->assertFalse($route->hasOption('parameters'));
+ }
+
/**
* Creates the entity manager mock returning entity type objects.
*/
diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/PathRootsSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/PathRootsSubscriberTest.php
index 9e77accfe..96a922829 100644
--- a/core/tests/Drupal/Tests/Core/EventSubscriber/PathRootsSubscriberTest.php
+++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PathRootsSubscriberTest.php
@@ -43,6 +43,11 @@ class PathRootsSubscriberTest extends UnitTestCase {
* @covers ::onRouteFinished
*/
public function testSubscribing() {
+
+ // Ensure that onRouteFinished can be called without throwing notices
+ // when no path roots got set.
+ $this->pathRootsSubscriber->onRouteFinished();
+
$route_collection = new RouteCollection();
$route_collection->add('test_route1', new Route('/test/bar'));
$route_collection->add('test_route2', new Route('/test/baz'));
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index 1530292d9..dfbf843a0 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -568,6 +568,29 @@ class FormBuilderTest extends FormTestBase {
$this->formBuilder->buildForm($form_arg, $form_state);
}
+ /**
+ * @covers ::buildForm
+ */
+ public function testGetPostAjaxRequest() {
+ $request = new Request([FormBuilderInterface::AJAX_FORM_REQUEST => TRUE], ['form_id' => 'different_form_id']);
+ $request->setMethod('POST');
+ $this->requestStack->push($request);
+
+ $form_state = (new FormState())
+ ->setUserInput([FormBuilderInterface::AJAX_FORM_REQUEST => TRUE])
+ ->setMethod('get')
+ ->setAlwaysProcess()
+ ->disableRedirect()
+ ->set('ajax', TRUE);
+
+ $form_id = '\Drupal\Tests\Core\Form\TestForm';
+ $expected_form = (new TestForm())->buildForm([], $form_state);
+
+ $form = $this->formBuilder->buildForm($form_id, $form_state);
+ $this->assertFormElement($expected_form, $form, 'test');
+ $this->assertSame('test-form', $form['#id']);
+ }
+
/**
* @covers ::buildForm
*
diff --git a/core/tests/Drupal/Tests/Core/Routing/ContentTypeHeaderMatcherTest.php b/core/tests/Drupal/Tests/Core/Routing/ContentTypeHeaderMatcherTest.php
index ec9783886..e2c73bbe9 100644
--- a/core/tests/Drupal/Tests/Core/Routing/ContentTypeHeaderMatcherTest.php
+++ b/core/tests/Drupal/Tests/Core/Routing/ContentTypeHeaderMatcherTest.php
@@ -4,12 +4,16 @@ namespace Drupal\Tests\Core\Routing;
use Drupal\Core\Routing\ContentTypeHeaderMatcher;
use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
/**
* Confirm that the content types partial matcher is functioning properly.
*
* @group Routing
+ *
+ * @coversDefaultClass \Drupal\Core\Routing\ContentTypeHeaderMatcher
*/
class ContentTypeHeaderMatcherTest extends UnitTestCase {
@@ -88,8 +92,7 @@ class ContentTypeHeaderMatcherTest extends UnitTestCase {
/**
* Confirms that the matcher throws an exception for no-route.
*
- * @expectedException \Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException
- * @expectedExceptionMessage No route found that matches "Content-Type: application/hal+json"
+ * @covers ::filter
*/
public function testNoRouteFound() {
$matcher = new ContentTypeHeaderMatcher();
@@ -97,8 +100,24 @@ class ContentTypeHeaderMatcherTest extends UnitTestCase {
$routes = $this->fixtures->contentRouteCollection();
$request = Request::create('path/two', 'POST');
$request->headers->set('Content-type', 'application/hal+json');
+ $this->setExpectedException(UnsupportedMediaTypeHttpException::class, 'No route found that matches "Content-Type: application/hal+json"');
+ $matcher->filter($routes, $request);
+ }
+
+ /**
+ * Confirms that the matcher throws an exception for missing request header.
+ *
+ * @covers ::filter
+ */
+ public function testContentTypeRequestHeaderMissing() {
+ $matcher = new ContentTypeHeaderMatcher();
+
+ $routes = $this->fixtures->contentRouteCollection();
+ $request = Request::create('path/two', 'POST');
+ // Delete all request headers that Request::create() sets by default.
+ $request->headers = new ParameterBag();
+ $this->setExpectedException(UnsupportedMediaTypeHttpException::class, 'No "Content-Type" request header specified');
$matcher->filter($routes, $request);
- $this->fail('No exception was thrown.');
}
}
diff --git a/core/themes/classy/templates/layout/html.html.twig b/core/themes/classy/templates/layout/html.html.twig
index 8330ccc77..4fe57a01c 100644
--- a/core/themes/classy/templates/layout/html.html.twig
+++ b/core/themes/classy/templates/layout/html.html.twig
@@ -8,7 +8,7 @@
* - root_path: The root path of the current page (e.g., node, admin, user).
* - node_type: The content type for the current node, if the page is a node.
* - head_title: List of text elements that make up the head_title variable.
- * May contain or more of the following:
+ * May contain one or more of the following:
* - title: The title of the page.
* - name: The name of the site.
* - slogan: The slogan of the site.
diff --git a/core/themes/stable/templates/admin/image-crop-summary.html.twig b/core/themes/stable/templates/admin/image-crop-summary.html.twig
index 6f3b0fcb3..e401f2423 100644
--- a/core/themes/stable/templates/admin/image-crop-summary.html.twig
+++ b/core/themes/stable/templates/admin/image-crop-summary.html.twig
@@ -16,15 +16,15 @@
*/
#}
{% if data.width and data.height -%}
- {{ data.width|e }}×{{ data.height|e }}
+ {{ data.width }}×{{ data.height }}
{%- else -%}
{% if data.width %}
{% trans %}
- width {{ data.width|e }}
+ width {{ data.width }}
{% endtrans %}
{% elseif data.height %}
{% trans %}
- height {{ data.height|e }}
+ height {{ data.height }}
{% endtrans %}
{% endif %}
{%- endif %}
diff --git a/core/themes/stable/templates/admin/image-resize-summary.html.twig b/core/themes/stable/templates/admin/image-resize-summary.html.twig
index fde09d23a..dc0ba198f 100644
--- a/core/themes/stable/templates/admin/image-resize-summary.html.twig
+++ b/core/themes/stable/templates/admin/image-resize-summary.html.twig
@@ -14,15 +14,15 @@
*/
#}
{% if data.width and data.height -%}
- {{ data.width|e }}×{{ data.height|e }}
+ {{ data.width }}×{{ data.height }}
{%- else -%}
{% if data.width %}
{% trans %}
- width {{ data.width|e }}
+ width {{ data.width }}
{% endtrans %}
{% elseif data.height %}
{% trans %}
- height {{ data.height|e }}
+ height {{ data.height }}
{% endtrans %}
{% endif %}
{%- endif %}
diff --git a/core/themes/stable/templates/admin/image-rotate-summary.html.twig b/core/themes/stable/templates/admin/image-rotate-summary.html.twig
index 542f2df80..c3f446e52 100644
--- a/core/themes/stable/templates/admin/image-rotate-summary.html.twig
+++ b/core/themes/stable/templates/admin/image-rotate-summary.html.twig
@@ -17,8 +17,9 @@
*/
#}
{% if data.random %}
+ {% set degrees = data.degrees|abs %}
{% trans %}
- random between -{{ data.degrees|abs }}° and {{ data.degrees|abs }}°
+ random between -{{ degrees }}° and {{ degrees }}°
{% endtrans %}
{% else %}
{{ data.degrees }}°
diff --git a/core/themes/stable/templates/admin/image-scale-summary.html.twig b/core/themes/stable/templates/admin/image-scale-summary.html.twig
index bf54445a0..5b7c6864c 100644
--- a/core/themes/stable/templates/admin/image-scale-summary.html.twig
+++ b/core/themes/stable/templates/admin/image-scale-summary.html.twig
@@ -15,15 +15,15 @@
*/
#}
{% if data.width and data.height -%}
- {{ data.width|e }}×{{ data.height|e }}
+ {{ data.width }}×{{ data.height }}
{%- else -%}
{% if data.width %}
{% trans %}
- width {{ data.width|e }}
+ width {{ data.width }}
{% endtrans %}
{% elseif data.height %}
{% trans %}
- height {{ data.height|e }}
+ height {{ data.height }}
{% endtrans %}
{% endif %}
{%- endif %}
diff --git a/core/themes/stable/templates/layout/html.html.twig b/core/themes/stable/templates/layout/html.html.twig
index 5e4f25e86..32079e6fa 100644
--- a/core/themes/stable/templates/layout/html.html.twig
+++ b/core/themes/stable/templates/layout/html.html.twig
@@ -8,7 +8,7 @@
* - root_path: The root path of the current page (e.g., node, admin, user).
* - node_type: The content type for the current node, if the page is a node.
* - head_title: List of text elements that make up the head_title variable.
- * May contain or more of the following:
+ * May contain one or more of the following:
* - title: The title of the page.
* - name: The name of the site.
* - slogan: The slogan of the site.