diff --git a/core/modules/field/src/Plugin/migrate/source/d7/Field.php b/core/modules/field/src/Plugin/migrate/source/d7/Field.php index f978146afba9e8b87c7af34882995a9c7ce4d2b6..3d123115348d53c08619eb345913254afda4546c 100644 --- a/core/modules/field/src/Plugin/migrate/source/d7/Field.php +++ b/core/modules/field/src/Plugin/migrate/source/d7/Field.php @@ -59,6 +59,7 @@ public function fields() { return [ 'id' => $this->t('The field ID.'), 'field_name' => $this->t('The field name.'), + 'entity_type' => $this->t('Entity type'), 'type' => $this->t('The field type.'), 'module' => $this->t('The module that implements the field type.'), 'active' => $this->t('The field status.'), diff --git a/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php b/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php index 4e0fcd24a6770bee2e04e0ea3548b423682ae1a3..1f050f82a57fca02748840e4b5e95a1d0dc2429f 100644 --- a/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php +++ b/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php @@ -105,13 +105,19 @@ public function fields() { 'p9' => $this->t('The ninth mlid in the materialized path. See p1.'), 'updated' => $this->t('Flag that indicates that this link was generated during the update from Drupal 5.'), ]; - $schema = $this->getDatabase()->schema(); - if ($schema->fieldExists('menu_links', 'language')) { - $fields['language'] = $this->t("Menu link language code."); - } - if ($schema->fieldExists('menu_links', 'i18n_tsid')) { - $fields['i18n_tsid'] = $this->t("Translation set id."); + + // The database connection may not exist, for example, when building + // the Migrate Message form. + if ($source_database = $this->database) { + $schema = $source_database->schema(); + if ($schema->fieldExists('menu_links', 'language')) { + $fields['language'] = $this->t("Menu link language code."); + } + if ($schema->fieldExists('menu_links', 'i18n_tsid')) { + $fields['i18n_tsid'] = $this->t("Translation set id."); + } } + return $fields; } diff --git a/core/modules/migrate/migrate.links.menu.yml b/core/modules/migrate/migrate.links.menu.yml new file mode 100644 index 0000000000000000000000000000000000000000..f63764ef67c3254cc666caecda117398e9e6dd7d --- /dev/null +++ b/core/modules/migrate/migrate.links.menu.yml @@ -0,0 +1,6 @@ +migrate.messages: + title: Migration messages + parent: system.admin_reports + description: View the migration messages. + route_name: migrate.messages + weight: 0 diff --git a/core/modules/migrate/migrate.permissions.yml b/core/modules/migrate/migrate.permissions.yml new file mode 100644 index 0000000000000000000000000000000000000000..90f41718bdb50921425acaf27e9bfcfbd36cce58 --- /dev/null +++ b/core/modules/migrate/migrate.permissions.yml @@ -0,0 +1,2 @@ +view migration messages: + title: 'View migration messages' diff --git a/core/modules/migrate/migrate.routing.yml b/core/modules/migrate/migrate.routing.yml new file mode 100644 index 0000000000000000000000000000000000000000..3899c6fa9638f38e1ccfd52a9806110de7b8b511 --- /dev/null +++ b/core/modules/migrate/migrate.routing.yml @@ -0,0 +1,15 @@ +migrate.messages: + path: '/admin/reports/migration-messages' + defaults: + _controller: '\Drupal\migrate\Controller\MigrateMessageController::overview' + _title: 'Migration messages' + requirements: + _permission: 'view migration messages' + +migrate.messages.detail: + path: '/admin/reports/migration-messages/{migration_id}' + defaults: + _controller: '\Drupal\migrate\Controller\MigrateMessageController::details' + _title_callback: '\Drupal\migrate\Controller\MigrateMessageController::title' + requirements: + _permission: 'view migration messages' diff --git a/core/modules/migrate/src/Controller/MigrateMessageController.php b/core/modules/migrate/src/Controller/MigrateMessageController.php new file mode 100644 index 0000000000000000000000000000000000000000..360482801e1dbe5f311fc1bca10f026456790389 --- /dev/null +++ b/core/modules/migrate/src/Controller/MigrateMessageController.php @@ -0,0 +1,307 @@ +<?php + +namespace Drupal\migrate\Controller; + +use Drupal\Core\Controller\ControllerBase; +use Drupal\Core\Database\Connection; +use Drupal\Core\Database\DatabaseConnectionRefusedException; +use Drupal\Core\Database\DatabaseNotFoundException; +use Drupal\Core\Form\FormBuilderInterface; +use Drupal\Core\StringTranslation\TranslatableMarkup; +use Drupal\Core\Url; +use Drupal\migrate\Exception\RequirementsException; +use Drupal\migrate\Plugin\MigrationInterface; +use Drupal\migrate\Plugin\MigrationPluginManagerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +// cspell:ignore sourceid + +/** + * Provides controller methods for the Message form. + */ +class MigrateMessageController extends ControllerBase { + + /** + * Constructs a MigrateController. + * + * @param \Drupal\Core\Database\Connection $database + * A database connection. + * @param \Drupal\Core\Form\FormBuilderInterface $formBuilder + * The form builder service. + * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migrationPluginManager + * The migration plugin manager. + */ + public function __construct( + protected Connection $database, + FormBuilderInterface $formBuilder, + protected MigrationPluginManagerInterface $migrationPluginManager, + ) { + $this->formBuilder = $formBuilder; + } + + /** + * Displays an overview of migrate messages. + * + * @return array + * A render array as expected by + * \Drupal\Core\Render\RendererInterface::render(). + */ + public function overview(): array { + // Check if there are migrate_message tables. + $tables = $this->database->schema()->findTables('migrate_message_%'); + if (empty($tables)) { + $build['no_tables'] = [ + '#type' => 'item', + '#markup' => $this->t('There are no migration message tables.'), + ]; + return $build; + } + + // There are migrate_message tables so build the overview form. + $migrations = $this->migrationPluginManager->createInstances([]); + + $header = [ + $this->t('Migration'), + $this->t('Machine Name'), + $this->t('Messages'), + ]; + + // Display the number of messages for each migration. + $rows = []; + foreach ($migrations as $id => $migration) { + $message_count = $migration->getIdMap()->messageCount(); + // The message count is zero when there are no messages or when the + // message table does not exist. + if ($message_count == 0) { + continue; + } + $row = []; + $row['label'] = $migration->label(); + $row['machine_name'] = $id; + $route_parameters = [ + 'migration_id' => $migration->id(), + ]; + $row['messages'] = [ + 'data' => [ + '#type' => 'link', + '#title' => $message_count, + '#url' => Url::fromRoute('migrate.messages.detail', $route_parameters), + ], + ]; + $rows[] = $row; + } + + $build['migrations_table'] = [ + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + '#empty' => $this->t('No migration messages available.'), + ]; + $build['message_pager'] = ['#type' => 'pager']; + + return $build; + } + + /** + * Displays a listing of migration messages for the given migration ID. + * + * @param string $migration_id + * A migration ID. + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * + * @return array + * A render array. + */ + public function details(string $migration_id, Request $request): array { + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ + $migration = $this->migrationPluginManager->createInstance($migration_id); + + if (!$migration) { + throw new NotFoundHttpException(); + } + + // Get the map and message table names. + $map_table = $migration->getIdMap()->mapTableName(); + $message_table = $migration->getIdMap()->messageTableName(); + + // If the map table does not exist then do not continue. + if (!$this->database->schema()->tableExists($map_table)) { + throw new NotFoundHttpException(); + } + + // If there is a map table but no message table display an error. + if (!$this->database->schema()->tableExists($message_table)) { + $this->messenger()->addError($this->t('The message table is missing for this migration.')); + return []; + } + + // Create the column header names. + $header = []; + $source_plugin = $migration->getSourcePlugin(); + // Create the column header names from the source plugin fields() method. + // Fallback to the source_id name when the source ID is missing from + // fields() method. + try { + $fields = $source_plugin->fields(); + } + catch (DatabaseConnectionRefusedException | DatabaseNotFoundException | RequirementsException | \PDOException $e) { + } + + $source_id_field_names = array_keys($source_plugin->getIds()); + $count = 1; + foreach ($source_id_field_names as $source_id_field_name) { + $display_name = preg_replace( + [ + '/^[Tt]he /', + '/\.$/', + ], '', $fields[$source_id_field_name] ?? $source_id_field_name); + $header[] = [ + 'data' => ucfirst($display_name), + 'field' => 'sourceid' . $count++, + 'class' => [RESPONSIVE_PRIORITY_MEDIUM], + ]; + } + + $header[] = [ + 'data' => $this->t('Severity level'), + 'field' => 'level', + 'class' => [RESPONSIVE_PRIORITY_LOW], + ]; + $header[] = [ + 'data' => $this->t('Message'), + 'field' => 'message', + ]; + + $levels = [ + MigrationInterface::MESSAGE_ERROR => $this->t('Error'), + MigrationInterface::MESSAGE_WARNING => $this->t('Warning'), + MigrationInterface::MESSAGE_NOTICE => $this->t('Notice'), + MigrationInterface::MESSAGE_INFORMATIONAL => $this->t('Info'), + ]; + + // Gets each message row and the source ID(s) for that message. + $query = $this->database->select($message_table, 'msg') + ->extend('\Drupal\Core\Database\Query\PagerSelectExtender') + ->extend('\Drupal\Core\Database\Query\TableSortExtender'); + // Not all messages have a matching row in the map table. + $query->leftJoin($map_table, 'map', 'msg.source_ids_hash = map.source_ids_hash'); + $query->fields('msg'); + $query->fields('map'); + $filter = $this->buildFilterQuery($request); + if (!empty($filter['where'])) { + $query->where($filter['where'], $filter['args']); + } + $result = $query + ->limit(50) + ->orderByHeader($header) + ->execute(); + + // Build the rows to display. + $rows = []; + $add_explanation = FALSE; + $num_ids = count($source_id_field_names); + foreach ($result as $message_row) { + $new_row = []; + for ($count = 1; $count <= $num_ids; $count++) { + $map_key = 'sourceid' . $count; + $new_row[$map_key] = $message_row->$map_key ?? NULL; + if (empty($new_row[$map_key])) { + $new_row[$map_key] = $this->t('Not available'); + $add_explanation = TRUE; + } + } + $new_row['level'] = $levels[$message_row->level]; + $new_row['message'] = $message_row->message; + $rows[] = $new_row; + } + + // Build the complete form. + $build['message_filter_form'] = $this->formBuilder->getForm('Drupal\migrate\Form\MessageForm'); + $build['message_table'] = [ + '#type' => 'table', + '#header' => $header, + '#rows' => $rows, + '#empty' => $this->t('No messages for this migration.'), + '#attributes' => ['id' => 'admin-migrate-msg', 'class' => ['admin-migrate-msg']], + ]; + $build['message_pager'] = ['#type' => 'pager']; + + if ($add_explanation) { + $build['explanation'] = [ + '#type' => 'item', + '#markup' => $this->t("When there is an error processing a row, the migration system saves the error message but not the source ID(s) of the row. That is why some messages in this table have 'Not available' in the source ID column(s)."), + ]; + } + return $build; + } + + /** + * Builds a query for migrate message administration. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * + * @return array|null + * An associative array with keys 'where' and 'args' or NULL if there were + * no filters set. + */ + protected function buildFilterQuery(Request $request): ?array { + $session_filters = $request->getSession()->get('migration_messages_overview_filter', []); + if (empty($session_filters)) { + return NULL; + } + + // Build query. + $where = $args = []; + foreach ($session_filters as $filter) { + $filter_where = []; + + switch ($filter['type']) { + case 'array': + foreach ($filter['value'] as $value) { + $filter_where[] = $filter['where']; + $args[] = $value; + } + break; + + case 'string': + $filter_where[] = $filter['where']; + $args[] = '%' . $filter['value'] . '%'; + break; + + default: + $filter_where[] = $filter['where']; + $args[] = $filter['value']; + } + + if (!empty($filter_where)) { + $where[] = '(' . implode(' OR ', $filter_where) . ')'; + } + } + $where = !empty($where) ? implode(' AND ', $where) : ''; + + return [ + 'where' => $where, + 'args' => $args, + ]; + } + + /** + * Gets the title for the details page. + * + * @param string $migration_id + * A migration ID. + * + * @return \Drupal\Core\StringTranslation\TranslatableMarkup + * The translated title. + */ + public function title(string $migration_id): TranslatableMarkup { + return $this->t( + 'Messages of %migration', + ['%migration' => $migration_id] + ); + } + +} diff --git a/core/modules/migrate/src/Form/MessageForm.php b/core/modules/migrate/src/Form/MessageForm.php new file mode 100644 index 0000000000000000000000000000000000000000..75522351418d19d88dd99ee46811dabb9ade4c72 --- /dev/null +++ b/core/modules/migrate/src/Form/MessageForm.php @@ -0,0 +1,118 @@ +<?php + +namespace Drupal\migrate\Form; + +use Drupal\Core\Form\FormBase; +use Drupal\Core\Form\FormStateInterface; +use Drupal\migrate\Plugin\MigrationInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Migrate messages form. + * + * @internal + */ +class MessageForm extends FormBase { + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + $form = new static(); + $form->setStringTranslation($container->get('string_translation')); + return $form; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'migrate_messages_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $session_filters = $this->getRequest()->getSession()->get('migration_messages_overview_filter', []); + $form['filters'] = [ + '#type' => 'details', + '#open' => TRUE, + '#title' => $this->t('Filter messages'), + '#weight' => 0, + ]; + $form['filters']['message'] = [ + '#type' => 'textfield', + '#title' => $this->t('Message'), + '#default_value' => $session_filters['message']['value'] ?? '', + ]; + $form['filters']['severity'] = [ + '#type' => 'select', + '#title' => $this->t('Severity level'), + '#default_value' => $session_filters['severity']['value'] ?? [], + '#options' => [ + MigrationInterface::MESSAGE_ERROR => $this->t('Error'), + MigrationInterface::MESSAGE_WARNING => $this->t('Warning'), + MigrationInterface::MESSAGE_NOTICE => $this->t('Notice'), + MigrationInterface::MESSAGE_INFORMATIONAL => $this->t('Info'), + ], + '#multiple' => TRUE, + '#size' => 4, + ]; + $form['filters']['actions'] = [ + '#type' => 'actions', + '#attributes' => ['class' => ['container-inline']], + ]; + $form['filters']['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Filter'), + ]; + $form['filters']['actions']['reset'] = [ + '#type' => 'submit', + '#value' => $this->t('Reset'), + '#submit' => ['::resetForm'], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $filters['message'] = [ + 'title' => $this->t('message'), + 'where' => 'msg.message LIKE ?', + 'type' => 'string', + ]; + $filters['severity'] = [ + 'title' => $this->t('Severity'), + 'where' => 'msg.level = ?', + 'type' => 'array', + ]; + $session_filters = $this->getRequest()->getSession()->get('migration_messages_overview_filter', []); + foreach ($filters as $name => $filter) { + if ($form_state->hasValue($name)) { + $session_filters[$name] = [ + 'where' => $filter['where'], + 'value' => $form_state->getValue($name), + 'type' => $filter['type'], + ]; + } + } + $this->getRequest()->getSession()->set('migration_messages_overview_filter', $session_filters); + } + + /** + * Resets the filter form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function resetForm(array $form, FormStateInterface $form_state): void { + $this->getRequest()->getSession()->remove('migration_messages_overview_filter'); + } + +} diff --git a/core/modules/migrate/tests/modules/message_test/message_test.info.yml b/core/modules/migrate/tests/modules/message_test/message_test.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..52c9270241511d5e69ebc993d96cd3399f13d2c8 --- /dev/null +++ b/core/modules/migrate/tests/modules/message_test/message_test.info.yml @@ -0,0 +1,5 @@ +name: Test display of migrate message +type: module +description: Tests the display of migrate messages. +package: Testing +version: VERSION diff --git a/core/modules/migrate/tests/modules/message_test/migrations/custom_test.yml b/core/modules/migrate/tests/modules/message_test/migrations/custom_test.yml new file mode 100644 index 0000000000000000000000000000000000000000..6d69485f44154a46d41e0162b7d4dd97183add21 --- /dev/null +++ b/core/modules/migrate/tests/modules/message_test/migrations/custom_test.yml @@ -0,0 +1,13 @@ +id: custom_test +label: Test display of upgrade messages +source: + plugin: embedded_data + data_rows: + - id: 0 + ids: + id: + type: integer +process: + id: id +destination: + plugin: null diff --git a/core/modules/migrate/tests/modules/message_test/migrations/custom_test_db.yml b/core/modules/migrate/tests/modules/message_test/migrations/custom_test_db.yml new file mode 100644 index 0000000000000000000000000000000000000000..43ca4b7da879d892fbbde91df5ee666b4d306764 --- /dev/null +++ b/core/modules/migrate/tests/modules/message_test/migrations/custom_test_db.yml @@ -0,0 +1,9 @@ +id: custom_test_db +label: Test display of upgrade messages +source: + plugin: extension + name: i18n_menu +process: + id: id +destination: + plugin: null diff --git a/core/modules/migrate/tests/src/Functional/MigrateMessageControllerTest.php b/core/modules/migrate/tests/src/Functional/MigrateMessageControllerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..16474bc32cbfddd9958d77f1c62f76d4c5cd8a4e --- /dev/null +++ b/core/modules/migrate/tests/src/Functional/MigrateMessageControllerTest.php @@ -0,0 +1,91 @@ +<?php + +namespace Drupal\Tests\migrate\Functional; + +/** + * Tests for the MigrateController class. + * + * @group migrate + */ +class MigrateMessageControllerTest extends MigrateMessageTestBase { + + /** + * Tests the overview page for migrate messages. + * + * Tests the overview page with the following scenarios; + * - No message tables. + * - With message tables. + */ + public function testOverview(): void { + $session = $this->assertSession(); + + // First, test with no source database or message tables. + $this->drupalGet('/admin/reports/migration-messages'); + $session->titleEquals('Migration messages | Drupal'); + $session->pageTextContainsOnce('There are no migration message tables.'); + + // Create map and message tables. + $this->createTables($this->migrationIds); + + // Now, test with message tables. + $this->drupalGet('/admin/reports/migration-messages'); + foreach ($this->migrationIds as $migration_id) { + $session->pageTextContains($migration_id); + } + } + + /** + * Tests the detail pages for migrate messages. + * + * Tests the detail page with the following scenarios; + * - No source database connection or message tables with a valid and an + * invalid migration. + * - A source database connection with message tables with a valid and an + * invalid migration. + * - A source database connection with message tables and a source plugin + * that does not have a description for a source ID in the values returned + * from fields(). + */ + public function testDetail(): void { + $session = $this->assertSession(); + + // Details page with invalid migration. + $this->drupalGet('/admin/reports/migration-messages/invalid'); + $session->statusCodeEquals(404); + + // Details page with valid migration. + $this->drupalGet('/admin/reports/migration-messages/custom_test'); + $session->statusCodeEquals(404); + + // Create map and message tables. + $this->createTables($this->migrationIds); + + $not_available_text = "When there is an error processing a row, the migration system saves the error message but not the source ID(s) of the row. That is why some messages in this table have 'Not available' in the source ID column(s)."; + + // Test details page for each migration. + foreach ($this->migrationIds as $migration_id) { + $this->drupalGet("/admin/reports/migration-messages/$migration_id"); + $session->pageTextContains($migration_id); + if ($migration_id == 'custom_test') { + $session->pageTextContains('Not available'); + $session->pageTextContains($not_available_text); + } + } + + // Details page with invalid migration. + $this->drupalGet('/admin/reports/migration-messages/invalid'); + $session->statusCodeEquals(404); + + // Details page for a migration without a map table. + $this->database->schema()->dropTable('migrate_map_custom_test'); + $this->drupalGet('/admin/reports/migration-messages/custom_test'); + $session->statusCodeEquals(404); + + // Details page for a migration with a map table but no message table. + $this->createTables($this->migrationIds); + $this->database->schema()->dropTable('migrate_message_custom_test'); + $this->drupalGet('/admin/reports/migration-messages/custom_test'); + $session->pageTextContains('The message table is missing for this migration.'); + } + +} diff --git a/core/modules/migrate/tests/src/Functional/MigrateMessageFormTest.php b/core/modules/migrate/tests/src/Functional/MigrateMessageFormTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8cead155e1d4ee4724ede019e610462fb7537bd4 --- /dev/null +++ b/core/modules/migrate/tests/src/Functional/MigrateMessageFormTest.php @@ -0,0 +1,103 @@ +<?php + +namespace Drupal\Tests\migrate\Functional; + +use Drupal\migrate\Plugin\MigrationInterface; + +/** + * Tests for the MessageForm class. + * + * @group migrate + */ +class MigrateMessageFormTest extends MigrateMessageTestBase { + + /** + * Tests the message form. + */ + public function testFilter(): void { + $session = $this->assertSession(); + + // Create map and message tables. + $this->createTables($this->migrationIds); + + // Expected counts for each error level. + $expected = [ + MigrationInterface::MESSAGE_ERROR => 3, + MigrationInterface::MESSAGE_WARNING => 0, + MigrationInterface::MESSAGE_NOTICE => 0, + MigrationInterface::MESSAGE_INFORMATIONAL => 1, + ]; + + // Confirm that all the entries are displayed. + $this->drupalGet('/admin/reports/migration-messages/custom_test'); + $session->statusCodeEquals(200); + $messages = $this->getMessages(); + $this->assertCount(4, $messages); + + // Set the filter to match each of the two filter-type attributes and + // confirm the correct number of entries are displayed. + foreach ($expected as $level => $expected_count) { + $edit['severity[]'] = $level; + $this->submitForm($edit, 'Filter'); + $count = $this->getLevelCounts($expected); + $this->assertEquals($expected_count, $count[$level], sprintf('Count for level %s failed', $level)); + } + + // Reset the filter + $this->submitForm([], 'Reset'); + $messages = $this->getMessages(); + $this->assertCount(4, $messages); + } + + /** + * Gets the count of migration messages by level. + * + * @param array $levels + * The error levels to check. + * + * @return array + * The count of each error level keyed by the error level. + */ + protected function getLevelCounts(array $levels): array { + $entries = $this->getMessages(); + $count = array_fill(1, count($levels), 0); + foreach ($entries as $entry) { + if (array_key_exists($entry['severity'], $levels)) { + $count[$entry['severity']]++; + } + } + return $count; + } + + /** + * Gets the migrate messages. + * + * @return array[] + * List of log events where each event is an array with following keys: + * - msg_id: (string) A message id. + * - severity: (int) The MigrationInterface error level. + * - message: (string) The migration message. + */ + protected function getMessages(): array { + $levels = [ + 'Error' => MigrationInterface::MESSAGE_ERROR, + 'Warning' => MigrationInterface::MESSAGE_WARNING, + 'Notice' => MigrationInterface::MESSAGE_NOTICE, + 'Info' => MigrationInterface::MESSAGE_INFORMATIONAL, + ]; + $entries = []; + $table = $this->xpath('.//table[@id="admin-migrate-msg"]/tbody/tr'); + foreach ($table as $row) { + $cells = $row->findAll('css', 'td'); + if (count($cells) === 3) { + $entries[] = [ + 'msg_id' => $cells[0]->getText(), + 'severity' => $levels[$cells[1]->getText()], + 'message' => $cells[2]->getText(), + ]; + } + } + return $entries; + } + +} diff --git a/core/modules/migrate/tests/src/Functional/MigrateMessageTestBase.php b/core/modules/migrate/tests/src/Functional/MigrateMessageTestBase.php new file mode 100644 index 0000000000000000000000000000000000000000..9840eb73213111be088a68ee7bec53089a2194b4 --- /dev/null +++ b/core/modules/migrate/tests/src/Functional/MigrateMessageTestBase.php @@ -0,0 +1,237 @@ +<?php + +namespace Drupal\Tests\migrate\Functional; + +use Drupal\Tests\BrowserTestBase; +use Drupal\migrate\Plugin\MigrateIdMapInterface; +use Drupal\migrate\Plugin\MigrationInterface; + +// cspell:ignore destid sourceid + +/** + * Provides base class for testing migrate messages. + * + * @group migrate + */ +class MigrateMessageTestBase extends BrowserTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'message_test', + 'migrate', + ]; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * The database connection. + * + * @var \Drupal\Core\Database\Connection + */ + protected $database; + + /** + * Migration IDs. + */ + protected $migrationIds = ['custom_test']; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $user = $this->createUser(['view migration messages']); + $this->drupalLogin($user); + $this->database = \Drupal::database(); + } + + /** + * Creates map and message tables for testing. + * + * @see \Drupal\migrate\Plugin\migrate\id_map\Sql::ensureTables + */ + protected function createTables($migration_ids): void { + foreach ($migration_ids as $migration_id) { + $map_table_name = "migrate_map_$migration_id"; + $message_table_name = "migrate_message_$migration_id"; + + if (!$this->database->schema()->tableExists($map_table_name)) { + $fields = []; + $fields['source_ids_hash'] = [ + 'type' => 'varchar', + 'length' => '64', + 'not null' => TRUE, + ]; + $fields['sourceid1'] = [ + 'type' => 'varchar', + 'length' => '255', + 'not null' => TRUE, + ]; + $fields['destid1'] = [ + 'type' => 'varchar', + 'length' => '255', + 'not null' => FALSE, + ]; + $fields['source_row_status'] = [ + 'type' => 'int', + 'size' => 'tiny', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => MigrateIdMapInterface::STATUS_IMPORTED, + ]; + $fields['rollback_action'] = [ + 'type' => 'int', + 'size' => 'tiny', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => MigrateIdMapInterface::ROLLBACK_DELETE, + ]; + $fields['last_imported'] = [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ]; + $fields['hash'] = [ + 'type' => 'varchar', + 'length' => '64', + 'not null' => FALSE, + ]; + $schema = [ + 'description' => '', + 'fields' => $fields, + 'primary key' => ['source_ids_hash'], + ]; + $this->database->schema()->createTable($map_table_name, $schema); + + $rows = [ + [ + 'source_ids_hash' => '37c655d', + 'sourceid1' => 'navigation', + 'destid1' => 'tools', + 'source_row_status' => '0', + 'rollback_action' => '1', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => '3a34190', + 'sourceid1' => 'menu-fixed-lang', + 'destid1' => 'menu-fixed-lang', + 'source_row_status' => '0', + 'rollback_action' => '0', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => '3e51f67', + 'sourceid1' => 'management', + 'destid1' => 'admin', + 'source_row_status' => '0', + 'rollback_action' => '1', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => '94a5caa', + 'sourceid1' => 'user-menu', + 'destid1' => 'account', + 'source_row_status' => '0', + 'rollback_action' => '1', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => 'c0efbcca', + 'sourceid1' => 'main-menu', + 'destid1' => 'main', + 'source_row_status' => '0', + 'rollback_action' => '1', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => 'f64cb72f', + 'sourceid1' => 'menu-test-menu', + 'destid1' => 'menu-test-menu', + 'source_row_status' => '0', + 'rollback_action' => '0', + 'last_imported' => '0', + 'hash' => '', + ], + ]; + foreach ($rows as $row) { + $this->database->insert($map_table_name)->fields($row)->execute(); + } + } + + if (!$this->database->schema()->tableExists($message_table_name)) { + $fields = []; + $fields['msgid'] = [ + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ]; + $fields['source_ids_hash'] = [ + 'type' => 'varchar', + 'length' => '64', + 'not null' => TRUE, + ]; + $fields['level'] = [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 1, + ]; + $fields['message'] = [ + 'type' => 'text', + 'size' => 'medium', + 'not null' => TRUE, + ]; + $schema = [ + 'description' => '', + 'fields' => $fields, + 'primary key' => ['msgid'], + ]; + $this->database->schema()->createTable($message_table_name, $schema); + + $rows = [ + [ + 'msgid' => '1', + 'source_ids_hash' => '28cfb3d1', + 'level' => (string) MigrationInterface::MESSAGE_ERROR, + 'message' => 'Config entities can not be stubbed.', + ], + [ + 'msgid' => '2', + 'source_ids_hash' => '28cfb3d1', + 'level' => (string) MigrationInterface::MESSAGE_ERROR, + 'message' => 'Config entities can not be stubbed.', + ], + [ + 'msgid' => '3', + 'source_ids_hash' => '05914d93', + 'level' => (string) MigrationInterface::MESSAGE_ERROR, + 'message' => 'Config entities can not be stubbed.', + ], + [ + 'msgid' => '4', + 'source_ids_hash' => '05914d93', + 'level' => (string) MigrationInterface::MESSAGE_INFORMATIONAL, + 'message' => 'Config entities can not be stubbed.', + ], + ]; + foreach ($rows as $row) { + $this->database->insert($message_table_name)->fields($row)->execute(); + } + } + } + } + +} diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.services.yml b/core/modules/migrate_drupal_ui/migrate_drupal_ui.services.yml new file mode 100644 index 0000000000000000000000000000000000000000..08de00e80a81099bf64df932ea5afbb65bf4e4d3 --- /dev/null +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.services.yml @@ -0,0 +1,5 @@ +services: + migrate_drupal_ui.route_subscriber: + class: Drupal\migrate_drupal_ui\Routing\MigrateDrupalUiRouteSubscriber + tags: + - { name: event_subscriber } diff --git a/core/modules/migrate_drupal_ui/src/Controller/MigrateMessageController.php b/core/modules/migrate_drupal_ui/src/Controller/MigrateMessageController.php new file mode 100644 index 0000000000000000000000000000000000000000..396b4a05cfcc0d34fc01256da893e6fa123b28bf --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Controller/MigrateMessageController.php @@ -0,0 +1,36 @@ +<?php + +namespace Drupal\migrate_drupal_ui\Controller; + +use Drupal\Core\Link; +use Drupal\Core\Url; +use Drupal\migrate\Controller\MigrateMessageController as BaseMessageController; + +/** + * Provides controller methods for the Message form. + */ +class MigrateMessageController extends BaseMessageController { + + /** + * Displays an overview of migrate messages. + * + * @return array + * A render array as expected by + * \Drupal\Core\Render\RendererInterface::render(). + */ + public function overview(): array { + $build = parent::overview(); + + $description['help'] = [ + '#type' => 'item', + '#markup' => $this->t('The upgrade process may log messages about steps that require user action or errors. This page allows you to view these messages'), + ]; + $description['info'] = [ + '#type' => 'item', + '#markup' => Link::fromTextAndUrl($this->t('Review the detailed upgrade log.'), Url::fromRoute('migrate_drupal_ui.log')) + ->toString(), + ]; + return $description + $build; + } + +} diff --git a/core/modules/migrate_drupal_ui/src/Routing/MigrateDrupalUiRouteSubscriber.php b/core/modules/migrate_drupal_ui/src/Routing/MigrateDrupalUiRouteSubscriber.php new file mode 100644 index 0000000000000000000000000000000000000000..bb2f1208bd6b0780be83b005b974cd4f7b92168d --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Routing/MigrateDrupalUiRouteSubscriber.php @@ -0,0 +1,23 @@ +<?php + +namespace Drupal\migrate_drupal_ui\Routing; + +use Drupal\Core\Routing\RouteSubscriberBase; +use Symfony\Component\Routing\RouteCollection; + +/** + * Sets the controller for Migrate Message route. + */ +class MigrateDrupalUiRouteSubscriber extends RouteSubscriberBase { + + /** + * {@inheritdoc} + */ + protected function alterRoutes(RouteCollection $collection) { + $route = $collection->get('migrate.messages'); + if ($route) { + $route->setDefault('_controller', '\Drupal\migrate_drupal_ui\Controller\MigrateMessageController::overview'); + } + } + +} diff --git a/core/modules/migrate_drupal_ui/tests/modules/migrate_drupal_message_test/migrate_drupal_message_test.info.yml b/core/modules/migrate_drupal_ui/tests/modules/migrate_drupal_message_test/migrate_drupal_message_test.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..52c9270241511d5e69ebc993d96cd3399f13d2c8 --- /dev/null +++ b/core/modules/migrate_drupal_ui/tests/modules/migrate_drupal_message_test/migrate_drupal_message_test.info.yml @@ -0,0 +1,5 @@ +name: Test display of migrate message +type: module +description: Tests the display of migrate messages. +package: Testing +version: VERSION diff --git a/core/modules/migrate_drupal_ui/tests/modules/migrate_drupal_message_test/migrations/d7_menu_test.yml b/core/modules/migrate_drupal_ui/tests/modules/migrate_drupal_message_test/migrations/d7_menu_test.yml new file mode 100644 index 0000000000000000000000000000000000000000..00056e819ca82fc69ab6b8f26adc91469e9271f4 --- /dev/null +++ b/core/modules/migrate_drupal_ui/tests/modules/migrate_drupal_message_test/migrations/d7_menu_test.yml @@ -0,0 +1,11 @@ +id: d7_menu_test +label: Test display of upgrade messages +migration_tags: + - Drupal 7 +source: + plugin: d7_menu_test +process: + message: site_offline_message +destination: + plugin: config + config_name: test.settings diff --git a/core/modules/migrate_drupal_ui/tests/modules/migrate_drupal_message_test/src/Plugin/migrate/source/MenuTest.php b/core/modules/migrate_drupal_ui/tests/modules/migrate_drupal_message_test/src/Plugin/migrate/source/MenuTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ffd347c34c4964a28ab2b8e27eec29c746d511fb --- /dev/null +++ b/core/modules/migrate_drupal_ui/tests/modules/migrate_drupal_message_test/src/Plugin/migrate/source/MenuTest.php @@ -0,0 +1,26 @@ +<?php + +namespace Drupal\migrate_drupal_message_test\Plugin\migrate\source; + +use Drupal\system\Plugin\migrate\source\Menu; + +/** + * Source plugin with a source id removed from the array returned by fields(). + * + * @MigrateSource( + * id = "d7_menu_test", + * source_module = "menu" + * ) + */ +class MenuTest extends Menu { + + /** + * {@inheritdoc} + */ + public function fields() { + $fields = parent::fields(); + unset($fields['menu_name']); + return $fields; + } + +} diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateMessageControllerTest.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateMessageControllerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..eac14ff857470d8e0e43db0c8f443494c09b29c0 --- /dev/null +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateMessageControllerTest.php @@ -0,0 +1,562 @@ +<?php + +namespace Drupal\Tests\migrate_drupal_ui\Functional; + +use Drupal\Core\Database\Database; +use Drupal\migrate\Plugin\MigrateIdMapInterface; + +/** + * Tests for the MigrateController class. + * + * @group migrate_drupal_ui + */ +class MigrateMessageControllerTest extends MigrateUpgradeTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'menu_link_content', + 'message_test', + 'migrate_drupal_message_test', + 'migrate_drupal_ui', + 'system', + ]; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * The database connection. + * + * @var \Drupal\Core\Database\Connection + */ + protected $database; + + /** + * Migration IDs. + * + * @var string[] + */ + protected $migrationIds = []; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + // Migrations that access the source database in fields(). + $this->migrationIds = [ + 'd6_menu', + 'd6_menu_links', + 'd6_profile_values', + 'd6_user', + 'd7_menu', + 'd7_menu_links', + 'd7_menu_test', + 'd7_user', + ]; + + $user = $this->createUser(['view migration messages']); + $this->drupalLogin($user); + $this->database = \Drupal::database(); + } + + /** + * Tests the overview page for migrate messages. + * + * Tests the overview page with the following scenarios; + * - No source database connection or message tables. + * - No source database connection with message tables. + * - A source database connection with message tables. + */ + public function testOverview(): void { + $session = $this->assertSession(); + + // First, test with no source database or message tables. + $this->drupalGet('/admin/reports/migration-messages'); + $session->titleEquals('Migration messages | Drupal'); + $session->pageTextContainsOnce('The upgrade process may log messages about steps that require user action or errors. This page allows you to view these messages'); + $session->pageTextContainsOnce('There are no migration message tables.'); + + // Create map and message tables. + $this->createMigrateTables($this->migrationIds); + + // Test overview with no source database connection and with message tables. + $this->drupalGet('/admin/reports/migration-messages'); + $session->statusCodeEquals(200); + $session->pageTextContains('Failed to connect to your database server'); + $session->pageTextContains('database connection configured for source plugin variable.'); + foreach ($this->migrationIds as $migration_id) { + $session->pageTextContains($migration_id); + } + + // Create a source database connection. + $this->createMigrationConnection(); + $this->sourceDatabase = Database::getConnection('default', 'migrate_drupal_ui'); + $this->createSourceTables(); + + // Now, test with a source database connection and with message tables. + $this->drupalGet('/admin/reports/migration-messages'); + $session->statusCodeEquals(200); + $session->pageTextNotContains('Failed to connect to your database server'); + foreach ($this->migrationIds as $migration_id) { + $session->pageTextContains($migration_id); + } + } + + /** + * Tests the detail pages for migrate messages. + * + * Tests the detail page with the following scenarios; + * - No source database connection or message tables with a valid and an + * invalid migration. + * - A source database connection with message tables with a valid and an + * invalid migration. + * - A source database connection with message tables and a source plugin + * that does not have a description for a source ID in the values returned + * from fields(). + */ + public function testDetail(): void { + $session = $this->assertSession(); + + // Details page with invalid migration. + $this->drupalGet('/admin/reports/migration-messages/invalid'); + $session->statusCodeEquals(404); + $session->pageTextContains('Failed to connect to your database server'); + + // Details page with valid migration. + $this->drupalGet('/admin/reports/migration-messages/d7_menu'); + $session->statusCodeEquals(404); + $session->pageTextNotContains('Failed to connect to your database server'); + + // Create map and message tables. + $this->createMigrateTables($this->migrationIds); + + $not_available_text = "When there is an error processing a row, the migration system saves the error message but not the source ID(s) of the row. That is why some messages in this table have 'Not available' in the source ID column(s)."; + + // Test overview without a source database connection and with message + // tables. + $this->drupalGet('/admin/reports/migration-messages'); + $session->statusCodeEquals(200); + foreach ($this->migrationIds as $migration_id) { + $session->pageTextContains($migration_id); + } + + // Test details page for each migration. + foreach ($this->migrationIds as $migration_id) { + $this->drupalGet("/admin/reports/migration-messages/$migration_id"); + $session->statusCodeEquals(200); + $session->pageTextNotContains('No database connection configured for source plugin'); + $session->pageTextContains($migration_id); + if ($migration_id == 'd7_menu') { + // Confirm the descriptions from fields() are displayed. + $session->pageTextContains('MENU NAME. PRIMARY KEY'); + $session->pageTextContains('Not available'); + $session->pageTextContains($not_available_text); + } + } + + // Create a source database connection. + $this->createMigrationConnection(); + $this->sourceDatabase = Database::getConnection('default', 'migrate_drupal_ui'); + $this->createSourceTables(); + + // Now, test with a source database connect and with message tables. + // Details page exists for each migration. + foreach ($this->migrationIds as $migration_id) { + $this->drupalGet("/admin/reports/migration-messages/$migration_id"); + $session->statusCodeEquals(200); + $session->pageTextNotContains('No database connection configured for source plugin'); + $session->pageTextContains($migration_id); + // Confirm the descriptions from fields() are displayed using d7_menu. + if ($migration_id == 'd7_menu') { + $session->pageTextContains('MENU NAME. PRIMARY KEY'); + $session->pageTextContains('Not available'); + $session->pageTextContains($not_available_text); + } + // Confirm the descriptions from fields() are displayed using + // d7_menu_test, which has a source plugin that is missing the + // 'menu_name' entry in fields(). + if ($migration_id == 'd7_menu_test') { + $session->pageTextContains('MENU_NAME'); + $session->pageTextContains('Not available'); + $session->pageTextContains($not_available_text); + } + } + + // Details page for a migration without a map table. + $this->database->schema()->dropTable('migrate_map_d7_menu'); + $this->drupalGet('/admin/reports/migration-messages/d7_menu'); + $session->statusCodeEquals(404); + + // Details page for a migration with a map table but no message table. + $this->database->schema()->dropTable('migrate_message_d7_menu_links'); + $this->drupalGet('/admin/reports/migration-messages/d7_menu_links'); + $session->statusCodeEquals(200); + $session->pageTextContains('The message table is missing for this migration.'); + } + + /** + * Creates map and message tables for testing. + * + * @see \Drupal\migrate\Plugin\migrate\id_map\Sql::ensureTables + */ + protected function createMigrateTables(array $migration_ids): void { + foreach ($migration_ids as $migration_id) { + $map_table_name = "migrate_map_$migration_id"; + $message_table_name = "migrate_message_$migration_id"; + + if (!$this->database->schema()->tableExists($map_table_name)) { + $fields = []; + $fields['source_ids_hash'] = [ + 'type' => 'varchar', + 'length' => '64', + 'not null' => TRUE, + ]; + $fields['sourceid1'] = [ + 'type' => 'varchar', + 'length' => '255', + 'not null' => TRUE, + ]; + $fields['destid1'] = [ + 'type' => 'varchar', + 'length' => '255', + 'not null' => FALSE, + ]; + $fields['source_row_status'] = [ + 'type' => 'int', + 'size' => 'tiny', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => MigrateIdMapInterface::STATUS_IMPORTED, + ]; + $fields['rollback_action'] = [ + 'type' => 'int', + 'size' => 'tiny', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => MigrateIdMapInterface::ROLLBACK_DELETE, + ]; + $fields['last_imported'] = [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ]; + $fields['hash'] = [ + 'type' => 'varchar', + 'length' => '64', + 'not null' => FALSE, + ]; + $schema = [ + 'description' => '', + 'fields' => $fields, + 'primary key' => ['source_ids_hash'], + ]; + $this->database->schema()->createTable($map_table_name, $schema); + + $rows = [ + [ + 'source_ids_hash' => '37c655d', + 'sourceid1' => 'navigation', + 'destid1' => 'tools', + 'source_row_status' => '0', + 'rollback_action' => '1', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => '3a34190', + 'sourceid1' => 'menu-fixedlang', + 'destid1' => 'menu-fixedlang', + 'source_row_status' => '0', + 'rollback_action' => '0', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => '3e51f67', + 'sourceid1' => 'management', + 'destid1' => 'admin', + 'source_row_status' => '0', + 'rollback_action' => '1', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => '94a5caa', + 'sourceid1' => 'user-menu', + 'destid1' => 'account', + 'source_row_status' => '0', + 'rollback_action' => '1', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => 'c0efbcca', + 'sourceid1' => 'main-menu', + 'destid1' => 'main', + 'source_row_status' => '0', + 'rollback_action' => '1', + 'last_imported' => '0', + 'hash' => '', + ], + [ + 'source_ids_hash' => 'f64cb72f', + 'sourceid1' => 'menu-test-menu', + 'destid1' => 'menu-test-menu', + 'source_row_status' => '0', + 'rollback_action' => '0', + 'last_imported' => '0', + 'hash' => '', + ], + ]; + foreach ($rows as $row) { + $this->database->insert($map_table_name)->fields($row)->execute(); + } + } + + if (!$this->database->schema()->tableExists($message_table_name)) { + $fields = []; + $fields['msgid'] = [ + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ]; + $fields['source_ids_hash'] = [ + 'type' => 'varchar', + 'length' => '64', + 'not null' => TRUE, + ]; + $fields['level'] = [ + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 1, + ]; + $fields['message'] = [ + 'type' => 'text', + 'size' => 'medium', + 'not null' => TRUE, + ]; + $schema = [ + 'description' => '', + 'fields' => $fields, + 'primary key' => ['msgid'], + ]; + $this->database->schema()->createTable($message_table_name, $schema); + + $rows = [ + [ + 'msgid' => '1', + 'source_ids_hash' => '28cfb3d1', + 'level' => '1', + 'message' => 'Config entities can not be stubbed.', + ], + [ + 'msgid' => '2', + 'source_ids_hash' => '28cfb3d1', + 'level' => '1', + 'message' => 'Config entities can not be stubbed.', + ], + [ + 'msgid' => '3', + 'source_ids_hash' => '05914d93', + 'level' => '1', + 'message' => 'Config entities can not be stubbed.', + ], + [ + 'msgid' => '4', + 'source_ids_hash' => '05914d93', + 'level' => '1', + 'message' => 'Config entities can not be stubbed.', + ], + ]; + foreach ($rows as $row) { + $this->database->insert($message_table_name)->fields($row)->execute(); + } + } + } + + } + + /** + * Create source tables. + */ + protected function createSourceTables(): void { + $this->sourceDatabase->schema()->createTable('menu_custom', [ + 'fields' => [ + 'menu_name' => [ + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '32', + 'default' => '', + ], + 'title' => [ + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '255', + 'default' => '', + ], + 'description' => [ + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'normal', + ], + ], + 'primary key' => [ + 'menu_name', + ], + 'mysql_character_set' => 'utf8', + ]); + + $this->sourceDatabase->schema()->createTable('profile_values', [ + 'fields' => [ + 'fid' => [ + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + 'unsigned' => TRUE, + ], + 'uid' => [ + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + 'unsigned' => TRUE, + ], + 'value' => [ + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'normal', + ], + ], + 'primary key' => [ + 'fid', + 'uid', + ], + 'mysql_character_set' => 'utf8', + ]); + + $this->sourceDatabase->schema()->createTable('profile_fields', [ + 'fields' => [ + 'fid' => [ + 'type' => 'serial', + 'not null' => TRUE, + 'size' => 'normal', + ], + 'title' => [ + 'type' => 'varchar', + 'not null' => FALSE, + 'length' => '255', + ], + 'name' => [ + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ], + 'explanation' => [ + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'normal', + ], + 'category' => [ + 'type' => 'varchar', + 'not null' => FALSE, + 'length' => '255', + ], + 'page' => [ + 'type' => 'varchar', + 'not null' => FALSE, + 'length' => '255', + ], + 'type' => [ + 'type' => 'varchar', + 'not null' => FALSE, + 'length' => '128', + ], + 'weight' => [ + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ], + 'required' => [ + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ], + 'register' => [ + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ], + 'visibility' => [ + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ], + 'autocomplete' => [ + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ], + 'options' => [ + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'normal', + ], + ], + 'primary key' => [ + 'fid', + ], + 'mysql_character_set' => 'utf8', + ]); + } + + /** + * {@inheritdoc} + */ + protected function getSourceBasePath(): string { + return ''; + } + + /** + * {@inheritdoc} + */ + protected function getEntityCounts(): array { + return []; + } + + /** + * {@inheritdoc} + */ + protected function getAvailablePaths(): array { + return []; + } + + /** + * {@inheritdoc} + */ + protected function getMissingPaths(): array { + return []; + } + + /** + * {@inheritdoc} + */ + protected function getEntityCountsIncremental(): array { + return []; + } + +} diff --git a/core/modules/system/src/Plugin/migrate/source/Menu.php b/core/modules/system/src/Plugin/migrate/source/Menu.php index 2e57e6eaa32b5d1a4209a7d645894c1cab7b8ccf..ad76fa6cbd3ead3e96dd45598502827d8ef2a85b 100644 --- a/core/modules/system/src/Plugin/migrate/source/Menu.php +++ b/core/modules/system/src/Plugin/migrate/source/Menu.php @@ -36,12 +36,19 @@ public function fields() { 'description' => $this->t('A description of the menu'), ]; - if ($this->database->schema()->fieldExists('menu_custom', 'language')) { - $fields += [ - 'language' => $this->t('Menu language.'), - 'i8n_mode' => $this->t('Menu i18n mode.'), - ]; + // The database connection may not exist, for example, when building + // the Migrate Message form. + if ($source_database = $this->database) { + if ($source_database + ->schema() + ->fieldExists('menu_custom', 'language')) { + $fields += [ + 'language' => $this->t('Menu language.'), + 'i8n_mode' => $this->t('Menu i18n mode.'), + ]; + } } + return $fields; } 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 da30f3fc988ccd63451116b45706f50252c5cb0d..24e32e02f584a3a409f904e2b2868f33a8ef078f 100644 --- a/core/modules/user/src/Plugin/migrate/source/d6/User.php +++ b/core/modules/user/src/Plugin/migrate/source/d6/User.php @@ -115,16 +115,20 @@ protected function baseFields() { 'data' => $this->t('User data'), ]; - // Possible field added by Date contributed module. - // @see https://api.drupal.org/api/drupal/modules%21user%21user.install/function/user_update_7002/7 - if ($this->getDatabase()->schema()->fieldExists('users', 'timezone_name')) { - $fields['timezone_name'] = $this->t('Timezone (Date)'); - } + // The database connection may not exist, for example, when building + // the Migrate Message form. + if ($source_database = $this->database) { + // Possible field added by Date contributed module. + // @see https://api.drupal.org/api/drupal/modules%21user%21user.install/function/user_update_7002/7 + if ($source_database->schema()->fieldExists('users', 'timezone_name')) { + $fields['timezone_name'] = $this->t('Timezone (Date)'); + } - // Possible field added by Event contributed module. - // @see https://api.drupal.org/api/drupal/modules%21user%21user.install/function/user_update_7002/7 - if ($this->getDatabase()->schema()->fieldExists('users', 'timezone_id')) { - $fields['timezone_id'] = $this->t('Timezone (Event)'); + // Possible field added by Event contributed module. + // @see https://api.drupal.org/api/drupal/modules%21user%21user.install/function/user_update_7002/7 + if ($source_database->schema()->fieldExists('users', 'timezone_id')) { + $fields['timezone_id'] = $this->t('Timezone (Event)'); + } } return $fields;