Commit 742dec39 authored by alexpott's avatar alexpott

Issue #2589237 by chx, webflo, benjy, dmoore: Menu links parent migration is broken

parent 5636af45
id: d6_menu_links
label: Menu links
migration_tags:
- Drupal 6
source:
plugin: menu_link
constants:
bundle: menu_link_content
process:
id: mlid
bundle: 'constants/bundle'
title: link_title
description:
plugin: extract
source:
- options
index:
- 0
- attributes
- title
default: ''
menu_name:
plugin: migration
migration: menu
source: menu_name
'link/uri':
plugin: internal_uri
source:
- link_path
'link/options': options
external: external
weight: weight
expanded: expanded
enabled: enabled
parent:
-
plugin: skip_on_empty
method: process
source: plid
-
plugin: migration
migration: d6_menu_links
changed: updated
destination:
plugin: entity:menu_link_content
migration_dependencies:
required:
- menu
id: d7_menu_links
id: menu_links
label: Menu links
migration_tags:
- Drupal 6
- Drupal 7
source:
plugin: menu_link
......@@ -10,19 +11,17 @@ process:
id: mlid
bundle: 'constants/bundle'
title: link_title
description:
plugin: extract
source:
- options
index:
- 0
- attributes
- title
default: ''
description: description
menu_name:
plugin: migration
migration: menu
source: menu_name
-
plugin: migration
migration: menu
source: menu_name
-
plugin: static_map
map:
management: admin
bypass: true
'link/uri':
plugin: internal_uri
source:
......@@ -42,16 +41,15 @@ process:
expanded: expanded
enabled: enabled
parent:
-
plugin: skip_on_empty
method: process
source: plid
-
plugin: migration
migration: d7_menu_links
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:
- menu
......@@ -7,6 +7,7 @@
namespace Drupal\menu_link_content\Plugin\migrate\source;
use Drupal\Component\Utility\Unicode;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
use Drupal\migrate\Row;
......@@ -23,12 +24,20 @@ class MenuLink extends DrupalSqlBase {
* {@inheritdoc}
*/
public function query() {
return $this->select('menu_links', 'ml')
->fields('ml')
->orderby('ml.depth')
->orderby('ml.mlid')
->condition('module', 'menu')
->condition('customized', 1);
$query = $this->select('menu_links', 'ml')
->fields('ml');
$and = $query->andConditionGroup()
->condition('ml.module', 'menu')
->condition('ml.router_path', ['admin/build/menu-customize/%', 'admin/structure/menu/manage/%'], 'NOT IN');
$condition = $query->orConditionGroup()
->condition('ml.customized', 1)
->condition($and);
$query->condition($condition);
$query->leftJoin('menu_links', 'pl', 'ml.plid = pl.mlid');
$query->addField('pl', 'link_path', 'parent_link_path');
$query->orderBy('ml.depth');
$query->orderby('ml.mlid');
return $query;
}
/**
......@@ -70,6 +79,7 @@ public function fields() {
public function prepareRow(Row $row) {
$row->setSourceProperty('options', unserialize($row->getSourceProperty('options')));
$row->setSourceProperty('enabled', !$row->getSourceProperty('hidden'));
$row->setSourceProperty('description', Unicode::truncate($row->getSourceProperty('options/attributes/title'), 255));
return parent::prepareRow($row);
}
......@@ -79,6 +89,7 @@ public function prepareRow(Row $row) {
*/
public function getIds() {
$ids['mlid']['type'] = 'integer';
$ids['mlid']['alias'] = 'ml';
return $ids;
}
......
......@@ -29,7 +29,7 @@ protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
$this->installEntitySchema('menu_link_content');
$this->executeMigrations(['menu', 'd6_menu_links']);
$this->executeMigrations(['menu', 'menu_links']);
}
/**
......
......@@ -8,6 +8,8 @@
namespace Drupal\menu_link_content\Tests\Migrate\d7;
use Drupal\Core\Database\Database;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Url;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\menu_link_content\MenuLinkContentInterface;
use Drupal\migrate_drupal\Tests\d7\MigrateDrupal7TestBase;
......@@ -18,6 +20,7 @@
* @group menu_link_content
*/
class MigrateMenuLinkTest extends MigrateDrupal7TestBase {
const MENU_NAME = 'menu-test-menu';
/**
* {@inheritdoc}
......@@ -32,6 +35,7 @@ protected function setUp() {
$this->installSchema('system', ['router']);
$this->installEntitySchema('menu_link_content');
$this->executeMigration('menu');
\Drupal::service('router.builder')->rebuild();
}
/**
......@@ -70,16 +74,44 @@ protected function assertEntity($id, $title, $menu, $description, $enabled, $exp
$this->assertIdentical($attributes, $menu_link->link->options);
$this->assertIdentical($uri, $menu_link->link->uri);
$this->assertIdentical($weight, $menu_link->getWeight());
return $menu_link;
}
/**
* Tests migration of menu links.
*/
public function testMenuLinks() {
$this->executeMigration('d7_menu_links');
$this->assertEntity(467, 'Google', 'menu-test-menu', 'Google', TRUE, FALSE, ['attributes' => ['title' => 'Google']], 'http://google.com', 0);
$this->assertEntity(468, 'Yahoo', 'menu-test-menu', 'Yahoo', TRUE, FALSE, ['attributes' => ['title' => 'Yahoo']], 'http://yahoo.com', 0);
$this->assertEntity(469, 'Bing', 'menu-test-menu', 'Bing', TRUE, FALSE, ['attributes' => ['title' => 'Bing']], 'http://bing.com', 0);
$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);
$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));
$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');
$google_found = TRUE;
}
}
$this->assertEqual(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());
if ($menu_link_tree_element->link->getTitle() == 'custom link test') {
$found = TRUE;
break;
}
}
$this->assertTrue($found);
}
/**
......@@ -94,8 +126,8 @@ public function testUndefinedLinkTitle() {
->condition('mlid', 467)
->execute();
$this->executeMigration('d7_menu_links');
$this->assertEntity(467, 'Google', 'menu-test-menu', NULL, TRUE, FALSE, [], 'http://google.com', 0);
$this->executeMigration('menu_links');
$this->assertEntity(467, 'Google', static::MENU_NAME, NULL, TRUE, FALSE, [], 'http://google.com', 0);
}
}
......@@ -6,6 +6,7 @@
*/
namespace Drupal\Tests\menu_link_content\Unit\Plugin\migrate\source;
use Drupal\Component\Utility\Unicode;
use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase;
/**
......@@ -25,6 +26,92 @@ class MenuLinkSourceTest extends MigrateSqlSourceTestCase {
);
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,
......@@ -51,6 +138,7 @@ class MenuLinkSourceTest extends MigrateSqlSourceTestCase {
'p8' => '0',
'p9' => '0',
'updated' => '0',
'description' => 'Test menu link 1',
),
array(
'menu_name' => 'menu-test-menu',
......@@ -78,6 +166,7 @@ class MenuLinkSourceTest extends MigrateSqlSourceTestCase {
'p8' => '0',
'p9' => '0',
'updated' => '0',
'description' => 'Test menu link 2',
),
);
......@@ -85,10 +174,47 @@ class MenuLinkSourceTest extends MigrateSqlSourceTestCase {
* {@inheritdoc}
*/
public function setUp() {
foreach ($this->expectedResults as $k => $row) {
$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']);
$this->databaseContents['menu_links'][$k] = $row;
}
});
parent::setUp();
}
......
......@@ -320,10 +320,10 @@ protected function getProcessNormalized(array $process) {
* {@inheritdoc}
*/
public function getDestinationPlugin($stub_being_requested = FALSE) {
if ($stub_being_requested && !empty($this->destination['no_stub'])) {
throw new MigrateSkipRowException;
}
if (!isset($this->destinationPlugin)) {
if ($stub_being_requested && !empty($this->destination['no_stub'])) {
throw new MigrateSkipRowException;
}
$this->destinationPlugin = \Drupal::service('plugin.manager.migrate.destination')->createInstance($this->destination['plugin'], $this->destination, $this);
}
return $this->destinationPlugin;
......
<?php
/**
* @file
* Contains \Drupal\migrate\Plugin\migrate\process\MenuLinkContent.
*/
namespace Drupal\migrate\Plugin\migrate\process;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Menu\MenuLinkManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Url;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\Plugin\MigrateProcessInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* This plugin figures out menu link parent plugin IDs.
*
* @MigrateProcessPlugin(
* id = "menu_link_parent"
* )
*/
class MenuLinkParent extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
*/
protected $menuLinkManager;
/**
* @var \Drupal\migrate\Plugin\MigrateProcessInterface
*/
protected $migrationPlugin;
/**
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $menuLinkStorage;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrateProcessInterface $migration_plugin, MenuLinkManagerInterface $menu_link_manager, EntityStorageInterface $menu_link_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->migrationPlugin = $migration_plugin;
$this->menuLinkManager = $menu_link_manager;
$this->menuLinkStorage = $menu_link_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
$migration_configuration['migration'][] = $migration->id();
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('plugin.manager.migrate.process')->createInstance('migration', $migration_configuration, $migration),
$container->get('plugin.manager.menu.link'),
$container->get('entity.manager')->getStorage('menu_link_content')
);
}
/**
* {@inheritdoc}
*
* Find the parent link GUID.
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
$parent_id = array_shift($value);
if (!$parent_id) {
// Top level item.
return '';
}
try {
$already_migrated_id = $this
->migrationPlugin
->transform($parent_id, $migrate_executable, $row, $destination_property);
if ($already_migrated_id && ($link = $this->menuLinkStorage->load($already_migrated_id))) {
return $link->getPluginId();
}
}
catch (MigrateSkipRowException $e) {
}
if (isset($value[1])) {
list($menu_name, $parent_link_path) = $value;
$url = Url::fromUserInput("/$parent_link_path");
if ($url->isRouted()) {
$links = $this->menuLinkManager->loadLinksByRoute($url->getRouteName(), $url->getRouteParameters(), $menu_name);
if (count($links) == 1) {
/** @var \Drupal\Core\Menu\MenuLinkInterface $link */
$link = reset($links);
return $link->getPluginId();
}
}
}
throw new MigrateSkipRowException;
}
}
......@@ -18871,7 +18871,7 @@
->values(array(
'menu_name' => 'menu-test-menu',
'mlid' => '467',
'plid' => '0',
'plid' => '469',
'link_path' => 'http://google.com',
'router_path' => '',
'link_title' => 'Google',
......@@ -18882,10 +18882,10 @@
'has_children' => '0',
'expanded' => '0',
'weight' => '0',
'depth' => '1',
'depth' => '2',
'customized' => '1',
'p1' => '467',
'p2' => '0',
'p1' => '469',
'p2' => '467',
'p3' => '0',
'p4' => '0',
'p5' => '0',
......@@ -19057,6 +19057,33 @@
'p9' => '0',
'updated' => '0',
))
->values(array(
'menu_name' => 'management',
'mlid' => '478',
'plid' => '20',
'link_path' => 'admin/content/book',
'router_path' => 'admin/content/book',
'link_title' => 'custom link test',
'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:0:"";}}',
'module' => 'menu',
'hidden' => '0',
'external' => '0',
'has_children' => '0',
'expanded' => '0',
'weight' => '0',
'depth' => '3',
'customized' => '1',
'p1' => '1',
'p2' => '20',
'p3' => '478',
'p4' => '0',
'p5' => '0',
'p6' => '0',
'p7' => '0',
'p8' => '0',
'p9' => '0',
'updated' => '0',
))
->execute();
$connection->schema()->createTable('menu_router', array(
......@@ -23,4 +23,4 @@ destination:
migration_dependencies:
required:
- d7_shortcut_set
- d7_menu_links
- menu_links
......@@ -38,9 +38,10 @@ protected function setUp() {
$this->installSchema('system', array('router'));
$this->installEntitySchema('shortcut');
$this->installEntitySchema('menu_link_content');
\Drupal::service('router.builder')->rebuild();