Update to Drupal 8.2.4. For more information, see https://www.drupal.org/project/drupal/releases/8.2.4

This commit is contained in:
Pantheon Automation 2016-12-07 12:19:38 -08:00 committed by Greg Anderson
parent 0a95b8440e
commit 8544b60b39
284 changed files with 12980 additions and 3199 deletions

View file

@ -3,6 +3,9 @@ encouraged to submit issues and changes (patches) to improve Drupal, and to
contribute in other ways -- see https://www.drupal.org/contribute to find out contribute in other ways -- see https://www.drupal.org/contribute to find out
how. how.
This file lists the active maintainers. For a list of past maintainers, see:
https://www.drupal.org/core/maintainers/past
Core committers Core committers
--------------- ---------------
@ -53,11 +56,10 @@ Actions
- ? - ?
Aggregator Aggregator
- Paris Liakos 'ParisLiakos' https://www.drupal.org/u/parisliakos - ?
Ajax Ajax
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia - Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Earl Miles 'merlinofchaos' https://www.drupal.org/u/merlinofchaos
- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett - Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
Asset Library API Asset Library API
@ -73,19 +75,17 @@ Ban
- ? - ?
Bartik Bartik
- Jen Simmons 'jensimmons' https://www.drupal.org/u/jensimmons
- Emma Maria Karayiannis 'emma.maria' https://www.drupal.org/u/emma.maria - Emma Maria Karayiannis 'emma.maria' https://www.drupal.org/u/emma.maria
Base system Base system
- Damien Tournoud 'damien-tournoud' https://www.drupal.org/u/damien-tournoud - ?
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
Basic Auth Basic Auth
- Klaus Purer 'klausi' https://www.drupal.org/u/klausi - Klaus Purer 'klausi' https://www.drupal.org/u/klausi
- Juampy Novillo Requena 'juampy' https://www.drupal.org/u/juampy - Juampy Novillo Requena 'juampy' https://www.drupal.org/u/juampy
Batch API Batch API
- Yves Chedemois 'yched' https://www.drupal.org/u/yched - ?
BigPipe BigPipe
- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers - Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
@ -109,9 +109,7 @@ Breakpoint
- Marc Drummond 'mdrummond' https://www.drupal.org/u/mdrummond - Marc Drummond 'mdrummond' https://www.drupal.org/u/mdrummond
Cache Cache
- Damien Tournoud 'damien-tournoud' https://www.drupal.org/u/damien-tournoud
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch - Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
- Mark Sonnabaum 'msonnabaum' https://www.drupal.org/u/msonnabaum
CKEditor CKEditor
- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers - Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
@ -130,13 +128,11 @@ Comment
- Andrey Postnikov 'andypost' https://www.drupal.org/u/andypost - Andrey Postnikov 'andypost' https://www.drupal.org/u/andypost
Configuration API Configuration API
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
- Alex Pott 'alexpott' https://www.drupal.org/u/alexpott - Alex Pott 'alexpott' https://www.drupal.org/u/alexpott
- Matthew Tift 'mtift' https://www.drupal.org/u/mtift - Matthew Tift 'mtift' https://www.drupal.org/u/mtift
Configuration Entity API Configuration Entity API
- Alex Pott 'alexpott' https://www.drupal.org/u/alexpott - Alex Pott 'alexpott' https://www.drupal.org/u/alexpott
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett - Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
Configuration UI Configuration UI
@ -159,10 +155,10 @@ Content Translation
- Francesco Placella 'plach' https://www.drupal.org/u/plach - Francesco Placella 'plach' https://www.drupal.org/u/plach
Contextual Contextual
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun - ?
Cron Cron
- Derek Wright 'dww' https://www.drupal.org/u/dww - ?
CSS CSS
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin - John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
@ -175,14 +171,13 @@ Database API
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss - David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
PostgreSQL DB driver PostgreSQL DB driver
- Damien Tournoud 'damien-tournoud' https://www.drupal.org/u/damien-tournoud - ?
- Josh Waihi 'fiasco' https://www.drupal.org/u/fiasco
Sqlite DB driver Sqlite DB driver
- Damien Tournoud 'damien-tournoud' https://www.drupal.org/u/damien-tournoud - ?
Database Logging Database Logging
- Khalid Baheyeldin 'kbahey' https://www.drupal.org/u/kbahey - ?
Database Update API Database Update API
- ? - ?
@ -203,7 +198,6 @@ Editor
- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers - Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
Entity API Entity API
- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch - Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
- Sascha Grossenbacher 'Berdir' https://www.drupal.org/u/berdir - Sascha Grossenbacher 'Berdir' https://www.drupal.org/u/berdir
- Francesco Placella 'plach' https://www.drupal.org/u/plach - Francesco Placella 'plach' https://www.drupal.org/u/plach
@ -213,7 +207,6 @@ Extension API
- ? - ?
Field API Field API
- Yves Chedemois 'yched' https://www.drupal.org/u/yched
- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu - Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
Field UI Field UI
@ -221,19 +214,16 @@ Field UI
- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu - Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
File File
- Andrew Morton 'drewish' https://www.drupal.org/u/drewish - ?
- Aaron Winborn 'aaron' https://www.drupal.org/u/aaron
Filter Filter
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun - ?
Forum Forum
- Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan - Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
Form API Form API
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia - Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett - Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
History History
@ -266,23 +256,25 @@ Link Field
- Weber Macedo 'Mac_Weber' https://www.drupal.org/u/mac_weber - Weber Macedo 'Mac_Weber' https://www.drupal.org/u/mac_weber
Lock Lock
- Damien Tournoud 'damien-tournoud' https://www.drupal.org/u/damien-tournoud - ?
Mail Mail
- ? - ?
Markup Markup
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun - ?
Migrate Migrate
- Adam Globus-Hoenich 'phenaproxima' https://www.drupal.org/u/phenaproxima - Adam Globus-Hoenich 'phenaproxima' https://www.drupal.org/u/phenaproxima
- Ben Dougherty 'benjy' https://www.drupal.org/u/benjy - Ben Dougherty 'benjy' https://www.drupal.org/u/benjy
- Lucas Hedding 'heddn' https://www.drupal.org/u/heddn
- Michael Anello 'ultimike' https://www.drupal.org/u/ultimike - Michael Anello 'ultimike' https://www.drupal.org/u/ultimike
- Mike Ryan 'mikeryan' https://www.drupal.org/u/mikeryan - Mike Ryan 'mikeryan' https://www.drupal.org/u/mikeryan
- Vicki Spagnolo 'quietone' https://www.drupal.org/u/quietone - Vicki Spagnolo 'quietone' https://www.drupal.org/u/quietone
Migrate (Drupal) Migrate (Drupal)
- Ben Dougherty 'benjy' https://www.drupal.org/u/benjy - Ben Dougherty 'benjy' https://www.drupal.org/u/benjy
- Lucas Hedding 'heddn' https://www.drupal.org/u/heddn
- Michael Anello 'ultimike' https://www.drupal.org/u/ultimike - Michael Anello 'ultimike' https://www.drupal.org/u/ultimike
- Mike Ryan 'mikeryan' https://www.drupal.org/u/mikeryan - Mike Ryan 'mikeryan' https://www.drupal.org/u/mikeryan
- Vicki Spagnolo 'quietone' https://www.drupal.org/u/quietone - Vicki Spagnolo 'quietone' https://www.drupal.org/u/quietone
@ -299,16 +291,15 @@ Menu UI
- ? - ?
Node Node
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman - ?
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
Node Access Node Access
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- Ken Rickard 'agentrickard' https://www.drupal.org/u/agentrickard - Ken Rickard 'agentrickard' https://www.drupal.org/u/agentrickard
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm - Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
Options Options
- Yves Chedemois 'yched' https://www.drupal.org/u/yched - ?
Outside In Outside In
- Ted Bowman 'tedbow' https://www.drupal.org/u/tedbow - Ted Bowman 'tedbow' https://www.drupal.org/u/tedbow
@ -327,7 +318,6 @@ Plugin
Queue Queue
- James Gilliland 'neclimdul' https://www.drupal.org/u/neclimdul - James Gilliland 'neclimdul' https://www.drupal.org/u/neclimdul
- Mark Sonnabaum 'msonnabaum' https://www.drupal.org/u/msonnabaum
Quick Edit Quick Edit
- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers - Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
@ -337,8 +327,8 @@ RDF
- Stéphane Corlosquet 'scor' https://www.drupal.org/u/scor - Stéphane Corlosquet 'scor' https://www.drupal.org/u/scor
Render API Render API
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia - Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
Request Processing Request Processing
- Larry Garfield 'Crell' https://www.drupal.org/u/crell - Larry Garfield 'Crell' https://www.drupal.org/u/crell
@ -388,10 +378,9 @@ System (module)
Taxonomy Taxonomy
- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm - Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch - Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
- Benjamin Doherty 'bangpound' https://www.drupal.org/u/bangpound
Telephone Telephone
- Dave Reid 'dave-reid' https://www.drupal.org/u/dave-reid - ?
Testing framework Testing framework
- Alex Pott 'alexpott' https://www.drupal.org/u/alexpott - Alex Pott 'alexpott' https://www.drupal.org/u/alexpott
@ -409,7 +398,7 @@ Theme API
- Lauri Eskola 'lauriii' https://www.drupal.org/u/lauriii - Lauri Eskola 'lauriii' https://www.drupal.org/u/lauriii
Token Token
- Dave Reid 'davereid' https://www.drupal.org/u/davereid - ?
Toolbar Toolbar
- Théodore Biadala 'nod_' https://www.drupal.org/u/nod_ - Théodore Biadala 'nod_' https://www.drupal.org/u/nod_
@ -418,22 +407,19 @@ Tour
- Nick Schuch 'nick_schuch' https://www.drupal.org/u/nick_schuch - Nick Schuch 'nick_schuch' https://www.drupal.org/u/nick_schuch
Tracker Tracker
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss - ?
Transliteration Transliteration
- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu - Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
- Damien Tournoud 'damien-tournoud' https://www.drupal.org/u/damien-tournoud
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Typed Data Typed Data
- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago - Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
Update UI Update UI
- Derek Wright 'dww' https://www.drupal.org/u/dww - ?
User User
- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman - Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
Views Views
- Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner - Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner
@ -442,18 +428,11 @@ Views
- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm - Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
- Len Swaneveld 'Lendude' https://www.drupal.org/u/lendude - Len Swaneveld 'Lendude' https://www.drupal.org/u/lendude
Views UI
- Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner
- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
- Damian Lee 'damiankloip' https://www.drupal.org/u/damiankloip
- Len Swaneveld 'Lendude' https://www.drupal.org/u/lendude
Topic maintainers Topic maintainers
----------------- -----------------
Accessibility Accessibility
- Mike Gifford 'mgifford' https://www.drupal.org/u/mgifford - Mike Gifford 'mgifford' https://www.drupal.org/u/mgifford
- Jesse Renée Beach 'jessebeach' https://www.drupal.org/u/jessebeach
- Andrew Macpherson 'andrewmacpherson' https://www.drupal.org/u/andrewmacpherson - Andrew Macpherson 'andrewmacpherson' https://www.drupal.org/u/andrewmacpherson
Documentation Documentation
@ -492,24 +471,16 @@ re-architect or otherwise improve large areas of Drupal core. See
https://www.drupal.org/community-initiatives/drupal-core for more information on https://www.drupal.org/community-initiatives/drupal-core for more information on
their responsibilities. The initiative coordinators for Drupal 8 are: their responsibilities. The initiative coordinators for Drupal 8 are:
Configuration management
- Greg Dunlap 'heyrocker' https://www.drupal.org/u/heyrocker
Design
- Jeff Burns 'Jeff Burnz' https://www.drupal.org/u/jeff-burnz
Mobile
- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Multi-lingual Multi-lingual
- Gábor Hojtsy 'Gábor Hojtsy' https://www.drupal.org/u/gábor-hojtsy - Gábor Hojtsy 'Gábor Hojtsy' https://www.drupal.org/u/gábor-hojtsy
Web services
- Larry Garfield 'Crell' https://www.drupal.org/u/crell
Workflow Initiative Workflow Initiative
- Dick Olsson 'dixon_' https://www.drupal.org/u/dixon_ - Dick Olsson 'dixon_' https://www.drupal.org/u/dixon_
PHPUnit Initiative
- Klaus Purer 'klausi' https://www.drupal.org/u/klausi
- Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner
Provisional membership: None at this time. Provisional membership: None at this time.

View file

