updated core to 8.6.3

This commit is contained in:
2018-11-21 12:49:46 +01:00
parent 8ca34853a3
commit c92c348eee
521 changed files with 12199 additions and 4578 deletions
@@ -40,7 +40,7 @@ class GotoAction extends ConfigurableActionBase implements ContainerFactoryPlugi
protected $unroutedUrlAssembler;
/**
* Constructs a new DeleteNode object.
* Constructs a GotoAction object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
@@ -94,7 +94,7 @@ class ItemsImporter implements ItemsImporterInterface {
watchdog_exception('aggregator', $e);
}
// Store instances in an array so we dont have to instantiate new objects.
// Store instances in an array so we don't have to instantiate new objects.
$processor_instances = [];
foreach ($this->config->get('processors') as $processor) {
try {
@@ -33,7 +33,7 @@ class BlockedIP extends DestinationBase implements ContainerFactoryPluginInterfa
* @param string $plugin_id
* The plugin ID.
* @param mixed $plugin_definition
* The plugin definiiton.
* The plugin definition.
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
* The current migration.
* @param \Drupal\ban\BanIpManagerInterface $ban_manager
@@ -2,8 +2,7 @@
namespace Drupal\block_content\Plugin\migrate\source\d6;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
use Drupal\block_content\Plugin\migrate\source\d7\BlockCustomTranslation as D7BlockCustomTranslation;
/**
* Gets Drupal 6 i18n custom block translations from database.
@@ -13,97 +12,12 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
* source_module = "i18nblocks"
* )
*/
class BoxTranslation extends DrupalSqlBase {
class BoxTranslation extends D7BlockCustomTranslation {
/**
* {@inheritdoc}
* Drupal 6 table names.
*/
public function query() {
// Build a query based on i18n_strings table where each row has the
// translation for only one property, either title or description. The
// method prepareRow() is then used to obtain the translation for the
// other property.
$query = $this->select('boxes', 'b')
->fields('b', ['bid', 'format', 'body'])
->fields('i18n', ['property'])
->fields('lt', ['lid', 'translation', 'language'])
->orderBy('b.bid')
->isNotNull('lt.lid');
// Use 'title' for the info field to match the property name in the
// i18n_strings table.
$query->addField('b', 'info', 'title');
// Add in the property, which is either title or body. Cast the bid to text
// so PostgreSQL can make the join.
$query->leftJoin('i18n_strings', 'i18n', 'i18n.objectid = CAST(b.bid as CHAR(255))');
$query->condition('i18n.type', 'block');
// Add in the translation for the property.
$query->leftJoin('locales_target', 'lt', 'lt.lid = i18n.lid');
return $query;
}
/**
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
$language = $row->getSourceProperty('language');
$bid = $row->getSourceProperty('bid');
// If this row has been migrated it is a duplicate then skip it.
if ($this->idMap->lookupDestinationIds(['bid' => $bid, 'language' => $language])) {
return FALSE;
}
// Save the translation for this property.
$property = $row->getSourceProperty('property');
$row->setSourceProperty($property . '_translated', $row->getSourceProperty('translation'));
// Get the translation for the property not already in the row.
$translation = ($property === 'title') ? 'body' : 'title';
$query = $this->select('i18n_strings', 'i18n')
->fields('i18n', ['lid'])
->condition('i18n.property', $translation)
->condition('i18n.objectid', $bid);
$query->leftJoin('locales_target', 'lt', 'i18n.lid = lt.lid');
$query->condition('lt.language', $language)
->addField('lt', 'translation');
$results = $query->execute()->fetchAssoc();
if (!$results) {
$row->setSourceProperty($translation . '_translated', NULL);
}
else {
$row->setSourceProperty($translation . '_translated', $results['translation']);
}
}
/**
* {@inheritdoc}
*/
public function fields() {
return [
'bid' => $this->t('The block numeric identifier.'),
'format' => $this->t('Input format of the custom block/box content.'),
'lid' => $this->t('i18n_string table id'),
'language' => $this->t('Language for this field.'),
'property' => $this->t('Block property'),
'translation' => $this->t('The translation of the value of "property".'),
'title' => $this->t('Block title.'),
'title_translated' => $this->t('Block title translation.'),
'body' => $this->t('Block body.'),
'body_translated' => $this->t('Block body translation.'),
];
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['bid']['type'] = 'integer';
$ids['bid']['alias'] = 'b';
$ids['language']['type'] = 'string';
return $ids;
}
const CUSTOM_BLOCK_TABLE = 'boxes';
const I18N_STRING_TABLE = 'i18n_strings';
}
@@ -0,0 +1,99 @@
<?php
namespace Drupal\block_content\Plugin\migrate\source\d7;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
use Drupal\content_translation\Plugin\migrate\source\I18nQueryTrait;
/**
* Gets Drupal 7 custom block translation from database.
*
* @MigrateSource(
* id = "d7_block_custom_translation",
* source_module = "block"
* )
*/
class BlockCustomTranslation extends DrupalSqlBase {
use I18nQueryTrait;
/**
* Drupal 7 table names.
*/
const CUSTOM_BLOCK_TABLE = 'block_custom';
const I18N_STRING_TABLE = 'i18n_string';
/**
* {@inheritdoc}
*/
public function query() {
// Build a query based on blockCustomTable table where each row has the
// translation for only one property, either title or description. The
// method prepareRow() is then used to obtain the translation for the
// other property.
$query = $this->select(static::CUSTOM_BLOCK_TABLE, 'b')
->fields('b', ['bid', 'format', 'body'])
->fields('i18n', ['property'])
->fields('lt', ['lid', 'translation', 'language'])
->orderBy('b.bid')
->isNotNull('lt.lid');
// Use 'title' for the info field to match the property name in
// i18nStringTable.
$query->addField('b', 'info', 'title');
// Add in the property, which is either title or body. Cast the bid to text
// so PostgreSQL can make the join.
$query->leftJoin(static::I18N_STRING_TABLE, 'i18n', 'i18n.objectid = CAST(b.bid as CHAR(255))');
$query->condition('i18n.type', 'block');
// Add in the translation for the property.
$query->leftJoin('locales_target', 'lt', 'lt.lid = i18n.lid');
return $query;
}
/**
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
parent::prepareRow($row);
// Set the i18n string table for use in I18nQueryTrait.
$this->i18nStringTable = static::I18N_STRING_TABLE;
// Save the translation for this property.
$property_in_row = $row->getSourceProperty('property');
// Get the translation for the property not already in the row and save it
// in the row.
$property_not_in_row = ($property_in_row === 'title') ? 'body' : 'title';
return $this->getPropertyNotInRowTranslation($row, $property_not_in_row, 'bid', $this->idMap);
}
/**
* {@inheritdoc}
*/
public function fields() {
return [
'bid' => $this->t('The block numeric identifier.'),
'format' => $this->t('Input format of the custom block/box content.'),
'lid' => $this->t('i18n_string table id'),
'language' => $this->t('Language for this field.'),
'property' => $this->t('Block property'),
'translation' => $this->t('The translation of the value of "property".'),
'title' => $this->t('Block title.'),
'title_translated' => $this->t('Block title translation.'),
'body' => $this->t('Block body.'),
'body_translated' => $this->t('Block body translation.'),
];
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['bid']['type'] = 'integer';
$ids['bid']['alias'] = 'b';
$ids['language']['type'] = 'string';
return $ids;
}
}
@@ -0,0 +1,69 @@
<?php
namespace Drupal\Tests\block_content\Kernel\Migrate\d7;
use Drupal\block_content\Entity\BlockContent;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migration of i18n custom block strings.
*
* @group migrate_drupal_7
*/
class MigrateCustomBlockContentTranslationTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'block_content',
'content_translation',
'filter',
'language',
'text',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['block_content']);
$this->installEntitySchema('block_content');
$this->executeMigrations([
'language',
'd7_filter_format',
'block_content_type',
'block_content_body_field',
'd7_custom_block',
'd7_custom_block_translation',
]);
}
/**
* Tests the Drupal 7 i18n custom block strings to Drupal 8 migration.
*/
public function testCustomBlockContentTranslation() {
/** @var \Drupal\block_content\Entity\BlockContent $block */
$block = BlockContent::load(1)->getTranslation('fr');
$this->assertSame('fr - Mildly amusing limerick of the day', $block->label());
$this->assertGreaterThanOrEqual($block->getChangedTime(), \Drupal::time()->getRequestTime());
$this->assertLessThanOrEqual(time(), $block->getChangedTime());
$this->assertSame('fr', $block->language()->getId());
$translation = "fr - A fellow jumped off a high wall\r\nAnd had a most terrible fall\r\nHe went back to bed\r\nWith a bump on his head\r\nThat's why you don't jump off a wall";
$this->assertSame($translation, $block->body->value);
$this->assertSame('filtered_html', $block->body->format);
$block = $block->getTranslation('is');
$this->assertSame('is - Mildly amusing limerick of the day', $block->label());
$this->assertGreaterThanOrEqual($block->getChangedTime(), \Drupal::time()->getRequestTime());
$this->assertLessThanOrEqual(time(), $block->getChangedTime());
$this->assertSame('is', $block->language()->getId());
$text = "A fellow jumped off a high wall\r\nAnd had a most terrible fall\r\nHe went back to bed\r\nWith a bump on his head\r\nThat's why you don't jump off a wall";
$this->assertSame($text, $block->body->value);
$this->assertSame('filtered_html', $block->body->format);
}
}
@@ -0,0 +1,148 @@
<?php
namespace Drupal\Tests\block_content\Kernel\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests i18n custom block translations source plugin.
*
* @covers \Drupal\block_content\Plugin\migrate\source\d7\BlockCustomTranslation
*
* @group content_translation
*/
class BlockCustomTranslationTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block_content', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['database']['block_custom'] = [
[
'bid' => 1,
'body' => 'box 1 body',
'info' => 'box 1 title',
'format' => '2',
],
[
'bid' => 2,
'body' => 'box 2 body',
'info' => 'box 2 title',
'format' => '2',
],
];
$tests[0]['database']['i18n_string'] = [
[
'lid' => 1,
'objectid' => 1,
'type' => 'block',
'property' => 'title',
'objectindex' => 1,
'format' => 0,
],
[
'lid' => 2,
'objectid' => 1,
'type' => 'block',
'property' => 'body',
'objectindex' => 1,
'format' => 0,
],
[
'lid' => 3,
'objectid' => 2,
'type' => 'block',
'property' => 'body',
'objectindex' => 2,
'format' => 2,
],
];
$tests[0]['database']['locales_target'] = [
[
'lid' => 1,
'language' => 'fr',
'translation' => 'fr - title translation',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'lid' => 2,
'language' => 'fr',
'translation' => 'fr - body translation',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'lid' => 3,
'language' => 'zu',
'translation' => 'zu - body translation',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
];
$tests[0]['database']['system'] = [
[
'type' => 'module',
'name' => 'system',
'schema_version' => '7001',
'status' => '1',
],
];
$tests[0]['expected_results'] = [
[
'lid' => '1',
'property' => 'title',
'language' => 'fr',
'translation' => 'fr - title translation',
'bid' => '1',
'format' => '2',
'title_translated' => 'fr - title translation',
'body_translated' => 'fr - body translation',
'title' => 'box 1 title',
'body' => 'box 1 body',
],
[
'lid' => '2',
'property' => 'body',
'language' => 'fr',
'translation' => 'fr - body translation',
'bid' => '1',
'format' => '2',
'title_translated' => 'fr - title translation',
'body_translated' => 'fr - body translation',
'title' => 'box 1 title',
'body' => 'box 1 body',
],
[
'lid' => '3',
'property' => 'body',
'language' => 'zu',
'translation' => 'zu - body translation',
'bid' => '2',
'format' => '2',
'title_translated' => NULL,
'body_translated' => 'zu - body translation',
'title' => 'box 2 title',
'body' => 'box 2 body',
],
];
return $tests;
}
}
@@ -177,7 +177,7 @@ class BookTest extends BrowserTestBase {
$this->checkBookNode($other_book, [$node], FALSE, FALSE, $node, []);
$this->checkBookNode($node, NULL, $other_book, $other_book, FALSE, [$other_book]);
// Test that we can save a book programatically.
// Test that we can save a book programmatically.
$this->drupalLogin($this->bookAuthor);
$book = $this->createBookNode('new');
$book->save();
@@ -282,7 +282,7 @@ class BookTest extends BrowserTestBase {
$nodes[$child] = $this->createBookNode($book->id(), $nodes[$parent]->id());
}
$this->drupalGet($nodes[0]->toUrl('edit-form'));
// Snice Node 0 has children 2 levels deep, nodes 10 and 11 should not
// Since Node 0 has children 2 levels deep, nodes 10 and 11 should not
// appear in the selector.
$this->assertNoOption('edit-book-pid', $nodes[10]->id());
$this->assertNoOption('edit-book-pid', $nodes[11]->id());
+1 -1
View File
@@ -68,7 +68,7 @@
* Reacts on a change in the editor element.
*
* @param {HTMLElement} element
* The element where the change occured.
* The element where the change occurred.
* @param {function} callback
* Callback called with the value of the editor.
*
@@ -24,6 +24,10 @@ class CKEditorPlugin extends Plugin {
/**
* The plugin ID.
*
* This MUST match the name of the CKEditor plugin itself (written in
* JavaScript). Otherwise CKEditor will throw JavaScript errors when it runs,
* because it fails to load this CKEditor plugin.
*
* @var string
*/
public $id;
@@ -9,7 +9,7 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\editor\Entity\Editor;
/**
* Defines a "LlamaContextualAndbutton" plugin, with a contextually OR toolbar
* Defines a "LlamaContextualAndButton" plugin, with a contextually OR toolbar
* builder-enabled "llama" feature.
*
* @CKEditorPlugin(
@@ -24,6 +24,13 @@ class CKEditorIntegrationTest extends WebDriverTestBase {
*/
protected $account;
/**
* The FilterFormat config entity used for testing.
*
* @var \Drupal\filter\FilterFormatInterface
*/
protected $filterFormat;
/**
* {@inheritdoc}
*/
@@ -36,12 +43,12 @@ class CKEditorIntegrationTest extends WebDriverTestBase {
parent::setUp();
// Create a text format and associate CKEditor.
$filtered_html_format = FilterFormat::create([
$this->filterFormat = FilterFormat::create([
'format' => 'filtered_html',
'name' => 'Filtered HTML',
'weight' => 0,
]);
$filtered_html_format->save();
$this->filterFormat->save();
Editor::create([
'format' => 'filtered_html',
@@ -119,4 +126,55 @@ class CKEditorIntegrationTest extends WebDriverTestBase {
self::assertEquals($before_url, $after_url, 'History back works.');
}
/**
* Tests if the Image button appears and works as expected.
*/
public function testDrupalImageDialog() {
$session = $this->getSession();
$web_assert = $this->assertSession();
$this->drupalGet('node/add/page');
$session->getPage();
// Asserts the Image button is present in the toolbar.
$web_assert->elementExists('css', '#cke_edit-body-0-value .cke_button__drupalimage');
// Asserts the image dialog opens when clicking the Image button.
$this->click('.cke_button__drupalimage');
$this->assertNotEmpty($web_assert->waitForElement('css', '.ui-dialog'));
$web_assert->elementContains('css', '.ui-dialog .ui-dialog-titlebar', 'Insert Image');
}
/**
* Tests if the Drupal Image Caption plugin appears and works as expected.
*/
public function testDrupalImageCaptionDialog() {
$web_assert = $this->assertSession();
// Disable the caption filter.
$this->filterFormat->setFilterConfig('filter_caption', [
'status' => FALSE,
]);
$this->filterFormat->save();
// If the caption filter is disabled, its checkbox should be absent.
$this->drupalGet('node/add/page');
$this->click('.cke_button__drupalimage');
$this->assertNotEmpty($web_assert->waitForElement('css', '.ui-dialog'));
$web_assert->elementNotExists('css', '.ui-dialog input[name="attributes[hasCaption]"]');
// Enable the caption filter again.
$this->filterFormat->setFilterConfig('filter_caption', [
'status' => TRUE,
]);
$this->filterFormat->save();
// If the caption filter is enabled, its checkbox should be present.
$this->drupalGet('node/add/page');
$this->click('.cke_button__drupalimage');
$this->assertNotEmpty($web_assert->waitForElement('css', '.ui-dialog'));
$web_assert->elementExists('css', '.ui-dialog input[name="attributes[hasCaption]"]');
}
}
+2 -1
View File
@@ -25,6 +25,7 @@ use Drupal\field\FieldConfigInterface;
use Drupal\field\FieldStorageConfigInterface;
use Drupal\node\NodeInterface;
use Drupal\user\RoleInterface;
use Drupal\user\UserInterface;
/**
* Anonymous posters cannot enter their contact information.
@@ -518,7 +519,7 @@ function comment_node_search_result(EntityInterface $node) {
/**
* Implements hook_user_cancel().
*/
function comment_user_cancel($edit, $account, $method) {
function comment_user_cancel($edit, UserInterface $account, $method) {
switch ($method) {
case 'user_cancel_block_unpublish':
$comments = entity_load_multiple_by_properties('comment', ['uid' => $account->id()]);
+13 -1
View File
@@ -16,7 +16,16 @@ process:
plugin: migration_lookup
migration: d6_comment
source: pid
entity_id: nid
entity_id:
-
plugin: migration_lookup
migration:
- d6_node
- d6_node_translation
source: nid
-
plugin: skip_on_empty
method: row
entity_type: 'constants/entity_type'
comment_type:
-
@@ -26,6 +35,7 @@ process:
-
plugin: skip_on_empty
method: row
langcode: language
field_name: '@comment_type'
subject: subject
uid: uid
@@ -52,3 +62,5 @@ migration_dependencies:
- d6_comment_entity_form_display
- d6_user
- d6_filter_format
optional:
- d6_node_translation
+13 -1
View File
@@ -17,7 +17,16 @@ process:
plugin: migration_lookup
migration: d7_comment
source: pid
entity_id: nid
entity_id:
-
plugin: migration_lookup
migration:
- d7_node
- d7_node_translation
source: nid
-
plugin: skip_on_empty
method: row
entity_type: 'constants/entity_type'
comment_type:
-
@@ -27,6 +36,7 @@ process:
-
plugin: skip_on_empty
method: row
langcode: language
field_name: '@comment_type'
subject: subject
uid: uid
@@ -45,3 +55,5 @@ migration_dependencies:
required:
- d7_node
- d7_comment_type
optional:
- d7_node_translation
@@ -50,7 +50,7 @@ class CommentFieldItemList extends FieldItemList {
return $return_as_object ? $result : $result->isAllowed();
}
if ($operation === 'view') {
// Only users with either post comments or access comments permisison can
// Only users with "post comments" or "access comments" permission can
// view the field value. The formatter,
// Drupal\comment\Plugin\Field\FieldFormatter\CommentDefaultFormatter,
// takes care of showing the thread and form based on individual
@@ -56,8 +56,9 @@ interface CommentInterface extends ContentEntityInterface, EntityChangedInterfac
/**
* Returns the entity to which the comment is attached.
*
* @return \Drupal\Core\Entity\FieldableEntityInterface
* The entity on which the comment is attached.
* @return \Drupal\Core\Entity\FieldableEntityInterface|null
* The entity on which the comment is attached or NULL if the comment is an
* orphan.
*/
public function getCommentedEntity();
@@ -25,7 +25,7 @@ class Comment extends DrupalSqlBase {
'mail', 'homepage', 'format',
]);
$query->innerJoin('node', 'n', 'c.nid = n.nid');
$query->fields('n', ['type']);
$query->fields('n', ['type', 'language']);
$query->orderBy('c.timestamp');
return $query;
}
@@ -61,6 +61,13 @@ class Comment extends DrupalSqlBase {
// In D6, status=0 means published, while in D8 means the opposite.
// See https://www.drupal.org/node/237636.
$row->setSourceProperty('status', !$row->getSourceProperty('status'));
// If node did not have a language, use site default language as a fallback.
if (!$row->getSourceProperty('language')) {
$language_default = $this->variableGet('language_default', NULL);
$language = $language_default ? $language_default->language : 'en';
$row->setSourceProperty('language', $language);
}
return $row;
}
@@ -84,6 +91,7 @@ class Comment extends DrupalSqlBase {
'mail' => $this->t("The comment author's email address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on."),
'homepage' => $this->t("The comment author's home page address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on."),
'type' => $this->t("The {node}.type to which this comment is a reply."),
'language' => $this->t("The {node}.language to which this comment is a reply. Site default language is used as a fallback if node does not have a language."),
];
}
@@ -36,8 +36,20 @@ class Comment extends FieldableEntity {
$comment_type = 'comment_node_' . $node_type;
$row->setSourceProperty('comment_type', 'comment_node_' . $node_type);
foreach (array_keys($this->getFields('comment', $comment_type)) as $field) {
$row->setSourceProperty($field, $this->getFieldValues('comment', $field, $cid));
// If this entity was translated using Entity Translation, we need to get
// its source language to get the field values in the right language.
// The translations will be migrated by the d7_comment_entity_translation
// migration.
$entity_translatable = $this->isEntityTranslatable('comment') && (int) $this->variableGet('language_content_type_' . $node_type, 0) === 4;
$source_language = $this->getEntityTranslationSourceLanguage('comment', $cid);
$language = $entity_translatable && $source_language ? $source_language : $row->getSourceProperty('language');
// Get Field API field values.
foreach ($this->getFields('comment', $comment_type) as $field_name => $field) {
// Ensure we're using the right language if the entity and the field are
// translatable.
$field_language = $entity_translatable && $field['translatable'] ? $language : NULL;
$row->setSourceProperty($field_name, $this->getFieldValues('comment', $field_name, $cid, NULL, $field_language));
}
// If the comment subject was replaced by a real field using the Drupal 7
@@ -72,6 +84,7 @@ class Comment extends FieldableEntity {
'name' => $this->t("The comment author's name. Uses {users}.name if the user is logged in, otherwise uses the value typed into the comment form."),
'mail' => $this->t("The comment author's email address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on."),
'homepage' => $this->t("The comment author's home page address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on."),
'language' => $this->t('The comment language.'),
'type' => $this->t("The {node}.type to which this comment is a reply."),
];
}
@@ -0,0 +1,103 @@
<?php
namespace Drupal\comment\Plugin\migrate\source\d7;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;
/**
* Provides Drupal 7 comment entity translation source plugin.
*
* @MigrateSource(
* id = "d7_comment_entity_translation",
* source_module = "entity_translation"
* )
*/
class CommentEntityTranslation extends FieldableEntity {
/**
* {@inheritdoc}
*/
public function query() {
$query = $this->select('entity_translation', 'et')
->fields('et')
->fields('c', [
'subject',
])
->condition('et.entity_type', 'comment')
->condition('et.source', '', '<>');
$query->innerJoin('comment', 'c', 'c.cid = et.entity_id');
$query->innerJoin('node', 'n', 'n.nid = c.nid');
$query->addField('n', 'type', 'node_type');
$query->orderBy('et.created');
return $query;
}
/**
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
$cid = $row->getSourceProperty('entity_id');
$language = $row->getSourceProperty('language');
$node_type = $row->getSourceProperty('node_type');
$comment_type = 'comment_node_' . $node_type;
// Get Field API field values.
foreach ($this->getFields('comment', $comment_type) as $field_name => $field) {
// Ensure we're using the right language if the entity is translatable.
$field_language = $field['translatable'] ? $language : NULL;
$row->setSourceProperty($field_name, $this->getFieldValues('comment', $field_name, $cid, NULL, $field_language));
}
// If the comment subject was replaced by a real field using the Drupal 7
// Title module, use the field value instead of the comment subject.
if ($this->moduleExists('title')) {
$subject_field = $row->getSourceProperty('subject_field');
if (isset($subject_field[0]['value'])) {
$row->setSourceProperty('subject', $subject_field[0]['value']);
}
}
return parent::prepareRow($row);
}
/**
* {@inheritdoc}
*/
public function fields() {
return [
'entity_type' => $this->t('The entity type this translation relates to'),
'entity_id' => $this->t('The entity ID this translation relates to'),
'revision_id' => $this->t('The entity revision ID this translation relates to'),
'language' => $this->t('The target language for this translation.'),
'source' => $this->t('The source language from which this translation was created.'),
'uid' => $this->t('The author of this translation.'),
'status' => $this->t('Boolean indicating whether the translation is published (visible to non-administrators).'),
'translate' => $this->t('A boolean indicating whether this translation needs to be updated.'),
'created' => $this->t('The Unix timestamp when the translation was created.'),
'changed' => $this->t('The Unix timestamp when the translation was most recently saved.'),
'subject' => $this->t('The comment title.'),
];
}
/**
* {@inheritdoc}
*/
public function getIds() {
return [
'entity_id' => [
'type' => 'integer',
'alias' => 'et',
],
'language' => [
'type' => 'string',
'alias' => 'et',
],
];
}
}
@@ -10,9 +10,9 @@ use Drupal\comment\Tests\CommentTestTrait;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field_ui\Tests\FieldUiTestTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
use Drupal\user\RoleInterface;
/**
@@ -14,7 +14,7 @@ class CommentHalJsonAnonTest extends CommentHalJsonTestBase {
/**
* {@inheritdoc}
*
* Anononymous users cannot edit their own comments.
* Anonymous users cannot edit their own comments.
*
* @see \Drupal\comment\CommentAccessControlHandler::checkAccess
*
@@ -24,7 +24,7 @@ class CommentJsonAnonTest extends CommentResourceTestBase {
/**
* {@inheritdoc}
*
* Anononymous users cannot edit their own comments.
* Anonymous users cannot edit their own comments.
*
* @see \Drupal\comment\CommentAccessControlHandler::checkAccess
*
@@ -60,7 +60,7 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
$this->grantPermissionsToTestedRole(['post comments']);
break;
case 'PATCH':
// Anononymous users are not ever allowed to edit their own comments. To
// Anonymous users are not ever allowed to edit their own comments. To
// be able to test PATCHing comments as the anonymous user, the more
// permissive 'administer comments' permission must be granted.
// @see \Drupal\comment\CommentAccessControlHandler::checkAccess
@@ -26,7 +26,7 @@ class CommentXmlAnonTest extends CommentResourceTestBase {
/**
* {@inheritdoc}
*
* Anononymous users cannot edit their own comments.
* Anonymous users cannot edit their own comments.
*
* @see \Drupal\comment\CommentAccessControlHandler::checkAccess
*
@@ -20,7 +20,12 @@ class MigrateCommentTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['comment', 'menu_ui'];
public static $modules = [
'comment',
'content_translation',
'language',
'menu_ui',
];
/**
* {@inheritdoc}
@@ -31,6 +36,7 @@ class MigrateCommentTest extends MigrateDrupal6TestBase {
$this->installEntitySchema('node');
$this->installEntitySchema('comment');
$this->installSchema('comment', ['comment_entity_statistics']);
$this->installSchema('node', ['node_access']);
$this->installConfig(['comment']);
// The entity.node.canonical route must exist when the RDF hook is called.
@@ -38,7 +44,10 @@ class MigrateCommentTest extends MigrateDrupal6TestBase {
$this->migrateContent();
$this->executeMigrations([
'language',
'd6_language_content_settings',
'd6_node',
'd6_node_translation',
'd6_comment_type',
'd6_comment_field',
'd6_comment_field_instance',
@@ -84,6 +93,31 @@ class MigrateCommentTest extends MigrateDrupal6TestBase {
$node = $comment->getCommentedEntity();
$this->assertInstanceOf(NodeInterface::class, $node);
$this->assertSame('1', $node->id());
// Tests that the language of the comment is migrated from the node.
$comment = Comment::load(7);
$this->assertSame('Comment to John Smith - EN', $comment->subject->value);
$this->assertSame('This is an English comment.', $comment->comment_body->value);
$this->assertSame('21', $comment->getCommentedEntityId());
$this->assertSame('node', $comment->getCommentedEntityTypeId());
$this->assertSame('en', $comment->language()->getId());
$node = $comment->getCommentedEntity();
$this->assertInstanceOf(NodeInterface::class, $node);
$this->assertSame('21', $node->id());
// Tests that the comment language is correct and that the commented entity
// is correctly migrated when the comment was posted to a node translation.
$comment = Comment::load(8);
$this->assertSame('Comment to John Smith - FR', $comment->subject->value);
$this->assertSame('This is a French comment.', $comment->comment_body->value);
$this->assertSame('21', $comment->getCommentedEntityId());
$this->assertSame('node', $comment->getCommentedEntityTypeId());
$this->assertSame('fr', $comment->language()->getId());
$node = $comment->getCommentedEntity();
$this->assertInstanceOf(NodeInterface::class, $node);
$this->assertSame('21', $node->id());
}
}
@@ -19,11 +19,15 @@ class MigrateCommentTest extends MigrateDrupal7TestBase {
*/
public static $modules = [
'comment',
'content_translation',
'datetime',
'filter',
'image',
'language',
'link',
'menu_ui',
// Required for translation migrations.
'migrate_drupal_multilingual',
'node',
'taxonomy',
'telephone',
@@ -38,14 +42,19 @@ class MigrateCommentTest extends MigrateDrupal7TestBase {
$this->installEntitySchema('node');
$this->installEntitySchema('comment');
$this->installEntitySchema('taxonomy_term');
$this->installConfig(['comment', 'node']);
$this->installSchema('comment', ['comment_entity_statistics']);
$this->installSchema('node', ['node_access']);
$this->executeMigrations([
'language',
'd7_node_type',
'd7_language_content_settings',
'd7_user_role',
'd7_user',
'd7_node_type',
'd7_node',
'd7_node_translation',
'd7_comment_type',
'd7_comment_field',
'd7_comment_field_instance',
@@ -55,6 +64,8 @@ class MigrateCommentTest extends MigrateDrupal7TestBase {
'd7_field',
'd7_field_instance',
'd7_comment',
'd7_entity_translation_settings',
'd7_comment_entity_translation',
]);
}
@@ -64,7 +75,7 @@ class MigrateCommentTest extends MigrateDrupal7TestBase {
public function testMigration() {
$comment = Comment::load(1);
$this->assertInstanceOf(Comment::class, $comment);
$this->assertSame('A comment', $comment->getSubject());
$this->assertSame('Subject field in English', $comment->getSubject());
$this->assertSame('1421727536', $comment->getCreatedTime());
$this->assertSame('1421727536', $comment->getChangedTime());
$this->assertTrue($comment->getStatus());
@@ -73,6 +84,7 @@ class MigrateCommentTest extends MigrateDrupal7TestBase {
$this->assertSame('This is a comment', $comment->comment_body->value);
$this->assertSame('filtered_html', $comment->comment_body->format);
$this->assertSame('2001:db8:ffff:ffff:ffff:ffff:ffff:ffff', $comment->getHostname());
$this->assertSame('en', $comment->language()->getId());
$this->assertSame('1000000', $comment->field_integer->value);
$node = $comment->getCommentedEntity();
@@ -85,6 +97,59 @@ class MigrateCommentTest extends MigrateDrupal7TestBase {
$this->assertInstanceOf(Comment::class, $comment);
$this->assertSame('TNG for the win!', $comment->getSubject());
$this->assertSame('TNG is better than DS9.', $comment->comment_body->value);
$this->assertSame('en', $comment->language()->getId());
// Tests that the commented entity is correctly migrated when the comment
// was posted to a node translation.
$comment = Comment::load(3);
$this->assertInstanceOf(Comment::class, $comment);
$this->assertSame('Comment to IS translation', $comment->getSubject());
$this->assertSame('This is a comment to an Icelandic translation.', $comment->comment_body->value);
$this->assertSame('2', $comment->getCommentedEntityId());
$this->assertSame('node', $comment->getCommentedEntityTypeId());
$this->assertSame('is', $comment->language()->getId());
$node = $comment->getCommentedEntity();
$this->assertInstanceOf(NodeInterface::class, $node);
$this->assertSame('2', $node->id());
}
/**
* Tests the migration of comment entity translations.
*/
public function testCommentEntityTranslations() {
$manager = $this->container->get('content_translation.manager');
// Get the comment and its translations.
$comment = Comment::load(1);
$comment_fr = $comment->getTranslation('fr');
$comment_is = $comment->getTranslation('is');
// Test that fields translated with Entity Translation are migrated.
$this->assertSame('Subject field in English', $comment->getSubject());
$this->assertSame('Subject field in French', $comment_fr->getSubject());
$this->assertSame('Subject field in Icelandic', $comment_is->getSubject());
$this->assertSame('1000000', $comment->field_integer->value);
$this->assertSame('2000000', $comment_fr->field_integer->value);
$this->assertSame('3000000', $comment_is->field_integer->value);
// Test that the French translation metadata is correctly migrated.
$metadata_fr = $manager->getTranslationMetadata($comment_fr);
$this->assertFalse($metadata_fr->isPublished());
$this->assertSame('en', $metadata_fr->getSource());
$this->assertSame('1', $metadata_fr->getAuthor()->uid->value);
$this->assertSame('1531837764', $metadata_fr->getCreatedTime());
$this->assertSame('1531837764', $metadata_fr->getChangedTime());
$this->assertFalse($metadata_fr->isOutdated());
// Test that the Icelandic translation metadata is correctly migrated.
$metadata_is = $manager->getTranslationMetadata($comment_is);
$this->assertTrue($metadata_is->isPublished());
$this->assertSame('en', $metadata_is->getSource());
$this->assertSame('2', $metadata_is->getAuthor()->uid->value);
$this->assertSame('1531838064', $metadata_is->getCreatedTime());
$this->assertSame('1531838064', $metadata_is->getChangedTime());
$this->assertTrue($metadata_is->isOutdated());
}
}
@@ -66,10 +66,12 @@ class CommentSourceWithHighWaterTest extends MigrateSqlSourceTestBase {
[
'nid' => 2,
'type' => 'story',
'language' => 'en',
],
[
'nid' => 3,
'type' => 'page',
'language' => 'fr',
],
];
@@ -91,6 +93,7 @@ class CommentSourceWithHighWaterTest extends MigrateSqlSourceTestBase {
'homepage' => '',
'format' => 'testformat2',
'type' => 'page',
'language' => 'fr',
],
];
@@ -65,10 +65,12 @@ class CommentTest extends MigrateSqlSourceTestBase {
[
'nid' => 2,
'type' => 'story',
'language' => 'en',
],
[
'nid' => 3,
'type' => 'page',
'language' => 'fr',
],
];
@@ -90,6 +92,7 @@ class CommentTest extends MigrateSqlSourceTestBase {
'homepage' => '',
'format' => 'testformat1',
'type' => 'story',
'language' => 'en',
],
[
'cid' => 2,
@@ -107,6 +110,7 @@ class CommentTest extends MigrateSqlSourceTestBase {
'homepage' => '',
'format' => 'testformat2',
'type' => 'page',
'language' => 'fr',
],
];
@@ -0,0 +1,281 @@
<?php
namespace Drupal\Tests\comment\Kernel\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D7 comment entity translation source plugin.
*
* @covers \Drupal\comment\Plugin\migrate\source\d7\CommentEntityTranslation
* @group comment
*/
class CommentEntityTranslationTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['comment', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['comment'] = [
[
'cid' => '1',
'pid' => '0',
'nid' => '1',
'uid' => '1',
'subject' => 'A comment',
'hostname' => '::1',
'created' => '1421727536',
'changed' => '1421727536',
'status' => '1',
'thread' => '01/',
'name' => 'admin',
'mail' => '',
'homepage' => '',
'language' => 'en',
],
];
$tests[0]['source_data']['entity_translation'] = [
[
'entity_type' => 'comment',
'entity_id' => 1,
'revision_id' => 1,
'language' => 'en',
'source' => '',
'uid' => 1,
'status' => 1,
'translate' => 0,
'created' => '1421727536',
'changed' => '1421727536',
],
[
'entity_type' => 'comment',
'entity_id' => 1,
'revision_id' => 1,
'language' => 'fr',
'source' => 'en',
'uid' => 1,
'status' => 0,
'translate' => 0,
'created' => 1531343508,
'changed' => 1531343508,
],
[
'entity_type' => 'comment',
'entity_id' => 1,
'revision_id' => 1,
'language' => 'es',
'source' => 'en',
'uid' => 2,
'status' => 1,
'translate' => 1,
'created' => 1531343528,
'changed' => 1531343528,
],
];
$tests[0]['source_data']['field_config'] = [
[
'id' => 1,
'field_name' => 'field_test',
'type' => 'text',
'module' => 'text',
'active' => 1,
'storage_type' => 'field_sql_storage',
'storage_module' => 'field_sql_storage',
'storage_active' => 1,
'locked' => 1,
'data' => 'a:0:{}',
'cardinality' => 1,
'translatable' => 1,
'deleted' => 0,
],
[
'id' => 2,
'field_name' => 'subject_field',
'type' => 'text',
'module' => 'text',
'active' => 1,
'storage_type' => 'field_sql_storage',
'storage_module' => 'field_sql_storage',
'storage_active' => 1,
'locked' => 1,
'data' => 'a:0:{}',
'cardinality' => 1,
'translatable' => 1,
'deleted' => 0,
],
];
$tests[0]['source_data']['field_config_instance'] = [
[
'id' => '1',
'field_id' => '1',
'field_name' => 'field_test',
'entity_type' => 'comment',
'bundle' => 'comment_node_test_content_type',
'data' => 'a:0:{}',
'deleted' => '0',
],
[
'id' => '2',
'field_id' => '2',
'field_name' => 'subject_field',
'entity_type' => 'comment',
'bundle' => 'comment_node_test_content_type',
'data' => 'a:0:{}',
'deleted' => '0',
],
];
$tests[0]['source_data']['field_data_field_test'] = [
[
'entity_type' => 'comment',
'bundle' => 'comment_node_test_content_type',
'deleted' => '0',
'entity_id' => '1',
'revision_id' => '1',
'language' => 'en',
'delta' => '0',
'field_test_value' => 'This is an English comment',
'field_test_format' => NULL,
],
[
'entity_type' => 'comment',
'bundle' => 'comment_node_test_content_type',
'deleted' => '0',
'entity_id' => '1',
'revision_id' => '1',
'language' => 'fr',
'delta' => '0',
'field_test_value' => 'This is a French comment',
'field_test_format' => NULL,
],
[
'entity_type' => 'comment',
'bundle' => 'comment_node_test_content_type',
'deleted' => '0',
'entity_id' => '1',
'revision_id' => '1',
'language' => 'es',
'delta' => '0',
'field_test_value' => 'This is a Spanish comment',
'field_test_format' => NULL,
],
];
$tests[0]['source_data']['field_data_subject_field'] = [
[
'entity_type' => 'comment',
'bundle' => 'comment_node_test_content_type',
'deleted' => '0',
'entity_id' => '1',
'revision_id' => '1',
'language' => 'en',
'delta' => '0',
'subject_field_value' => 'Comment subject in English',
'subject_field_format' => NULL,
],
[
'entity_type' => 'comment',
'bundle' => 'comment_node_test_content_type',
'deleted' => '0',
'entity_id' => '1',
'revision_id' => '1',
'language' => 'fr',
'delta' => '0',
'subject_field_value' => 'Comment subject in French',
'subject_field_format' => NULL,
],
[
'entity_type' => 'comment',
'bundle' => 'comment_node_test_content_type',
'deleted' => '0',
'entity_id' => '1',
'revision_id' => '1',
'language' => 'es',
'delta' => '0',
'subject_field_value' => 'Comment subject in Spanish',
'subject_field_format' => NULL,
],
];
$tests[0]['source_data']['node'] = [
[
'nid' => '1',
'vid' => '1',
'type' => 'test_content_type',
'language' => 'en',
'title' => 'A Node',
'uid' => '1',
'status' => '1',
'created' => '1421727515',
'changed' => '1421727515',
'comment' => '2',
'promote' => '1',
'sticky' => '0',
'tnid' => '0',
'translate' => '0',
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
'subject' => 'A comment',
'entity_type' => 'comment',
'entity_id' => '1',
'revision_id' => '1',
'language' => 'fr',
'source' => 'en',
'uid' => '1',
'status' => '0',
'translate' => '0',
'created' => '1531343508',
'changed' => '1531343508',
'field_test' => [
[
'value' => 'This is a French comment',
'format' => NULL,
],
],
'subject_field' => [
[
'value' => 'Comment subject in French',
'format' => NULL,
],
],
],
[
'subject' => 'A comment',
'entity_type' => 'comment',
'entity_id' => '1',
'revision_id' => '1',
'language' => 'es',
'source' => 'en',
'uid' => '2',
'status' => '1',
'translate' => '1',
'created' => '1531343528',
'changed' => '1531343528',
'field_test' => [
[
'value' => 'This is a Spanish comment',
'format' => NULL,
],
],
'subject_field' => [
[
'value' => 'Comment subject in Spanish',
'format' => NULL,
],
],
],
];
return $tests;
}
}
@@ -107,7 +107,16 @@ class ConfigInstallProfileOverrideTest extends BrowserTestBase {
$this->container->get('module_installer')->install(['dblog']);
$this->rebuildContainer();
$this->assertEqual($config_test_storage->load('override_unmet')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration and is installed when its dependencies are met.');
$this->assertEqual($config_test_storage->load('completely_new')->label(), 'Completely new optional configuration', 'The optional config_test entity is provided by the profile optional configuration and is installed when its dependencies are met.');
$config_test_new = $config_test_storage->load('completely_new');
$this->assertEqual($config_test_new->label(), 'Completely new optional configuration', 'The optional config_test entity is provided by the profile optional configuration and is installed when its dependencies are met.');
$config_test_new->delete();
// Install another module that provides optional configuration and ensure
// that deleted profile configuration is not re-created.
$this->container->get('module_installer')->install(['config_other_module_config_test']);
$this->rebuildContainer();
$config_test_storage = \Drupal::entityManager()->getStorage('config_test');
$this->assertNull($config_test_storage->load('completely_new'));
}
}
@@ -55,7 +55,7 @@ class ConfigUninstallViaCliImportTest extends KernelTestBase {
}
/**
* Tests that the config mopdule can be uninstalled via CLI config import.
* Tests that the config module can be uninstalled via CLI config import.
*
* @see \Drupal\config\ConfigSubscriber
*/
@@ -0,0 +1,57 @@
id: d6_field_instance_label_description_translation
label: Field label and description translation
migration_tags:
- Drupal 6
- Configuration
- Multilingual
source:
plugin: d6_field_instance_label_description_translation
constants:
entity_type: node
process:
langcode:
plugin: skip_on_empty
source: language
method: row
translation:
plugin: skip_on_empty
source: translation
method: row
field_name_parts:
plugin: explode
source: objectid
delimiter: '-'
field_name:
plugin: extract
source: '@field_name_parts'
index:
- 1
bundle:
plugin: extract
source: '@field_name_parts'
index:
- 0
exists:
-
plugin: migration_lookup
migration: d6_field_instance
source:
- '@field_name'
- '@bundle'
-
plugin: skip_on_empty
method: row
entity_type: 'constants/entity_type'
property:
plugin: static_map
source: property
bypass: true
map:
widget_label: label
widget_description: description
destination:
plugin: entity:field_config
translations: true
migration_dependencies:
required:
- d6_field_instance
@@ -0,0 +1,178 @@
id: d6_field_instance_option_translation
label: Field instance option configuration translation
migration_tags:
- Drupal 6
- Configuration
- Multilingual
source:
plugin: d6_field_instance_option_translation
skip_count: true
constants:
entity_type: node
property: settings
process:
# We skip field types that don't exist because they weren't migrated by the
# field migration.
field_type_exists:
-
plugin: migration_lookup
migration: d6_field
source:
- objectid
-
plugin: extract
index:
- 1
-
plugin: skip_on_empty
method: row
# Use the process from d6_field to determine the field type.
type:
plugin: field_type
source:
- type
- widget_type
map:
userreference:
userreference_select: entity_reference
userreference_buttons: entity_reference
userreference_autocomplete: entity_reference
nodereference:
nodereference_select: entity_reference
number_integer:
number: integer
optionwidgets_select: list_integer
optionwidgets_buttons: list_integer
optionwidgets_onoff: boolean
number_decimal:
number: decimal
optionwidgets_select: list_float
optionwidgets_buttons: list_float
optionwidgets_onoff: boolean
number_float:
number: float
optionwidgets_select: list_float
optionwidgets_buttons: list_float
optionwidgets_onoff: boolean
email:
email_textfield: email
filefield:
imagefield_widget: image
filefield_widget: file
fr_phone:
phone_textfield: telephone
be_phone:
phone_textfield: telephone
it_phone:
phone_textfield: telephone
el_phone:
phone_textfield: telephone
ch_phone:
phone_textfield: telephone
ca_phone:
phone_textfield: telephone
cr_phone:
phone_textfield: telephone
pa_phone:
phone_textfield: telephone
gb_phone:
phone_textfield: telephone
ru_phone:
phone_textfield: telephone
ua_phone:
phone_textfield: telephone
es_phone:
phone_textfield: telephone
au_phone:
phone_textfield: telephone
cs_phone:
phone_textfield: telephone
hu_phone:
phone_textfield: telephone
pl_phone:
phone_textfield: telephone
nl_phone:
phone_textfield: telephone
se_phone:
phone_textfield: telephone
za_phone:
phone_textfield: telephone
il_phone:
phone_textfield: telephone
nz_phone:
phone_textfield: telephone
br_phone:
phone_textfield: telephone
cl_phone:
phone_textfield: telephone
cn_phone:
phone_textfield: telephone
hk_phone:
phone_textfield: telephone
mo_phone:
phone_textfield: telephone
ph_phone:
phone_textfield: telephone
sg_phone:
phone_textfield: telephone
jo_phone:
phone_textfield: telephone
eg_phone:
phone_textfield: telephone
pk_phone:
phone_textfield: telephone
int_phone:
phone_textfield: telephone
boolean_type:
-
plugin: static_map
source: '@type'
map:
boolean: boolean
default_value: false
-
plugin: skip_on_empty
method: row
bundle:
-
plugin: migration_lookup
migration: d6_node_type
source: type_name
-
plugin: skip_on_empty
method: row
langcode:
plugin: skip_on_empty
source: language
method: row
field_name: objectid
entity_type: 'constants/entity_type'
results:
plugin: d6_field_instance_option_translation
source:
- '@type'
- global_settings
translation:
-
plugin: extract
source: '@results'
index: [1]
-
plugin: skip_on_empty
method: row
property:
-
plugin: extract
source: '@results'
index: [0]
-
plugin: skip_on_empty
method: row
destination:
plugin: entity:field_config
translations: true
migration_dependencies:
required:
- d6_node_type
- d6_field_instance
- d6_field_option_translation
@@ -0,0 +1,144 @@
id: d6_field_option_translation
label: Field option configuration translation
migration_tags:
- Drupal 6
- Configuration
- Multilingual
source:
plugin: d6_field_option_translation
skip_count: true
constants:
entity_type: node
allowed_values: settings
process:
entity_type: 'constants/entity_type'
status: active
langcode:
plugin: skip_on_empty
source: language
method: row
field_name: objectid
# Use the process from d6_field to determine the field type.
type:
plugin: field_type
source:
- type
- widget_type
map:
userreference:
userreference_select: entity_reference
userreference_buttons: entity_reference
userreference_autocomplete: entity_reference
nodereference:
nodereference_select: entity_reference
number_integer:
number: integer
optionwidgets_select: list_integer
optionwidgets_buttons: list_integer
optionwidgets_onoff: boolean
number_decimal:
number: decimal
optionwidgets_select: list_float
optionwidgets_buttons: list_float
optionwidgets_onoff: boolean
number_float:
number: float
optionwidgets_select: list_float
optionwidgets_buttons: list_float
optionwidgets_onoff: boolean
email:
email_textfield: email
filefield:
imagefield_widget: image
filefield_widget: file
fr_phone:
phone_textfield: telephone
be_phone:
phone_textfield: telephone
it_phone:
phone_textfield: telephone
el_phone:
phone_textfield: telephone
ch_phone:
phone_textfield: telephone
ca_phone:
phone_textfield: telephone
cr_phone:
phone_textfield: telephone
pa_phone:
phone_textfield: telephone
gb_phone:
phone_textfield: telephone
ru_phone:
phone_textfield: telephone
ua_phone:
phone_textfield: telephone
es_phone:
phone_textfield: telephone
au_phone:
phone_textfield: telephone
cs_phone:
phone_textfield: telephone
hu_phone:
phone_textfield: telephone
pl_phone:
phone_textfield: telephone
nl_phone:
phone_textfield: telephone
se_phone:
phone_textfield: telephone
za_phone:
phone_textfield: telephone
il_phone:
phone_textfield: telephone
nz_phone:
phone_textfield: telephone
br_phone:
phone_textfield: telephone
cl_phone:
phone_textfield: telephone
cn_phone:
phone_textfield: telephone
hk_phone:
phone_textfield: telephone
mo_phone:
phone_textfield: telephone
ph_phone:
phone_textfield: telephone
sg_phone:
phone_textfield: telephone
jo_phone:
phone_textfield: telephone
eg_phone:
phone_textfield: telephone
pk_phone:
phone_textfield: telephone
int_phone:
phone_textfield: telephone
results:
plugin: d6_field_option_translation
source:
- '@type'
- global_settings
translation:
-
plugin: extract
source: '@results'
index: [1]
-
plugin: skip_on_empty
method: row
property:
-
plugin: extract
source: '@results'
index: [0]
-
plugin: skip_on_empty
method: row
destination:
plugin: entity:field_storage_config
translations: true
migration_dependencies:
required:
- d6_field
@@ -5,7 +5,7 @@ migration_tags:
- Configuration
- Multilingual
source:
plugin: variable_translation
plugin: d6_variable_translation
variables:
- site_offline_message
source_module: i18n
@@ -5,7 +5,7 @@ migration_tags:
- Configuration
- Multilingual
source:
plugin: variable_translation
plugin: d6_variable_translation
constants:
slash: '/'
variables:
@@ -5,7 +5,7 @@ migration_tags:
- Configuration
- Multilingual
source:
plugin: variable_translation
plugin: d6_variable_translation
variables:
- user_mail_status_activated_subject
- user_mail_status_activated_body
@@ -5,7 +5,7 @@ migration_tags:
- Configuration
- Multilingual
source:
plugin: variable_translation
plugin: d6_variable_translation
variables:
- user_mail_status_blocked_notify
- user_mail_status_activated_notify
@@ -0,0 +1,17 @@
id: d7_system_maintenance_translation
label: Maintenance page configuration
migration_tags:
- Drupal 7
- Configuration
- Multilingual
source:
plugin: d7_variable_translation
variables:
- maintenance_mode_message
process:
langcode: language
message: maintenance_mode_message
destination:
plugin: config
config_name: system.maintenance
translations: true
@@ -0,0 +1,27 @@
id: d7_system_site_translation
label: Site configuration translation
migration_tags:
- Drupal 7
- Configuration
- Multilingual
source:
plugin: d7_variable_translation
variables:
- site_name
- site_mail
- site_slogan
- site_frontpage
- site_403
- site_404
process:
langcode: language
name: site_name
mail: site_mail
slogan: site_slogan
'page/front': site_frontpage
'page/403': site_403
'page/404': site_404
destination:
plugin: config
config_name: system.site
translations: true
@@ -0,0 +1,79 @@
id: d7_user_mail_translation
label: User mail configuration translation
migration_tags:
- Drupal 7
- Configuration
- Multilingual
source:
plugin: d7_variable_translation
variables:
- user_mail_cancel_confirm_subject
- user_mail_cancel_confirm_body
- user_mail_password_reset_subject
- user_mail_password_reset_body
- user_mail_register_admin_created_subject
- user_mail_register_admin_created_body
- user_mail_register_no_approval_required_subject
- user_mail_register_no_approval_required_body
- user_mail_register_pending_approval_subject
- user_mail_register_pending_approval_body
- user_mail_status_activated_subject
- user_mail_status_activated_body
- user_mail_status_blocked_subject
- user_mail_status_blocked_body
- user_mail_status_canceled_subject
- user_mail_status_canceled_body
process:
langcode: language
'cancel_confirm/subject':
plugin: convert_tokens
source: user_mail_cancel_confirm_subject
'cancel_confirm/body':
plugin: convert_tokens
source: user_mail_cancel_confirm_body
'password_reset/subject':
plugin: convert_tokens
source: user_mail_password_reset_subject
'password_reset/body':
plugin: convert_tokens
source: user_mail_password_reset_body
'register_admin_created/subject':
plugin: convert_tokens
source: user_mail_register_admin_created_subject
'register_admin_created/body':
plugin: convert_tokens
source: user_mail_register_admin_created_body
'register_no_approval_required/subject':
plugin: convert_tokens
source: user_mail_register_no_approval_required_subject
'register_no_approval_required/body':
plugin: convert_tokens
source: user_mail_register_no_approval_required_body
'register_pending_approval/subject':
plugin: convert_tokens
source: user_mail_register_pending_approval_subject
'register_pending_approval/body':
plugin: convert_tokens
source: user_mail_register_pending_approval_body
'status_activated/subject':
plugin: convert_tokens
source: user_mail_status_activated_subject
'status_activated/body':
plugin: convert_tokens
source: user_mail_status_activated_body
'status_blocked/subject':
plugin: convert_tokens
source: user_mail_status_blocked_subject
'status_blocked/body':
plugin: convert_tokens
source: user_mail_status_blocked_body
'status_canceled/subject':
plugin: convert_tokens
source: user_mail_status_canceled_subject
'status_canceled/body':
plugin: convert_tokens
source: user_mail_status_canceled_body
destination:
plugin: config
config_name: user.mail
translations: true
@@ -0,0 +1,17 @@
id: d7_user_settings_translation
label: User settings configuration translation
migration_tags:
- Drupal 7
- Configuration
- Multilingual
source:
plugin: d7_variable_translation
variables:
- anonymous
process:
langcode: language
anonymous: anonymous
destination:
plugin: config
config_name: user.settings
translations: true
@@ -23,7 +23,7 @@ interface ElementInterface {
public static function create(TypedDataInterface $schema);
/**
* Builds a render array containg the source and translation form elements.
* Builds a render array containing the source and translation form elements.
*
* @param \Drupal\Core\Language\LanguageInterface $source_language
* The source language of the configuration object.
@@ -0,0 +1,37 @@
<?php
namespace Drupal\Tests\config_translation\Kernel\Migrate\d7;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migrations of i18n maintenance variable.
*
* @group migrate_drupal_7
*/
class MigrateSystemMaintenanceTranslationTest extends MigrateDrupal7TestBase {
public static $modules = [
'language',
'config_translation',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d7_system_maintenance_translation');
}
/**
* Tests migrations of i18n maintenance variable.
*/
public function testSystemMaintenance() {
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('is', 'system.maintenance');
$this->assertSame('is - This is a custom maintenance mode message.', $config->get('message'));
}
}
@@ -0,0 +1,54 @@
<?php
namespace Drupal\Tests\config_translation\Kernel\Migrate\d7;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Migrate multilingual site variables.
*
* @group migrate_drupal_7
*/
class MigrateSystemSiteTranslationTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'language',
'config_translation',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d7_system_site_translation');
}
/**
* Tests migration of system (site) variables to system.site.yml.
*/
public function testSystemSite() {
$language_manager = \Drupal::service('language_manager');
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'system.site');
$this->assertSame('The Site Name', $config_translation->get('name'));
$this->assertSame('fr - The Slogan', $config_translation->get('slogan'));
$this->assertSame('node', $config_translation->get('page.403'));
$this->assertSame('node', $config_translation->get('page.404'));
$this->assertSame('node', $config_translation->get('page.front'));
$this->assertSame(NULL, $config_translation->get('admin_compact_mode'));
$config_translation = $language_manager->getLanguageConfigOverride('is', 'system.site');
$this->assertSame('is - The Site Name', $config_translation->get('name'));
$this->assertSame('is - The Slogan', $config_translation->get('slogan'));
$this->assertSame('node/1', $config_translation->get('page.403'));
$this->assertSame('node/6', $config_translation->get('page.404'));
$this->assertSame('node/4', $config_translation->get('page.front'));
$this->assertNULL($config_translation->get('admin_compact_mode'));
}
}
@@ -0,0 +1,74 @@
<?php
namespace Drupal\Tests\config_translation\Kernel\Migrate\d7;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Test migration of i18n user variables.
*
* @group migrate_drupal_7
*/
class MigrateUserConfigsTranslationTest extends MigrateDrupal7TestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'language',
'locale',
'config_translation',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('locale', [
'locales_source',
'locales_target',
'locales_location',
]);
$this->executeMigrations([
'd7_user_mail_translation',
'd7_user_settings_translation',
]);
}
/**
* Tests migration of i18n user variables to user.mail and user.settings.
*/
public function testUserConfig() {
// Tests migration of i18n user variables to user.mail.yml.
$language_manager = \Drupal::service('language_manager');
$config = $language_manager->getLanguageConfigOverride('is', 'user.mail');
$this->assertSame('is - Are you sure?', $config->get('cancel_confirm.subject'));
$this->assertSame('is - A little birdie said you wanted to cancel your account.', $config->get('cancel_confirm.body'));
$this->assertSame('is - Fix your password', $config->get('password_reset.subject'));
$this->assertSame("is - Nope! You're locked out forever.", $config->get('password_reset.body'));
$this->assertSame('is - Gawd made you an account', $config->get('register_admin_created.subject'));
$this->assertSame("is - ...and she could take it away.\r\n[site:name], [site:url]", $config->get('register_admin_created.body'));
$this->assertSame('is - Welcome!', $config->get('register_no_approval_required.subject'));
$this->assertSame('is - You can now log in if you can figure out how to use Drupal!', $config->get('register_no_approval_required.body'));
$this->assertSame('is - Soon...', $config->get('register_pending_approval.subject'));
$this->assertSame('is - ...you will join our Circle. Let the Drupal flow through you.', $config->get('register_pending_approval.body'));
$this->assertSame('is - Your account is approved!', $config->get('status_activated.subject'));
$this->assertSame('is - Your account was activated, and there was much rejoicing.', $config->get('status_activated.body'));
$this->assertSame('is - BEGONE!', $config->get('status_blocked.subject'));
$this->assertSame('is - You no longer please the robot overlords. Go to your room and chill out.', $config->get('status_blocked.body'));
$this->assertSame('is - So long, bub', $config->get('status_canceled.subject'));
$this->assertSame('is - The gates of Drupal are closed to you. Now you will work in the salt mines.', $config->get('status_canceled.body'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'user.mail', $config->get());
// Tests migration of i18n user variables to user.settings.yml.
$config = $language_manager->getLanguageConfigOverride('is', 'user.settings');
$this->assertSame('is - anonymous', $config->get('anonymous'));
}
}
@@ -5,9 +5,9 @@ namespace Drupal\Tests\contact\Functional;
use Drupal\contact\Entity\ContactForm;
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\field_ui\Tests\FieldUiTestTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
use Drupal\user\RoleInterface;
/**
@@ -127,7 +127,7 @@ class ModerationStateWidget extends OptionsSelectWidget implements ContainerFact
$transitions = $this->validator->getValidTransitions($entity, $this->currentUser);
$transition_labels = [];
$default_value = NULL;
$default_value = $items->value;
foreach ($transitions as $transition) {
$transition_to_state = $transition->to();
$transition_labels[$transition_to_state->id()] = $transition_to_state->label();
@@ -16,7 +16,6 @@ use Drupal\Core\TypedData\ComputedItemListTrait;
class ModerationStateFieldItemList extends FieldItemList {
use ComputedItemListTrait {
ensureComputedValue as traitEnsureComputedValue;
get as traitGet;
}
@@ -34,19 +33,6 @@ class ModerationStateFieldItemList extends FieldItemList {
}
}
/**
* {@inheritdoc}
*/
protected function ensureComputedValue() {
// If the moderation state field is set to an empty value, always recompute
// the state. Empty is not a valid moderation state value, when none is
// present the default state is used.
if (!isset($this->list[0]) || $this->list[0]->isEmpty()) {
$this->valueComputed = FALSE;
}
$this->traitEnsureComputedValue();
}
/**
* Gets the moderation state ID linked to a content entity revision.
*
@@ -140,10 +126,8 @@ class ModerationStateFieldItemList extends FieldItemList {
*/
public function setValue($values, $notify = TRUE) {
parent::setValue($values, $notify);
$this->valueComputed = TRUE;
if (isset($this->list[0])) {
$this->valueComputed = TRUE;
}
// If the parent created a field item and if the parent should be notified
// about the change (e.g. this is not initialized with the current value),
// update the moderated entity.
@@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
@@ -89,6 +90,13 @@ class ModerationStateConstraintValidator extends ConstraintValidator implements
return;
}
// If the entity is moderated and the item list is empty, ensure users see
// the same required message as typical NotNull constraints.
if ($value->isEmpty()) {
$this->context->addViolation((new NotNullConstraint())->message);
return;
}
$workflow = $this->moderationInformation->getWorkflowForEntity($entity);
if (!$workflow->getTypePlugin()->hasState($entity->moderation_state->value)) {
@@ -102,20 +102,70 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
}
/**
* Tests the computed field when it is unset or set to an empty value.
* Tests the item list when it is emptied and appended to.
*/
public function testSetEmptyState() {
public function testEmptyStateAndAppend() {
// This test case mimics the lifecycle of an entity that is being patched in
// a rest resource.
$this->testNode->moderation_state->setValue([]);
$this->assertTrue($this->testNode->moderation_state->isEmpty());
$this->assertEmptiedModerationFieldItemList();
$this->testNode->moderation_state->appendItem();
$this->assertEquals(1, $this->testNode->moderation_state->count());
$this->assertEquals(NULL, $this->testNode->moderation_state->value);
$this->assertEmptiedModerationFieldItemList();
}
/**
* Test an empty value assigned to the field item.
*/
public function testEmptyFieldItem() {
$this->testNode->moderation_state->value = '';
$this->assertEquals('draft', $this->testNode->moderation_state->value);
$this->assertEquals('', $this->testNode->moderation_state->value);
$this->assertEmptiedModerationFieldItemList();
}
/**
* Test an empty value assigned to the field item list.
*/
public function testEmptyFieldItemList() {
$this->testNode->moderation_state = '';
$this->assertEquals('draft', $this->testNode->moderation_state->value);
$this->assertEquals('', $this->testNode->moderation_state->value);
$this->assertEmptiedModerationFieldItemList();
}
/**
* Test the field item when it is unset.
*/
public function testUnsetItemList() {
unset($this->testNode->moderation_state);
$this->assertEquals('draft', $this->testNode->moderation_state->value);
$this->assertEquals(NULL, $this->testNode->moderation_state->value);
$this->assertEmptiedModerationFieldItemList();
}
/**
* Test the field item when it is assigned NULL.
*/
public function testAssignNullItemList() {
$this->testNode->moderation_state = NULL;
$this->assertEquals('draft', $this->testNode->moderation_state->value);
$this->assertEquals(NULL, $this->testNode->moderation_state->value);
$this->assertEmptiedModerationFieldItemList();
}
/**
* Assert the set of expectations when the moderation state field is emptied.
*/
protected function assertEmptiedModerationFieldItemList() {
$this->assertTrue($this->testNode->moderation_state->isEmpty());
// Test the empty value causes a violation in the entity.
$violations = $this->testNode->validate();
$this->assertCount(1, $violations);
$this->assertEquals('This value should not be null.', $violations->get(0)->getMessage());
// Test that incorrectly saving the entity regardless will not produce a
// change in the moderation state.
$this->testNode->save();
$this->assertEquals('draft', Node::load($this->testNode->id())->moderation_state->value);
}
/**
@@ -131,6 +181,7 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
$unmoderated_node->moderation_state = NULL;
$this->assertEquals(0, $unmoderated_node->moderation_state->count());
$this->assertCount(0, $unmoderated_node->validate());
}
/**
@@ -0,0 +1,28 @@
id: d7_comment_entity_translation
label: Comment entity translations
migration_tags:
- Drupal 7
- translation
- Content
class: Drupal\comment\Plugin\migrate\D7Comment
source:
plugin: d7_comment_entity_translation
process:
cid: entity_id
subject: subject
langcode: language
uid: uid
status: status
created: created
changed: changed
content_translation_source: source
content_translation_outdated: translate
destination:
plugin: entity:comment
translations: true
destination_module: content_translation
migration_dependencies:
required:
- language
- d7_entity_translation_settings
- d7_comment
@@ -0,0 +1,50 @@
id: d7_custom_block_translation
label: Custom block translations
migration_tags:
- Drupal 7
- Content
- Multilingual
source:
plugin: d7_block_custom_translation
process:
id:
plugin: migration_lookup
migration: d7_custom_block
source:
- bid
langcode: language
info:
-
plugin: callback
source:
- title_translated
- title
callable: array_filter
-
plugin: callback
callable: current
'body/value':
-
plugin: callback
source:
- body_translated
- body
callable: array_filter
-
plugin: callback
callable: current
'body/format':
plugin: migration_lookup
migration: d7_filter_format
source: format
destination:
plugin: entity:block_content
no_stub: true
translations: true
destination_module: content_translation
migration_dependencies:
required:
- d7_filter_format
- block_content_body_field
- d7_custom_block
- language
@@ -23,10 +23,14 @@ process:
revision_log: log
revision_timestamp: timestamp
content_translation_source: source
# Boolean indicating whether this translation needs to be updated.
content_translation_outdated: translate
destination:
plugin: entity:node
translations: true
destination_module: content_translation
migration_dependencies:
required:
- language
- d7_entity_translation_settings
- d7_node
@@ -0,0 +1,32 @@
id: d7_taxonomy_term_entity_translation
label: Taxonomy term entity translations
migration_tags:
- Drupal 7
- translation
- Content
- Multilingual
deriver: Drupal\taxonomy\Plugin\migrate\D7TaxonomyTermDeriver
source:
plugin: d7_taxonomy_term_entity_translation
process:
tid: entity_id
name: name
description/value: description
description/format: format
langcode: language
status: status
content_translation_source: source
content_translation_outdated: translate
content_translation_uid: uid
content_translation_created: created
changed: changed
forum_container: is_container
destination:
plugin: entity:taxonomy_term
translations: true
destination_module: content_translation
migration_dependencies:
required:
- language
- d7_entity_translation_settings
- d7_taxonomy_term
@@ -145,37 +145,14 @@ class ContentTranslationController extends ControllerBase {
$translations = $entity->getTranslationLanguages();
}
$add_url = new Url(
"entity.$entity_type_id.content_translation_add",
[
'source' => $original,
'target' => $language->getId(),
$entity_type_id => $entity->id(),
],
[
'language' => $language,
]
);
$edit_url = new Url(
"entity.$entity_type_id.content_translation_edit",
[
'language' => $language->getId(),
$entity_type_id => $entity->id(),
],
[
'language' => $language,
]
);
$delete_url = new Url(
"entity.$entity_type_id.content_translation_delete",
[
'language' => $language->getId(),
$entity_type_id => $entity->id(),
],
[
'language' => $language,
]
);
$options = ['language' => $language];
$add_url = $entity->toUrl('drupal:content-translation-add', $options)
->setRouteParameter('source', $original)
->setRouteParameter('target', $language->getId());
$edit_url = $entity->toUrl('drupal:content-translation-edit', $options)
->setRouteParameter('language', $language->getId());
$delete_url = $entity->toUrl('drupal:content-translation-delete', $options)
->setRouteParameter('language', $language->getId());
$operations = [
'data' => [
'#type' => 'operations',
@@ -0,0 +1,87 @@
<?php
namespace Drupal\content_translation\Plugin\migrate\source;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\MigrateException;
use Drupal\migrate\Row;
/**
* Gets an i18n translation from the source database.
*/
trait I18nQueryTrait {
/**
* The i18n string table name.
*
* @var string
*/
protected $i18nStringTable;
/**
* Gets the translation for the property not already in the row.
*
* For some i18n migrations there are two translation values, such as a
* translated title and a translated description, that need to be retrieved.
* Since these values are stored in separate rows of the i18nStringTable
* table we get them individually, one in the source plugin query() and the
* other in prepareRow(). The names of the properties varies, for example,
* in BoxTranslation they are 'body' and 'title' whereas in
* MenuLinkTranslation they are 'title' and 'description'. This will save both
* translations to the row.
*
* @param \Drupal\migrate\Row $row
* The current migration row which must include both a 'language' property
* and an 'objectid' property. The 'objectid' is the value for the
* 'objectid' field in the i18n_string table.
* @param string $property_not_in_row
* The name of the property to get the translation for.
* @param string $object_id_name
* The value of the objectid in the i18n table.
* @param \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map
* The ID map.
*
* @return bool
* FALSE if the property has already been migrated.
*
* @throws \Drupal\migrate\MigrateException
*/
protected function getPropertyNotInRowTranslation(Row $row, $property_not_in_row, $object_id_name, MigrateIdMapInterface $id_map) {
$language = $row->getSourceProperty('language');
if (!$language) {
throw new MigrateException('No language found.');
}
$object_id = $row->getSourceProperty($object_id_name);
if (!$object_id) {
throw new MigrateException('No objectid found.');
}
// If this row has been migrated it is a duplicate so skip it.
if ($id_map->lookupDestinationIds([$object_id_name => $object_id, 'language' => $language])) {
return FALSE;
}
// Save the translation for the property already in the row.
$property_in_row = $row->getSourceProperty('property');
$row->setSourceProperty($property_in_row . '_translated', $row->getSourceProperty('translation'));
// Get the translation, if one exists, for the property not already in the
// row.
$query = $this->select($this->i18nStringTable, 'i18n')
->fields('i18n', ['lid'])
->condition('i18n.property', $property_not_in_row)
->condition('i18n.objectid', $object_id);
$query->leftJoin('locales_target', 'lt', 'i18n.lid = lt.lid');
$query->condition('lt.language', $language);
$query->addField('lt', 'translation');
$results = $query->execute()->fetchAssoc();
if (!$results) {
$row->setSourceProperty($property_not_in_row . '_translated', NULL);
}
else {
$row->setSourceProperty($property_not_in_row . '_translated', $results['translation']);
}
return TRUE;
}
}
@@ -36,18 +36,6 @@ class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
*/
protected function alterRoutes(RouteCollection $collection) {
foreach ($this->contentTranslationManager->getSupportedEntityTypes() as $entity_type_id => $entity_type) {
// Try to get the route from the current collection.
$link_template = $entity_type->getLinkTemplate('canonical');
if (strpos($link_template, '/') !== FALSE) {
$base_path = '/' . $link_template;
}
else {
if (!$entity_route = $collection->get("entity.$entity_type_id.canonical")) {
continue;
}
$base_path = $entity_route->getPath();
}
// Inherit admin route status from edit route, if exists.
$is_admin = FALSE;
$route_name = "entity.$entity_type_id.edit_form";
@@ -55,115 +43,122 @@ class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
$is_admin = (bool) $edit_route->getOption('_admin_route');
}
$path = $base_path . '/translations';
$load_latest_revision = ContentTranslationManager::isPendingRevisionSupportEnabled($entity_type_id);
$route = new Route(
$path,
[
'_controller' => '\Drupal\content_translation\Controller\ContentTranslationController::overview',
'entity_type_id' => $entity_type_id,
],
[
'_entity_access' => $entity_type_id . '.view',
'_access_content_translation_overview' => $entity_type_id,
],
[
'parameters' => [
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
if ($entity_type->hasLinkTemplate('drupal:content-translation-overview')) {
$route = new Route(
$entity_type->getLinkTemplate('drupal:content-translation-overview'),
[
'_controller' => '\Drupal\content_translation\Controller\ContentTranslationController::overview',
'entity_type_id' => $entity_type_id,
],
'_admin_route' => $is_admin,
]
);
$route_name = "entity.$entity_type_id.content_translation_overview";
$collection->add($route_name, $route);
$route = new Route(
$path . '/add/{source}/{target}',
[
'_controller' => '\Drupal\content_translation\Controller\ContentTranslationController::add',
'source' => NULL,
'target' => NULL,
'_title' => 'Add',
'entity_type_id' => $entity_type_id,
],
[
'_entity_access' => $entity_type_id . '.view',
'_access_content_translation_manage' => 'create',
],
[
'parameters' => [
'source' => [
'type' => 'language',
],
'target' => [
'type' => 'language',
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
[
'_entity_access' => $entity_type_id . '.view',
'_access_content_translation_overview' => $entity_type_id,
],
'_admin_route' => $is_admin,
]
);
$collection->add("entity.$entity_type_id.content_translation_add", $route);
[
'parameters' => [
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
]
);
$route_name = "entity.$entity_type_id.content_translation_overview";
$collection->add($route_name, $route);
}
$route = new Route(
$path . '/edit/{language}',
[
'_controller' => '\Drupal\content_translation\Controller\ContentTranslationController::edit',
'language' => NULL,
'_title' => 'Edit',
'entity_type_id' => $entity_type_id,
],
[
'_access_content_translation_manage' => 'update',
],
[
'parameters' => [
'language' => [
'type' => 'language',
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
]
);
$collection->add("entity.$entity_type_id.content_translation_edit", $route);
if ($entity_type->hasLinkTemplate('drupal:content-translation-add')) {
$route = new Route(
$entity_type->getLinkTemplate('drupal:content-translation-add'),
[
'_controller' => '\Drupal\content_translation\Controller\ContentTranslationController::add',
'source' => NULL,
'target' => NULL,
'_title' => 'Add',
'entity_type_id' => $entity_type_id,
$route = new Route(
$path . '/delete/{language}',
[
'_entity_form' => $entity_type_id . '.content_translation_deletion',
'language' => NULL,
'_title' => 'Delete',
'entity_type_id' => $entity_type_id,
],
[
'_access_content_translation_manage' => 'delete',
],
[
'parameters' => [
'language' => [
'type' => 'language',
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
]
);
$collection->add("entity.$entity_type_id.content_translation_delete", $route);
[
'_entity_access' => $entity_type_id . '.view',
'_access_content_translation_manage' => 'create',
],
[
'parameters' => [
'source' => [
'type' => 'language',
],
'target' => [
'type' => 'language',
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
]
);
$collection->add("entity.$entity_type_id.content_translation_add", $route);
}
if ($entity_type->hasLinkTemplate('drupal:content-translation-edit')) {
$route = new Route(
$entity_type->getLinkTemplate('drupal:content-translation-edit'),
[
'_controller' => '\Drupal\content_translation\Controller\ContentTranslationController::edit',
'language' => NULL,
'_title' => 'Edit',
'entity_type_id' => $entity_type_id,
],
[
'_access_content_translation_manage' => 'update',
],
[
'parameters' => [
'language' => [
'type' => 'language',
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
]
);
$collection->add("entity.$entity_type_id.content_translation_edit", $route);
}
if ($entity_type->hasLinkTemplate('drupal:content-translation-delete')) {
$route = new Route(
$entity_type->getLinkTemplate('drupal:content-translation-delete'),
[
'_entity_form' => $entity_type_id . '.content_translation_deletion',
'language' => NULL,
'_title' => 'Delete',
'entity_type_id' => $entity_type_id,
],
[
'_access_content_translation_manage' => 'delete',
],
[
'parameters' => [
'language' => [
'type' => 'language',
],
$entity_type_id => [
'type' => 'entity:' . $entity_type_id,
'load_latest_revision' => $load_latest_revision,
],
],
'_admin_route' => $is_admin,
]
);
$collection->add("entity.$entity_type_id.content_translation_delete", $route);
}
// Add our custom translation deletion access checker.
if ($load_latest_revision) {
@@ -249,7 +249,7 @@ class ContentTranslationSyncUnitTest extends KernelTestBase {
for ($delta = 0; $delta < $this->cardinality; $delta++) {
foreach ($this->columns as $column) {
// If the column is synchronized, the value should have been synced,
// for unsychronized columns, the value must not change.
// for unsynchronized columns, the value must not change.
$expected_value = in_array($column, $this->synchronized) ? $changed_items[$delta][$column] : $this->unchangedFieldValues[$langcode][$delta][$column];
$this->assertEqual($field_values[$langcode][$delta][$column], $expected_value, "Differing Item $delta column $column for langcode $langcode synced correctly");
}
+1 -1
View File
@@ -99,7 +99,7 @@
// Set the destination parameter on each of the contextual links.
const destination = `destination=${Drupal.encodePath(
drupalSettings.path.currentPath,
Drupal.url(drupalSettings.path.currentPath),
)}`;
$contextual.find('.contextual-links a').each(function() {
const url = this.getAttribute('href');
+1 -1
View File
@@ -55,7 +55,7 @@
$contextual.html(html).addClass('contextual').prepend(Drupal.theme('contextualTrigger'));
var destination = 'destination=' + Drupal.encodePath(drupalSettings.path.currentPath);
var destination = 'destination=' + Drupal.encodePath(Drupal.url(drupalSettings.path.currentPath));
$contextual.find('.contextual-links a').each(function () {
var url = this.getAttribute('href');
var glue = url.indexOf('?') === -1 ? '?' : '&';
@@ -96,9 +96,9 @@
* The keypress event.
*/
onKeypress(event) {
// The first tab key press is tracked so that an annoucement about tabbing
// constraints can be raised if edit mode is enabled when the page is
// loaded.
// The first tab key press is tracked so that an announcement about
// tabbing constraints can be raised if edit mode is enabled when the page
// is loaded.
if (
!this.announcedOnce &&
event.keyCode === 9 &&
@@ -92,4 +92,19 @@ class ContextualLinksTest extends WebDriverTestBase {
$this->assertSession()->pageTextContains('Everything is contextual!');
}
/**
* Test the contextual links destination.
*/
public function testContextualLinksDestination() {
$this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), [
'access contextual links',
'administer blocks',
]);
$this->drupalGet('user');
$this->assertSession()->waitForElement('css', '.contextual button');
$expected_destination_value = (string) $this->loggedInUser->toUrl()->toString();
$contextual_link_url_parsed = parse_url($this->getSession()->getPage()->findLink('Configure block')->getAttribute('href'));
$this->assertEquals("destination=$expected_destination_value", $contextual_link_url_parsed['query']);
}
}
@@ -2,17 +2,9 @@
namespace Drupal\datetime\Plugin\Field\FieldFormatter;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampAgoFormatter;
/**
* Plugin implementation of the 'Time ago' formatter for 'datetime' fields.
@@ -25,80 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* }
* )
*/
class DateTimeTimeAgoFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* The current Request object.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* Constructs a DateTimeTimeAgoFormatter object.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Third party settings.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, DateFormatterInterface $date_formatter, Request $request) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->dateFormatter = $date_formatter;
$this->request = $request;
}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
$settings = [
'future_format' => '@interval hence',
'past_format' => '@interval ago',
'granularity' => 2,
] + parent::defaultSettings();
return $settings;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('date.formatter'),
$container->get('request_stack')->getCurrentRequest()
);
}
class DateTimeTimeAgoFormatter extends TimestampAgoFormatter {
/**
* {@inheritdoc}
@@ -118,50 +37,6 @@ class DateTimeTimeAgoFormatter extends FormatterBase implements ContainerFactory
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form = parent::settingsForm($form, $form_state);
$form['future_format'] = [
'#type' => 'textfield',
'#title' => $this->t('Future format'),
'#default_value' => $this->getSetting('future_format'),
'#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
];
$form['past_format'] = [
'#type' => 'textfield',
'#title' => $this->t('Past format'),
'#default_value' => $this->getSetting('past_format'),
'#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
];
$form['granularity'] = [
'#type' => 'number',
'#title' => $this->t('Granularity'),
'#default_value' => $this->getSetting('granularity'),
'#description' => $this->t('How many time units should be shown in the formatted output.'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = parent::settingsSummary();
$future_date = new DrupalDateTime('1 year 1 month 1 week 1 day 1 hour 1 minute');
$past_date = new DrupalDateTime('-1 year -1 month -1 week -1 day -1 hour -1 minute');
$summary[] = t('Future date: %display', ['%display' => $this->formatDate($future_date)]);
$summary[] = t('Past date: %display', ['%display' => $this->formatDate($past_date)]);
return $summary;
}
/**
* Formats a date/time as a time interval.
*
@@ -172,27 +47,7 @@ class DateTimeTimeAgoFormatter extends FormatterBase implements ContainerFactory
* The formatted date/time string using the past or future format setting.
*/
protected function formatDate(DrupalDateTime $date) {
$granularity = $this->getSetting('granularity');
$timestamp = $date->getTimestamp();
$options = [
'granularity' => $granularity,
'return_as_object' => TRUE,
];
if ($this->request->server->get('REQUEST_TIME') > $timestamp) {
$result = $this->dateFormatter->formatTimeDiffSince($timestamp, $options);
$build = [
'#markup' => new FormattableMarkup($this->getSetting('past_format'), ['@interval' => $result->getString()]),
];
}
else {
$result = $this->dateFormatter->formatTimeDiffUntil($timestamp, $options);
$build = [
'#markup' => new FormattableMarkup($this->getSetting('future_format'), ['@interval' => $result->getString()]),
];
}
CacheableMetadata::createFromObject($result)->applyTo($build);
return $build;
return parent::formatTimestamp($date->getTimestamp());
}
}
@@ -50,7 +50,7 @@ class Date extends NumericDate implements ContainerFactoryPluginInterface {
protected $calculateOffset = TRUE;
/**
* The request stack used to determin current time.
* The request stack used to determine current time.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
@@ -171,7 +171,7 @@ class DateTimeFieldTest extends DateTestBase {
// past. First update the test entity so that the date difference always
// has the same interval. Since the database always stores UTC, and the
// interval will use this, force the test date to use UTC and not the local
// or user timezome.
// or user timezone.
$timestamp = REQUEST_TIME - 87654321;
$entity = EntityTest::load($id);
$field_name = $this->fieldStorage->getName();
@@ -201,7 +201,7 @@ class DateTimeFieldTest extends DateTestBase {
// future. First update the test entity so that the date difference always
// has the same interval. Since the database always stores UTC, and the
// interval will use this, force the test date to use UTC and not the local
// or user timezome.
// or user timezone.
$timestamp = REQUEST_TIME + 87654321;
$entity = EntityTest::load($id);
$field_name = $this->fieldStorage->getName();
@@ -323,7 +323,7 @@ class DateTimeFieldTest extends DateTestBase {
// past. First update the test entity so that the date difference always
// has the same interval. Since the database always stores UTC, and the
// interval will use this, force the test date to use UTC and not the local
// or user timezome.
// or user timezone.
$timestamp = REQUEST_TIME - 87654321;
$entity = EntityTest::load($id);
$field_name = $this->fieldStorage->getName();
@@ -350,7 +350,7 @@ class DateTimeFieldTest extends DateTestBase {
// future. First update the test entity so that the date difference always
// has the same interval. Since the database always stores UTC, and the
// interval will use this, force the test date to use UTC and not the local
// or user timezome.
// or user timezone.
$timestamp = REQUEST_TIME + 87654321;
$entity = EntityTest::load($id);
$field_name = $this->fieldStorage->getName();
@@ -0,0 +1,122 @@
<?php
namespace Drupal\Tests\datetime\Functional;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the functionality of DateTimeTimeAgoFormatter field formatter.
*
* @group field
*/
class DateTimeTimeAgoFormatterTest extends BrowserTestBase {
/**
* An array of display options to pass to entity_get_display().
*
* @var array
*/
protected $displayOptions;
/**
* A field storage to use in this test class.
*
* @var \Drupal\field\Entity\FieldStorageConfig
*/
protected $fieldStorage;
/**
* The field used in this test class.
*
* @var \Drupal\field\Entity\FieldConfig
*/
protected $field;
/**
* {@inheritdoc}
*/
public static $modules = ['datetime', 'entity_test', 'field_ui'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$web_user = $this->drupalCreateUser([
'access administration pages',
'view test entity',
'administer entity_test content',
'administer entity_test fields',
'administer entity_test display',
'administer entity_test form display',
'view the administration theme',
]);
$this->drupalLogin($web_user);
$field_name = 'field_datetime';
$type = 'datetime';
$widget_type = 'datetime_default';
$formatter_type = 'datetime_time_ago';
$this->fieldStorage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => $type,
]);
$this->fieldStorage->save();
$this->field = FieldConfig::create([
'field_storage' => $this->fieldStorage,
'bundle' => 'entity_test',
'required' => TRUE,
]);
$this->field->save();
EntityFormDisplay::load('entity_test.entity_test.default')
->setComponent($field_name, ['type' => $widget_type])
->save();
$this->displayOptions = [
'type' => $formatter_type,
'label' => 'hidden',
];
EntityViewDisplay::create([
'targetEntityType' => $this->field->getTargetEntityTypeId(),
'bundle' => $this->field->getTargetBundle(),
'mode' => 'full',
'status' => TRUE,
])->setComponent($field_name, $this->displayOptions)
->save();
}
/**
* Tests the formatter settings.
*/
public function testSettings() {
$this->drupalGet('entity_test/structure/entity_test/display');
$edit = [
'fields[field_datetime][region]' => 'content',
'fields[field_datetime][type]' => 'datetime_time_ago',
];
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->drupalPostForm(NULL, [], 'field_datetime_settings_edit');
$edit = [
'fields[field_datetime][settings_edit_form][settings][future_format]' => 'ends in @interval',
'fields[field_datetime][settings_edit_form][settings][past_format]' => 'started @interval ago',
'fields[field_datetime][settings_edit_form][settings][granularity]' => 3,
];
$this->drupalPostForm(NULL, $edit, 'Update');
$this->drupalPostForm(NULL, [], 'Save');
$this->assertSession()->pageTextContains('ends in 1 year 1 month 1 week');
$this->assertSession()->pageTextContains('started 1 year 1 month 1 week ago');
}
}
@@ -53,9 +53,13 @@ function datetime_range_view_presave(ViewEntityInterface $view) {
if (isset($display['display_options']['filters'])) {
foreach ($display['display_options']['filters'] as $field_name => &$filter) {
if ($filter['plugin_id'] === 'string') {
$table_data = Views::viewsData()->get($filter['table']);
if (!$table_data) {
continue;
}
// Get field config.
$filter_views_data = Views::viewsData()->get($filter['table'])[$filter['field']]['filter'];
$filter_views_data = $table_data[$filter['field']]['filter'];
if (!isset($filter_views_data['entity_type']) || !isset($filter_views_data['field_name'])) {
continue;
}
@@ -128,9 +132,13 @@ function datetime_range_view_presave(ViewEntityInterface $view) {
if (isset($display['display_options']['sorts'])) {
foreach ($display['display_options']['sorts'] as $field_name => &$sort) {
if ($sort['plugin_id'] === 'standard') {
$table_data = Views::viewsData()->get($sort['table']);
if (!$table_data) {
continue;
}
// Get field config.
$sort_views_data = Views::viewsData()->get($sort['table'])[$sort['field']]['sort'];
$sort_views_data = $table_data[$sort['field']]['sort'];
if (!isset($sort_views_data['entity_type']) || !isset($sort_views_data['field_name'])) {
continue;
}
@@ -0,0 +1,8 @@
name: 'Datetime range test'
type: module
description: 'Provides a testing module for datetime_range.'
package: Testing
version: VERSION
core: 8.x
dependencies:
- drupal:taxonomy
@@ -0,0 +1,17 @@
<?php
/**
* @file
* Contains datetime_range_test.module
*/
/**
* Implements hook_entity_type_alter().
*/
function datetime_range_test_entity_type_alter(array &$entity_types) {
// Inhibit views data for the 'taxonomy_term' entity type in order to cover
// the case when an entity type provides no views data.
// @see https://www.drupal.org/project/drupal/issues/2995578
// @see \Drupal\Tests\datetime_range\Kernel\Views\EntityTypeWithoutViewsDataTest
$entity_types['taxonomy_term']->setHandlerClass('views_data', NULL);
}
@@ -0,0 +1,43 @@
<?php
namespace Drupal\Tests\datetime_range\Kernel\Views;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Serialization\Yaml;
use Drupal\KernelTests\KernelTestBase;
use Drupal\views\Entity\View;
/**
* Tests datetime_range.module when an entity type provides no views data.
*
* @group datetime
*/
class EntityTypeWithoutViewsDataTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'datetime_range',
'datetime_range_test',
'node',
'system',
'taxonomy',
'text',
'user',
'views',
];
/**
* Tests the case when an entity type provides no views data.
*
* @see datetime_test_entity_type_alter()
* @see datetime_range_view_presave()
*/
public function testEntityTypeWithoutViewsData() {
$view_yaml = drupal_get_path('module', 'taxonomy') . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY . '/views.view.taxonomy_term.yml';
$values = Yaml::decode(file_get_contents($view_yaml));
$this->assertEquals(SAVED_NEW, View::create($values)->save());
}
}
@@ -8,7 +8,6 @@ use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Url;
use Drupal\dblog\Controller\DbLogController;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\Traits\Core\CronRunTrait;
/**
* Generate events and verify dblog entries; verify user access to log reports
@@ -17,7 +16,6 @@ use Drupal\Tests\Traits\Core\CronRunTrait;
* @group dblog
*/
class DbLogTest extends BrowserTestBase {
use CronRunTrait;
use FakeLogEntries;
/**
@@ -67,7 +65,6 @@ class DbLogTest extends BrowserTestBase {
$row_limit = 100;
$this->verifyRowLimit($row_limit);
$this->verifyCron($row_limit);
$this->verifyEvents();
$this->verifyReports();
$this->verifyBreadcrumbs();
@@ -137,52 +134,6 @@ class DbLogTest extends BrowserTestBase {
$this->assertTrue($current_limit == $row_limit, format_string('[Cache] Row limit variable of @count equals row limit of @limit', ['@count' => $current_limit, '@limit' => $row_limit]));
}
/**
* Verifies that cron correctly applies the database log row limit.
*
* @param int $row_limit
* The row limit.
*/
private function verifyCron($row_limit) {
// Generate additional log entries.
$this->generateLogEntries($row_limit + 10);
// Verify that the database log row count exceeds the row limit.
$count = db_query('SELECT COUNT(wid) FROM {watchdog}')->fetchField();
$this->assertTrue($count > $row_limit, format_string('Dblog row count of @count exceeds row limit of @limit', ['@count' => $count, '@limit' => $row_limit]));
// Get the number of enabled modules. Cron adds a log entry for each module.
$list = \Drupal::moduleHandler()->getImplementations('cron');
$module_count = count($list);
$cron_detailed_count = $this->runCron();
$this->assertTrue($cron_detailed_count == $module_count + 2, format_string('Cron added @count of @expected new log entries', ['@count' => $cron_detailed_count, '@expected' => $module_count + 2]));
// Test disabling of detailed cron logging.
$this->config('system.cron')->set('logging', 0)->save();
$cron_count = $this->runCron();
$this->assertTrue($cron_count = 1, format_string('Cron added @count of @expected new log entries', ['@count' => $cron_count, '@expected' => 1]));
}
/**
* Runs cron and returns number of new log entries.
*
* @return int
* Number of new watchdog entries.
*/
private function runCron() {
// Get last ID to compare against; log entries get deleted, so we can't
// reliably add the number of newly created log entries to the current count
// to measure number of log entries created by cron.
$last_id = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField();
// Run a cron job.
$this->cronRun();
// Get last ID after cron was run.
$current_id = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField();
return $current_id - $last_id;
}
/**
* Clear the entry logs by clicking on 'Clear log messages' button.
*/
@@ -65,7 +65,7 @@ class DblogFiltersAndFieldsUpgradeTest extends UpdatePathTestBase {
// Now save the view. This trigger dblog_view_presave().
$view->save();
// Finally check the same convertion proccess ran.
// Finally check the same conversion process ran.
$data = $view->storage->toArray();
$fields = $data['display']['default']['display_options']['fields'];
$filters = $data['display']['default']['display_options']['filters'];
@@ -6,7 +6,7 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\views\Views;
/**
* Test the upgrade path of changing the emtpy text area for watchdog view.
* Test the upgrade path of changing the empty text area for watchdog view.
*
* @see dblog_update_8600()
*
@@ -5,7 +5,7 @@ namespace Drupal\Tests\dblog\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Ensures that update hook that creates the watchdog view ran sucessfully.
* Ensures that update hook that creates the watchdog view ran successfully.
*
* @group Update
* @group legacy
@@ -25,7 +25,7 @@ class DblogRecentLogsUsingViewsUpdateTest extends UpdatePathTestBase {
* Ensures that update hook is run for dblog module.
*/
public function testUpdate() {
// Make sure the watchog view doesn't exist before the updates.
// Make sure the watchdog view doesn't exist before the updates.
$view = \Drupal::entityTypeManager()->getStorage('view')->load('watchdog');
$this->assertNull($view);
@@ -0,0 +1,77 @@
<?php
namespace Drupal\Tests\dblog\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\dblog\Functional\FakeLogEntries;
/**
* Generate events and verify dblog entries.
*
* @group dblog
*/
class DbLogTest extends KernelTestBase {
use FakeLogEntries;
/**
* {@inheritdoc}
*/
protected static $modules = ['dblog', 'system'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('dblog', ['watchdog']);
$this->installSchema('system', ['key_value_expire', 'sequences']);
$this->installConfig(['system']);
}
/**
* Tests that cron correctly applies the database log row limit.
*/
public function testDbLogCron() {
$row_limit = 100;
// Generate additional log entries.
$this->generateLogEntries($row_limit + 10);
// Verify that the database log row count exceeds the row limit.
$count = db_query('SELECT COUNT(wid) FROM {watchdog}')->fetchField();
$this->assertGreaterThan($row_limit, $count, format_string('Dblog row count of @count exceeds row limit of @limit', ['@count' => $count, '@limit' => $row_limit]));
// Get the number of enabled modules. Cron adds a log entry for each module.
$list = $this->container->get('module_handler')->getImplementations('cron');
$module_count = count($list);
$cron_detailed_count = $this->runCron();
$this->assertEquals($module_count + 2, $cron_detailed_count, format_string('Cron added @count of @expected new log entries', ['@count' => $cron_detailed_count, '@expected' => $module_count + 2]));
// Test disabling of detailed cron logging.
$this->config('system.cron')->set('logging', 0)->save();
$cron_count = $this->runCron();
$this->assertEquals(1, $cron_count, format_string('Cron added @count of @expected new log entries', ['@count' => $cron_count, '@expected' => 1]));
}
/**
* Runs cron and returns number of new log entries.
*
* @return int
* Number of new watchdog entries.
*/
private function runCron() {
// Get last ID to compare against; log entries get deleted, so we can't
// reliably add the number of newly created log entries to the current count
// to measure number of log entries created by cron.
$last_id = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField();
// Run a cron job.
$this->container->get('cron')->run();
// Get last ID after cron was run.
$current_id = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField();
return $current_id - $last_id;
}
}
+1 -1
View File
@@ -292,7 +292,7 @@ function hook_field_widget_multivalue_WIDGET_TYPE_form_alter(array &$elements, \
// Code here will only act on widgets of type WIDGET_TYPE. For example,
// hook_field_widget_multivalue_mymodule_autocomplete_form_alter() will only
// act on widgets of type 'mymodule_autocomplete'.
// Change the autcomplete route for each autocomplete element within the
// Change the autocomplete route for each autocomplete element within the
// multivalue widget.
foreach (Element::children($elements) as $delta => $element) {
$elements[$delta]['#autocomplete_route_name'] = 'mymodule.autocomplete_route';
@@ -0,0 +1,53 @@
<?php
namespace Drupal\field\Plugin\migrate\process\d6;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
/**
* Determines the settings property and translation for boolean fields.
*
* @MigrateProcessPlugin(
* id = "d6_field_instance_option_translation",
* handle_multiples = TRUE
* )
*/
class FieldInstanceOptionTranslation extends ProcessPluginBase {
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
list($field_type, $global_settings) = $value;
$option_key = 0;
$translation = '';
if (isset($global_settings['allowed_values'])) {
$list = explode("\n", $global_settings['allowed_values']);
$list = array_map('trim', $list);
$list = array_filter($list, 'strlen');
switch ($field_type) {
case 'boolean';
$option = preg_replace('/^option_/', '', $row->getSourceProperty('property'));
for ($i = 0; $i < 2; $i++) {
$value = $list[$i];
$tmp = explode("|", $value);
$original_option_key = isset($tmp[0]) ? $tmp[0] : NULL;
$option_key = ($i === 0) ? 'off_label' : 'on_label';
// Find property with name matching the original option.
if ($option == $original_option_key) {
$translation = $row->getSourceProperty('translation');
break;
}
}
break;
default:
}
}
return ['settings.' . $option_key, $translation];
}
}
@@ -0,0 +1,59 @@
<?php
namespace Drupal\field\Plugin\migrate\process\d6;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
/**
* Determines the allowed values translation for select lists.
*
* @MigrateProcessPlugin(
* id = "d6_field_option_translation",
* handle_multiples = TRUE
* )
*/
class FieldOptionTranslation extends ProcessPluginBase {
/**
* {@inheritdoc}
*
* Get the field default/mapped settings.
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
list($field_type, $global_settings) = $value;
$allowed_values = '';
$i = 0;
if (isset($global_settings['allowed_values'])) {
$list = explode("\n", $global_settings['allowed_values']);
$list = array_map('trim', $list);
$list = array_filter($list, 'strlen');
switch ($field_type) {
case 'list_string':
case 'list_integer':
case 'list_float':
// Remove the prefix used in the i18n_strings table for field options
// to get the option value.
$option = preg_replace('/^option_/', '', $row->getSourceProperty('property'));
$i = 0;
foreach ($list as $allowed_value) {
// Get the key for this allowed value which may be a key|label pair
// or or just key.
$value = explode("|", $allowed_value);
if (isset($value[0]) && ($value[0] == $option)) {
$allowed_values = ['label' => $row->getSourceProperty('translation')];
break;
}
$i++;
}
break;
default:
}
}
return ["settings.allowed_values.$i", $allowed_values];
}
}
@@ -0,0 +1,35 @@
<?php
namespace Drupal\field\Plugin\migrate\source\d6;
/**
* Gets field instance option label translations.
*
* @MigrateSource(
* id = "d6_field_instance_option_translation",
* source_module = "i18ncck"
* )
*/
class FieldInstanceOptionTranslation extends FieldOptionTranslation {
/**
* {@inheritdoc}
*/
public function query() {
$query = parent::query();
$query->join('content_node_field_instance', 'cnfi', 'cnf.field_name = cnfi.field_name');
$query->addField('cnfi', 'type_name');
return $query;
}
/**
* {@inheritdoc}
*/
public function fields() {
$fields = [
'type_name' => $this->t('Type (article, page, ....)'),
];
return parent::fields() + $fields;
}
}
@@ -0,0 +1,60 @@
<?php
namespace Drupal\field\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
/**
* Gets field label and description translations.
*
* @MigrateSource(
* id = "d6_field_instance_label_description_translation",
* source_module = "i18ncck"
* )
*/
class FieldLabelDescriptionTranslation extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
public function query() {
// Get translations for field labels and descriptions.
$query = $this->select('i18n_strings', 'i18n')
->fields('i18n', ['property', 'objectid', 'type'])
->fields('lt', ['lid', 'translation', 'language'])
->condition('i18n.type', 'field')
->isNotNull('language')
->isNotNull('translation');
$condition = $query->orConditionGroup()
->condition('property', 'widget_label')
->condition('property', 'widget_description');
$query->condition($condition);
$query->leftJoin('locales_target', 'lt', 'lt.lid = i18n.lid');
return $query;
}
/**
* {@inheritdoc}
*/
public function fields() {
return [
'property' => $this->t('Profile field ID.'),
'lid' => $this->t('Locales target language ID.'),
'language' => $this->t('Language for this field.'),
'translation' => $this->t('Translation of either the title or explanation.'),
];
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['property']['type'] = 'string';
$ids['language']['type'] = 'string';
$ids['lid']['type'] = 'integer';
$ids['lid']['alias'] = 'lt';
return $ids;
}
}
@@ -0,0 +1,78 @@
<?php
namespace Drupal\field\Plugin\migrate\source\d6;
/**
* Gets field option label translations.
*
* @MigrateSource(
* id = "d6_field_option_translation",
* source_module = "i18ncck"
* )
*/
class FieldOptionTranslation extends Field {
/**
* {@inheritdoc}
*/
public function query() {
// Get the fields that have field options translations.
$query = $this->select('i18n_strings', 'i18n')
->fields('i18n')
->fields('lt', [
'translation',
'language',
'plid',
'plural',
'i18n_status',
])
->condition('i18n.type', 'field')
->condition('property', 'option\_%', 'LIKE')
->isNotNull('translation');
$query->leftJoin('locales_target', 'lt', 'lt.lid = i18n.lid');
$query->leftjoin('content_node_field', 'cnf', 'cnf.field_name = i18n.objectid');
$query->addField('cnf', 'field_name');
$query->addField('cnf', 'global_settings');
// Minimise changes to the d6_field_option_translation.yml, which is copied
// from d6_field.yml, by ensuring the 'type' property is from
// content_node_field table.
$query->addField('cnf', 'type');
$query->addField('i18n', 'type', 'i18n_type');
return $query;
}
/**
* {@inheritdoc}
*/
public function fields() {
$fields = [
'property' => $this->t('Option ID.'),
'objectid' => $this->t('Object ID'),
'objectindex' => $this->t('Integer value of Object ID'),
'format' => $this->t('The input format used by this string'),
'lid' => $this->t('Source string ID'),
'language' => $this->t('Language code'),
'translation' => $this->t('Translation of the option'),
'plid' => $this->t('Parent lid'),
'plural' => $this->t('Plural index number in case of plural strings'),
];
return parent::fields() + $fields;
}
/**
* {@inheritdoc}
*/
/**
* {@inheritdoc}
*/
public function getIds() {
return parent::getIds() +
[
'language' => ['type' => 'string'],
'property' => ['type' => 'string'],
];
}
}
@@ -2,63 +2,18 @@
namespace Drupal\field\Tests\EntityReference;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait as nonDeprecatedEntityReferenceTestTrait;
/**
* Provides common functionality for the EntityReference test classes.
*
* @deprecated in Drupal 8.6.2 for removal before 9.0.0. Use
* Drupal\Tests\field\Traits\EntityReferenceTestTrait instead.
*
* @see https://www.drupal.org/node/2998888
*/
trait EntityReferenceTestTrait {
/**
* Creates a field of an entity reference field storage on the specified bundle.
*
* @param string $entity_type
* The type of entity the field will be attached to.
* @param string $bundle
* The bundle name of the entity the field will be attached to.
* @param string $field_name
* The name of the field; if it already exists, a new instance of the existing
* field will be created.
* @param string $field_label
* The label of the field.
* @param string $target_entity_type
* The type of the referenced entity.
* @param string $selection_handler
* The selection handler used by this field.
* @param array $selection_handler_settings
* An array of settings supported by the selection handler specified above.
* (e.g. 'target_bundles', 'sort', 'auto_create', etc).
* @param int $cardinality
* The cardinality of the field.
*
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase::buildConfigurationForm()
*/
protected function createEntityReferenceField($entity_type, $bundle, $field_name, $field_label, $target_entity_type, $selection_handler = 'default', $selection_handler_settings = [], $cardinality = 1) {
// Look for or add the specified field to the requested entity bundle.
if (!FieldStorageConfig::loadByName($entity_type, $field_name)) {
FieldStorageConfig::create([
'field_name' => $field_name,
'type' => 'entity_reference',
'entity_type' => $entity_type,
'cardinality' => $cardinality,
'settings' => [
'target_type' => $target_entity_type,
],
])->save();
}
if (!FieldConfig::loadByName($entity_type, $bundle, $field_name)) {
FieldConfig::create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'label' => $field_label,
'settings' => [
'handler' => $selection_handler,
'handler_settings' => $selection_handler_settings,
],
])->save();
}
}
use nonDeprecatedEntityReferenceTestTrait;
}
@@ -5,7 +5,7 @@ namespace Drupal\Tests\field\Functional\EntityReference;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\Tests\BrowserTestBase;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\node\Entity\Node;
use Drupal\field\Entity\FieldStorageConfig;
@@ -6,8 +6,8 @@ use Drupal\Component\Render\FormattableMarkup;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\Tests\BrowserTestBase;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\Tests\config\Traits\AssertConfigEntityImportTrait;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
/**
* Tests various Entity reference UI components.
@@ -5,7 +5,7 @@ namespace Drupal\Tests\field\Functional\EntityReference;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Tests\BrowserTestBase;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
/**
* Tests possible XSS security issues in entity references.
@@ -31,7 +31,7 @@ class DisplayApiTest extends FieldKernelTestBase {
/**
* The field cardinality to use in this test.
*
* @var number
* @var int
*/
protected $cardinality;
@@ -8,12 +8,12 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\filter\Entity\FilterFormat;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
use Drupal\entity_test\Entity\EntityTestLabel;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
/**
* Tests the formatters functionality.
@@ -14,7 +14,6 @@ use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestStringId;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\node\Entity\NodeType;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\TermInterface;
@@ -24,6 +23,7 @@ use Drupal\node\Entity\Node;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\user\Entity\User;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
/**
* Tests the new entity API for the entity reference field type.
@@ -5,10 +5,10 @@ namespace Drupal\Tests\field\Kernel\EntityReference;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\node\Entity\NodeType;
use Drupal\KernelTests\KernelTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
use Symfony\Component\Debug\BufferingLogger;
/**
@@ -4,9 +4,9 @@ namespace Drupal\Tests\field\Kernel\EntityReference\Views;
use Drupal\entity_test\Entity\EntityTestMulChanged;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\views\Views;
@@ -331,6 +331,15 @@ class EntityReferenceRelationshipTest extends ViewsKernelTestBase {
// Fourth result has no reference from EntityTestMul hence the output for
// should be empty.
$this->assertEqual('', $view->getStyle()->getField(3, 'name_2'));
$fields = $view->field;
// Check getValue for reference with a value. The first 3 rows reference
// EntityTestMul, so have value 'name1'.
$this->assertEquals('name1', $fields['name_2']->getValue($view->result[0]));
$this->assertEquals('name1', $fields['name_2']->getValue($view->result[1]));
$this->assertEquals('name1', $fields['name_2']->getValue($view->result[2]));
// Ensure getValue works on empty references.
$this->assertNull($fields['name_2']->getValue($view->result[3]));
}
}
@@ -0,0 +1,134 @@
<?php
namespace Drupal\Tests\field\Kernel\Migrate\d6;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Core\Database\Database;
use Drupal\Tests\migrate\Kernel\MigrateDumpAlterInterface;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Tests migration field label and description i18n translations.
*
* @group migrate_drupal_6
* @group legacy
*/
class MigrateFieldInstanceLabelDescriptionTest extends MigrateDrupal6TestBase implements MigrateDumpAlterInterface {
/**
* {@inheritdoc}
*/
public static $modules = [
'config_translation',
'locale',
'language',
'menu_ui',
'node',
'field',
];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->migrateFields();
$this->installEntitySchema('node');
$this->installConfig(['node']);
$this->installSchema('node', ['node_access']);
$this->installSchema('system', ['sequences']);
$this->executeMigration('language');
$this->executeMigration('d6_field_instance_label_description_translation');
}
/**
* {@inheritdoc}
*/
public static function migrateDumpAlter(KernelTestBase $test) {
$db = Database::getConnection('default', 'migrate');
// Alter the database to test the migration is successful when a translated
// field is deleted but the translation data for that field remains in both
// the i18n_strings and locales_target tables.
$db->delete('content_node_field_instance')
->condition('field_name', 'field_test')
->condition('type_name', 'story')
->execute();
}
/**
* Tests migration of file variables to file.settings.yml.
*/
public function testFieldInstanceLabelDescriptionTranslationMigration() {
$language_manager = $this->container->get('language_manager');
// Tests fields on 'story' node type.
// Check that the deleted field with translations was skipped.
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test');
$this->assertNull($config_translation->get('label'));
$this->assertNull($config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_two');
$this->assertSame("fr - Integer Field", $config_translation->get('label'));
$this->assertSame("fr - An example integer field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_four');
$this->assertSame("fr - Float Field", $config_translation->get('label'));
$this->assertSame("fr - An example float field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_email');
$this->assertSame("fr - Email Field", $config_translation->get('label'));
$this->assertSame("fr - An example email field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_imagefield');
$this->assertSame("fr - Image Field", $config_translation->get('label'));
$this->assertSame("fr - An example image field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('zu', 'field.field.node.story.field_test_imagefield');
$this->assertSame("zu - Image Field", $config_translation->get('label'));
$this->assertSame("zu - An example image field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_filefield');
$this->assertSame("fr - File Field", $config_translation->get('label'));
$this->assertSame("fr - An example file field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_link');
$this->assertSame("fr - Link Field", $config_translation->get('label'));
$this->assertSame("fr - An example link field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_date');
$this->assertSame("fr - Date Field", $config_translation->get('label'));
$this->assertSame("fr - An example date field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_datetime');
$this->assertSame("fr - Datetime Field", $config_translation->get('label'));
$this->assertSame("fr - An example datetime field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_datestamp');
$this->assertSame("fr - Date Stamp Field", $config_translation->get('label'));
$this->assertSame("fr - An example date stamp field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_date');
$this->assertSame("fr - Date Field", $config_translation->get('label'));
$this->assertSame("fr - An example date field.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_phone');
$this->assertSame("fr - Phone Field", $config_translation->get('label'));
$this->assertSame("fr - An example phone field.", $config_translation->get('description'));
// Tests fields on 'test_page' node type.
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.test_page.field_test');
$this->assertSame("Champ de texte", $config_translation->get('label'));
$this->assertSame("fr - An example text field.", $config_translation->get('description'));
// Tests fields on 'test_planet' node type.
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.test_planet.field_multivalue');
$this->assertSame("fr - Decimal Field", $config_translation->get('label'));
$this->assertSame("Un exemple plusieurs valeurs champ décimal.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.test_planet.field_test_text_single_checkbox');
$this->assertNull($config_translation->get('label'));
$this->assertSame('fr - An example text field using a single on/off checkbox.', $config_translation->get('description'));
}
}
@@ -0,0 +1,75 @@
<?php
namespace Drupal\Tests\field\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Migrate field instance option translations.
*
* @group migrate_drupal_6
*/
class MigrateFieldInstanceOptionTranslationTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules =
[
'config_translation',
'language',
'locale',
'menu_ui',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['node']);
$this->executeMigrations([
'language',
'd6_node_type',
'd6_field',
'd6_field_instance',
'd6_field_option_translation',
'd6_field_instance_option_translation',
]);
}
/**
* Tests migration of file variables to file.settings.yml.
*/
public function testFieldInstanceOptionTranslation() {
$language_manager = $this->container->get('language_manager');
/** @var \Drupal\language\Config\LanguageConfigOverride $config_translation */
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_float_single_checkbox');
$option_translation = ['on_label' => 'fr - 1.234'];
$this->assertSame($option_translation, $config_translation->get('settings'));
$config_translation = $language_manager->getLanguageConfigOverride('zu', 'field.field.node.story.field_test_float_single_checkbox');
$option_translation = ['on_label' => 'zu - 1.234'];
$this->assertSame($option_translation, $config_translation->get('settings'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_text_single_checkbox');
$option_translation = [
'off_label' => 'fr - Hello',
'on_label' => 'fr - Goodbye',
];
$this->assertSame($option_translation, $config_translation->get('settings'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.node.story.field_test_text_single_checkbox2');
$option_translation = [
'off_label' => 'fr - Off',
'on_label' => 'fr - Hello',
];
$this->assertSame($option_translation, $config_translation->get('settings'));
$config_translation = $language_manager->getLanguageConfigOverride('zu', 'field.field.node.story.field_test_text_single_checkbox2');
$option_translation = ['on_label' => 'zu - Hello'];
$this->assertSame($option_translation, $config_translation->get('settings'));
}
}
@@ -0,0 +1,84 @@
<?php
namespace Drupal\Tests\field\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Migrate field option translations.
*
* @group migrate_drupal_6
*/
class MigrateFieldOptionTranslationTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'config_translation',
'language',
'locale',
'menu_ui',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigrations([
'language',
'd6_field',
'd6_field_option_translation',
]);
}
/**
* Tests the Drupal 6 field to Drupal 8 migration.
*/
public function testFieldOptionTranslation() {
$language_manager = $this->container->get('language_manager');
// Test a select list with allowed values of key only.
/** @var \Drupal\language\Config\LanguageConfigOverride $config_translation */
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.storage.node.field_test_integer_selectlist');
$allowed_values = [
1 => [
'label' => 'fr - 2341',
],
3 => [
'label' => 'fr - 4123',
],
];
$this->assertSame($allowed_values, $config_translation->get('settings.allowed_values'));
$config_translation = $language_manager->getLanguageConfigOverride('zu', 'field.storage.node.field_test_integer_selectlist');
$allowed_values = [
1 => [
'label' => 'zu - 2341',
],
];
$this->assertSame($allowed_values, $config_translation->get('settings.allowed_values'));
// Test a select list with allowed values of key|label.
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.storage.node.field_test_string_selectlist');
$allowed_values = [
0 => [
'label' => 'Noir',
],
];
$this->assertSame($allowed_values, $config_translation->get('settings.allowed_values'));
$config_translation = $language_manager->getLanguageConfigOverride('zu', 'field.storage.node.field_test_string_selectlist');
$allowed_values = [
0 => [
'label' => 'Okumnyama',
],
1 => [
'label' => 'Mhlophe',
],
];
$this->assertSame($allowed_values, $config_translation->get('settings.allowed_values'));
}
}
@@ -0,0 +1,79 @@
<?php
namespace Drupal\Tests\field\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests the field label and description translation source plugin.
*
* @covers \Drupal\field\Plugin\migrate\source\d6\FieldLabelDescriptionTranslation
* @group migrate_drupal
*/
class FieldInstanceLabelDescriptionTranslationTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['config_translation', 'migrate_drupal', 'field'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$test = [];
// The source data.
$test[0]['source_data'] = [
'i18n_strings' => [
[
'lid' => 10,
'objectid' => 'story-field_test_two',
'type' => 'field',
'property' => 'widget_label',
],
[
'lid' => 11,
'objectid' => 'story-field_test_two',
'type' => 'field',
'property' => 'widget_description',
],
[
'lid' => 12,
'objectid' => 'story-field_test_two',
'type' => 'field',
'property' => 'widget_description',
],
],
'locales_target' => [
[
'lid' => 10,
'translation' => "fr - Integer Field",
'language' => 'fr',
],
[
'lid' => 11,
'translation' => 'fr - An example integer field.',
'language' => 'fr',
],
],
];
$test[0]['expected_results'] = [
[
'property' => 'widget_label',
'translation' => "fr - Integer Field",
'language' => 'fr',
'lid' => '10',
],
[
'property' => 'widget_description',
'translation' => 'fr - An example integer field.',
'language' => 'fr',
'lid' => '11',
],
];
return $test;
}
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\field\Kernel\Plugin\migrate\source\d6;
/**
* Tests the field instance option translation source plugin.
*
* @covers \Drupal\field\Plugin\migrate\source\d6\FieldInstanceOptionTranslation
* @group migrate_drupal
*/
class FieldInstanceOptionTranslationTest extends FieldOptionTranslationTest {
/**
* {@inheritdoc}
*/
public static $modules = ['field', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$test = parent::providerSource();
// FieldInstanceOptionTranslation extends FieldOptionTranslation so the
// same test can be used with the addition of the 'type' field to the
// output.
$test[0]['expected_results'][0]['type'] = 'text';
$test[0]['expected_results'][1]['type'] = 'text';
$test[0]['expected_results'][2]['type'] = 'number_integer';
$test[0]['expected_results'][3]['type'] = 'number_integer';
return $test;
}
}
@@ -0,0 +1,242 @@
<?php
namespace Drupal\Tests\field\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests the field option translation source plugin.
*
* @covers \Drupal\field\Plugin\migrate\source\d6\FieldOptionTranslation
* @group migrate_drupal
*/
class FieldOptionTranslationTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['field', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$test = [];
// The source data.
$test[0]['source_data']['content_node_field'] = [
[
'field_name' => 'field_test_text_single_checkbox',
'type' => 'text',
'global_settings' => 'a:4:{s:15:"text_processing";s:1:"0";s:10:"max_length";s:0:"";s:14:"allowed_values";s:10:"Off\\nHello";s:18:"allowed_values_php";s:0:"";}',
'required' => 0,
'multiple' => 0,
'db_storage' => 1,
'module' => 'text',
],
[
'field_name' => 'field_test_integer_selectlist',
'type' => 'number_integer',
'global_settings' => 'a:6:{s:6:"prefix";s:0:"";s:6:"suffix";s:0:"";s:3:"min";s:0:"";s:3:"max";s:0:"";s:14:"allowed_values";s:22:"1234\\n2341\\n3412\\n4123";s:18:"allowed_values_php";s:0:"";}',
'required' => 0,
'multiple' => 0,
'db_storage' => 1,
'module' => 'text',
],
];
$test[0]['source_data']['content_node_field_instance'] = [
[
'field_name' => 'field_test_text_single_checkbox',
'type_name' => 'story',
'weight' => 1,
'label' => 'Text Single Checkbox Field',
'widget_type' => 'optionwidgets_onoff',
'description' => 'An example text field using a single on/off checkbox.',
'widget_module' => 'optionwidgets',
'widget_active' => 1,
'required' => 1,
'active' => 1,
'global_settings' => 'a:0;',
'widget_settings' => 'a:0;',
'display_settings' => 'a:0;',
],
[
'field_name' => 'field_test_integer_selectlist',
'type_name' => 'story',
'weight' => 1,
'label' => 'Integer Select List Field',
'widget_type' => 'optionwidgets_select',
'description' => 'An example integer field using a select list.',
'widget_module' => 'optionwidgets',
'widget_active' => 1,
'required' => 1,
'active' => 1,
'global_settings' => 'a:0;',
'widget_settings' => 'a:0;',
'display_settings' => 'a:0;',
],
];
$test[0]['source_data']['i18n_strings'] = [
[
'lid' => 10,
'objectid' => 'field_test_text_single_checkbox',
'type' => 'field',
'property' => 'option_0',
'objectindex' => 0,
'format' => 0,
],
[
'lid' => 11,
'objectid' => 'field_test_text_single_checkbox',
'type' => 'field',
'property' => 'option_1',
'objectindex' => 0,
'format' => 0,
],
[
'lid' => 20,
'objectid' => 'field_test_integer_selectlist',
'type' => 'field',
'property' => 'option_1234',
'objectindex' => 0,
'format' => 0,
],
[
'lid' => 21,
'objectid' => 'field_test_integer_selectlist',
'type' => 'field',
'property' => 'option_4123',
'objectindex' => 0,
'format' => 0,
],
];
$test[0]['source_data']['locales_target'] = [
[
'lid' => 10,
'translation' => "fr - Hello",
'language' => 'fr',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'lid' => 11,
'translation' => 'fr - Goodbye',
'language' => 'fr',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'lid' => 20,
'translation' => "fr - 4444",
'language' => 'fr',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'lid' => 21,
'translation' => 'fr - 5555',
'language' => 'fr',
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
];
$test[0]['expected_results'] = [
[
'field_name' => 'field_test_text_single_checkbox',
'type' => 'text',
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => 'Off\nHello',
'allowed_values_php' => '',
'max_length' => '',
'text_processing' => '0',
],
'db_columns' => '',
'property' => 'option_0',
'objectid' => 'field_test_text_single_checkbox',
'language' => 'fr',
'translation' => 'fr - Hello',
'objectindex' => 0,
'format' => 0,
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'field_name' => 'field_test_text_single_checkbox',
'type' => 'text',
'widget_type' => 'optionwidgets_onoff',
'global_settings' => [
'allowed_values' => 'Off\nHello',
'allowed_values_php' => '',
'max_length' => '',
'text_processing' => '0',
],
'db_columns' => '',
'property' => 'option_1',
'objectid' => 'field_test_text_single_checkbox',
'language' => 'fr',
'translation' => 'fr - Goodbye',
'objectindex' => 0,
'format' => 0,
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'field_name' => 'field_test_integer_selectlist',
'type' => 'number_integer',
'widget_type' => 'optionwidgets_select',
'global_settings' => [
'allowed_values' => '1234\n2341\n3412\n4123',
'max' => '',
'min' => '',
'prefix' => '',
'suffix' => '',
'allowed_values_php' => '',
],
'db_columns' => '',
'property' => 'option_1234',
'objectid' => 'field_test_integer_selectlist',
'language' => 'fr',
'translation' => 'fr - 4444',
'objectindex' => 0,
'format' => 0,
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
[
'field_name' => 'field_test_integer_selectlist',
'type' => 'number_integer',
'widget_type' => 'optionwidgets_select',
'global_settings' => [
'allowed_values' => '1234\n2341\n3412\n4123',
'max' => '',
'min' => '',
'prefix' => '',
'suffix' => '',
'allowed_values_php' => '',
],
'db_columns' => '',
'property' => 'option_4123',
'objectid' => 'field_test_integer_selectlist',
'language' => 'fr',
'translation' => 'fr - 5555',
'objectindex' => 0,
'format' => 0,
'plid' => 0,
'plural' => 0,
'i18n_status' => 0,
],
];
return $test;
}
}
@@ -0,0 +1,64 @@
<?php
namespace Drupal\Tests\field\Traits;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Provides common functionality for the EntityReference test classes.
*/
trait EntityReferenceTestTrait {
/**
* Creates a field of an entity reference field storage on the specified bundle.
*
* @param string $entity_type
* The type of entity the field will be attached to.
* @param string $bundle
* The bundle name of the entity the field will be attached to.
* @param string $field_name
* The name of the field; if it already exists, a new instance of the existing
* field will be created.
* @param string $field_label
* The label of the field.
* @param string $target_entity_type
* The type of the referenced entity.
* @param string $selection_handler
* The selection handler used by this field.
* @param array $selection_handler_settings
* An array of settings supported by the selection handler specified above.
* (e.g. 'target_bundles', 'sort', 'auto_create', etc).
* @param int $cardinality
* The cardinality of the field.
*
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase::buildConfigurationForm()
*/
protected function createEntityReferenceField($entity_type, $bundle, $field_name, $field_label, $target_entity_type, $selection_handler = 'default', $selection_handler_settings = [], $cardinality = 1) {
// Look for or add the specified field to the requested entity bundle.
if (!FieldStorageConfig::loadByName($entity_type, $field_name)) {
FieldStorageConfig::create([
'field_name' => $field_name,
'type' => 'entity_reference',
'entity_type' => $entity_type,
'cardinality' => $cardinality,
'settings' => [
'target_type' => $target_entity_type,
],
])->save();
}
if (!FieldConfig::loadByName($entity_type, $bundle, $field_name)) {
FieldConfig::create([
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'label' => $field_label,
'settings' => [
'handler' => $selection_handler,
'handler_settings' => $selection_handler_settings,
],
])->save();
}
}
}
@@ -6,6 +6,7 @@
*/
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\Display\EntityDisplayInterface;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\field_layout\Display\EntityDisplayWithLayoutInterface;
@@ -15,8 +16,10 @@ use Drupal\field_layout\Display\EntityDisplayWithLayoutInterface;
*/
function field_layout_install() {
// Ensure each entity display has a layout.
$entity_save = function (EntityDisplayWithLayoutInterface $entity) {
$entity->ensureLayout()->save();
$entity_save = function (EntityDisplayInterface $entity) {
if ($entity instanceof EntityDisplayWithLayoutInterface) {
$entity->ensureLayout()->save();
}
};
array_map($entity_save, EntityViewDisplay::loadMultiple());
array_map($entity_save, EntityFormDisplay::loadMultiple());
@@ -31,8 +34,10 @@ function field_layout_install() {
function field_layout_uninstall() {
// Reset each entity display to use the one-column layout to best approximate
// the absence of layouts.
$entity_save = function (EntityDisplayWithLayoutInterface $entity) {
$entity->setLayoutId('layout_onecol')->save();
$entity_save = function (EntityDisplayInterface $entity) {
if ($entity instanceof EntityDisplayWithLayoutInterface) {
$entity->setLayoutId('layout_onecol')->save();
}
};
array_map($entity_save, EntityViewDisplay::loadMultiple());
array_map($entity_save, EntityFormDisplay::loadMultiple());

Some files were not shown because too many files have changed in this diff Show More