Commit c9cb4780 authored by larowlan's avatar larowlan

Issue #2914974 by quietone, rakesh.gectcr, maxocub, heddn, Jo Fitzgerald,...

Issue #2914974 by quietone, rakesh.gectcr, maxocub, heddn, Jo Fitzgerald, Yogesh Pawar, masipila, phenaproxima, jhodgdon, Gábor Hojtsy, larowlan, neclimdul: Migrate UI - handle sources that do not need an upgrade

(cherry picked from commit 5fcde4a7)
parent c060f5de
......@@ -62,11 +62,11 @@ protected function enableAllModules() {
public function testFieldProvidersExist() {
$expected_mappings = [
'userreference' => [
'source_module' => 'user_reference',
'source_module' => 'userreference',
'destination_module' => 'core',
],
'nodereference' => [
'source_module' => 'node_reference',
'source_module' => 'nodereference',
'destination_module' => 'core',
],
'optionwidgets' => [
......
......@@ -11,7 +11,7 @@
* type_map = {
* "nodereference" = "entity_reference",
* },
* source_module = "node_reference",
* source_module = "nodereference",
* destination_module = "core",
* )
*/
......
......@@ -11,7 +11,7 @@
* type_map = {
* "userreference" = "entity_reference",
* },
* source_module = "user_reference",
* source_module = "userreference",
* destination_module = "core",
* )
*/
......
......@@ -68,6 +68,102 @@ class MigrateUpgradeForm extends ConfirmFormBase {
*/
protected $moduleHandler;
/**
* List of extensions that do not need an upgrade path.
*
* This property is an array where the keys are the major Drupal core version
* from which we are upgrading, and the values are arrays of extension names
* that do not need an upgrade path.
*
* @var array[]
*/
protected $noUpgradePaths = [
'6' => [
'blog',
'blogapi',
'calendarsignup',
'color',
'content_copy',
'content_multigroup',
'content_permissions',
'date_api',
'date_locale',
'date_php4',
'date_popup',
'date_repeat',
'date_timezone',
'date_tools',
'datepicker',
'ddblock',
'event',
'fieldgroup',
'filefield_meta',
'help',
'i18n',
'i18nstrings',
'imageapi',
'imageapi_gd',
'imageapi_imagemagick',
'imagecache_ui',
'jquery_ui',
'nodeaccess',
'number',
'openid',
'php',
'ping',
'poll',
'throttle',
'tracker',
'translation',
'trigger',
'variable',
'variable_admin',
'views_export',
'views_ui',
],
'7' => [
'blog',
'bulk_export',
'contextual',
'ctools',
'ctools_access_ruleset',
'ctools_ajax_sample',
'ctools_custom_content',
'dashboard',
'date_all_day',
'date_api',
'date_context',
'date_migrate',
'date_popup',
'date_repeat',
'date_repeat_field',
'date_tools',
'date_views',
'entity',
'entity_feature',
'entity_token',
'entityreference',
'field_ui',
'help',
'openid',
'overlay',
'page_manager',
'php',
'poll',
'search_embedded_form',
'search_extra_type',
'search_node_tags',
'simpletest',
'stylizer',
'term_depth',
'toolbar',
'translation',
'trigger',
'views_content',
'views_ui',
],
];
/**
* Constructs the MigrateUpgradeForm.
*
......@@ -269,7 +365,7 @@ public function buildCredentialForm(array $form, FormStateInterface $form_state)
'#type' => 'radios',
'#default_value' => 7,
'#title' => $this->t('Drupal version of the source site'),
'#options' => [6 => $this->t('Drupal 6'), 7 => $this->t('Drupal 7')],
'#options' => ['6' => $this->t('Drupal 6'), '7' => $this->t('Drupal 7')],
'#required' => TRUE,
];
......@@ -332,7 +428,7 @@ public function buildCredentialForm(array $form, FormStateInterface $form_state)
'#description' => $this->t('To import files from your current Drupal site, enter a local file directory containing your site (e.g. /var/www/docroot), or your site address (for example http://example.com).'),
'#states' => [
'visible' => [
':input[name="version"]' => ['value' => 6],
':input[name="version"]' => ['value' => '6'],
],
],
];
......@@ -343,7 +439,7 @@ public function buildCredentialForm(array $form, FormStateInterface $form_state)
'#description' => $this->t('To import public files from your current Drupal site, enter a local file directory containing your site (e.g. /var/www/docroot), or your site address (for example http://example.com).'),
'#states' => [
'visible' => [
':input[name="version"]' => ['value' => 7],
':input[name="version"]' => ['value' => '7'],
],
],
];
......@@ -355,7 +451,7 @@ public function buildCredentialForm(array $form, FormStateInterface $form_state)
'#description' => $this->t('To import private files from your current Drupal site, enter a local file directory containing your site (e.g. /var/www/docroot).'),
'#states' => [
'visible' => [
':input[name="version"]' => ['value' => 7],
':input[name="version"]' => ['value' => '7'],
],
],
];
......@@ -404,11 +500,11 @@ public function validateCredentialForm(array &$form, FormStateInterface $form_st
try {
$connection = $this->getConnection($database);
$version = $this->getLegacyDrupalVersion($connection);
$version = (string) $this->getLegacyDrupalVersion($connection);
if (!$version) {
$form_state->setErrorByName($database['driver'] . '][0', $this->t('Source database does not contain a recognizable Drupal version.'));
}
elseif ($version != $form_state->getValue('version')) {
elseif ($version !== (string) $form_state->getValue('version')) {
$form_state->setErrorByName($database['driver'] . '][0', $this->t('Source database is Drupal version @version but version @selected was selected.', [
'@version' => $version,
'@selected' => $form_state->getValue('version'),
......@@ -431,7 +527,7 @@ public function validateCredentialForm(array &$form, FormStateInterface $form_st
// Store the retrieved migration IDs in form storage.
$form_state->set('version', $version);
$form_state->set('migrations', $migration_array);
if ($version == 6) {
if ($version === '6') {
$form_state->set('source_base_path', $form_state->getValue('d6_source_base_path'));
}
else {
......@@ -661,10 +757,25 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
// Get the source_module and destination_module from the field plugins.
$definitions = $this->fieldPluginManager->getDefinitions();
foreach ($definitions as $definition) {
// This is not strict so that we find field plugins with an annotation
// where the Drupal core version is an integer and when it is a string.
if (in_array($version, $definition['core'])) {
$source_module = $definition['source_module'];
$destination_module = $definition['destination_module'];
$table_data[$source_module][$destination_module][$definition['id']] = $definition['id'];
}
}
// Fetch the system data at the first opportunity.
$system_data = $form_state->get('system_data');
// Add source_module and destination_module for modules that do not need an
// upgrade path and are enabled on the source site.
foreach ($this->noUpgradePaths[$version] as $extension) {
if ($system_data['module'][$extension]['status']) {
$table_data[$extension]['core'][$extension] = $extension;
}
}
// Sort the table by source module names and within that destination
// module names.
......@@ -673,8 +784,6 @@ public function buildConfirmForm(array $form, FormStateInterface $form_state) {
ksort($table_data[$source_module]);
}
// Fetch the system data at the first opportunity.
$system_data = $form_state->get('system_data');
// Remove core profiles from the system data.
foreach (['standard', 'minimal'] as $profile) {
unset($system_data['module'][$profile]);
......
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate_drupal\MigrationConfigurationTrait;
use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait;
/**
* Provides a base class for testing a complete upgrade via the UI.
*/
abstract class MigrateUpgradeExecuteTestBase extends MigrateUpgradeTestBase {
use MigrationConfigurationTrait;
use CreateTestContentEntitiesTrait;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create content.
$this->createContent();
}
/**
* Executes all steps of migrations upgrade.
*/
public function testMigrateUpgradeExecute() {
$connection_options = $this->sourceDatabase->getConnectionOptions();
$this->drupalGet('/upgrade');
$session = $this->assertSession();
$session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
$session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
$session->fieldExists('mysql[host]');
$driver = $connection_options['driver'];
$connection_options['prefix'] = $connection_options['prefix']['default'];
// Use the driver connection form to get the correct options out of the
// database settings. This supports all of the databases we test against.
$drivers = drupal_get_database_types();
$form = $drivers[$driver]->getFormOptions($connection_options);
$connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']);
$version = $this->getLegacyDrupalVersion($this->sourceDatabase);
$edit = [
$driver => $connection_options,
'source_private_file_path' => $this->getSourceBasePath(),
'version' => $version,
];
if ($version == 6) {
$edit['d6_source_base_path'] = $this->getSourceBasePath();
}
else {
$edit['source_base_path'] = $this->getSourceBasePath();
}
if (count($drivers) !== 1) {
$edit['driver'] = $driver;
}
$edits = $this->translatePostValues($edit);
// Ensure submitting the form with invalid database credentials gives us a
// nice warning.
$this->drupalPostForm(NULL, [$driver . '[database]' => 'wrong'] + $edits, t('Review upgrade'));
$session->pageTextContains('Resolve the issue below to continue the upgrade.');
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
// Ensure we get errors about missing modules.
$session->pageTextContains(t('Resolve the issue below to continue the upgrade'));
$session->pageTextContains(t('The no_source_module plugin must define the source_module property.'));
// Uninstall the module causing the missing module error messages.
$this->container->get('module_installer')->uninstall(['migration_provider_test'], TRUE);
// Restart the upgrade process.
$this->drupalGet('/upgrade');
$session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
$session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
$session->fieldExists('mysql[host]');
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$session->pageTextContains('WARNING: Content may be overwritten on your new site.');
$session->pageTextContains('There is conflicting content of these types:');
$session->pageTextContains('aggregator feed entities');
$session->pageTextContains('aggregator feed item entities');
$session->pageTextContains('custom block entities');
$session->pageTextContains('custom menu link entities');
$session->pageTextContains('file entities');
$session->pageTextContains('taxonomy term entities');
$session->pageTextContains('user entities');
$session->pageTextContains('comments');
$session->pageTextContains('content item revisions');
$session->pageTextContains('content items');
$session->pageTextContains('There is translated content of these types:');
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
$session->statusCodeEquals(200);
$session->pageTextContains('Upgrade analysis report');
// Ensure there are no errors about missing modules from the test module.
$session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
$session->pageTextNotContains(t('Source module not found for migration_provider_test.'));
// Ensure there are no errors about any other missing migration providers.
$session->pageTextNotContains(t('module not found'));
// Test the upgrade paths.
$available_paths = $this->getAvailablePaths();
$missing_paths = $this->getMissingPaths();
$this->assertUpgradePaths($session, $available_paths, $missing_paths);
$this->drupalPostForm(NULL, [], t('Perform upgrade'));
$this->assertText(t('Congratulations, you upgraded Drupal!'));
// Have to reset all the statics after migration to ensure entities are
// loadable.
$this->resetAll();
$expected_counts = $this->getEntityCounts();
foreach (array_keys(\Drupal::entityTypeManager()
->getDefinitions()) as $entity_type) {
$real_count = \Drupal::entityQuery($entity_type)->count()->execute();
$expected_count = isset($expected_counts[$entity_type]) ? $expected_counts[$entity_type] : 0;
$this->assertEqual($expected_count, $real_count, "Found $real_count $entity_type entities, expected $expected_count.");
}
$plugin_manager = \Drupal::service('plugin.manager.migration');
/** @var \Drupal\migrate\Plugin\Migration[] $all_migrations */
$all_migrations = $plugin_manager->createInstancesByTag('Drupal ' . $version);
foreach ($all_migrations as $migration) {
$id_map = $migration->getIdMap();
foreach ($id_map as $source_id => $map) {
// Convert $source_id into a keyless array so that
// \Drupal\migrate\Plugin\migrate\id_map\Sql::getSourceHash() works as
// expected.
$source_id_values = array_values(unserialize($source_id));
$row = $id_map->getRowBySource($source_id_values);
$destination = serialize($id_map->currentDestination());
$message = "Migration of $source_id to $destination as part of the {$migration->id()} migration. The source row status is " . $row['source_row_status'];
// A completed migration should have maps with
// MigrateIdMapInterface::STATUS_IGNORED or
// MigrateIdMapInterface::STATUS_IMPORTED.
if ($row['source_row_status'] == MigrateIdMapInterface::STATUS_FAILED || $row['source_row_status'] == MigrateIdMapInterface::STATUS_NEEDS_UPDATE) {
$this->fail($message);
}
else {
$this->pass($message);
}
}
}
\Drupal::service('module_installer')->install(['forum']);
\Drupal::service('module_installer')->install(['book']);
}
}
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional;
use Drupal\migrate_drupal\MigrationConfigurationTrait;
use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait;
/**
* Provides a base class for testing the review step of the Upgrade form.
*/
abstract class MigrateUpgradeReviewPageTestBase extends MigrateUpgradeTestBase {
use MigrationConfigurationTrait;
use CreateTestContentEntitiesTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'language',
'content_translation',
'migrate_drupal_ui',
'telephone',
'aggregator',
'book',
'forum',
'statistics',
'syslog',
'tracker',
'update',
];
/**
* Tests the migrate upgrade review form.
*
* The upgrade review form displays a list of modules that will be upgraded
* and a list of modules that will not be upgraded. This test is to ensure
* that the review page works correctly for all contributed Drupal 6 and
* Drupal 7 modules that have moved to core, e.g. Views, and for modules that
* were in Drupal 6 or Drupal 7 core but are not in Drupal 8 core, e.g.
* Overlay.
*
* To do this all modules in the source fixtures are enabled, except test and
* example modules. This means that we can test that the modules listed in the
* the $noUpgradePath property of the update form class are correct, since
* there will be no available migrations which declare those modules as their
* source_module. It is assumed that the test fixtures include all modules
* that have moved to or dropped from core.
*
* The upgrade review form will also display errors for each migration that
* does not have a source_module definition. That function is not tested here.
*
* @see \Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase
*/
public function testMigrateUpgradeReviewPage() {
$connection_options = $this->sourceDatabase->getConnectionOptions();
$driver = $connection_options['driver'];
$connection_options['prefix'] = $connection_options['prefix']['default'];
// Use the driver connection form to get the correct options out of the
// database settings. This supports all of the databases we test against.
$drivers = drupal_get_database_types();
$form = $drivers[$driver]->getFormOptions($connection_options);
$connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']);
$version = $this->getLegacyDrupalVersion($this->sourceDatabase);
$edit = [
$driver => $connection_options,
'source_private_file_path' => $this->getSourceBasePath(),
'version' => $version,
];
if ($version == 6) {
$edit['d6_source_base_path'] = $this->getSourceBasePath();
}
else {
$edit['source_base_path'] = $this->getSourceBasePath();
}
if (count($drivers) !== 1) {
$edit['driver'] = $driver;
}
$edits = $this->translatePostValues($edit);
// Enable all modules in the source except test and example modules, but
// include simpletest.
/** @var \Drupal\Core\Database\Query\SelectInterface $update */
$update = $this->sourceDatabase->update('system')
->fields(['status' => 1])
->condition('type', 'module');
$and = $update->andConditionGroup()
->condition('name', '%test%', 'NOT LIKE')
->condition('name', '%example%', 'NOT LIKE');
$conditions = $update->orConditionGroup();
$conditions->condition($and);
$conditions->condition('name', 'simpletest');
$update->condition($conditions);
$update->execute();
// Start the upgrade process.
$this->drupalGet('/upgrade');
$this->drupalPostForm(NULL, [], t('Continue'));
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
// Ensure there are no errors about missing modules from the test module.
$session = $this->assertSession();
$session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
$session->pageTextNotContains(t('Source module not found for migration_provider_test.'));
$session->pageTextNotContains(t('Destination module not found for migration_provider_test'));
// Ensure there are no errors about any other missing migration providers.
$session->pageTextNotContains(t('module not found'));
// Test the upgrade paths.
$available_paths = $this->getAvailablePaths();
$missing_paths = $this->getMissingPaths();
$this->assertUpgradePaths($session, $available_paths, $missing_paths);
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [];
}
}
......@@ -3,10 +3,10 @@
namespace Drupal\Tests\migrate_drupal_ui\Functional;
use Drupal\Core\Database\Database;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate_drupal\MigrationConfigurationTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait;
use Drupal\Tests\WebAssert;
/**
* Provides a base class for testing migration upgrades in the UI.
......@@ -40,9 +40,6 @@ protected function setUp() {
// Log in as user 1. Migrations in the UI can only be performed as user 1.
$this->drupalLogin($this->rootUser);
// Create content.
$this->createContent();
}
/**
......@@ -99,145 +96,6 @@ protected function tearDown() {
parent::tearDown();
}
/**
* Executes all steps of migrations upgrade.
*/
public function testMigrateUpgrade() {
$connection_options = $this->sourceDatabase->getConnectionOptions();
$this->drupalGet('/upgrade');
$session = $this->assertSession();
$session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
$session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
$session->fieldExists('mysql[host]');
$driver = $connection_options['driver'];
$connection_options['prefix'] = $connection_options['prefix']['default'];
// Use the driver connection form to get the correct options out of the
// database settings. This supports all of the databases we test against.
$drivers = drupal_get_database_types();
$form = $drivers[$driver]->getFormOptions($connection_options);
$connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']);
$version = $this->getLegacyDrupalVersion($this->sourceDatabase);
$edit = [
$driver => $connection_options,
'source_private_file_path' => $this->getSourceBasePath(),
'version' => $version,
];
if ($version == 6) {
$edit['d6_source_base_path'] = $this->getSourceBasePath();
}
else {
$edit['source_base_path'] = $this->getSourceBasePath();
}
if (count($drivers) !== 1) {
$edit['driver'] = $driver;
}
$edits = $this->translatePostValues($edit);
// Ensure submitting the form with invalid database credentials gives us a
// nice warning.
$this->drupalPostForm(NULL, [$driver . '[database]' => 'wrong'] + $edits, t('Review upgrade'));
$session->pageTextContains('Resolve the issue below to continue the upgrade.');
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
// Ensure we get errors about missing modules.
$session->pageTextContains(t('Resolve the issue below to continue the upgrade'));
$session->pageTextContains(t('The no_source_module plugin must define the source_module property.'));
// Uninstall the module causing the missing module error messages.
$this->container->get('module_installer')->uninstall(['migration_provider_test'], TRUE);
// Restart the upgrade process.
$this->drupalGet('/upgrade');
$session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
$session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
$session->fieldExists('mysql[host]');
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$session->pageTextContains('WARNING: Content may be overwritten on your new site.');
$session->pageTextContains('There is conflicting content of these types:');
$session->pageTextContains('aggregator feed entities');
$session->pageTextContains('aggregator feed item entities');
$session->pageTextContains('custom block entities');
$session->pageTextContains('custom menu link entities');
$session->pageTextContains('file entities');
$session->pageTextContains('taxonomy term entities');
$session->pageTextContains('user entities');
$session->pageTextContains('comments');
$session->pageTextContains('content item revisions');
$session->pageTextContains('content items');
$session->pageTextContains('There is translated content of these types:');
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
$session->statusCodeEquals(200);
$session->pageTextContains('Upgrade analysis report');
// Ensure there are no errors about the missing modules from the test module.
$session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
$session->pageTextNotContains(t('Source module not found for migration_provider_test.'));
// Ensure there are no errors about any other missing migration providers.
$session->pageTextNotContains(t('module not found'));
// Test the available migration paths.
$all_available = $this->getAvailablePaths();
foreach ($all_available as $available) {
$session->elementExists('xpath', "//span[contains(@class, 'checked') and text() = '$available']");
$session->elementNotExists('xpath', "//span[contains(@class, 'warning') and text() = '$available']");
}
// Test the missing migration paths.
$all_missing = $this->getMissingPaths();
foreach ($all_missing as $missing) {
$session->elementExists('xpath', "//span[contains(@class, 'warning') and text() = '$missing']");
$session->elementNotExists('xpath', "//span[contains(@class, 'checked') and text() = '$missing']");
}
$this->drupalPostForm(NULL, [], t('Perform upgrade'));
$this->assertText(t('Congratulations, you upgraded Drupal!'));
// Have to reset all the statics after migration to ensure entities are
// loadable.
$this->resetAll();
$expected_counts = $this->getEntityCounts();
foreach (array_keys(\Drupal::entityTypeManager()
->getDefinitions()) as $entity_type) {
$real_count = \Drupal::entityQuery($entity_type)->count()->execute();
$expected_count = isset($expected_counts[$entity_type]) ? $expected_counts[$entity_type] : 0;
$this->assertEqual($expected_count, $real_count, "Found $real_count $entity_type entities, expected $expected_count.");
}
$plugin_manager = \Drupal::service('plugin.manager.migration');
/** @var \Drupal\migrate\Plugin\Migration[] $all_migrations */
$all_migrations = $plugin_manager->createInstancesByTag('Drupal ' . $version);
foreach ($all_migrations as $migration) {
$id_map = $migration->getIdMap();
foreach ($id_map as $source_id => $map) {
// Convert $source_id into a keyless array so that
// \Drupal\migrate\Plugin\migrate\id_map\Sql::getSourceHash() works as
// expected.
$source_id_values = array_values(unserialize($source_id));
$row = $id_map->getRowBySource($source_id_values);
$destination = serialize($id_map->currentDestination());
$message = "Migration of $source_id to $destination as part of the {$migration->id()} migration. The source row status is " . $row['source_row_status'];
// A completed migration should have maps with
// MigrateIdMapInterface::STATUS_IGNORED or
// MigrateIdMapInterface::STATUS_IMPORTED.
if ($row['source_row_status'] == MigrateIdMapInterface::STATUS_FAILED || $row['source_row_status'] == MigrateIdMapInterface::STATUS_NEEDS_UPDATE) {
$this->fail($message);
}
else {
$this->pass($message);
}
}
}
\Drupal::service('module_installer')->install(['forum']);
\Drupal::service('module_installer')->install(['book']);
}
/**
* Transforms a nested array into a flat array suitable for BrowserTestBase::drupalPostForm().
*
......@@ -260,6 +118,34 @@ protected function translatePostValues(array $values) {
return $edit;