@ -31,7 +31,7 @@
"symfony/psr-http-message-bridge": "v0.2", "symfony/psr-http-message-bridge": "v0.2",
"zendframework/zend-diactoros": "~1.1", "zendframework/zend-diactoros": "~1.1",
"composer/semver": "~1.0", "composer/semver": "~1.0",
"paragonie/random_compat": "~1.0", "paragonie/random_compat": "^1|^2",
"asm89/stack-cors": "~1.0" "asm89/stack-cors": "~1.0"
}, },
"require-dev": { "require-dev": {

View file

@ -1073,8 +1073,9 @@
* yourmodule/tests/src/Unit directory, according to the PSR-4 standard. * yourmodule/tests/src/Unit directory, according to the PSR-4 standard.
* - Your test class needs a phpDoc comment block with a description and * - Your test class needs a phpDoc comment block with a description and
* a @group annotation, which gives information about the test. * a @group annotation, which gives information about the test.
* - Methods in your test class whose names start with 'test' are the actual * - Add test cases by adding method names that start with 'test' and have no
* test cases. Each one should test a logical subset of the functionality. * arguments, for example testYourTestCase(). Each one should test a logical
* subset of the functionality.
* For more details, see: * For more details, see:
* - https://www.drupal.org/phpunit for full documentation on how to write * - https://www.drupal.org/phpunit for full documentation on how to write
* PHPUnit tests for Drupal. * PHPUnit tests for Drupal.
@ -1110,9 +1111,9 @@
* set up content types and similar procedures. * set up content types and similar procedures.
* - In some cases, you may need to write a test module to support your test; * - In some cases, you may need to write a test module to support your test;
* put such modules under the yourmodule/tests/modules directory. * put such modules under the yourmodule/tests/modules directory.
* - Methods in your test class whose names start with 'test', and which have * - Add test cases by adding method names that start with 'test' and have no
* no arguments, are the actual test cases. Each one should test a logical * arguments, for example testYourTestCase(). Each one should test a logical
* subset of the functionality, and each one runs in a new, isolated test * subset of the functionality. Each method runs in a new, isolated test
* environment, so it can only rely on the setUp() method, not what has * environment, so it can only rely on the setUp() method, not what has
* been set up by other test methods. * been set up by other test methods.
* For more details, see: * For more details, see:
@ -1121,6 +1122,52 @@
* - @link oo_conventions Object-oriented programming topic @endlink for more * - @link oo_conventions Object-oriented programming topic @endlink for more
* on PSR-4, namespaces, and where to place classes. * on PSR-4, namespaces, and where to place classes.
* *
* @section write_functional_phpunit Write functional PHP tests (phpunit)
* Functional tests extend the BrowserTestBase base class, and use PHPUnit as
* their underlying framework. They use a simulated browser, in which the test
* can click links, visit URLs, post to forms, etc. To write a functional test:
* - Extend \Drupal\Tests\BrowserTestBase.
* - Place the test in the yourmodule/tests/src/Functional/ directory and use
* the \Drupal\Tests\yourmodule\Functional namespace.
* - Add a @group annotation. For example, if the test is for a Drupal 6
* migration process, the group core uses is migrate_drupal_6. Use yourmodule
* as the group name if the test does not belong to another larger group.
* - You may also override the default setUp() method, which can be used to set
* up content types and similar procedures. Don't forget to call the parent
* method.
* - In some cases, you may need to write a test module to support your test;
* put such modules under the yourmodule/tests/modules directory.
* - Add test cases by adding method names that start with 'test' and have no
* arguments, for example testYourTestCase(). Each one should test a logical
* subset of the functionality. Each method runs in a new, isolated test
* environment, so it can only rely on the setUp() method, not what has
* been set up by other test methods.
* For more details, see:
* - https://www.drupal.org/docs/8/phpunit/phpunit-browser-test-tutorial for
* a full tutorial on how to write functional PHPUnit tests for Drupal.
* - https://www.drupal.org/phpunit for the full documentation on how to write
* PHPUnit tests for Drupal.
*
* @section write_jsfunctional_phpunit Write functional JavaScript tests (phpunit)
* To write a functional test that relies on JavaScript:
* - Extend \Drupal\FunctionalJavaScriptTests\JavascriptTestBase.
* - Place the test into the yourmodule/tests/src/FunctionalJavascript/
* directory and use the \Drupal\Tests\yourmodule\FunctionalJavascript
* namespace.
* - Add a @group annotation. Use yourmodule as the group name if the test does
* not belong to another larger group.
* - Set up PhantomJS; see http://phantomjs.org/download.html.
* - To run tests, see core/tests/README.md.
* - When clicking a link/button with Ajax behavior attached, keep in mind that
* the underlying browser might take time to deliver changes to the HTML. Use
* $this->assertSession()->assertWaitOnAjaxRequest() to wait for the Ajax
* request to finish.
* For more details, see:
* - https://www.drupal.org/docs/8/phpunit/phpunit-javascript-testing-tutorial
* for a full tutorial on how to write PHPUnit JavaScript tests for Drupal.
* - https://www.drupal.org/phpunit for the full documentation on how to write
* PHPUnit tests for Drupal.
*
* @section running Running tests * @section running Running tests
* You can run both Simpletest and PHPUnit tests by enabling the core Testing * You can run both Simpletest and PHPUnit tests by enabling the core Testing
* module (core/modules/simpletest). Once that module is enabled, tests can be * module (core/modules/simpletest). Once that module is enabled, tests can be
@ -2493,7 +2540,7 @@ function hook_validation_constraint_alter(array &$definitions) {
* this class is subscribed to, and which methods on the class should be * this class is subscribed to, and which methods on the class should be
* called for each one. Example: * called for each one. Example:
* @code * @code
* public function getSubscribedEvents() { * public static function getSubscribedEvents() {
* // Subscribe to kernel terminate with priority 100. * // Subscribe to kernel terminate with priority 100.
* $events[KernelEvents::TERMINATE][] = array('onTerminate', 100); * $events[KernelEvents::TERMINATE][] = array('onTerminate', 100);
* // Subscribe to kernel request with default priority of 0. * // Subscribe to kernel request with default priority of 0.

View file

@ -539,12 +539,12 @@ function entity_get_display($entity_type, $bundle, $view_mode) {
* When the entity form display is not available in configuration, you can * When the entity form display is not available in configuration, you can
* create a new EntityFormDisplay object using: * create a new EntityFormDisplay object using:
* @code * @code
* $values = ('entity_form_display', array( * $values = array(
* 'targetEntityType' => $entity_type, * 'targetEntityType' => $entity_type,
* 'bundle' => $bundle, * 'bundle' => $bundle,
* 'mode' => $form_mode, * 'mode' => $form_mode,
* 'status' => TRUE, * 'status' => TRUE,
* )); * );
* \Drupal::entityTypeManager() * \Drupal::entityTypeManager()
* ->getStorage('entity_form_display') * ->getStorage('entity_form_display')
* ->create($values); * ->create($values);

View file

@ -81,7 +81,7 @@ class Drupal {
/** /**
* The current system version. * The current system version.
*/ */
const VERSION = '8.2.3'; const VERSION = '8.2.4';
/** /**
* Core API compatibility. * Core API compatibility.

View file

@ -36,6 +36,16 @@ class DiffFormatter {
*/ */
public $trailing_context_lines = 0; public $trailing_context_lines = 0;
/**
* The line stats.
*
* @var array
*/
protected $line_stats = array(
'counter' => array('x' => 0, 'y' => 0),
'offset' => array('x' => 0, 'y' => 0),
);
/** /**
* Format a diff. * Format a diff.
* *

View file

@ -54,7 +54,7 @@ class CsrfAccessCheck implements RoutingAccessInterface {
$path = str_replace("{{$param}}", $value, $path); $path = str_replace("{{$param}}", $value, $path);
} }
if ($this->csrfToken->validate($request->query->get('token'), $path)) { if ($this->csrfToken->validate($request->query->get('token', ''), $path)) {
$result = AccessResult::allowed(); $result = AccessResult::allowed();
} }
else { else {

View file

@ -87,7 +87,7 @@ class CsrfTokenGenerator {
return FALSE; return FALSE;
} }
return $token === $this->computeToken($seed, $value); return Crypt::hashEquals($this->computeToken($seed, $value), $token);
} }
/** /**

View file

@ -34,6 +34,11 @@ abstract class DraggableListBuilder extends ConfigEntityListBuilder implements F
*/ */
protected $weightKey = FALSE; protected $weightKey = FALSE;
/**
* {@inheritdoc}
*/
protected $limit = FALSE;
/** /**
* The form builder. * The form builder.
* *

View file

@ -19,16 +19,6 @@ class DiffFormatter extends DiffFormatterBase {
*/ */
protected $rows = array(); protected $rows = array();
/**
* The line stats.
*
* @var array
*/
protected $line_stats = array(
'counter' => array('x' => 0, 'y' => 0),
'offset' => array('x' => 0, 'y' => 0),
);
/** /**
* Creates a DiffFormatter to render diffs in a table. * Creates a DiffFormatter to render diffs in a table.
* *

View file

@ -348,13 +348,10 @@ class EntityAutocomplete extends Textfield {
public static function extractEntityIdFromAutocompleteInput($input) { public static function extractEntityIdFromAutocompleteInput($input) {
$match = NULL; $match = NULL;
// Take "label (entity id)', match the ID from parenthesis when it's a // Take "label (entity id)', match the ID from inside the parentheses.
// number. // @todo Add support for entities containing parentheses in their ID.
if (preg_match("/.+\s\((\d+)\)/", $input, $matches)) { // @see https://www.drupal.org/node/2520416
$match = $matches[1]; if (preg_match("/.+\s\(([^\)]+)\)/", $input, $matches)) {
}
// Match the ID when it's a string (e.g. for config entity types).
elseif (preg_match("/.+\s\(([\w.]+)\)/", $input, $matches)) {
$match = $matches[1]; $match = $matches[1];
} }

View file

@ -94,7 +94,7 @@ abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDispl
protected $plugins = array(); protected $plugins = array();
/** /**
* Context in which this entity will be used (e.g. 'display', 'form'). * Context in which this entity will be used (e.g. 'view', 'form').
* *
* @var string * @var string
*/ */

View file

@ -9,6 +9,7 @@ use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\Element; use Drupal\Core\Render\Element;
use Drupal\Core\Theme\Registry;
use Drupal\Core\TypedData\TranslatableInterface; use Drupal\Core\TypedData\TranslatableInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
@ -54,6 +55,13 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf
*/ */
protected $languageManager; protected $languageManager;
/**
* The theme registry.
*
* @var \Drupal\Core\Theme\Registry
*/
protected $themeRegistry;
/** /**
* The EntityViewDisplay objects created for individual field rendering. * The EntityViewDisplay objects created for individual field rendering.
* *
@ -72,12 +80,15 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf
* The entity manager service. * The entity manager service.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager. * The language manager.
* @param \Drupal\Core\Theme\Registry $theme_registry
* The theme registry.
*/ */
public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) { public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, Registry $theme_registry = NULL) {
$this->entityTypeId = $entity_type->id(); $this->entityTypeId = $entity_type->id();
$this->entityType = $entity_type; $this->entityType = $entity_type;
$this->entityManager = $entity_manager; $this->entityManager = $entity_manager;
$this->languageManager = $language_manager; $this->languageManager = $language_manager;
$this->themeRegistry = $theme_registry ?: \Drupal::service('theme.registry');
} }
/** /**
@ -87,7 +98,8 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf
return new static( return new static(
$entity_type, $entity_type,
$container->get('entity.manager'), $container->get('entity.manager'),
$container->get('language_manager') $container->get('language_manager'),
$container->get('theme.registry')
); );
} }
@ -148,7 +160,6 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf
$this->moduleHandler()->alter('entity_view_mode', $view_mode, $entity, $context); $this->moduleHandler()->alter('entity_view_mode', $view_mode, $entity, $context);
$build = array( $build = array(
'#theme' => $this->entityTypeId,
"#{$this->entityTypeId}" => $entity, "#{$this->entityTypeId}" => $entity,
'#view_mode' => $view_mode, '#view_mode' => $view_mode,
// Collect cache defaults for this entity. // Collect cache defaults for this entity.
@ -159,6 +170,11 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf
), ),
); );
// Add the default #theme key if a template exists for it.
if ($this->themeRegistry->getRuntime()->has($this->entityTypeId)) {
$build['#theme'] = $this->entityTypeId;
}
// Cache the rendered output if permitted by the view mode and global entity // Cache the rendered output if permitted by the view mode and global entity
// type configuration. // type configuration.
if ($this->isViewModeCacheable($view_mode) && !$entity->isNew() && $entity->isDefaultRevision() && $this->entityType->isRenderCacheable()) { if ($this->isViewModeCacheable($view_mode) && !$entity->isNew() && $entity->isDefaultRevision() && $this->entityType->isRenderCacheable()) {

View file

@ -35,13 +35,18 @@ interface QueryInterface extends AlterableInterface {
* *
* @param $field * @param $field
* Name of the field being queried. It must contain a field name, optionally * Name of the field being queried. It must contain a field name, optionally
* followed by a column name. The column can be "entity" for reference * followed by a column name. The column can be the reference property,
* fields and that can be followed similarly by a field name and so on. Some * usually "entity", for reference fields and that can be followed
* examples: * similarly by a field name and so on. Additionally, the target entity type
* can be specified by appending the ":target_entity_type_id" to "entity".
* Some examples:
* - nid * - nid
* - tags.value * - tags.value
* - tags * - tags
* - tags.entity.name
* - tags.entity:taxonomy_term.name
* - uid.entity.name * - uid.entity.name
* - uid.entity:user.name
* "tags" "is the same as "tags.value" as value is the default column. * "tags" "is the same as "tags.value" as value is the default column.
* If two or more conditions have the same field names they apply to the * If two or more conditions have the same field names they apply to the
* same delta within that field. In order to limit the condition to a * same delta within that field. In order to limit the condition to a

View file

@ -6,6 +6,8 @@ use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\Query\QueryException; use Drupal\Core\Entity\Query\QueryException;
use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
use Drupal\Core\Entity\Sql\TableMappingInterface; use Drupal\Core\Entity\Sql\TableMappingInterface;
use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
/** /**
* Adds tables and fields to the SQL entity query. * Adds tables and fields to the SQL entity query.
@ -253,10 +255,20 @@ class Tables implements TablesInterface {
$relationship_specifier = $specifiers[$key + 1]; $relationship_specifier = $specifiers[$key + 1];
$next_index_prefix = $relationship_specifier; $next_index_prefix = $relationship_specifier;
} }
$entity_type_id = NULL;
// Relationship specifier can also contain the entity type ID, i.e.
// entity:node, entity:user or entity:taxonomy.
if (strpos($relationship_specifier, ':') !== FALSE) {
list($relationship_specifier, $entity_type_id) = explode(':', $relationship_specifier, 2);
}
// Check for a valid relationship. // Check for a valid relationship.
if (isset($propertyDefinitions[$relationship_specifier]) && $field_storage->getPropertyDefinition('entity')->getDataType() == 'entity_reference' ) { if (isset($propertyDefinitions[$relationship_specifier]) && $propertyDefinitions[$relationship_specifier] instanceof DataReferenceDefinitionInterface) {
// If it is, use the entity type. // If it is, use the entity type if specified already, otherwise use
$entity_type_id = $propertyDefinitions[$relationship_specifier]->getTargetDefinition()->getEntityTypeId(); // the definition.
$target_definition = $propertyDefinitions[$relationship_specifier]->getTargetDefinition();
if (!$entity_type_id && $target_definition instanceof EntityDataDefinitionInterface) {
$entity_type_id = $target_definition->getEntityTypeId();
}
$entity_type = $this->entityManager->getDefinition($entity_type_id); $entity_type = $this->entityManager->getDefinition($entity_type_id);
$field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
// Add the new entity base table using the table and sql column. // Add the new entity base table using the table and sql column.

View file

@ -1257,7 +1257,11 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
foreach ($storage_definition->getColumns() as $column => $attributes) { foreach ($storage_definition->getColumns() as $column => $attributes) {
$column_name = $table_mapping->getFieldColumnName($storage_definition, $column); $column_name = $table_mapping->getFieldColumnName($storage_definition, $column);
// Serialize the value if specified in the column schema. // Serialize the value if specified in the column schema.
$record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column; $value = $item->$column;
if (!empty($attributes['serialize'])) {
$value = serialize($value);
}
$record[$column_name] = drupal_schema_get_field_value($attributes, $value);
} }
$query->values($record); $query->values($record);
if ($this->entityType->isRevisionable()) { if ($this->entityType->isRevisionable()) {

View file

@ -168,6 +168,10 @@ class EntityReferenceEntityFormatter extends EntityReferenceFormatterBase implem
$recursive_render_id = $items->getFieldDefinition()->getTargetEntityTypeId() $recursive_render_id = $items->getFieldDefinition()->getTargetEntityTypeId()
. $items->getFieldDefinition()->getTargetBundle() . $items->getFieldDefinition()->getTargetBundle()
. $items->getName() . $items->getName()
// We include the referencing entity, so we can render default images
// without hitting recursive protections.
. $items->getEntity()->id()
. $entity->getEntityTypeId()
. $entity->id(); . $entity->id();
if (isset(static::$recursiveRenderDepth[$recursive_render_id])) { if (isset(static::$recursiveRenderDepth[$recursive_render_id])) {

View file

@ -3,6 +3,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType; namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
/** /**
* Defines the 'uuid' entity field type. * Defines the 'uuid' entity field type.
@ -48,4 +49,12 @@ class UuidItem extends StringItem {
return $schema; return $schema;
} }
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$values['value'] = \Drupal::service('uuid')->generate();
return $values;
}
} }

View file

@ -36,7 +36,7 @@ abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface
/** /**
* Constructs a WidgetBase object. * Constructs a WidgetBase object.
* *
* @param array $plugin_id * @param string $plugin_id
* The plugin_id for the widget. * The plugin_id for the widget.
* @param mixed $plugin_definition * @param mixed $plugin_definition
* The plugin implementation definition. * The plugin implementation definition.

View file

@ -318,7 +318,7 @@ class LanguageManager implements LanguageManagerInterface {
'th' => array('Thai', 'ภาษาไทย'), 'th' => array('Thai', 'ภาษาไทย'),
'tr' => array('Turkish', 'Türkçe'), 'tr' => array('Turkish', 'Türkçe'),
'tyv' => array('Tuvan', 'Тыва дыл'), 'tyv' => array('Tuvan', 'Тыва дыл'),
'ug' => array('Uyghur', 'Уйғур'), 'ug' => array('Uyghur', /* Left-to-right marker "" */ 'ئۇيغۇرچە', LanguageInterface::DIRECTION_RTL),
'uk' => array('Ukrainian', 'Українська'), 'uk' => array('Ukrainian', 'Українська'),
'ur' => array('Urdu', /* Left-to-right marker "" */ 'اردو', LanguageInterface::DIRECTION_RTL), 'ur' => array('Urdu', /* Left-to-right marker "" */ 'اردو', LanguageInterface::DIRECTION_RTL),
'vi' => array('Vietnamese', 'Tiếng Việt'), 'vi' => array('Vietnamese', 'Tiếng Việt'),

View file

@ -2,6 +2,7 @@
namespace Drupal\Core\Plugin\Context; namespace Drupal\Core\Plugin\Context;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\TypedData\TypedDataTrait; use Drupal\Core\TypedData\TypedDataTrait;
/** /**
@ -9,6 +10,8 @@ use Drupal\Core\TypedData\TypedDataTrait;
*/ */
class ContextDefinition implements ContextDefinitionInterface { class ContextDefinition implements ContextDefinitionInterface {
use DependencySerializationTrait;
use TypedDataTrait; use TypedDataTrait;
/** /**

View file

@ -419,9 +419,13 @@ class HtmlResponseAttachmentsProcessor implements AttachmentsResponseProcessorIn
if ($should_add_header) { if ($should_add_header) {
// Also add a HTTP header "Link:". // Also add a HTTP header "Link:".
$href = '<' . Html::escape($attributes['href'] . '>'); $href = '<' . Html::escape($attributes['href']) . '>';
unset($attributes['href']); unset($attributes['href']);
$attached['http_header'][] = ['Link', $href . drupal_http_header_attributes($attributes), TRUE]; if ($param = drupal_http_header_attributes($attributes)) {
$href .= ';' . $param;
}
$attached['http_header'][] = ['Link', $href, FALSE];
} }
} }
return $attached; return $attached;

View file

@ -3,6 +3,7 @@
namespace Drupal\Core\StringTranslation\Translator; namespace Drupal\Core\StringTranslation\Translator;
use Drupal\Core\Site\Settings; use Drupal\Core\Site\Settings;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
/** /**
* String translator using overrides from variables. * String translator using overrides from variables.
@ -12,6 +13,8 @@ use Drupal\Core\Site\Settings;
*/ */
class CustomStrings extends StaticTranslation { class CustomStrings extends StaticTranslation {
use DependencySerializationTrait;
/** /**
* The settings read only object. * The settings read only object.
* *

View file

@ -77,6 +77,11 @@
*/ */
function searchHandler(event) { function searchHandler(event) {
var options = autocomplete.options; var options = autocomplete.options;
if (options.isComposing) {
return false;
}
var term = autocomplete.extractLastTerm(event.target.value); var term = autocomplete.extractLastTerm(event.target.value);
// Abort search if the first character is in firstCharacterBlacklist. // Abort search if the first character is in firstCharacterBlacklist.
if (term.length > 0 && options.firstCharacterBlacklist.indexOf(term[0]) !== -1) { if (term.length > 0 && options.firstCharacterBlacklist.indexOf(term[0]) !== -1) {
@ -225,6 +230,14 @@
.each(function () { .each(function () {
$(this).data('ui-autocomplete')._renderItem = autocomplete.options.renderItem; $(this).data('ui-autocomplete')._renderItem = autocomplete.options.renderItem;
}); });
// Use CompositionEvent to handle IME inputs. It requests remote server on "compositionend" event only.
$autocomplete.on('compositionstart.autocomplete', function () {
autocomplete.options.isComposing = true;
});
$autocomplete.on('compositionend.autocomplete', function () {
autocomplete.options.isComposing = false;
});
} }
}, },
detach: function (context, settings, trigger) { detach: function (context, settings, trigger) {
@ -261,7 +274,9 @@
renderItem: renderItem, renderItem: renderItem,
minLength: 1, minLength: 1,
// Custom options, used by Drupal.autocomplete. // Custom options, used by Drupal.autocomplete.
firstCharacterBlacklist: '' firstCharacterBlacklist: '',
// Custom options, indicate IME usage status.
isComposing: false
}, },
ajax: { ajax: {
dataType: 'json' dataType: 'json'

View file

@ -151,7 +151,7 @@ function hook_block_view_BASE_BLOCK_ID_alter(array &$build, \Drupal\Core\Block\B
function hook_block_build_alter(array &$build, \Drupal\Core\Block\BlockPluginInterface $block) { function hook_block_build_alter(array &$build, \Drupal\Core\Block\BlockPluginInterface $block) {
// Add the 'user' cache context to some blocks. // Add the 'user' cache context to some blocks.
if ($some_condition) { if ($some_condition) {
$build['#contexts'][] = 'user'; $build['#cache']['contexts'][] = 'user';
} }
} }

View file

@ -73,7 +73,7 @@ class BookJavascriptTest extends JavascriptTestBase {
$dragged->dragTo($target); $dragged->dragTo($target);
// Give javascript some time to manipulate the DOM. // Give javascript some time to manipulate the DOM.
$this->getSession()->wait(1000, 'jQuery(".tabledrag-changed-warning").is(":visible")'); $this->assertJsCondition('jQuery(".tabledrag-changed-warning").is(":visible")');
// Check that the 'unsaved changes' text appeared in the message area. // Check that the 'unsaved changes' text appeared in the message area.
$this->assertSession()->pageTextContains('You have unsaved changes.'); $this->assertSession()->pageTextContains('You have unsaved changes.');

View file

@ -8,7 +8,6 @@ use Drupal\ckeditor\CKEditorPluginCssInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManager; use Drupal\Core\Language\LanguageManager;
use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\editor\Entity\Editor; use Drupal\editor\Entity\Editor;
/** /**
@ -115,7 +114,7 @@ class Language extends CKEditorPluginBase implements CKEditorPluginConfigurableI
], ],
'#default_value' => $config['language_list'], '#default_value' => $config['language_list'],
'#description' => $this->t('The list of languages to show in the language dropdown. The basic list will only show the <a href=":url">six official languages of the UN</a>. The extended list will show all @count languages that are available in Drupal.', [ '#description' => $this->t('The list of languages to show in the language dropdown. The basic list will only show the <a href=":url">six official languages of the UN</a>. The extended list will show all @count languages that are available in Drupal.', [
':url' => Url::fromUri('http://www.un.org/en/aboutun/languages.shtml/')->toString(), ':url' => 'https://www.un.org/en/sections/about-un/official-languages',
'@count' => count($predefined_languages), '@count' => count($predefined_languages),
]), ]),
'#attached' => ['library' => ['ckeditor/drupal.ckeditor.language.admin']], '#attached' => ['library' => ['ckeditor/drupal.ckeditor.language.admin']],

View file

@ -7,6 +7,8 @@ source:
constants: constants:
entity_type: node entity_type: node
process: process:
# If you are using this file to build a custom migration consider removing
# the cid field to allow incremental migrations.
cid: cid cid: cid
pid: pid:
plugin: migration plugin: migration

View file

@ -13,7 +13,10 @@ process:
type: 'constants/type' type: 'constants/type'
'settings/comment_type': comment_type 'settings/comment_type': comment_type
destination: destination:
plugin: md_entity:field_storage_config plugin: entity:field_storage_config
dependencies:
module:
- comment
migration_dependencies: migration_dependencies:
required: required:
- d6_comment_type - d6_comment_type

View file

@ -7,6 +7,8 @@ source:
constants: constants:
entity_type: node entity_type: node
process: process:
# If you are using this file to build a custom migration consider removing
# the cid field to allow incremental migrations.
cid: cid cid: cid
pid: pid:
plugin: migration plugin: migration

View file

@ -110,12 +110,13 @@ class CommentAccessControlHandler extends EntityAccessControlHandler {
// access. // access.
return AccessResult::forbidden(); return AccessResult::forbidden();
} }
$is_name = $field_definition->getName() === 'name';
/** @var \Drupal\comment\CommentInterface $entity */ /** @var \Drupal\comment\CommentInterface $entity */
$entity = $items->getEntity(); $entity = $items->getEntity();
$commented_entity = $entity->getCommentedEntity(); $commented_entity = $entity->getCommentedEntity();
$anonymous_contact = $commented_entity->get($entity->getFieldName())->getFieldDefinition()->getSetting('anonymous'); $anonymous_contact = $commented_entity->get($entity->getFieldName())->getFieldDefinition()->getSetting('anonymous');
$admin_access = AccessResult::allowedIfHasPermission($account, 'administer comments'); $admin_access = AccessResult::allowedIfHasPermission($account, 'administer comments');
$anonymous_access = AccessResult::allowedIf($entity->isNew() && $account->isAnonymous() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT && $account->hasPermission('post comments')) $anonymous_access = AccessResult::allowedIf($entity->isNew() && $account->isAnonymous() && ($anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT || $is_name) && $account->hasPermission('post comments'))
->cachePerPermissions() ->cachePerPermissions()
->addCacheableDependency($entity) ->addCacheableDependency($entity)
->addCacheableDependency($field_definition->getConfig($commented_entity->bundle())) ->addCacheableDependency($field_definition->getConfig($commented_entity->bundle()))

View file

@ -65,6 +65,16 @@ class CommentAnonymousTest extends CommentTestBase {
$anonymous_comment1 = $this->postComment($this->node, $this->randomMachineName(), $this->randomMachineName()); $anonymous_comment1 = $this->postComment($this->node, $this->randomMachineName(), $this->randomMachineName());
$this->assertTrue($this->commentExists($anonymous_comment1), 'Anonymous comment without contact info found.'); $this->assertTrue($this->commentExists($anonymous_comment1), 'Anonymous comment without contact info found.');
// Ensure anonymous users cannot post in the name of registered users.
$edit = array(
'name' => $this->adminUser->getUsername(),
'comment_body[0][value]' => $this->randomMachineName(),
);
$this->drupalPostForm('comment/reply/node/' . $this->node->id() . '/comment', $edit, t('Save'));
$this->assertRaw(t('The name you used (%name) belongs to a registered user.', [
'%name' => $this->adminUser->getUsername(),
]));
// Allow contact info. // Allow contact info.
$this->drupalLogin($this->adminUser); $this->drupalLogin($this->adminUser);
$this->setCommentAnonymous(COMMENT_ANONYMOUS_MAY_CONTACT); $this->setCommentAnonymous(COMMENT_ANONYMOUS_MAY_CONTACT);

View file

@ -0,0 +1,49 @@
<?php
namespace Drupal\Tests\config\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\Role;
/**
* Tests draggable list builder.
*
* @group config
*/
class ConfigDraggableListBuilderTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('config_test');
/**
* Test draggable lists.
*/
public function testDraggableList() {
$this->drupalLogin($this->drupalCreateUser(array('administer permissions')));
// Create more than 50 roles.
for ($i = 0; $i < 51; $i++) {
$role = Role::create([
'id' => 'role_' . $i,
'label' => "Role $i",
]);
$role->save();
}
// Navigate to Roles page
$this->drupalGet('admin/people/roles');
// Test for the page title.
$this->assertSession()->titleEquals(t('Roles') . ' | Drupal');
// Count the number of rows in table.
$rows = $this->xpath('//form[@class="user-admin-roles-form"]/table/tbody/tr');
$this->assertGreaterThan(50, count($rows));
for ($i = 0; $i < 51; $i++) {
$this->assertSession()->pageTextContains("Role $i");
}
}
}

View file

@ -152,7 +152,6 @@ class EntityOperations implements ContainerInjectionInterface {
$entity_type_id = $entity->getEntityTypeId(); $entity_type_id = $entity->getEntityTypeId();
$entity_id = $entity->id(); $entity_id = $entity->id();
$entity_revision_id = $entity->getRevisionId(); $entity_revision_id = $entity->getRevisionId();
$entity_langcode = $entity->language()->getId();
$storage = $this->entityTypeManager->getStorage('content_moderation_state'); $storage = $this->entityTypeManager->getStorage('content_moderation_state');
$entities = $storage->loadByProperties([ $entities = $storage->loadByProperties([
@ -174,11 +173,14 @@ class EntityOperations implements ContainerInjectionInterface {
} }
// Sync translations. // Sync translations.
if (!$content_moderation_state->hasTranslation($entity_langcode)) { if ($entity->getEntityType()->hasKey('langcode')) {
$content_moderation_state->addTranslation($entity_langcode); $entity_langcode = $entity->language()->getId();
} if (!$content_moderation_state->hasTranslation($entity_langcode)) {
if ($content_moderation_state->language()->getId() !== $entity_langcode) { $content_moderation_state->addTranslation($entity_langcode);
$content_moderation_state = $content_moderation_state->getTranslation($entity_langcode); }
if ($content_moderation_state->language()->getId() !== $entity_langcode) {
$content_moderation_state = $content_moderation_state->getTranslation($entity_langcode);
}
} }
// Create the ContentModerationState entity for the inserted entity. // Create the ContentModerationState entity for the inserted entity.

View file

@ -292,8 +292,8 @@ class EntityTypeInfo implements ContainerInjectionInterface {
$fields = []; $fields = [];
$fields['moderation_state'] = BaseFieldDefinition::create('entity_reference') $fields['moderation_state'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Moderation state')) ->setLabel($this->t('Moderation state'))
->setDescription(t('The moderation state of this piece of content.')) ->setDescription($this->t('The moderation state of this piece of content.'))
->setComputed(TRUE) ->setComputed(TRUE)
->setClass(ModerationStateFieldItemList::class) ->setClass(ModerationStateFieldItemList::class)
->setSetting('target_type', 'moderation_state') ->setSetting('target_type', 'moderation_state')
@ -310,6 +310,7 @@ class EntityTypeInfo implements ContainerInjectionInterface {
->addConstraint('ModerationState', []) ->addConstraint('ModerationState', [])
->setDisplayConfigurable('form', FALSE) ->setDisplayConfigurable('form', FALSE)
->setDisplayConfigurable('view', FALSE) ->setDisplayConfigurable('view', FALSE)
->setReadOnly(FALSE)
->setTranslatable(TRUE); ->setTranslatable(TRUE);
return $fields; return $fields;

View file

@ -43,12 +43,14 @@ class ModerationStateFieldItemList extends EntityReferenceFieldItemList {
->loadRevision($revision_to_load); ->loadRevision($revision_to_load);
// Return the correct translation. // Return the correct translation.
$langcode = $entity->language()->getId(); if ($entity->getEntityType()->hasKey('langcode')) {
if (!$content_moderation_state->hasTranslation($langcode)) { $langcode = $entity->language()->getId();
$content_moderation_state->addTranslation($langcode); if (!$content_moderation_state->hasTranslation($langcode)) {
} $content_moderation_state->addTranslation($langcode);
if ($content_moderation_state->language()->getId() !== $langcode) { }
$content_moderation_state = $content_moderation_state->getTranslation($langcode); if ($content_moderation_state->language()->getId() !== $langcode) {
$content_moderation_state = $content_moderation_state->getTranslation($langcode);
}
} }
return $content_moderation_state->get('moderation_state')->entity; return $content_moderation_state->get('moderation_state')->entity;

View file

@ -4,6 +4,8 @@ namespace Drupal\Tests\content_moderation\Kernel;
use Drupal\content_moderation\Entity\ContentModerationState; use Drupal\content_moderation\Entity\ContentModerationState;
use Drupal\content_moderation\Entity\ModerationState; use Drupal\content_moderation\Entity\ModerationState;
use Drupal\entity_test\Entity\EntityTestBundle;
use Drupal\entity_test\Entity\EntityTestWithBundle;
use Drupal\KernelTests\KernelTestBase; use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage; use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\Node; use Drupal\node\Entity\Node;
@ -21,12 +23,14 @@ class ContentModerationStateTest extends KernelTestBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
public static $modules = [ public static $modules = [
'entity_test',
'node', 'node',
'content_moderation', 'content_moderation',
'user', 'user',
'system', 'system',
'language', 'language',
'content_translation', 'content_translation',
'text',
]; ];
/** /**
@ -38,6 +42,7 @@ class ContentModerationStateTest extends KernelTestBase {
$this->installSchema('node', 'node_access'); $this->installSchema('node', 'node_access');
$this->installEntitySchema('node'); $this->installEntitySchema('node');
$this->installEntitySchema('user'); $this->installEntitySchema('user');
$this->installEntitySchema('entity_test_with_bundle');
$this->installEntitySchema('content_moderation_state'); $this->installEntitySchema('content_moderation_state');
$this->installConfig('content_moderation'); $this->installConfig('content_moderation');
} }
@ -210,6 +215,91 @@ class ContentModerationStateTest extends KernelTestBase {
$this->assertEquals(6, $english_node->getRevisionId()); $this->assertEquals(6, $english_node->getRevisionId());
} }
/**
* Tests that a non-translatable entity type with a langcode can be moderated.
*/
public function testNonTranslatableEntityTypeModeration() {
// Make the 'entity_test_with_bundle' entity type revisionable.
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
$keys = $entity_type->getKeys();
$keys['revision'] = 'revision_id';
$entity_type->set('entity_keys', $keys);
\Drupal::state()->set('entity_test_with_bundle.entity_type', $entity_type);
\Drupal::entityDefinitionUpdateManager()->applyUpdates();
// Create a test bundle.
$entity_test_bundle = EntityTestBundle::create([
'id' => 'example',
]);
$entity_test_bundle->setThirdPartySetting('content_moderation', 'enabled', TRUE);
$entity_test_bundle->setThirdPartySetting('content_moderation', 'allowed_moderation_states', [
'draft',
'published'
]);
$entity_test_bundle->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
$entity_test_bundle->save();
// Check that the tested entity type is not translatable.
$entity_type = \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
$this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.');
// Create a test entity.
$entity_test_with_bundle = EntityTestWithBundle::create([
'type' => 'example'
]);
$entity_test_with_bundle->save();
$this->assertEquals('draft', $entity_test_with_bundle->moderation_state->entity->id());
$entity_test_with_bundle->moderation_state->target_id = 'published';
$entity_test_with_bundle->save();
$this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->entity->id());
}
/**
* Tests that a non-translatable entity type without a langcode can be
* moderated.
*/
public function testNonLangcodeEntityTypeModeration() {
// Make the 'entity_test_with_bundle' entity type revisionable and unset
// the langcode entity key.
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
$keys = $entity_type->getKeys();
$keys['revision'] = 'revision_id';
unset($keys['langcode']);
$entity_type->set('entity_keys', $keys);
\Drupal::state()->set('entity_test_with_bundle.entity_type', $entity_type);
\Drupal::entityDefinitionUpdateManager()->applyUpdates();
// Create a test bundle.
$entity_test_bundle = EntityTestBundle::create([
'id' => 'example',
]);
$entity_test_bundle->setThirdPartySetting('content_moderation', 'enabled', TRUE);
$entity_test_bundle->setThirdPartySetting('content_moderation', 'allowed_moderation_states', [
'draft',
'published'
]);
$entity_test_bundle->setThirdPartySetting('content_moderation', 'default_moderation_state', 'draft');
$entity_test_bundle->save();
// Check that the tested entity type is not translatable.
$entity_type = \Drupal::entityTypeManager()->getDefinition('entity_test_with_bundle');
$this->assertFalse($entity_type->isTranslatable(), 'The test entity type is not translatable.');
// Create a test entity.
$entity_test_with_bundle = EntityTestWithBundle::create([
'type' => 'example'
]);
$entity_test_with_bundle->save();
$this->assertEquals('draft', $entity_test_with_bundle->moderation_state->entity->id());
$entity_test_with_bundle->moderation_state->target_id = 'published';
$entity_test_with_bundle->save();
$this->assertEquals('published', EntityTestWithBundle::load($entity_test_with_bundle->id())->moderation_state->entity->id());
}
/** /**
* Reloads the node after clearing the static cache. * Reloads the node after clearing the static cache.
* *

View file

@ -0,0 +1,63 @@
<?php
namespace Drupal\Tests\content_moderation\Kernel;
use Drupal\content_moderation\Entity\Handler\ModerationHandler;
use Drupal\content_moderation\EntityTypeInfo;
use Drupal\KernelTests\KernelTestBase;
/**
* @coversDefaultClass \Drupal\content_moderation\EntityTypeInfo
*
* @group content_moderation
*/
class EntityTypeInfoTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'content_moderation',
'entity_test',
];
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity type info class.
*
* @var \Drupal\content_moderation\EntityTypeInfo
*/
protected $entityTypeInfo;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->entityTypeInfo = $this->container->get('class_resolver')->getInstanceFromDefinition(EntityTypeInfo::class);
$this->entityTypeManager = $this->container->get('entity_type.manager');
}
/**
* @covers ::entityBaseFieldInfo
*/
public function testEntityBaseFieldInfo() {
$definition = $this->entityTypeManager->getDefinition('entity_test');
$definition->setHandlerClass('moderation', ModerationHandler::class);
$base_fields = $this->entityTypeInfo->entityBaseFieldInfo($definition);
$this->assertFalse($base_fields['moderation_state']->isReadOnly());
$this->assertTrue($base_fields['moderation_state']->isComputed());
$this->assertTrue($base_fields['moderation_state']->isTranslatable());
}
}

View file

@ -0,0 +1,113 @@
<?php
namespace Drupal\Tests\datetime\Kernel;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests serializing a form with an injected datetime instance.
*
* @group datetime
*/
class DateTimeFormInjectionTest extends KernelTestBase implements FormInterface {
use DependencySerializationTrait;
/**
* A Dblog logger instance.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['system', 'datetime'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['key_value_expire', 'sequences']);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'datetime_test_injection_form';
}
/**
* Process callback.
*
* @param array $element
* Form element.
*
* @return array
* Processed element.
*/
public function process($element) {
return $element;
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['datelist_element'] = [
'#title' => 'datelist test',
'#type' => 'datelist',
'#default_value' => new DrupalDateTime('2000-01-01 00:00:00'),
'#date_part_order' => [
'month',
'day',
'year',
'hour',
'minute', 'ampm',
],
'#date_text_parts' => ['year'],
'#date_year_range' => '2010:2020',
'#date_increment' => 15,
];
$form['#process'][] = [$this, 'process'];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->assertTrue(TRUE);
$form_state->setRebuild();
}
/**
* Tests custom string injection serialization.
*/
public function testDatetimeSerialization() {
$form_state = new FormState();
$form_state->setRequestMethod('POST');
$form_state->setCached();
$form_builder = $this->container->get('form_builder');
$form_id = $form_builder->getFormId($this, $form_state);
$form = $form_builder->retrieveForm($form_id, $form_state);
$form_builder->prepareForm($form_id, $form, $form_state);
$form_builder->processForm($form_id, $form, $form_state);
}
}

View file

@ -123,4 +123,4 @@ process:
- '@type' - '@type'
- global_settings - global_settings
destination: destination:
plugin: md_entity:field_storage_config plugin: entity:field_storage_config

View file

@ -22,6 +22,7 @@ process:
datestamp: datetime datestamp: datetime
datetime: datetime datetime: datetime
email: email email: email
entityreference: entity_reference
file: file file: file
image: image image: image
link_field: link link_field: link

View file

@ -51,7 +51,7 @@ process:
- -
plugin: static_map plugin: static_map
bypass: true bypass: true
source: type source: formatter_type
map: map:
date_default: datetime_default date_default: datetime_default
email_default: email_mailto email_default: email_mailto
@ -61,6 +61,9 @@ process:
link_default: link link_default: link
phone: basic_string phone: basic_string
taxonomy_term_reference_link: entity_reference_label taxonomy_term_reference_link: entity_reference_label
entityreference_label: entity_reference_label
entityreference_entity_id: entity_reference_entity_id
entityreference_entity_view: entity_reference_entity_view
- -
plugin: skip_on_empty plugin: skip_on_empty
method: row method: row

View file

@ -21,6 +21,7 @@ process:
source: source:
- instance_settings - instance_settings
- widget_settings - widget_settings
- field_settings
default_value_function: '' default_value_function: ''
default_value: default_value:
plugin: d7_field_instance_defaults plugin: d7_field_instance_defaults

View file

@ -46,6 +46,7 @@ process:
phone_textfield: telephone_default phone_textfield: telephone_default
options_onoff: boolean_checkbox options_onoff: boolean_checkbox
entityreference_autocomplete: entity_reference_autocomplete entityreference_autocomplete: entity_reference_autocomplete
entityreference_autocomplete_tags: entity_reference_autocomplete_tags
taxonomy_autocomplete: entity_reference_autocomplete taxonomy_autocomplete: entity_reference_autocomplete
'options/settings': 'options/settings':
plugin: field_instance_widget_settings plugin: field_instance_widget_settings

View file

@ -17,9 +17,38 @@ class FieldInstanceSettings extends ProcessPluginBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) { public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
list($instance_settings, $widget_settings) = $value; list($instance_settings, $widget_settings, $field_settings) = $value;
$widget_type = $widget_settings['type']; $widget_type = $widget_settings['type'];
// Get entityreference handler settings from source field configuration.
if ($row->getSourceProperty('type') == "entityreference") {
$instance_settings['handler'] = 'default:' . $field_settings['target_type'];
// Transform the sort settings to D8 structure.
$sort = [
'field' => '_none',
'direction' => 'ASC',
];
if (!empty(array_filter($field_settings['handler_settings']['sort']))) {
if ($field_settings['handler_settings']['sort']['type'] == "property") {
$sort = [
'field' => $field_settings['handler_settings']['sort']['property'],
'direction' => $field_settings['handler_settings']['sort']['direction'],
];
}
elseif ($field_settings['handler_settings']['sort']['type'] == "field") {
$sort = [
'field' => $field_settings['handler_settings']['sort']['field'],
'direction' => $field_settings['handler_settings']['sort']['direction'],
];
}
}
if (empty($field_settings['handler_settings']['target_bundles'])) {
$field_settings['handler_settings']['target_bundles'] = NULL;
}
$field_settings['handler_settings']['sort'] = $sort;
$instance_settings['handler_settings'] = $field_settings['handler_settings'];
}
switch ($widget_type) { switch ($widget_type) {
case 'image_image': case 'image_image':
$settings = $instance_settings; $settings = $instance_settings;

View file

@ -28,6 +28,7 @@ class FieldInstance extends DrupalSqlBase {
->fields('fc', array('type')); ->fields('fc', array('type'));
$query->innerJoin('field_config', 'fc', 'fci.field_id = fc.id'); $query->innerJoin('field_config', 'fc', 'fci.field_id = fc.id');
$query->addField('fc', 'data', 'field_data');
// Optionally filter by entity type and bundle. // Optionally filter by entity type and bundle.
if (isset($this->configuration['entity_type'])) { if (isset($this->configuration['entity_type'])) {
@ -53,6 +54,7 @@ class FieldInstance extends DrupalSqlBase {
'instance_settings' => $this->t('Field instance settings.'), 'instance_settings' => $this->t('Field instance settings.'),
'widget_settings' => $this->t('Widget settings.'), 'widget_settings' => $this->t('Widget settings.'),
'display_settings' => $this->t('Display settings.'), 'display_settings' => $this->t('Display settings.'),
'field_settings' => $this->t('Field settings.'),
); );
} }
@ -81,6 +83,9 @@ class FieldInstance extends DrupalSqlBase {
// This is for parity with the d6_field_instance plugin. // This is for parity with the d6_field_instance plugin.
$row->setSourceProperty('widget_type', $data['widget']['type']); $row->setSourceProperty('widget_type', $data['widget']['type']);
$field_data = unserialize($row->getSourceProperty('field_data'));
$row->setSourceProperty('field_settings', $field_data['settings']);
return parent::prepareRow($row); return parent::prepareRow($row);
} }

View file

@ -24,8 +24,15 @@ class FieldInstancePerViewMode extends DrupalSqlBase {
$data = unserialize($field_instance['data']); $data = unserialize($field_instance['data']);
// We don't need to include the serialized data in the returned rows. // We don't need to include the serialized data in the returned rows.
unset($field_instance['data']); unset($field_instance['data']);
foreach ($data['display'] as $view_mode => $info) { foreach ($data['display'] as $view_mode => $info) {
$rows[] = array_merge($field_instance, $info, array('view_mode' => $view_mode)); // Rename type to formatter_type in the info array.
$info['formatter_type'] = $info['type'];
unset($info['type']);
$rows[] = array_merge($field_instance, $info, [
'view_mode' => $view_mode,
]);
} }
} }
return new \ArrayIterator($rows); return new \ArrayIterator($rows);
@ -35,8 +42,11 @@ class FieldInstancePerViewMode extends DrupalSqlBase {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function query() { public function query() {
return $this->select('field_config_instance', 'fci') $query = $this->select('field_config_instance', 'fci')
->fields('fci', array('entity_type', 'bundle', 'field_name', 'data')); ->fields('fci', ['entity_type', 'bundle', 'field_name', 'data'])
->fields('fc', ['type']);
$query->join('field_config', 'fc', 'fc.field_name = fci.field_name');
return $query;
} }
/** /**
@ -49,7 +59,8 @@ class FieldInstancePerViewMode extends DrupalSqlBase {
'field_name' => $this->t('Machine name of the field.'), 'field_name' => $this->t('Machine name of the field.'),
'view_mode' => $this->t('The original machine name of the view mode.'), 'view_mode' => $this->t('The original machine name of the view mode.'),
'label' => $this->t('The display label of the field.'), 'label' => $this->t('The display label of the field.'),
'type' => $this->t('The formatter ID.'), 'type' => $this->t('The field ID.'),
'formatter_type' => $this->t('The formatter ID.'),
'settings' => $this->t('Array of formatter-specific settings.'), 'settings' => $this->t('Array of formatter-specific settings.'),
'module' => $this->t('The module providing the formatter.'), 'module' => $this->t('The module providing the formatter.'),
'weight' => $this->t('Display weight of the field.'), 'weight' => $this->t('Display weight of the field.'),

View file

@ -20,7 +20,12 @@ class BooleanFieldTest extends WebTestBase {
* *
* @var array * @var array
*/ */
public static $modules = array('entity_test', 'field_ui', 'options'); public static $modules = [
'entity_test',
'field_ui',
'options',
'field_test_boolean_access_denied',
];
/** /**
* A field to use in this test class. * A field to use in this test class.
@ -179,4 +184,66 @@ class BooleanFieldTest extends WebTestBase {
$this->assertFieldById('edit-settings-off-label', $off); $this->assertFieldById('edit-settings-off-label', $off);
} }
/**
* Test field access.
*/
public function testFormAccess() {
$on = 'boolean_on';
$off = 'boolean_off';
$label = 'boolean_label';
$field_name = 'boolean_name';
$this->fieldStorage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'boolean',
]);
$this->fieldStorage->save();
$this->field = FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
'label' => $label,
'settings' => [
'on_label' => $on,
'off_label' => $off,
],
]);
$this->field->save();
// Create a form display for the default form mode.
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($field_name, [
'type' => 'boolean_checkbox',
])
->save();
// Create a display for the full view mode.
entity_get_display('entity_test', 'entity_test', 'full')
->setComponent($field_name, [
'type' => 'boolean',
])
->save();
// Display creation form.
$this->drupalGet('entity_test/add');
$this->assertFieldByName("{$field_name}[value]");
// Should be posted OK.
$this->drupalPostForm(NULL, [], t('Save'));
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
$id = $match[1];
$this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
// Tell the test module to disable access to the field.
\Drupal::state()->set('field.test_boolean_field_access_field', $field_name);
$this->drupalGet('entity_test/add');
// Field should not be there anymore.
$this->assertNoFieldByName("{$field_name}[value]");
// Should still be able to post the form.
$this->drupalPostForm(NULL, [], t('Save'));
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
$id = $match[1];
$this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
}
} }

View file

@ -0,0 +1,62 @@
<?php
namespace Drupal\field_test\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Field\FieldItemBase;
/**
* Defines the 'test_object_field' entity field item.
*
* @FieldType(
* id = "test_object_field",
* label = @Translation("Test object field"),
* description = @Translation("Test field type that has an object to test serialization"),
* default_widget = "test_object_field_widget",
* default_formatter = "object_field_test_default"
* )
*/
class TestObjectItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties['value'] = DataDefinition::create('any')
->setLabel(t('Value'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
return [
'columns' => [
'value' => [
'description' => 'The object item value.',
'type' => 'blob',
'not null' => TRUE,
'serialize' => TRUE,
],
],
];
}
/**
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
if (isset($values['value'])) {
// @todo Remove this in https://www.drupal.org/node/2788637.
if (is_string($values['value'])) {
$values['value'] = unserialize($values['value']);
}
}
parent::setValue($values, $notify);
}
}

View file

@ -0,0 +1,8 @@
name: 'Boolean field Test'
type: module
description: 'Support module for the field and entity display tests.'
core: 8.x
package: Testing
version: VERSION
dependencies:
- field

View file

@ -0,0 +1,18 @@
<?php
/**
* @file
* Module for testing denying access to boolean fields.
*/
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Implements hook_entity_field_access().
*/
function field_test_boolean_access_denied_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
return AccessResult::forbiddenIf($field_definition->getName() === \Drupal::state()->get('field.test_boolean_field_access_field'));
}

View file

@ -277,6 +277,49 @@ class EntityReferenceFormatterTest extends EntityKernelTestBase {
$this->assertEqual($actual_occurrences, $expected_occurrences); $this->assertEqual($actual_occurrences, $expected_occurrences);
} }
/**
* Renders the same entity referenced from different places.
*/
public function testEntityReferenceRecursiveProtectionWithManyRenderedEntities() {
$formatter = 'entity_reference_entity_view';
$view_builder = $this->entityManager->getViewBuilder($this->entityType);
// Set the default view mode to use the 'entity_reference_entity_view'
// formatter.
entity_get_display($this->entityType, $this->bundle, 'default')
->setComponent($this->fieldName, [
'type' => $formatter,
])
->save();
$storage = $this->entityManager->getStorage($this->entityType);
/** @var \Drupal\Core\Entity\ContentEntityInterface $referenced_entity */
$referenced_entity = $storage->create(['name' => $this->randomMachineName()]);
$range = range(0, 30);
$referencing_entities = array_map(function () use ($storage, $referenced_entity) {
$referencing_entity = $storage->create([
'name' => $this->randomMachineName(),
$this->fieldName => $referenced_entity,
]);
$referencing_entity->save();
return $referencing_entity;
}, $range);
$build = $view_builder->viewMultiple($referencing_entities, 'default');
$output = $this->render($build);
// The title of entity_test entities is printed twice by default, so we have
// to multiply the formatter's recursive rendering protection limit by 2.
// Additionally, we have to take into account 2 additional occurrences of
// the entity title because we're rendering the full entity, not just the
// reference field.
$expected_occurrences = 30 * 2 + 2;
$actual_occurrences = substr_count($output, $referenced_entity->get('name')->value);
$this->assertEquals($expected_occurrences, $actual_occurrences);
}
/** /**
* Tests the label formatter. * Tests the label formatter.
*/ */

View file

@ -276,6 +276,9 @@ class MigrateFieldFormatterSettingsTest extends MigrateDrupal7TestBase {
$this->assertComponent('node.test_content_type.default', 'field_text_list', 'list_default', 'above', 10); $this->assertComponent('node.test_content_type.default', 'field_text_list', 'list_default', 'above', 10);
$this->assertComponent('node.test_content_type.default', 'field_integer_list', 'list_default', 'above', 11); $this->assertComponent('node.test_content_type.default', 'field_integer_list', 'list_default', 'above', 11);
$this->assertComponent('node.test_content_type.default', 'field_long_text', 'text_default', 'above', 12); $this->assertComponent('node.test_content_type.default', 'field_long_text', 'text_default', 'above', 12);
$this->assertComponent('node.test_content_type.default', 'field_node_entityreference', 'entity_reference_label', 'above', 15);
$this->assertComponent('node.test_content_type.default', 'field_user_entityreference', 'entity_reference_label', 'above', 16);
$this->assertComponent('node.test_content_type.default', 'field_term_entityreference', 'entity_reference_label', 'above', 17);
$this->assertComponentNotExists('node.test_content_type.default', 'field_term_reference'); $this->assertComponentNotExists('node.test_content_type.default', 'field_term_reference');
$this->assertComponentNotExists('node.test_content_type.default', 'field_text'); $this->assertComponentNotExists('node.test_content_type.default', 'field_text');

View file

@ -141,6 +141,9 @@ class MigrateFieldInstanceTest extends MigrateDrupal7TestBase {
$this->assertEntity('node.test_content_type.field_integer_list', 'Integer List', 'list_integer', FALSE); $this->assertEntity('node.test_content_type.field_integer_list', 'Integer List', 'list_integer', FALSE);
$this->assertEntity('node.test_content_type.field_long_text', 'Long text', 'text_with_summary', FALSE); $this->assertEntity('node.test_content_type.field_long_text', 'Long text', 'text_with_summary', FALSE);
$this->assertEntity('node.test_content_type.field_term_reference', 'Term Reference', 'entity_reference', FALSE); $this->assertEntity('node.test_content_type.field_term_reference', 'Term Reference', 'entity_reference', FALSE);
$this->assertEntity('node.test_content_type.field_node_entityreference', 'Node Entity Reference', 'entity_reference', FALSE);
$this->assertEntity('node.test_content_type.field_user_entityreference', 'User Entity Reference', 'entity_reference', FALSE);
$this->assertEntity('node.test_content_type.field_term_entityreference', 'Term Entity Reference', 'entity_reference', FALSE);
$this->assertEntity('node.test_content_type.field_text', 'Text', 'text', FALSE); $this->assertEntity('node.test_content_type.field_text', 'Text', 'text', FALSE);
$this->assertEntity('comment.comment_node_test_content_type.field_integer', 'Integer', 'integer', FALSE); $this->assertEntity('comment.comment_node_test_content_type.field_integer', 'Integer', 'integer', FALSE);
$this->assertEntity('user.user.field_file', 'File', 'file', FALSE); $this->assertEntity('user.user.field_file', 'File', 'file', FALSE);

View file

@ -125,6 +125,9 @@ class MigrateFieldInstanceWidgetSettingsTest extends MigrateDrupal7TestBase {
$this->assertComponent('node.test_content_type.default', 'field_long_text', 'text_textarea_with_summary', 13); $this->assertComponent('node.test_content_type.default', 'field_long_text', 'text_textarea_with_summary', 13);
$this->assertComponent('node.test_content_type.default', 'field_phone', 'telephone_default', 6); $this->assertComponent('node.test_content_type.default', 'field_phone', 'telephone_default', 6);
$this->assertComponent('node.test_content_type.default', 'field_term_reference', 'entity_reference_autocomplete', 14); $this->assertComponent('node.test_content_type.default', 'field_term_reference', 'entity_reference_autocomplete', 14);
$this->assertComponent('node.test_content_type.default', 'field_node_entityreference', 'entity_reference_autocomplete', 16);
$this->assertComponent('node.test_content_type.default', 'field_user_entityreference', 'options_buttons', 17);
$this->assertComponent('node.test_content_type.default', 'field_term_entityreference', 'entity_reference_autocomplete_tags', 18);
$this->assertComponent('node.test_content_type.default', 'field_text', 'text_textfield', 15); $this->assertComponent('node.test_content_type.default', 'field_text', 'text_textfield', 15);
$this->assertComponent('node.test_content_type.default', 'field_text_list', 'options_select', 11); $this->assertComponent('node.test_content_type.default', 'field_text_list', 'options_select', 11);

View file

@ -100,6 +100,9 @@ class MigrateFieldTest extends MigrateDrupal7TestBase {
$this->assertEntity('node.field_phone', 'telephone', FALSE, 1); $this->assertEntity('node.field_phone', 'telephone', FALSE, 1);
$this->assertEntity('node.field_date', 'datetime', FALSE, 1); $this->assertEntity('node.field_date', 'datetime', FALSE, 1);
$this->assertEntity('node.field_date_with_end_time', 'datetime', FALSE, 1); $this->assertEntity('node.field_date_with_end_time', 'datetime', FALSE, 1);
$this->assertEntity('node.field_node_entityreference', 'entity_reference', FALSE, -1);
$this->assertEntity('node.field_user_entityreference', 'entity_reference', FALSE, 1);
$this->assertEntity('node.field_term_entityreference', 'entity_reference', FALSE, -1);
// Assert that the taxonomy term reference fields are referencing the // Assert that the taxonomy term reference fields are referencing the
// correct entity type. // correct entity type.
@ -108,6 +111,15 @@ class MigrateFieldTest extends MigrateDrupal7TestBase {
$field = FieldStorageConfig::load('node.taxonomy_forums'); $field = FieldStorageConfig::load('node.taxonomy_forums');
$this->assertIdentical('taxonomy_term', $field->getSetting('target_type')); $this->assertIdentical('taxonomy_term', $field->getSetting('target_type'));
// Assert that the entityreference fields are referencing the correct
// entity type.
$field = FieldStorageConfig::load('node.field_node_entityreference');
$this->assertIdentical('node', $field->getSetting('target_type'));
$field = FieldStorageConfig::load('node.field_user_entityreference');
$this->assertIdentical('user', $field->getSetting('target_type'));
$field = FieldStorageConfig::load('node.field_term_entityreference');
$this->assertIdentical('taxonomy_term', $field->getSetting('target_type'));
// Validate that the source count and processed count match up. // Validate that the source count and processed count match up.
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
$migration = $this->getMigration('d7_field'); $migration = $this->getMigration('d7_field');

View file

@ -41,6 +41,24 @@ class FieldInstancePerViewModeTest extends MigrateSqlSourceTestBase {
], ],
]; ];
$tests[0]['source_data']['field_config'] = [
[
'id' => '2',
'field_name' => 'body',
'type' => 'text_with_summary',
'module' => 'text',
'active' => '1',
'storage_type' => 'field_sql_storage',
'storage_module' => 'field_sql_storage',
'storage_active' => '1',
'locked' => '0',
'data' => 'a:7:{s:12:"entity_types";a:1:{i:0;s:4:"node";}s:7:"indexes";a:1:{s:6:"format";a:1:{i:0;s:6:"format";}}s:8:"settings";a:0:{}s:12:"translatable";i:0;s:12:"foreign keys";a:1:{s:6:"format";a:2:{s:5:"table";s:13:"filter_format";s:7:"columns";a:1:{s:6:"format";s:6:"format";}}}s:7:"storage";a:4:{s:4:"type";s:17:"field_sql_storage";s:8:"settings";a:0:{}s:6:"module";s:17:"field_sql_storage";s:6:"active";s:1:"1";}s:2:"id";s:2:"25";}',
'cardinality' => '1',
'translatable' => '0',
'deleted' => '0',
],
];
// The expected results. // The expected results.
$tests[0]['expected_data'] = [ $tests[0]['expected_data'] = [
[ [
@ -48,7 +66,8 @@ class FieldInstancePerViewModeTest extends MigrateSqlSourceTestBase {
'bundle' => 'page', 'bundle' => 'page',
'field_name' => 'body', 'field_name' => 'body',
'label' => 'hidden', 'label' => 'hidden',
'type' => 'text_default', 'type' => 'text_with_summary',
'formatter_type' => 'text_default',
'settings' => [], 'settings' => [],
'module' => 'text', 'module' => 'text',
'weight' => 0, 'weight' => 0,
@ -59,7 +78,8 @@ class FieldInstancePerViewModeTest extends MigrateSqlSourceTestBase {
'bundle' => 'page', 'bundle' => 'page',
'field_name' => 'body', 'field_name' => 'body',
'label' => 'hidden', 'label' => 'hidden',
'type' => 'text_summary_or_trimmed', 'type' => 'text_with_summary',
'formatter_type' => 'text_summary_or_trimmed',
'settings' => [ 'settings' => [
'trim_length' => 600, 'trim_length' => 600,
], ],

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\field\Kernel\String;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
use Drupal\Component\Uuid\Uuid;
/**
* Tests the UUID field.
*
* @group field
*/
class UuidItemTest extends FieldKernelTestBase {
/**
* Tests 'uuid' random values.
*/
public function testSampleValue() {
$entity = EntityTest::create([]);
$entity->save();
$uuid_field = $entity->get('uuid');
// Test the generateSampleValue() method.
$uuid_field->generateSampleItems();
$this->assertTrue(Uuid::isValid($uuid_field->value));
}
}

View file

@ -0,0 +1,59 @@
<?php
namespace Drupal\Tests\field\Kernel;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests the serialization of an object.
*
* @group field
*/
class TestObjectItemTest extends FieldKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('field_test');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create a 'test_field' field and storage for validation.
FieldStorageConfig::create(array(
'field_name' => 'field_test',
'entity_type' => 'entity_test',
'type' => 'test_object_field',
))->save();
FieldConfig::create([
'entity_type' => 'entity_test',
'field_name' => 'field_test',
'bundle' => 'entity_test',
])->save();
}
/**
* Tests the serialization of a field type that has an object.
*/
public function testTestObjectItem() {
$object = new \stdClass();
$object->foo = 'bar';
$entity = EntityTest::create();
$entity->field_test->value = $object;
$entity->save();
// Verify that the entity has been created properly.
$id = $entity->id();
$entity = EntityTest::load($id);
$this->assertTrue($entity->field_test->value instanceof \stdClass);
$this->assertEquals($object, $entity->field_test->value);
}
}

View file

@ -28,7 +28,7 @@ class FieldInstanceSettingsTest extends MigrateTestCase {
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
$value = $plugin->transform([[], ['type' => 'image_image']], $executable, $row, 'foo'); $value = $plugin->transform([[], ['type' => 'image_image'], []], $executable, $row, 'foo');
$this->assertInternalType('array', $value['default_image']); $this->assertInternalType('array', $value['default_image']);
$this->assertSame('', $value['default_image']['alt']); $this->assertSame('', $value['default_image']['alt']);
$this->assertSame('', $value['default_image']['title']); $this->assertSame('', $value['default_image']['title']);

View file

@ -13,6 +13,9 @@ source:
# configuration in this migration's process pipeline as an example. # configuration in this migration's process pipeline as an example.
source_base_path: '' source_base_path: ''
process: process:
# If you are using both this migration and d6_user_picture_file in a custom
# migration and executing migrations incrementally, it is recommended that
# you remove the fid mapping here to avoid potential ID conflicts.
fid: fid fid: fid
filename: filename filename: filename
source_full_path: source_full_path:

View file

@ -20,4 +20,7 @@ process:
cardinality: 'constants/cardinality' cardinality: 'constants/cardinality'
'settings/display_field': 'constants/display_field' 'settings/display_field': 'constants/display_field'
destination: destination:
plugin: md_entity:field_storage_config plugin: entity:field_storage_config
dependencies:
module:
- file

View file

@ -13,6 +13,8 @@ source:
# configuration in this migration's process pipeline as an example. # configuration in this migration's process pipeline as an example.
source_base_path: '' source_base_path: ''
process: process:
# If you are using this file to build a custom migration consider removing
# the fid field to allow incremental migrations.
fid: fid fid: fid
filename: filename filename: filename
source_full_path: source_full_path:

View file

@ -5,7 +5,6 @@ namespace Drupal\file\Plugin\migrate\process\d6;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\Plugin\MigrateProcessInterface; use Drupal\migrate\Plugin\MigrateProcessInterface;
use Drupal\migrate\ProcessPluginBase; use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row; use Drupal\migrate\Row;
@ -76,18 +75,7 @@ class CckFile extends ProcessPluginBase implements ContainerFactoryPluginInterfa
// some reason -- file migration is notoriously brittle -- and we do NOT // some reason -- file migration is notoriously brittle -- and we do NOT
// want to send invalid file references into the field system (it causes // want to send invalid file references into the field system (it causes
// fatals), so return an empty item instead. // fatals), so return an empty item instead.
try { if ($fid = $this->migrationPlugin->transform($value['fid'], $migrate_executable, $row, $destination_property)) {
$fid = $this->migrationPlugin->transform($value['fid'], $migrate_executable, $row, $destination_property);
}
// If the migration plugin completely fails its lookup process, it will
// throw a MigrateSkipRowException. It shouldn't, but that is being dealt
// with at https://www.drupal.org/node/2487568. Until that lands, return
// an empty item.
catch (MigrateSkipRowException $e) {
return [];
}
if ($fid) {
return [ return [
'target_id' => $fid, 'target_id' => $fid,
'display' => $value['list'], 'display' => $value['list'],

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Block;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Block\BlockResourceTestBase;
/**
* @group hal
*/
class BlockHalJsonAnonTest extends BlockResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,46 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Block;
use Drupal\Tests\hal\Functional\HalJsonBasicAuthWorkaroundFor2805281Trait;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Block\BlockResourceTestBase;
/**
* @group hal
*/
class BlockHalJsonBasicAuthTest extends BlockResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal', 'basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use HalJsonBasicAuthWorkaroundFor2805281Trait {
HalJsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Block;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Block\BlockResourceTestBase;
/**
* @group hal
*/
class BlockHalJsonCookieTest extends BlockResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Comment;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group hal
*/
class CommentHalJsonAnonTest extends CommentHalJsonTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*
* Anononymous users cannot edit their own comments.
*
* @see \Drupal\comment\CommentAccessControlHandler::checkAccess
*
* Therefore we grant them the 'administer comments' permission for the
* purpose of this test. Then they are able to edit their own comments, but
* some fields are still not editable, even with that permission.
*
* @see ::setUpAuthorization
*/
protected static $patchProtectedFieldNames = [
'changed',
'thread',
'entity_type',
'field_name',
'entity_id',
];
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Comment;
use Drupal\Tests\hal\Functional\HalJsonBasicAuthWorkaroundFor2805281Trait;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class CommentHalJsonBasicAuthTest extends CommentHalJsonTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use HalJsonBasicAuthWorkaroundFor2805281Trait {
HalJsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Comment;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class CommentHalJsonCookieTest extends CommentHalJsonTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,144 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Comment;
use Drupal\Core\Cache\Cache;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\EntityResource\Comment\CommentResourceTestBase;
use Drupal\user\Entity\User;
abstract class CommentHalJsonTestBase extends CommentResourceTestBase {
use HalEntityNormalizationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*
* The HAL+JSON format causes different PATCH-protected fields. For some
* reason, the 'pid' and 'homepage' fields are NOT PATCH-protected, even
* though they are for non-HAL+JSON serializations.
*
* @todo fix in https://www.drupal.org/node/2824271
*/
protected static $patchProtectedFieldNames = [
'created',
'changed',
'status',
'thread',
'entity_type',
'field_name',
'entity_id',
'uid',
];
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$default_normalization = parent::getExpectedNormalizedEntity();
$normalization = $this->applyHalFieldNormalization($default_normalization);
// Because \Drupal\comment\Entity\Comment::getOwner() generates an in-memory
// User entity without a UUID, we cannot use it.
$author = User::load($this->entity->getOwnerId());
$commented_entity = EntityTest::load(1);
return $normalization + [
'_links' => [
'self' => [
'href' => $this->baseUrl . '/comment/1?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/comment/comment',
],
$this->baseUrl . '/rest/relation/comment/comment/entity_id' => [
[
'href' => $this->baseUrl . '/entity_test/1?_format=hal_json',
],
],
$this->baseUrl . '/rest/relation/comment/comment/uid' => [
[
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
'lang' => 'en',
],
],
],
'_embedded' => [
$this->baseUrl . '/rest/relation/comment/comment/entity_id' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . '/entity_test/1?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/entity_test/bar',
],
],
'uuid' => [
['value' => $commented_entity->uuid()]
],
],
],
$this->baseUrl . '/rest/relation/comment/comment/uid' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
'uuid' => [
['value' => $author->uuid()]
],
'lang' => 'en',
],
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/comment/comment',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
// The 'url.site' cache context is added for '_links' in the response.
return Cache::mergeTags(parent::getExpectedCacheContexts(), ['url.site']);
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\ConfigTest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\ConfigTest\ConfigTestResourceTestBase;
/**
* @group hal
*/
class ConfigTestHalJsonAnonTest extends ConfigTestResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,46 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\ConfigTest;
use Drupal\Tests\hal\Functional\HalJsonBasicAuthWorkaroundFor2805281Trait;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\ConfigTest\ConfigTestResourceTestBase;
/**
* @group hal
*/
class ConfigTestHalJsonBasicAuthTest extends ConfigTestResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal', 'basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use HalJsonBasicAuthWorkaroundFor2805281Trait {
HalJsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\ConfigTest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\ConfigTest\ConfigTestResourceTestBase;
/**
* @group hal
*/
class ConfigTestHalJsonCookieTest extends ConfigTestResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,105 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\EntityTest;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityTest\EntityTestResourceTestBase;
use Drupal\user\Entity\User;
/**
* @group hal
*/
class EntityTestHalJsonAnonTest extends EntityTestResourceTestBase {
use HalEntityNormalizationTrait;
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$default_normalization = parent::getExpectedNormalizedEntity();
$normalization = $this->applyHalFieldNormalization($default_normalization);
$author = User::load(0);
return $normalization + [
'_links' => [
'self' => [
'href' => $this->baseUrl . '/entity_test/1?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/entity_test/entity_test',
],
$this->baseUrl . '/rest/relation/entity_test/entity_test/user_id' => [
[
'href' => $this->baseUrl . '/user/0?_format=hal_json',
'lang' => 'en',
],
],
],
'_embedded' => [
$this->baseUrl . '/rest/relation/entity_test/entity_test/user_id' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/0?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
'uuid' => [
['value' => $author->uuid()]
],
'lang' => 'en',
],
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/entity_test/entity_test',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
// The 'url.site' cache context is added for '_links' in the response.
return Cache::mergeTags(parent::getExpectedCacheContexts(), ['url.site']);
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\EntityTest;
use Drupal\Tests\hal\Functional\HalJsonBasicAuthWorkaroundFor2805281Trait;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class EntityTestHalJsonBasicAuthTest extends EntityTestHalJsonAnonTest {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use HalJsonBasicAuthWorkaroundFor2805281Trait {
HalJsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\EntityTest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class EntityTestHalJsonCookieTest extends EntityTestHalJsonAnonTest {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,130 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Url;
use GuzzleHttp\RequestOptions;
/**
* Trait for EntityResourceTestBase subclasses testing formats using HAL.
*/
trait HalEntityNormalizationTrait {
/**
* Applies the HAL entity field normalization to an entity normalization.
*
* The HAL normalization:
* - adds a 'lang' attribute to every translatable field
* - omits reference fields, since references are stored in _links & _embedded
* - omits empty fields (fields without value)
*
* @param array $normalization
* An entity normalization.
*
* @return array
* The updated entity normalization.
*/
protected function applyHalFieldNormalization(array $normalization) {
if (!$this->entity instanceof FieldableEntityInterface) {
throw new \LogicException('This trait should only be used for fieldable entity types.');
}
// In the HAL normalization, all translatable fields get a 'lang' attribute.
$translatable_non_reference_fields = array_keys(array_filter($this->entity->getTranslatableFields(), function (FieldItemListInterface $field) {
return !$field instanceof EntityReferenceFieldItemListInterface;
}));
foreach ($translatable_non_reference_fields as $field_name) {
if (isset($normalization[$field_name])) {
$normalization[$field_name][0]['lang'] = 'en';
}
}
// In the HAL normalization, reference fields are omitted, except for the
// bundle field.
$bundle_key = $this->entity->getEntityType()->getKey('bundle');
$reference_fields = array_keys(array_filter($this->entity->getFields(), function (FieldItemListInterface $field) use ($bundle_key) {
return $field instanceof EntityReferenceFieldItemListInterface && $field->getName() !== $bundle_key;
}));
foreach ($reference_fields as $field_name) {
unset($normalization[$field_name]);
}
// In the HAL normalization, the bundle field omits the 'target_type' and
// 'target_uuid' properties, because it's encoded in the '_links' section.
if ($bundle_key) {
unset($normalization[$bundle_key][0]['target_type']);
unset($normalization[$bundle_key][0]['target_uuid']);
}
// In the HAL normalization, empty fields are omitted.
$empty_fields = array_keys(array_filter($this->entity->getFields(), function (FieldItemListInterface $field) {
return $field->isEmpty();
}));
foreach ($empty_fields as $field_name) {
unset($normalization[$field_name]);
}
return $normalization;
}
/**
* {@inheritdoc}
*/
protected function removeFieldsFromNormalization(array $normalization, $field_names) {
$normalization = parent::removeFieldsFromNormalization($normalization, $field_names);
foreach ($field_names as $field_name) {
$relation_url = Url::fromUri('base:rest/relation/' . static::$entityTypeId . '/' . $this->entity->bundle() . '/' . $field_name)
->setAbsolute(TRUE)
->toString();
$normalization['_links'] = array_diff_key($normalization['_links'], [$relation_url => TRUE]);
if (isset($normalization['_embedded'])) {
$normalization['_embedded'] = array_diff_key($normalization['_embedded'], [$relation_url => TRUE]);
}
}
return array_diff_key($normalization, array_flip($field_names));
}
/**
* {@inheritdoc}
*/
protected function assertNormalizationEdgeCases($method, Url $url, array $request_options) {
// \Drupal\serialization\Normalizer\EntityNormalizer::denormalize(): entity
// types with bundles MUST send their bundle field to be denormalizable.
if ($this->entity->getEntityType()->hasKey('bundle')) {
$normalization = $this->getNormalizedPostEntity();
// @todo Uncomment this in https://www.drupal.org/node/2824827.
// @codingStandardsIgnoreStart
/*
$normalization['_links']['type'] = Url::fromUri('base:rest/type/' . static::$entityTypeId . '/bad_bundle_name');
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 400 when incorrect entity type bundle is specified.
$response = $this->request($method, $url, $request_options);
// @todo Uncomment, remove next 3 in https://www.drupal.org/node/2813853.
// $this->assertResourceErrorResponse(400, 'The type link relation must be specified.', $response);
$this->assertSame(400, $response->getStatusCode());
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
$this->assertSame($this->serializer->encode(['error' => 'The type link relation must be specified.'], static::$format), (string) $response->getBody());
*/
// @codingStandardsIgnoreEnd
unset($normalization['_links']['type']);
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 400 when no entity type bundle is specified.
$response = $this->request($method, $url, $request_options);
// @todo Uncomment, remove next 3 in https://www.drupal.org/node/2813853.
// $this->assertResourceErrorResponse(400, 'The type link relation must be specified.', $response);
$this->assertSame(400, $response->getStatusCode());
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
$this->assertSame($this->serializer->encode(['error' => 'The type link relation must be specified.'], static::$format), (string) $response->getBody());
}
}
}

View file

@ -0,0 +1,137 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Node;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase;
use Drupal\user\Entity\User;
/**
* @group hal
*/
class NodeHalJsonAnonTest extends NodeResourceTestBase {
use HalEntityNormalizationTrait;
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'created',
'changed',
'promote',
'sticky',
'revision_timestamp',
'revision_uid',
];
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$default_normalization = parent::getExpectedNormalizedEntity();
$normalization = $this->applyHalFieldNormalization($default_normalization);
$author = User::load($this->entity->getOwnerId());
return $normalization + [
'_links' => [
'self' => [
'href' => $this->baseUrl . '/node/1?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/node/camelids',
],
$this->baseUrl . '/rest/relation/node/camelids/uid' => [
[
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
'lang' => 'en',
],
],
$this->baseUrl . '/rest/relation/node/camelids/revision_uid' => [
[
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
],
],
],
'_embedded' => [
$this->baseUrl . '/rest/relation/node/camelids/uid' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
'uuid' => [
['value' => $author->uuid()]
],
'lang' => 'en',
],
],
$this->baseUrl . '/rest/relation/node/camelids/revision_uid' => [
[
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/' . $author->id() . '?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
'uuid' => [
['value' => $author->uuid()]
],
],
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/node/camelids',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
// The 'url.site' cache context is added for '_links' in the response.
return Cache::mergeContexts(parent::getExpectedCacheContexts(), ['url.site']);
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Node;
use Drupal\Tests\hal\Functional\HalJsonBasicAuthWorkaroundFor2805281Trait;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class NodeHalJsonBasicAuthTest extends NodeHalJsonAnonTest {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use HalJsonBasicAuthWorkaroundFor2805281Trait {
HalJsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Node;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class NodeHalJsonCookieTest extends NodeHalJsonAnonTest {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Role;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Role\RoleResourceTestBase;
/**
* @group hal
*/
class RoleHalJsonAnonTest extends RoleResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,46 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Role;
use Drupal\Tests\hal\Functional\HalJsonBasicAuthWorkaroundFor2805281Trait;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Role\RoleResourceTestBase;
/**
* @group hal
*/
class RoleHalJsonBasicAuthTest extends RoleResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal', 'basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use HalJsonBasicAuthWorkaroundFor2805281Trait {
HalJsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Role;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Role\RoleResourceTestBase;
/**
* @group hal
*/
class RoleHalJsonCookieTest extends RoleResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,79 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Term;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Term\TermResourceTestBase;
/**
* @group hal
*/
class TermHalJsonAnonTest extends TermResourceTestBase {
use HalEntityNormalizationTrait;
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$default_normalization = parent::getExpectedNormalizedEntity();
$normalization = $this->applyHalFieldNormalization($default_normalization);
return $normalization + [
'_links' => [
'self' => [
'href' => $this->baseUrl . '/taxonomy/term/1?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/taxonomy_term/camelids',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
// The 'url.site' cache context is added for '_links' in the response.
return Cache::mergeContexts(parent::getExpectedCacheContexts(), ['url.site']);
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Term;
use Drupal\Tests\hal\Functional\HalJsonBasicAuthWorkaroundFor2805281Trait;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class TermHalJsonBasicAuthTest extends TermHalJsonAnonTest {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use HalJsonBasicAuthWorkaroundFor2805281Trait {
HalJsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Term;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class TermHalJsonCookieTest extends TermHalJsonAnonTest {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,79 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\User;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\User\UserResourceTestBase;
/**
* @group hal
*/
class UserHalJsonAnonTest extends UserResourceTestBase {
use HalEntityNormalizationTrait;
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$default_normalization = parent::getExpectedNormalizedEntity();
$normalization = $this->applyHalFieldNormalization($default_normalization);
return $normalization + [
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/3?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
// The 'url.site' cache context is added for '_links' in the response.
return Cache::mergeContexts(parent::getExpectedCacheContexts(), ['url.site']);
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\User;
use Drupal\Tests\hal\Functional\HalJsonBasicAuthWorkaroundFor2805281Trait;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class UserHalJsonBasicAuthTest extends UserHalJsonAnonTest {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use HalJsonBasicAuthWorkaroundFor2805281Trait {
HalJsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\User;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class UserHalJsonCookieTest extends UserHalJsonAnonTest {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,42 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Vocabulary;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Vocabulary\VocabularyResourceTestBase;
/**
* @group hal
*/
class VocabularyHalJsonAnonTest extends VocabularyResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* @todo Remove this override in https://www.drupal.org/node/2805281.
*/
public function testGet() {
$this->markTestSkipped();
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Vocabulary;
use Drupal\Tests\hal\Functional\HalJsonBasicAuthWorkaroundFor2805281Trait;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Vocabulary\VocabularyResourceTestBase;
/**
* @group hal
*/
class VocabularyHalJsonBasicAuthTest extends VocabularyResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal', 'basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use HalJsonBasicAuthWorkaroundFor2805281Trait {
HalJsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\hal\Functional\EntityResource\Vocabulary;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\Vocabulary\VocabularyResourceTestBase;
/**
* @group hal
*/
class VocabularyHalJsonCookieTest extends VocabularyResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,26 @@
<?php
namespace Drupal\Tests\hal\Functional;
use Psr\Http\Message\ResponseInterface;
trait HalJsonBasicAuthWorkaroundFor2805281Trait {
/**
* {@inheritdoc}
*
* Note how the response claims it contains a application/hal+json body, but
* in reality it contains a text/plain body! Also, the correct error MIME type
* is application/json.
*
* @todo Fix in https://www.drupal.org/node/2805281: remove this trait.
*/
protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
$this->assertSame(401, $response->getStatusCode());
// @todo this works fine locally, but on testbot it comes back with
// 'text/plain; charset=UTF-8'. WTF.
// $this->assertSame(['application/hal+json'], $response->getHeader('Content-Type'));
$this->assertSame('No authentication credentials provided.', (string) $response->getBody());
}
}

View file

@ -7,7 +7,7 @@ use Drupal\field\Entity\FieldConfig;
use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Exception\UnexpectedValueException;
/** /**
* Tests that entities can be denormalized from HAL. * Tests HAL denormalization edge cases for EntityResource.
* *
* @group hal * @group hal
*/ */
@ -110,98 +110,4 @@ class DenormalizeTest extends NormalizerTestBase {
$this->assertEqual($entity->field_test_text->count(), 0); $this->assertEqual($entity->field_test_text->count(), 0);
} }
/**
* Test that non-reference fields can be denormalized.
*/
public function testBasicFieldDenormalization() {
$data = array(
'_links' => array(
'type' => array(
'href' => Url::fromUri('base:rest/type/entity_test/entity_test', array('absolute' => TRUE))->toString(),
),
),
'uuid' => array(
array(
'value' => 'e5c9fb96-3acf-4a8d-9417-23de1b6c3311',
),
),
'field_test_text' => array(
array(
'value' => $this->randomMachineName(),
'format' => 'full_html',
),
),
'field_test_translatable_text' => array(
array(
'value' => $this->randomMachineName(),
'format' => 'full_html',
),
array(
'value' => $this->randomMachineName(),
'format' => 'filtered_html',
),
array(
'value' => $this->randomMachineName(),
'format' => 'filtered_html',
'lang' => 'de',
),
array(
'value' => $this->randomMachineName(),
'format' => 'full_html',
'lang' => 'de',
),
),
);
$expected_value_default = array(
array (
'value' => $data['field_test_translatable_text'][0]['value'],
'format' => 'full_html',
),
array (
'value' => $data['field_test_translatable_text'][1]['value'],
'format' => 'filtered_html',
),
);
$expected_value_de = array(
array (
'value' => $data['field_test_translatable_text'][2]['value'],
'format' => 'filtered_html',
),
array (
'value' => $data['field_test_translatable_text'][3]['value'],
'format' => 'full_html',
),
);
$denormalized = $this->serializer->denormalize($data, $this->entityClass, $this->format);
$this->assertEqual($data['uuid'], $denormalized->get('uuid')->getValue(), 'A preset value (e.g. UUID) is overridden by incoming data.');
$this->assertEqual($data['field_test_text'], $denormalized->get('field_test_text')->getValue(), 'A basic text field is denormalized.');
$this->assertEqual($expected_value_default, $denormalized->get('field_test_translatable_text')->getValue(), 'Values in the default language are properly handled for a translatable field.');
$this->assertEqual($expected_value_de, $denormalized->getTranslation('de')->get('field_test_translatable_text')->getValue(), 'Values in a translation language are properly handled for a translatable field.');
}
/**
* Verifies that the denormalized entity is correct in the PATCH context.
*/
public function testPatchDenormalization() {
$data = array(
'_links' => array(
'type' => array(
'href' => Url::fromUri('base:rest/type/entity_test/entity_test', array('absolute' => TRUE))->toString(),
),
),
'field_test_text' => array(
array(
'value' => $this->randomMachineName(),
'format' => 'full_html',
),
),
);
$denormalized = $this->serializer->denormalize($data, $this->entityClass, $this->format, array('request_method' => 'patch'));
// Check that the one field got populated as expected.
$this->assertEqual($data['field_test_text'], $denormalized->get('field_test_text')->getValue());
// Check the custom property that contains the list of fields to merge.
$this->assertEqual($denormalized->_restSubmittedFields, ['field_test_text']);
}
} }

View file

@ -1,206 +0,0 @@
<?php
namespace Drupal\Tests\hal\Kernel;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\comment\Entity\Comment;
use Drupal\node\Entity\Node;
use Drupal\user\Entity\User;
use Drupal\node\Entity\NodeType;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests that nodes and terms are correctly normalized and denormalized.
*
* @group hal
*/
class EntityNormalizeTest extends NormalizerTestBase {
use CommentTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node', 'taxonomy', 'comment');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
\Drupal::service('router.builder')->rebuild();
$this->installSchema('system', array('sequences'));
$this->installSchema('comment', array('comment_entity_statistics'));
$this->installEntitySchema('taxonomy_term');
$this->installConfig(['node', 'comment']);
}
/**
* Tests the normalization of nodes.
*/
public function testNode() {
$node_type = NodeType::create(['type' => 'example_type']);
$node_type->save();
$user = User::create(['name' => $this->randomMachineName()]);
$user->save();
// Add comment type.
$this->container->get('entity.manager')->getStorage('comment_type')->create(array(
'id' => 'comment',
'label' => 'comment',
'target_entity_type_id' => 'node',
))->save();
$this->addDefaultCommentField('node', 'example_type');
$node = Node::create([
'title' => $this->randomMachineName(),
'uid' => $user->id(),
'type' => $node_type->id(),
'status' => NODE_PUBLISHED,
'promote' => 1,
'sticky' => 0,
'body' => [
'value' => $this->randomMachineName(),
'format' => $this->randomMachineName()
],
'revision_log' => $this->randomString(),
]);
$node->save();
$original_values = $node->toArray();
$normalized = $this->serializer->normalize($node, $this->format);
/** @var \Drupal\node\NodeInterface $denormalized_node */
$denormalized_node = $this->serializer->denormalize($normalized, 'Drupal\node\Entity\Node', $this->format);
$this->assertEqual($original_values, $denormalized_node->toArray(), 'Node values are restored after normalizing and denormalizing.');
}
/**
* Tests the normalization of terms.
*/
public function testTerm() {
$vocabulary = Vocabulary::create(['vid' => 'example_vocabulary']);
$vocabulary->save();
$account = User::create(['name' => $this->randomMachineName()]);
$account->save();
// @todo Until https://www.drupal.org/node/2327935 is fixed, if no parent is
// set, the test fails because target_id => 0 is reserialized to NULL.
$term_parent = Term::create([
'name' => $this->randomMachineName(),
'vid' => $vocabulary->id(),
]);
$term_parent->save();
$term = Term::create([
'name' => $this->randomMachineName(),
'vid' => $vocabulary->id(),
'description' => array(
'value' => $this->randomMachineName(),
'format' => $this->randomMachineName(),
),
'parent' => $term_parent->id(),
]);
$term->save();
$original_values = $term->toArray();
$normalized = $this->serializer->normalize($term, $this->format, ['account' => $account]);
/** @var \Drupal\taxonomy\TermInterface $denormalized_term */
$denormalized_term = $this->serializer->denormalize($normalized, 'Drupal\taxonomy\Entity\Term', $this->format, ['account' => $account]);
$this->assertEqual($original_values, $denormalized_term->toArray(), 'Term values are restored after normalizing and denormalizing.');
}
/**
* Tests the normalization of comments.
*/
public function testComment() {
$node_type = NodeType::create(['type' => 'example_type']);
$node_type->save();
$account = User::create(['name' => $this->randomMachineName()]);
$account->save();
// Add comment type.
$this->container->get('entity.manager')->getStorage('comment_type')->create(array(
'id' => 'comment',
'label' => 'comment',
'target_entity_type_id' => 'node',
))->save();
$this->addDefaultCommentField('node', 'example_type');
$node = Node::create([
'title' => $this->randomMachineName(),
'uid' => $account->id(),
'type' => $node_type->id(),
'status' => NODE_PUBLISHED,
'promote' => 1,
'sticky' => 0,
'body' => [[
'value' => $this->randomMachineName(),
'format' => $this->randomMachineName()
]],
]);
$node->save();
$parent_comment = Comment::create(array(
'uid' => $account->id(),
'subject' => $this->randomMachineName(),
'comment_body' => [
'value' => $this->randomMachineName(),
'format' => NULL,
],
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
));
$parent_comment->save();
$comment = Comment::create(array(
'uid' => $account->id(),
'subject' => $this->randomMachineName(),
'comment_body' => [
'value' => $this->randomMachineName(),
'format' => NULL,
],
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'pid' => $parent_comment->id(),
'mail' => 'dries@drupal.org',
'homepage' => 'http://buytaert.net',
));
$comment->save();
$original_values = $comment->toArray();
// Hostname will always be denied view access.
// No value will exist for name as this is only for anonymous users.
unset($original_values['hostname'], $original_values['name']);
$normalized = $this->serializer->normalize($comment, $this->format, ['account' => $account]);
// Assert that the hostname field does not appear at all in the normalized
// data.
$this->assertFalse(array_key_exists('hostname', $normalized), 'Hostname was not found in normalized comment data.');
/** @var \Drupal\comment\CommentInterface $denormalized_comment */
$denormalized_comment = $this->serializer->denormalize($normalized, 'Drupal\comment\Entity\Comment', $this->format, ['account' => $account]);
// Before comparing, unset values that are expected to differ.
$denormalized_comment_values = $denormalized_comment->toArray();
unset($denormalized_comment_values['hostname'], $denormalized_comment_values['name']);
$this->assertEqual($original_values, $denormalized_comment_values, 'The expected comment values are restored after normalizing and denormalizing.');
}
}

View file

@ -7,7 +7,7 @@ use Drupal\Core\Url;
use Drupal\entity_test\Entity\EntityTest; use Drupal\entity_test\Entity\EntityTest;
/** /**
* Tests that entities can be normalized in HAL. * Tests HAL normalization edge cases for EntityResource.
* *
* @group hal * @group hal
*/ */

View file

@ -0,0 +1,34 @@
id: d6_language_negotiation_settings
label: Language negotiation settings
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- language_negotiation
process:
session/parameter:
plugin: default_value
default_value: 'language'
selected_langcode:
plugin: default_value
default_value: 'site_default'
url/source:
plugin: static_map
source: language_negotiation
default_value: path_prefix
map:
# LANGUAGE_NEGOTIATION_NONE = 0
# LANGUAGE_NEGOTIATION_PATH_DEFAULT = 1
# LANGUAGE_NEGOTIATION_PATH = 2
# LANGUAGE_NEGOTIATION_DOMAIN = 3
0: path_prefix
1: path_prefix
2: path_prefix
3: domain
destination:
plugin: config
config_name: language.negotiation
migration_dependencies:
required:
- language

View file

@ -0,0 +1,52 @@
id: d6_language_types
label: Language types
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- language_negotiation
process:
all:
plugin: default_value
default_value:
- 'language_interface'
- 'language_content'
- 'language_url'
configurable:
plugin: default_value
default_value:
- 'language_interface'
negotiation/language_content/enabled:
plugin: default_value
default_value:
'language-interface': 0
negotiation/language_url/enabled:
plugin: default_value
default_value:
'language-url': 0
'language-url-fallback': 1
negotiation/language_interface/enabled:
plugin: static_map
source: language_negotiation
map:
# LANGUAGE_NEGOTIATION_NONE = 0
# LANGUAGE_NEGOTIATION_PATH_DEFAULT = 1
# LANGUAGE_NEGOTIATION_PATH = 2
# LANGUAGE_NEGOTIATION_DOMAIN = 3
0:
'language-selected': 0
1:
'language-url': 0
'language-selected': 1
2:
'language-url': 0
'language-user': 1
'language-browser': 2
'language-selected': 3
3:
'language-url': 0
'language-selected': 1
destination:
plugin: config
config_name: language.types

View file

@ -8,8 +8,25 @@ source:
- locale_language_negotiation_session_param - locale_language_negotiation_session_param
- locale_language_negotiation_url_part - locale_language_negotiation_url_part
process: process:
'session/parameter': locale_language_negotiation_session_param session/parameter:
'url/source': locale_language_negotiation_url_part plugin: default_value
source: locale_language_negotiation_session_param
default_value: 'language'
selected_langcode:
plugin: default_value
default_value: 'site_default'
url/source:
plugin: static_map
source: locale_language_negotiation_url_part
default_value: path_prefix
map:
# LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX = 0
# LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN = 1
0: path_prefix
1: domain
destination: destination:
plugin: config plugin: config
config_name: language.negotiation config_name: language.negotiation
migration_dependencies:
required:
- language

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