Menu Migration
The Menu Migration module facilitates the import and export of menu hierarchies from one Drupal copy to another one.
Out of the box, the module allows the following:
- Export menu hierarchies to a file in the codebase
- Import menu hierarchies from a file in the codebase
- Export menu hierarchies to a downloadable file
- Import menu hierarchies from an uploaded file
The module only supports MenuLinkContent menu items (the ones that are manually created), any other menu items will not be exported/imported (e.g. Views, Taonomy Menu etc.). It currently offers two formats: JSON & YAML.
The export destinations, import sources and formats can be extended through the Plugin system.
For a full description of the module, visit the project page.
For a detailed documentation, visit the module documentation.
Submit bug reports and feature suggestions, or track changes in the issue queue.
Chat, news, support & feedback in the dedicated Slack channel.
Table of contents
- Requirements
- Installation
- Configuration
- Access and Permissions
- Drush integration
- Extending Destinations
- Extending Formats
- Extending Sources
- Troubleshooting
- Maintainers
Requirements
File permissions actions might be required on the server for the Codebase Export Destination.
Installation
Install as you would normally install a contributed Drupal module. For further information, see Installing Drupal Modules.
Configuration
There are three configuration areas that are available:
- Menu Exports - allows defining menu exports that can later trigger exports
- Menu Imports - allows defining menu imports that can later trigger imports
- Quick Action Settings - allows configuring the quick exports and imports using Drush, without having to create Menu Exports and Menu Imports
Defining a menu export
In order to perform a menu export, a Menu Export needs to be created.
Note that this step is not required if you plan to only use the quick exports
functionality. See the drush menu_migration:quick-export
(alias drush mmqe
)
command.
- Navigate to Configuration → Development → Menu Migration → Menu Exports
- Click on Add menu export and fill out the form
- The Menu export name can be anything relevant to you
- The machine name will be the ID of the menu export and will be used with Drush
- The Export Destination can have one of the following values: Codebase or Download
- The Codebase exports the menus to the file system, and the Download generates a downloadable file
- Set the Format to JSON or YAML
- Select the Menus that you wish to export
- Fill out any other field depending on the Export Destination choice.
Note: On the Menu Exports listing page, you can drag and drop the menu exports to reorder them, which will update their weight accordingly.
Export menu
- Navigate to Configuration → Development → Menu Migration → Menu Exports
- Here you have a listing of the available menu exports
- In the Operations column, click on Export for one of the menu exports
- You will be presented with a confirmation form that details all information about the export process & location
- If you are satisfied with the conditions, click on export and that's it
Defining a menu import
In order to perform a menu import, a Menu Import needs to be created.
Note that this step is not required if you plan to only use the quick imports
functionality. See the drush menu_migration:quick-import
(alias drush mmqi
)
command.
- Navigate to Configuration → Development → Menu Migration → Menu Imports
- Click on Add Menu import and fill out the form
- The Menu import name can be anything relevant to you
- The machine name will be the ID of the Menu import and will be used with Drush
- The Import Source can have one of the following values: Codebase or File Upload.
- The Codebase Imports the menus from the file system, and the File Upload imports them from an uploaded file
- Set the Format to JSON or YAML
- Select the Menus that you wish to Import
- Fill out any other field depending on the Import Source choice.
Note: On the Menu Imports listing page, you can drag and drop the menu imports to reorder them, which will update their weight accordingly.
Import menu
- Navigate to Configuration → Development → Menu Migration → Menu Imports
- Here you have a listing of the available menu imports
- In the Operations column, click on Import for one of the menu exports
- You will be presented with a confirmation form that details all information about the import process & source
- If you are satisfied with the conditions, click on import and that's it
Quick Action Settings
Quick Action is a feature that allows Drush users to export and import menus directly by providing the menu IDs as arguments to the associated Drush commands. This allows for bypassing the creation of configuration entities which are intended for more stable and repetitive exports and imports.
The quick action settings influence the behaviour of the following Drush
commands: drush menu_migration:quick-export
(alias drush mmqe
) and
drush menu_migration:quick-import
(alias drush mmqi
).
- Navigate to Configuration → Development → Menu Migration → Quick Action Settings
- Here you can control the export/import format (JSON or YAML) and the target/source directory for exports and imports.
- By default, the format is
JSON
and the directory is set to'../config/menu_migration/quick-export'
Access and Permissions
The following permissions are available:
Machine name | Title | Usage |
---|---|---|
administer menu migration |
Administer menu migration | Bypasses any other menu_migration permission, and gives access to everything. |
administer menu migration export types |
Administer menu exports | Administer Menu Exports: add, edit, delete, export. Anything related to Menu Exports. |
administer menu migration import types |
Administer menu imports | Administer Menu Imports: add, edit, delete, export. Anything related to Menu Imports. |
perform export on menu migrations |
Perform export on any Menu Exports | Access to the Menu Exports listing page and permission to perform exports. |
perform import on menu migrations |
Perform import on any Menu Imports | Access to the Menu Exports listing page and permission to perform imports. |
Drush integration
Menu migration provides six Drush commands for managing imports and exports. You can import one or more menus at a time through your already defined menu exports and menu imports, or you can import/export menus directly without having to define Configuration Entities using the quick import/export commands.
Export Command
Performs the export for the given menu export ID. Requires confirmation.
- Command:
drush menu_migration:export
- Alias:
drush mme
Examples using the full command
# Export the Menu Export with ID my_export_id.
drush menu_migration:export my_export_id
# Export the Menu Export with ID my_export_id, and skip confirmation question.
drush menu_migration:export my_export_id -y
Examples using the alias
# Export the Menu Export with ID my_export_id.
drush mme my_export_id
# Export the Menu Export with ID my_export_id, and skip confirmation question.
drush mme my_export_id -y
Export List Command
Lists all the available menu exports that support Drush.
- Command:
drush menu_migration:export-list
- Alias:
drush mmel
Example using the full command
# List all the available export entities
drush menu_migration:export-list
Example using the alias
# List all the available export entities
drush mmel
Quick Export Command
Performs the export of one or more menus by menu IDs. Requires confirmation.
- Command:
drush menu_migration:quick-export
- Alias:
drush mmqe
- Options:
-
--format[=FORMAT]
(optional)
-
Examples using the full command
# Export the main menu.
drush menu_migration:quick-export main
# Export the main menu and override the format (e.g. yaml).
drush menu_migration:quick-export main --format=yaml
# Export the main, footer, custom_menu menus.
drush menu_migration:quick-export main,footer,custom_menu
# Export the main menu, and skip confirmation question.
drush menu_migration:quick-export main -y
# Export the main, footer, custom_menu menus and skip the confirmation question.
drush menu_migration:quick-export main,footer,custom_menu -y
Examples using the alias
# Export the main menu.
drush mmqe main
# Export the main menu and override the format (e.g. yaml).
drush mmqe main --format=yaml
# Export the main, footer, custom_menu menus.
drush mmqe main,footer,custom_menu
# Export the main menu, and skip confirmation question.
drush mmqe main -y
# Export the main, footer, custom_menu menus and skip the confirmation question.
drush mmqe main,footer,custom_menu -y
Import Command
Performs the import for the given menu export ID. Requires confirmation.
- Command:
drush menu_migration:import
- Alias:
drush mmi
Examples using the full command
# Import the Menu Import with ID my_import_id.
drush menu_migration:import my_import_id
# Import the Menu Import with ID my_import_id, and skip the confirmation question.
drush menu_migration:import my_import_id -y
Examples using the alias
# Import the Menu Import with ID my_import_id.
drush mmi my_import_id
# Import the Menu Import with ID my_import_id, and skip the confirmation question.
drush mmi my_import_id -y
Import List Command
Lists all the available menu imports that support Drush.
- Command:
drush menu_migration:import-list
- Alias:
drush mmil
Example using the full command
# List all the available import entities
drush menu_migration:import-list
Example using the alias
# List all the available import entities
drush mmil
Quick Import Command
Performs the import of one or more menus by menu IDs. Requires confirmation.
- Command:
drush menu_migration:quick-import
- Alias:
drush mmqi
- Options:
-
--format[=FORMAT]
(optional)
-
Examples using the full command
# Import the main menu.
drush menu_migration:quick-import main
# Import the main menu and override the format (e.g. yaml).
drush menu_migration:quick-import main --format=yaml
# Import the main, footer, custom_menu menus.
drush menu_migration:quick-import main,footer,custom_menu
# Import the main menu, and skip confirmation question.
drush menu_migration:quick-import main -y
# Import the main, footer, custom_menu menus and skip the confirmation question.
drush menu_migration:quick-import main,footer,custom_menu -y
Examples using the alias
# Import the main menu.
drush mmqi main
# Import the main menu and override the format (e.g. yaml).
drush mmqi main --format=yaml
# Import the main, footer, custom_menu menus.
drush mmqi main,footer,custom_menu
# Import the main menu, and skip confirmation question.
drush mmqi main -y
# Import the main, footer, custom_menu menus and skip the confirmation question.
drush mmqi main,footer,custom_menu -y
Extending Destinations
You can easily extend export destinations, and add your own custom destination by implementing a MenuMigrationDestination plugin and defining its schema.
Existing examples with more code in:
src/Plugin/menu_migration/ExportDestination/Codebase.php
src/Plugin/menu_migration/ExportDestination/Download.php
Example:
File: src/Plugin/menu_migration/ExportDestination/MyDestination.php
File Contents:
<?php
namespace Drupal\mymodule\Plugin\menu_migration\ExportDestination;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\menu_migration\Attribute\MenuMigrationDestination;
use Drupal\menu_migration\Plugin\menu_migration\ExportDestination\ExportDestinationBase;
/**
* Provides a MyDestination export destination.
*/
#[MenuMigrationDestination(
id: 'mydestination',
label: new TranslatableMarkup('My Destination'),
multiple: FALSE, # Optional, defaults to TRUE
cli: TRUE # Optional, defaults to FALSE
)]
class MyDestination extends ExportDestinationBase {
/**
* {@inheritdoc}
*/
public function exportMenu(string $menuName) {
// TODO: Implement the export logic and return a boolean.
// If the export fails, and you wish to display e detailed error message
// about what went wrong, you interrupt the process and do the following:
throw new MenuMigrationException(sprintf('My error message with the "%s" argument.', $argument));
}
/**
* {@inheritdoc}
*/
public function getExportDescription() {
$description = parent::getExportDescription();
// The parent contains the general export description. You can add details
// specific to your export plugin here.
$description[] = $this->t('The menus will have something special.');
return $description;
}
}
Additionally, if you'd like to display extra information in the listing pages
about the destination plugin, you can implement the configurationSummary()
method to do so. This method must return an array of one or more lines of text.
A concrete example can be found in the Codebase
destination plugin.
The example below displays the Codebase export path below the plugin label in the export listing page:
/**
* {@inheritdoc}
*/
public function configurationSummary() {
return [
$this->configuration['export_path'],
];
}
Define a schema for your new plugin by adding or modifying the following file:
mymodule/config/schema/mymodule.schema.yml
# This definition applies to ExportDestination plugins that don't allow multiple
# menus, and that don't have extra configurable fields other than menus and
# format.
menu_migration.destination_config.mydestination:
type: source_destination_config_single
label: 'My Destination'
where:
-
mydestination
is the ID of your ExportDestination plugin -
source_destination_config_single
is the data type for plugins that don't allow multiple menus. If your plugin allows multiple menus, change this tosource_destination_config_multiple
If our example plugin were to define an extra configurable field called
extra_config
as textfield, our schema would become:
menu_migration.destination_config.mydestination:
type: source_destination_config_single
label: 'My Destination'
mapping:
extra_config:
type: string
label: 'Extra Config'
Exising examples can be found in
menu_migration/config/schema/menu_migration.schema.yml
Extending Formats
You can easily extend export/import formats, and add your own custom format by implementing a MenuMigrationFormat plugin.
Existing examples with more code in:
src/Plugin/menu_migration/Format/JsonFormat.php
Example:
File: src/Plugin/menu_migration/Format/MyFormat.php
File Contents:
<?php
namespace Drupal\mymodule\Plugin\menu_migration\Format;
use Drupal\Component\Serialization\Json;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\menu_migration\Attribute\MenuMigrationFormat;
use Drupal\menu_migration\Plugin\menu_migration\Format\FormatBase;
/**
* Provides the myFormat import/export Format.
*/
#[MenuMigrationFormat(
id: 'myformat',
label: new TranslatableMarkup('My Format')
)]
class MyFormat extends FormatBase {
/**
* {@inheritdoc}
*/
public function encode(array $menuTree) {
// Here you encode your menu tree. Example with JSON.
return Json::encode($menuTree);
}
/**
* {@inheritdoc}
*/
public function decode(mixed $menuTree) {
// Here you decode your menu tree. Example with JSON.
return Json::decode($menuTree);
}
/**
* {@inheritdoc}
*/
public function allowedExtensions() {
// Here you add allowed file extensions for sources like file_upload. If
// applicable?
return ['json'];
}
/**
* {@inheritdoc}
*/
public function defaultExtension() {
// Here you add the default file extension for exports. If applicable?
return 'json';
}
/**
* {@inheritdoc}
*/
public function mimeType() {
// Here you add the default file mimetype for exports like download. If
// applicable?
return 'application/json';
}
}
Extending Sources
You can easily extend import sources, and add your own custom sources by implementing a MenuMigrationSource plugin and defining its schema.
Existing examples with more code in:
src/Plugin/menu_migration/ImportSource/Codebase.php
src/Plugin/menu_migration/ImportSource/FileUpload.php
Example:
File: src/Plugin/menu_migration/ImportSource/MySource.php
File Contents:
<?php
namespace Drupal\mymodule\Plugin\menu_migration\ImportSource;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\menu_migration\Attribute\MenuMigrationSource;
use Drupal\menu_migration\Plugin\menu_migration\ImportSource\ImportSourceBase;
/**
* Provides a MySource import source.
*/
#[MenuMigrationSource(
id: 'mysource',
label: new TranslatableMarkup('My Source'),
multiple: FALSE, # Optional, defaults to TRUE
cli: TRUE # Optional, defaults to FALSE
)]
class MySource extends ImportSourceBase {
/**
* {@inheritdoc}
*/
public function importMenu(string $menuName) {
// TODO: Implement the import logic and return a boolean.
// If the import fails, and you wish to display e detailed error message
// about what went wrong, you interrupt the process and do the following:
throw new MenuMigrationException(sprintf('My error message with the "%s" argument.', $argument));
}
/**
* {@inheritdoc}
*/
public function getImportDescription() {
$description = parent::getImportDescription();
// The parent contains the general import description. You can add details
// specific to your export plugin here.
$description[] = $this->t('The menus will have something special.');
return $description;
}
}
Additionally, if you'd like to display extra information in the listing pages
about the source plugin, you can implement the configurationSummary()
method
to do so. This method must return an array of one or more lines of text. A
concrete example can be found in the Codebase
source plugin.
The example below displays the Codebase import path below the plugin label in the import listing page:
/**
* {@inheritdoc}
*/
public function configurationSummary() {
return [
$this->configuration['import_path'],
];
}
Define a schema for your new plugin by adding or modifying the following file:
mymodule/config/schema/mymodule.schema.yml
# This definition applies to ImportSource plugins that don't allow multiple
# menus, and that don't have extra configurable fields other than menus and
# format.
menu_migration.source_config.mysource:
type: source_destination_config_single
label: 'My Source'
where:
-
mysource
is the ID of your ImportSource plugin -
source_destination_config_single
is the data type for plugins that don't allow multiple menus. If your plugin allows multiple menus, change this tosource_destination_config_multiple
If our example plugin were to define an extra configurable field called
extra_config
as textfield, our schema would become:
menu_migration.source_config.mysource:
type: source_destination_config_single
label: 'My Source'
mapping:
extra_config:
type: string
label: 'Extra Config'
Exising examples can be found in
menu_migration/config/schema/menu_migration.schema.yml
Troubleshooting
If you're getting errors that the menus can't be exported, please make sure that the target directory has writing access by the server user that is about to execute the exports when using the Codebase destination.
If the menu item hierarchy is messed up after import, make sure menu items are only added via the menu UI (for example "Main navigation") and not a View.
Maintainers
Cristina B. - bbu23