diff --git a/composer.json b/composer.json index b9536cf..97c131f 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "wikimedia/composer-merge-plugin": "^1.4" }, "replace": { - "drupal/core": "^8.4" + "drupal/core": "^8.5" }, "minimum-stability": "dev", "prefer-stable": true, diff --git a/composer.lock b/composer.lock index 21fb0fd..835ea15 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "bec46eaaa9fa07a4cbd8c84302f0d275", + "content-hash": "97de1708c79f6205a295cfc9808c0c72", "packages": [ { "name": "asm89/stack-cors", diff --git a/core/CHANGELOG.txt b/core/CHANGELOG.txt index 4caa24c..10e824f 100644 --- a/core/CHANGELOG.txt +++ b/core/CHANGELOG.txt @@ -1,412 +1,3 @@ -Drupal 8.4.x, xxxx-xx-xx ------------------------- -### Drush users: Update to Drush 8.1.12 - -[Versions of Drush earlier than 8.1.12 will not work with Drupal 8.4.x](https://www.drupal.org/node/2874827). -Update Drush to 8.1.12 before using it to update to Drupal core 8.4.x or you - will encounter fatal errors. - -### Updated browser requirements: Internet Explorer 9 and 10 no longer supported - -In April 2017, Microsoft discontinued all support for Internet Explorer 9 and -10. Therefore, [Drupal 8.4 has as well](https://www.drupal.org/node/2897971). -Drupal 8.4 still mostly works in these browser versions, but bugs that affect -them only will no longer be fixed, and existing workarounds for them will -be removed beginning in Drupal 8.5. - -Additionally, Drupal 8's [browser requirements documentation page](https://www.drupal.org/docs/8/system-requirements/browser-requirements) -currently lists incorrect information regarding very outdated browser versions -such as Safari 5 and Firefox 5. [Clarifications to the browser policy and documentation](https://www.drupal.org/node/2390621) -are underway and we hope to finalize it before 8.4.0-rc1. - -### Known Issues - -* Drupal 8.4.0-alpha1 includes major version updates for two dependencies: - Symfony 3.2 and jQuery 3. Both updates may introduce backwards compatibility - issues for some sites or modules, so test carefully. - For more information, see the "Third-party library updates" section below. -* [Modal tour tips provided by the Tour module are not displayed correctly](https://www.drupal.org/node/2898808) - because the third-party Joyride library has an incompatibility with jQuery 3. - Tour tips are no longer centered and may be displayed entirely off-screen for - many screen sizes. Work is underway on an upstream bug fix. -* Some sites that have files with 0 recorded usages may encounter - [validation errors when saving content referencing these files](https://www.drupal.org/node/2896480). - If your site's users report errors when saving content, you can - [set the `file.settings.make_unused_managed_files_temporary` setting to `true`](https://www.drupal.org/node/2891902), - but make sure you also set "Delete orphaned files" to "Never" on - `/admin/config/media/file-system` - to avoid permanent deletion of the affected files. - -### Important fixes since 8.3.x - -Translators should take note of several [string additions and changes since the last release](https://www.drupal.org/project/issues/search/drupal?project_issue_followers=&issue_tags_op=%3D&issue_tags=String+change+in+8.4.0). - -#### File usage tracking - -Drupal 8 has several longstanding [file usage tracking -bugs](https://www.drupal.org/node/2821423). To prevent further data loss, -Drupal 8.4 has [disabled the automatic deletion of files with no known remaining usages](https://www.drupal.org/node/2801777). -This will result of the accumulation of unused files on sites, but ensures that -files erroneously reporting 0 usages are not deleted while in use. -[The change record explains how sites can opt back into marking files temporary](https://www.drupal.org/node/2891902). -If you choose to enable the setting, you can also set "Delete orphaned files" -to "Never" on `/admin/config/media/file-system` to avoid permanent deletion of -the affected files. - -While the files will no longer be deleted by default, file usage is still not -tracked correctly in several scenarios, regardless of the setting. Discussion -on [how to evolve the file usage tracking system](https://www.drupal.org/node/2821423) -is underway. - -#### Configuration export sorting - -* [#2361539: Config export key order is not predictable for sequences, add orderby property to config schema](https://www.drupal.org/node/2361539) - resolves an issue where sequences in configuration were not sorted unless - the code responsible for saving configuration explicitly performed a sort. - This resulted in unpredictable changes in configuration ordering and - confusing diffs even when nothing had changed. To resolve this issue, we've - [added an `orderby` key to the config schema](https://www.drupal.org/node/2852566) - that allows it to be sorted either by key or by value. Adding a preferred - sort is strongly recommended. -* Two related issues remain open: - * [#2860531: Add orderby key to third party settings](https://www.drupal.org/node/2860531) - relates to unsorted sequences which result in unexpected discrepancies - in configuration during a configuration import. - * [#2885368: Config export key order for sequences: "orderedby" does not support cases where the order actually matters](https://www.drupal.org/node/2885368) - relates to various sequences in core and contributed modules in which - the source order is important. - -#### Revision data integrity fixes - -* Previously, data from draft revisions for [path aliases](https://www.drupal.org/node/2856363), - [menus](https://www.drupal.org/node/2858434), and [books](https://www.drupal.org/node/2858431) - could leak into the live site. Drupal 8.4.0-alpha1 hotfixes all three issues - by preventing changes to this data from being saved on any revision that is - not the default revision. These fixes improve revision support for both - stable features and the experimental Content Moderation module. -* Correspondingly, [Content Moderation now avoids such scenarios with non-default revisions](https://www.drupal.org/node/2883868) - by setting the 'default revision' flag earlier. -* Previously, [saving a revision of an entity translation could cause draft revisions to go "missing"](https://www.drupal.org/node/2766957). - Drupal 8.4. prevents this by preventing the moderation state from being set - to anything that would make the revision go "missing". - [A similar but unrelated bug in Content Moderation](https://www.drupal.org/node/2862988) - has also been fixed in this release. - -### New stable modules - -The following modules, previously considered experimental, are now stable and -safe for use on production sites, with full backwards compatibility and upgrade -paths from 8.4.0 to future releases: - -#### Datetime Range - -The [Datetime Range module](https://www.drupal.org/node/2893128) provides a -field type that allows end dates to support contributed modules like -[Calendar](https://www.drupal.org/project/calendar). This stable release is -backwards-compatible with the 8.3.x experimental version and shares a -consistent API with other Datetime fields. - -Future releases may improve Views support, usability, Datetime Range field -validation, and REST support. For bugs or feature requests for this -module, [see the core Datetime issue queue](https://www.drupal.org/project/issues/search/drupal?project_issue_followers=&status%5B%5D=Open&version%5B%5D=8.x&component%5B%5D=datetime.module&issue_tags_op=%3D). - -#### Layout Discovery - -The [Layout Discovery module](https://www.drupal.org/node/2834025) provides -an API for modules or themes to register layouts as well as five common -layouts. Providing this API in core enables -core and contributed layout solutions to be compatible with each other. This -stable release is backwards-compatible with the 8.3.x experimental version -and introduces [support for per-region attributes](https://www.drupal.org/node/2885877). - -#### Media - -The new core [Media module](https://www.drupal.org/node/2831274) provides an -API for reusable media entities and references. It is based on the contributed -[Media Entity module](https://www.drupal.org/project/media_entity). - -Since there is a rich ecosystem of Drupal contributed modules built on Media -Entity, the top priority for this release is to -[provide a stable core API and data model](https://www.drupal.org/node/2895059) -for a smoother transition for these modules. Developers and expert -site builders can now add Media as a dependency. Work is underway to -[provide an update path for existing sites' Media Entity data](https://www.drupal.org/node/2880334) -and to [port existing contributed modules to the refined core API](https://www.drupal.org/node/2860796). - -Note that **the core Media module is currently marked hidden** and will not -appear on the 'Extend' (module administration) page. (Enabling a contributed -module that depends on the core Media module will also enable Media -automatically.) The module will be displayed to site builders normally once -user experience issues with it are resolved in a future release. - -Similarly, the REST API and normalizations for Media is not final and support -for decoupled applications will be improved in a future release. - -#### Inline Form Errors - -The [Inline Form Errors module](https://www.drupal.org/node/2897652) provides a -summary of any validation errors at the top of a form and places the individual -error messages next to the form elements themselves. This helps users -understand which entries need to be fixed, and how. Inline Form Errors was -provided as an experimental module from Drupal 8.0.0 on, but it is now stable -and polished enough for production use. See the core -[Inline Form Errors module issue queue](https://www.drupal.org/project/issues/drupal?text=&status=Open&priorities=All&categories=All&version=All&component=inline_form_errors.module) -for outstanding issues. - -### Content authoring and site administration improvements - -* The "Save and keep (un)published" dropbutton has been replaced with [a "Published" checkbox and single "Save" button](https://www.drupal.org/node/2068063). - The "Save and..." dropbutton was a new design in Drupal 8, but users found it - confusing, so we have restored a design that is more similar to the user - interface for Drupal 7 and earlier. -* Previously, deleting a field on a content type would also delete any views - depending on the field. While the confirmation form did indicate that the - view would be deleted, users did not expect the behavior and often missed the - message, leading to data loss. - [Now, the view is disabled instead](https://www.drupal.org/node/2468045). In - the future, we intend to - [notify users that configuration has been disabled](https://www.drupal.org/node/2832558) - (as in this fix) as well as - [give users clearer warnings for other highly destructive operations](https://www.drupal.org/node/2773205). -* The [Drupal toolbar no longer flickers](https://www.drupal.org/node/2542050) - during page rendering, thus improving perceived front-end performance. -* Options in [timezones selector are now grouped by regions](https://www.drupal.org/node/2847651) - and labeled by cities instead of timezone names, making it much easier for users to find and select the specific timezone they need. -* Both the ["Comments" administration page at `/admin/content/comment`](https://www.drupal.org/node/1986606) and the ["Recent log messages" report provided by dblog](https://www.drupal.org/node/2015149) are now configurable views. -* Useful meta information about a node's status is typically displayed at the - top of the node sidebar. Previously, this meta information was provided by - the Seven theme, so it was not available in other administrative themes. - [This meta information is now provided by node.module itself](https://www.drupal.org/node/2803875) - so other administration themes can access it. - -### REST and API-first improvements - -* Authenticated REST API performance increased by 15% by - [utilizing the Dynamic Page Cache](https://www.drupal.org/node/2827797). -* POSTing entities [can now happen at `/node`, `/taxonomy/term` and so on](https://www.drupal.org/node/2293697), - instead of `/entity/node`, `/entity/taxonomy_term`. Instead of confusingly - different URLs, they therefore now use the URLs you'd expect. Backwards - compatibility is maintained. -* There is now a dedicated resource for [resetting a user's password](https://www.drupal.org/node/2847708). -* Time fields now are [normalized to RFC3339 timestamps by default](https://www.drupal.org/node/2768651), fixing - time ambiguity. Existing sites continue to receive UNIX timestamps, but can - opt in. [See the change record for more information about backwards compatibility and on how to opt in](https://www.drupal.org/node/2859657). -* [Path alias fields now are normalized too](https://www.drupal.org/node/2846554). - [See the change record for information about how this impacts API-first modules and other features relying on serialized entities](https://www.drupal.org/node/2856220). -* When denormalization fails, a [422 response is now returned](https://www.drupal.org/node/2827084) - instead of a 400, per the HTTP specification. -* With CORS enabled to allow origins besides the site's own host, - [submitting forms was broken](https://www.drupal.org/node/2853201) unless the - site's own host was also explicitly allowed. -* Fatal errors and exceptions [now show a backtrace](https://www.drupal.org/node/2853300) - for all non-HTML requests as well as HTML requests, which makes for easier - debugging and better bug reports. -* Massive expansion of test coverage. - -### Performance and scalability improvements - -* The internal page cache now [has a dedicated cache bin](https://www.drupal.org/node/2889603) - distinct from the rest of the render cache (a significant scalability - improvement). -* The service collector pattern instantiates all services it collects, which is expensive, and unnecessary for some use cases. For those use cases, a [new service ID collector](https://www.drupal.org/node/2472337) - pattern was added. The theme negotiator was updated to use it. - [See the change record for information about how to use the service ID collector](https://www.drupal.org/node/2598944) for improved performance. -* The maximum time in-progress forms are cached [is now customizable](https://www.drupal.org/node/1286154) - rather than being limited to a default cache lifetime of 6 hours. Sites can - decrease the lifetime to reduce cache footprint, or increase it if needed for - a particular site's usecase. - [See the change record to learn how to access this new setting](https://www.drupal.org/node/2886836). -* If there are no status messages, the corresponding rendering - [is now skipped](https://www.drupal.org/node/2853509). On simple sites, this - can result in a 10% improvement when there are no messages! -* [Optimized the early Drupal installer](https://www.drupal.org/node/2872611) - to check whether any themes are installed first before invoking an - unnecessary function, which improves Drupal install time measurably for - both sites and automated tests. - -### Developer experience improvements - -* [Adopted Airbnb JavaScript style guide 14.1](https://www.drupal.org/node/2815077) as the new baseline set of - coding standards for Drupal core and contributed modules. - [See the change record for information about how to configure your project for eslint](https://www.drupal.org/node/2873849). -* Field type definitions can now [enforce the cardinality of the field](https://www.drupal.org/node/2403703). - [See the change record for information about how to specify a cardinality via the annotation](https://www.drupal.org/node/2869873). -* [Added new methods](https://www.drupal.org/node/2869809) to make getting - typed configuration entity representations easier. - [See the change record for more information about how to invoke these methods](https://www.drupal.org/node/2877282). -* The `html_tag` render element now [supports nested render arrays](https://www.drupal.org/node/2694535), - enabling the creation of dynamic SVGs. - [See the change record for information about how you can use this in your theme](https://www.drupal.org/node/2887146). -* [Added more helpful errors](https://www.drupal.org/node/2705037) when CSS - is not properly nested under an existing category in asset libraries. -* Also see the [change records for the 8.4.x branch](https://www.drupal.org/list-changes/drupal/published?keywords_description=&to_branch=8.4.x&version=&created_op=%3E%3D&created%5Bvalue%5D=&created%5Bmin%5D=&created%5Bmax%5D=) - for other changes for developers. - -### Automated testing improvements - -* [PHPUnit has been updated from 4.8.28 to 4.8.35](https://www.drupal.org/node/2850797) - in order to incorporate a forward compatibility layer for PHPUnit 4.8, useful - during a future migration to PHPUnit 5 or PHPUnit 6. -* Many former WebTestBase tests were converted to BrowserTestBase. - [Track current progress](http://simpletest-countdown.org/). -* The default approach for testing deprecated code has changed to - [require use of the Drupal core deprecation policy](https://www.drupal.org/node/2488860) - (`@trigger_error()`) to mark code deprecated; otherwise a test error will - be thrown. - [See the change record for information about how to update `phpunit.xml` and how to test deprecated code](https://www.drupal.org/node/2811561). -* [Resolved random test failures](https://www.drupal.org/node/2866056) due to - ResourceTestBase's HTTP client timeout of 30 seconds. - -### Third-party library updates - -* [Drupal's Symfony dependency has been updated from Symfony 2.8 to Symfony - 3.2](https://www.drupal.org/node/2712647). This major version update is - necessary because Symfony 2.8 support will end around the release of Drupal - 8.6.0 next year. See the change record for information about [Symfony 3 backwards compatibility - breaks that affected Drupal core](https://www.drupal.org/node/2743809). - [Drupal 8 also requires Symfony 3.2.8](https://www.drupal.org/node/2871253) - because of a bug in Symfony 3.2.7. -* [#2533498: Update jQuery to version 3](https://www.drupal.org/node/2533498). - Now that jQuery 3.0 has been released, jQuery 2.x will only be receiving - security updates, so Drupal 8.4.0 ships with this library update. jQuery 3 - features numerous improvements, including better error reporting. See the - [jQuery Core 3.0 Upgrade Guide](https://jquery.com/upgrade-guide/3.0/) for - information on jQuery 3 backwards compatibility breaks that might affect the - JavaScript code in your modules, themes, and sites. Note that we may consider - rolling back this library update if the [bug affecting tours](https://www.drupal.org/node/2898808) is not resolved in time for Drupal 8.4.0-beta1. -* [zendframework/zend-diactoros has been updated from 1.3.10 to 1.4.0](https://www.drupal.org/node/2874817). -* [jQuery UI has been updated from 1.11.4 to 1.12.1](https://www.drupal.org/node/2809427). -* [CKEditor has been updated from 4.6.2 to 4.7.1](https://www.drupal.org/node/2893566). -* [asm89/stack-cors has been updated from 1.0 to 1.1](https://www.drupal.org/node/2853201). - -### Experimental modules - -#### Migrate ([beta stability](https://www.drupal.org/core/experimental#beta)) - -Migrate provides a general API for migrations. It will be considered completely -stable once all issues tagged [Migrate critical](https://www.drupal.org/project/issues/search/drupal?project_issue_followers=&status%5B%5D=Open&version%5B%5D=8.x&issue_tags_op=%3D&issue_tags=Migrate+critical) are resolved. - -* Renamed [`migration` process plugin to `migration_lookup`](https://www.drupal.org/node/2845486) - and - [`iterator` process plugin to `sub_process`](https://www.drupal.org/node/2845483) - to better capture their purposes. (Backwards compatibility is provided for - both process plugins since Migrate is in beta.) - -#### Migrate Drupal and Migrate Drupal UI ([alpha stability](https://www.drupal.org/core/experimental#alpha)) - -Migrate Drupal module provides API support for Drupal-to-Drupal migrations, and -Migrate Drupal UI offers a simple user interface to run migrations from older -Drupal versions. - -* This release adds [date](https://www.drupal.org/node/2566779) and - [node reference](https://www.drupal.org/node/2814949) support for Drupal 6 to - 8 migrations. -* Core provides migrations for most Drupal 6 data and can be used for migrating - Drupal 6 sites to Drupal 8, and the Drupal 6 to 8 migration path is nearing - beta stability. Some gaps remain, such as for some internationalization data. - ([Outstanding issues for the Drupal 6 to Drupal 8 migration](https://www.drupal.org/project/issues/search/drupal?project_issue_followers=&status%5B%5D=1&status%5B%5D=13&status%5B%5D=8&status%5B%5D=14&status%5B%5D=15&status%5B%5D=4&issue_tags_op=%3D&issue_tags=migrate-d6-d8)) -* The Drupal 7 to Drupal 8 migration is incomplete but is suitable for - developers who would like to help improve the migration and can be used to - test upgrades especially for simple Drupal 7 sites. Most high-priority - migrations are available. - ([Outstanding issues for the Drupal 7 to Drupal 8 migration](https://www.drupal.org/node/2456259)) -* Drush support for Migrate is currently only available in the - [Drupal Upgrade](https://www.drupal.org/project/migrate_upgrade) contributed - module. (See the - [pull request to add support to Drush](https://github.com/drush-ops/drush/issues/2140).) -* [Added field plugin](https://www.drupal.org/node/2814949) to handle - migration of node reference field values from Drupal 6 to Drupal 8. -* [Added date field plugin](https://www.drupal.org/node/2566779) to handle - migration of CCK date fields in Drupal 6 to Drupal 8. -* [Renamed migration field plugins and classes](https://www.drupal.org/node/2683435) - referring to custom fields provided by the Drupal 6 module CCK, which was - replaced in Drupal 7 by the core Field API. [See the change record for more information about how this impacts your migration plugins](https://www.drupal.org/node/2751897). - -#### Workflows ([beta stability](https://www.drupal.org/core/experimental#beta)) - -The Workflows module provides an abstract system of states (like Draft, -Archived, and Published) and transitions between them. Workflows can be used by -modules that implement non-publishing workflows (such as for users or products) -as well as content publishing workflows. - -Drupal 8.4 introduces a final significant backwards compatibility and data -model break for this module, -[moving responsibility for workflow states and transitions from the Workflow entity to the Workflow type plugin](https://www.drupal.org/node/2849827). -Read [Workflow type plugins are now responsible for state and transition schema](https://www.drupal.org/node/2897706) -for full details on the API and data model changes related to this fix. Now -that this change is complete, the Workflows module has reached beta stability, -and it may furthermore be marked stable in time for Drupal 8.4.0! - -#### Content Moderation ([beta stability](https://www.drupal.org/core/experimental#beta)) - -Content Moderation allows workflows from the Workflows module to be applied to -content. Content Moderation has beta stability in 8.4.0-alpha1, but may become -stable in time for 8.4.0! Notable improvements in this release: - -* Workflow states are now [selected from a select list, rather than under a drop-button](https://www.drupal.org/node/2753717), which represents a significant - usability improvement. -* Now that workflows can be applied to any revisionable entity type, Content - Moderation [adds entity type checkboxes to the workflow form](https://www.drupal.org/node/2843083). - This allows site administrators to configure which entity types should have - the workflow at the same time as they configure the workflow itself, for a - more intuitive user experience. -* Content Moderation now [prevents the deletion of workflows that are currently in use](https://www.drupal.org/node/2830740) - to prevent fatal errors and data integrity problems. -* The confusing terminology of - ["forward revisions" has been replaced with that of "pending revisions"](https://www.drupal.org/node/2890364). - If your contributed module refers to revisions that are not yet published, it - should use this new term. - -#### Field Layout ([alpha stability](https://www.drupal.org/core/experimental#alpha)) - -This module provides the ability for site builders to rearrange fields on -content types, block types, etc. into new regions, for both the form and -display, on the same forms provided by the normal field user interface. Field -Layout has had several bugfixes since 8.3.0, but no significant changes. See -the [entity display layout roadmap](https://www.drupal.org/node/2795833) for -the next steps for this module, which needs to become stable by 8.5.0 to remain -in Drupal core. - -#### Settings Tray ([alpha stability](https://www.drupal.org/core/experimental#alpha)) - -The Settings Tray module allows configuring page elements such as blocks and -menus from the frontend of your site. Settings Tray has improved significantly -since Drupal 8.3.0. The goal for this release is to get Settings Tray to beta -stability. Only two issues remain before that milestone: to -[move the off-canvas dialog renderer into a core component](https://www.drupal.org/node/2784443), and to -[rename the machine name of the module to settings_tray](https://www.drupal.org/node/2803375), -to match its user-facing name. We hope to make Settings Tray stable by 8.5.0. -To track progress, see the ["outside in" roadmap issue](https://www.drupal.org/node/2762505). - -* [A CSS reset has been added to the Settings Tray](https://www.drupal.org/node/2826722) to improve the themer experience. -* Form validation messages now [appear in the Settings Tray instead of main page](https://www.drupal.org/node/2785047). -* [The toolbar background in Edit mode now matches the edit button](https://www.drupal.org/node/2894427), - instead of the white background that many users found distracting or - misunderstood to be an indication that something was broken. -* The block title field in the tray is now [labeled more clearly and only shown when the block title itself is shown](https://www.drupal.org/node/2882729). -* In the contextual links, ["Quick edit" is now listed before "Configure"](https://www.drupal.org/node/2784567), - and the [custom blocks instead have a "Quick edit settings" link](https://www.drupal.org/node/2786193) - (to distinguish them from the links provided by the Quick Edit module, which - allow editing the content of the custom block itself). -* Edit mode [now behaves the same way whether accessed by clicking "Quick edit" or clicking through the toolbar](https://www.drupal.org/node/2847664) -* Users can now [escape from Edit mode with the ESC key](https://www.drupal.org/node/2784571), for better accessibility. - -#### Place Blocks ([alpha stability](https://www.drupal.org/core/experimental#alpha)) - -This feature allows the user to place a block on any page and see the region -where it will be displayed, without having to navigate to a backend -administration form. -[8.4.0-alpha1 was the deadline for Place Blocks to stabilize](https://www.drupal.org/core/experimental#versions), -but the module's roadmap was not completed. Furthermore, the module is not -intended as a standalone feature and should instead be a built-in part of the -Block system. For these reasons, -[Place Blocks module has been marked hidden in this release](https://www.drupal.org/node/2898267) -(it can still be enabled with Drush). The Place Blocks module itself will be -turned into an empty module in Drupal 8.5.x, since ideally the core Block -system will offer the same functionality in 8.5.0 (though this depends on -completion of a [core patch for the feature](https://www.drupal.org/node/2739075).) - - Drupal 8.3.0, 2017-04-05 ------------------------ - Added modules: diff --git a/core/core.services.yml b/core/core.services.yml index 598754e..21a3e59 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1152,7 +1152,7 @@ services: arguments: ['@state', '@current_user'] maintenance_mode_subscriber: class: Drupal\Core\EventSubscriber\MaintenanceModeSubscriber - arguments: ['@maintenance_mode', '@config.factory', '@string_translation', '@url_generator', '@current_user', '@bare_html_page_renderer'] + arguments: ['@maintenance_mode', '@config.factory', '@string_translation', '@url_generator', '@current_user', '@bare_html_page_renderer', '@messenger'] tags: - { name: event_subscriber } path_subscriber: @@ -1660,3 +1660,6 @@ services: class: Drupal\Core\EventSubscriber\RssResponseRelativeUrlFilter tags: - { name: event_subscriber } + messenger: + class: Drupal\Core\Messenger\LegacyMessenger + arguments: ['@page_cache_kill_switch'] diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 52f5742..5a1cfbe 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -11,8 +11,6 @@ use Drupal\Component\Utility\Unicode; use Drupal\Core\Config\BootstrapConfigStorageFactory; use Drupal\Core\Logger\RfcLogLevel; -use Drupal\Core\Render\Markup; -use Drupal\Component\Render\MarkupInterface; use Drupal\Core\Test\TestDatabase; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Site\Settings; @@ -459,28 +457,10 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia * @see status-messages.html.twig */ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) { - if (isset($message)) { - if (!isset($_SESSION['messages'][$type])) { - $_SESSION['messages'][$type] = []; - } - - // Convert strings which are safe to the simplest Markup objects. - if (!($message instanceof Markup) && $message instanceof MarkupInterface) { - $message = Markup::create((string) $message); - } - - // Do not use strict type checking so that equivalent string and - // MarkupInterface objects are detected. - if ($repeat || !in_array($message, $_SESSION['messages'][$type])) { - $_SESSION['messages'][$type][] = $message; - } - - // Mark this page as being uncacheable. - \Drupal::service('page_cache_kill_switch')->trigger(); - } - - // Messages not set when DB connection fails. - return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL; + /* @var \Drupal\Core\Messenger\MessengerInterface $messenger */ + $messenger = \Drupal::service('messenger'); + $messenger->addMessage($message, $type, $repeat); + return $messenger->all(); } /** @@ -509,10 +489,12 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) * @see status-messages.html.twig */ function drupal_get_messages($type = NULL, $clear_queue = TRUE) { - if ($messages = drupal_set_message()) { + /** @var \Drupal\Core\Messenger\MessengerInterface $messenger */ + $messenger = \Drupal::hasService('messenger') ? \Drupal::service('messenger') : NULL; + if ($messenger && ($messages = $messenger->all())) { if ($type) { if ($clear_queue) { - unset($_SESSION['messages'][$type]); + $messenger->deleteByType($type); } if (isset($messages[$type])) { return [$type => $messages[$type]]; @@ -520,7 +502,7 @@ function drupal_get_messages($type = NULL, $clear_queue = TRUE) { } else { if ($clear_queue) { - unset($_SESSION['messages']); + $messenger->deleteAll(); } return $messages; } diff --git a/core/includes/errors.inc b/core/includes/errors.inc index 40e2903..284d183 100644 --- a/core/includes/errors.inc +++ b/core/includes/errors.inc @@ -164,7 +164,8 @@ function _drupal_log_error($error, $fatal = FALSE) { // installer. if (\Drupal::hasService('logger.factory')) { try { - \Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file) @backtrace_string.', $error); + // Provide the PHP backtrace to logger implementations. + \Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file) @backtrace_string.', $error + ['backtrace' => $backtrace]); } catch (\Exception $e) { // We can't log, for example because the database connection is not diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index 1acfc12..07dc12f 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -81,7 +81,7 @@ class Drupal { /** * The current system version. */ - const VERSION = '8.4.0-dev'; + const VERSION = '8.5.0-dev'; /** * Core API compatibility. diff --git a/core/lib/Drupal/Core/Access/AccessResult.php b/core/lib/Drupal/Core/Access/AccessResult.php index 1cd6b8b..f4962ec 100644 --- a/core/lib/Drupal/Core/Access/AccessResult.php +++ b/core/lib/Drupal/Core/Access/AccessResult.php @@ -87,13 +87,16 @@ public static function allowedIf($condition) { * * @param bool $condition * The condition to evaluate. + * @param string|null $reason + * (optional) The reason why access is forbidden. Intended for developers, + * hence not translatable * * @return \Drupal\Core\Access\AccessResult * If $condition is TRUE, isForbidden() will be TRUE, otherwise isNeutral() * will be TRUE. */ - public static function forbiddenIf($condition) { - return $condition ? static::forbidden() : static::neutral(); + public static function forbiddenIf($condition, $reason = NULL) { + return $condition ? static::forbidden($reason) : static::neutral(); } /** diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityListBuilder.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListBuilder.php index 38b7fa8..e26a03c 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityListBuilder.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListBuilder.php @@ -37,14 +37,14 @@ public function getDefaultOperations(EntityInterface $entity) { $operations['enable'] = [ 'title' => t('Enable'), 'weight' => -10, - 'url' => $entity->urlInfo('enable'), + 'url' => $this->ensureDestination($entity->toUrl('enable')), ]; } elseif ($entity->hasLinkTemplate('disable')) { $operations['disable'] = [ 'title' => t('Disable'), 'weight' => 40, - 'url' => $entity->urlInfo('disable'), + 'url' => $this->ensureDestination($entity->toUrl('disable')), ]; } } diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php index 16f0184..e696f24 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php @@ -112,7 +112,7 @@ public static function open(array &$connection_options = []) { // so backslashes in the password need to be doubled up. // The bug was reported against pdo_pgsql 1.0.2, backslashes in passwords // will break on this doubling up when the bug is fixed, so check the version - //elseif (phpversion('pdo_pgsql') < 'version_this_was_fixed_in') { + // elseif (phpversion('pdo_pgsql') < 'version_this_was_fixed_in') { else { $connection_options['password'] = str_replace('\\', '\\\\', $connection_options['password']); } diff --git a/core/lib/Drupal/Core/Database/Query/Select.php b/core/lib/Drupal/Core/Database/Query/Select.php index 6ea9681..80533fd 100644 --- a/core/lib/Drupal/Core/Database/Query/Select.php +++ b/core/lib/Drupal/Core/Database/Query/Select.php @@ -128,7 +128,7 @@ class Select extends Query implements SelectInterface { * @param array $options * Array of query options. */ - public function __construct($table, $alias = NULL, Connection $connection, $options = []) { + public function __construct($table, $alias, Connection $connection, $options = []) { $options['return'] = Database::RETURN_STATEMENT; parent::__construct($connection, $options); $conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND'; diff --git a/core/lib/Drupal/Core/Database/StatementInterface.php b/core/lib/Drupal/Core/Database/StatementInterface.php index a34bf61..a97043a 100644 --- a/core/lib/Drupal/Core/Database/StatementInterface.php +++ b/core/lib/Drupal/Core/Database/StatementInterface.php @@ -37,7 +37,7 @@ * "the access type must be omitted" if it is protected; i.e., conflicting * statements). The access type has to be protected. */ - //protected function __construct(Connection $dbh); + // protected function __construct(Connection $dbh); /** * Executes a prepared statement diff --git a/core/lib/Drupal/Core/Entity/ContentEntityForm.php b/core/lib/Drupal/Core/Entity/ContentEntityForm.php index 02eee80..981fb90 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityForm.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityForm.php @@ -127,6 +127,15 @@ public function form(array $form, FormStateInterface $form_state) { $this->addRevisionableFormFields($form); } + $form['footer'] = [ + '#type' => 'container', + '#weight' => 99, + '#attributes' => [ + 'class' => ['entity-content-form-footer'] + ], + '#optional' => TRUE, + ]; + return $form; } diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php index 0ff59a8..e92930a 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php @@ -80,6 +80,40 @@ protected function doCreate(array $values) { } /** + * {@inheritdoc} + */ + public function createWithSampleValues($bundle = FALSE, array $values = []) { + // ID and revision should never have sample values generated for them. + $forbidden_keys = [ + $this->entityType->getKey('id'), + ]; + if ($revision_key = $this->entityType->getKey('revision')) { + $forbidden_keys[] = $revision_key; + } + if ($bundle_key = $this->entityType->getKey('bundle')) { + if (!$bundle) { + throw new EntityStorageException("No entity bundle was specified"); + } + if (!array_key_exists($bundle, $this->entityManager->getBundleInfo($this->entityTypeId))) { + throw new EntityStorageException(sprintf("Missing entity bundle. The \"%s\" bundle does not exist", $bundle)); + } + $values[$bundle_key] = $bundle; + // Bundle is already set + $forbidden_keys[] = $bundle_key; + } + // Forbid sample generation on any keys whose values were submitted. + $forbidden_keys = array_merge($forbidden_keys, array_keys($values)); + /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */ + $entity = $this->create($values); + foreach ($entity as $field_name => $value) { + if (!in_array($field_name, $forbidden_keys, TRUE)) { + $entity->get($field_name)->generateSampleItems(); + } + } + return $entity; + } + + /** * Initializes field values. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php index 47e058d..eb979c7 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php @@ -23,4 +23,21 @@ */ public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []); + + /** + * Creates an entity with sample field values. + * + * @param string|bool $bundle + * (optional) The entity bundle. + * @param array $values + * (optional) Any default values to use during generation. + * + * @return \Drupal\Core\Entity\FieldableEntityInterface + * A fieldable content entity. + * + * @throws \Drupal\Core\Entity\EntityStorageException + * Thrown if the bundle does not exist or was needed but not specified. + */ + public function createWithSampleValues($bundle = FALSE, array $values = []); + } diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index 95e85e2..4fe3786 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -89,6 +89,15 @@ protected function entityTypeManager() { } /** + * Gets the entity type bundle info service. + * + * @return \Drupal\Core\Entity\EntityTypeBundleInfoInterface + */ + protected function entityTypeBundleInfo() { + return \Drupal::service('entity_type.bundle.info'); + } + + /** * Gets the language manager. * * @return \Drupal\Core\Language\LanguageManagerInterface @@ -198,7 +207,7 @@ public function toUrl($rel = 'canonical', array $options = []) { $bundle = $this->bundle(); // A bundle-specific callback takes precedence over the generic one for // the entity type. - $bundles = $this->entityManager()->getBundleInfo($this->getEntityTypeId()); + $bundles = $this->entityTypeBundleInfo()->getBundleInfo($this->getEntityTypeId()); if (isset($bundles[$bundle]['uri_callback'])) { $uri_callback = $bundles[$bundle]['uri_callback']; } @@ -344,11 +353,11 @@ public function uriRelationships() { */ public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) { if ($operation == 'create') { - return $this->entityManager() + return $this->entityTypeManager() ->getAccessControlHandler($this->entityTypeId) ->createAccess($this->bundle(), $account, [], $return_as_object); } - return $this->entityManager() + return $this->entityTypeManager() ->getAccessControlHandler($this->entityTypeId) ->access($this, $operation, $account, $return_as_object); } @@ -374,7 +383,8 @@ public function language() { * {@inheritdoc} */ public function save() { - return $this->entityManager()->getStorage($this->entityTypeId)->save($this); + $storage = $this->entityTypeManager()->getStorage($this->entityTypeId); + return $storage->save($this); } /** @@ -382,7 +392,7 @@ public function save() { */ public function delete() { if (!$this->isNew()) { - $this->entityManager()->getStorage($this->entityTypeId)->delete([$this->id() => $this]); + $this->entityTypeManager()->getStorage($this->entityTypeId)->delete([$this->id() => $this]); } } @@ -407,7 +417,7 @@ public function createDuplicate() { * {@inheritdoc} */ public function getEntityType() { - return $this->entityManager()->getDefinition($this->getEntityTypeId()); + return $this->entityTypeManager()->getDefinition($this->getEntityTypeId()); } /** @@ -508,24 +518,30 @@ public function getCacheMaxAge() { * {@inheritdoc} */ public static function load($id) { - $entity_manager = \Drupal::entityManager(); - return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->load($id); + $entity_type_repository = \Drupal::service('entity_type.repository'); + $entity_type_manager = \Drupal::entityTypeManager(); + $storage = $entity_type_manager->getStorage($entity_type_repository->getEntityTypeFromClass(get_called_class())); + return $storage->load($id); } /** * {@inheritdoc} */ public static function loadMultiple(array $ids = NULL) { - $entity_manager = \Drupal::entityManager(); - return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->loadMultiple($ids); + $entity_type_repository = \Drupal::service('entity_type.repository'); + $entity_type_manager = \Drupal::entityTypeManager(); + $storage = $entity_type_manager->getStorage($entity_type_repository->getEntityTypeFromClass(get_called_class())); + return $storage->loadMultiple($ids); } /** * {@inheritdoc} */ public static function create(array $values = []) { - $entity_manager = \Drupal::entityManager(); - return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->create($values); + $entity_type_repository = \Drupal::service('entity_type.repository'); + $entity_type_manager = \Drupal::entityTypeManager(); + $storage = $entity_type_manager->getStorage($entity_type_repository->getEntityTypeFromClass(get_called_class())); + return $storage->create($values); } /** diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayModeBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayModeBase.php index 0ec97b7..fe7f105 100644 --- a/core/lib/Drupal/Core/Entity/EntityDisplayModeBase.php +++ b/core/lib/Drupal/Core/Entity/EntityDisplayModeBase.php @@ -107,4 +107,16 @@ public static function preDelete(EntityStorageInterface $storage, array $entitie \Drupal::entityManager()->clearCachedFieldDefinitions(); } + /** + * {@inheritdoc} + */ + protected function urlRouteParameters($rel) { + $uri_route_parameters = parent::urlRouteParameters($rel); + if ($rel === 'add-form') { + $uri_route_parameters['entity_type_id'] = $this->getTargetType(); + } + + return $uri_route_parameters; + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityListBuilder.php b/core/lib/Drupal/Core/Entity/EntityListBuilder.php index bb5046c..7191fae 100644 --- a/core/lib/Drupal/Core/Entity/EntityListBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityListBuilder.php @@ -2,6 +2,8 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Routing\RedirectDestinationTrait; +use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -11,6 +13,8 @@ */ class EntityListBuilder extends EntityHandlerBase implements EntityListBuilderInterface, EntityHandlerInterface { + use RedirectDestinationTrait; + /** * The entity storage class. * @@ -143,14 +147,14 @@ protected function getDefaultOperations(EntityInterface $entity) { $operations['edit'] = [ 'title' => $this->t('Edit'), 'weight' => 10, - 'url' => $entity->urlInfo('edit-form'), + 'url' => $this->ensureDestination($entity->toUrl('edit-form')), ]; } if ($entity->access('delete') && $entity->hasLinkTemplate('delete-form')) { $operations['delete'] = [ 'title' => $this->t('Delete'), 'weight' => 100, - 'url' => $entity->urlInfo('delete-form'), + 'url' => $this->ensureDestination($entity->toUrl('delete-form')), ]; } @@ -247,4 +251,17 @@ protected function getTitle() { return; } + /** + * Ensures that a destination is present on the given URL. + * + * @param \Drupal\Core\Url $url + * The URL object to which the destination should be added. + * + * @return \Drupal\Core\Url + * The updated URL object. + */ + protected function ensureDestination(Url $url) { + return $url->mergeOptions(['query' => $this->getRedirectDestination()->getAsArray()]); + } + } diff --git a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php index c42e7c0..b3fc12d 100644 --- a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueContentEntityStorage.php @@ -18,4 +18,9 @@ public function createTranslation(ContentEntityInterface $entity, $langcode, arr // https://www.drupal.org/node/2618436. } + /** + * {@inheritdoc} + */ + public function createWithSampleValues($bundle = FALSE, array $values = []) {} + } diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php index 95a766f..99f5aa4 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php @@ -8,6 +8,7 @@ use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; use Drupal\Core\Entity\Sql\TableMappingInterface; use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface; +use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\TypedData\DataReferenceDefinitionInterface; /** @@ -273,14 +274,6 @@ public function addField($field, $type, $langcode) { $entity_type = $this->entityManager->getDefinition($entity_type_id); $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); // Add the new entity base table using the table and sql column. - // An additional $field_storage argument is being passed to - // addNextBaseTable() in order to improve its functionality, for - // example by allowing extra processing based on the field type of the - // storage. In order to maintain backwards compatibility in 8.4.x, the - // new argument has not been added to the signature of that method, - // and it will be added only in 8.5.x. - // @todo Add the $field_storage argument to addNextBaseTable() in - // 8.5.x. https://www.drupal.org/node/2909425 $base_table = $this->addNextBaseTable($entity_type, $table, $sql_column, $field_storage); $propertyDefinitions = []; $key++; @@ -403,11 +396,13 @@ protected function getTableMapping($table, $entity_type_id) { * This is the table being joined, in the above example, {users}. * @param string $sql_column * This is the SQL column in the existing table being joined to. + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage + * The field storage definition for the field referencing this column. * * @return string * The alias of the next entity table joined in. */ - protected function addNextBaseTable(EntityType $entity_type, $table, $sql_column) { + protected function addNextBaseTable(EntityType $entity_type, $table, $sql_column, FieldStorageDefinitionInterface $field_storage) { $join_condition = '%alias.' . $entity_type->getKey('id') . " = $table.$sql_column"; return $this->sqlQuery->leftJoin($entity_type->getBaseTable(), NULL, $join_condition); } diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php index ba82988..c2688ed 100644 --- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php @@ -5,6 +5,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Render\BareHtmlPageRendererInterface; +use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Routing\RouteMatch; use Drupal\Core\Routing\UrlGeneratorInterface; use Drupal\Core\Session\AccountInterface; @@ -59,6 +60,13 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface { protected $bareHtmlPageRenderer; /** + * The messenger. + * + * @var \Drupal\Core\Messenger\MessengerInterface + */ + protected $messenger; + + /** * Constructs a new MaintenanceModeSubscriber. * * @param \Drupal\Core\Site\MaintenanceModeInterface $maintenance_mode @@ -73,14 +81,17 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface { * The current user. * @param \Drupal\Core\Render\BareHtmlPageRendererInterface $bare_html_page_renderer * The bare HTML page renderer. + * @param \Drupal\Core\Messenger\MessengerInterface $messenger + * The messenger. */ - public function __construct(MaintenanceModeInterface $maintenance_mode, ConfigFactoryInterface $config_factory, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account, BareHtmlPageRendererInterface $bare_html_page_renderer) { + public function __construct(MaintenanceModeInterface $maintenance_mode, ConfigFactoryInterface $config_factory, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account, BareHtmlPageRendererInterface $bare_html_page_renderer, MessengerInterface $messenger) { $this->maintenanceMode = $maintenance_mode; $this->config = $config_factory; $this->stringTranslation = $translation; $this->urlGenerator = $url_generator; $this->account = $account; $this->bareHtmlPageRenderer = $bare_html_page_renderer; + $this->messenger = $messenger; } /** @@ -118,10 +129,10 @@ public function onKernelRequestMaintenance(GetResponseEvent $event) { // settings page. if ($route_match->getRouteName() != 'system.site_maintenance_mode') { if ($this->account->hasPermission('administer site configuration')) { - $this->drupalSetMessage($this->t('Operating in maintenance mode. Go online.', [':url' => $this->urlGenerator->generate('system.site_maintenance_mode')]), 'status', FALSE); + $this->messenger->addMessage($this->t('Operating in maintenance mode. Go online.', [':url' => $this->urlGenerator->generate('system.site_maintenance_mode')]), 'status', FALSE); } else { - $this->drupalSetMessage($this->t('Operating in maintenance mode.'), 'status', FALSE); + $this->messenger->addMessage($this->t('Operating in maintenance mode.'), 'status', FALSE); } } } @@ -141,13 +152,6 @@ protected function getSiteMaintenanceMessage() { } /** - * Wraps the drupal_set_message function. - */ - protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) { - return drupal_set_message($message, $type, $repeat); - } - - /** * {@inheritdoc} */ public static function getSubscribedEvents() { diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index cfbaa61..d8d3608 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -106,7 +106,7 @@ class ModuleHandler implements ModuleHandlerInterface { * @see \Drupal\Core\DrupalKernel * @see \Drupal\Core\CoreServiceProvider */ - public function __construct($root, array $module_list = [], CacheBackendInterface $cache_backend) { + public function __construct($root, array $module_list, CacheBackendInterface $cache_backend) { $this->root = $root; $this->moduleList = []; foreach ($module_list as $name => $module) { diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php index 25df63b..991385f 100644 --- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php +++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php @@ -838,4 +838,21 @@ public function setStorageRequired($required) { return $this; } + /** + * Magic method: Implements a deep clone. + */ + public function __clone() { + parent::__clone(); + + // The itemDefinition (\Drupal\Core\Field\TypedData\FieldItemDataDefinition) + // has a property fieldDefinition, which is a recursive reference to the + // parent BaseFieldDefinition, therefore the reference to the old object has + // to be overwritten with a reference to the cloned one. + $this->itemDefinition->setFieldDefinition($this); + // Reset the static cache of the field property definitions in order to + // ensure that the clone will reference different field property definitions + // objects. + $this->propertyDefinitions = NULL; + } + } diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php index a1a1ebd..5d13a5e 100644 --- a/core/lib/Drupal/Core/Field/FieldItemList.php +++ b/core/lib/Drupal/Core/Field/FieldItemList.php @@ -259,7 +259,7 @@ public function view($display_options = []) { */ public function generateSampleItems($count = 1) { $field_definition = $this->getFieldDefinition(); - $field_type_class = \Drupal::service('plugin.manager.field.field_type')->getPluginClass($field_definition->getType()); + $field_type_class = $field_definition->getItemDefinition()->getClass(); for ($delta = 0; $delta < $count; $delta++) { $values[$delta] = $field_type_class::generateSampleValue($field_definition); } diff --git a/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php b/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php index 4a6d877..bc8f9c0 100644 --- a/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php +++ b/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php @@ -3,7 +3,6 @@ namespace Drupal\Core\Field\TypedData; use Drupal\Core\Field\BaseFieldDefinition; -use Drupal\Core\TypedData\ComplexDataDefinitionInterface; use Drupal\Core\TypedData\DataDefinition; /** @@ -14,7 +13,7 @@ * by the field definitions, this class does not benefit and thus does not * extend from MapDefinition or ComplexDataDefinitionBase. */ -class FieldItemDataDefinition extends DataDefinition implements ComplexDataDefinitionInterface { +class FieldItemDataDefinition extends DataDefinition implements FieldItemDataDefinitionInterface { /** * The field definition the item definition belongs to. @@ -74,13 +73,18 @@ public function getMainPropertyName() { } /** - * Gets the field item's field definition. - * - * @return \Drupal\Core\Field\FieldDefinitionInterface - * The field definition for this field item. + * {@inheritdoc} */ public function getFieldDefinition() { return $this->fieldDefinition; } + /** + * {@inheritdoc} + */ + public function setFieldDefinition($field_definition) { + $this->fieldDefinition = $field_definition; + return $this; + } + } diff --git a/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinitionInterface.php b/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinitionInterface.php new file mode 100644 index 0000000..e757119 --- /dev/null +++ b/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinitionInterface.php @@ -0,0 +1,37 @@ +isDirectory($path) && $recursive) { $filelist = @ftp_nlist($this->connection, $path); if (!$filelist) { - //empty directory - returns false + // empty directory - returns false return; } foreach ($filelist as $file) { diff --git a/core/lib/Drupal/Core/Messenger/LegacyMessenger.php b/core/lib/Drupal/Core/Messenger/LegacyMessenger.php new file mode 100644 index 0000000..8c9751f --- /dev/null +++ b/core/lib/Drupal/Core/Messenger/LegacyMessenger.php @@ -0,0 +1,212 @@ +killSwitch = $killSwitch; + } + + /** + * {@inheritdoc} + */ + public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE) { + $this->setMessage($message, $type, $repeat); + } + + /** + * {@inheritdoc} + */ + public function addStatus($message, $repeat = FALSE) { + return $this->addMessage($message, static::TYPE_STATUS); + } + + /** + * {@inheritdoc} + */ + public function addError($message, $repeat = FALSE) { + return $this->addMessage($message, static::TYPE_ERROR); + } + + /** + * {@inheritdoc} + */ + public function addWarning($message, $repeat = FALSE) { + return $this->addMessage($message, static::TYPE_WARNING); + } + + /** + * {@inheritdoc} + */ + public function all() { + return $this->getMessages(NULL, FALSE); + } + + /** + * {@inheritdoc} + */ + public function messagesByType($type) { + return $this->getMessages($type, FALSE); + } + + /** + * {@inheritdoc} + */ + public function deleteAll() { + return $this->getMessages(NULL, TRUE); + } + + /** + * {@inheritdoc} + */ + public function deleteByType($type) { + return $this->getMessages($type, TRUE); + } + + /** + * Sets a message to display to the user. + * + * Messages are stored in a session variable and displayed in the page template + * via the $messages theme variable. + * + * Example usage: + * @code + * drupal_set_message(t('An error occurred and processing did not complete.'), 'error'); + * @endcode + * + * @param string|\Drupal\Component\Render\MarkupInterface $message + * (optional) The translated message to be displayed to the user. For + * consistency with other messages, it should begin with a capital letter and + * end with a period. + * @param string $type + * (optional) The message's type. Defaults to 'status'. These values are + * supported: + * - 'status' + * - 'warning' + * - 'error' + * @param bool $repeat + * (optional) If this is FALSE and the message is already set, then the + * message won't be repeated. Defaults to FALSE. + * + * @return array|null + * A multidimensional array with keys corresponding to the set message types. + * The indexed array values of each contain the set messages for that type, + * and each message is an associative array with the following format: + * - safe: Boolean indicating whether the message string has been marked as + * safe. Non-safe strings will be escaped automatically. + * - message: The message string. + * So, the following is an example of the full return array structure: + * @code + * array( + * 'status' => array( + * array( + * 'safe' => TRUE, + * 'message' => 'A safe markup string.', + * ), + * array( + * 'safe' => FALSE, + * 'message' => "$arbitrary_user_input to escape.", + * ), + * ), + * ); + * @endcode + * If there are no messages set, the function returns NULL. + * + * @internal + */ + private function setMessage($message = NULL, $type = 'status', $repeat = FALSE) { + if (isset($message)) { + if (!isset($_SESSION['messages'][$type])) { + $_SESSION['messages'][$type] = []; + } + + // Convert strings which are safe to the simplest Markup objects. + if (!($message instanceof Markup) && $message instanceof MarkupInterface) { + $message = Markup::create((string) $message); + } + + // Do not use strict type checking so that equivalent string and + // MarkupInterface objects are detected. + if ($repeat || !in_array($message, $_SESSION['messages'][$type])) { + $_SESSION['messages'][$type][] = $message; + } + + // Mark this page as being uncacheable. + $this->killSwitch->trigger(); + } + + // Messages not set when DB connection fails. + return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL; + } + + /** + * Returns all messages that have been set with drupal_set_message(). + * + * @param string $type + * (optional) Limit the messages returned by type. Defaults to NULL, meaning + * all types. These values are supported: + * - NULL + * - 'status' + * - 'warning' + * - 'error' + * @param bool $clear_queue + * (optional) If this is TRUE, the queue will be cleared of messages of the + * type specified in the $type parameter. Otherwise the queue will be left + * intact. Defaults to TRUE. + * + * @return array + * An associative, nested array of messages grouped by message type, with + * the top-level keys as the message type. The messages returned are + * limited to the type specified in the $type parameter, if any. If there + * are no messages of the specified type, an empty array is returned. See + * drupal_set_message() for the array structure of individual messages. + * + * @see drupal_set_message() + * @see status-messages.html.twig + * + * @internal + */ + private function getMessages($type = NULL, $clear_queue = TRUE) { + if ($messages = $this->setMessage()) { + if ($type) { + if ($clear_queue) { + unset($_SESSION['messages'][$type]); + } + if (isset($messages[$type])) { + return [$type => $messages[$type]]; + } + } + else { + if ($clear_queue) { + unset($_SESSION['messages']); + } + return $messages; + } + } + return []; + } + +} diff --git a/core/lib/Drupal/Core/Messenger/MessengerInterface.php b/core/lib/Drupal/Core/Messenger/MessengerInterface.php new file mode 100644 index 0000000..216835bc --- /dev/null +++ b/core/lib/Drupal/Core/Messenger/MessengerInterface.php @@ -0,0 +1,129 @@ + and adds attributes such as classes or * an HTML ID. * + * Properties: + * - #optional: Indicates whether the container should render when it has no + * visible children. Defaults to FALSE. + * * Usage example: * @code * $form['needs_accommodation'] = array( @@ -46,12 +51,14 @@ class Container extends RenderElement { public function getInfo() { $class = get_class($this); return [ + '#optional' => FALSE, '#process' => [ [$class, 'processGroup'], [$class, 'processContainer'], ], '#pre_render' => [ [$class, 'preRenderGroup'], + [$class, 'preRenderContainer'], ], '#theme_wrappers' => ['container'], ]; @@ -79,4 +86,22 @@ public static function processContainer(&$element, FormStateInterface $form_stat return $element; } + /** + * Prevents optional containers from rendering if they have no children. + * + * @param array $element + * An associative array containing the properties and children of the + * container. + * + * @return array + * The modified element. + */ + public static function preRenderContainer($element) { + // Do not render optional container elements if there are no children. + if (empty($element['#printed']) && !empty($element['#optional']) && !Element::getVisibleChildren($element)) { + $element['#printed'] = TRUE; + } + return $element; + } + } diff --git a/core/lib/Drupal/Core/Template/Loader/FilesystemLoader.php b/core/lib/Drupal/Core/Template/Loader/FilesystemLoader.php index 3f727cf..b84e790 100644 --- a/core/lib/Drupal/Core/Template/Loader/FilesystemLoader.php +++ b/core/lib/Drupal/Core/Template/Loader/FilesystemLoader.php @@ -24,7 +24,7 @@ class FilesystemLoader extends \Twig_Loader_Filesystem { * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler * The theme handler service. */ - public function __construct($paths = [], ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) { + public function __construct($paths, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) { parent::__construct($paths); // Add namespaced paths for modules and themes. diff --git a/core/lib/Drupal/Core/TypedData/ListDataDefinition.php b/core/lib/Drupal/Core/TypedData/ListDataDefinition.php index 3109cd2..25258a4 100644 --- a/core/lib/Drupal/Core/TypedData/ListDataDefinition.php +++ b/core/lib/Drupal/Core/TypedData/ListDataDefinition.php @@ -108,4 +108,13 @@ public function setItemDefinition(DataDefinitionInterface $definition) { return $this; } + /** + * Magic method: Implements a deep clone. + */ + public function __clone() { + // Ensure the itemDefinition property is actually cloned by overwriting the + // original reference. + $this->itemDefinition = clone $this->itemDefinition; + } + } diff --git a/core/modules/action/tests/src/Functional/ConfigurationTest.php b/core/modules/action/tests/src/Functional/ConfigurationTest.php index 472ba17..a260c07 100644 --- a/core/modules/action/tests/src/Functional/ConfigurationTest.php +++ b/core/modules/action/tests/src/Functional/ConfigurationTest.php @@ -44,14 +44,15 @@ public function testActionConfiguration() { $this->drupalPostForm('admin/config/system/actions/add/' . Crypt::hashBase64('action_goto_action'), $edit, t('Save')); $this->assertResponse(200); + $action_id = $edit['id']; + // Make sure that the new complex action was saved properly. $this->assertText(t('The action has been successfully saved.'), "Make sure we get a confirmation that we've successfully saved the complex action."); $this->assertText($action_label, "Make sure the action label appears on the configuration page after we've saved the complex action."); // Make another POST request to the action edit page. $this->clickLink(t('Configure')); - preg_match('|admin/config/system/actions/configure/(.+)|', $this->getUrl(), $matches); - $aid = $matches[1]; + $edit = []; $new_action_label = $this->randomMachineName(); $edit['label'] = $new_action_label; @@ -73,7 +74,7 @@ public function testActionConfiguration() { $this->clickLink(t('Delete')); $this->assertResponse(200); $edit = []; - $this->drupalPostForm("admin/config/system/actions/configure/$aid/delete", $edit, t('Delete')); + $this->drupalPostForm(NULL, $edit, t('Delete')); $this->assertResponse(200); // Make sure that the action was actually deleted. @@ -82,7 +83,7 @@ public function testActionConfiguration() { $this->assertResponse(200); $this->assertNoText($new_action_label, "Make sure the action label does not appear on the overview page after we've deleted the action."); - $action = Action::load($aid); + $action = Action::load($action_id); $this->assertFalse($action, 'Make sure the action is gone after being deleted.'); } diff --git a/core/modules/block/tests/src/Unit/BlockConfigEntityUnitTest.php b/core/modules/block/tests/src/Unit/BlockConfigEntityUnitTest.php index 8df0e93..921b5d2 100644 --- a/core/modules/block/tests/src/Unit/BlockConfigEntityUnitTest.php +++ b/core/modules/block/tests/src/Unit/BlockConfigEntityUnitTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\block\Unit; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Tests\Core\Plugin\Fixtures\TestConfigurablePlugin; use Drupal\Tests\UnitTestCase; @@ -20,11 +21,11 @@ class BlockConfigEntityUnitTest extends UnitTestCase { protected $entityType; /** - * The entity manager used for testing. + * The entity type manager used for testing. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The ID of the type of the entity under test. @@ -51,8 +52,8 @@ protected function setUp() { ->method('getProvider') ->will($this->returnValue('block')); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); - $this->entityManager->expects($this->any()) + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue($this->entityType)); @@ -60,7 +61,7 @@ protected function setUp() { $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); $container = new ContainerBuilder(); - $container->set('entity.manager', $this->entityManager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); \Drupal::setContainer($container); } diff --git a/core/modules/block/tests/src/Unit/BlockFormTest.php b/core/modules/block/tests/src/Unit/BlockFormTest.php index b846349..f19f921 100644 --- a/core/modules/block/tests/src/Unit/BlockFormTest.php +++ b/core/modules/block/tests/src/Unit/BlockFormTest.php @@ -3,6 +3,8 @@ namespace Drupal\Tests\block\Unit; use Drupal\block\BlockForm; +use Drupal\block\Entity\Block; +use Drupal\Core\Block\BlockBase; use Drupal\Core\Plugin\PluginFormFactoryInterface; use Drupal\Tests\UnitTestCase; @@ -83,6 +85,32 @@ protected function setUp() { } /** + * Mocks a block with a block plugin. + * + * @param string $machine_name + * The machine name of the block plugin. + * + * @return \Drupal\block\BlockInterface|\PHPUnit_Framework_MockObject_MockObject + * The mocked block. + */ + protected function getBlockMockWithMachineName($machine_name) { + $plugin = $this->getMockBuilder(BlockBase::class) + ->disableOriginalConstructor() + ->getMock(); + $plugin->expects($this->any()) + ->method('getMachineNameSuggestion') + ->will($this->returnValue($machine_name)); + + $block = $this->getMockBuilder(Block::class) + ->disableOriginalConstructor() + ->getMock(); + $block->expects($this->any()) + ->method('getPlugin') + ->will($this->returnValue($plugin)); + return $block; + } + + /** * Tests the unique machine name generator. * * @see \Drupal\block\BlockForm::getUniqueMachineName() diff --git a/core/modules/block_content/block_content.install b/core/modules/block_content/block_content.install index 6af2ac4..c6556e7 100644 --- a/core/modules/block_content/block_content.install +++ b/core/modules/block_content/block_content.install @@ -6,6 +6,23 @@ */ use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\StringTranslation\TranslatableMarkup; + +/** + * Implements hook_update_dependencies(). + */ +function block_content_update_dependencies() { + // The update function that adds the status field must run after + // content_translation_update_8400() which fixes NULL values for the + // 'content_translation_status' field. + if (\Drupal::moduleHandler()->moduleExists('content_translation')) { + $dependencies['block_content'][8400] = [ + 'content_translation' => 8400, + ]; + + return $dependencies; + } +} /** * Add 'revision_translation_affected' field to 'block_content' entities. @@ -70,5 +87,54 @@ function block_content_update_8300() { $entity_type = $definition_update_manager->getEntityType('block_content'); $entity_type->set('revision_data_table', 'block_content_field_revision'); $definition_update_manager->updateEntityType($entity_type); +} + +/** + * Add a publishing status field for block_content entities. + */ +function block_content_update_8400() { + $definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + + // Add the published entity key to the block_content entity type. + $entity_type = $definition_update_manager->getEntityType('block_content'); + $entity_keys = $entity_type->getKeys(); + $entity_keys['published'] = 'status'; + $entity_type->set('entity_keys', $entity_keys); + $definition_update_manager->updateEntityType($entity_type); + + // Add the publishing status field to the block_content entity type. + $status = BaseFieldDefinition::create('boolean') + ->setLabel(new TranslatableMarkup('Publishing status')) + ->setDescription(new TranslatableMarkup('A boolean indicating the published state.')) + ->setRevisionable(TRUE) + ->setTranslatable(TRUE) + ->setDefaultValue(TRUE); + + $has_content_translation_status_field = \Drupal::moduleHandler()->moduleExists('content_translation') && $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'block_content'); + if ($has_content_translation_status_field) { + $status->setInitialValueFromField('content_translation_status'); + } + else { + $status->setInitialValue(TRUE); + } + $definition_update_manager->installFieldStorageDefinition('status', 'block_content', 'block_content', $status); + + // Uninstall the 'content_translation_status' field if needed. + $database = \Drupal::database(); + if ($has_content_translation_status_field) { + // First we have to remove the field data. + $database->update($entity_type->getDataTable()) + ->fields(['content_translation_status' => NULL]) + ->execute(); + + // A site may have disabled revisionability for this entity type. + if ($entity_type->isRevisionable()) { + $database->update($entity_type->getRevisionDataTable()) + ->fields(['content_translation_status' => NULL]) + ->execute(); + } + $content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'block_content'); + $definition_update_manager->uninstallFieldStorageDefinition($content_translation_status); + } } diff --git a/core/modules/block_content/src/BlockContentAccessControlHandler.php b/core/modules/block_content/src/BlockContentAccessControlHandler.php index d0c19c5..7079ef4 100644 --- a/core/modules/block_content/src/BlockContentAccessControlHandler.php +++ b/core/modules/block_content/src/BlockContentAccessControlHandler.php @@ -19,7 +19,8 @@ class BlockContentAccessControlHandler extends EntityAccessControlHandler { */ protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { if ($operation === 'view') { - return AccessResult::allowed(); + return AccessResult::allowedIf($entity->isPublished())->addCacheableDependency($entity) + ->orIf(AccessResult::allowedIfHasPermission($account, 'administer blocks')); } return parent::checkAccess($entity, $operation, $account); } diff --git a/core/modules/block_content/src/BlockContentInterface.php b/core/modules/block_content/src/BlockContentInterface.php index 130cae1..75fdc59 100644 --- a/core/modules/block_content/src/BlockContentInterface.php +++ b/core/modules/block_content/src/BlockContentInterface.php @@ -4,12 +4,13 @@ use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityChangedInterface; +use Drupal\Core\Entity\EntityPublishedInterface; use Drupal\Core\Entity\RevisionLogInterface; /** * Provides an interface defining a custom block entity. */ -interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface { +interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface, EntityPublishedInterface { /** * Returns the block revision log message. diff --git a/core/modules/block_content/src/BlockContentListBuilder.php b/core/modules/block_content/src/BlockContentListBuilder.php index 9625917..7a4bdfc 100644 --- a/core/modules/block_content/src/BlockContentListBuilder.php +++ b/core/modules/block_content/src/BlockContentListBuilder.php @@ -4,7 +4,6 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityListBuilder; -use Drupal\Core\Routing\RedirectDestinationTrait; /** * Defines a class to build a listing of custom block entities. @@ -13,8 +12,6 @@ */ class BlockContentListBuilder extends EntityListBuilder { - use RedirectDestinationTrait; - /** * {@inheritdoc} */ @@ -31,15 +28,4 @@ public function buildRow(EntityInterface $entity) { return $row + parent::buildRow($entity); } - /** - * {@inheritdoc} - */ - public function getDefaultOperations(EntityInterface $entity) { - $operations = parent::getDefaultOperations($entity); - if (isset($operations['edit'])) { - $operations['edit']['query']['destination'] = $this->getRedirectDestination()->get(); - } - return $operations; - } - } diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php index 66a7a8f..3c8858f 100644 --- a/core/modules/block_content/src/Entity/BlockContent.php +++ b/core/modules/block_content/src/Entity/BlockContent.php @@ -2,8 +2,7 @@ namespace Drupal\block_content\Entity; -use Drupal\Core\Entity\ContentEntityBase; -use Drupal\Core\Entity\EntityChangedTrait; +use Drupal\Core\Entity\EditorialContentEntityBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; @@ -51,7 +50,8 @@ * "bundle" = "type", * "label" = "info", * "langcode" = "langcode", - * "uuid" = "uuid" + * "uuid" = "uuid", + * "published" = "status", * }, * revision_metadata_keys = { * "revision_user" = "revision_user", @@ -68,9 +68,7 @@ * caching. * See https://www.drupal.org/node/2284917#comment-9132521 for more information. */ -class BlockContent extends ContentEntityBase implements BlockContentInterface { - - use EntityChangedTrait; +class BlockContent extends EditorialContentEntityBase implements BlockContentInterface { /** * The theme the block is being created in. @@ -174,6 +172,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['type']->setLabel(t('Block type')) ->setDescription(t('The block type.')); + $fields['revision_log']->setDescription(t('The log entry explaining the changes in this revision.')); + $fields['info'] = BaseFieldDefinition::create('string') ->setLabel(t('Block description')) ->setDescription(t('A brief description of your block.')) @@ -187,35 +187,12 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDisplayConfigurable('form', TRUE) ->addConstraint('UniqueField', []); - $fields['revision_log'] = BaseFieldDefinition::create('string_long') - ->setLabel(t('Revision log message')) - ->setDescription(t('The log entry explaining the changes in this revision.')) - ->setRevisionable(TRUE) - ->setDisplayOptions('form', [ - 'type' => 'string_textarea', - 'weight' => 25, - 'settings' => [ - 'rows' => 4, - ], - ]); - $fields['changed'] = BaseFieldDefinition::create('changed') ->setLabel(t('Changed')) ->setDescription(t('The time that the custom block was last edited.')) ->setTranslatable(TRUE) ->setRevisionable(TRUE); - $fields['revision_created'] = BaseFieldDefinition::create('created') - ->setLabel(t('Revision create time')) - ->setDescription(t('The time that the current revision was created.')) - ->setRevisionable(TRUE); - - $fields['revision_user'] = BaseFieldDefinition::create('entity_reference') - ->setLabel(t('Revision user')) - ->setDescription(t('The user ID of the author of the current revision.')) - ->setSetting('target_type', 'user') - ->setRevisionable(TRUE); - return $fields; } diff --git a/core/modules/block_content/tests/src/Functional/UnpublishedBlockTest.php b/core/modules/block_content/tests/src/Functional/UnpublishedBlockTest.php new file mode 100644 index 0000000..026b0e5 --- /dev/null +++ b/core/modules/block_content/tests/src/Functional/UnpublishedBlockTest.php @@ -0,0 +1,47 @@ + 'Test block', + 'type' => 'basic', + ]); + $block_content->save(); + + $this->placeBlock('block_content:' . $block_content->uuid()); + + $this->drupalGet(''); + $page = $this->getSession()->getPage(); + $this->assertTrue($page->has('css', '.block-block-content' . $block_content->uuid())); + + $block_content->setPublished(FALSE); + $block_content->save(); + + $this->drupalGet(''); + $page = $this->getSession()->getPage(); + $this->assertFalse($page->has('css', '.block-block-content' . $block_content->uuid())); + } + +} diff --git a/core/modules/block_content/tests/src/Functional/Update/BlockContentUpdateTest.php b/core/modules/block_content/tests/src/Functional/Update/BlockContentUpdateTest.php index 804bf38..f2653b7 100644 --- a/core/modules/block_content/tests/src/Functional/Update/BlockContentUpdateTest.php +++ b/core/modules/block_content/tests/src/Functional/Update/BlockContentUpdateTest.php @@ -43,4 +43,27 @@ public function testSimpleUpdates() { $this->assertEqual('block_content_field_revision', $entity_type->getRevisionDataTable()); } + /** + * Tests adding a status field to the block content entity type. + * + * @see block_content_update_8400() + */ + public function testStatusFieldAddition() { + $schema = \Drupal::database()->schema(); + $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + + // Run updates. + $this->runUpdates(); + + // Check that the field exists and has the correct label. + $updated_field = $entity_definition_update_manager->getFieldStorageDefinition('status', 'block_content'); + $this->assertEqual('Publishing status', $updated_field->getLabel()); + + $content_translation_status = $entity_definition_update_manager->getFieldStorageDefinition('content_translation_status', 'block_content'); + $this->assertNull($content_translation_status); + + $this->assertFalse($schema->fieldExists('block_content_field_revision', 'content_translation_status')); + $this->assertFalse($schema->fieldExists('block_content_field_data', 'content_translation_status')); + } + } diff --git a/core/modules/book/tests/src/Functional/BookTestTrait.php b/core/modules/book/tests/src/Functional/BookTestTrait.php index f694406..26e1613 100644 --- a/core/modules/book/tests/src/Functional/BookTestTrait.php +++ b/core/modules/book/tests/src/Functional/BookTestTrait.php @@ -72,15 +72,15 @@ public function createBook($edit = []) { * @param $nodes * Nodes that should be in outline. * @param $previous - * (optional) Previous link node. Defaults to FALSE. + * Previous link node. * @param $up - * (optional) Up link node. Defaults to FALSE. + * Up link node. * @param $next - * (optional) Next link node. Defaults to FALSE. + * Next link node. * @param array $breadcrumb * The nodes that should be displayed in the breadcrumb. */ - public function checkBookNode(EntityInterface $node, $nodes, $previous = FALSE, $up = FALSE, $next = FALSE, array $breadcrumb) { + public function checkBookNode(EntityInterface $node, $nodes, $previous, $up, $next, array $breadcrumb) { // $number does not use drupal_static as it should not be reset // since it uniquely identifies each call to checkBookNode(). static $number = 0; diff --git a/core/modules/comment/src/Controller/CommentController.php b/core/modules/comment/src/Controller/CommentController.php index c834fa6..a01106d 100644 --- a/core/modules/comment/src/Controller/CommentController.php +++ b/core/modules/comment/src/Controller/CommentController.php @@ -288,7 +288,7 @@ public function replyFormAccess(EntityInterface $entity, $field_name, $pid = NUL // Check if the user has the proper permissions. $access = $access->andIf(AccessResult::allowedIfHasPermission($account, 'access comments')); - /// Load the parent comment. + // Load the parent comment. $comment = $this->entityManager()->getStorage('comment')->load($pid); // Check if the parent comment is published and belongs to the entity. $access = $access->andIf(AccessResult::allowedIf($comment && $comment->isPublished() && $comment->getCommentedEntityId() == $entity->id())); diff --git a/core/modules/comment/src/Plugin/views/sort/Thread.php b/core/modules/comment/src/Plugin/views/sort/Thread.php index 1984934..44fba29 100644 --- a/core/modules/comment/src/Plugin/views/sort/Thread.php +++ b/core/modules/comment/src/Plugin/views/sort/Thread.php @@ -16,14 +16,14 @@ class Thread extends SortPluginBase { public function query() { $this->ensureMyTable(); - //Read comment_render() in comment.module for an explanation of the - //thinking behind this sort. + // Read comment_render() in comment.module for an explanation of the + // thinking behind this sort. if ($this->options['order'] == 'DESC') { $this->query->addOrderBy($this->tableAlias, $this->realField, $this->options['order']); } else { $alias = $this->tableAlias . '_' . $this->realField . 'asc'; - //@todo is this secure? + // @todo is this secure? $this->query->addOrderBy(NULL, "SUBSTRING({$this->tableAlias}.{$this->realField}, 1, (LENGTH({$this->tableAlias}.{$this->realField}) - 1))", $this->options['order'], $alias); } } diff --git a/core/modules/comment/src/Plugin/views/wizard/Comment.php b/core/modules/comment/src/Plugin/views/wizard/Comment.php index 61d92c5..a74168d 100644 --- a/core/modules/comment/src/Plugin/views/wizard/Comment.php +++ b/core/modules/comment/src/Plugin/views/wizard/Comment.php @@ -28,14 +28,6 @@ class Comment extends WizardPluginBase { * Set default values for the filters. */ protected $filters = [ - 'status' => [ - 'value' => TRUE, - 'table' => 'comment_field_data', - 'field' => 'status', - 'plugin_id' => 'boolean', - 'entity_type' => 'comment', - 'entity_field' => 'status', - ], 'status_node' => [ 'value' => TRUE, 'table' => 'node_field_data', diff --git a/core/modules/comment/tests/src/Kernel/CommentBundlesTest.php b/core/modules/comment/tests/src/Kernel/CommentBundlesTest.php new file mode 100644 index 0000000..d907233 --- /dev/null +++ b/core/modules/comment/tests/src/Kernel/CommentBundlesTest.php @@ -0,0 +1,83 @@ +entityFieldManager = $this->container->get('entity_field.manager'); + + $this->installEntitySchema('comment'); + + // Create multiple comment bundles, + // each of which has a different target entity type. + $this->targetEntityTypes = [ + 'comment' => 'Comment', + 'node' => 'Node', + 'taxonomy_term' => 'Taxonomy Term', + ]; + foreach ($this->targetEntityTypes as $id => $label) { + CommentType::create([ + 'id' => 'comment_on_' . $id, + 'label' => 'Comment on ' . $label, + 'target_entity_type_id' => $id, + ])->save(); + } + } + + /** + * Test that the entity_id field is set correctly for each comment bundle. + */ + public function testEntityIdField() { + $field_definitions = []; + + foreach (array_keys($this->targetEntityTypes) as $id) { + $bundle = 'comment_on_' . $id; + $field_definitions[$bundle] = $this->entityFieldManager + ->getFieldDefinitions('comment', $bundle); + } + // Test that the value of the entity_id field for each bundle is correct. + foreach ($field_definitions as $bundle => $definition) { + $entity_type_id = str_replace('comment_on_', '', $bundle); + $target_type = $definition['entity_id']->getSetting('target_type'); + $this->assertEquals($entity_type_id, $target_type); + + // Verify that the target type remains correct + // in the deeply-nested object properties. + $nested_target_type = $definition['entity_id']->getItemDefinition()->getFieldDefinition()->getSetting('target_type'); + $this->assertEquals($entity_type_id, $nested_target_type); + } + + } + +} diff --git a/core/modules/config/src/Tests/ConfigEntityListTest.php b/core/modules/config/src/Tests/ConfigEntityListTest.php index e9950ea..46c0c06 100644 --- a/core/modules/config/src/Tests/ConfigEntityListTest.php +++ b/core/modules/config/src/Tests/ConfigEntityListTest.php @@ -2,6 +2,7 @@ namespace Drupal\config\Tests; +use Drupal\Core\Routing\RedirectDestinationTrait; use Drupal\simpletest\WebTestBase; use Drupal\config_test\Entity\ConfigTest; use Drupal\Core\Entity\EntityStorageInterface; @@ -13,6 +14,8 @@ */ class ConfigEntityListTest extends WebTestBase { + use RedirectDestinationTrait; + /** * Modules to enable. * @@ -54,17 +57,17 @@ public function testList() { 'edit' => [ 'title' => t('Edit'), 'weight' => 10, - 'url' => $entity->urlInfo(), + 'url' => $entity->toUrl()->setOption('query', $this->getRedirectDestination()->getAsArray()), ], 'disable' => [ 'title' => t('Disable'), 'weight' => 40, - 'url' => $entity->urlInfo('disable'), + 'url' => $entity->toUrl('disable')->setOption('query', $this->getRedirectDestination()->getAsArray()), ], 'delete' => [ 'title' => t('Delete'), 'weight' => 100, - 'url' => $entity->urlInfo('delete-form'), + 'url' => $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()), ], ]; @@ -129,12 +132,12 @@ public function testList() { 'edit' => [ 'title' => t('Edit'), 'weight' => 10, - 'url' => $entity->urlInfo(), + 'url' => $entity->toUrl()->setOption('query', $this->getRedirectDestination()->getAsArray()), ], 'delete' => [ 'title' => t('Delete'), 'weight' => 100, - 'url' => $entity->urlInfo('delete-form'), + 'url' => $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()), ], ]; diff --git a/core/modules/contact/tests/src/Unit/MailHandlerTest.php b/core/modules/contact/tests/src/Unit/MailHandlerTest.php index 898ea22..ba3bfad 100644 --- a/core/modules/contact/tests/src/Unit/MailHandlerTest.php +++ b/core/modules/contact/tests/src/Unit/MailHandlerTest.php @@ -234,7 +234,7 @@ public function getSendMailMessages() { $results[] = $result + $default_result; $data[] = [$message, $sender, $results]; - //For authenticated user. + // For authenticated user. $results = []; $message = $this->getAuthenticatedMockMessage(); $sender = $this->getMockSender(FALSE, 'user@drupal.org'); diff --git a/core/modules/content_moderation/src/ParamConverter/EntityRevisionConverter.php b/core/modules/content_moderation/src/ParamConverter/EntityRevisionConverter.php index 561c0c0..8e7e361 100644 --- a/core/modules/content_moderation/src/ParamConverter/EntityRevisionConverter.php +++ b/core/modules/content_moderation/src/ParamConverter/EntityRevisionConverter.php @@ -75,7 +75,7 @@ protected function isEditFormPage(Route $route) { return FALSE; } $entity_type = $this->entityManager->getDefinition($entity_type_id); - return $operation == 'edit' && $entity_type && $entity_type->isRevisionable(); + return in_array($operation, ['default', 'edit']) && $entity_type && $entity_type->isRevisionable(); } } diff --git a/core/modules/content_moderation/tests/src/Kernel/EntityRevisionConverterTest.php b/core/modules/content_moderation/tests/src/Kernel/EntityRevisionConverterTest.php index 6f209d1..3deaffa 100644 --- a/core/modules/content_moderation/tests/src/Kernel/EntityRevisionConverterTest.php +++ b/core/modules/content_moderation/tests/src/Kernel/EntityRevisionConverterTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\content_moderation\Kernel; use Drupal\entity_test\Entity\EntityTest; +use Drupal\entity_test\Entity\EntityTestRev; use Drupal\KernelTests\KernelTestBase; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; @@ -24,19 +25,38 @@ class EntityRevisionConverterTest extends KernelTestBase { ]; /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * The router without access checks. + * + * @var \Symfony\Component\Routing\RouterInterface + */ + protected $router; + + /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); $this->installEntitySchema('entity_test'); + $this->installEntitySchema('entity_test_rev'); $this->installEntitySchema('node'); $this->installEntitySchema('user'); $this->installEntitySchema('content_moderation_state'); $this->installSchema('system', 'router'); $this->installSchema('system', 'sequences'); $this->installSchema('node', 'node_access'); + $this->installConfig(['content_moderation']); \Drupal::service('router.builder')->rebuild(); + + $this->entityTypeManager = $this->container->get('entity_type.manager'); + $this->router = $this->container->get('router.no_access_checks'); } /** @@ -49,19 +69,43 @@ public function testConvertNonRevisionableEntityType() { $entity_test->save(); - /** @var \Symfony\Component\Routing\RouterInterface $router */ - $router = \Drupal::service('router.no_access_checks'); - $result = $router->match('/entity_test/' . $entity_test->id()); + $result = $this->router->match('/entity_test/' . $entity_test->id()); $this->assertInstanceOf(EntityTest::class, $result['entity_test']); $this->assertEquals($entity_test->getRevisionId(), $result['entity_test']->getRevisionId()); } /** + * @covers ::applies + */ + public function testConvertNoEditFormHandler() { + $workflow = Workflow::load('editorial'); + $workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_rev', 'entity_test_rev'); + $workflow->save(); + + $entity_test_rev = EntityTestRev::create([ + 'name' => 'Default Revision', + 'moderation_state' => 'published', + ]); + $entity_test_rev->save(); + + $entity_test_rev->name = 'Pending revision'; + $entity_test_rev->moderation_state = 'draft'; + $entity_test_rev->save(); + + // Ensure the entity type does not provide an explicit 'edit' form class. + $definition = $this->entityTypeManager->getDefinition($entity_test_rev->getEntityTypeId()); + $this->assertNull($definition->getFormClass('edit')); + + // Ensure the revision converter is invoked for the edit route. + $result = $this->router->match("/entity_test_rev/manage/{$entity_test_rev->id()}/edit"); + $this->assertEquals($entity_test_rev->getRevisionId(), $result['entity_test_rev']->getRevisionId()); + } + + /** * @covers ::convert */ public function testConvertWithRevisionableEntityType() { - $this->installConfig(['content_moderation']); $node_type = NodeType::create([ 'type' => 'article', ]); @@ -89,9 +133,7 @@ public function testConvertWithRevisionableEntityType() { $node->save(); $revision_ids[] = $node->getRevisionId(); - /** @var \Symfony\Component\Routing\RouterInterface $router */ - $router = \Drupal::service('router.no_access_checks'); - $result = $router->match('/node/' . $node->id() . '/edit'); + $result = $this->router->match('/node/' . $node->id() . '/edit'); $this->assertInstanceOf(Node::class, $result['node']); $this->assertEquals($revision_ids[2], $result['node']->getRevisionId()); diff --git a/core/modules/content_translation/content_translation.install b/core/modules/content_translation/content_translation.install index 455d5ed..0e47270 100644 --- a/core/modules/content_translation/content_translation.install +++ b/core/modules/content_translation/content_translation.install @@ -59,6 +59,7 @@ function content_translation_update_8400() { $entity_type_manager = \Drupal::entityTypeManager(); $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + $entity_type_manager->clearCachedDefinitions(); foreach ($content_translation_manager->getSupportedEntityTypes() as $entity_type_id => $entity_type_definition) { $storage = $entity_type_manager->getStorage($entity_type_id); if ($storage instanceof SqlEntityStorageInterface) { diff --git a/core/modules/editor/tests/src/Unit/EditorConfigEntityUnitTest.php b/core/modules/editor/tests/src/Unit/EditorConfigEntityUnitTest.php index 4e96f4a..e40e349 100644 --- a/core/modules/editor/tests/src/Unit/EditorConfigEntityUnitTest.php +++ b/core/modules/editor/tests/src/Unit/EditorConfigEntityUnitTest.php @@ -3,6 +3,8 @@ namespace Drupal\Tests\editor\Unit; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\editor\Entity\Editor; use Drupal\Tests\UnitTestCase; @@ -22,9 +24,9 @@ class EditorConfigEntityUnitTest extends UnitTestCase { /** * The entity manager used for testing. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The ID of the type of the entity under test. @@ -66,8 +68,8 @@ protected function setUp() { ->method('getProvider') ->will($this->returnValue('editor')); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); - $this->entityManager->expects($this->any()) + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue($this->entityType)); @@ -78,10 +80,16 @@ protected function setUp() { ->disableOriginalConstructor() ->getMock(); + $entity_manager = new EntityManager(); + $container = new ContainerBuilder(); - $container->set('entity.manager', $this->entityManager); + $container->set('entity.manager', $entity_manager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); $container->set('plugin.manager.editor', $this->editorPluginManager); + // Inject the container into entity.manager so it can defer to + // entity_type.manager. + $entity_manager->setContainer($container); \Drupal::setContainer($container); } @@ -120,7 +128,7 @@ public function testCalculateDependencies() { ->with($format_id) ->will($this->returnValue($filter_format)); - $this->entityManager->expects($this->once()) + $this->entityTypeManager->expects($this->once()) ->method('getStorage') ->with('filter_format') ->will($this->returnValue($storage)); diff --git a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAutoCreateTest.php b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAutoCreateTest.php index 90895b1..1e6511f 100644 --- a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAutoCreateTest.php +++ b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAutoCreateTest.php @@ -213,17 +213,17 @@ public function testMultipleTargetBundles() { // a way to catch and assert user-triggered errors. // Test the case when the field config settings are inconsistent. - //unset($handler_settings['auto_create_bundle']); - //$field_config->setSetting('handler_settings', $handler_settings); - //$field_config->save(); + // unset($handler_settings['auto_create_bundle']); + // $field_config->setSetting('handler_settings', $handler_settings); + // $field_config->save(); // - //$this->drupalGet('node/add/' . $this->referencingType); - //$error_message = sprintf( + // $this->drupalGet('node/add/' . $this->referencingType); + // $error_message = sprintf( // "Create referenced entities if they don't already exist option is enabled but a specific destination bundle is not set. You should re-visit and fix the settings of the '%s' (%s) field.", // $field_config->getLabel(), // $field_config->getName() - //); - //$this->assertErrorLogged($error_message); + // ); + // $this->assertErrorLogged($error_message); } } diff --git a/core/modules/field/tests/src/Kernel/EntityReference/Views/EntityReferenceRelationshipTest.php b/core/modules/field/tests/src/Kernel/EntityReference/Views/EntityReferenceRelationshipTest.php index 2b0be85..5f4c8ee 100644 --- a/core/modules/field/tests/src/Kernel/EntityReference/Views/EntityReferenceRelationshipTest.php +++ b/core/modules/field/tests/src/Kernel/EntityReference/Views/EntityReferenceRelationshipTest.php @@ -272,7 +272,7 @@ public function testDataTableRelationshipWithLongFieldName() { $this->assertEqual($row->_entity->id(), $this->entities[$index]->id()); // Test the forward relationship. - //$this->assertEqual($row->entity_test_entity_test_mul__field_data_test_id, 1); + // $this->assertEqual($row->entity_test_entity_test_mul__field_data_test_id, 1); // Test that the correct relationship entity is on the row. $this->assertEqual($row->_relationship_entities['field_test_data_with_a_long_name']->id(), 1); diff --git a/core/modules/field/tests/src/Kernel/FieldDefinitionIntegrityTest.php b/core/modules/field/tests/src/Kernel/FieldDefinitionIntegrityTest.php index b4add53..85d6b0b 100644 --- a/core/modules/field/tests/src/Kernel/FieldDefinitionIntegrityTest.php +++ b/core/modules/field/tests/src/Kernel/FieldDefinitionIntegrityTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\field\Kernel; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Extension\Extension; use Drupal\KernelTests\KernelTestBase; @@ -21,8 +22,8 @@ class FieldDefinitionIntegrityTest extends KernelTestBase { * Tests the integrity of field plugin definitions. */ public function testFieldPluginDefinitionIntegrity() { - - // Enable all core modules that provide field plugins. + // Enable all core modules that provide field plugins, and their + // dependencies. $modules = system_rebuild_module_data(); $modules = array_filter($modules, function (Extension $module) { // Filter contrib, hidden, already enabled modules and modules in the @@ -36,7 +37,13 @@ public function testFieldPluginDefinitionIntegrity() { } return FALSE; }); - $this->enableModules(array_keys($modules)); + // Gather the dependencies of the modules. + $dependencies = NestedArray::mergeDeepArray(array_map(function (Extension $module) { + return array_keys($module->requires); + }, $modules)); + $modules = array_unique(NestedArray::mergeDeep(array_keys($modules), $dependencies)); + + $this->enableModules($modules); /** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */ $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); diff --git a/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php b/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php index 578eeda..ba47d91 100644 --- a/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php +++ b/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php @@ -93,7 +93,7 @@ public function testNumberItem() { $this->assertEqual($entity->field_float->value, $new_float); $this->assertEqual($entity->field_decimal->value, $new_decimal); - /// Test sample item generation. + // Test sample item generation. $entity = EntityTest::create(); $entity->field_integer->generateSampleItems(); $entity->field_float->generateSampleItems(); diff --git a/core/modules/field/tests/src/Unit/FieldConfigEntityUnitTest.php b/core/modules/field/tests/src/Unit/FieldConfigEntityUnitTest.php index d1824cf..b7dadf4 100644 --- a/core/modules/field/tests/src/Unit/FieldConfigEntityUnitTest.php +++ b/core/modules/field/tests/src/Unit/FieldConfigEntityUnitTest.php @@ -10,6 +10,9 @@ use Drupal\Core\Entity\EntityType; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityFieldManagerInterface; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\field\Entity\FieldConfig; use Drupal\Tests\UnitTestCase; @@ -34,6 +37,20 @@ class FieldConfigEntityUnitTest extends UnitTestCase { protected $entityManager; /** + * The entity type manager used for testing. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityTypeManager; + + /** + * The entity field manager used for testing. + * + * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityFieldManager; + + /** * The ID of the type of the entity under test. * * @var string @@ -75,7 +92,9 @@ protected function setUp() { $this->entityTypeId = $this->randomMachineName(); $this->entityType = $this->getMock('\Drupal\Core\Config\Entity\ConfigEntityTypeInterface'); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager = new EntityManager(); + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + $this->entityFieldManager = $this->getMock(EntityFieldManagerInterface::class); $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); @@ -85,9 +104,14 @@ protected function setUp() { $container = new ContainerBuilder(); $container->set('entity.manager', $this->entityManager); + $container->set('entity_field.manager', $this->entityFieldManager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); $container->set('config.typed', $this->typedConfigManager); $container->set('plugin.manager.field.field_type', $this->fieldTypePluginManager); + // Inject the container into entity.manager so it can defer to + // entity_type.manager, etc. + $this->entityManager->setContainer($container); \Drupal::setContainer($container); // Create a mock FieldStorageConfig object. @@ -102,7 +126,7 @@ protected function setUp() { ->method('getSettings') ->willReturn([]); // Place the field in the mocked entity manager's field registry. - $this->entityManager->expects($this->any()) + $this->entityFieldManager->expects($this->any()) ->method('getFieldStorageDefinitions') ->with('test_entity_type') ->will($this->returnValue([ @@ -120,19 +144,19 @@ public function testCalculateDependencies() { ->method('getBundleConfigDependency') ->will($this->returnValue(['type' => 'config', 'name' => 'test.test_entity_type.id'])); - $this->entityManager->expects($this->at(0)) + $this->entityTypeManager->expects($this->at(0)) ->method('getDefinition') ->with($this->entityTypeId) ->willReturn($this->entityType); - $this->entityManager->expects($this->at(1)) + $this->entityTypeManager->expects($this->at(1)) ->method('getDefinition') ->with($this->entityTypeId) ->willReturn($this->entityType); - $this->entityManager->expects($this->at(2)) + $this->entityTypeManager->expects($this->at(2)) ->method('getDefinition') ->with($this->entityTypeId) ->willReturn($this->entityType); - $this->entityManager->expects($this->at(3)) + $this->entityTypeManager->expects($this->at(3)) ->method('getDefinition') ->with('test_entity_type') ->willReturn($target_entity_type); @@ -168,7 +192,7 @@ public function testCalculateDependenciesIncorrectBundle() { ->with('test_bundle_not_exists') ->will($this->returnValue(NULL)); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getStorage') ->with('bundle_entity_type') ->will($this->returnValue($storage)); @@ -178,19 +202,19 @@ public function testCalculateDependenciesIncorrectBundle() { 'bundle_entity_type' => 'bundle_entity_type', ]); - $this->entityManager->expects($this->at(0)) + $this->entityTypeManager->expects($this->at(0)) ->method('getDefinition') ->with($this->entityTypeId) ->willReturn($this->entityType); - $this->entityManager->expects($this->at(1)) + $this->entityTypeManager->expects($this->at(1)) ->method('getDefinition') ->with($this->entityTypeId) ->willReturn($this->entityType); - $this->entityManager->expects($this->at(2)) + $this->entityTypeManager->expects($this->at(2)) ->method('getDefinition') ->with($this->entityTypeId) ->willReturn($this->entityType); - $this->entityManager->expects($this->at(3)) + $this->entityTypeManager->expects($this->at(3)) ->method('getDefinition') ->with('test_entity_type') ->willReturn($target_entity_type); @@ -267,7 +291,7 @@ public function testToArray() { 'dependencies' => [], 'field_type' => 'test_field', ]; - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue($this->entityType)); @@ -289,7 +313,7 @@ public function testToArray() { public function testGetType() { // Ensure that FieldConfig::getType() is not delegated to // FieldStorage. - $this->entityManager->expects($this->never()) + $this->entityFieldManager->expects($this->never()) ->method('getFieldStorageDefinitions'); $this->fieldStorage->expects($this->never()) ->method('getType'); diff --git a/core/modules/field/tests/src/Unit/FieldStorageConfigAccessControlHandlerTest.php b/core/modules/field/tests/src/Unit/FieldStorageConfigAccessControlHandlerTest.php index 4ed7512..1d47017 100644 --- a/core/modules/field/tests/src/Unit/FieldStorageConfigAccessControlHandlerTest.php +++ b/core/modules/field/tests/src/Unit/FieldStorageConfigAccessControlHandlerTest.php @@ -6,8 +6,9 @@ use Drupal\Core\Cache\Context\CacheContextsManager; use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; use Drupal\Core\DependencyInjection\Container; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityManager; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Session\AccountInterface; use Drupal\field\Entity\FieldStorageConfig; @@ -126,31 +127,37 @@ protected function setUp() { $storage_access_control_handler = new FieldStorageConfigAccessControlHandler($storageType); $storage_access_control_handler->setModuleHandler($this->moduleHandler); - $entityManager = $this->getMock(EntityManagerInterface::class); - $entityManager + $entity_type_manager = $this->getMock(EntityTypeManagerInterface::class); + $entity_type_manager ->expects($this->any()) ->method('getDefinition') ->willReturnMap([ ['field_storage_config', TRUE, $storageType], ['node', TRUE, $entityType], ]); - $entityManager + $entity_type_manager ->expects($this->any()) ->method('getStorage') ->willReturnMap([ ['field_storage_config', $this->getMock(EntityStorageInterface::class)], ]); - $entityManager + $entity_type_manager ->expects($this->any()) ->method('getAccessControlHandler') ->willReturnMap([ ['field_storage_config', $storage_access_control_handler], ]); + $entity_manager = new EntityManager(); + $container = new Container(); - $container->set('entity.manager', $entityManager); + $container->set('entity.manager', $entity_manager); + $container->set('entity_type.manager', $entity_type_manager); $container->set('uuid', $this->getMock(UuidInterface::class)); $container->set('cache_contexts_manager', $this->prophesize(CacheContextsManager::class)); + // Inject the container into entity.manager so it can defer to + // entity_type.manager. + $entity_manager->setContainer($container); \Drupal::setContainer($container); $this->fieldStorage = new FieldStorageConfig([ diff --git a/core/modules/field/tests/src/Unit/FieldStorageConfigEntityUnitTest.php b/core/modules/field/tests/src/Unit/FieldStorageConfigEntityUnitTest.php index 89b7396..970532a 100644 --- a/core/modules/field/tests/src/Unit/FieldStorageConfigEntityUnitTest.php +++ b/core/modules/field/tests/src/Unit/FieldStorageConfigEntityUnitTest.php @@ -8,6 +8,8 @@ namespace Drupal\Tests\field\Unit; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldException; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; @@ -22,11 +24,11 @@ class FieldStorageConfigEntityUnitTest extends UnitTestCase { /** - * The entity manager used for testing. + * The entity type manager used for testing. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The ID of the type of the entity under test. @@ -53,14 +55,19 @@ class FieldStorageConfigEntityUnitTest extends UnitTestCase { * {@inheritdoc} */ protected function setUp() { - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $entity_manager = new EntityManager(); + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); $this->fieldTypeManager = $this->getMock(FieldTypePluginManagerInterface::class); $container = new ContainerBuilder(); - $container->set('entity.manager', $this->entityManager); + $container->set('entity.manager', $entity_manager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); $container->set('plugin.manager.field.field_type', $this->fieldTypeManager); + // Inject the container into entity.manager so it can defer to + // entity_type.manager. + $entity_manager->setContainer($container); \Drupal::setContainer($container); } @@ -85,7 +92,7 @@ public function testCalculateDependencies() { // ConfigEntityBase::addDependency() to get the provider of the field config // entity type and once in FieldStorageConfig::calculateDependencies() to // get the provider of the entity type that field is attached to. - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->willReturnMap([ ['field_storage_config', TRUE, $fieldStorageConfigentityType], diff --git a/core/modules/field_ui/tests/src/Functional/EntityDisplayModeTest.php b/core/modules/field_ui/tests/src/Functional/EntityDisplayModeTest.php index 3bb0b13..78764fb 100644 --- a/core/modules/field_ui/tests/src/Functional/EntityDisplayModeTest.php +++ b/core/modules/field_ui/tests/src/Functional/EntityDisplayModeTest.php @@ -2,6 +2,9 @@ namespace Drupal\Tests\field_ui\Functional; +use Drupal\Core\Entity\Entity\EntityFormMode; +use Drupal\Core\Entity\Entity\EntityViewMode; +use Drupal\Core\Url; use Drupal\Tests\BrowserTestBase; /** @@ -68,6 +71,14 @@ public function testEntityViewModeUI() { // Test editing the view mode. $this->drupalGet('admin/structure/display-modes/view/manage/entity_test.' . $edit['id']); + // Test that the link templates added by field_ui_entity_type_build() are + // generating valid routes. + $view_mode = EntityViewMode::load('entity_test.' . $edit['id']); + $this->assertEquals(Url::fromRoute('entity.entity_view_mode.collection')->toString(), $view_mode->toUrl('collection')->toString()); + $this->assertEquals(Url::fromRoute('entity.entity_view_mode.add_form', ['entity_type_id' => $view_mode->getTargetType()])->toString(), $view_mode->toUrl('add-form')->toString()); + $this->assertEquals(Url::fromRoute('entity.entity_view_mode.edit_form', ['entity_view_mode' => $view_mode->id()])->toString(), $view_mode->toUrl('edit-form')->toString()); + $this->assertEquals(Url::fromRoute('entity.entity_view_mode.delete_form', ['entity_view_mode' => $view_mode->id()])->toString(), $view_mode->toUrl('delete-form')->toString()); + // Test deleting the view mode. $this->clickLink(t('Delete')); $this->assertRaw(t('Are you sure you want to delete the view mode %label?', ['%label' => $edit['label']])); @@ -114,6 +125,14 @@ public function testEntityFormModeUI() { // Test editing the form mode. $this->drupalGet('admin/structure/display-modes/form/manage/entity_test.' . $edit['id']); + // Test that the link templates added by field_ui_entity_type_build() are + // generating valid routes. + $form_mode = EntityFormMode::load('entity_test.' . $edit['id']); + $this->assertEquals(Url::fromRoute('entity.entity_form_mode.collection')->toString(), $form_mode->toUrl('collection')->toString()); + $this->assertEquals(Url::fromRoute('entity.entity_form_mode.add_form', ['entity_type_id' => $form_mode->getTargetType()])->toString(), $form_mode->toUrl('add-form')->toString()); + $this->assertEquals(Url::fromRoute('entity.entity_form_mode.edit_form', ['entity_form_mode' => $form_mode->id()])->toString(), $form_mode->toUrl('edit-form')->toString()); + $this->assertEquals(Url::fromRoute('entity.entity_form_mode.delete_form', ['entity_form_mode' => $form_mode->id()])->toString(), $form_mode->toUrl('delete-form')->toString()); + // Test deleting the form mode. $this->clickLink(t('Delete')); $this->assertRaw(t('Are you sure you want to delete the form mode %label?', ['%label' => $edit['label']])); diff --git a/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/FileUriTest.php b/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/FileUriTest.php index 78d5082..5a59182 100644 --- a/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/FileUriTest.php +++ b/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/FileUriTest.php @@ -4,7 +4,6 @@ use Drupal\file\Plugin\migrate\process\d6\FileUri; use Drupal\migrate\MigrateExecutable; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Row; use Drupal\Tests\migrate\Unit\MigrateTestCase; @@ -69,7 +68,7 @@ public function testTemporary() { } protected function doTransform(array $value) { - $executable = new MigrateExecutable($this->getMigration(), new MigrateMessage()); + $executable = new MigrateExecutable($this->getMigration()); $row = new Row(); return (new FileUri([], 'file_uri', [])) diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index e309534..87ff713 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -496,8 +496,8 @@ function _filter_url($text, $filter) { $valid_url_query_chars = '[a-zA-Z0-9!?\*\'@\(\);:&=\+\$\/%#\[\]\-_\.,~|]'; $valid_url_query_ending_chars = '[a-zA-Z0-9_&=#\/]'; - //full path - //and allow @ in a url, but only in the middle. Catch things like http://example.com/@user/ + // full path + // and allow @ in a url, but only in the middle. Catch things like http://example.com/@user/ $valid_url_path = '(?:(?:' . $valid_url_path_characters . '*(?:' . $valid_url_balanced_parens . $valid_url_path_characters . '*)*' . $valid_url_ending_characters . ')|(?:@' . $valid_url_path_characters . '+\/))'; // Prepare domain name pattern. diff --git a/core/modules/filter/tests/src/Functional/FilterAdminTest.php b/core/modules/filter/tests/src/Functional/FilterAdminTest.php index 21431e7..a6e4293 100644 --- a/core/modules/filter/tests/src/Functional/FilterAdminTest.php +++ b/core/modules/filter/tests/src/Functional/FilterAdminTest.php @@ -4,6 +4,7 @@ use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Url; use Drupal\filter\Entity\FilterFormat; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; @@ -135,16 +136,9 @@ public function testFormatAdmin() { // Edit text format. $this->drupalGet('admin/config/content/formats'); - // Cannot use the assertNoLinkByHref method as it does partial url matching - // and 'admin/config/content/formats/manage/' . $format_id . '/disable' - // exists. - // @todo: See https://www.drupal.org/node/2031223 for the above. - $edit_link = $this->xpath('//a[@href=:href]', [ - ':href' => \Drupal::url('entity.filter_format.edit_form', ['filter_format' => $format_id]) - ]); - $this->assertNotEmpty($edit_link, format_string('Link href %href found.', - ['%href' => 'admin/config/content/formats/manage/' . $format_id] - )); + $destination = Url::fromRoute('filter.admin_overview')->toString(); + $edit_href = Url::fromRoute('entity.filter_format.edit_form', ['filter_format' => $format_id], ['query' => ['destination' => $destination]])->toString(); + $this->assertSession()->linkByHrefExists($edit_href); $this->drupalGet('admin/config/content/formats/manage/' . $format_id); $this->drupalPostForm(NULL, [], t('Save configuration')); diff --git a/core/modules/forum/tests/src/Functional/ForumTest.php b/core/modules/forum/tests/src/Functional/ForumTest.php index 260f63a..9ba4be7 100644 --- a/core/modules/forum/tests/src/Functional/ForumTest.php +++ b/core/modules/forum/tests/src/Functional/ForumTest.php @@ -118,7 +118,7 @@ protected function setUp() { * Tests forum functionality through the admin and user interfaces. */ public function testForum() { - //Check that the basic forum install creates a default forum topic + // Check that the basic forum install creates a default forum topic $this->drupalGet('/forum'); // Look for the "General discussion" default forum $this->assertRaw(Link::createFromRoute(t('General discussion'), 'forum.page', ['taxonomy_term' => 1])->toString(), "Found the default forum at the /forum listing"); diff --git a/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonAnonTest.php new file mode 100644 index 0000000..d4ee9ab --- /dev/null +++ b/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonAnonTest.php @@ -0,0 +1,74 @@ +applyHalFieldNormalization($default_normalization); + + return $normalization + [ + '_links' => [ + 'self' => [ + 'href' => $this->baseUrl . '/block/1?_format=hal_json', + ], + 'type' => [ + 'href' => $this->baseUrl . '/rest/type/block_content/basic', + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + protected function getNormalizedPostEntity() { + return parent::getNormalizedPostEntity() + [ + '_links' => [ + 'type' => [ + 'href' => $this->baseUrl . '/rest/type/block_content/basic', + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + protected function getExpectedCacheContexts() { + // The 'url.site' cache context is added for '_links' in the response. + return Cache::mergeTags(parent::getExpectedCacheContexts(), ['url.site']); + } + +} diff --git a/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonBasicAuthTest.php b/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonBasicAuthTest.php new file mode 100644 index 0000000..94d3ff4 --- /dev/null +++ b/core/modules/hal/tests/src/Functional/EntityResource/BlockContent/BlockContentHalJsonBasicAuthTest.php @@ -0,0 +1,24 @@ +entityTypeId = $this->randomMachineName(); $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager = new EntityManager(); + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); @@ -72,9 +83,13 @@ protected function setUp() { $container = new ContainerBuilder(); $container->set('entity.manager', $this->entityManager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); $container->set('config.typed', $this->typedConfigManager); $container->set('config.storage', $this->configEntityStorageInterface); + // Inject the container into entity.manager so it can defer to other entity + // services. + $this->entityManager->setContainer($container); \Drupal::setContainer($container); } @@ -88,7 +103,7 @@ public function testCalculateDependencies() { ->method('getBundleConfigDependency') ->will($this->returnValue(['type' => 'config', 'name' => 'test.test_entity_type.id'])); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with('test_entity_type') ->will($this->returnValue($target_entity_type)); @@ -254,16 +269,20 @@ public function testLoadByEntityTypeBundle($config_id, ContentLanguageSettings $ ->method('create') ->will($this->returnValue($nullConfig)); - $this->entityManager + $this->entityTypeManager ->expects($this->any()) ->method('getStorage') ->with('language_content_settings') ->will($this->returnValue($this->configEntityStorageInterface)); - $this->entityManager->expects($this->any()) + + $entity_type_repository = $this->getMockForAbstractClass(EntityTypeRepositoryInterface::class); + $entity_type_repository->expects($this->any()) ->method('getEntityTypeFromClass') - ->with('Drupal\language\Entity\ContentLanguageSettings') + ->with(ContentLanguageSettings::class) ->willReturn('language_content_settings'); + \Drupal::getContainer()->set('entity_type.repository', $entity_type_repository); + $config = ContentLanguageSettings::loadByEntityTypeBundle($type, $bundle); $this->assertSame($expected_langcode, $config->getDefaultLangcode()); diff --git a/core/modules/locale/tests/src/Functional/LocaleConfigTranslationTest.php b/core/modules/locale/tests/src/Functional/LocaleConfigTranslationTest.php index 4d1b9df..beae85d 100644 --- a/core/modules/locale/tests/src/Functional/LocaleConfigTranslationTest.php +++ b/core/modules/locale/tests/src/Functional/LocaleConfigTranslationTest.php @@ -231,7 +231,7 @@ protected function assertNodeConfig($required, $optional) { } // Check the optional default configuration in node module. - $string = $this->storage->findString(['source' => 'No front page content has been created yet.', 'context' => '', 'type' => 'configuration']); + $string = $this->storage->findString(['source' => 'No front page content has been created yet.
Follow the User Guide to start building your site.', 'context' => '', 'type' => 'configuration']); if ($optional) { $this->assertFalse($this->config('views.view.frontpage')->isNew()); $this->assertTrue($string, 'Node view text can be found with node and views modules.'); diff --git a/core/modules/media/src/Plugin/views/wizard/Media.php b/core/modules/media/src/Plugin/views/wizard/Media.php index eb45701..30a7ad6 100644 --- a/core/modules/media/src/Plugin/views/wizard/Media.php +++ b/core/modules/media/src/Plugin/views/wizard/Media.php @@ -23,22 +23,6 @@ class Media extends WizardPluginBase { protected $createdColumn = 'media_field_data-created'; /** - * Set default values for the filters. - * - * @var array - */ - protected $filters = [ - 'status' => [ - 'value' => '1', - 'table' => 'media_field_data', - 'field' => 'status', - 'plugin_id' => 'boolean', - 'entity_type' => 'media', - 'entity_field' => 'status', - ], - ]; - - /** * {@inheritdoc} */ public function getAvailableSorts() { diff --git a/core/modules/media/src/Plugin/views/wizard/MediaRevision.php b/core/modules/media/src/Plugin/views/wizard/MediaRevision.php index 7abc41a..10b5154 100644 --- a/core/modules/media/src/Plugin/views/wizard/MediaRevision.php +++ b/core/modules/media/src/Plugin/views/wizard/MediaRevision.php @@ -23,22 +23,6 @@ class MediaRevision extends WizardPluginBase { protected $createdColumn = 'media_field_revision-created'; /** - * Set default values for the filters. - * - * @var array - */ - protected $filters = [ - 'status' => [ - 'value' => '1', - 'table' => 'media_field_revision', - 'field' => 'status', - 'plugin_id' => 'boolean', - 'entity_type' => 'media', - 'entity_field' => 'status', - ], - ]; - - /** * {@inheritdoc} */ protected function defaultDisplayOptions() { diff --git a/core/modules/migrate/src/MigrateExecutable.php b/core/modules/migrate/src/MigrateExecutable.php index 8a593f2..94075b7 100644 --- a/core/modules/migrate/src/MigrateExecutable.php +++ b/core/modules/migrate/src/MigrateExecutable.php @@ -96,16 +96,16 @@ class MigrateExecutable implements MigrateExecutableInterface { * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration to run. * @param \Drupal\migrate\MigrateMessageInterface $message - * The migrate message service. + * (optional) The migrate message service. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher - * The event dispatcher. + * (optional) The event dispatcher. * * @throws \Drupal\migrate\MigrateException */ - public function __construct(MigrationInterface $migration, MigrateMessageInterface $message, EventDispatcherInterface $event_dispatcher = NULL) { + public function __construct(MigrationInterface $migration, MigrateMessageInterface $message = NULL, EventDispatcherInterface $event_dispatcher = NULL) { $this->migration = $migration; - $this->message = $message; - $this->migration->getIdMap()->setMessage($message); + $this->message = $message ?: new MigrateMessage(); + $this->migration->getIdMap()->setMessage($this->message); $this->eventDispatcher = $event_dispatcher; // Record the memory limit in bytes $limit = trim(ini_get('memory_limit')); diff --git a/core/modules/migrate/src/Plugin/Migration.php b/core/modules/migrate/src/Plugin/Migration.php index c7457b2..c81d498 100644 --- a/core/modules/migrate/src/Plugin/Migration.php +++ b/core/modules/migrate/src/Plugin/Migration.php @@ -572,7 +572,7 @@ public function setProcessOfProperty($property, $process_of_property) { */ public function mergeProcessOfProperty($property, array $process_of_property) { // If we already have a process value then merge the incoming process array - //otherwise simply set it. + // otherwise simply set it. $current_process = $this->getProcess(); if (isset($current_process[$property])) { $this->process = NestedArray::mergeDeepArray([$current_process, $this->getProcessNormalized([$property => $process_of_property])], TRUE); diff --git a/core/modules/migrate/tests/src/Functional/process/DownloadFunctionalTest.php b/core/modules/migrate/tests/src/Functional/process/DownloadFunctionalTest.php index 2a73d70..ccbebff 100644 --- a/core/modules/migrate/tests/src/Functional/process/DownloadFunctionalTest.php +++ b/core/modules/migrate/tests/src/Functional/process/DownloadFunctionalTest.php @@ -3,7 +3,6 @@ namespace Drupal\Tests\migrate\Functional\process; use Drupal\migrate\MigrateExecutable; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\migrate\Plugin\MigrationInterface; use Drupal\Tests\BrowserTestBase; @@ -51,7 +50,7 @@ public function testExceptionThrow() { $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); - $executable = new MigrateExecutable($migration, new MigrateMessage()); + $executable = new MigrateExecutable($migration); $result = $executable->import(); // Check that the migration has completed. diff --git a/core/modules/migrate/tests/src/Kernel/MigrateEntityContentBaseTest.php b/core/modules/migrate/tests/src/Kernel/MigrateEntityContentBaseTest.php index 0ebc719..54c60ba 100644 --- a/core/modules/migrate/tests/src/Kernel/MigrateEntityContentBaseTest.php +++ b/core/modules/migrate/tests/src/Kernel/MigrateEntityContentBaseTest.php @@ -5,7 +5,6 @@ use Drupal\KernelTests\KernelTestBase; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\migrate\MigrateExecutable; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Plugin\migrate\destination\EntityContentBase; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\migrate\Plugin\MigrationInterface; @@ -190,7 +189,7 @@ public function testEntityWithStringId() { ]; $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); - $executable = new MigrateExecutable($migration, new MigrateMessage()); + $executable = new MigrateExecutable($migration); $result = $executable->import(); $this->assertEquals(MigrationInterface::RESULT_COMPLETED, $result); diff --git a/core/modules/migrate/tests/src/Kernel/MigrateEventsTest.php b/core/modules/migrate/tests/src/Kernel/MigrateEventsTest.php index 4de21fd..8dc4aa9 100644 --- a/core/modules/migrate/tests/src/Kernel/MigrateEventsTest.php +++ b/core/modules/migrate/tests/src/Kernel/MigrateEventsTest.php @@ -7,7 +7,6 @@ use Drupal\migrate\Event\MigrateMapSaveEvent; use Drupal\migrate\Event\MigratePostRowSaveEvent; use Drupal\migrate\Event\MigratePreRowSaveEvent; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Event\MigrateEvents; use Drupal\migrate\MigrateExecutable; use Drupal\KernelTests\KernelTestBase; @@ -76,7 +75,7 @@ public function testMigrateEvents() { $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); - $executable = new MigrateExecutable($migration, new MigrateMessage()); + $executable = new MigrateExecutable($migration); // As the import runs, events will be dispatched, recording the received // information in state. $executable->import(); diff --git a/core/modules/migrate/tests/src/Kernel/MigrateInterruptionTest.php b/core/modules/migrate/tests/src/Kernel/MigrateInterruptionTest.php index d99329e..207af4d 100644 --- a/core/modules/migrate/tests/src/Kernel/MigrateInterruptionTest.php +++ b/core/modules/migrate/tests/src/Kernel/MigrateInterruptionTest.php @@ -3,7 +3,6 @@ namespace Drupal\Tests\migrate\Kernel; use Drupal\migrate\Event\MigratePostRowSaveEvent; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Event\MigrateEvents; use Drupal\migrate\MigrateExecutable; @@ -56,7 +55,7 @@ public function testMigrateEvents() { $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); - $executable = new MigrateExecutable($migration, new MigrateMessage()); + $executable = new MigrateExecutable($migration); // When the import runs, the first row imported will trigger an // interruption. $result = $executable->import(); diff --git a/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php b/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php index 3b0091d..8c5e01e 100644 --- a/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php +++ b/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php @@ -3,7 +3,6 @@ namespace Drupal\Tests\migrate\Kernel; use Drupal\KernelTests\KernelTestBase; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateExecutable; use Drupal\migrate\Plugin\MigrateIdMapInterface; @@ -50,7 +49,7 @@ public function testPrepareRowSkip() { $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); - $executable = new MigrateExecutable($migration, new MigrateMessage()); + $executable = new MigrateExecutable($migration); $result = $executable->import(); $this->assertEqual($result, MigrationInterface::RESULT_COMPLETED); @@ -85,7 +84,7 @@ public function testPrepareRowSkip() { ]; $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); - $executable = new MigrateExecutable($migration, new MigrateMessage()); + $executable = new MigrateExecutable($migration); $result = $executable->import(); $this->assertEquals($result, MigrationInterface::RESULT_COMPLETED); diff --git a/core/modules/migrate/tests/src/Kernel/process/ExtractTest.php b/core/modules/migrate/tests/src/Kernel/process/ExtractTest.php index 8c747ab..9338b9e 100644 --- a/core/modules/migrate/tests/src/Kernel/process/ExtractTest.php +++ b/core/modules/migrate/tests/src/Kernel/process/ExtractTest.php @@ -4,7 +4,6 @@ use Drupal\KernelTests\KernelTestBase; use Drupal\migrate\MigrateExecutable; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Plugin\MigrationInterface; /** @@ -66,7 +65,7 @@ public function testMultipleValueExplode(array $source_data, array $expected_dat $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); - $executable = new MigrateExecutable($migration, new MigrateMessage()); + $executable = new MigrateExecutable($migration); $result = $executable->import(); // Migration needs to succeed before further assertions are made. diff --git a/core/modules/migrate/tests/src/Kernel/process/HandleMultiplesTest.php b/core/modules/migrate/tests/src/Kernel/process/HandleMultiplesTest.php index b4cc0e8..dc63de3 100644 --- a/core/modules/migrate/tests/src/Kernel/process/HandleMultiplesTest.php +++ b/core/modules/migrate/tests/src/Kernel/process/HandleMultiplesTest.php @@ -4,7 +4,6 @@ use Drupal\KernelTests\KernelTestBase; use Drupal\migrate\MigrateExecutable; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Plugin\MigrationInterface; /** @@ -94,7 +93,7 @@ public function testScalarAndMultipleValues(array $source_data, array $expected_ $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); - $executable = new MigrateExecutable($migration, new MigrateMessage()); + $executable = new MigrateExecutable($migration); $result = $executable->import(); // Migration needs to succeed before further assertions are made. diff --git a/core/modules/migrate/tests/src/Unit/process/UrlEncodeTest.php b/core/modules/migrate/tests/src/Unit/process/UrlEncodeTest.php index 1f7d94a..df41e82 100644 --- a/core/modules/migrate/tests/src/Unit/process/UrlEncodeTest.php +++ b/core/modules/migrate/tests/src/Unit/process/UrlEncodeTest.php @@ -4,7 +4,6 @@ use Drupal\migrate\Plugin\migrate\process\UrlEncode; use Drupal\migrate\MigrateExecutable; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Row; use Drupal\Tests\migrate\Unit\MigrateTestCase; @@ -56,7 +55,7 @@ public function testUrls($input, $output) { * Encoded URL. */ protected function doTransform($value) { - $executable = new MigrateExecutable($this->getMigration(), new MigrateMessage()); + $executable = new MigrateExecutable($this->getMigration()); $row = new Row(); return (new UrlEncode([], 'urlencode', [])) diff --git a/core/modules/migrate_drupal/migrate_drupal.module b/core/modules/migrate_drupal/migrate_drupal.module index 2de2595..9f623ea 100644 --- a/core/modules/migrate_drupal/migrate_drupal.module +++ b/core/modules/migrate_drupal/migrate_drupal.module @@ -9,7 +9,6 @@ use Drupal\Core\Routing\RouteMatchInterface; use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\MigrateExecutable; -use Drupal\migrate\MigrateMessage; use Drupal\migrate\Plugin\RequirementsInterface; /** @@ -49,7 +48,7 @@ function migrate_drupal_migration_plugins_alter(&$definitions) { if ($source_plugin instanceof RequirementsInterface) { $source_plugin->checkRequirements(); } - $executable = new MigrateExecutable($vocabulary_migration, new MigrateMessage()); + $executable = new MigrateExecutable($vocabulary_migration); $process = ['vid' => $definitions['d6_taxonomy_vocabulary']['process']['vid']]; foreach ($source_plugin as $row) { $executable->processRow($row, $process); diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php index f1ccf8a..e1481c2 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php @@ -7284,6 +7284,39 @@ 'mysql_character_set' => 'utf8', )); +$connection->insert('field_data_taxonomy_forums') +->fields(array( + 'entity_type', + 'bundle', + 'deleted', + 'entity_id', + 'revision_id', + 'language', + 'delta', + 'taxonomy_forums_tid', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'forum', + 'deleted' => '0', + 'entity_id' => '6', + 'revision_id' => '6', + 'language' => 'und', + 'delta' => '0', + 'taxonomy_forums_tid' => '1', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'forum', + 'deleted' => '0', + 'entity_id' => '7', + 'revision_id' => '7', + 'language' => 'und', + 'delta' => '0', + 'taxonomy_forums_tid' => '1', +)) +->execute(); + $connection->schema()->createTable('field_revision_body', array( 'fields' => array( 'entity_type' => array( @@ -10475,6 +10508,39 @@ 'mysql_character_set' => 'utf8', )); +$connection->insert('field_revision_taxonomy_forums') +->fields(array( + 'entity_type', + 'bundle', + 'deleted', + 'entity_id', + 'revision_id', + 'language', + 'delta', + 'taxonomy_forums_tid', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'forum', + 'deleted' => '0', + 'entity_id' => '6', + 'revision_id' => '6', + 'language' => 'und', + 'delta' => '0', + 'taxonomy_forums_tid' => '1', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'forum', + 'deleted' => '0', + 'entity_id' => '7', + 'revision_id' => '7', + 'language' => 'und', + 'delta' => '0', + 'taxonomy_forums_tid' => '1', +)) +->execute(); + $connection->schema()->createTable('file_managed', array( 'fields' => array( 'fid' => array( @@ -11075,6 +11141,24 @@ 'mysql_character_set' => 'utf8', )); +$connection->insert('forum') +->fields(array( + 'nid', + 'vid', + 'tid', +)) +->values(array( + 'nid' => '6', + 'vid' => '6', + 'tid' => '1', +)) +->values(array( + 'nid' => '7', + 'vid' => '7', + 'tid' => '1', +)) +->execute(); + $connection->schema()->createTable('forum_index', array( 'fields' => array( 'nid' => array( @@ -11127,6 +11211,36 @@ 'mysql_character_set' => 'utf8', )); +$connection->insert('forum_index') +->fields(array( + 'nid', + 'title', + 'tid', + 'sticky', + 'created', + 'last_comment_timestamp', + 'comment_count', +)) +->values(array( + 'nid' => '6', + 'title' => 'Comments are closed :-(', + 'tid' => '1', + 'sticky' => '0', + 'created' => '1504715414', + 'last_comment_timestamp' => '1504715414', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '7', + 'title' => 'Comments are open :-)', + 'tid' => '1', + 'sticky' => '0', + 'created' => '1504715432', + 'last_comment_timestamp' => '1504715432', + 'comment_count' => '0', +)) +->execute(); + $connection->schema()->createTable('history', array( 'fields' => array( 'uid' => array( @@ -33197,7 +33311,7 @@ 'status' => '1', 'created' => '1478755274', 'changed' => '1478755274', - 'comment' => '2', + 'comment' => '1', 'promote' => '1', 'sticky' => '0', 'tnid' => '4', @@ -33213,12 +33327,44 @@ 'status' => '1', 'created' => '1478755314', 'changed' => '1478755314', - 'comment' => '2', + 'comment' => '1', 'promote' => '1', 'sticky' => '0', 'tnid' => '4', 'translate' => '0', )) +->values(array( + 'nid' => '6', + 'vid' => '6', + 'type' => 'forum', + 'language' => 'en', + 'title' => 'Comments are closed :-(', + 'uid' => '1', + 'status' => '1', + 'created' => '1504715414', + 'changed' => '1504715414', + 'comment' => '1', + 'promote' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) +->values(array( + 'nid' => '7', + 'vid' => '7', + 'type' => 'forum', + 'language' => 'en', + 'title' => 'Comments are open :-)', + 'uid' => '1', + 'status' => '1', + 'created' => '1504715432', + 'changed' => '1504715432', + 'comment' => '2', + 'promote' => '0', + 'sticky' => '0', + 'tnid' => '0', + 'translate' => '0', +)) ->execute(); $connection->schema()->createTable('node_access', array( @@ -33379,6 +33525,22 @@ 'last_comment_uid' => '1', 'comment_count' => '0', )) +->values(array( + 'nid' => '6', + 'cid' => '0', + 'last_comment_timestamp' => '1504715414', + 'last_comment_name' => NULL, + 'last_comment_uid' => '1', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '7', + 'cid' => '0', + 'last_comment_timestamp' => '1504715432', + 'last_comment_name' => NULL, + 'last_comment_uid' => '1', + 'comment_count' => '0', +)) ->execute(); $connection->schema()->createTable('node_counter', array( @@ -33454,6 +33616,18 @@ 'daycount' => '1', 'timestamp' => '1478755314', )) +->values(array( + 'nid' => '6', + 'totalcount' => '2', + 'daycount' => '2', + 'timestamp' => '1504715439', +)) +->values(array( + 'nid' => '7', + 'totalcount' => '2', + 'daycount' => '2', + 'timestamp' => '1504715438', +)) ->execute(); $connection->schema()->createTable('node_revision', array( @@ -33582,7 +33756,7 @@ 'log' => '', 'timestamp' => '1478755274', 'status' => '1', - 'comment' => '2', + 'comment' => '1', 'promote' => '1', 'sticky' => '0', )) @@ -33594,10 +33768,34 @@ 'log' => '', 'timestamp' => '1478755314', 'status' => '1', - 'comment' => '2', + 'comment' => '1', 'promote' => '1', 'sticky' => '0', )) +->values(array( + 'nid' => '6', + 'vid' => '6', + 'uid' => '1', + 'title' => 'Comments are closed :-(', + 'log' => '', + 'timestamp' => '1504715414', + 'status' => '1', + 'comment' => '1', + 'promote' => '0', + 'sticky' => '0', +)) +->values(array( + 'nid' => '7', + 'vid' => '7', + 'uid' => '1', + 'title' => 'Comments are open :-)', + 'log' => '', + 'timestamp' => '1504715432', + 'status' => '1', + 'comment' => '2', + 'promote' => '0', + 'sticky' => '0', +)) ->execute(); $connection->schema()->createTable('node_type', array( @@ -44779,6 +44977,18 @@ 'sticky' => '0', 'created' => '1421727515', )) +->values(array( + 'nid' => '6', + 'tid' => '1', + 'sticky' => '0', + 'created' => '1504715414', +)) +->values(array( + 'nid' => '7', + 'tid' => '1', + 'sticky' => '0', + 'created' => '1504715432', +)) ->execute(); $connection->schema()->createTable('taxonomy_term_data', array( diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php index 71bc7a5..f98d3a5 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php @@ -57,7 +57,7 @@ protected function getEntityCounts() { 'image_style' => 6, 'language_content_settings' => 2, 'migration' => 73, - 'node' => 3, + 'node' => 5, 'node_type' => 6, 'rdf_mapping' => 7, 'search_page' => 2, diff --git a/core/modules/node/config/optional/views.view.frontpage.yml b/core/modules/node/config/optional/views.view.frontpage.yml index d05e291..8decfdf 100644 --- a/core/modules/node/config/optional/views.view.frontpage.yml +++ b/core/modules/node/config/optional/views.view.frontpage.yml @@ -28,7 +28,7 @@ display: empty: area_text_custom: admin_label: '' - content: 'No front page content has been created yet.' + content: 'No front page content has been created yet.
Follow the User Guide to start building your site.' empty: true field: area_text_custom group_type: group diff --git a/core/modules/node/migration_templates/d7_node.yml b/core/modules/node/migration_templates/d7_node.yml index 5de3055..359be81 100644 --- a/core/modules/node/migration_templates/d7_node.yml +++ b/core/modules/node/migration_templates/d7_node.yml @@ -35,3 +35,4 @@ migration_dependencies: - d7_node_type optional: - d7_field_instance + - d7_comment_field_instance diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php index 4c9bbac..552e3fb 100644 --- a/core/modules/node/src/NodeForm.php +++ b/core/modules/node/src/NodeForm.php @@ -142,13 +142,6 @@ public function form(array $form, FormStateInterface $form_state) { '#wrapper_attributes' => ['class' => ['entity-meta__author']], ]; - $form['footer'] = [ - '#type' => 'container', - '#weight' => 99, - '#attributes' => [ - 'class' => ['node-form-footer'] - ] - ]; $form['status']['#group'] = 'footer'; // Node author information for administrators. diff --git a/core/modules/node/src/NodeListBuilder.php b/core/modules/node/src/NodeListBuilder.php index 4dbdf27..eac48fd 100644 --- a/core/modules/node/src/NodeListBuilder.php +++ b/core/modules/node/src/NodeListBuilder.php @@ -26,13 +26,6 @@ class NodeListBuilder extends EntityListBuilder { protected $dateFormatter; /** - * The redirect destination service. - * - * @var \Drupal\Core\Routing\RedirectDestinationInterface - */ - protected $redirectDestination; - - /** * Constructs a new NodeListBuilder object. * * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type @@ -128,17 +121,4 @@ public function buildRow(EntityInterface $entity) { return $row + parent::buildRow($entity); } - /** - * {@inheritdoc} - */ - protected function getDefaultOperations(EntityInterface $entity) { - $operations = parent::getDefaultOperations($entity); - - $destination = $this->redirectDestination->getAsArray(); - foreach ($operations as $key => $operation) { - $operations[$key]['query'] = $destination; - } - return $operations; - } - } diff --git a/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php b/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php index da01c70..28c61b8 100644 --- a/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php +++ b/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php @@ -141,6 +141,16 @@ public function getDerivativeDefinitions($base_plugin_definition) { $values['source']['node_type'] = $node_type; $values['destination']['default_bundle'] = $node_type; + // Comment status must be mapped to correct comment type. + // Comment type migration creates a separate comment type for each + // node type except for Forum which uses 'comment_forum'. + $comment_type = 'comment_node_' . $node_type; + if ($node_type == 'forum') { + $comment_type = 'comment_forum'; + } + $nested_key = $comment_type . '/0/status'; + $values['process'][$nested_key] = 'comment'; + // If this migration is based on the d7_node_revision migration or // is for translations of nodes, it should explicitly depend on the // corresponding d7_node variant. diff --git a/core/modules/node/src/Plugin/migrate/source/d6/Node.php b/core/modules/node/src/Plugin/migrate/source/d6/Node.php index 9ad7e9c..d20545f 100644 --- a/core/modules/node/src/Plugin/migrate/source/d6/Node.php +++ b/core/modules/node/src/Plugin/migrate/source/d6/Node.php @@ -214,7 +214,7 @@ protected function getFieldInfo($node_type) { $query->fields('cnf'); foreach ($query->execute() as $field) { - $this->fieldInfo[ $field['type_name'] ][ $field['field_name'] ] = $field; + $this->fieldInfo[$field['type_name']][$field['field_name']] = $field; } foreach ($this->fieldInfo as $type => $fields) { diff --git a/core/modules/node/src/Plugin/views/wizard/Node.php b/core/modules/node/src/Plugin/views/wizard/Node.php index 34d4bcf..1be211f 100644 --- a/core/modules/node/src/Plugin/views/wizard/Node.php +++ b/core/modules/node/src/Plugin/views/wizard/Node.php @@ -26,20 +26,6 @@ class Node extends WizardPluginBase { protected $createdColumn = 'node_field_data-created'; /** - * Set default values for the filters. - */ - protected $filters = [ - 'status' => [ - 'value' => TRUE, - 'table' => 'node_field_data', - 'field' => 'status', - 'plugin_id' => 'boolean', - 'entity_type' => 'node', - 'entity_field' => 'status', - ] - ]; - - /** * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::getAvailableSorts(). * * @return array diff --git a/core/modules/node/src/Plugin/views/wizard/NodeRevision.php b/core/modules/node/src/Plugin/views/wizard/NodeRevision.php index 9c3465d..258d6d3 100644 --- a/core/modules/node/src/Plugin/views/wizard/NodeRevision.php +++ b/core/modules/node/src/Plugin/views/wizard/NodeRevision.php @@ -25,20 +25,6 @@ class NodeRevision extends WizardPluginBase { protected $createdColumn = 'changed'; /** - * Set default values for the filters. - */ - protected $filters = [ - 'status' => [ - 'value' => TRUE, - 'table' => 'node_field_revision', - 'field' => 'status', - 'plugin_id' => 'boolean', - 'entity_type' => 'node', - 'entity_field' => 'status', - ] - ]; - - /** * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::rowStyleOptions(). * * Node revisions do not support full posts or teasers, so remove them. diff --git a/core/modules/node/tests/src/Functional/NodeActionsConfigurationTest.php b/core/modules/node/tests/src/Functional/NodeActionsConfigurationTest.php index fbfda8f..c2c7617 100644 --- a/core/modules/node/tests/src/Functional/NodeActionsConfigurationTest.php +++ b/core/modules/node/tests/src/Functional/NodeActionsConfigurationTest.php @@ -43,14 +43,14 @@ public function testAssignOwnerNodeActionConfiguration() { $this->drupalPostForm('admin/config/system/actions/add/' . Crypt::hashBase64('node_assign_owner_action'), $edit, t('Save')); $this->assertResponse(200); + $action_id = $edit['id']; + // Make sure that the new action was saved properly. $this->assertText(t('The action has been successfully saved.'), 'The node_assign_owner_action action has been successfully saved.'); $this->assertText($action_label, 'The label of the node_assign_owner_action action appears on the actions administration page after saving.'); // Make another POST request to the action edit page. $this->clickLink(t('Configure')); - preg_match('|admin/config/system/actions/configure/(.+)|', $this->getUrl(), $matches); - $aid = $matches[1]; $edit = []; $new_action_label = $this->randomMachineName(); $edit['label'] = $new_action_label; @@ -68,7 +68,7 @@ public function testAssignOwnerNodeActionConfiguration() { $this->clickLink(t('Delete')); $this->assertResponse(200); $edit = []; - $this->drupalPostForm("admin/config/system/actions/configure/$aid/delete", $edit, t('Delete')); + $this->drupalPostForm(NULL, $edit, t('Delete')); $this->assertResponse(200); // Make sure that the action was actually deleted. @@ -77,7 +77,7 @@ public function testAssignOwnerNodeActionConfiguration() { $this->assertResponse(200); $this->assertNoText($new_action_label, 'The label for the node_assign_owner_action action does not appear on the actions administration page after deleting.'); - $action = Action::load($aid); + $action = Action::load($action_id); $this->assertFalse($action, 'The node_assign_owner_action action is not available after being deleted.'); } diff --git a/core/modules/node/tests/src/Functional/Views/NodeRevisionWizardTest.php b/core/modules/node/tests/src/Functional/Views/NodeRevisionWizardTest.php index db70b2c..4cbecf3 100644 --- a/core/modules/node/tests/src/Functional/Views/NodeRevisionWizardTest.php +++ b/core/modules/node/tests/src/Functional/Views/NodeRevisionWizardTest.php @@ -45,16 +45,19 @@ public function testViewAdd() { $view['show[wizard_key]'] = 'node_revision'; $this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit')); - $view_storage_controller = \Drupal::entityManager()->getStorage('view'); - /** @var \Drupal\views\Entity\View $view */ - $view = $view_storage_controller->load($view['id']); + $view = Views::getView($view['id']); + $view->initHandlers(); - $this->assertEqual($view->get('base_table'), 'node_field_revision'); + $this->assertEqual($view->getBaseTables(), ['node_field_revision' => TRUE, '#global' => TRUE]); - $executable = Views::executableFactory()->get($view); - $this->executeView($executable); + // Check for the default filters. + $this->assertEqual($view->filter['status']->table, 'node_field_revision'); + $this->assertEqual($view->filter['status']->field, 'status'); + $this->assertTrue($view->filter['status']->value); - $this->assertIdenticalResultset($executable, [['vid' => 1], ['vid' => 3], ['vid' => 2], ['vid' => 4]], + $this->executeView($view); + + $this->assertIdenticalResultset($view, [['vid' => 1], ['vid' => 3], ['vid' => 2], ['vid' => 4]], ['vid' => 'vid']); } diff --git a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php index b4fd246..dc053a8 100644 --- a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php +++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\node\Kernel\Migrate\d7; +use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\Tests\file\Kernel\Migrate\d7\FileMigrationSetupTrait; use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase; use Drupal\node\Entity\Node; @@ -25,6 +26,7 @@ class MigrateNodeTest extends MigrateDrupal7TestBase { 'datetime', 'file', 'filter', + 'forum', 'image', 'language', 'link', @@ -47,6 +49,8 @@ protected function setUp() { $this->installEntitySchema('comment'); $this->installEntitySchema('taxonomy_term'); $this->installConfig(static::$modules); + $this->installSchema('comment', ['comment_entity_statistics']); + $this->installSchema('forum', ['forum', 'forum_index']); $this->installSchema('node', ['node_access']); $this->installSchema('system', ['sequences']); @@ -57,6 +61,8 @@ protected function setUp() { 'd7_node_type', 'd7_language_content_settings', 'd7_comment_type', + 'd7_comment_field', + 'd7_comment_field_instance', 'd7_taxonomy_vocabulary', 'd7_field', 'd7_field_instance', @@ -92,18 +98,18 @@ protected function setUp() { protected function assertEntity($id, $type, $langcode, $title, $uid, $status, $created, $changed, $promoted, $sticky) { /** @var \Drupal\node\NodeInterface $node */ $node = Node::load($id); - $this->assertTrue($node instanceof NodeInterface); - $this->assertIdentical($type, $node->getType()); - $this->assertIdentical($langcode, $node->langcode->value); - $this->assertIdentical($title, $node->getTitle()); - $this->assertIdentical($uid, $node->getOwnerId()); - $this->assertIdentical($status, $node->isPublished()); - $this->assertIdentical($created, $node->getCreatedTime()); + $this->assertInstanceOf(NodeInterface::class, $node); + $this->assertEquals($type, $node->getType()); + $this->assertEquals($langcode, $node->langcode->value); + $this->assertEquals($title, $node->getTitle()); + $this->assertEquals($uid, $node->getOwnerId()); + $this->assertEquals($status, $node->isPublished()); + $this->assertEquals($created, $node->getCreatedTime()); if (isset($changed)) { - $this->assertIdentical($changed, $node->getChangedTime()); + $this->assertEquals($changed, $node->getChangedTime()); } - $this->assertIdentical($promoted, $node->isPromoted()); - $this->assertIdentical($sticky, $node->isSticky()); + $this->assertEquals($promoted, $node->isPromoted()); + $this->assertEquals($sticky, $node->isSticky()); } /** @@ -122,11 +128,11 @@ protected function assertEntity($id, $type, $langcode, $title, $uid, $status, $c */ protected function assertRevision($id, $title, $uid, $log, $timestamp) { $revision = \Drupal::entityManager()->getStorage('node')->loadRevision($id); - $this->assertTrue($revision instanceof NodeInterface); - $this->assertIdentical($title, $revision->getTitle()); - $this->assertIdentical($uid, $revision->getRevisionUser()->id()); - $this->assertIdentical($log, $revision->revision_log->value); - $this->assertIdentical($timestamp, $revision->getRevisionCreationTime()); + $this->assertInstanceOf(NodeInterface::class, $revision); + $this->assertEquals($title, $revision->getTitle()); + $this->assertEquals($uid, $revision->getRevisionUser()->id()); + $this->assertEquals($log, $revision->revision_log->value); + $this->assertEquals($timestamp, $revision->getRevisionCreationTime()); } /** @@ -138,53 +144,64 @@ public function testNode() { $node = Node::load(1); $this->assertTrue($node->field_boolean->value); - $this->assertIdentical('99-99-99-99', $node->field_phone->value); - // Use assertEqual() here instead, since SQLite interprets floats strictly. - $this->assertEqual('1', $node->field_float->value); - $this->assertIdentical('5', $node->field_integer->value); - $this->assertIdentical('Some more text', $node->field_text_list[0]->value); - $this->assertIdentical('7', $node->field_integer_list[0]->value); - $this->assertIdentical('qwerty', $node->field_text->value); - $this->assertIdentical('2', $node->field_file->target_id); - $this->assertIdentical('file desc', $node->field_file->description); + $this->assertEquals('99-99-99-99', $node->field_phone->value); + $this->assertEquals('1', $node->field_float->value); + $this->assertEquals('5', $node->field_integer->value); + $this->assertEquals('Some more text', $node->field_text_list[0]->value); + $this->assertEquals('7', $node->field_integer_list[0]->value); + $this->assertEquals('qwerty', $node->field_text->value); + $this->assertEquals('2', $node->field_file->target_id); + $this->assertEquals('file desc', $node->field_file->description); $this->assertTrue($node->field_file->display); - $this->assertIdentical('1', $node->field_images->target_id); - $this->assertIdentical('alt text', $node->field_images->alt); - $this->assertIdentical('title text', $node->field_images->title); - $this->assertIdentical('93', $node->field_images->width); - $this->assertIdentical('93', $node->field_images->height); - $this->assertIdentical('http://google.com', $node->field_link->uri); - $this->assertIdentical('Click Here', $node->field_link->title); + $this->assertEquals('1', $node->field_images->target_id); + $this->assertEquals('alt text', $node->field_images->alt); + $this->assertEquals('title text', $node->field_images->title); + $this->assertEquals('93', $node->field_images->width); + $this->assertEquals('93', $node->field_images->height); + $this->assertEquals('http://google.com', $node->field_link->uri); + $this->assertEquals('Click Here', $node->field_link->title); // Test that an email field is migrated. - $this->assertSame('default@example.com', $node->field_email->value); - $this->assertSame('another@example.com', $node->field_email[1]->value); + $this->assertEquals('default@example.com', $node->field_email->value); + $this->assertEquals('another@example.com', $node->field_email[1]->value); + $this->assertEquals(CommentItemInterface::OPEN, $node->comment_node_test_content_type->status); $node = Node::load(2); - $this->assertSame('en', $node->langcode->value); - $this->assertIdentical("...is that it's the absolute best show ever. Trust me, I would know.", $node->body->value); - $this->assertSame('The thing about Deep Space 9', $node->label()); - $this->assertIdentical('internal:/', $node->field_link->uri); - $this->assertIdentical('Home', $node->field_link->title); + $this->assertEquals('en', $node->langcode->value); + $this->assertEquals("...is that it's the absolute best show ever. Trust me, I would know.", $node->body->value); + $this->assertEquals('The thing about Deep Space 9', $node->label()); + $this->assertEquals('internal:/', $node->field_link->uri); + $this->assertEquals('Home', $node->field_link->title); + $this->assertEquals(CommentItemInterface::OPEN, $node->comment_node_article->status); $this->assertTrue($node->hasTranslation('is'), "Node 2 has an Icelandic translation"); $translation = $node->getTranslation('is'); - $this->assertSame('is', $translation->langcode->value); - $this->assertSame("is - ...is that it's the absolute best show ever. Trust me, I would know.", $translation->body->value); - $this->assertSame('is - The thing about Deep Space 9', $translation->label()); - $this->assertSame('internal:/', $translation->field_link->uri); - $this->assertSame('Home', $translation->field_link->title); + $this->assertEquals('is', $translation->langcode->value); + $this->assertEquals("is - ...is that it's the absolute best show ever. Trust me, I would know.", $translation->body->value); + $this->assertEquals('is - The thing about Deep Space 9', $translation->label()); + $this->assertEquals('internal:/', $translation->field_link->uri); + $this->assertEquals(CommentItemInterface::OPEN, $translation->comment_node_article->status); + $this->assertEquals('Home', $translation->field_link->title); // Test that content_translation_source is set. $manager = $this->container->get('content_translation.manager'); - $this->assertSame('en', $manager->getTranslationMetadata($node->getTranslation('is'))->getSource()); + $this->assertEquals('en', $manager->getTranslationMetadata($node->getTranslation('is'))->getSource()); // Node 3 is a translation of node 2, and should not be imported separately. $this->assertNull(Node::load(3), "Node 3 doesn't exist in D8, it was a translation"); // Test that content_translation_source for a source other than English. $node = Node::load(4); - $this->assertSame('is', $manager->getTranslationMetadata($node->getTranslation('en'))->getSource()); + $this->assertEquals('is', $manager->getTranslationMetadata($node->getTranslation('en'))->getSource()); + $this->assertEquals(CommentItemInterface::CLOSED, $node->comment_node_article->status); + $translation = $node->getTranslation('en'); + $this->assertEquals(CommentItemInterface::CLOSED, $translation->comment_node_article->status); + + $node = Node::load(6); + $this->assertEquals(CommentItemInterface::CLOSED, $node->comment_forum->status); + + $node = Node::load(7); + $this->assertEquals(CommentItemInterface::OPEN, $node->comment_forum->status); } } diff --git a/core/modules/quickedit/tests/src/Kernel/MetadataGeneratorTest.php b/core/modules/quickedit/tests/src/Kernel/MetadataGeneratorTest.php index 9a453fb..546ac3a 100644 --- a/core/modules/quickedit/tests/src/Kernel/MetadataGeneratorTest.php +++ b/core/modules/quickedit/tests/src/Kernel/MetadataGeneratorTest.php @@ -172,7 +172,7 @@ public function testEditorWithCustomMetadata() { 'format' => 'full_html' ], ]; - $this->assertEqual($expected, $metadata); //, 'The correct metadata (including custom metadata) is generated.'); + $this->assertEqual($expected, $metadata); // , 'The correct metadata (including custom metadata) is generated.'); } } diff --git a/core/modules/rdf/src/Tests/CommentAttributesTest.php b/core/modules/rdf/src/Tests/CommentAttributesTest.php deleted file mode 100644 index 8ca5798..0000000 --- a/core/modules/rdf/src/Tests/CommentAttributesTest.php +++ /dev/null @@ -1,376 +0,0 @@ - TRUE, - 'post comments' => TRUE, - 'skip comment approval' => TRUE, - ]); - // Allows anonymous to leave their contact information. - $this->setCommentAnonymous(COMMENT_ANONYMOUS_MAY_CONTACT); - $this->setCommentPreview(DRUPAL_OPTIONAL); - $this->setCommentForm(TRUE); - $this->setCommentSubject(TRUE); - $this->setCommentSettings('comment_default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.'); - - // Prepares commonly used URIs. - $this->baseUri = \Drupal::url('', [], ['absolute' => TRUE]); - $this->nodeUri = $this->node->url('canonical', ['absolute' => TRUE]); - - // Set relation between node and comment. - $article_mapping = rdf_get_mapping('node', 'article'); - $comment_count_mapping = [ - 'properties' => ['sioc:num_replies'], - 'datatype' => 'xsd:integer', - 'datatype_callback' => ['callable' => 'Drupal\rdf\CommonDataConverter::rawValue'], - ]; - $article_mapping->setFieldMapping('comment_count', $comment_count_mapping)->save(); - - // Save user mapping. - $user_mapping = rdf_get_mapping('user', 'user'); - $username_mapping = [ - 'properties' => ['foaf:name'], - ]; - $user_mapping->setFieldMapping('name', $username_mapping)->save(); - $user_mapping->setFieldMapping('homepage', ['properties' => ['foaf:page'], 'mapping_type' => 'rel'])->save(); - - // Save comment mapping. - $mapping = rdf_get_mapping('comment', 'comment'); - $mapping->setBundleMapping(['types' => ['sioc:Post', 'sioct:Comment']])->save(); - $field_mappings = [ - 'subject' => [ - 'properties' => ['dc:title'], - ], - 'created' => [ - 'properties' => ['dc:date', 'dc:created'], - 'datatype' => 'xsd:dateTime', - 'datatype_callback' => ['callable' => 'Drupal\rdf\CommonDataConverter::dateIso8601Value'], - ], - 'changed' => [ - 'properties' => ['dc:modified'], - 'datatype' => 'xsd:dateTime', - 'datatype_callback' => ['callable' => 'Drupal\rdf\CommonDataConverter::dateIso8601Value'], - ], - 'comment_body' => [ - 'properties' => ['content:encoded'], - ], - 'pid' => [ - 'properties' => ['sioc:reply_of'], - 'mapping_type' => 'rel', - ], - 'uid' => [ - 'properties' => ['sioc:has_creator'], - 'mapping_type' => 'rel', - ], - 'name' => [ - 'properties' => ['foaf:name'], - ], - ]; - // Iterate over shared field mappings and save. - foreach ($field_mappings as $field_name => $field_mapping) { - $mapping->setFieldMapping($field_name, $field_mapping)->save(); - } - } - - /** - * Tests the presence of the RDFa markup for the number of comments. - */ - public function testNumberOfCommentsRdfaMarkup() { - // Posts 2 comments on behalf of registered user. - $this->saveComment($this->node->id(), $this->webUser->id()); - $this->saveComment($this->node->id(), $this->webUser->id()); - - // Tests number of comments in teaser view. - $this->drupalLogin($this->webUser); - $parser = new \EasyRdf_Parser_Rdfa(); - $graph = new \EasyRdf_Graph(); - $parser->parse($graph, $this->drupalGet('node'), 'rdfa', $this->baseUri); - - // Number of comments. - $expected_value = [ - 'type' => 'literal', - 'value' => 2, - 'datatype' => 'http://www.w3.org/2001/XMLSchema#integer', - ]; - $this->assertTrue($graph->hasProperty($this->nodeUri, 'http://rdfs.org/sioc/ns#num_replies', $expected_value), 'Number of comments found in RDF output of teaser view (sioc:num_replies).'); - - // Tests number of comments in full node view, expected value is the same. - $parser = new \EasyRdf_Parser_Rdfa(); - $graph = new \EasyRdf_Graph(); - $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); - $this->assertTrue($graph->hasProperty($this->nodeUri, 'http://rdfs.org/sioc/ns#num_replies', $expected_value), 'Number of comments found in RDF output of full node view mode (sioc:num_replies).'); - } - - /** - * Tests comment author link markup has not been broken by RDF. - */ - public function testCommentRdfAuthorMarkup() { - // Post a comment as a registered user. - $this->saveComment($this->node->id(), $this->webUser->id()); - - // Give the user access to view user profiles so the profile link shows up. - user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['access user profiles']); - $this->drupalLogin($this->webUser); - - // Ensure that the author link still works properly after the author output - // is modified by the RDF module. - $this->drupalGet('node/' . $this->node->id()); - $this->assertLink($this->webUser->getUsername()); - $this->assertLinkByHref('user/' . $this->webUser->id()); - } - - /** - * Tests if RDFa markup for meta information is present in comments. - * - * Tests presence of RDFa markup for the title, date and author and homepage - * on comments from registered and anonymous users. - */ - public function testCommentRdfaMarkup() { - // Posts comment #1 on behalf of registered user. - $comment1 = $this->saveComment($this->node->id(), $this->webUser->id()); - - // Tests comment #1 with access to the user profile. - $this->drupalLogin($this->webUser); - $parser = new \EasyRdf_Parser_Rdfa(); - $graph = new \EasyRdf_Graph(); - $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); - $this->_testBasicCommentRdfaMarkup($graph, $comment1); - - // Tests comment #1 with no access to the user profile (as anonymous user). - $this->drupalLogout(); - $parser = new \EasyRdf_Parser_Rdfa(); - $graph = new \EasyRdf_Graph(); - $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); - $this->_testBasicCommentRdfaMarkup($graph, $comment1); - - // Posts comment #2 as anonymous user. - $anonymous_user = []; - $anonymous_user['name'] = $this->randomMachineName(); - $anonymous_user['mail'] = 'tester@simpletest.org'; - $anonymous_user['homepage'] = 'http://example.org/'; - $comment2 = $this->saveComment($this->node->id(), 0, $anonymous_user); - - // Tests comment #2 as anonymous user. - $parser = new \EasyRdf_Parser_Rdfa(); - $graph = new \EasyRdf_Graph(); - $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); - $this->_testBasicCommentRdfaMarkup($graph, $comment2, $anonymous_user); - - // Tests comment #2 as logged in user. - $this->drupalLogin($this->webUser); - $parser = new \EasyRdf_Parser_Rdfa(); - $graph = new \EasyRdf_Graph(); - $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); - $this->_testBasicCommentRdfaMarkup($graph, $comment2, $anonymous_user); - } - - /** - * Tests RDF comment replies. - */ - public function testCommentReplyOfRdfaMarkup() { - // Posts comment #1 on behalf of registered user. - $this->drupalLogin($this->webUser); - $comment_1 = $this->saveComment($this->node->id(), $this->webUser->id()); - - $comment_1_uri = $comment_1->url('canonical', ['absolute' => TRUE]); - - // Posts a reply to the first comment. - $comment_2 = $this->saveComment($this->node->id(), $this->webUser->id(), NULL, $comment_1->id()); - $comment_2_uri = $comment_2->url('canonical', ['absolute' => TRUE]); - - $parser = new \EasyRdf_Parser_Rdfa(); - $graph = new \EasyRdf_Graph(); - $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); - - // Tests the reply_of relationship of a first level comment. - $expected_value = [ - 'type' => 'uri', - 'value' => $this->nodeUri, - ]; - $this->assertTrue($graph->hasProperty($comment_1_uri, 'http://rdfs.org/sioc/ns#reply_of', $expected_value), 'Comment relation to its node found in RDF output (sioc:reply_of).'); - - // Tests the reply_of relationship of a second level comment. - $expected_value = [ - 'type' => 'uri', - 'value' => $this->nodeUri, - ]; - $this->assertTrue($graph->hasProperty($comment_2_uri, 'http://rdfs.org/sioc/ns#reply_of', $expected_value), 'Comment relation to its node found in RDF output (sioc:reply_of).'); - $expected_value = [ - 'type' => 'uri', - 'value' => $comment_1_uri, - ]; - $this->assertTrue($graph->hasProperty($comment_2_uri, 'http://rdfs.org/sioc/ns#reply_of', $expected_value), 'Comment relation to its parent comment found in RDF output (sioc:reply_of).'); - } - - /** - * Helper function for testCommentRdfaMarkup(). - * - * Tests the current page for basic comment RDFa markup. - * - * @param $comment - * Comment object. - * @param $account - * An array containing information about an anonymous user. - */ - public function _testBasicCommentRdfaMarkup($graph, CommentInterface $comment, $account = []) { - $comment_uri = $comment->url('canonical', ['absolute' => TRUE]); - - // Comment type. - $expected_value = [ - 'type' => 'uri', - 'value' => 'http://rdfs.org/sioc/types#Comment', - ]; - $this->assertTrue($graph->hasProperty($comment_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Comment type found in RDF output (sioct:Comment).'); - // Comment type. - $expected_value = [ - 'type' => 'uri', - 'value' => 'http://rdfs.org/sioc/ns#Post', - ]; - $this->assertTrue($graph->hasProperty($comment_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Comment type found in RDF output (sioc:Post).'); - - // Comment title. - $expected_value = [ - 'type' => 'literal', - 'value' => $comment->getSubject(), - 'lang' => 'en', - ]; - $this->assertTrue($graph->hasProperty($comment_uri, 'http://purl.org/dc/terms/title', $expected_value), 'Comment subject found in RDF output (dc:title).'); - - // Comment date. - $expected_value = [ - 'type' => 'literal', - 'value' => format_date($comment->getCreatedTime(), 'custom', 'c', 'UTC'), - 'datatype' => 'http://www.w3.org/2001/XMLSchema#dateTime', - ]; - $this->assertTrue($graph->hasProperty($comment_uri, 'http://purl.org/dc/terms/date', $expected_value), 'Comment date found in RDF output (dc:date).'); - // Comment date. - $expected_value = [ - 'type' => 'literal', - 'value' => format_date($comment->getCreatedTime(), 'custom', 'c', 'UTC'), - 'datatype' => 'http://www.w3.org/2001/XMLSchema#dateTime', - ]; - $this->assertTrue($graph->hasProperty($comment_uri, 'http://purl.org/dc/terms/created', $expected_value), 'Comment date found in RDF output (dc:created).'); - - // Comment body. - $expected_value = [ - 'type' => 'literal', - 'value' => $comment->comment_body->value . "\n", - 'lang' => 'en', - ]; - $this->assertTrue($graph->hasProperty($comment_uri, 'http://purl.org/rss/1.0/modules/content/encoded', $expected_value), 'Comment body found in RDF output (content:encoded).'); - - // The comment author can be a registered user or an anonymous user. - if ($comment->getOwnerId() > 0) { - $author_uri = \Drupal::url('entity.user.canonical', ['user' => $comment->getOwnerId()], ['absolute' => TRUE]); - // Comment relation to author. - $expected_value = [ - 'type' => 'uri', - 'value' => $author_uri, - ]; - $this->assertTrue($graph->hasProperty($comment_uri, 'http://rdfs.org/sioc/ns#has_creator', $expected_value), 'Comment relation to author found in RDF output (sioc:has_creator).'); - } - else { - // The author is expected to be a blank node. - $author_uri = $graph->get($comment_uri, ''); - if ($author_uri instanceof \EasyRdf_Resource) { - $this->assertTrue($author_uri->isBnode(), 'Comment relation to author found in RDF output (sioc:has_creator) and author is blank node.'); - } - else { - $this->fail('Comment relation to author found in RDF output (sioc:has_creator).'); - } - } - - // Author name. - $name = empty($account["name"]) ? $this->webUser->getUsername() : $account["name"] . " (not verified)"; - $expected_value = [ - 'type' => 'literal', - 'value' => $name, - ]; - $this->assertTrue($graph->hasProperty($author_uri, 'http://xmlns.com/foaf/0.1/name', $expected_value), 'Comment author name found in RDF output (foaf:name).'); - - // Comment author homepage (only for anonymous authors). - if ($comment->getOwnerId() == 0) { - $expected_value = [ - 'type' => 'uri', - 'value' => 'http://example.org/', - ]; - $this->assertTrue($graph->hasProperty($author_uri, 'http://xmlns.com/foaf/0.1/page', $expected_value), 'Comment author link found in RDF output (foaf:page).'); - } - } - - /** - * Creates a comment entity. - * - * @param $nid - * Node id which will hold the comment. - * @param $uid - * User id of the author of the comment. Can be NULL if $contact provided. - * @param $contact - * Set to NULL for no contact info, TRUE to ignore success checking, and - * array of values to set contact info. - * @param $pid - * Comment id of the parent comment in a thread. - * - * @return \Drupal\comment\Entity\Comment - * The saved comment. - */ - public function saveComment($nid, $uid, $contact = NULL, $pid = 0) { - $values = [ - 'entity_id' => $nid, - 'entity_type' => 'node', - 'field_name' => 'comment', - 'uid' => $uid, - 'pid' => $pid, - 'subject' => $this->randomMachineName(), - 'comment_body' => $this->randomMachineName(), - 'status' => 1, - ]; - if ($contact) { - $values += $contact; - } - - $comment = Comment::create($values); - $comment->save(); - return $comment; - } - -} diff --git a/core/modules/rdf/src/Tests/GetNamespacesTest.php b/core/modules/rdf/src/Tests/GetNamespacesTest.php deleted file mode 100644 index ca46c5c..0000000 --- a/core/modules/rdf/src/Tests/GetNamespacesTest.php +++ /dev/null @@ -1,50 +0,0 @@ -drupalGet(''); - - $element = $this->xpath('//html[contains(@prefix, :prefix_binding)]', [ - ':prefix_binding' => 'rdfs: http://www.w3.org/2000/01/rdf-schema#', - ]); - $this->assertTrue(!empty($element), 'A prefix declared once is displayed.'); - - $element = $this->xpath('//html[contains(@prefix, :prefix_binding)]', [ - ':prefix_binding' => 'foaf: http://xmlns.com/foaf/0.1/', - ]); - $this->assertTrue(!empty($element), 'The same prefix declared in several implementations of hook_rdf_namespaces() is valid as long as all the namespaces are the same.'); - - $element = $this->xpath('//html[contains(@prefix, :prefix_binding)]', [ - ':prefix_binding' => 'foaf1: http://xmlns.com/foaf/0.1/', - ]); - $this->assertTrue(!empty($element), 'Two prefixes can be assigned the same namespace.'); - - $element = $this->xpath('//html[contains(@prefix, :prefix_binding)]', [ - ':prefix_binding' => 'dc: http://purl.org/dc/terms/', - ]); - $this->assertTrue(!empty($element), 'When a prefix has conflicting namespaces, the first declared one is used.'); - } - -} diff --git a/core/modules/rdf/src/Tests/ImageFieldAttributesTest.php b/core/modules/rdf/src/Tests/ImageFieldAttributesTest.php deleted file mode 100644 index d4d7d35..0000000 --- a/core/modules/rdf/src/Tests/ImageFieldAttributesTest.php +++ /dev/null @@ -1,113 +0,0 @@ -fieldName = 'field_image'; - - // Create the image field. - $this->createImageField($this->fieldName, 'article'); - - // Set the RDF mapping for the new field. - rdf_get_mapping('node', 'article') - ->setFieldMapping($this->fieldName, [ - 'properties' => ['og:image'], - 'mapping_type' => 'rel', - ]) - ->setBundleMapping(['types' => []]) - ->save(); - - // Get the test image that simpletest provides. - $image = current($this->drupalGetTestFiles('image')); - - // Save a node with the image. - $nid = $this->uploadNodeImage($image, $this->fieldName, 'article', $this->randomMachineName()); - $this->node = Node::load($nid); - $this->file = File::load($this->node->{$this->fieldName}->target_id); - } - - /** - * Tests that image fields in teasers have correct resources. - */ - public function testNodeTeaser() { - // Set the display options for the teaser. - $display_options = [ - 'type' => 'image', - 'settings' => ['image_style' => 'medium', 'image_link' => 'content'], - ]; - $display = entity_get_display('node', 'article', 'teaser'); - $display->setComponent($this->fieldName, $display_options) - ->save(); - - // Render the teaser. - $node_render_array = node_view($this->node, 'teaser'); - $html = \Drupal::service('renderer')->renderRoot($node_render_array); - - // Parse the teaser. - $parser = new \EasyRdf_Parser_Rdfa(); - $graph = new \EasyRdf_Graph(); - $base_uri = \Drupal::url('', [], ['absolute' => TRUE]); - $parser->parse($graph, $html, 'rdfa', $base_uri); - - // Construct the node and image URIs for testing. - $node_uri = $this->node->url('canonical', ['absolute' => TRUE]); - $image_uri = ImageStyle::load('medium')->buildUrl($this->file->getFileUri()); - - // Test relations from node to image. - $expected_value = [ - 'type' => 'uri', - 'value' => $image_uri, - ]; - $this->assertTrue($graph->hasProperty($node_uri, 'http://ogp.me/ns#image', $expected_value), 'Node to file relation found in RDF output (og:image).'); - - // Test image type. - $expected_value = [ - 'type' => 'uri', - 'value' => 'http://xmlns.com/foaf/0.1/Image', - ]; - $this->assertTrue($graph->hasProperty($image_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Image type found in RDF output (foaf:Image).'); - } - -} diff --git a/core/modules/rdf/tests/src/Functional/CommentAttributesTest.php b/core/modules/rdf/tests/src/Functional/CommentAttributesTest.php new file mode 100644 index 0000000..0c19438 --- /dev/null +++ b/core/modules/rdf/tests/src/Functional/CommentAttributesTest.php @@ -0,0 +1,376 @@ + TRUE, + 'post comments' => TRUE, + 'skip comment approval' => TRUE, + ]); + // Allows anonymous to leave their contact information. + $this->setCommentAnonymous(COMMENT_ANONYMOUS_MAY_CONTACT); + $this->setCommentPreview(DRUPAL_OPTIONAL); + $this->setCommentForm(TRUE); + $this->setCommentSubject(TRUE); + $this->setCommentSettings('comment_default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.'); + + // Prepares commonly used URIs. + $this->baseUri = \Drupal::url('', [], ['absolute' => TRUE]); + $this->nodeUri = $this->node->url('canonical', ['absolute' => TRUE]); + + // Set relation between node and comment. + $article_mapping = rdf_get_mapping('node', 'article'); + $comment_count_mapping = [ + 'properties' => ['sioc:num_replies'], + 'datatype' => 'xsd:integer', + 'datatype_callback' => ['callable' => 'Drupal\rdf\CommonDataConverter::rawValue'], + ]; + $article_mapping->setFieldMapping('comment_count', $comment_count_mapping)->save(); + + // Save user mapping. + $user_mapping = rdf_get_mapping('user', 'user'); + $username_mapping = [ + 'properties' => ['foaf:name'], + ]; + $user_mapping->setFieldMapping('name', $username_mapping)->save(); + $user_mapping->setFieldMapping('homepage', ['properties' => ['foaf:page'], 'mapping_type' => 'rel'])->save(); + + // Save comment mapping. + $mapping = rdf_get_mapping('comment', 'comment'); + $mapping->setBundleMapping(['types' => ['sioc:Post', 'sioct:Comment']])->save(); + $field_mappings = [ + 'subject' => [ + 'properties' => ['dc:title'], + ], + 'created' => [ + 'properties' => ['dc:date', 'dc:created'], + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => ['callable' => 'Drupal\rdf\CommonDataConverter::dateIso8601Value'], + ], + 'changed' => [ + 'properties' => ['dc:modified'], + 'datatype' => 'xsd:dateTime', + 'datatype_callback' => ['callable' => 'Drupal\rdf\CommonDataConverter::dateIso8601Value'], + ], + 'comment_body' => [ + 'properties' => ['content:encoded'], + ], + 'pid' => [ + 'properties' => ['sioc:reply_of'], + 'mapping_type' => 'rel', + ], + 'uid' => [ + 'properties' => ['sioc:has_creator'], + 'mapping_type' => 'rel', + ], + 'name' => [ + 'properties' => ['foaf:name'], + ], + ]; + // Iterate over shared field mappings and save. + foreach ($field_mappings as $field_name => $field_mapping) { + $mapping->setFieldMapping($field_name, $field_mapping)->save(); + } + } + + /** + * Tests the presence of the RDFa markup for the number of comments. + */ + public function testNumberOfCommentsRdfaMarkup() { + // Posts 2 comments on behalf of registered user. + $this->saveComment($this->node->id(), $this->webUser->id()); + $this->saveComment($this->node->id(), $this->webUser->id()); + + // Tests number of comments in teaser view. + $this->drupalLogin($this->webUser); + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $parser->parse($graph, $this->drupalGet('node'), 'rdfa', $this->baseUri); + + // Number of comments. + $expected_value = [ + 'type' => 'literal', + 'value' => 2, + 'datatype' => 'http://www.w3.org/2001/XMLSchema#integer', + ]; + $this->assertTrue($graph->hasProperty($this->nodeUri, 'http://rdfs.org/sioc/ns#num_replies', $expected_value), 'Number of comments found in RDF output of teaser view (sioc:num_replies).'); + + // Tests number of comments in full node view, expected value is the same. + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); + $this->assertTrue($graph->hasProperty($this->nodeUri, 'http://rdfs.org/sioc/ns#num_replies', $expected_value), 'Number of comments found in RDF output of full node view mode (sioc:num_replies).'); + } + + /** + * Tests comment author link markup has not been broken by RDF. + */ + public function testCommentRdfAuthorMarkup() { + // Post a comment as a registered user. + $this->saveComment($this->node->id(), $this->webUser->id()); + + // Give the user access to view user profiles so the profile link shows up. + user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['access user profiles']); + $this->drupalLogin($this->webUser); + + // Ensure that the author link still works properly after the author output + // is modified by the RDF module. + $this->drupalGet('node/' . $this->node->id()); + $this->assertLink($this->webUser->getUsername()); + $this->assertLinkByHref('user/' . $this->webUser->id()); + } + + /** + * Tests if RDFa markup for meta information is present in comments. + * + * Tests presence of RDFa markup for the title, date and author and homepage + * on comments from registered and anonymous users. + */ + public function testCommentRdfaMarkup() { + // Posts comment #1 on behalf of registered user. + $comment1 = $this->saveComment($this->node->id(), $this->webUser->id()); + + // Tests comment #1 with access to the user profile. + $this->drupalLogin($this->webUser); + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); + $this->_testBasicCommentRdfaMarkup($graph, $comment1); + + // Tests comment #1 with no access to the user profile (as anonymous user). + $this->drupalLogout(); + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); + $this->_testBasicCommentRdfaMarkup($graph, $comment1); + + // Posts comment #2 as anonymous user. + $anonymous_user = []; + $anonymous_user['name'] = $this->randomMachineName(); + $anonymous_user['mail'] = 'tester@simpletest.org'; + $anonymous_user['homepage'] = 'http://example.org/'; + $comment2 = $this->saveComment($this->node->id(), 0, $anonymous_user); + + // Tests comment #2 as anonymous user. + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); + $this->_testBasicCommentRdfaMarkup($graph, $comment2, $anonymous_user); + + // Tests comment #2 as logged in user. + $this->drupalLogin($this->webUser); + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); + $this->_testBasicCommentRdfaMarkup($graph, $comment2, $anonymous_user); + } + + /** + * Tests RDF comment replies. + */ + public function testCommentReplyOfRdfaMarkup() { + // Posts comment #1 on behalf of registered user. + $this->drupalLogin($this->webUser); + $comment_1 = $this->saveComment($this->node->id(), $this->webUser->id()); + + $comment_1_uri = $comment_1->url('canonical', ['absolute' => TRUE]); + + // Posts a reply to the first comment. + $comment_2 = $this->saveComment($this->node->id(), $this->webUser->id(), NULL, $comment_1->id()); + $comment_2_uri = $comment_2->url('canonical', ['absolute' => TRUE]); + + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $parser->parse($graph, $this->drupalGet('node/' . $this->node->id()), 'rdfa', $this->baseUri); + + // Tests the reply_of relationship of a first level comment. + $expected_value = [ + 'type' => 'uri', + 'value' => $this->nodeUri, + ]; + $this->assertTrue($graph->hasProperty($comment_1_uri, 'http://rdfs.org/sioc/ns#reply_of', $expected_value), 'Comment relation to its node found in RDF output (sioc:reply_of).'); + + // Tests the reply_of relationship of a second level comment. + $expected_value = [ + 'type' => 'uri', + 'value' => $this->nodeUri, + ]; + $this->assertTrue($graph->hasProperty($comment_2_uri, 'http://rdfs.org/sioc/ns#reply_of', $expected_value), 'Comment relation to its node found in RDF output (sioc:reply_of).'); + $expected_value = [ + 'type' => 'uri', + 'value' => $comment_1_uri, + ]; + $this->assertTrue($graph->hasProperty($comment_2_uri, 'http://rdfs.org/sioc/ns#reply_of', $expected_value), 'Comment relation to its parent comment found in RDF output (sioc:reply_of).'); + } + + /** + * Helper function for testCommentRdfaMarkup(). + * + * Tests the current page for basic comment RDFa markup. + * + * @param $comment + * Comment object. + * @param $account + * An array containing information about an anonymous user. + */ + public function _testBasicCommentRdfaMarkup($graph, CommentInterface $comment, $account = []) { + $comment_uri = $comment->url('canonical', ['absolute' => TRUE]); + + // Comment type. + $expected_value = [ + 'type' => 'uri', + 'value' => 'http://rdfs.org/sioc/types#Comment', + ]; + $this->assertTrue($graph->hasProperty($comment_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Comment type found in RDF output (sioct:Comment).'); + // Comment type. + $expected_value = [ + 'type' => 'uri', + 'value' => 'http://rdfs.org/sioc/ns#Post', + ]; + $this->assertTrue($graph->hasProperty($comment_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Comment type found in RDF output (sioc:Post).'); + + // Comment title. + $expected_value = [ + 'type' => 'literal', + 'value' => $comment->getSubject(), + 'lang' => 'en', + ]; + $this->assertTrue($graph->hasProperty($comment_uri, 'http://purl.org/dc/terms/title', $expected_value), 'Comment subject found in RDF output (dc:title).'); + + // Comment date. + $expected_value = [ + 'type' => 'literal', + 'value' => format_date($comment->getCreatedTime(), 'custom', 'c', 'UTC'), + 'datatype' => 'http://www.w3.org/2001/XMLSchema#dateTime', + ]; + $this->assertTrue($graph->hasProperty($comment_uri, 'http://purl.org/dc/terms/date', $expected_value), 'Comment date found in RDF output (dc:date).'); + // Comment date. + $expected_value = [ + 'type' => 'literal', + 'value' => format_date($comment->getCreatedTime(), 'custom', 'c', 'UTC'), + 'datatype' => 'http://www.w3.org/2001/XMLSchema#dateTime', + ]; + $this->assertTrue($graph->hasProperty($comment_uri, 'http://purl.org/dc/terms/created', $expected_value), 'Comment date found in RDF output (dc:created).'); + + // Comment body. + $expected_value = [ + 'type' => 'literal', + 'value' => $comment->comment_body->value . "\n", + 'lang' => 'en', + ]; + $this->assertTrue($graph->hasProperty($comment_uri, 'http://purl.org/rss/1.0/modules/content/encoded', $expected_value), 'Comment body found in RDF output (content:encoded).'); + + // The comment author can be a registered user or an anonymous user. + if ($comment->getOwnerId() > 0) { + $author_uri = \Drupal::url('entity.user.canonical', ['user' => $comment->getOwnerId()], ['absolute' => TRUE]); + // Comment relation to author. + $expected_value = [ + 'type' => 'uri', + 'value' => $author_uri, + ]; + $this->assertTrue($graph->hasProperty($comment_uri, 'http://rdfs.org/sioc/ns#has_creator', $expected_value), 'Comment relation to author found in RDF output (sioc:has_creator).'); + } + else { + // The author is expected to be a blank node. + $author_uri = $graph->get($comment_uri, ''); + if ($author_uri instanceof \EasyRdf_Resource) { + $this->assertTrue($author_uri->isBnode(), 'Comment relation to author found in RDF output (sioc:has_creator) and author is blank node.'); + } + else { + $this->fail('Comment relation to author found in RDF output (sioc:has_creator).'); + } + } + + // Author name. + $name = empty($account["name"]) ? $this->webUser->getUsername() : $account["name"] . " (not verified)"; + $expected_value = [ + 'type' => 'literal', + 'value' => $name, + ]; + $this->assertTrue($graph->hasProperty($author_uri, 'http://xmlns.com/foaf/0.1/name', $expected_value), 'Comment author name found in RDF output (foaf:name).'); + + // Comment author homepage (only for anonymous authors). + if ($comment->getOwnerId() == 0) { + $expected_value = [ + 'type' => 'uri', + 'value' => 'http://example.org/', + ]; + $this->assertTrue($graph->hasProperty($author_uri, 'http://xmlns.com/foaf/0.1/page', $expected_value), 'Comment author link found in RDF output (foaf:page).'); + } + } + + /** + * Creates a comment entity. + * + * @param $nid + * Node id which will hold the comment. + * @param $uid + * User id of the author of the comment. Can be NULL if $contact provided. + * @param $contact + * Set to NULL for no contact info, TRUE to ignore success checking, and + * array of values to set contact info. + * @param $pid + * Comment id of the parent comment in a thread. + * + * @return \Drupal\comment\Entity\Comment + * The saved comment. + */ + public function saveComment($nid, $uid, $contact = NULL, $pid = 0) { + $values = [ + 'entity_id' => $nid, + 'entity_type' => 'node', + 'field_name' => 'comment', + 'uid' => $uid, + 'pid' => $pid, + 'subject' => $this->randomMachineName(), + 'comment_body' => $this->randomMachineName(), + 'status' => 1, + ]; + if ($contact) { + $values += $contact; + } + + $comment = Comment::create($values); + $comment->save(); + return $comment; + } + +} diff --git a/core/modules/rdf/tests/src/Functional/EntityReferenceFieldAttributesTest.php b/core/modules/rdf/tests/src/Functional/EntityReferenceFieldAttributesTest.php index 629c4aa..b91215d 100644 --- a/core/modules/rdf/tests/src/Functional/EntityReferenceFieldAttributesTest.php +++ b/core/modules/rdf/tests/src/Functional/EntityReferenceFieldAttributesTest.php @@ -125,23 +125,23 @@ public function testNodeTeaser() { 'value' => 'http://www.w3.org/2004/02/skos/core#Concept', ]; // @todo Enable with https://www.drupal.org/node/2072791. - //$this->assertTrue($graph->hasProperty($taxonomy_term_1_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Taxonomy term type found in RDF output (skos:Concept).'); + // $this->assertTrue($graph->hasProperty($taxonomy_term_1_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Taxonomy term type found in RDF output (skos:Concept).'); $expected_value = [ 'type' => 'literal', 'value' => $term1->getName(), ]; - //$this->assertTrue($graph->hasProperty($taxonomy_term_1_uri, 'http://www.w3.org/2000/01/rdf-schema#label', $expected_value), 'Taxonomy term name found in RDF output (rdfs:label).'); + // $this->assertTrue($graph->hasProperty($taxonomy_term_1_uri, 'http://www.w3.org/2000/01/rdf-schema#label', $expected_value), 'Taxonomy term name found in RDF output (rdfs:label).'); // Term 2. $expected_value = [ 'type' => 'uri', 'value' => 'http://www.w3.org/2004/02/skos/core#Concept', ]; - //$this->assertTrue($graph->hasProperty($taxonomy_term_2_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Taxonomy term type found in RDF output (skos:Concept).'); + // $this->assertTrue($graph->hasProperty($taxonomy_term_2_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Taxonomy term type found in RDF output (skos:Concept).'); $expected_value = [ 'type' => 'literal', 'value' => $term2->getName(), ]; - //$this->assertTrue($graph->hasProperty($taxonomy_term_2_uri, 'http://www.w3.org/2000/01/rdf-schema#label', $expected_value), 'Taxonomy term name found in RDF output (rdfs:label).'); + // $this->assertTrue($graph->hasProperty($taxonomy_term_2_uri, 'http://www.w3.org/2000/01/rdf-schema#label', $expected_value), 'Taxonomy term name found in RDF output (rdfs:label).'); } } diff --git a/core/modules/rdf/tests/src/Functional/GetNamespacesTest.php b/core/modules/rdf/tests/src/Functional/GetNamespacesTest.php new file mode 100644 index 0000000..abdecc2 --- /dev/null +++ b/core/modules/rdf/tests/src/Functional/GetNamespacesTest.php @@ -0,0 +1,51 @@ +drupalGet(''); + + $element = $this->xpath('//*[ancestor::html[contains(@prefix, :prefix_binding)]]', [ + ':prefix_binding' => 'rdfs: http://www.w3.org/2000/01/rdf-schema#', + ]); + + $this->assertTrue(!empty($element[0]->getText()), 'A prefix declared once is displayed.'); + + $element = $this->xpath('//*[ancestor::html[contains(@prefix, :prefix_binding)]]', [ + ':prefix_binding' => 'foaf: http://xmlns.com/foaf/0.1/', + ]); + $this->assertTrue(!empty($element[0]->getText()), 'The same prefix declared in several implementations of hook_rdf_namespaces() is valid as long as all the namespaces are the same.'); + + $element = $this->xpath('//*[ancestor::html[contains(@prefix, :prefix_binding)]]', [ + ':prefix_binding' => 'foaf1: http://xmlns.com/foaf/0.1/', + ]); + $this->assertTrue(!empty($element[0]->getText()), 'Two prefixes can be assigned the same namespace.'); + + $element = $this->xpath('//*[ancestor::html[contains(@prefix, :prefix_binding)]]', [ + ':prefix_binding' => 'dc: http://purl.org/dc/terms/', + ]); + $this->assertTrue(!empty($element[0]->getText()), 'When a prefix has conflicting namespaces, the first declared one is used.'); + } + +} diff --git a/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php b/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php new file mode 100644 index 0000000..e570759 --- /dev/null +++ b/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php @@ -0,0 +1,118 @@ +fieldName = 'field_image'; + + // Create the image field. + $this->createImageField($this->fieldName, 'article'); + + // Set the RDF mapping for the new field. + rdf_get_mapping('node', 'article') + ->setFieldMapping($this->fieldName, [ + 'properties' => ['og:image'], + 'mapping_type' => 'rel', + ]) + ->setBundleMapping(['types' => []]) + ->save(); + + // Get the test image that simpletest provides. + $image = current($this->drupalGetTestFiles('image')); + + // Save a node with the image. + $nid = $this->uploadNodeImage($image, $this->fieldName, 'article', $this->randomMachineName()); + $this->node = Node::load($nid); + $this->file = File::load($this->node->{$this->fieldName}->target_id); + } + + /** + * Tests that image fields in teasers have correct resources. + */ + public function testNodeTeaser() { + // Set the display options for the teaser. + $display_options = [ + 'type' => 'image', + 'settings' => ['image_style' => 'medium', 'image_link' => 'content'], + ]; + $display = entity_get_display('node', 'article', 'teaser'); + $display->setComponent($this->fieldName, $display_options) + ->save(); + + // Render the teaser. + $node_render_array = node_view($this->node, 'teaser'); + $html = \Drupal::service('renderer')->renderRoot($node_render_array); + + // Parse the teaser. + $parser = new \EasyRdf_Parser_Rdfa(); + $graph = new \EasyRdf_Graph(); + $base_uri = \Drupal::url('', [], ['absolute' => TRUE]); + $parser->parse($graph, $html, 'rdfa', $base_uri); + + // Construct the node and image URIs for testing. + $node_uri = $this->node->url('canonical', ['absolute' => TRUE]); + $image_uri = ImageStyle::load('medium')->buildUrl($this->file->getFileUri()); + + // Test relations from node to image. + $expected_value = [ + 'type' => 'uri', + 'value' => $image_uri, + ]; + $this->assertTrue($graph->hasProperty($node_uri, 'http://ogp.me/ns#image', $expected_value), 'Node to file relation found in RDF output (og:image).'); + + // Test image type. + $expected_value = [ + 'type' => 'uri', + 'value' => 'http://xmlns.com/foaf/0.1/Image', + ]; + $this->assertTrue($graph->hasProperty($image_uri, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $expected_value), 'Image type found in RDF output (foaf:Image).'); + } + +} diff --git a/core/modules/rdf/tests/src/Functional/StandardProfileTest.php b/core/modules/rdf/tests/src/Functional/StandardProfileTest.php index 76ae39b..8662b9d 100644 --- a/core/modules/rdf/tests/src/Functional/StandardProfileTest.php +++ b/core/modules/rdf/tests/src/Functional/StandardProfileTest.php @@ -407,7 +407,7 @@ protected function assertRdfaArticleProperties($graph, $message_prefix) { // Tag type. // @todo Enable with https://www.drupal.org/node/2072791. - //$this->assertEqual($graph->type($this->termUri), 'schema:Thing', 'Tag type was found (schema:Thing).'); + // $this->assertEqual($graph->type($this->termUri), 'schema:Thing', 'Tag type was found (schema:Thing).'); // Tag name. $expected_value = [ @@ -416,7 +416,7 @@ protected function assertRdfaArticleProperties($graph, $message_prefix) { 'lang' => 'en', ]; // @todo Enable with https://www.drupal.org/node/2072791. - //$this->assertTrue($graph->hasProperty($this->termUri, 'http://schema.org/name', $expected_value), "$message_prefix name was found (schema:name)."); + // $this->assertTrue($graph->hasProperty($this->termUri, 'http://schema.org/name', $expected_value), "$message_prefix name was found (schema:name)."); } /** diff --git a/core/modules/rdf/tests/src/Unit/RdfMappingConfigEntityUnitTest.php b/core/modules/rdf/tests/src/Unit/RdfMappingConfigEntityUnitTest.php index 5908ee7..620dbcb 100644 --- a/core/modules/rdf/tests/src/Unit/RdfMappingConfigEntityUnitTest.php +++ b/core/modules/rdf/tests/src/Unit/RdfMappingConfigEntityUnitTest.php @@ -3,6 +3,8 @@ namespace Drupal\Tests\rdf\Unit; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Tests\UnitTestCase; use Drupal\rdf\Entity\RdfMapping; @@ -27,6 +29,13 @@ class RdfMappingConfigEntityUnitTest extends UnitTestCase { protected $entityManager; /** + * The entity type manager used for testing. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityTypeManager; + + /** * The ID of the type of the entity under test. * * @var string @@ -51,13 +60,16 @@ protected function setUp() { ->method('getProvider') ->will($this->returnValue('entity')); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager = new EntityManager(); + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); $container = new ContainerBuilder(); $container->set('entity.manager', $this->entityManager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); + $this->entityManager->setContainer($container); \Drupal::setContainer($container); } @@ -77,11 +89,11 @@ public function testCalculateDependencies() { ->method('getBundleEntityType') ->will($this->returnValue(NULL)); - $this->entityManager->expects($this->at(0)) + $this->entityTypeManager->expects($this->at(0)) ->method('getDefinition') ->with($target_entity_type_id) ->will($this->returnValue($target_entity_type)); - $this->entityManager->expects($this->at(1)) + $this->entityTypeManager->expects($this->at(1)) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue($this->entityType)); @@ -108,11 +120,11 @@ public function testCalculateDependenciesWithEntityBundle() { ->method('getBundleConfigDependency') ->will($this->returnValue(['type' => 'config', 'name' => 'test_module.type.' . $bundle_id])); - $this->entityManager->expects($this->at(0)) + $this->entityTypeManager->expects($this->at(0)) ->method('getDefinition') ->with($target_entity_type_id) ->will($this->returnValue($target_entity_type)); - $this->entityManager->expects($this->at(1)) + $this->entityTypeManager->expects($this->at(1)) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue($this->entityType)); diff --git a/core/modules/responsive_image/tests/src/Unit/ResponsiveImageStyleConfigEntityUnitTest.php b/core/modules/responsive_image/tests/src/Unit/ResponsiveImageStyleConfigEntityUnitTest.php index bc9663d..0c64b05 100644 --- a/core/modules/responsive_image/tests/src/Unit/ResponsiveImageStyleConfigEntityUnitTest.php +++ b/core/modules/responsive_image/tests/src/Unit/ResponsiveImageStyleConfigEntityUnitTest.php @@ -3,6 +3,8 @@ namespace Drupal\Tests\responsive_image\Unit; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Entity\EntityTypeRepositoryInterface; use Drupal\responsive_image\Entity\ResponsiveImageStyle; use Drupal\Tests\UnitTestCase; @@ -20,11 +22,11 @@ class ResponsiveImageStyleConfigEntityUnitTest extends UnitTestCase { protected $entityType; /** - * The entity manager used for testing. + * The entity type manager used for testing. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The breakpoint manager used for testing. @@ -42,8 +44,8 @@ protected function setUp() { ->method('getProvider') ->will($this->returnValue('responsive_image')); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); - $this->entityManager->expects($this->any()) + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with('responsive_image_style') ->will($this->returnValue($this->entityType)); @@ -51,7 +53,7 @@ protected function setUp() { $this->breakpointManager = $this->getMock('\Drupal\breakpoint\BreakpointManagerInterface'); $container = new ContainerBuilder(); - $container->set('entity.manager', $this->entityManager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('breakpoint.manager', $this->breakpointManager); \Drupal::setContainer($container); } @@ -74,11 +76,14 @@ public function testCalculateDependencies() { ->method('loadMultiple') ->with(array_keys($styles)) ->willReturn($styles); - $this->entityManager->expects($this->any()) + + $this->entityTypeManager->expects($this->any()) ->method('getStorage') ->with('image_style') ->willReturn($storage); - $this->entityManager->expects($this->any()) + + $entity_type_repository = $this->getMockForAbstractClass(EntityTypeRepositoryInterface::class); + $entity_type_repository->expects($this->any()) ->method('getEntityTypeFromClass') ->with('Drupal\image\Entity\ImageStyle') ->willReturn('image_style'); @@ -103,6 +108,8 @@ public function testCalculateDependencies() { ->with('test_group') ->willReturn(['bartik' => 'theme', 'toolbar' => 'module']); + \Drupal::getContainer()->set('entity_type.repository', $entity_type_repository); + $dependencies = $entity->calculateDependencies()->getDependencies(); $this->assertEquals(['toolbar'], $dependencies['module']); $this->assertEquals(['bartik'], $dependencies['theme']); diff --git a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php index df77281..ef33817 100644 --- a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php +++ b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php @@ -79,7 +79,7 @@ public function onResponse(FilterResponseEvent $event) { * Determines the format to respond in. * * Respects the requested format if one is specified. However, it is common to - * forget to specify a request format in case of a POST or PATCH. Rather than + * forget to specify a response format in case of a POST or PATCH. Rather than * simply throwing an error, we apply the robustness principle: when POSTing * or PATCHing using a certain format, you probably expect a response in that * same format. @@ -94,33 +94,35 @@ public function onResponse(FilterResponseEvent $event) { */ public function getResponseFormat(RouteMatchInterface $route_match, Request $request) { $route = $route_match->getRouteObject(); - $acceptable_request_formats = $route->hasRequirement('_format') ? explode('|', $route->getRequirement('_format')) : []; - $acceptable_content_type_formats = $route->hasRequirement('_content_type_format') ? explode('|', $route->getRequirement('_content_type_format')) : []; - $acceptable_formats = $request->isMethodCacheable() ? $acceptable_request_formats : $acceptable_content_type_formats; + $acceptable_response_formats = $route->hasRequirement('_format') ? explode('|', $route->getRequirement('_format')) : []; + $acceptable_request_formats = $route->hasRequirement('_content_type_format') ? explode('|', $route->getRequirement('_content_type_format')) : []; + $acceptable_formats = $request->isMethodCacheable() ? $acceptable_response_formats : $acceptable_request_formats; $requested_format = $request->getRequestFormat(); $content_type_format = $request->getContentType(); - // If an acceptable format is requested, then use that. Otherwise, including - // and particularly when the client forgot to specify a format, then use - // heuristics to select the format that is most likely expected. - if (in_array($requested_format, $acceptable_formats)) { + // If an acceptable response format is requested, then use that. Otherwise, + // including and particularly when the client forgot to specify a response + // format, then use heuristics to select the format that is most likely + // expected. + if (in_array($requested_format, $acceptable_response_formats, TRUE)) { return $requested_format; } + // If a request body is present, then use the format corresponding to the // request body's Content-Type for the response, if it's an acceptable // format for the request. - elseif (!empty($request->getContent()) && in_array($content_type_format, $acceptable_content_type_formats)) { + if (!empty($request->getContent()) && in_array($content_type_format, $acceptable_request_formats, TRUE)) { return $content_type_format; } + // Otherwise, use the first acceptable format. - elseif (!empty($acceptable_formats)) { + if (!empty($acceptable_formats)) { return $acceptable_formats[0]; } + // Sometimes, there are no acceptable formats, e.g. DELETE routes. - else { - return NULL; - } + return NULL; } /** diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php index e5437cc..4b7020c 100644 --- a/core/modules/rest/src/RequestHandler.php +++ b/core/modules/rest/src/RequestHandler.php @@ -2,8 +2,10 @@ namespace Drupal\rest; +use Drupal\Component\Utility\ArgumentsResolver; use Drupal\Core\Cache\CacheableResponseInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; @@ -119,17 +121,17 @@ public function handle(RouteMatchInterface $route_match, Request $request) { // Determine the request parameters that should be passed to the resource // plugin. - $route_parameters = $route_match->getParameters(); - $parameters = []; - // Filter out all internal parameters starting with "_". - foreach ($route_parameters as $key => $parameter) { - if ($key{0} !== '_') { - $parameters[] = $parameter; - } + $argument_resolver = $this->createArgumentResolver($route_match, $unserialized, $request); + try { + $arguments = $argument_resolver->getArguments([$resource, $method]); + } + catch (\RuntimeException $exception) { + @trigger_error('Passing in arguments the legacy way is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Provide the right parameter names in the method, similar to controllers. See https://www.drupal.org/node/2894819', E_USER_DEPRECATED); + $arguments = $this->getLegacyParameters($route_match, $unserialized, $request); } // Invoke the operation on the resource plugin. - $response = call_user_func_array([$resource, $method], array_merge($parameters, [$unserialized, $request])); + $response = call_user_func_array([$resource, $method], $arguments); if ($response instanceof CacheableResponseInterface) { // Add rest config's cache tags. @@ -139,4 +141,100 @@ public function handle(RouteMatchInterface $route_match, Request $request) { return $response; } + /** + * Creates an argument resolver, containing all REST parameters. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match. + * @param mixed $unserialized + * The unserialized data. + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * + * @return \Drupal\Component\Utility\ArgumentsResolver + * An instance of the argument resolver containing information like the + * 'entity' we process and the 'unserialized' content from the request body. + */ + protected function createArgumentResolver(RouteMatchInterface $route_match, $unserialized, Request $request) { + $route = $route_match->getRouteObject(); + + // Defaults for the parameters defined on the route object need to be added + // to the raw arguments. + $raw_route_arguments = $route_match->getRawParameters()->all() + $route->getDefaults(); + + $route_arguments = $route_match->getParameters()->all(); + $upcasted_route_arguments = $route_arguments; + + // For request methods that have request bodies, ResourceInterface plugin + // methods historically receive the unserialized request body as the N+1th + // method argument, where N is the number of route parameters specified on + // the accompanying route. To be able to use the argument resolver, which is + // not based on position but on name and typehint, specify commonly used + // names here. Similarly, those methods receive the original stored object + // as the first method argument. + + $route_arguments_entity = NULL; + // Try to find a parameter which is an entity. + foreach ($route_arguments as $value) { + if ($value instanceof EntityInterface) { + $route_arguments_entity = $value; + break; + } + } + + if (in_array($request->getMethod(), ['PATCH', 'POST'], TRUE)) { + $upcasted_route_arguments['entity'] = $unserialized; + $upcasted_route_arguments['data'] = $unserialized; + $upcasted_route_arguments['unserialized'] = $unserialized; + $upcasted_route_arguments['original_entity'] = $route_arguments_entity; + } + else { + $upcasted_route_arguments['entity'] = $route_arguments_entity; + } + + // Parameters which are not defined on the route object, but still are + // essential for access checking are passed as wildcards to the argument + // resolver. + $wildcard_arguments = [$route, $route_match]; + $wildcard_arguments[] = $request; + if (isset($unserialized)) { + $wildcard_arguments[] = $unserialized; + } + + return new ArgumentsResolver($raw_route_arguments, $upcasted_route_arguments, $wildcard_arguments); + } + + /** + * Provides the parameter usable without an argument resolver. + * + * This creates an list of parameters in a statically defined order. + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The route match + * @param mixed $unserialized + * The unserialized data. + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * + * @deprecated in Drupal 8.4.0, will be removed before Drupal 9.0.0. Use the + * argument resolver method instead, see ::createArgumentResolver(). + * + * @see https://www.drupal.org/node/2894819 + * + * @return array + * An array of parameters. + */ + protected function getLegacyParameters(RouteMatchInterface $route_match, $unserialized, Request $request) { + $route_parameters = $route_match->getParameters(); + $parameters = []; + // Filter out all internal parameters starting with "_". + foreach ($route_parameters as $key => $parameter) { + if ($key{0} !== '_') { + $parameters[] = $parameter; + } + } + + return array_merge($parameters, [$unserialized, $request]); + } + } diff --git a/core/modules/rest/tests/modules/rest_test/src/Plugin/rest/resource/NoSerializationClassTestResource.php b/core/modules/rest/tests/modules/rest_test/src/Plugin/rest/resource/NoSerializationClassTestResource.php index 3e83a4c..2689588 100644 --- a/core/modules/rest/tests/modules/rest_test/src/Plugin/rest/resource/NoSerializationClassTestResource.php +++ b/core/modules/rest/tests/modules/rest_test/src/Plugin/rest/resource/NoSerializationClassTestResource.php @@ -25,7 +25,7 @@ class NoSerializationClassTestResource extends ResourceBase { * * @return \Drupal\rest\ResourceResponse */ - public function post(array $data = []) { + public function post(array $data) { return new ResourceResponse($data); } diff --git a/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentJsonAnonTest.php b/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentJsonAnonTest.php new file mode 100644 index 0000000..16688cb --- /dev/null +++ b/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentJsonAnonTest.php @@ -0,0 +1,24 @@ +grantPermissionsToTestedRole(['administer blocks']); + } + + /** + * {@inheritdoc} + */ + protected function createEntity() { + if (!BlockContentType::load('basic')) { + $block_content_type = BlockContentType::create([ + 'id' => 'basic', + 'label' => 'basic', + 'revision' => TRUE, + ]); + $block_content_type->save(); + block_content_add_body_field($block_content_type->id()); + } + + // Create a "Llama" custom block. + $block_content = BlockContent::create([ + 'info' => 'Llama', + 'type' => 'basic', + 'body' => [ + 'value' => 'The name "llama" was adopted by European settlers from native Peruvians.', + 'format' => 'plain_text', + ], + ]) + ->setPublished(FALSE); + $block_content->save(); + return $block_content; + } + + /** + * {@inheritdoc} + */ + protected function getExpectedNormalizedEntity() { + return [ + 'id' => [ + [ + 'value' => 1, + ], + ], + 'uuid' => [ + [ + 'value' => $this->entity->uuid(), + ], + ], + 'langcode' => [ + [ + 'value' => 'en', + ], + ], + 'type' => [ + [ + 'target_id' => 'basic', + 'target_type' => 'block_content_type', + 'target_uuid' => BlockContentType::load('basic')->uuid(), + ], + ], + 'info' => [ + [ + 'value' => 'Llama', + ], + ], + 'revision_log' => [], + 'changed' => [ + $this->formatExpectedTimestampItemValues($this->entity->getChangedTime()), + ], + 'revision_id' => [ + [ + 'value' => 1, + ], + ], + 'revision_created' => [ + $this->formatExpectedTimestampItemValues((int) $this->entity->getRevisionCreationTime()), + ], + 'revision_user' => [], + 'revision_translation_affected' => [ + [ + 'value' => TRUE, + ], + ], + 'default_langcode' => [ + [ + 'value' => TRUE, + ], + ], + 'body' => [ + [ + 'value' => 'The name "llama" was adopted by European settlers from native Peruvians.', + 'format' => 'plain_text', + 'summary' => NULL, + ], + ], + 'status' => [ + [ + 'value' => FALSE, + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + protected function getNormalizedPostEntity() { + return [ + 'type' => [ + [ + 'target_id' => 'basic', + ], + ], + 'info' => [ + [ + 'value' => 'Dramallama', + ], + ], + ]; + } + + + /** + * {@inheritdoc} + */ + protected function getExpectedUnauthorizedAccessMessage($method) { + if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) { + return parent::getExpectedUnauthorizedAccessMessage($method); + } + + return parent::getExpectedUnauthorizedAccessMessage($method); + } + +} diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php index 5b51014..bade2a7 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php @@ -279,7 +279,7 @@ public function testPostDxWithoutCriticalBaseFields() { $this->assertSame(500, $response->getStatusCode()); $this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type')); $this->assertStringStartsWith('The website encountered an unexpected error. Please try again later.

Symfony\Component\HttpKernel\Exception\HttpException: Internal Server Error in Drupal\rest\Plugin\rest\resource\EntityResource->post()', (string) $response->getBody()); - //$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response); + // $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response); // DX: 422 when missing 'entity_id' field. $request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['entity_id' => TRUE]), static::$format); @@ -288,14 +288,14 @@ public function testPostDxWithoutCriticalBaseFields() { try { $response = $this->request('POST', $url, $request_options); // This happens on DrupalCI. - //$this->assertSame(500, $response->getStatusCode()); + // $this->assertSame(500, $response->getStatusCode()); } catch (\Exception $e) { // This happens on Wim's local machine. - //$this->assertSame("Error: Call to a member function get() on null\nDrupal\\comment\\Plugin\\Validation\\Constraint\\CommentNameConstraintValidator->getAnonymousContactDetailsSetting()() (Line: 96)\n", $e->getMessage()); + // $this->assertSame("Error: Call to a member function get() on null\nDrupal\\comment\\Plugin\\Validation\\Constraint\\CommentNameConstraintValidator->getAnonymousContactDetailsSetting()() (Line: 96)\n", $e->getMessage()); } - //$response = $this->request('POST', $url, $request_options); - //$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response); + // $response = $this->request('POST', $url, $request_options); + // $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response); // DX: 422 when missing 'entity_type' field. $request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['field_name' => TRUE]), static::$format); @@ -303,7 +303,7 @@ public function testPostDxWithoutCriticalBaseFields() { // @todo Uncomment, remove next 2 lines in https://www.drupal.org/node/2820364. $this->assertSame(500, $response->getStatusCode()); $this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type')); - //$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nfield_name: This value should not be null.\n", $response); + // $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nfield_name: This value should not be null.\n", $response); } /** diff --git a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php index 2b88559..808d434 100644 --- a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php +++ b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php @@ -93,9 +93,9 @@ public function testHandle() { */ class StubRequestHandlerResourcePlugin extends ResourceBase { - public function get() {} + public function get($example, Request $request) {} public function post() {} - public function patch() {} + public function patch($example_original, Request $request) {} public function delete() {} } diff --git a/core/modules/rest/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php b/core/modules/rest/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php index d09fdce..eab2d9f 100644 --- a/core/modules/rest/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php +++ b/core/modules/rest/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php @@ -78,7 +78,7 @@ public function providerTestSerialization() { * * @dataProvider providerTestResponseFormat */ - public function testResponseFormat($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) { + public function testResponseFormat($methods, array $supported_response_formats, array $supported_request_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) { foreach ($request_headers as $key => $value) { unset($request_headers[$key]); $key = strtoupper(str_replace('-', '_', $key)); @@ -92,8 +92,10 @@ public function testResponseFormat($methods, array $supported_formats, $request_ if ($request_format) { $request->setRequestFormat($request_format); } - $route_requirement_key_format = $request->isMethodCacheable() ? '_format' : '_content_type_format'; - $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $this->randomMachineName()], [$route_requirement_key_format => implode('|', $supported_formats)])); + + $route_requirements = $this->generateRouteRequirements($supported_response_formats, $supported_request_formats); + + $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $this->randomMachineName()], $route_requirements)); $resource_response_subscriber = new ResourceResponseSubscriber( $this->prophesize(SerializerInterface::class)->reveal(), @@ -113,9 +115,7 @@ public function testResponseFormat($methods, array $supported_formats, $request_ * * @dataProvider providerTestResponseFormat */ - public function testOnResponseWithCacheableResponse($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) { - $rest_config_name = $this->randomMachineName(); - + public function testOnResponseWithCacheableResponse($methods, array $supported_response_formats, array $supported_request_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) { foreach ($request_headers as $key => $value) { unset($request_headers[$key]); $key = strtoupper(str_replace('-', '_', $key)); @@ -129,8 +129,10 @@ public function testOnResponseWithCacheableResponse($methods, array $supported_f if ($request_format) { $request->setRequestFormat($request_format); } - $route_requirement_key_format = $request->isMethodCacheable() ? '_format' : '_content_type_format'; - $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $rest_config_name], [$route_requirement_key_format => implode('|', $supported_formats)])); + + $route_requirements = $this->generateRouteRequirements($supported_response_formats, $supported_request_formats); + + $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $this->randomMachineName()], $route_requirements)); // The RequestHandler must return a ResourceResponseInterface object. $handler_response = new ResourceResponse($method !== 'DELETE' ? ['REST' => 'Drupal'] : NULL); @@ -163,9 +165,7 @@ public function testOnResponseWithCacheableResponse($methods, array $supported_f * * @dataProvider providerTestResponseFormat */ - public function testOnResponseWithUncacheableResponse($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) { - $rest_config_name = $this->randomMachineName(); - + public function testOnResponseWithUncacheableResponse($methods, array $supported_response_formats, array $supported_request_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) { foreach ($request_headers as $key => $value) { unset($request_headers[$key]); $key = strtoupper(str_replace('-', '_', $key)); @@ -179,8 +179,10 @@ public function testOnResponseWithUncacheableResponse($methods, array $supported if ($request_format) { $request->setRequestFormat($request_format); } - $route_requirement_key_format = $request->isMethodCacheable() ? '_format' : '_content_type_format'; - $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $rest_config_name], [$route_requirement_key_format => implode('|', $supported_formats)])); + + $route_requirements = $this->generateRouteRequirements($supported_response_formats, $supported_request_formats); + + $route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $this->randomMachineName()], $route_requirements)); // The RequestHandler must return a ResourceResponseInterface object. $handler_response = new ModifiedResourceResponse($method !== 'DELETE' ? ['REST' => 'Drupal'] : NULL); @@ -225,6 +227,7 @@ public function providerTestResponseFormat() { // @todo add 'HEAD' in https://www.drupal.org/node/2752325 ['GET'], ['xml', 'json'], + [], 'json', [], NULL, @@ -236,6 +239,7 @@ public function providerTestResponseFormat() { // @todo add 'HEAD' in https://www.drupal.org/node/2752325 ['GET'], ['xml', 'json'], + [], 'xml', [], NULL, @@ -247,6 +251,7 @@ public function providerTestResponseFormat() { // @todo add 'HEAD' in https://www.drupal.org/node/2752325 ['GET'], ['json', 'xml'], + [], FALSE, [], NULL, @@ -258,6 +263,7 @@ public function providerTestResponseFormat() { // @todo add 'HEAD' in https://www.drupal.org/node/2752325 ['GET'], ['xml', 'json'], + [], FALSE, [], NULL, @@ -271,6 +277,7 @@ public function providerTestResponseFormat() { 'unsafe methods with response (POST, PATCH): client requested no format, response should use request body format (JSON)' => [ ['POST', 'PATCH'], ['xml', 'json'], + ['xml', 'json'], FALSE, ['Content-Type' => 'application/json'], $json_encoded, @@ -281,6 +288,7 @@ public function providerTestResponseFormat() { 'unsafe methods with response (POST, PATCH): client requested no format, response should use request body format (XML)' => [ ['POST', 'PATCH'], ['xml', 'json'], + ['xml', 'json'], FALSE, ['Content-Type' => 'text/xml'], $xml_encoded, @@ -291,6 +299,7 @@ public function providerTestResponseFormat() { 'unsafe methods with response (POST, PATCH): client requested format other than request body format (JSON): response format should use requested format (XML)' => [ ['POST', 'PATCH'], ['xml', 'json'], + ['xml', 'json'], 'xml', ['Content-Type' => 'application/json'], $json_encoded, @@ -301,6 +310,7 @@ public function providerTestResponseFormat() { 'unsafe methods with response (POST, PATCH): client requested format other than request body format (XML), but is allowed for the request body (JSON)' => [ ['POST', 'PATCH'], ['xml', 'json'], + ['xml', 'json'], 'json', ['Content-Type' => 'text/xml'], $xml_encoded, @@ -308,12 +318,35 @@ public function providerTestResponseFormat() { 'application/json', $json_encoded, ], + 'unsafe methods with response (POST, PATCH): client requested format other than request body format when only XML is allowed as a content type format' => [ + ['POST', 'PATCH'], + ['xml'], + ['json'], + 'json', + ['Content-Type' => 'text/xml'], + $xml_encoded, + 'json', + 'application/json', + $json_encoded, + ], + 'unsafe methods with response (POST, PATCH): client requested format other than request body format when only JSON is allowed as a content type format' => [ + ['POST', 'PATCH'], + ['json'], + ['xml'], + 'xml', + ['Content-Type' => 'application/json'], + $json_encoded, + 'xml', + 'text/xml', + $xml_encoded, + ], ]; $unsafe_method_bodyless_test_cases = [ - 'unsafe methods with response bodies (DELETE): client requested no format, response should have no format' => [ + 'unsafe methods without response bodies (DELETE): client requested no format, response should have no format' => [ ['DELETE'], ['xml', 'json'], + ['xml', 'json'], FALSE, ['Content-Type' => 'application/json'], NULL, @@ -321,9 +354,10 @@ public function providerTestResponseFormat() { NULL, '', ], - 'unsafe methods with response bodies (DELETE): client requested format (XML), response should have no format' => [ + 'unsafe methods without response bodies (DELETE): client requested format (XML), response should have no format' => [ ['DELETE'], ['xml', 'json'], + ['xml', 'json'], 'xml', ['Content-Type' => 'application/json'], NULL, @@ -331,9 +365,10 @@ public function providerTestResponseFormat() { NULL, '', ], - 'unsafe methods with response bodies (DELETE): client requested format (JSON), response should have no format' => [ + 'unsafe methods without response bodies (DELETE): client requested format (JSON), response should have no format' => [ ['DELETE'], ['xml', 'json'], + ['xml', 'json'], 'json', ['Content-Type' => 'application/json'], NULL, @@ -368,4 +403,26 @@ protected function getFunctioningResourceResponseSubscriber(RouteMatchInterface return $resource_response_subscriber; } + /** + * Generates route requirements based on supported formats. + * + * @param array $supported_response_formats + * The supported response formats to add to the route requirements. + * @param array $supported_request_formats + * The supported request formats to add to the route requirements. + * + * @return array + * An array of route requirements. + */ + protected function generateRouteRequirements(array $supported_response_formats, array $supported_request_formats) { + $route_requirements = [ + '_format' => implode('|', $supported_response_formats), + ]; + if (!empty($supported_request_formats)) { + $route_requirements['_content_type_format'] = implode('|', $supported_request_formats); + } + + return $route_requirements; + } + } diff --git a/core/modules/simpletest/src/TestDiscovery.php b/core/modules/simpletest/src/TestDiscovery.php index b3844d1..9e6c32c 100644 --- a/core/modules/simpletest/src/TestDiscovery.php +++ b/core/modules/simpletest/src/TestDiscovery.php @@ -189,14 +189,17 @@ public function getTestClasses($extension = NULL, array $types = []) { // abstract class, trait or test fixture. continue; } - // Skip this test class if it requires unavailable modules. - // @todo PHPUnit skips tests with unmet requirements when executing a test - // (instead of excluding them upfront). Refactor test runner to follow - // that approach. + // Skip this test class if it is a Simpletest-based test and requires + // unavailable modules. TestDiscovery should not filter out module + // requirements for PHPUnit-based test classes. + // @todo Move this behavior to \Drupal\simpletest\TestBase so tests can be + // marked as skipped, instead. // @see https://www.drupal.org/node/1273478 - if (!empty($info['requires']['module'])) { - if (array_diff($info['requires']['module'], $this->availableExtensions['module'])) { - continue; + if ($info['type'] == 'Simpletest') { + if (!empty($info['requires']['module'])) { + if (array_diff($info['requires']['module'], $this->availableExtensions['module'])) { + continue; + } } } diff --git a/core/modules/simpletest/src/Tests/SkipRequiredModulesTest.php b/core/modules/simpletest/src/Tests/SkipRequiredModulesTest.php new file mode 100644 index 0000000..c68237c --- /dev/null +++ b/core/modules/simpletest/src/Tests/SkipRequiredModulesTest.php @@ -0,0 +1,28 @@ +fail('This test should have been skipped during discovery.'); + } + +} diff --git a/core/modules/simpletest/tests/src/Functional/MissingDependentModuleUnitTest.php b/core/modules/simpletest/tests/src/Functional/MissingDependentModuleUnitTest.php deleted file mode 100644 index acd55dd..0000000 --- a/core/modules/simpletest/tests/src/Functional/MissingDependentModuleUnitTest.php +++ /dev/null @@ -1,22 +0,0 @@ -fail('Running test with missing required module.'); - } - -} diff --git a/core/modules/system/src/Tests/Form/TriggeringElementTest.php b/core/modules/system/src/Tests/Form/TriggeringElementTest.php index 973d37d..fb59cb5 100644 --- a/core/modules/system/src/Tests/Form/TriggeringElementTest.php +++ b/core/modules/system/src/Tests/Form/TriggeringElementTest.php @@ -89,7 +89,7 @@ public function testAttemptAccessControlBypass() { // Ensure that the triggering element was not set to the restricted button. // Do this with both a negative and positive assertion, because negative // assertions alone can be brittle. See testNoButtonInfoInPost() for why the - //triggering element gets set to 'button2'. + // triggering element gets set to 'button2'. $this->assertNoText('The clicked button is button1.', '$form_state->getTriggeringElement() not set to a restricted button.'); $this->assertText('The clicked button is button2.', '$form_state->getTriggeringElement() not set to a restricted button.'); } diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 86ff866..a9ca4c3 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -250,7 +250,7 @@ function system_theme() { 'file' => 'system.admin.inc', ], 'admin_block' => [ - 'variables' => ['block' => NULL], + 'variables' => ['block' => NULL, 'attributes' => []], 'file' => 'system.admin.inc', ], 'admin_block_content' => [ diff --git a/core/modules/system/templates/admin-block.html.twig b/core/modules/system/templates/admin-block.html.twig index c3dab0e..6d1fa53 100644 --- a/core/modules/system/templates/admin-block.html.twig +++ b/core/modules/system/templates/admin-block.html.twig @@ -10,11 +10,17 @@ * - content: (optional) The content of the block. * - description: (optional) A description of the block. * (Description should only be output if content is not available). + * - attributes: HTML attributes for the containing div element. * * @ingroup themeable */ #} -
+{% + set classes = [ + 'panel', + ] +%} + {% if block.title %}

{{ block.title }}

{% endif %} diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml index 0ee125c..9e178cd 100644 --- a/core/modules/system/tests/modules/form_test/form_test.routing.yml +++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml @@ -497,3 +497,11 @@ form_test.get_form: _form: '\Drupal\form_test\Form\FormTestGetForm' requirements: _access: 'TRUE' + +form_test.optional_container: + path: '/form-test/optional-container' + defaults: + _form: '\Drupal\form_test\Form\FormTestOptionalContainerForm' + _title: 'Optional container testing' + requirements: + _access: 'TRUE' diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestOptionalContainerForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestOptionalContainerForm.php new file mode 100644 index 0000000..7cacf2e --- /dev/null +++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestOptionalContainerForm.php @@ -0,0 +1,60 @@ + 'container', + '#attributes' => ['class' => ['empty_optional']], + '#optional' => TRUE, + ]; + $form['empty_nonoptional'] = [ + '#type' => 'container', + '#attributes' => ['class' => ['empty_nonoptional']], + '#optional' => FALSE, + ]; + + // Non-empty containers + $form['nonempty_optional'] = [ + '#type' => 'container', + '#attributes' => ['class' => ['nonempty_optional']], + '#optional' => TRUE, + ]; + $form['nonempty_optional']['child_1'] = []; + + $form['nonempty_nonoptional'] = [ + '#type' => 'container', + '#attributes' => ['class' => ['nonempty_nonoptional']], + '#optional' => FALSE, + ]; + $form['nonempty_nonoptional']['child_2'] = []; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + } + +} diff --git a/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php b/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php index 76d8be8..04b1f66 100644 --- a/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php +++ b/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php @@ -60,7 +60,7 @@ public function _writeDirectory($base, $files = []) { $this->_writeDirectory($base . DIRECTORY_SEPARATOR . $key, $file); } else { - //just write the filename into the file + // just write the filename into the file file_put_contents($base . DIRECTORY_SEPARATOR . $file, $file); } } diff --git a/core/modules/system/tests/src/Functional/Form/ElementsContainerTest.php b/core/modules/system/tests/src/Functional/Form/ElementsContainerTest.php new file mode 100644 index 0000000..a7d404b --- /dev/null +++ b/core/modules/system/tests/src/Functional/Form/ElementsContainerTest.php @@ -0,0 +1,33 @@ +drupalGet('form-test/optional-container'); + $assertSession = $this->assertSession(); + $assertSession->elementNotExists('css', 'div.empty_optional'); + $assertSession->elementExists('css', 'div.empty_nonoptional'); + $assertSession->elementExists('css', 'div.nonempty_optional'); + $assertSession->elementExists('css', 'div.nonempty_nonoptional'); + } + +} diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 32b5b8c..385434a 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -349,7 +349,7 @@ function update_get_available($refresh = FALSE) { foreach ($projects as $key => $project) { // If there's no data at all, we clearly need to fetch some. if (empty($available[$key])) { - //update_create_fetch_task($project); + // update_create_fetch_task($project); \Drupal::service('update.processor')->createFetchTask($project); $needs_refresh = TRUE; continue; diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php index 7529532..f254ccf 100644 --- a/core/modules/user/src/Entity/User.php +++ b/core/modules/user/src/Entity/User.php @@ -9,6 +9,7 @@ use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Language\LanguageInterface; use Drupal\user\RoleInterface; +use Drupal\user\TimeZoneItem; use Drupal\user\UserInterface; /** @@ -498,6 +499,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->addPropertyConstraints('value', [ 'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedTimezones'], ]); + $fields['timezone']->getItemDefinition()->setClass(TimeZoneItem::class); $fields['status'] = BaseFieldDefinition::create('boolean') ->setLabel(t('User status')) diff --git a/core/modules/user/src/Tests/Views/AccessRoleTest.php b/core/modules/user/src/Tests/Views/AccessRoleTest.php index 5705557..addda24 100644 --- a/core/modules/user/src/Tests/Views/AccessRoleTest.php +++ b/core/modules/user/src/Tests/Views/AccessRoleTest.php @@ -128,8 +128,8 @@ public function testRenderCaching() { // @todo Fix this in https://www.drupal.org/node/2551037, // DisplayPluginBase::applyDisplayCacheabilityMetadata() is not invoked when // using buildBasicRenderable() and a Views access plugin returns FALSE. - //$this->assertTrue(in_array('user.roles', $build['#cache']['contexts'])); - //$this->assertEqual([], $build['#cache']['tags']); + // $this->assertTrue(in_array('user.roles', $build['#cache']['contexts'])); + // $this->assertEqual([], $build['#cache']['tags']); $this->assertEqual(Cache::PERMANENT, $build['#cache']['max-age']); $this->assertEqual($result, ''); } diff --git a/core/modules/user/src/TimeZoneItem.php b/core/modules/user/src/TimeZoneItem.php new file mode 100644 index 0000000..f109691 --- /dev/null +++ b/core/modules/user/src/TimeZoneItem.php @@ -0,0 +1,24 @@ +installEntitySchema('user'); + } + + /** * Tests some of the methods. * * @see \Drupal\user\Entity\User::getRoles() @@ -65,4 +73,22 @@ public function testUserMethods() { $this->assertEqual([RoleInterface::AUTHENTICATED_ID, 'test_role_two'], $user->getRoles()); } + /** + * Tests that all user fields validate properly. + * + * @see \Drupal\Core\Field\FieldItemListInterface::generateSampleItems + * @see \Drupal\Core\Field\FieldItemInterface::generateSampleValue() + * @see \Drupal\Core\Entity\FieldableEntityInterface::validate() + */ + public function testUserValidation() { + $user = User::create([]); + foreach ($user as $field_name => $field) { + if (!in_array($field_name, ['uid'])) { + $user->$field_name->generateSampleItems(); + } + } + $violations = $user->validate(); + $this->assertFalse((bool) $violations->count()); + } + } diff --git a/core/modules/user/tests/src/Unit/Views/Argument/RolesRidTest.php b/core/modules/user/tests/src/Unit/Views/Argument/RolesRidTest.php index b8f2888..4e48fba 100644 --- a/core/modules/user/tests/src/Unit/Views/Argument/RolesRidTest.php +++ b/core/modules/user/tests/src/Unit/Views/Argument/RolesRidTest.php @@ -3,6 +3,8 @@ namespace Drupal\Tests\user\Unit\Views\Argument; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Tests\UnitTestCase; use Drupal\user\Entity\Role; use Drupal\user\Plugin\views\argument\RolesRid; @@ -44,23 +46,27 @@ public function testTitleQuery() { ->with('label') ->will($this->returnValue('label')); - $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); - $entity_manager->expects($this->any()) + $entity_manager = new EntityManager(); + $entity_type_manager = $this->getMock(EntityTypeManagerInterface::class); + $entity_type_manager->expects($this->any()) ->method('getDefinition') ->with($this->equalTo('user_role')) ->will($this->returnValue($entity_type)); - $entity_manager + $entity_type_manager ->expects($this->once()) ->method('getStorage') ->with($this->equalTo('user_role')) ->will($this->returnValue($role_storage)); - // @todo \Drupal\Core\Entity\Entity::entityType() uses a global call to - // entity_get_info(), which in turn wraps \Drupal::entityManager(). Set - // the entity manager until this is fixed. + // Set up a minimal container to satisfy Drupal\Core\Entity\Entity's + // dependency on it. $container = new ContainerBuilder(); $container->set('entity.manager', $entity_manager); + $container->set('entity_type.manager', $entity_type_manager); + // Inject the container into entity.manager so it can defer to + // entity_type.manager. + $entity_manager->setContainer($container); \Drupal::setContainer($container); $roles_rid_argument = new RolesRid([], 'user__roles_rid', [], $entity_manager); diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php index 4b11f07..5f9c978 100644 --- a/core/modules/views/src/Plugin/views/PluginBase.php +++ b/core/modules/views/src/Plugin/views/PluginBase.php @@ -355,7 +355,7 @@ protected function viewsTokenReplace($text, $tokens) { foreach ($tokens as $token => $replacement) { // Twig wants a token replacement array stripped of curly-brackets. // Some Views tokens come with curly-braces, others do not. - //@todo: https://www.drupal.org/node/2544392 + // @todo: https://www.drupal.org/node/2544392 if (strpos($token, '{{') !== FALSE) { // Twig wants a token replacement array stripped of curly-brackets. $token = trim(str_replace(['{{', '}}'], '', $token)); diff --git a/core/modules/views/src/Plugin/views/field/EntityOperations.php b/core/modules/views/src/Plugin/views/field/EntityOperations.php index 33f37fe..6f08829 100644 --- a/core/modules/views/src/Plugin/views/field/EntityOperations.php +++ b/core/modules/views/src/Plugin/views/field/EntityOperations.php @@ -84,7 +84,7 @@ public function defineOptions() { $options = parent::defineOptions(); $options['destination'] = [ - 'default' => TRUE, + 'default' => FALSE, ]; return $options; @@ -99,7 +99,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { $form['destination'] = [ '#type' => 'checkbox', '#title' => $this->t('Include destination'), - '#description' => $this->t('Include a destination parameter in the link to return the user to the original view upon completing the link action.'), + '#description' => $this->t('Enforce a destination parameter in the link to return the user to the original view upon completing the link action. Most operations include a destination by default and this setting is no longer needed.'), '#default_value' => $this->options['destination'], ]; } diff --git a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php index 1771c71..0b3dcc9 100644 --- a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php +++ b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php @@ -3,6 +3,7 @@ namespace Drupal\views\Plugin\views\wizard; use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Entity\EntityPublishedInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\UrlGeneratorTrait; @@ -140,7 +141,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $entity_types = \Drupal::entityManager()->getDefinitions(); foreach ($entity_types as $entity_type_id => $entity_type) { - if ($this->base_table == $entity_type->getBaseTable() || $this->base_table == $entity_type->getDataTable()) { + if (in_array($this->base_table, [$entity_type->getBaseTable(), $entity_type->getDataTable(), $entity_type->getRevisionTable(), $entity_type->getRevisionDataTable()], TRUE)) { $this->entityType = $entity_type; $this->entityTypeId = $entity_type_id; } @@ -165,6 +166,21 @@ public function getCreatedColumn() { public function getFilters() { $filters = []; + // Add a default filter on the publishing status field, if available. + if ($this->entityType && is_subclass_of($this->entityType->getClass(), EntityPublishedInterface::class)) { + $field_name = $this->entityType->getKey('published'); + $this->filters = [ + $field_name => [ + 'value' => TRUE, + 'table' => $this->base_table, + 'field' => $field_name, + 'plugin_id' => 'boolean', + 'entity_type' => $this->entityTypeId, + 'entity_field' => $field_name, + ] + ] + $this->filters; + } + $default = $this->filter_defaults; foreach ($this->filters as $name => $info) { diff --git a/core/modules/views/tests/src/Functional/Handler/FieldEntityOperationsTest.php b/core/modules/views/tests/src/Functional/Handler/FieldEntityOperationsTest.php index 653ce11..7ced4f7 100644 --- a/core/modules/views/tests/src/Functional/Handler/FieldEntityOperationsTest.php +++ b/core/modules/views/tests/src/Functional/Handler/FieldEntityOperationsTest.php @@ -73,7 +73,10 @@ public function testEntityOperations() { $this->assertTrue(count($operations) > 0, 'There are operations.'); foreach ($operations as $operation) { $expected_destination = Url::fromUri('internal:/test-entity-operations')->toString(); - $result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[@href=:path and text()=:title]', [':path' => $operation['url']->toString() . '?destination=' . $expected_destination, ':title' => (string) $operation['title']]); + // Update destination property of the URL as generating it in the + // test would by default point to the frontpage. + $operation['url']->setOption('query', ['destination' => $expected_destination]); + $result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[@href=:path and text()=:title]', [':path' => $operation['url']->toString(), ':title' => (string) $operation['title']]); $this->assertEqual(count($result), 1, t('Found entity @operation link with destination parameter.', ['@operation' => $operation['title']])); // Entities which were created in Hungarian should link to the Hungarian // edit form, others to the English one (which has no path prefix here). diff --git a/core/modules/views/tests/src/Functional/Plugin/ArgumentDefaultTest.php b/core/modules/views/tests/src/Functional/Plugin/ArgumentDefaultTest.php index b3956de..ea2f5b2 100644 --- a/core/modules/views/tests/src/Functional/Plugin/ArgumentDefaultTest.php +++ b/core/modules/views/tests/src/Functional/Plugin/ArgumentDefaultTest.php @@ -131,7 +131,7 @@ public function testArgumentDefaultFixed() { /** * @todo Test php default argument. */ - //function testArgumentDefaultPhp() {} + // function testArgumentDefaultPhp() {} /** * Test node default argument. diff --git a/core/modules/views/tests/src/Kernel/Handler/SortTranslationTest.php b/core/modules/views/tests/src/Kernel/Handler/SortTranslationTest.php index 549761d..e49ef7a 100644 --- a/core/modules/views/tests/src/Kernel/Handler/SortTranslationTest.php +++ b/core/modules/views/tests/src/Kernel/Handler/SortTranslationTest.php @@ -42,7 +42,7 @@ protected function setUp($import_test_views = TRUE) { $this->installEntitySchema('node'); $this->installEntitySchema('user'); - //$this->installConfig('node'); + // $this->installConfig('node'); $this->container->get('kernel')->rebuildContainer(); $node_type = NodeType::create(['type' => 'article']); diff --git a/core/modules/views/tests/src/Unit/ViewExecutableTest.php b/core/modules/views/tests/src/Unit/ViewExecutableTest.php index c11e648..e0aada0 100644 --- a/core/modules/views/tests/src/Unit/ViewExecutableTest.php +++ b/core/modules/views/tests/src/Unit/ViewExecutableTest.php @@ -311,7 +311,7 @@ public function testBuildThemeFunctions() { ]; $this->assertEquals($expected, $view->buildThemeFunctions('test_hook')); - //Change the name of the display plugin and make sure that is in the array. + // Change the name of the display plugin and make sure that is in the array. $view->display_handler->display['display_plugin'] = 'default2'; $expected = [ diff --git a/core/modules/workflows/src/WorkflowListBuilder.php b/core/modules/workflows/src/WorkflowListBuilder.php index eb652ad..c36171a 100644 --- a/core/modules/workflows/src/WorkflowListBuilder.php +++ b/core/modules/workflows/src/WorkflowListBuilder.php @@ -93,7 +93,7 @@ public function render() { $build = parent::render(); $workflow_types_count = count($this->workflowTypeManager->getDefinitions()); if ($workflow_types_count === 0) { - $build['table']['#empty'] = $this->t('There are no workflow types available. In order to create workflows you need to install a module that provides a workflow type. For example, the Content Moderation module provides a workflow type that enables workflows for content entities.'); + $build['table']['#empty'] = $this->t('There are no workflow types available. In order to create workflows you need to install a module that provides a workflow type. For example, the Content Moderation module provides a workflow type that enables workflows for content entities.', [':content-moderation' => '/admin/modules#module-content-moderation']); } return $build; } diff --git a/core/modules/workflows/tests/src/Functional/WorkflowUiNoTypeTest.php b/core/modules/workflows/tests/src/Functional/WorkflowUiNoTypeTest.php index c0c7de6..197386e 100644 --- a/core/modules/workflows/tests/src/Functional/WorkflowUiNoTypeTest.php +++ b/core/modules/workflows/tests/src/Functional/WorkflowUiNoTypeTest.php @@ -38,6 +38,7 @@ public function testWorkflowUiWithNoType() { $this->drupalGet('admin/config/workflow/workflows'); $this->assertSession()->pageTextContains('There are no workflow types available. In order to create workflows you need to install a module that provides a workflow type. For example, the Content Moderation module provides a workflow type that enables workflows for content entities.'); + $this->assertSession()->linkExists('Content Moderation'); $this->assertSession()->pageTextNotContains('Add workflow'); $this->container->get('module_installer')->install(['workflow_type_test']); diff --git a/core/phpcs.xml.dist b/core/phpcs.xml.dist index 037e82e..5b07e1e 100644 --- a/core/phpcs.xml.dist +++ b/core/phpcs.xml.dist @@ -63,6 +63,15 @@ + + + + + + + + + @@ -143,6 +152,7 @@ 0 + @@ -150,6 +160,7 @@ + diff --git a/core/scripts/js/babel-es6-watch.js b/core/scripts/js/babel-es6-watch.js index 9b49482..c9d1b14 100644 --- a/core/scripts/js/babel-es6-watch.js +++ b/core/scripts/js/babel-es6-watch.js @@ -39,8 +39,5 @@ watcher fs.stat(`${fileName}.js`, () => { fs.unlink(`${fileName}.js`, unlinkHandler); }); - fs.stat(`${fileName}.js.map`, () => { - fs.unlink(`${fileName}.js.map`, unlinkHandler); - }); }) .on('ready', () => log(`Watching '${fileMatch}' for changes.`)); diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php index b3dab6d..5ded484 100644 --- a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php @@ -504,7 +504,7 @@ public function testSchemaFallback() { $definition2 = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something.something'); // This should be the schema of config_schema_test.wildcard_fallback.* as - //well. + // well. $this->assertSame($definition, $definition2); } diff --git a/core/tests/Drupal/KernelTests/Core/Database/LoggingTest.php b/core/tests/Drupal/KernelTests/Core/Database/LoggingTest.php index 0216d3e..c262e38 100644 --- a/core/tests/Drupal/KernelTests/Core/Database/LoggingTest.php +++ b/core/tests/Drupal/KernelTests/Core/Database/LoggingTest.php @@ -64,7 +64,7 @@ public function testEnableTargetLogging() { db_query('SELECT name FROM {test} WHERE age > :age', [':age' => 25])->fetchCol(); - db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Ringo'], ['target' => 'replica']);//->fetchCol(); + db_query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Ringo'], ['target' => 'replica']);// ->fetchCol(); $queries1 = Database::getLog('testing1'); diff --git a/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php b/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php new file mode 100644 index 0000000..b5bcd48 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/CreateSampleEntityTest.php @@ -0,0 +1,89 @@ +installEntitySchema('file'); + $this->installEntitySchema('user'); + $this->installEntitySchema('node'); + $this->installEntitySchema('node_type'); + $this->installEntitySchema('file'); + $this->installEntitySchema('comment'); + $this->installEntitySchema('comment_type'); + $this->installEntitySchema('taxonomy_vocabulary'); + $this->installEntitySchema('taxonomy_term'); + $this->entityTypeManager = $this->container->get('entity_type.manager'); + NodeType::create(['type' => 'article', 'name' => 'Article'])->save(); + NodeType::create(['type' => 'page', 'name' => 'Page'])->save(); + Vocabulary::create(['name' => 'Tags', 'vid' => 'tags'])->save(); + } + + /** + * Tests sample value content entity creation of all types. + * + * @covers ::createWithSampleValues + */ + public function testSampleValueContentEntity() { + foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $definition) { + if ($definition->entityClassImplements(FieldableEntityInterface::class)) { + $label = $definition->getKey('label'); + $values = []; + if ($label) { + $title = $this->randomString(); + $values[$label] = $title; + } + // Create sample entities with bundles. + if ($bundle_type = $definition->getBundleEntityType()) { + foreach ($this->entityTypeManager->getStorage($bundle_type)->loadMultiple() as $bundle) { + $entity = $this->entityTypeManager->getStorage($entity_type_id)->createWithSampleValues($bundle->id(), $values); + $violations = $entity->validate(); + $this->assertCount(0, $violations); + if ($label) { + $this->assertEquals($title, $entity->label()); + } + } + } + // Create sample entities without bundles. + else { + $entity = $this->entityTypeManager->getStorage($entity_type_id)->createWithSampleValues(FALSE, $values); + $violations = $entity->validate(); + $this->assertCount(0, $violations); + if ($label) { + $this->assertEquals($title, $entity->label()); + } + } + } + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php index 255a3be..c18910e 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php @@ -353,7 +353,7 @@ public function testEntityAutocompleteAccess() { public function testEntityAutocompleteIdInput() { /** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */ $form_builder = $this->container->get('form_builder'); - //$form = $form_builder->getForm($this); + // $form = $form_builder->getForm($this); $form_state = (new FormState()) ->setMethod('GET') ->setValues([ diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php index a066616..7d7ea76 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php @@ -115,7 +115,7 @@ public function testEntityTypeUpdateWithoutData() { t('The %field_name field needs to be installed.', ['%field_name' => 'Revision ID']), ], ]; - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected); //, 'EntityDefinitionUpdateManager reports the expected change summary.'); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected); // , 'EntityDefinitionUpdateManager reports the expected change summary.'); // Run the update and ensure the revision table is created. $this->entityDefinitionUpdateManager->applyUpdates(); diff --git a/core/tests/Drupal/KernelTests/Core/Field/FieldSettingsTest.php b/core/tests/Drupal/KernelTests/Core/Field/FieldSettingsTest.php index 583e55b..590a96c 100644 --- a/core/tests/Drupal/KernelTests/Core/Field/FieldSettingsTest.php +++ b/core/tests/Drupal/KernelTests/Core/Field/FieldSettingsTest.php @@ -47,6 +47,34 @@ public function testBaseFieldSettings() { } /** + * Tests the base field settings on a cloned base field definition object. + */ + public function testBaseFieldSettingsOnClone() { + $base_field = BaseFieldDefinition::create('test_field'); + + // Check that the default settings have been populated. + $expected_settings = [ + 'test_field_storage_setting' => 'dummy test string', + 'changeable' => 'a changeable field storage setting', + 'unchangeable' => 'an unchangeable field storage setting', + 'translatable_storage_setting' => 'a translatable field storage setting', + 'test_field_setting' => 'dummy test string', + 'translatable_field_setting' => 'a translatable field setting', + ]; + $this->assertEquals($expected_settings, $base_field->getSettings()); + + // Clone the base field object and change one single setting using + // setSettings() on the cloned base field and check that it has been + // changed only on the cloned object. + $clone_base_field = clone $base_field; + $expected_settings_clone = $expected_settings; + $expected_settings_clone['changeable'] = $expected_settings['changeable'] . ' (clone)'; + $clone_base_field->setSetting('changeable', $expected_settings_clone['changeable']); + $this->assertEquals($expected_settings, $base_field->getSettings()); + $this->assertEquals($expected_settings_clone, $clone_base_field->getSettings()); + } + + /** * @covers \Drupal\field\Entity\FieldStorageConfig::getSettings * @covers \Drupal\field\Entity\FieldStorageConfig::setSettings */ diff --git a/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php b/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php index 7048632..1f49b09 100644 --- a/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php +++ b/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php @@ -16,16 +16,16 @@ class AliasTest extends PathUnitTestBase { public function testCRUD() { - //Prepare database table. + // Prepare database table. $connection = Database::getConnection(); $this->fixtures->createTables($connection); - //Create Path object. + // Create Path object. $aliasStorage = new AliasStorage($connection, $this->container->get('module_handler')); $aliases = $this->fixtures->sampleUrlAliases(); - //Create a few aliases + // Create a few aliases foreach ($aliases as $idx => $alias) { $aliasStorage->save($alias['source'], $alias['alias'], $alias['langcode']); @@ -34,11 +34,11 @@ public function testCRUD() { $this->assertEqual(count($rows), 1, format_string('Created an entry for %alias.', ['%alias' => $alias['alias']])); - //Cache the pid for further tests. + // Cache the pid for further tests. $aliases[$idx]['pid'] = $rows[0]->pid; } - //Load a few aliases + // Load a few aliases foreach ($aliases as $alias) { $pid = $alias['pid']; $loadedAlias = $aliasStorage->load(['pid' => $pid]); @@ -49,7 +49,7 @@ public function testCRUD() { $loadedAlias = $aliasStorage->load(['source' => '/node/1']); $this->assertEqual($loadedAlias['alias'], '/alias_for_node_1_und', 'The last created alias loaded by default.'); - //Update a few aliases + // Update a few aliases foreach ($aliases as $alias) { $fields = $aliasStorage->save($alias['source'], $alias['alias'] . '_updated', $alias['langcode'], $alias['pid']); @@ -61,7 +61,7 @@ public function testCRUD() { $this->assertEqual($pid, $alias['pid'], format_string('Updated entry for pid %pid.', ['%pid' => $pid])); } - //Delete a few aliases + // Delete a few aliases foreach ($aliases as $alias) { $pid = $alias['pid']; $aliasStorage->delete(['pid' => $pid]); @@ -74,11 +74,11 @@ public function testCRUD() { } public function testLookupPath() { - //Prepare database table. + // Prepare database table. $connection = Database::getConnection(); $this->fixtures->createTables($connection); - //Create AliasManager and Path object. + // Create AliasManager and Path object. $aliasManager = $this->container->get('path.alias_manager'); $aliasStorage = new AliasStorage($connection, $this->container->get('module_handler')); diff --git a/core/tests/Drupal/KernelTests/Core/Test/BrowserTestBaseTest.php b/core/tests/Drupal/KernelTests/Core/Test/BrowserTestBaseTest.php new file mode 100644 index 0000000..c93069c --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Test/BrowserTestBaseTest.php @@ -0,0 +1,71 @@ +setName('testRequiresModule'); + + // We cannot use $this->setExpectedException() because PHPUnit would skip + // the test before comparing the exception type. + try { + $stub_test->publicCheckRequirements(); + $this->fail('Missing required module throws skipped test exception.'); + } + catch (\PHPUnit_Framework_SkippedTestError $e) { + $this->assertEqual('Required modules: module_does_not_exist', $e->getMessage()); + } + } + + /** + * Tests that a test case is skipped when it requires a module not present. + * + * In order to catch checkRequirements() regressions, we have to make a new + * test object and run checkRequirements() here. + * + * @covers ::checkRequirements + * @covers ::checkModuleRequirements + */ + public function testRequiresModule() { + require __DIR__ . '/../../../../fixtures/BrowserMissingDependentModuleTest.php'; + + $stub_test = new BrowserMissingDependentModuleTest(); + // We have to setName() to the method name we're concerned with. + $stub_test->setName('testRequiresModule'); + + // We cannot use $this->setExpectedException() because PHPUnit would skip + // the test before comparing the exception type. + try { + $stub_test->publicCheckRequirements(); + $this->fail('Missing required module throws skipped test exception.'); + } + catch (\PHPUnit_Framework_SkippedTestError $e) { + $this->assertEqual('Required modules: module_does_not_exist', $e->getMessage()); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index 09a82c3..d2e61ee 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -21,6 +21,7 @@ use Drupal\Tests\AssertHelperTrait; use Drupal\Tests\ConfigTestTrait; use Drupal\Tests\RandomGeneratorTrait; +use Drupal\Tests\TestRequirementsTrait; use Drupal\simpletest\TestServiceProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Reference; @@ -46,9 +47,6 @@ * * @see \Drupal\Tests\KernelTestBase::$modules * @see \Drupal\Tests\KernelTestBase::enableModules() - * - * @todo Extend ::setRequirementsFromAnnotation() and ::checkRequirements() to - * account for '@requires module'. */ abstract class KernelTestBase extends TestCase implements ServiceProviderInterface { @@ -57,6 +55,7 @@ use AssertHelperTrait; use RandomGeneratorTrait; use ConfigTestTrait; + use TestRequirementsTrait; /** * {@inheritdoc} @@ -213,15 +212,6 @@ public static function setUpBeforeClass() { } /** - * Returns the drupal root directory. - * - * @return string - */ - protected static function getDrupalRoot() { - return dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__)))); - } - - /** * {@inheritdoc} */ protected function setUp() { @@ -814,7 +804,7 @@ protected function enableModules(array $modules) { foreach ($modules as $module) { if ($module_handler->moduleExists($module)) { - throw new \LogicException("$module module is already enabled."); + continue; } $module_handler->addModule($module, $module_list[$module]->getPath()); // Maintain the list of enabled modules in configuration. diff --git a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php index a503019..f44c965 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php +++ b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php @@ -9,6 +9,7 @@ /** * @coversDefaultClass \Drupal\KernelTests\KernelTestBase + * * @group PHPUnit * @group Test * @group KernelTests @@ -223,6 +224,60 @@ public function testLocalTimeZone() { } /** + * Tests that a test method is skipped when it requires a module not present. + * + * In order to catch checkRequirements() regressions, we have to make a new + * test object and run checkRequirements() here. + * + * @covers ::checkRequirements + * @covers ::checkModuleRequirements + */ + public function testMethodRequiresModule() { + require __DIR__ . '/../../fixtures/KernelMissingDependentModuleMethodTest.php'; + + $stub_test = new KernelMissingDependentModuleMethodTest(); + // We have to setName() to the method name we're concerned with. + $stub_test->setName('testRequiresModule'); + + // We cannot use $this->setExpectedException() because PHPUnit would skip + // the test before comparing the exception type. + try { + $stub_test->publicCheckRequirements(); + $this->fail('Missing required module throws skipped test exception.'); + } + catch (\PHPUnit_Framework_SkippedTestError $e) { + $this->assertEqual('Required modules: module_does_not_exist', $e->getMessage()); + } + } + + /** + * Tests that a test case is skipped when it requires a module not present. + * + * In order to catch checkRequirements() regressions, we have to make a new + * test object and run checkRequirements() here. + * + * @covers ::checkRequirements + * @covers ::checkModuleRequirements + */ + public function testRequiresModule() { + require __DIR__ . '/../../fixtures/KernelMissingDependentModuleTest.php'; + + $stub_test = new KernelMissingDependentModuleTest(); + // We have to setName() to the method name we're concerned with. + $stub_test->setName('testRequiresModule'); + + // We cannot use $this->setExpectedException() because PHPUnit would skip + // the test before comparing the exception type. + try { + $stub_test->publicCheckRequirements(); + $this->fail('Missing required module throws skipped test exception.'); + } + catch (\PHPUnit_Framework_SkippedTestError $e) { + $this->assertEqual('Required modules: module_does_not_exist', $e->getMessage()); + } + } + + /** * {@inheritdoc} */ protected function tearDown() { diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php index 2324316..abdedd9 100644 --- a/core/tests/Drupal/Tests/BrowserTestBase.php +++ b/core/tests/Drupal/Tests/BrowserTestBase.php @@ -56,6 +56,7 @@ createContentType as drupalCreateContentType; } use ConfigTestTrait; + use TestRequirementsTrait; use UserCreationTrait { createRole as drupalCreateRole; createUser as drupalCreateUser; diff --git a/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php b/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php index fc3722f..5bd6220 100644 --- a/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php +++ b/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php @@ -128,6 +128,9 @@ public function testAccessForbiddenReason() { $reason = $this->getRandomGenerator()->string(); $b = AccessResult::forbidden($reason); $verify($b, $reason); + + $b = AccessResult::forbiddenIf(TRUE, $reason); + $verify($b, $reason); } /** diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php index 0a5d3d3..0d417ab 100644 --- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php @@ -10,6 +10,7 @@ use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Core\Config\Schema\SchemaIncompleteException; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\Language; use Drupal\Core\Plugin\DefaultLazyPluginCollection; use Drupal\Tests\Core\Config\Entity\Fixtures\ConfigEntityBaseWithPluginCollections; @@ -37,11 +38,11 @@ class ConfigEntityBaseUnitTest extends UnitTestCase { protected $entityType; /** - * The entity manager used for testing. + * The entity type manager used for testing. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The ID of the type of the entity under test. @@ -112,8 +113,8 @@ protected function setUp() { ->method('getConfigPrefix') ->willReturn('test_provider.' . $this->entityTypeId); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); - $this->entityManager->expects($this->any()) + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue($this->entityType)); @@ -131,7 +132,7 @@ protected function setUp() { $this->typedConfigManager = $this->getMock('Drupal\Core\Config\TypedConfigManagerInterface'); $container = new ContainerBuilder(); - $container->set('entity.manager', $this->entityManager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); $container->set('language_manager', $this->languageManager); $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator); @@ -468,7 +469,7 @@ public function testCreateDuplicate() { * @covers ::sort */ public function testSort() { - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue([ diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php index 01dafab..3d86ecd 100644 --- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php @@ -17,8 +17,8 @@ use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityMalformedException; -use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityStorageException; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\Query\QueryFactoryInterface; use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Extension\ModuleHandlerInterface; @@ -136,8 +136,8 @@ protected function setUp() { $this->entityStorage = new ConfigEntityStorage($entity_type, $this->configFactory->reveal(), $this->uuidService->reveal(), $this->languageManager->reveal()); $this->entityStorage->setModuleHandler($this->moduleHandler->reveal()); - $entity_manager = $this->prophesize(EntityManagerInterface::class); - $entity_manager->getDefinition('test_entity_type')->willReturn($entity_type); + $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class); + $entity_type_manager->getDefinition('test_entity_type')->willReturn($entity_type); $this->cacheTagsInvalidator = $this->prophesize(CacheTagsInvalidatorInterface::class); @@ -149,7 +149,7 @@ protected function setUp() { $this->configManager = $this->prophesize(ConfigManagerInterface::class); $container = new ContainerBuilder(); - $container->set('entity.manager', $entity_manager->reveal()); + $container->set('entity_type.manager', $entity_type_manager->reveal()); $container->set('entity.query.config', $entity_query_factory->reveal()); $container->set('config.typed', $typed_config_manager->reveal()); $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator->reveal()); diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/EntityDisplayModeBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/EntityDisplayModeBaseUnitTest.php index 728fdca..0f1ffc4 100644 --- a/core/tests/Drupal/Tests/Core/Config/Entity/EntityDisplayModeBaseUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Config/Entity/EntityDisplayModeBaseUnitTest.php @@ -3,6 +3,8 @@ namespace Drupal\Tests\Core\Config\Entity; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Tests\UnitTestCase; /** @@ -33,6 +35,13 @@ class EntityDisplayModeBaseUnitTest extends UnitTestCase { protected $entityManager; /** + * The entity type manager used for testing. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityTypeManager; + + /** * The ID of the type of the entity under test. * * @var string @@ -57,13 +66,21 @@ protected function setUp() { ->method('getProvider') ->will($this->returnValue('entity')); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + + $this->entityManager = new EntityManager(); $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); $container = new ContainerBuilder(); $container->set('entity.manager', $this->entityManager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); + + // Inject the container into entity.manager so it can defer to + // entity_type.manager. + $this->entityManager->setContainer($container); + \Drupal::setContainer($container); } @@ -79,11 +96,11 @@ public function testCalculateDependencies() { ->will($this->returnValue('test_module')); $values = ['targetEntityType' => $target_entity_type_id]; - $this->entityManager->expects($this->at(0)) + $this->entityTypeManager->expects($this->at(0)) ->method('getDefinition') ->with($target_entity_type_id) ->will($this->returnValue($target_entity_type)); - $this->entityManager->expects($this->at(1)) + $this->entityTypeManager->expects($this->at(1)) ->method('getDefinition') ->with($this->entityType) ->will($this->returnValue($this->entityInfo)); diff --git a/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php index 541ec88..0d56516 100644 --- a/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php @@ -4,7 +4,12 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\ContentEntityInterface; +use Drupal\Core\Entity\EntityFieldManagerInterface; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Entity\EntityTypeBundleInfoInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\TypedData\TypedDataManagerInterface; @@ -55,6 +60,27 @@ class ContentEntityBaseUnitTest extends UnitTestCase { protected $entityManager; /** + * The entity field manager used for testing. + * + * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityFieldManager; + + /** + * The entity type bundle manager used for testing. + * + * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityTypeBundleInfo; + + /** + * The entity type manager used for testing. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityTypeManager; + + /** * The type ID of the entity under test. * * @var string @@ -124,12 +150,18 @@ protected function setUp() { 'uuid' => 'uuid', ])); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); - $this->entityManager->expects($this->any()) + $this->entityManager = new EntityManager(); + + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue($this->entityType)); + $this->entityFieldManager = $this->getMock(EntityFieldManagerInterface::class); + + $this->entityTypeBundleInfo = $this->getMock(EntityTypeBundleInfoInterface::class); + $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); $this->typedDataManager = $this->getMock(TypedDataManagerInterface::class); @@ -168,10 +200,16 @@ protected function setUp() { $container = new ContainerBuilder(); $container->set('entity.manager', $this->entityManager); + $container->set('entity_field.manager', $this->entityFieldManager); + $container->set('entity_type.bundle.info', $this->entityTypeBundleInfo); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); $container->set('typed_data_manager', $this->typedDataManager); $container->set('language_manager', $this->languageManager); $container->set('plugin.manager.field.field_type', $this->fieldTypePluginManager); + // Inject the container into entity.manager so it can defer to + // entity_type.manager and other services. + $this->entityManager->setContainer($container); \Drupal::setContainer($container); $this->fieldDefinitions = [ @@ -179,14 +217,14 @@ protected function setUp() { 'revision_id' => BaseFieldDefinition::create('integer'), ]; - $this->entityManager->expects($this->any()) + $this->entityFieldManager->expects($this->any()) ->method('getFieldDefinitions') ->with($this->entityTypeId, $this->bundle) ->will($this->returnValue($this->fieldDefinitions)); - $this->entity = $this->getMockForAbstractClass('\Drupal\Core\Entity\ContentEntityBase', [$values, $this->entityTypeId, $this->bundle], '', TRUE, TRUE, TRUE, ['isNew']); + $this->entity = $this->getMockForAbstractClass(ContentEntityBase::class, [$values, $this->entityTypeId, $this->bundle], '', TRUE, TRUE, TRUE, ['isNew']); $values['defaultLangcode'] = [LanguageInterface::LANGCODE_DEFAULT => LanguageInterface::LANGCODE_NOT_SPECIFIED]; - $this->entityUnd = $this->getMockForAbstractClass('\Drupal\Core\Entity\ContentEntityBase', [$values, $this->entityTypeId, $this->bundle]); + $this->entityUnd = $this->getMockForAbstractClass(ContentEntityBase::class, [$values, $this->entityTypeId, $this->bundle]); } /** @@ -283,7 +321,7 @@ public function testGetRevisionId() { * @covers ::isTranslatable */ public function testIsTranslatable() { - $this->entityManager->expects($this->any()) + $this->entityTypeBundleInfo->expects($this->any()) ->method('getBundleInfo') ->with($this->entityTypeId) ->will($this->returnValue([ @@ -382,7 +420,7 @@ public function testRequiredValidation() { $entity->preSave($storage); }); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getStorage') ->with($this->entityTypeId) ->will($this->returnValue($storage)); @@ -434,7 +472,7 @@ public function testAccess() { $access->expects($this->at(3)) ->method('createAccess') ->will($this->returnValue(AccessResult::allowed())); - $this->entityManager->expects($this->exactly(4)) + $this->entityTypeManager->expects($this->exactly(4)) ->method('getAccessControlHandler') ->will($this->returnValue($access)); $this->assertTrue($this->entity->access($operation)); diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityLinkTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityLinkTest.php index 60bd425..38e9502 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityLinkTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityLinkTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\Core\Entity; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\Language; use Drupal\Core\Link; use Drupal\Tests\UnitTestCase; @@ -14,11 +15,11 @@ class EntityLinkTest extends UnitTestCase { /** - * The mocked entity manager. + * The mocked entity type manager. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The tested link generator. @@ -40,12 +41,12 @@ class EntityLinkTest extends UnitTestCase { protected function setUp() { parent::setUp(); - $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); $this->linkGenerator = $this->getMock('Drupal\Core\Utility\LinkGeneratorInterface'); $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $container = new ContainerBuilder(); - $container->set('entity.manager', $this->entityManager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('link_generator', $this->linkGenerator); $container->set('language_manager', $this->languageManager); \Drupal::setContainer($container); @@ -86,7 +87,7 @@ public function testLink($entity_label, $link_text, $expected_text, $link_rel = ['langcode', 'langcode'], ]); - $this->entityManager + $this->entityTypeManager ->expects($this->any()) ->method('getDefinition') ->with($entity_type_id) @@ -148,7 +149,7 @@ public function testToLink($entity_label, $link_text, $expected_text, $link_rel ['langcode', 'langcode'], ]); - $this->entityManager + $this->entityTypeManager ->expects($this->any()) ->method('getDefinition') ->with($entity_type_id) diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php index 44e771c..ad68e2d 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php @@ -11,6 +11,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityListBuilder; +use Drupal\Core\Routing\RedirectDestinationInterface; use Drupal\entity_test\EntityTestListBuilder; use Drupal\Tests\UnitTestCase; @@ -63,6 +64,13 @@ class EntityListBuilderTest extends UnitTestCase { protected $role; /** + * The redirect destination service. + * + * @var \Drupal\Core\Routing\RedirectDestinationInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $redirectDestination; + + /** * The EntityListBuilder object to test. * * @var \Drupal\Core\Entity\EntityListBuilder @@ -80,7 +88,8 @@ protected function setUp() { $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface'); $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface'); $this->translationManager = $this->getMock('\Drupal\Core\StringTranslation\TranslationInterface'); - $this->entityListBuilder = new TestEntityListBuilder($this->entityType, $this->roleStorage, $this->moduleHandler); + $this->entityListBuilder = new TestEntityListBuilder($this->entityType, $this->roleStorage); + $this->redirectDestination = $this->getMock(RedirectDestinationInterface::class); $this->container = new ContainerBuilder(); \Drupal::setContainer($this->container); } @@ -117,12 +126,20 @@ public function testGetOperations() { $url->expects($this->any()) ->method('toArray') ->will($this->returnValue([])); + $url->expects($this->atLeastOnce()) + ->method('mergeOptions') + ->with(['query' => ['destination' => '/foo/bar']]); $this->role->expects($this->any()) - ->method('urlInfo') + ->method('toUrl') ->will($this->returnValue($url)); - $list = new EntityListBuilder($this->entityType, $this->roleStorage, $this->moduleHandler); + $this->redirectDestination->expects($this->atLeastOnce()) + ->method('getAsArray') + ->willReturn(['destination' => '/foo/bar']); + + $list = new EntityListBuilder($this->entityType, $this->roleStorage); $list->setStringTranslation($this->translationManager); + $list->setRedirectDestination($this->redirectDestination); $operations = $list->getOperations($this->role); $this->assertInternalType('array', $operations); diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeBundleInfoTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeBundleInfoTest.php index 1cc5076..2596471 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeBundleInfoTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeBundleInfoTest.php @@ -100,7 +100,7 @@ protected function setUp() { $container = $this->prophesize(ContainerInterface::class); $container->get('cache_tags.invalidator')->willReturn($this->cacheTagsInvalidator->reveal()); - //$container->get('typed_data_manager')->willReturn($this->typedDataManager->reveal()); + // $container->get('typed_data_manager')->willReturn($this->typedDataManager->reveal()); \Drupal::setContainer($container->reveal()); $this->entityTypeBundleInfo = new EntityTypeBundleInfo($this->entityTypeManager->reveal(), $this->languageManager->reveal(), $this->moduleHandler->reveal(), $this->typedDataManager->reveal(), $this->cacheBackend->reveal()); diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php index ad9e49c..1acb20c 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php @@ -5,7 +5,11 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\Cache\Cache; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Entity\EntityTypeRepositoryInterface; use Drupal\Core\Language\Language; +use Drupal\entity_test\Entity\EntityTestMul; use Drupal\Tests\UnitTestCase; /** @@ -30,11 +34,11 @@ class EntityUnitTest extends UnitTestCase { protected $entityType; /** - * The entity manager used for testing. + * The entity type manager used for testing. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The ID of the type of the entity under test. @@ -94,8 +98,8 @@ protected function setUp() { ->method('getListCacheTags') ->willReturn([$this->entityTypeId . '_list']); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); - $this->entityManager->expects($this->any()) + $this->entityTypeManager = $this->getMockForAbstractClass(EntityTypeManagerInterface::class); + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue($this->entityType)); @@ -111,7 +115,9 @@ protected function setUp() { $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidator'); $container = new ContainerBuilder(); - $container->set('entity.manager', $this->entityManager); + // Ensure that Entity doesn't use the deprecated entity.manager service. + $container->set('entity.manager', NULL); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('uuid', $this->uuid); $container->set('language_manager', $this->languageManager); $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator); @@ -186,7 +192,7 @@ public function testLabel() { // Set a dummy property on the entity under test to test that the label can // be returned form a property if there is no callback. - $this->entityManager->expects($this->at(1)) + $this->entityTypeManager->expects($this->at(1)) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue([ @@ -213,9 +219,10 @@ public function testAccess() { $access->expects($this->at(1)) ->method('createAccess') ->will($this->returnValue(AccessResult::allowed())); - $this->entityManager->expects($this->exactly(2)) + $this->entityTypeManager->expects($this->exactly(2)) ->method('getAccessControlHandler') ->will($this->returnValue($access)); + $this->assertEquals(AccessResult::allowed(), $this->entity->access($operation)); $this->assertEquals(AccessResult::allowed(), $this->entity->access('create')); } @@ -239,11 +246,11 @@ public function setupTestLoad() { // Base our mocked entity on a real entity class so we can test if calling // Entity::load() on the base class will bubble up to an actual entity. $this->entityTypeId = 'entity_test_mul'; - $methods = get_class_methods('Drupal\entity_test\Entity\EntityTestMul'); + $methods = get_class_methods(EntityTestMul::class); unset($methods[array_search('load', $methods)]); unset($methods[array_search('loadMultiple', $methods)]); unset($methods[array_search('create', $methods)]); - $this->entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTestMul') + $this->entity = $this->getMockBuilder(EntityTestMul::class) ->disableOriginalConstructor() ->setMethods($methods) ->getMock(); @@ -260,21 +267,25 @@ public function testLoad() { $class_name = get_class($this->entity); - $this->entityManager->expects($this->once()) + $entity_type_repository = $this->getMockForAbstractClass(EntityTypeRepositoryInterface::class); + $entity_type_repository->expects($this->once()) ->method('getEntityTypeFromClass') ->with($class_name) ->willReturn($this->entityTypeId); - $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface'); + $storage = $this->getMock(EntityStorageInterface::class); $storage->expects($this->once()) ->method('load') ->with(1) ->will($this->returnValue($this->entity)); - $this->entityManager->expects($this->once()) + + $this->entityTypeManager->expects($this->once()) ->method('getStorage') ->with($this->entityTypeId) ->will($this->returnValue($storage)); + \Drupal::getContainer()->set('entity_type.repository', $entity_type_repository); + // Call Entity::load statically and check that it returns the mock entity. $this->assertSame($this->entity, $class_name::load(1)); } @@ -290,21 +301,25 @@ public function testLoadMultiple() { $class_name = get_class($this->entity); - $this->entityManager->expects($this->once()) + $entity_type_repository = $this->getMockForAbstractClass(EntityTypeRepositoryInterface::class); + $entity_type_repository->expects($this->once()) ->method('getEntityTypeFromClass') ->with($class_name) ->willReturn($this->entityTypeId); - $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface'); + $storage = $this->getMock(EntityStorageInterface::class); $storage->expects($this->once()) ->method('loadMultiple') ->with([1]) ->will($this->returnValue([1 => $this->entity])); - $this->entityManager->expects($this->once()) + + $this->entityTypeManager->expects($this->once()) ->method('getStorage') ->with($this->entityTypeId) ->will($this->returnValue($storage)); + \Drupal::getContainer()->set('entity_type.repository', $entity_type_repository); + // Call Entity::loadMultiple statically and check that it returns the mock // entity. $this->assertSame([1 => $this->entity], $class_name::loadMultiple([1])); @@ -317,21 +332,26 @@ public function testCreate() { $this->setupTestLoad(); $class_name = get_class($this->entity); - $this->entityManager->expects($this->once()) + + $entity_type_repository = $this->getMockForAbstractClass(EntityTypeRepositoryInterface::class); + $entity_type_repository->expects($this->once()) ->method('getEntityTypeFromClass') ->with($class_name) ->willReturn($this->entityTypeId); - $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface'); + $storage = $this->getMock(EntityStorageInterface::class); $storage->expects($this->once()) ->method('create') ->with([]) ->will($this->returnValue($this->entity)); - $this->entityManager->expects($this->once()) + + $this->entityTypeManager->expects($this->once()) ->method('getStorage') ->with($this->entityTypeId) ->will($this->returnValue($storage)); + \Drupal::getContainer()->set('entity_type.repository', $entity_type_repository); + // Call Entity::create() statically and check that it returns the mock // entity. $this->assertSame($this->entity, $class_name::create([])); @@ -345,10 +365,12 @@ public function testSave() { $storage->expects($this->once()) ->method('save') ->with($this->entity); - $this->entityManager->expects($this->once()) + + $this->entityTypeManager->expects($this->once()) ->method('getStorage') ->with($this->entityTypeId) ->will($this->returnValue($storage)); + $this->entity->save(); } @@ -361,10 +383,12 @@ public function testDelete() { // Testing the argument of the delete() method consumes too much memory. $storage->expects($this->once()) ->method('delete'); - $this->entityManager->expects($this->once()) + + $this->entityTypeManager->expects($this->once()) ->method('getStorage') ->with($this->entityTypeId) ->will($this->returnValue($storage)); + $this->entity->delete(); } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php index ebde776..1e9ee7c 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php @@ -4,7 +4,7 @@ use Drupal\Core\Entity\Entity; use Drupal\Core\Entity\EntityMalformedException; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException; use Drupal\Core\Entity\RevisionableInterface; @@ -21,11 +21,11 @@ class EntityUrlTest extends UnitTestCase { /** - * The entity manager mock used in this test. + * The entity type bundle info service mock used in this test. * - * @var \Prophecy\Prophecy\ProphecyInterface|\Drupal\Core\Entity\EntityManagerInterface + * @var \Prophecy\Prophecy\ProphecyInterface|\Drupal\Core\Entity\EntityTypeBundleInfoInterface */ - protected $entityManager; + protected $entityTypeBundleInfo; /** * The ID of the entity type used in this test. @@ -511,7 +511,7 @@ public function providerTestUrl() { * @return \Drupal\Core\Entity\Entity|\PHPUnit_Framework_MockObject_MockObject */ protected function getEntity($class, array $values, array $methods = []) { - $methods = array_merge($methods, ['getEntityType', 'entityManager']); + $methods = array_merge($methods, ['getEntityType', 'entityManager', 'entityTypeBundleInfo']); // Prophecy does not allow prophesizing abstract classes while actually // calling their code. We use Prophecy below because that allows us to @@ -526,8 +526,8 @@ protected function getEntity($class, array $values, array $methods = []) { $this->entityType->getKey('langcode')->willReturn(FALSE); $entity->method('getEntityType')->willReturn($this->entityType->reveal()); - $this->entityManager = $this->prophesize(EntityManagerInterface::class); - $entity->method('entityManager')->willReturn($this->entityManager->reveal()); + $this->entityTypeBundleInfo = $this->prophesize(EntityTypeBundleInfoInterface::class); + $entity->method('entityTypeBundleInfo')->willReturn($this->entityTypeBundleInfo->reveal()); return $entity; } @@ -581,7 +581,7 @@ protected function registerLinkTemplate($link_template) { * The bundle information to register. */ protected function registerBundleInfo($bundle_info) { - $this->entityManager + $this->entityTypeBundleInfo ->getBundleInfo($this->entityTypeId) ->willReturn([$this->entityTypeId => $bundle_info]) ; diff --git a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php index 6f20444..4a2dac5 100644 --- a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php @@ -4,9 +4,12 @@ use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityMalformedException; +use Drupal\Core\Entity\EntityManager; use Drupal\Core\Entity\EntityStorageException; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\Language; use Drupal\Tests\UnitTestCase; use Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage; @@ -58,13 +61,27 @@ class KeyValueEntityStorageTest extends UnitTestCase { protected $entityStorage; /** - * The mocked entity manager. + * The entity manager. * * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $entityManager; /** + * The mocked entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityTypeManager; + + /** + * The mocked entity field manager. + * + * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityFieldManager; + + /** * The mocked cache tags invalidator. * * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject @@ -102,12 +119,16 @@ protected function setUpKeyValueEntityStorage($uuid_key = 'uuid') { ->method('getListCacheTags') ->willReturn(['test_entity_type_list']); - $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); - $this->entityManager->expects($this->any()) + $this->entityManager = new EntityManager(); + + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with('test_entity_type') ->will($this->returnValue($this->entityType)); + $this->entityFieldManager = $this->getMock(EntityFieldManagerInterface::class); + $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface'); $this->keyValueStore = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface'); @@ -127,8 +148,13 @@ protected function setUpKeyValueEntityStorage($uuid_key = 'uuid') { $container = new ContainerBuilder(); $container->set('entity.manager', $this->entityManager); + $container->set('entity_field.manager', $this->entityFieldManager); + $container->set('entity_type.manager', $this->entityTypeManager); $container->set('language_manager', $this->languageManager); $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator); + // Inject the container into entity.manager so it can defer to + // entity_type.manager and other services. + $this->entityManager->setContainer($container); \Drupal::setContainer($container); } diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php index aecc3e4..db92ca9 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php @@ -1151,7 +1151,7 @@ public function providerTestRequiresEntityDataMigration() { // Case 6: same storage class, ::hasData() === TRUE, no structure changes. [$updated_entity_type_definition, $original_entity_type_definition, TRUE, FALSE, FALSE], // Case 7: different storage class, original storage class exists, - //::hasData() === TRUE, no structure changes. + // ::hasData() === TRUE, no structure changes. [$updated_entity_type_definition, $original_entity_type_definition_other_existing, TRUE, FALSE, FALSE], ]; } diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php index 98f5718..bf3fd46 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php @@ -8,8 +8,11 @@ namespace Drupal\Tests\Core\Entity\Sql; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityManager; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\Query\QueryFactoryInterface; use Drupal\Core\Entity\Sql\SqlContentEntityStorage; use Drupal\Core\Language\Language; @@ -51,6 +54,20 @@ class SqlContentEntityStorageTest extends UnitTestCase { protected $entityManager; /** + * The mocked entity type manager used in this test. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityTypeManager; + + /** + * The mocked entity field manager used in this test. + * + * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityFieldManager; + + /** * The entity type ID. * * @var string @@ -104,7 +121,12 @@ protected function setUp() { $this->container = new ContainerBuilder(); \Drupal::setContainer($this->container); - $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); + $this->entityManager = new EntityManager(); + // Inject the container into entity.manager so it can defer to + // entity_type.manager and other services. + $this->entityManager->setContainer($this->container); + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + $this->entityFieldManager = $this->getMock(EntityFieldManagerInterface::class); $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); @@ -114,6 +136,10 @@ protected function setUp() { $this->connection = $this->getMockBuilder('Drupal\Core\Database\Connection') ->disableOriginalConstructor() ->getMock(); + + $this->container->set('entity.manager', $this->entityManager); + $this->container->set('entity_type.manager', $this->entityTypeManager); + $this->container->set('entity_field.manager', $this->entityFieldManager); } /** @@ -986,7 +1012,6 @@ public function testCreate() { ->will($this->returnValue($language)); $this->container->set('language_manager', $language_manager); - $this->container->set('entity.manager', $this->entityManager); $this->container->set('module_handler', $this->moduleHandler); $entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase') @@ -1006,14 +1031,14 @@ public function testCreate() { // ContentEntityStorageBase iterates over the entity which calls this method // internally in ContentEntityBase::getProperties(). - $this->entityManager->expects($this->once()) + $this->entityFieldManager->expects($this->once()) ->method('getFieldDefinitions') ->will($this->returnValue([])); $this->entityType->expects($this->atLeastOnce()) ->method('isRevisionable') ->will($this->returnValue(FALSE)); - $this->entityManager->expects($this->atLeastOnce()) + $this->entityTypeManager->expects($this->atLeastOnce()) ->method('getDefinition') ->with($this->entityType->id()) ->will($this->returnValue($this->entityType)); @@ -1077,15 +1102,15 @@ protected function setUpEntityStorage() { ->disableOriginalConstructor() ->getMock(); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->will($this->returnValue($this->entityType)); - $this->entityManager->expects($this->any()) + $this->entityFieldManager->expects($this->any()) ->method('getFieldStorageDefinitions') ->will($this->returnValue($this->fieldDefinitions)); - $this->entityManager->expects($this->any()) + $this->entityFieldManager->expects($this->any()) ->method('getBaseFieldDefinitions') ->will($this->returnValue($this->fieldDefinitions)); @@ -1259,15 +1284,15 @@ public function testHasData() { ->disableOriginalConstructor() ->getMock(); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->will($this->returnValue($this->entityType)); - $this->entityManager->expects($this->any()) + $this->entityFieldManager->expects($this->any()) ->method('getFieldStorageDefinitions') ->will($this->returnValue($this->fieldDefinitions)); - $this->entityManager->expects($this->any()) + $this->entityFieldManager->expects($this->any()) ->method('getBaseFieldDefinitions') ->will($this->returnValue($this->fieldDefinitions)); diff --git a/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php index 2140112..d932993c 100644 --- a/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php @@ -3,6 +3,9 @@ namespace Drupal\Tests\Core\Entity\TypedData; use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityFieldManagerInterface; +use Drupal\Core\Entity\EntityManager; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\Plugin\DataType\EntityAdapter; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Language\LanguageInterface; @@ -54,6 +57,19 @@ class EntityAdapterUnitTest extends UnitTestCase { protected $entityManager; /** + * The entity type manager used for testing. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityTypeManager; + + /** + * + * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityFieldManager; + + /** * The type ID of the entity under test. * * @var string @@ -130,8 +146,10 @@ protected function setUp() { 'uuid' => 'uuid', ])); - $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); - $this->entityManager->expects($this->any()) + $this->entityManager = new EntityManager(); + + $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with($this->entityTypeId) ->will($this->returnValue($this->entityType)); @@ -183,23 +201,30 @@ protected function setUp() { ->method('createFieldItemList') ->willReturn($this->fieldItemList); + $this->entityFieldManager = $this->getMockForAbstractClass(EntityFieldManagerInterface::class); + $container = new ContainerBuilder(); $container->set('entity.manager', $this->entityManager); + $container->set('entity_type.manager', $this->entityTypeManager); + $container->set('entity_field.manager', $this->entityFieldManager); $container->set('uuid', $this->uuid); $container->set('typed_data_manager', $this->typedDataManager); $container->set('language_manager', $this->languageManager); $container->set('plugin.manager.field.field_type', $this->fieldTypePluginManager); + // Inject the container into entity.manager so it can defer to + // entity_type.manager and other services. + $this->entityManager->setContainer($container); \Drupal::setContainer($container); $this->fieldDefinitions = [ 'id' => BaseFieldDefinition::create('integer'), 'revision_id' => BaseFieldDefinition::create('integer'), ]; - - $this->entityManager->expects($this->any()) + $this->entityFieldManager->expects($this->any()) ->method('getFieldDefinitions') ->with($this->entityTypeId, $this->bundle) ->will($this->returnValue($this->fieldDefinitions)); + $this->entity = $this->getMockForAbstractClass('\Drupal\Core\Entity\ContentEntityBase', [$values, $this->entityTypeId, $this->bundle]); $this->entityAdapter = EntityAdapter::createFromEntity($this->entity); diff --git a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php index d2b283a..2512602 100644 --- a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php @@ -113,14 +113,14 @@ public function providerTestLog() { $request_mock->headers = $this->getMock('Symfony\Component\HttpFoundation\ParameterBag'); // No request or account. - $cases [] = [ + $cases[] = [ function ($context) { return $context['channel'] == 'test' && empty($context['uid']) && empty($context['ip']); }, ]; // With account but not request. Since the request is not available the // current user should not be used. - $cases [] = [ + $cases[] = [ function ($context) { return $context['uid'] === 0 && empty($context['ip']); }, @@ -128,14 +128,14 @@ function ($context) { $account_mock, ]; // With request but not account. - $cases [] = [ + $cases[] = [ function ($context) { return $context['ip'] === '127.0.0.1' && empty($context['uid']); }, $request_mock, ]; // Both request and account. - $cases [] = [ + $cases[] = [ function ($context) { return $context['ip'] === '127.0.0.1' && $context['uid'] === 1; }, diff --git a/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php index 2cd750b..53a9241 100644 --- a/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php +++ b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php @@ -50,7 +50,7 @@ public function providerTestFormatPlural() { /** * @dataProvider providerTestFormatPlural */ - public function testFormatPlural($count, $singular, $plural, array $args = [], array $options = [], $expected) { + public function testFormatPlural($count, $singular, $plural, array $args, array $options, $expected) { $langcode = empty($options['langcode']) ? 'fr' : $options['langcode']; $translator = $this->getMock('\Drupal\Core\StringTranslation\Translator\TranslatorInterface'); $translator->expects($this->once()) @@ -78,7 +78,7 @@ public function testFormatPlural($count, $singular, $plural, array $args = [], a * * @dataProvider providerTestTranslatePlaceholder */ - public function testTranslatePlaceholder($string, array $args = [], $expected_string) { + public function testTranslatePlaceholder($string, array $args, $expected_string) { $actual = $this->translationManager->translate($string, $args); $this->assertInstanceOf(MarkupInterface::class, $actual); $this->assertEquals($expected_string, (string) $actual); diff --git a/core/tests/Drupal/Tests/TestRequirementsTrait.php b/core/tests/Drupal/Tests/TestRequirementsTrait.php new file mode 100644 index 0000000..84937ad --- /dev/null +++ b/core/tests/Drupal/Tests/TestRequirementsTrait.php @@ -0,0 +1,91 @@ +getAnnotations(); + if (!empty($annotations['class']['requires'])) { + $this->checkModuleRequirements($root, $annotations['class']['requires']); + } + if (!empty($annotations['method']['requires'])) { + $this->checkModuleRequirements($root, $annotations['method']['requires']); + } + } + + /** + * Checks missing module requirements. + * + * Iterates through a list of requires annotations and looks for missing + * modules. The test will be skipped if any of the required modules is + * missing. + * + * @param string $root + * The path to the root of the Drupal installation to scan. + * @param string[] $annotations + * A list of requires annotations from either a method or class annotation. + * + * @throws \PHPUnit_Framework_SkippedTestError + * Thrown when the requirements are not met, and this test should be + * skipped. Callers should not catch this exception. + */ + private function checkModuleRequirements($root, array $annotations) { + // drupal_valid_ua() might not be loaded. + require_once $root . '/core/includes/bootstrap.inc'; + + // Make a list of required modules. + $required_modules = []; + foreach ($annotations as $requirement) { + if (strpos($requirement, 'module ') === 0) { + $required_modules[] = trim(str_replace('module ', '', $requirement)); + } + } + + // If there are required modules, check if they're available. + if (!empty($required_modules)) { + // Scan for modules. + $discovery = new ExtensionDiscovery($root, FALSE); + $discovery->setProfileDirectories([]); + $list = array_keys($discovery->scan('module')); + $not_available = array_diff($required_modules, $list); + if (!empty($not_available)) { + throw new \PHPUnit_Framework_SkippedTestError('Required modules: ' . implode(', ', $not_available)); + } + } + } + +} diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php index 8aaeb85..5139f97 100644 --- a/core/tests/Drupal/Tests/UnitTestCase.php +++ b/core/tests/Drupal/Tests/UnitTestCase.php @@ -180,6 +180,12 @@ public function getConfigStorageStub(array $configs) { * * @return \Drupal\block\BlockInterface|\PHPUnit_Framework_MockObject_MockObject * The mocked block. + * + * @deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Unit test + * base classes should not have dependencies on extensions. Set up mocks in + * individual tests. + * + * @see https://www.drupal.org/node/2896072 */ protected function getBlockMockWithMachineName($machine_name) { $plugin = $this->getMockBuilder('Drupal\Core\Block\BlockBase') @@ -195,6 +201,7 @@ protected function getBlockMockWithMachineName($machine_name) { $block->expects($this->any()) ->method('getPlugin') ->will($this->returnValue($plugin)); + @trigger_error(__METHOD__ . ' is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Unit test base classes should not have dependencies on extensions. Set up mocks in individual tests.', E_USER_DEPRECATED); return $block; } diff --git a/core/tests/Drupal/Tests/UnitTestCaseDeprecationTest.php b/core/tests/Drupal/Tests/UnitTestCaseDeprecationTest.php new file mode 100644 index 0000000..8ecb70c --- /dev/null +++ b/core/tests/Drupal/Tests/UnitTestCaseDeprecationTest.php @@ -0,0 +1,22 @@ +getBlockMockWithMachineName('test_name'); + $this->assertEquals('test_name', $block_mock->getPlugin()->getMachineNameSuggestion()); + } + +} diff --git a/core/tests/fixtures/BrowserMissingDependentModuleMethodTest.php b/core/tests/fixtures/BrowserMissingDependentModuleMethodTest.php new file mode 100644 index 0000000..4a94a53 --- /dev/null +++ b/core/tests/fixtures/BrowserMissingDependentModuleMethodTest.php @@ -0,0 +1,35 @@ +fail('Running test with missing required module.'); + } + + /** + * Public access for checkRequirements() to avoid reflection. + */ + public function publicCheckRequirements() { + return parent::checkRequirements(); + } + +} diff --git a/core/tests/fixtures/BrowserMissingDependentModuleTest.php b/core/tests/fixtures/BrowserMissingDependentModuleTest.php new file mode 100644 index 0000000..9650fc1 --- /dev/null +++ b/core/tests/fixtures/BrowserMissingDependentModuleTest.php @@ -0,0 +1,37 @@ +fail('Running test with missing required module.'); + } + + /** + * Public access for checkRequirements() to avoid reflection. + */ + public function publicCheckRequirements() { + return parent::checkRequirements(); + } + +} diff --git a/core/tests/fixtures/KernelMissingDependentModuleMethodTest.php b/core/tests/fixtures/KernelMissingDependentModuleMethodTest.php new file mode 100644 index 0000000..33dc793 --- /dev/null +++ b/core/tests/fixtures/KernelMissingDependentModuleMethodTest.php @@ -0,0 +1,33 @@ +fail('Running test with missing required module.'); + } + + /** + * Public access for checkRequirements() to avoid reflection. + */ + public function publicCheckRequirements() { + return parent::checkRequirements(); + } + +} diff --git a/core/tests/fixtures/KernelMissingDependentModuleTest.php b/core/tests/fixtures/KernelMissingDependentModuleTest.php new file mode 100644 index 0000000..3b8f316 --- /dev/null +++ b/core/tests/fixtures/KernelMissingDependentModuleTest.php @@ -0,0 +1,35 @@ +fail('Running test with missing required module.'); + } + + /** + * Public access for checkRequirements() to avoid reflection. + */ + public function publicCheckRequirements() { + return parent::checkRequirements(); + } + +} diff --git a/core/themes/bartik/css/components/form.css b/core/themes/bartik/css/components/form.css index 672e591..8ec05a3 100644 --- a/core/themes/bartik/css/components/form.css +++ b/core/themes/bartik/css/components/form.css @@ -172,7 +172,7 @@ input.form-submit:focus { .node-form .form-wrapper { margin-bottom: 2em; } -.node-form .node-form-footer, +.node-form .entity-content-form-footer, .node-form .field--name-status { margin-bottom: 0; } diff --git a/core/themes/seven/css/components/colors.css b/core/themes/seven/css/components/colors.css deleted file mode 100644 index 54358d8..0000000 --- a/core/themes/seven/css/components/colors.css +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Reusable colors. - */ -.color-success { - color: #325e1c; - background-color: #f3faef; -} -.color-warning { - color: #734c00; - background-color: #fdf8ed; -} -.color-error { - color: #a51b00; - background-color: #fcf4f2; -} - diff --git a/core/themes/seven/css/theme/colors.css b/core/themes/seven/css/theme/colors.css new file mode 100644 index 0000000..54358d8 --- /dev/null +++ b/core/themes/seven/css/theme/colors.css @@ -0,0 +1,16 @@ +/** + * Reusable colors. + */ +.color-success { + color: #325e1c; + background-color: #f3faef; +} +.color-warning { + color: #734c00; + background-color: #fdf8ed; +} +.color-error { + color: #a51b00; + background-color: #fcf4f2; +} + diff --git a/core/themes/seven/seven.libraries.yml b/core/themes/seven/seven.libraries.yml index 21ac5bc..75e8c1a 100644 --- a/core/themes/seven/seven.libraries.yml +++ b/core/themes/seven/seven.libraries.yml @@ -10,7 +10,6 @@ global-styling: css/components/content-header.css: {} css/components/breadcrumb.css: {} css/components/buttons.css: {} - css/components/colors.css: {} css/components/messages.css: {} css/components/dropbutton.component.css: {} css/components/entity-meta.css: {} @@ -33,6 +32,8 @@ global-styling: css/components/system-status-counter.css: {} css/components/tabs.css: {} css/components/views-ui.css: {} + theme: + css/theme/colors.css: {} layout: css/layout/layout.css: {} dependencies: diff --git a/core/themes/stable/templates/admin/admin-block.html.twig b/core/themes/stable/templates/admin/admin-block.html.twig index 9464d40..7401695 100644 --- a/core/themes/stable/templates/admin/admin-block.html.twig +++ b/core/themes/stable/templates/admin/admin-block.html.twig @@ -10,9 +10,15 @@ * - content: (optional) The content of the block. * - description: (optional) A description of the block. * (Description should only be output if content is not available). + * - attributes: HTML attributes for the containing div element. */ #} -
+{% + set classes = [ + 'panel', + ] +%} + {% if block.title %}

{{ block.title }}

{% endif %}