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

This commit is contained in:
Pantheon Automation 2016-07-07 09:44:38 -07:00 committed by Greg Anderson
parent 13b6ca7cc2
commit 38ba7c357d
342 changed files with 7814 additions and 1534 deletions

12
composer.lock generated
View file

@ -891,16 +891,16 @@
}, },
{ {
"name": "masterminds/html5", "name": "masterminds/html5",
"version": "2.1.2", "version": "2.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Masterminds/html5-php.git", "url": "https://github.com/Masterminds/html5-php.git",
"reference": "8f782e0f01a6e33a319bdc8f6de9cfd6569979a4" "reference": "170aa5cb35b29fccafbf5ea63487c013f396fdc9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/8f782e0f01a6e33a319bdc8f6de9cfd6569979a4", "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/170aa5cb35b29fccafbf5ea63487c013f396fdc9",
"reference": "8f782e0f01a6e33a319bdc8f6de9cfd6569979a4", "reference": "170aa5cb35b29fccafbf5ea63487c013f396fdc9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -910,7 +910,7 @@
"require-dev": { "require-dev": {
"phpunit/phpunit": "4.*", "phpunit/phpunit": "4.*",
"sami/sami": "~2.0", "sami/sami": "~2.0",
"satooshi/php-coveralls": "0.6.*" "satooshi/php-coveralls": "1.0.*"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -952,7 +952,7 @@
"serializer", "serializer",
"xml" "xml"
], ],
"time": "2015-06-07 08:43:18" "time": "2016-05-10 14:11:45"
}, },
{ {
"name": "paragonie/random_compat", "name": "paragonie/random_compat",

View file

@ -27,7 +27,7 @@ Drupal requires:
- Percona Server 5.5.8 (or greater) (http://www.percona.com/). Percona - Percona Server 5.5.8 (or greater) (http://www.percona.com/). Percona
Server is a backwards-compatible replacement for MySQL. Server is a backwards-compatible replacement for MySQL.
- PostgreSQL 9.1.2 (or greater) (http://www.postgresql.org/). - PostgreSQL 9.1.2 (or greater) (http://www.postgresql.org/).
- SQLite 3.6.8 (or greater) (http://www.sqlite.org/). - SQLite 3.7.11 (or greater) (http://www.sqlite.org/).
For more detailed information about Drupal requirements, including a list of For more detailed information about Drupal requirements, including a list of
PHP extensions and configurations that are required, see "System requirements" PHP extensions and configurations that are required, see "System requirements"

View file

@ -28,8 +28,12 @@ Provisional membership:
- Scott Reeves 'Cottser' https://www.drupal.org/u/cottser - Scott Reeves 'Cottser' https://www.drupal.org/u/cottser
Drupal 7 Drupal 7
- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
(Framework Manager)
- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein - David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
(Release Manager, Framework Manager, Product Manager) (Release Manager, Framework Manager, Product Manager)
- Stefan Ruijsenaars 'stefan.r' https://www.drupal.org/u/stefanr-0
(Release Manager, Product Manager)
Provisional membership: None at this time. Provisional membership: None at this time.
@ -200,7 +204,6 @@ Transliteration system
- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu - Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
- Damien Tournoud 'damien-tournoud' https://www.drupal.org/u/damien-tournoud - Damien Tournoud 'damien-tournoud' https://www.drupal.org/u/damien-tournoud
- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun - Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
- Jennifer Hodgdon 'jhodgdon' https://www.drupal.org/u/jhodgdon
Typed data system Typed data system
- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago - Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
@ -213,7 +216,7 @@ Accessibility
- Jesse Renée Beach 'jessebeach' https://www.drupal.org/u/jessebeach - Jesse Renée Beach 'jessebeach' https://www.drupal.org/u/jessebeach
Documentation Documentation
- Jennifer Hodgdon 'jhodgdon' https://www.drupal.org/u/jhodgdon - ?
Performance Performance
- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch - Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
@ -413,7 +416,6 @@ Responsive Image module
- Jelle Sebreghts 'Jelle_S' https://www.drupal.org/u/jelle_s - Jelle Sebreghts 'Jelle_S' https://www.drupal.org/u/jelle_s
Search module Search module
- Jennifer Hodgdon 'jhodgdon' https://www.drupal.org/u/jhodgdon
- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin - Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
Serialization module Serialization module

View file

@ -64,8 +64,10 @@
"drupal/content_translation": "self.version", "drupal/content_translation": "self.version",
"drupal/contextual": "self.version", "drupal/contextual": "self.version",
"drupal/core-annotation": "self.version", "drupal/core-annotation": "self.version",
"drupal/core-assertion": "self.version",
"drupal/core-bridge": "self.version", "drupal/core-bridge": "self.version",
"drupal/core-datetime": "self.version", "drupal/core-datetime": "self.version",
"drupal/core-dependency-injection": "self.version",
"drupal/core-diff": "self.version", "drupal/core-diff": "self.version",
"drupal/core-discovery": "self.version", "drupal/core-discovery": "self.version",
"drupal/core-event-dispatcher": "self.version", "drupal/core-event-dispatcher": "self.version",

View file

@ -32,12 +32,13 @@ function entity_render_cache_clear() {
* The bundle info for a specific entity type, or all entity types. * The bundle info for a specific entity type, or all entity types.
* *
* @deprecated in Drupal 8.x-dev and will be removed before Drupal 9.0.0. Use * @deprecated in Drupal 8.x-dev and will be removed before Drupal 9.0.0. Use
* \Drupal\Core\Entity\EntityManagerInterface::getBundleInfo() for a single * \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getBundleInfo() for a
* bundle, or \Drupal\Core\Entity\EntityManagerInterface::getAllBundleInfo() * single bundle, or
* for all bundles. * \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getAllBundleInfo() for
* all bundles.
* *
* @see \Drupal\Core\Entity\EntityManagerInterface::getBundleInfo() * @see \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getBundleInfo()
* @see \Drupal\Core\Entity\EntityManagerInterface::getAllBundleInfo() * @see \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getAllBundleInfo()
*/ */
function entity_get_bundles($entity_type = NULL) { function entity_get_bundles($entity_type = NULL) {
if (isset($entity_type)) { if (isset($entity_type)) {
@ -67,11 +68,11 @@ function entity_get_bundles($entity_type = NULL) {
* entity type is variable, use the entity manager service to load the entity * entity type is variable, use the entity manager service to load the entity
* from the entity storage: * from the entity storage:
* @code * @code
* \Drupal::entityManager()->getStorage($entity_type)->load($id) * \Drupal::entityTypeManager()->getStorage($entity_type)->load($id);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityInterface::load() * @see \Drupal\Core\Entity\EntityInterface::load()
* @see \Drupal\Core\Entity\EntityManagerInterface::getStorage() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
* @see \Drupal\Core\Entity\EntityStorageInterface::load() * @see \Drupal\Core\Entity\EntityStorageInterface::load()
* @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage * @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage
* @see \Drupal\Core\Entity\Query\QueryInterface * @see \Drupal\Core\Entity\Query\QueryInterface
@ -100,10 +101,12 @@ function entity_load($entity_type, $id, $reset = FALSE) {
* the entity storage's loadRevision() method to load a specific entity * the entity storage's loadRevision() method to load a specific entity
* revision: * revision:
* @code * @code
* \Drupal::entityManager()->getStorage($entity_type)->loadRevision($revision_id); * \Drupal::entityTypeManager()
* ->getStorage($entity_type)
* ->loadRevision($revision_id);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityManagerInterface::getStorage() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
* @see \Drupal\Core\Entity\EntityStorageInterface::loadRevision() * @see \Drupal\Core\Entity\EntityStorageInterface::loadRevision()
* @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage * @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage
*/ */
@ -125,10 +128,12 @@ function entity_revision_load($entity_type, $revision_id) {
* the entity storage's deleteRevision() method to delete a specific entity * the entity storage's deleteRevision() method to delete a specific entity
* revision: * revision:
* @code * @code
* \Drupal::entityManager()->getStorage($entity_type)>deleteRevision($revision_id); * \Drupal::entityTypeManager()
* ->getStorage($entity_type)
* ->deleteRevision($revision_id);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityManagerInterface::getStorage() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
* @see \Drupal\Core\Entity\EntityStorageInterface::deleteRevision() * @see \Drupal\Core\Entity\EntityStorageInterface::deleteRevision()
*/ */
function entity_revision_delete($entity_type, $revision_id) { function entity_revision_delete($entity_type, $revision_id) {
@ -171,11 +176,11 @@ function entity_revision_delete($entity_type, $revision_id) {
* the entity type is variable, use the entity manager service to load the * the entity type is variable, use the entity manager service to load the
* entity from the entity storage: * entity from the entity storage:
* @code * @code
* \Drupal::entityManager()->getStorage($entity_type)->loadMultiple($id) * \Drupal::entityTypeManager()->getStorage($entity_type)->loadMultiple($id);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityInterface::loadMultiple() * @see \Drupal\Core\Entity\EntityInterface::loadMultiple()
* @see \Drupal\Core\Entity\EntityManagerInterface::getStorage() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
* @see \Drupal\Core\Entity\EntityStorageInterface::loadMultiple() * @see \Drupal\Core\Entity\EntityStorageInterface::loadMultiple()
* @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage * @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage
* @see \Drupal\Core\Entity\Query\QueryInterface * @see \Drupal\Core\Entity\Query\QueryInterface
@ -205,10 +210,12 @@ function entity_load_multiple($entity_type, array $ids = NULL, $reset = FALSE) {
* the entity storage's loadByProperties() method to load an entity by their * the entity storage's loadByProperties() method to load an entity by their
* property values: * property values:
* @code * @code
* \Drupal::entityManager()->getStorage($entity_type)->loadByProperties($values); * \Drupal::entityTypeManager()
* ->getStorage($entity_type)
* ->loadByProperties($values);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityManagerInterface::getStorage() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
* @see \Drupal\Core\Entity\EntityStorageInterface::loadByProperties() * @see \Drupal\Core\Entity\EntityStorageInterface::loadByProperties()
*/ */
function entity_load_multiple_by_properties($entity_type, array $values) { function entity_load_multiple_by_properties($entity_type, array $values) {
@ -236,10 +243,10 @@ function entity_load_multiple_by_properties($entity_type, array $values) {
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use * @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
* the entity storage's loadUnchanged() method to load an unchanged entity: * the entity storage's loadUnchanged() method to load an unchanged entity:
* @code * @code
* \Drupal::entityManager()->getStorage($entity_type)->loadUnchanged($id). * \Drupal::entityTypeManager()->getStorage($entity_type)->loadUnchanged($id);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityManagerInterface::getStorage() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
* @see \Drupal\Core\Entity\EntityStorageInterface::loadUnchanged() * @see \Drupal\Core\Entity\EntityStorageInterface::loadUnchanged()
*/ */
function entity_load_unchanged($entity_type, $id) { function entity_load_unchanged($entity_type, $id) {
@ -259,12 +266,12 @@ function entity_load_unchanged($entity_type, $id) {
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use * @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
* the entity storage's delete() method to delete multiple entities: * the entity storage's delete() method to delete multiple entities:
* @code * @code
* $storage_handler = \Drupal::entityManager()->getStorage($entity_type); * $storage_handler = \Drupal::entityTypeManager()->getStorage($entity_type);
* $entities = $storage_handler->loadMultiple($ids); * $entities = $storage_handler->loadMultiple($ids);
* $storage_handler->delete($entities); * $storage_handler->delete($entities);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityManagerInterface::getStorage() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
* @see \Drupal\Core\Entity\EntityStorageInterface::loadMultiple() * @see \Drupal\Core\Entity\EntityStorageInterface::loadMultiple()
* @see \Drupal\Core\Entity\EntityStorageInterface::delete() * @see \Drupal\Core\Entity\EntityStorageInterface::delete()
*/ */
@ -292,10 +299,10 @@ function entity_delete_multiple($entity_type, array $ids) {
* entity type is variable, use the entity storage's create() method to * entity type is variable, use the entity storage's create() method to
* construct a new entity: * construct a new entity:
* @code * @code
* \Drupal::entityManager()->getStorage($entity_type)->create($values) * \Drupal::entityTypeManager()->getStorage($entity_type)->create($values);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityManagerInterface::getStorage() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
* @see \Drupal\Core\Entity\EntityStorageInterface::create() * @see \Drupal\Core\Entity\EntityStorageInterface::create()
*/ */
function entity_create($entity_type, array $values = array()) { function entity_create($entity_type, array $values = array()) {
@ -349,11 +356,12 @@ function entity_page_label(EntityInterface $entity, $langcode = NULL) {
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. * @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0.
* Use the entity view builder's view() method for creating a render array: * Use the entity view builder's view() method for creating a render array:
* @code * @code
* $view_builder = \Drupal::entityManager()->getViewBuilder($entity->getEntityTypeId()); * $view_builder = \Drupal::entityTypeManager()
* ->getViewBuilder($entity->getEntityTypeId());
* return $view_builder->view($entity, $view_mode, $langcode); * return $view_builder->view($entity, $view_mode, $langcode);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityManagerInterface::getViewBuilder() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getViewBuilder()
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::view() * @see \Drupal\Core\Entity\EntityViewBuilderInterface::view()
*/ */
function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL, $reset = FALSE) { function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL, $reset = FALSE) {
@ -386,11 +394,12 @@ function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL, $res
* Use the entity view builder's viewMultiple() method for creating a render * Use the entity view builder's viewMultiple() method for creating a render
* array for the provided entities: * array for the provided entities:
* @code * @code
* $view_builder = \Drupal::entityManager()->getViewBuilder($entity->getEntityTypeId()); * $view_builder = \Drupal::entityTypeManager()
* ->getViewBuilder($entity->getEntityTypeId());
* return $view_builder->viewMultiple($entities, $view_mode, $langcode); * return $view_builder->viewMultiple($entities, $view_mode, $langcode);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityManagerInterface::getViewBuilder() * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getViewBuilder()
* @see \Drupal\Core\Entity\EntityViewBuilderInterface::viewMultiple() * @see \Drupal\Core\Entity\EntityViewBuilderInterface::viewMultiple()
*/ */
function entity_view_multiple(array $entities, $view_mode, $langcode = NULL, $reset = FALSE) { function entity_view_multiple(array $entities, $view_mode, $langcode = NULL, $reset = FALSE) {
@ -443,7 +452,9 @@ function entity_view_multiple(array $entities, $view_mode, $langcode = NULL, $re
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. * @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0.
* If the display is available in configuration use: * If the display is available in configuration use:
* @code * @code
* \Drupal::entityManager()->getStorage('entity_view_display')->load($entity_type . '.' . $bundle . '.' . $view_mode); * \Drupal::entityTypeManager()
* ->getStorage('entity_view_display')
* ->load($entity_type . '.' . $bundle . '.' . $view_mode);
* @endcode * @endcode
* When the display is not available in configuration, you can create a new * When the display is not available in configuration, you can create a new
* EntityViewDisplay object using: * EntityViewDisplay object using:
@ -454,7 +465,9 @@ function entity_view_multiple(array $entities, $view_mode, $langcode = NULL, $re
* 'mode' => $view_mode, * 'mode' => $view_mode,
* 'status' => TRUE, * 'status' => TRUE,
* )); * ));
* \Drupal::entityManager()->getStorage('entity_view_display')->create($values); * \Drupal::entityTypeManager()
* ->getStorage('entity_view_display')
* ->create($values);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityStorageInterface::create() * @see \Drupal\Core\Entity\EntityStorageInterface::create()
@ -519,10 +532,12 @@ function entity_get_display($entity_type, $bundle, $view_mode) {
* @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. * @deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0.
* If the entity form display is available in configuration use: * If the entity form display is available in configuration use:
* @code * @code
* \Drupal::entityManager()->getStorage('entity_form_display')->load($entity_type . '.' . $bundle . '.' . $form_mode); * \Drupal::entityTypeManager()
* ->getStorage('entity_form_display')
* ->load($entity_type . '.' . $bundle . '.' . $form_mode);
* @endcode * @endcode
* When the entity form display is not available in configuration, you can create a new * When the entity form display is not available in configuration, you can
* EntityFormDisplay object using: * create a new EntityFormDisplay object using:
* @code * @code
* $values = ('entity_form_display', array( * $values = ('entity_form_display', array(
* 'targetEntityType' => $entity_type, * 'targetEntityType' => $entity_type,
@ -530,7 +545,9 @@ function entity_get_display($entity_type, $bundle, $view_mode) {
* 'mode' => $form_mode, * 'mode' => $form_mode,
* 'status' => TRUE, * 'status' => TRUE,
* )); * ));
* \Drupal::entityManager()->getStorage('entity_form_display')->create($values); * \Drupal::entityTypeManager()
* ->getStorage('entity_form_display')
* ->create($values);
* @endcode * @endcode
* *
* @see \Drupal\Core\Entity\EntityStorageInterface::create() * @see \Drupal\Core\Entity\EntityStorageInterface::create()

View file

@ -397,7 +397,7 @@ function file_valid_uri($uri) {
} }
/** /**
* Copies a file to a new location without invoking the file API. * Copies a file to a new location without database changes or hook invocation.
* *
* This is a powerful function that in many ways performs like an advanced * This is a powerful function that in many ways performs like an advanced
* version of copy(). * version of copy().
@ -407,10 +407,9 @@ function file_valid_uri($uri) {
* - If the $source and $destination are equal, the behavior depends on the * - If the $source and $destination are equal, the behavior depends on the
* $replace parameter. FILE_EXISTS_REPLACE will error out. FILE_EXISTS_RENAME * $replace parameter. FILE_EXISTS_REPLACE will error out. FILE_EXISTS_RENAME
* will rename the file until the $destination is unique. * will rename the file until the $destination is unique.
* - Provides a fallback using realpaths if the move fails using stream * - Works around a PHP bug where copy() does not properly support streams if
* wrappers. This can occur because PHP's copy() function does not properly * safe_mode or open_basedir are enabled.
* support streams if open_basedir is enabled. See * @see https://bugs.php.net/bug.php?id=60456
* https://bugs.php.net/bug.php?id=60456
* *
* @param $source * @param $source
* A string specifying the filepath or URI of the source file. * A string specifying the filepath or URI of the source file.
@ -431,18 +430,66 @@ function file_valid_uri($uri) {
* @see file_copy() * @see file_copy()
*/ */
function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
if (!file_unmanaged_prepare($source, $destination, $replace)) {
return FALSE;
}
// Attempt to resolve the URIs. This is necessary in certain configurations
// (see above).
$real_source = drupal_realpath($source) ?: $source;
$real_destination = drupal_realpath($destination) ?: $destination;
// Perform the copy operation.
if (!@copy($real_source, $real_destination)) {
\Drupal::logger('file')->error('The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => $destination));
return FALSE;
}
// Set the permissions on the new file.
drupal_chmod($destination);
return $destination;
}
/**
* Internal function that prepares the destination for a file_unmanaged_copy or
* file_unmanaged_move operation.
*
* - Checks if $source and $destination are valid and readable/writable.
* - Checks that $source is not equal to $destination; if they are an error
* is reported.
* - If file already exists in $destination either the call will error out,
* replace the file or rename the file based on the $replace parameter.
*
* @param $source
* A string specifying the filepath or URI of the source file.
* @param $destination
* A URI containing the destination that $source should be moved/copied to.
* The URI may be a bare filepath (without a scheme) and in that case the
* default scheme (file://) will be used. If this value is omitted, Drupal's
* default files scheme will be used, usually "public://".
* @param $replace
* Replace behavior when the destination file already exists:
* - FILE_EXISTS_REPLACE - Replace the existing file.
* - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
* unique.
* - FILE_EXISTS_ERROR - Do nothing and return FALSE.
*
* @return
* TRUE, or FALSE in the event of an error.
*
* @see file_unmanaged_copy()
* @see file_unmanaged_move()
*/
function file_unmanaged_prepare($source, &$destination = NULL, $replace = FILE_EXISTS_RENAME) {
$original_source = $source; $original_source = $source;
$logger = \Drupal::logger('file'); $logger = \Drupal::logger('file');
// Assert that the source file actually exists. // Assert that the source file actually exists.
if (!file_exists($source)) { if (!file_exists($source)) {
// @todo Replace drupal_set_message() calls with exceptions instead. // @todo Replace drupal_set_message() calls with exceptions instead.
drupal_set_message(t('The specified file %file could not be copied because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $original_source)), 'error'); drupal_set_message(t('The specified file %file could not be moved/copied because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $original_source)), 'error');
if (($realpath = drupal_realpath($original_source)) !== FALSE) { if (($realpath = drupal_realpath($original_source)) !== FALSE) {
$logger->notice('File %file (%realpath) could not be copied because it does not exist.', array('%file' => $original_source, '%realpath' => $realpath)); $logger->notice('File %file (%realpath) could not be moved/copied because it does not exist.', array('%file' => $original_source, '%realpath' => $realpath));
} }
else { else {
$logger->notice('File %file could not be copied because it does not exist.', array('%file' => $original_source)); $logger->notice('File %file could not be moved/copied because it does not exist.', array('%file' => $original_source));
} }
return FALSE; return FALSE;
} }
@ -463,8 +510,8 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
$dirname = drupal_dirname($destination); $dirname = drupal_dirname($destination);
if (!file_prepare_directory($dirname)) { if (!file_prepare_directory($dirname)) {
// The destination is not valid. // The destination is not valid.
$logger->notice('File %file could not be copied because the destination directory %destination is not configured correctly.', array('%file' => $original_source, '%destination' => $dirname)); $logger->notice('File %file could not be moved/copied because the destination directory %destination is not configured correctly.', array('%file' => $original_source, '%destination' => $dirname));
drupal_set_message(t('The specified file %file could not be copied because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', array('%file' => $original_source)), 'error'); drupal_set_message(t('The specified file %file could not be moved/copied because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', array('%file' => $original_source)), 'error');
return FALSE; return FALSE;
} }
} }
@ -472,8 +519,8 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
// Determine whether we can perform this operation based on overwrite rules. // Determine whether we can perform this operation based on overwrite rules.
$destination = file_destination($destination, $replace); $destination = file_destination($destination, $replace);
if ($destination === FALSE) { if ($destination === FALSE) {
drupal_set_message(t('The file %file could not be copied because a file by that name already exists in the destination directory.', array('%file' => $original_source)), 'error'); drupal_set_message(t('The file %file could not be moved/copied because a file by that name already exists in the destination directory.', array('%file' => $original_source)), 'error');
$logger->notice('File %file could not be copied because a file by that name already exists in the destination directory (%destination)', array('%file' => $original_source, '%destination' => $destination)); $logger->notice('File %file could not be moved/copied because a file by that name already exists in the destination directory (%destination)', array('%file' => $original_source, '%destination' => $destination));
return FALSE; return FALSE;
} }
@ -481,26 +528,13 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
$real_source = drupal_realpath($source); $real_source = drupal_realpath($source);
$real_destination = drupal_realpath($destination); $real_destination = drupal_realpath($destination);
if ($source == $destination || ($real_source !== FALSE) && ($real_source == $real_destination)) { if ($source == $destination || ($real_source !== FALSE) && ($real_source == $real_destination)) {
drupal_set_message(t('The specified file %file was not copied because it would overwrite itself.', array('%file' => $source)), 'error'); drupal_set_message(t('The specified file %file was not moved/copied because it would overwrite itself.', array('%file' => $source)), 'error');
$logger->notice('File %file could not be copied because it would overwrite itself.', array('%file' => $source)); $logger->notice('File %file could not be moved/copied because it would overwrite itself.', array('%file' => $source));
return FALSE; return FALSE;
} }
// Make sure the .htaccess files are present. // Make sure the .htaccess files are present.
file_ensure_htaccess(); file_ensure_htaccess();
// Perform the copy operation. return TRUE;
if (!@copy($source, $destination)) {
// If the copy failed and realpaths exist, retry the operation using them
// instead.
if ($real_source === FALSE || $real_destination === FALSE || !@copy($real_source, $real_destination)) {
$logger->error('The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => $destination));
return FALSE;
}
}
// Set the permissions on the new file.
drupal_chmod($destination);
return $destination;
} }
/** /**
@ -551,12 +585,24 @@ function file_destination($destination, $replace) {
/** /**
* Moves a file to a new location without database changes or hook invocation. * Moves a file to a new location without database changes or hook invocation.
* *
* This is a powerful function that in many ways performs like an advanced
* version of rename().
* - Checks if $source and $destination are valid and readable/writable.
* - Checks that $source is not equal to $destination; if they are an error
* is reported.
* - If file already exists in $destination either the call will error out,
* replace the file or rename the file based on the $replace parameter.
* - Works around a PHP bug where rename() does not properly support streams if
* safe_mode or open_basedir are enabled.
* @see https://bugs.php.net/bug.php?id=60456
*
* @param $source * @param $source
* A string specifying the filepath or URI of the original file. * A string specifying the filepath or URI of the source file.
* @param $destination * @param $destination
* A string containing the destination that $source should be moved to. * A URI containing the destination that $source should be moved to. The
* This must be a stream wrapper URI. If this value is omitted, Drupal's * URI may be a bare filepath (without a scheme) and in that case the default
* default files scheme will be used, usually "public://". * scheme (file://) will be used. If this value is omitted, Drupal's default
* files scheme will be used, usually "public://".
* @param $replace * @param $replace
* Replace behavior when the destination file already exists: * Replace behavior when the destination file already exists:
* - FILE_EXISTS_REPLACE - Replace the existing file. * - FILE_EXISTS_REPLACE - Replace the existing file.
@ -565,16 +611,37 @@ function file_destination($destination, $replace) {
* - FILE_EXISTS_ERROR - Do nothing and return FALSE. * - FILE_EXISTS_ERROR - Do nothing and return FALSE.
* *
* @return * @return
* The URI of the moved file, or FALSE in the event of an error. * The path to the new file, or FALSE in the event of an error.
* *
* @see file_move() * @see file_move()
*/ */
function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) { function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
$filepath = file_unmanaged_copy($source, $destination, $replace); if (!file_unmanaged_prepare($source, $destination, $replace)) {
if ($filepath == FALSE || file_unmanaged_delete($source) == FALSE) {
return FALSE; return FALSE;
} }
return $filepath; // Ensure compatibility with Windows.
// @see drupal_unlink()
if ((substr(PHP_OS, 0, 3) == 'WIN') && (!file_stream_wrapper_valid_scheme(file_uri_scheme($source)))) {
chmod($source, 0600);
}
// Attempt to resolve the URIs. This is necessary in certain configurations
// (see above) and can also permit fast moves across local schemes.
$real_source = drupal_realpath($source) ?: $source;
$real_destination = drupal_realpath($destination) ?: $destination;
// Perform the move operation.
if (!@rename($real_source, $real_destination)) {
// Fall back to slow copy and unlink procedure. This is necessary for
// renames across schemes that are not local, or where rename() has not been
// implemented. It's not necessary to use drupal_unlink() as the Windows
// issue has already been resolved above.
if (!@copy($real_source, $real_destination) || !@unlink($real_source)) {
\Drupal::logger('file')->error('The specified file %file could not be moved to %destination.', array('%file' => $source, '%destination' => $destination));
return FALSE;
}
}
// Set the permissions on the new file.
drupal_chmod($destination);
return $destination;
} }
/** /**

View file

@ -1084,7 +1084,7 @@ function install_verify_completed_task() {
* The site path. * The site path.
* *
* @return bool * @return bool
* TRUE if there are no database errors. * TRUE if there are no database errors.
*/ */
function install_verify_database_settings($site_path) { function install_verify_database_settings($site_path) {
if ($database = Database::getConnectionInfo()) { if ($database = Database::getConnectionInfo()) {

View file

@ -115,8 +115,8 @@ function drupal_install_profile_distribution_name() {
* Loads the installation profile, extracting its defined version. * Loads the installation profile, extracting its defined version.
* *
* @return string Distribution version defined in the profile's .info.yml file. * @return string Distribution version defined in the profile's .info.yml file.
* Defaults to \Drupal::VERSION if no version is explicitly provided * Defaults to \Drupal::VERSION if no version is explicitly provided
* by the installation profile. * by the installation profile.
* *
* @see install_profile_info() * @see install_profile_info()
*/ */
@ -139,7 +139,7 @@ function drupal_install_profile_distribution_version() {
* Detects all supported databases that are compiled into PHP. * Detects all supported databases that are compiled into PHP.
* *
* @return * @return
* An array of database types compiled into PHP. * An array of database types compiled into PHP.
*/ */
function drupal_detect_database_types() { function drupal_detect_database_types() {
$databases = drupal_get_database_types(); $databases = drupal_get_database_types();
@ -724,7 +724,7 @@ function drupal_verify_install_file($file, $mask = NULL, $type = 'file') {
* (optional) Whether to output messages. Defaults to TRUE. * (optional) Whether to output messages. Defaults to TRUE.
* *
* @return * @return
* TRUE/FALSE whether or not the directory was successfully created. * TRUE/FALSE whether or not the directory was successfully created.
*/ */
function drupal_install_mkdir($file, $mask, $message = TRUE) { function drupal_install_mkdir($file, $mask, $message = TRUE) {
$mod = 0; $mod = 0;
@ -773,7 +773,7 @@ function drupal_install_mkdir($file, $mask, $message = TRUE) {
* (optional) Whether to output messages. Defaults to TRUE. * (optional) Whether to output messages. Defaults to TRUE.
* *
* @return * @return
* TRUE/FALSE whether or not we were able to fix the file's permissions. * TRUE/FALSE whether or not we were able to fix the file's permissions.
*/ */
function drupal_install_fix_file($file, $mask, $message = TRUE) { function drupal_install_fix_file($file, $mask, $message = TRUE) {
// If $file does not exist, fileperms() issues a PHP warning. // If $file does not exist, fileperms() issues a PHP warning.

View file

@ -65,7 +65,7 @@ function update_check_incompatibility($name, $type = 'module') {
* Returns whether the minimum schema requirement has been satisfied. * Returns whether the minimum schema requirement has been satisfied.
* *
* @return array * @return array
* A requirements info array. * A requirements info array.
*/ */
function update_system_schema_requirements() { function update_system_schema_requirements() {
$requirements = array(); $requirements = array();

View file

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

View file

@ -49,7 +49,7 @@ class Plugin implements AnnotationInterface {
* The annotation array. * The annotation array.
* *
* @return array * @return array
* The parsed annotation as a definition. * The parsed annotation as a definition.
*/ */
protected function parse(array $values) { protected function parse(array $values) {
$definitions = array(); $definitions = array();

View file

@ -455,7 +455,7 @@ class DateTimePlus {
/** /**
* Detects if there were errors in the processing of this date. * Detects if there were errors in the processing of this date.
* *
* @return boolean * @return bool
* TRUE if there were errors in the processing of this date, FALSE * TRUE if there were errors in the processing of this date, FALSE
* otherwise. * otherwise.
*/ */

View file

@ -373,7 +373,7 @@ class Container implements IntrospectableContainerInterface, ResettableContainer
* {@inheritdoc} * {@inheritdoc}
*/ */
public function has($id) { public function has($id) {
return isset($this->services[$id]) || isset($this->serviceDefinitions[$id]); return isset($this->aliases[$id]) || isset($this->services[$id]) || isset($this->serviceDefinitions[$id]);
} }
/** /**

View file

@ -60,6 +60,7 @@ class OptimizedPhpArrayDumper extends Dumper {
*/ */
public function getArray() { public function getArray() {
$definition = array(); $definition = array();
$this->aliases = $this->getAliases();
$definition['aliases'] = $this->getAliases(); $definition['aliases'] = $this->getAliases();
$definition['parameters'] = $this->getParameters(); $definition['parameters'] = $this->getParameters();
$definition['services'] = $this->getServiceDefinitions(); $definition['services'] = $this->getServiceDefinitions();
@ -454,6 +455,9 @@ class OptimizedPhpArrayDumper extends Dumper {
} }
// Private shared service. // Private shared service.
if (isset($this->aliases[$id])) {
$id = $this->aliases[$id];
}
$definition = $this->container->getDefinition($id); $definition = $this->container->getDefinition($id);
if (!$definition->isPublic()) { if (!$definition->isPublic()) {
// The ContainerBuilder does not share a private service, but this means a // The ContainerBuilder does not share a private service, but this means a

View file

@ -12,8 +12,8 @@
}, },
"require": { "require": {
"php": ">=5.5.9", "php": ">=5.5.9",
"symfony/dependency-injection": "2.7.*", "symfony/dependency-injection": "~2.8",
"symfony/expression-language": "2.7.*" "symfony/expression-language": "~2.7"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View file

@ -6,8 +6,8 @@
"license": "GPL-2.0+", "license": "GPL-2.0+",
"require": { "require": {
"php": ">=5.5.9", "php": ">=5.5.9",
"symfony/dependency-injection": "2.7.*", "symfony/dependency-injection": "~2.8",
"symfony/event-dispatcher": "2.7.*" "symfony/event-dispatcher": "~2.7"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View file

@ -6,7 +6,7 @@
"license": "GPL-2.0+", "license": "GPL-2.0+",
"require": { "require": {
"php": ">=5.5.9", "php": ">=5.5.9",
"symfony/http-foundation": "2.7.*" "symfony/http-foundation": "~2.7"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View file

@ -30,8 +30,8 @@ trait DiscoveryTrait {
* @param string $plugin_id * @param string $plugin_id
* A plugin id. * A plugin id.
* @param bool $exception_on_invalid * @param bool $exception_on_invalid
* (optional) If TRUE, an invalid plugin ID will throw an exception. * If TRUE, an invalid plugin ID will cause an exception to be thrown; if
* Defaults to FALSE. * FALSE, NULL will be returned.
* *
* @return array|null * @return array|null
* A plugin definition, or NULL if the plugin ID is invalid and * A plugin definition, or NULL if the plugin ID is invalid and

View file

@ -6,7 +6,7 @@
"license": "GPL-2.0+", "license": "GPL-2.0+",
"require": { "require": {
"php": ">=5.5.9", "php": ">=5.5.9",
"symfony/validator": "2.7.*" "symfony/validator": "~2.7"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View file

@ -7,7 +7,7 @@ namespace Drupal\Component\Render;
* *
* Output strategies assist in transforming HTML strings into strings that are * Output strategies assist in transforming HTML strings into strings that are
* appropriate for a given context (e.g. plain-text), through performing the * appropriate for a given context (e.g. plain-text), through performing the
* relevant formatting. No santization is applied. * relevant formatting. No sanitization is applied.
*/ */
interface OutputStrategyInterface { interface OutputStrategyInterface {

View file

@ -6,7 +6,7 @@
"license": "GPL-2.0+", "license": "GPL-2.0+",
"require": { "require": {
"php": ">=5.5.9", "php": ">=5.5.9",
"symfony/yaml": "2.7.*" "symfony/yaml": "~2.7"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View file

@ -149,7 +149,7 @@ class Cache {
* Gets all cache bin services. * Gets all cache bin services.
* *
* @return array * @return array
* An array of cache backend objects keyed by cache bins. * An array of cache backend objects keyed by cache bins.
*/ */
public static function getBins() { public static function getBins() {
$bins = array(); $bins = array();

View file

@ -37,7 +37,7 @@ interface CacheableDependencyInterface {
* When this object is modified, these cache tags will be invalidated. * When this object is modified, these cache tags will be invalidated.
* *
* @return string[] * @return string[]
* A set of cache tags. * A set of cache tags.
*/ */
public function getCacheTags(); public function getCacheTags();

View file

@ -87,8 +87,16 @@ class ChainedFastBackend implements CacheBackendInterface, CacheTagsInvalidatorI
* The fast cache backend. * The fast cache backend.
* @param string $bin * @param string $bin
* The cache bin for which the object is created. * The cache bin for which the object is created.
*
* @throws \Exception
* When the consistent cache backend and the fast cache backend are the same
* service.
*/ */
public function __construct(CacheBackendInterface $consistent_backend, CacheBackendInterface $fast_backend, $bin) { public function __construct(CacheBackendInterface $consistent_backend, CacheBackendInterface $fast_backend, $bin) {
if ($consistent_backend == $fast_backend) {
// @todo: should throw a proper exception. See https://www.drupal.org/node/2751847.
trigger_error('Consistent cache backend and fast cache backend cannot use the same service.', E_USER_ERROR);
}
$this->consistentBackend = $consistent_backend; $this->consistentBackend = $consistent_backend;
$this->fastBackend = $fast_backend; $this->fastBackend = $fast_backend;
$this->bin = 'cache_' . $bin; $this->bin = 'cache_' . $bin;

View file

@ -25,20 +25,22 @@ class QueryArgsCacheContext extends RequestStackCacheContextBase implements Calc
*/ */
public function getContext($query_arg = NULL) { public function getContext($query_arg = NULL) {
if ($query_arg === NULL) { if ($query_arg === NULL) {
return $this->requestStack->getCurrentRequest()->getQueryString(); // All arguments requested. Use normalized query string to minimize
// variations.
$value = $this->requestStack->getCurrentRequest()->getQueryString();
return ($value !== NULL) ? $value : '';
} }
elseif ($this->requestStack->getCurrentRequest()->query->has($query_arg)) { elseif ($this->requestStack->getCurrentRequest()->query->has($query_arg)) {
$value = $this->requestStack->getCurrentRequest()->query->get($query_arg); $value = $this->requestStack->getCurrentRequest()->query->get($query_arg);
if ($value !== '') { if (is_array($value)) {
return http_build_query($value);
}
elseif ($value !== '') {
return $value; return $value;
} }
else { return '?valueless?';
return '?valueless?';
}
}
else {
return NULL;
} }
return '';
} }
/** /**

View file

@ -307,8 +307,9 @@ class ConfigManager implements ConfigManagerInterface {
// Try to fix any dependencies and find out what will happen to the // Try to fix any dependencies and find out what will happen to the
// dependency graph. Entities are processed in the order of most dependent // dependency graph. Entities are processed in the order of most dependent
// first. For example, this ensures that fields are removed before // first. For example, this ensures that Menu UI third party dependencies on
// field storages. // node types are fixed before processing the node type's other
// dependencies.
while ($dependent = array_pop($dependents)) { while ($dependent = array_pop($dependents)) {
/** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $dependent */ /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $dependent */
if ($dry_run) { if ($dry_run) {
@ -346,7 +347,9 @@ class ConfigManager implements ConfigManagerInterface {
// If the entity cannot be fixed then it has to be deleted. // If the entity cannot be fixed then it has to be deleted.
if (!$fixed) { if (!$fixed) {
$delete_uuids[] = $dependent->uuid(); $delete_uuids[] = $dependent->uuid();
$return['delete'][] = $dependent; // Deletes should occur in the order of the least dependent first. For
// example, this ensures that fields are removed before field storages.
array_unshift($return['delete'], $dependent);
} }
} }
// Use the lists of UUIDs to filter the original list to work out which // Use the lists of UUIDs to filter the original list to work out which

View file

@ -173,7 +173,9 @@ class ConfigDependencyManager {
// dependent is at the top. For example, this ensures that fields are // dependent is at the top. For example, this ensures that fields are
// always after field storages. This is because field storages need to be // always after field storages. This is because field storages need to be
// created before a field. // created before a field.
return array_reverse(array_intersect_key($this->graph, $dependencies)); $graph = $this->getGraph();
uasort($graph, array($this, 'sortGraph'));
return array_replace(array_intersect_key($graph, $dependencies), $dependencies);
} }
/** /**
@ -185,10 +187,32 @@ class ConfigDependencyManager {
*/ */
public function sortAll() { public function sortAll() {
$graph = $this->getGraph(); $graph = $this->getGraph();
// Sort by reverse weight and alphabetically. The most dependent entities // Sort by weight and alphabetically. The most dependent entities
// are last and entities with the same weight are alphabetically ordered. // are last and entities with the same weight are alphabetically ordered.
uasort($graph, array($this, 'sortGraph')); uasort($graph, array($this, 'sortGraphByWeight'));
return array_keys($graph); // Use array_intersect_key() to exclude modules and themes from the list.
return array_keys(array_intersect_key($graph, $this->data));
}
/**
* Sorts the dependency graph by weight and alphabetically.
*
* @param array $a
* First item for comparison. The compared items should be associative
* arrays that include a 'weight' and a 'name' key.
* @param array $b
* Second item for comparison.
*
* @return int
* The comparison result for uasort().
*/
protected function sortGraphByWeight(array $a, array $b) {
$weight_cmp = SortArray::sortByKeyInt($a, $b, 'weight');
if ($weight_cmp === 0) {
return SortArray::sortByKeyString($a, $b, 'name');
}
return $weight_cmp;
} }
/** /**
@ -196,7 +220,7 @@ class ConfigDependencyManager {
* *
* @param array $a * @param array $a
* First item for comparison. The compared items should be associative * First item for comparison. The compared items should be associative
* arrays that include a 'weight' and a 'component' key. * arrays that include a 'weight' and a 'name' key.
* @param array $b * @param array $b
* Second item for comparison. * Second item for comparison.
* *
@ -207,7 +231,7 @@ class ConfigDependencyManager {
$weight_cmp = SortArray::sortByKeyInt($a, $b, 'weight') * -1; $weight_cmp = SortArray::sortByKeyInt($a, $b, 'weight') * -1;
if ($weight_cmp === 0) { if ($weight_cmp === 0) {
return SortArray::sortByKeyString($a, $b, 'component'); return SortArray::sortByKeyString($a, $b, 'name');
} }
return $weight_cmp; return $weight_cmp;
} }
@ -228,9 +252,11 @@ class ConfigDependencyManager {
$graph = $this->getGraph(); $graph = $this->getGraph();
foreach ($entities_to_check as $entity) { foreach ($entities_to_check as $entity) {
if (isset($graph[$entity]) && !empty($graph[$entity]['reverse_paths'])) { if (isset($graph[$entity]) && !empty($graph[$entity]['paths'])) {
foreach ($graph[$entity]['reverse_paths'] as $dependency => $value) { foreach ($graph[$entity]['paths'] as $dependency => $value) {
$dependent_entities[$dependency] = $this->data[$dependency]; if (isset($this->data[$dependency])) {
$dependent_entities[$dependency] = $this->data[$dependency];
}
} }
} }
} }
@ -248,14 +274,21 @@ class ConfigDependencyManager {
$graph = array(); $graph = array();
foreach ($this->data as $entity) { foreach ($this->data as $entity) {
$graph_key = $entity->getConfigDependencyName(); $graph_key = $entity->getConfigDependencyName();
$graph[$graph_key]['edges'] = array(); if (!isset($graph[$graph_key])) {
$dependencies = $entity->getDependencies('config'); $graph[$graph_key] = [
if (!empty($dependencies)) { 'edges' => [],
foreach ($dependencies as $dependency) { 'name' => $graph_key,
$graph[$graph_key]['edges'][$dependency] = TRUE; ];
} }
// Include all dependencies in the graph so that topographical sorting
// works.
foreach (array_merge($entity->getDependencies('config'), $entity->getDependencies('module'), $entity->getDependencies('theme')) as $dependency) {
$graph[$dependency]['edges'][$graph_key] = TRUE;
$graph[$dependency]['name'] = $dependency;
} }
} }
// Ensure that order of the graph is consistent.
krsort($graph);
$graph_object = new Graph($graph); $graph_object = new Graph($graph);
$this->graph = $graph_object->searchAndSort(); $this->graph = $graph_object->searchAndSort();
} }

View file

@ -93,28 +93,28 @@ interface StorageInterface {
/** /**
* Encodes configuration data into the storage-specific format. * Encodes configuration data into the storage-specific format.
* *
* This is a publicly accessible static method to allow for alternative
* usages in data conversion scripts and also tests.
*
* @param array $data * @param array $data
* The configuration data to encode. * The configuration data to encode.
* *
* @return string * @return string
* The encoded configuration data. * The encoded configuration data.
*
* This is a publicly accessible static method to allow for alternative
* usages in data conversion scripts and also tests.
*/ */
public function encode($data); public function encode($data);
/** /**
* Decodes configuration data from the storage-specific format. * Decodes configuration data from the storage-specific format.
* *
* This is a publicly accessible static method to allow for alternative
* usages in data conversion scripts and also tests.
*
* @param string $raw * @param string $raw
* The raw configuration data string to decode. * The raw configuration data string to decode.
* *
* @return array * @return array
* The decoded configuration data as an associative array. * The decoded configuration data as an associative array.
*
* This is a publicly accessible static method to allow for alternative
* usages in data conversion scripts and also tests.
*/ */
public function decode($raw); public function decode($raw);

View file

@ -753,14 +753,11 @@ abstract class Connection {
*/ */
public function getDriverClass($class) { public function getDriverClass($class) {
if (empty($this->driverClasses[$class])) { if (empty($this->driverClasses[$class])) {
$driver = $this->driver(); if (empty($this->connectionOptions['namespace'])) {
if (!empty($this->connectionOptions['namespace'])) { // Fallback for Drupal 7 settings.php and the test runner script.
$driver_class = $this->connectionOptions['namespace'] . '\\' . $class; $this->connectionOptions['namespace'] = (new \ReflectionObject($this))->getNamespaceName();
}
else {
// Fallback for Drupal 7 settings.php.
$driver_class = "Drupal\\Core\\Database\\Driver\\{$driver}\\{$class}";
} }
$driver_class = $this->connectionOptions['namespace'] . '\\' . $class;
$this->driverClasses[$class] = class_exists($driver_class) ? $driver_class : $class; $this->driverClasses[$class] = class_exists($driver_class) ? $driver_class : $class;
} }
return $this->driverClasses[$class]; return $this->driverClasses[$class];

View file

@ -246,6 +246,9 @@ class Tasks extends InstallTasks {
// concurrency issues, when both try to update at the same time. // concurrency issues, when both try to update at the same time.
try { try {
$connection = Database::getConnection(); $connection = Database::getConnection();
// When testing, two installs might try to run the CREATE FUNCTION queries
// at the same time. Do not let that happen.
$connection->query('SELECT pg_advisory_lock(1)');
// Don't use {} around pg_proc table. // Don't use {} around pg_proc table.
if (!$connection->query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'")->fetchField()) { if (!$connection->query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'")->fetchField()) {
$connection->query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS $connection->query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
@ -264,6 +267,7 @@ class Tasks extends InstallTasks {
[ 'allow_delimiter_in_query' => TRUE ] [ 'allow_delimiter_in_query' => TRUE ]
); );
} }
$connection->query('SELECT pg_advisory_unlock(1)');
$this->pass(t('PostgreSQL has initialized itself.')); $this->pass(t('PostgreSQL has initialized itself.'));
} }

View file

@ -28,7 +28,7 @@ class Tasks extends InstallTasks {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function minimumVersion() { public function minimumVersion() {
return '3.6.8'; return '3.7.11';
} }
/** /**

View file

@ -420,8 +420,12 @@ class Schema extends DatabaseSchema {
* *
* @param $table * @param $table
* Name of the table. * Name of the table.
*
* @return * @return
* An array representing the schema, from drupal_get_schema(). * An array representing the schema, from drupal_get_schema().
*
* @throws \Exception
* If a column of the table could not be parsed.
*/ */
protected function introspectSchema($table) { protected function introspectSchema($table) {
$mapped_fields = array_flip($this->getFieldTypeMap()); $mapped_fields = array_flip($this->getFieldTypeMap());
@ -459,7 +463,7 @@ class Schema extends DatabaseSchema {
} }
} }
else { else {
new \Exception("Unable to parse the column type " . $row->type); throw new \Exception("Unable to parse the column type " . $row->type);
} }
} }
$indexes = array(); $indexes = array();
@ -710,7 +714,7 @@ class Schema extends DatabaseSchema {
// Can't use query placeholders for the schema because the query would // Can't use query placeholders for the schema because the query would
// have to be :prefixsqlite_master, which does not work. We also need to // have to be :prefixsqlite_master, which does not work. We also need to
// ignore the internal SQLite tables. // ignore the internal SQLite tables.
$result = db_query("SELECT name FROM " . $schema . ".sqlite_master WHERE type = :type AND name LIKE :table_name AND name NOT LIKE :pattern", array( $result = $this->connection->query("SELECT name FROM " . $schema . ".sqlite_master WHERE type = :type AND name LIKE :table_name AND name NOT LIKE :pattern", array(
':type' => 'table', ':type' => 'table',
':table_name' => $table_expression, ':table_name' => $table_expression,
':pattern' => 'sqlite_%', ':pattern' => 'sqlite_%',

View file

@ -6,11 +6,11 @@ namespace Drupal\Core\Database\Query;
* Interface for extendable query objects. * Interface for extendable query objects.
* *
* "Extenders" follow the "Decorator" OOP design pattern. That is, they wrap * "Extenders" follow the "Decorator" OOP design pattern. That is, they wrap
* and "decorate" another object. In our case, they implement the same interface * and "decorate" another object. In our case, they implement the same
* as select queries and wrap a select query, to which they delegate almost all * interface as select queries and wrap a select query, to which they delegate
* operations. Subclasses of this class may implement additional methods or * almost all operations. Subclasses of this class may implement additional
* override existing methods as appropriate. Extenders may also wrap other * methods or override existing methods as appropriate. Extenders may also wrap
* extender objects, allowing for arbitrarily complex "enhanced" queries. * other extender objects, allowing for arbitrarily complex "enhanced" queries.
*/ */
interface ExtendableInterface { interface ExtendableInterface {
@ -18,9 +18,12 @@ interface ExtendableInterface {
* Enhance this object by wrapping it in an extender object. * Enhance this object by wrapping it in an extender object.
* *
* @param $extender_name * @param $extender_name
* The base name of the extending class. The base name will be checked * The fully-qualified name of the extender class, without the leading '\'
* against the current database connection to allow driver-specific subclasses * (for example, Drupal\my_module\myExtenderClass). The extender name will
* as well, using the same logic as the query objects themselves. * be checked against the current database connection to allow
* driver-specific subclasses as well, using the same logic as the query
* objects themselves.
*
* @return \Drupal\Core\Database\Query\ExtendableInterface * @return \Drupal\Core\Database\Query\ExtendableInterface
* The extender object, which now contains a reference to this object. * The extender object, which now contains a reference to this object.
*/ */

View file

@ -39,7 +39,7 @@ class Select extends Query implements SelectInterface {
* 'type' => $join_type (one of INNER, LEFT OUTER, RIGHT OUTER), * 'type' => $join_type (one of INNER, LEFT OUTER, RIGHT OUTER),
* 'table' => $table, * 'table' => $table,
* 'alias' => $alias_of_the_table, * 'alias' => $alias_of_the_table,
* 'condition' => $condition_clause_on_which_to_join, * 'condition' => $join_condition (string or Condition object),
* 'arguments' => $array_of_arguments_for_placeholders_in_the condition. * 'arguments' => $array_of_arguments_for_placeholders_in_the condition.
* 'all_fields' => TRUE to SELECT $alias.*, FALSE or NULL otherwise. * 'all_fields' => TRUE to SELECT $alias.*, FALSE or NULL otherwise.
* ) * )
@ -47,6 +47,10 @@ class Select extends Query implements SelectInterface {
* If $table is a string, it is taken as the name of a table. If it is * If $table is a string, it is taken as the name of a table. If it is
* a Select query object, it is taken as a subquery. * a Select query object, it is taken as a subquery.
* *
* If $join_condition is a Condition object, any arguments should be
* incorporated into the object; a separate array of arguments does not
* need to be provided.
*
* @var array * @var array
*/ */
protected $tables = array(); protected $tables = array();
@ -196,6 +200,10 @@ class Select extends Query implements SelectInterface {
if ($table['table'] instanceof SelectInterface) { if ($table['table'] instanceof SelectInterface) {
$args += $table['table']->arguments(); $args += $table['table']->arguments();
} }
// If the join condition is an object, grab its arguments recursively.
if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
$args += $table['condition']->arguments();
}
} }
foreach ($this->expressions as $expression) { foreach ($this->expressions as $expression) {
@ -225,6 +233,10 @@ class Select extends Query implements SelectInterface {
if ($table['table'] instanceof SelectInterface) { if ($table['table'] instanceof SelectInterface) {
$table['table']->compile($connection, $queryPlaceholder); $table['table']->compile($connection, $queryPlaceholder);
} }
// Make sure join conditions are also compiled.
if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
$table['condition']->compile($connection, $queryPlaceholder);
}
} }
// If there are any dependent queries to UNION, compile it recursively. // If there are any dependent queries to UNION, compile it recursively.
@ -248,6 +260,11 @@ class Select extends Query implements SelectInterface {
return FALSE; return FALSE;
} }
} }
if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
if (!$table['condition']->compiled()) {
return FALSE;
}
}
} }
foreach ($this->union as $union) { foreach ($this->union as $union) {
@ -822,7 +839,7 @@ class Select extends Query implements SelectInterface {
$query .= $table_string . ' ' . $this->connection->escapeTable($table['alias']); $query .= $table_string . ' ' . $this->connection->escapeTable($table['alias']);
if (!empty($table['condition'])) { if (!empty($table['condition'])) {
$query .= ' ON ' . $table['condition']; $query .= ' ON ' . (string) $table['condition'];
} }
} }

View file

@ -268,6 +268,7 @@ class Datelist extends DateElementBase {
'#options' => $options, '#options' => $options,
'#required' => $element['#required'], '#required' => $element['#required'],
'#error_no_message' => FALSE, '#error_no_message' => FALSE,
'#empty_option' => $title,
); );
} }

View file

@ -58,8 +58,6 @@ class YamlFileLoader
public function load($file) public function load($file)
{ {
// Load from the file cache, fall back to loading the file. // Load from the file cache, fall back to loading the file.
// @todo Refactor this to cache parsed definition objects in
// https://www.drupal.org/node/2464053
$content = $this->fileCache->get($file); $content = $this->fileCache->get($file);
if (!$content) { if (!$content) {
$content = $this->loadFile($file); $content = $this->loadFile($file);

View file

@ -306,7 +306,7 @@ abstract class Entity implements EntityInterface {
// The entity ID is needed as a route parameter. // The entity ID is needed as a route parameter.
$uri_route_parameters[$this->getEntityTypeId()] = $this->id(); $uri_route_parameters[$this->getEntityTypeId()] = $this->id();
} }
if ($rel === 'revision') { if ($rel === 'revision' && $this instanceof RevisionableInterface) {
$uri_route_parameters[$this->getEntityTypeId() . '_revision'] = $this->getRevisionId(); $uri_route_parameters[$this->getEntityTypeId() . '_revision'] = $this->getRevisionId();
} }

View file

@ -339,10 +339,17 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
public static function preCreate(EntityStorageInterface $storage, array &$values); public static function preCreate(EntityStorageInterface $storage, array &$values);
/** /**
* Acts on an entity after it is created but before hooks are invoked. * Acts on a created entity before hooks are invoked.
*
* Used after the entity is created, but before saving the entity and before
* any of the presave hooks are invoked.
*
* See the @link entity_crud Entity CRUD topic @endlink for more information.
* *
* @param \Drupal\Core\Entity\EntityStorageInterface $storage * @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The entity storage object. * The entity storage object.
*
* @see \Drupal\Core\Entity\EntityInterface::create()
*/ */
public function postCreate(EntityStorageInterface $storage); public function postCreate(EntityStorageInterface $storage);

View file

@ -11,7 +11,10 @@ interface EntityTypeBundleInfoInterface {
* Get the bundle info of all entity types. * Get the bundle info of all entity types.
* *
* @return array * @return array
* An array of all bundle information. * An array of bundle information where the outer array is keyed by entity
* type. The next level is keyed by the bundle name. The inner arrays are
* associative arrays of bundle information, such as the label for the
* bundle.
*/ */
public function getAllBundleInfo(); public function getAllBundleInfo();
@ -22,7 +25,10 @@ interface EntityTypeBundleInfoInterface {
* The entity type. * The entity type.
* *
* @return array * @return array
* Returns the bundle information for the specified entity type. * An array of bundle information where the outer array is keyed by the
* bundle name, or the entity type name if the entity does not have bundles.
* The inner arrays are associative arrays of bundle information, such as
* the label for the bundle.
*/ */
public function getBundleInfo($entity_type); public function getBundleInfo($entity_type);

View file

@ -28,24 +28,47 @@ interface QueryInterface extends AlterableInterface {
* and the Polish 'siema' within a 'greetings' text field: * and the Polish 'siema' within a 'greetings' text field:
* @code * @code
* $entity_ids = \Drupal::entityQuery($entity_type) * $entity_ids = \Drupal::entityQuery($entity_type)
* ->condition('greetings', 'merhaba', '=', 'tr'); * ->condition('greetings', 'merhaba', '=', 'tr')
* ->condition('greetings.value', 'siema', '=', 'pl'); * ->condition('greetings.value', 'siema', '=', 'pl')
* ->execute(); * ->execute();
* $entity_ids = $query->execute(); * $entity_ids = $query->execute();
* @endcode * @endcode
* *
* @param $field * @param $field
* Name of the field being queried. It must contain a field name, * Name of the field being queried. It must contain a field name, optionally
* optionally followed by a column name. The column can be "entity" for * followed by a column name. The column can be "entity" for reference
* reference fields and that can be followed similarly by a field name * fields and that can be followed similarly by a field name and so on. Some
* and so on. Some examples: * examples:
* - nid * - nid
* - tags.value * - tags.value
* - tags * - tags
* - uid.entity.name * - uid.entity.name
* "tags" "is the same as "tags.value" as value is the default column. * "tags" "is the same as "tags.value" as value is the default column.
* If two or more conditions have the same field names they apply to the * If two or more conditions have the same field names they apply to the
* same delta within that field. * same delta within that field. In order to limit the condition to a
* specific item a numeric delta should be added between the field name and
* the column name.
* @code
* ->condition('tags.5.value', 'news')
* @endcode
* This will require condition to be satisfied on a specific delta of the
* field. The condition above will require the 6th value of the field to
* match the provided value. Further, it's possible to create a condition on
* the delta itself by using '%delta'. For example,
* @code
* ->condition('tags.%delta', 5)
* @endcode
* will find only entities which have at least six tags. Finally, the
* condition on the delta itself accompanied with a condition on the value
* will require the value to appear in the specific delta range. For
* example,
* @code
* ->condition('tags.%delta', 0, '>'))
* ->condition('tags.%delta.value', 'news'))
* @endcode
* will only find the "news" tag if it is not the first value. It should be
* noted that conditions on specific deltas and delta ranges are only
* supported when querying content entities.
* @param $value * @param $value
* The value for $field. In most cases, this is a scalar and it's treated as * The value for $field. In most cases, this is a scalar and it's treated as
* case-insensitive. For more complex operators, it is an array. The meaning * case-insensitive. For more complex operators, it is an array. The meaning

View file

@ -5,6 +5,7 @@ namespace Drupal\Core\Entity\Query\Sql;
use Drupal\Core\Database\Query\SelectInterface; use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\Query\QueryException; use Drupal\Core\Entity\Query\QueryException;
use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
use Drupal\Core\Entity\Sql\TableMappingInterface;
/** /**
* Adds tables and fields to the SQL entity query. * Adds tables and fields to the SQL entity query.
@ -112,11 +113,37 @@ class Tables implements TablesInterface {
// Check whether this field is stored in a dedicated table. // Check whether this field is stored in a dedicated table.
if ($field_storage && $table_mapping->requiresDedicatedTableStorage($field_storage)) { if ($field_storage && $table_mapping->requiresDedicatedTableStorage($field_storage)) {
$delta = NULL;
// Find the field column. // Find the field column.
$column = $field_storage->getMainPropertyName(); $column = $field_storage->getMainPropertyName();
if ($key < $count) { if ($key < $count) {
$next = $specifiers[$key + 1]; $next = $specifiers[$key + 1];
// If this is a numeric specifier we're adding a condition on the
// specific delta.
if (is_numeric($next)) {
$delta = $next;
$index_prefix .= ".$delta";
// Do not process it again.
$key++;
$next = $specifiers[$key + 1];
}
// If this specifier is the reserved keyword "%delta" we're adding a
// condition on a delta range.
elseif ($next == TableMappingInterface::DELTA) {
$index_prefix .= TableMappingInterface::DELTA;
// Do not process it again.
$key++;
// If there are more specifiers to work with then continue
// processing. If this is the last specifier then use the reserved
// keyword as a column name.
if ($key < $count) {
$next = $specifiers[$key + 1];
}
else {
$column = TableMappingInterface::DELTA;
}
}
// Is this a field column? // Is this a field column?
$columns = $field_storage->getColumns(); $columns = $field_storage->getColumns();
if (isset($columns[$next]) || in_array($next, $table_mapping->getReservedColumns())) { if (isset($columns[$next]) || in_array($next, $table_mapping->getReservedColumns())) {
@ -140,7 +167,7 @@ class Tables implements TablesInterface {
$next_index_prefix = "$relationship_specifier.$column"; $next_index_prefix = "$relationship_specifier.$column";
} }
} }
$table = $this->ensureFieldTable($index_prefix, $field_storage, $type, $langcode, $base_table, $entity_id_field, $field_id_field); $table = $this->ensureFieldTable($index_prefix, $field_storage, $type, $langcode, $base_table, $entity_id_field, $field_id_field, $delta);
$sql_column = $table_mapping->getFieldColumnName($field_storage, $column); $sql_column = $table_mapping->getFieldColumnName($field_storage, $column);
$property_definitions = $field_storage->getPropertyDefinitions(); $property_definitions = $field_storage->getPropertyDefinitions();
if (isset($property_definitions[$column])) { if (isset($property_definitions[$column])) {
@ -173,6 +200,27 @@ class Tables implements TablesInterface {
// next one is a column of this field. // next one is a column of this field.
if ($key < $count) { if ($key < $count) {
$next = $specifiers[$key + 1]; $next = $specifiers[$key + 1];
// If this specifier is the reserved keyword "%delta" we're adding a
// condition on a delta range.
if ($next == TableMappingInterface::DELTA) {
$key++;
if ($key < $count) {
$next = $specifiers[$key + 1];
}
else {
return 0;
}
}
// If this is a numeric specifier we're adding a condition on the
// specific delta. Since we know that this is a single value base
// field no other value than 0 makes sense.
if (is_numeric($next)) {
if ($next > 0) {
$this->sqlQuery->condition('1 <> 1');
}
$key++;
$next = $specifiers[$key + 1];
}
// Is this a field column? // Is this a field column?
$columns = $field_storage->getColumns(); $columns = $field_storage->getColumns();
if (isset($columns[$next]) || in_array($next, $table_mapping->getReservedColumns())) { if (isset($columns[$next]) || in_array($next, $table_mapping->getReservedColumns())) {
@ -264,7 +312,7 @@ class Tables implements TablesInterface {
* @return string * @return string
* @throws \Drupal\Core\Entity\Query\QueryException * @throws \Drupal\Core\Entity\Query\QueryException
*/ */
protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $base_table, $entity_id_field, $field_id_field) { protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $base_table, $entity_id_field, $field_id_field, $delta) {
$field_name = $field->getName(); $field_name = $field->getName();
if (!isset($this->fieldTables[$index_prefix . $field_name])) { if (!isset($this->fieldTables[$index_prefix . $field_name])) {
$entity_type_id = $this->sqlQuery->getMetaData('entity_type'); $entity_type_id = $this->sqlQuery->getMetaData('entity_type');
@ -274,12 +322,12 @@ class Tables implements TablesInterface {
if ($field->getCardinality() != 1) { if ($field->getCardinality() != 1) {
$this->sqlQuery->addMetaData('simple_query', FALSE); $this->sqlQuery->addMetaData('simple_query', FALSE);
} }
$this->fieldTables[$index_prefix . $field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = $base_table.$entity_id_field", $langcode); $this->fieldTables[$index_prefix . $field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = $base_table.$entity_id_field", $langcode, $delta);
} }
return $this->fieldTables[$index_prefix . $field_name]; return $this->fieldTables[$index_prefix . $field_name];
} }
protected function addJoin($type, $table, $join_condition, $langcode) { protected function addJoin($type, $table, $join_condition, $langcode, $delta = NULL) {
$arguments = array(); $arguments = array();
if ($langcode) { if ($langcode) {
$entity_type_id = $this->sqlQuery->getMetaData('entity_type'); $entity_type_id = $this->sqlQuery->getMetaData('entity_type');
@ -291,6 +339,11 @@ class Tables implements TablesInterface {
$join_condition .= ' AND %alias.' . $langcode_key . ' = ' . $placeholder; $join_condition .= ' AND %alias.' . $langcode_key . ' = ' . $placeholder;
$arguments[$placeholder] = $langcode; $arguments[$placeholder] = $langcode;
} }
if (isset($delta)) {
$placeholder = ':delta' . $this->sqlQuery->nextPlaceholder();
$join_condition .= ' AND %alias.delta = ' . $placeholder;
$arguments[$placeholder] = $delta;
}
return $this->sqlQuery->addJoin($type, $table, NULL, $join_condition, $arguments); return $this->sqlQuery->addJoin($type, $table, NULL, $join_condition, $arguments);
} }

View file

@ -11,8 +11,10 @@ interface TablesInterface {
* Adds a field to a database query. * Adds a field to a database query.
* *
* @param string $field * @param string $field
* If it contains a dot, then field name dot field column. If it doesn't * If it doesn't contain a dot, then an entity base field name. If it
* then entity property name. * contains a dot, then either field name dot field column or field name dot
* delta dot field column. Delta can be a numeric value or a "%delta" for
* any value.
* @param string $type * @param string $type
* Join type, can either be INNER or LEFT. * Join type, can either be INNER or LEFT.
* @param string $langcode * @param string $langcode

View file

@ -204,7 +204,12 @@ class DefaultTableMapping implements TableMappingInterface {
$column_name = count($storage_definition->getColumns()) == 1 ? $field_name : $field_name . '__' . $property_name; $column_name = count($storage_definition->getColumns()) == 1 ? $field_name : $field_name . '__' . $property_name;
} }
elseif ($this->requiresDedicatedTableStorage($storage_definition)) { elseif ($this->requiresDedicatedTableStorage($storage_definition)) {
$column_name = !in_array($property_name, $this->getReservedColumns()) ? $field_name . '_' . $property_name : $property_name; if ($property_name == TableMappingInterface::DELTA) {
$column_name = 'delta';
}
else {
$column_name = !in_array($property_name, $this->getReservedColumns()) ? $field_name . '_' . $property_name : $property_name;
}
} }
else { else {
throw new SqlContentEntityStorageException("Column information not available for the '$field_name' field."); throw new SqlContentEntityStorageException("Column information not available for the '$field_name' field.");

View file

@ -150,7 +150,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
* The database connection to be used. * The database connection to be used.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager. * The entity manager.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache backend to be used. * The cache backend to be used.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager. * The language manager.

View file

@ -1837,6 +1837,24 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
} }
} }
// Add unique keys.
foreach ($schema['unique keys'] as $index_name => $columns) {
$real_name = $this->getFieldIndexName($storage_definition, $index_name);
foreach ($columns as $column_name) {
// Unique keys can be specified as either a column name or an array with
// column name and length. Allow for either case.
if (is_array($column_name)) {
$data_schema['unique keys'][$real_name][] = array(
$table_mapping->getFieldColumnName($storage_definition, $column_name[0]),
$column_name[1],
);
}
else {
$data_schema['unique keys'][$real_name][] = $table_mapping->getFieldColumnName($storage_definition, $column_name);
}
}
}
// Add foreign keys. // Add foreign keys.
foreach ($schema['foreign keys'] as $specifier => $specification) { foreach ($schema['foreign keys'] as $specifier => $specification) {
$real_name = $this->getFieldIndexName($storage_definition, $specifier); $real_name = $this->getFieldIndexName($storage_definition, $specifier);

View file

@ -19,6 +19,11 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface;
*/ */
interface TableMappingInterface { interface TableMappingInterface {
/**
* A property that represents delta used in entity query conditions.
*/
const DELTA = '%delta';
/** /**
* Gets a list of table names for this mapping. * Gets a list of table names for this mapping.
* *

View file

@ -3,7 +3,6 @@
namespace Drupal\Core\EventSubscriber; namespace Drupal\Core\EventSubscriber;
use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Render\BareHtmlPageRendererInterface; use Drupal\Core\Render\BareHtmlPageRendererInterface;
use Drupal\Core\Routing\RouteMatch; use Drupal\Core\Routing\RouteMatch;
@ -13,6 +12,7 @@ use Drupal\Core\Site\MaintenanceModeInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\KernelEvents;
@ -90,18 +90,25 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
* The event to process. * The event to process.
*/ */
public function onKernelRequestMaintenance(GetResponseEvent $event) { public function onKernelRequestMaintenance(GetResponseEvent $event) {
$route_match = RouteMatch::createFromRequest($event->getRequest()); $request = $event->getRequest();
$route_match = RouteMatch::createFromRequest($request);
if ($this->maintenanceMode->applies($route_match)) { if ($this->maintenanceMode->applies($route_match)) {
// Don't cache maintenance mode pages. // Don't cache maintenance mode pages.
\Drupal::service('page_cache_kill_switch')->trigger(); \Drupal::service('page_cache_kill_switch')->trigger();
if (!$this->maintenanceMode->exempt($this->account)) { if (!$this->maintenanceMode->exempt($this->account)) {
// Deliver the 503 page if the site is in maintenance mode and the // Deliver the 503 page if the site is in maintenance mode and the
// logged in user is not allowed to bypass it. // logged in user is not allowed to bypass it.
// If the request format is not 'html' then show default maintenance
// mode page else show a text/plain page with maintenance message.
if ($request->getRequestFormat() !== 'html') {
$response = new Response($this->getSiteMaintenanceMessage(), 503, array('Content-Type' => 'text/plain'));
$event->setResponse($response);
return;
}
drupal_maintenance_theme(); drupal_maintenance_theme();
$content = Xss::filterAdmin(SafeMarkup::format($this->config->get('system.maintenance')->get('message'), array( $response = $this->bareHtmlPageRenderer->renderBarePage(['#markup' => $this->getSiteMaintenanceMessage()], $this->t('Site under maintenance'), 'maintenance_page');
'@site' => $this->config->get('system.site')->get('name'),
)));
$response = $this->bareHtmlPageRenderer->renderBarePage(['#markup' => $content], $this->t('Site under maintenance'), 'maintenance_page');
$response->setStatusCode(503); $response->setStatusCode(503);
$event->setResponse($response); $event->setResponse($response);
} }
@ -121,6 +128,18 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
} }
} }
/**
* Gets the site maintenance message.
*
* @return \Drupal\Component\Render\MarkupInterface
* The formatted site maintenance message.
*/
protected function getSiteMaintenanceMessage() {
return SafeMarkup::format($this->config->get('system.maintenance')->get('message'), array(
'@site' => $this->config->get('system.site')->get('name'),
));
}
/** /**
* Wraps the drupal_set_message function. * Wraps the drupal_set_message function.
*/ */

View file

@ -214,7 +214,7 @@ interface ModuleHandlerInterface {
* The name of the module (without the .module extension). * The name of the module (without the .module extension).
* @param string $hook * @param string $hook
* The name of the hook to invoke. * The name of the hook to invoke.
* @param ... * @param array $args
* Arguments to pass to the hook implementation. * Arguments to pass to the hook implementation.
* *
* @return mixed * @return mixed

View file

@ -72,6 +72,8 @@ use Drupal\Core\Utility\UpdateException;
* frequently called should be left in the main module file so that they are * frequently called should be left in the main module file so that they are
* always available. * always available.
* *
* See system_hook_info() for all hook groups defined by Drupal core.
*
* @return * @return
* An associative array whose keys are hook names and whose values are an * An associative array whose keys are hook names and whose values are an
* associative array containing: * associative array containing:
@ -79,8 +81,6 @@ use Drupal\Core\Utility\UpdateException;
* system will determine whether a file with the name $module.$group.inc * system will determine whether a file with the name $module.$group.inc
* exists, and automatically load it when required. * exists, and automatically load it when required.
* *
* See system_hook_info() for all hook groups defined by Drupal core.
*
* @see hook_hook_info_alter() * @see hook_hook_info_alter()
*/ */
function hook_hook_info() { function hook_hook_info() {

View file

@ -28,6 +28,8 @@ class PasswordItem extends StringItem {
->setSetting('case_sensitive', TRUE); ->setSetting('case_sensitive', TRUE);
$properties['existing'] = DataDefinition::create('string') $properties['existing'] = DataDefinition::create('string')
->setLabel(new TranslatableMarkup('Existing password')); ->setLabel(new TranslatableMarkup('Existing password'));
$properties['pre_hashed'] = DataDefinition::create('boolean')
->setLabel(new TranslatableMarkup('Determines if a password needs hashing'));
return $properties; return $properties;
} }
@ -40,8 +42,11 @@ class PasswordItem extends StringItem {
$entity = $this->getEntity(); $entity = $this->getEntity();
// Update the user password if it has changed. if ($this->pre_hashed) {
if ($entity->isNew() || (strlen(trim($this->value)) > 0 && $this->value != $entity->original->{$this->getFieldDefinition()->getName()}->value)) { // Reset the pre_hashed value since it has now been used.
$this->pre_hashed = FALSE;
}
elseif ($entity->isNew() || (strlen(trim($this->value)) > 0 && $this->value != $entity->original->{$this->getFieldDefinition()->getName()}->value)) {
// Allow alternate password hashing schemes. // Allow alternate password hashing schemes.
$this->value = \Drupal::service('password')->hash(trim($this->value)); $this->value = \Drupal::service('password')->hash(trim($this->value));
// Abort if the hashing failed and returned FALSE. // Abort if the hashing failed and returned FALSE.

View file

@ -15,7 +15,29 @@ use Symfony\Component\HttpFoundation\RequestStack;
/** /**
* Provides a base class for forms. * Provides a base class for forms.
* *
* This class exists as a mid-point between dependency injection through
* ContainerInjectionInterface, and a less-structured use of traits which
* default to using the \Drupal accessor for service discovery.
*
* To properly inject services, override create() and use the setters provided
* by the traits to inject the needed services.
*
* @code
* public static function create($container) {
* $form = new static();
* // In this example we only need string translation so we use the
* // setStringTranslation() method provided by StringTranslationTrait.
* $form->setStringTranslation($container->get('string_translation'));
* return $form;
* }
* @endcode
*
* Alternately, do not use FormBase. A class can implement FormInterface, use
* the traits it needs, and inject services from the container as required.
*
* @ingroup form_api * @ingroup form_api
*
* @see \Drupal\Core\DependencyInjection\ContainerInjectionInterface
*/ */
abstract class FormBase implements FormInterface, ContainerInjectionInterface { abstract class FormBase implements FormInterface, ContainerInjectionInterface {

View file

@ -137,9 +137,9 @@ interface FormBuilderInterface {
* by calling $form_state->getErrors(). * by calling $form_state->getErrors().
* *
* @param \Drupal\Core\Form\FormInterface|string $form_arg * @param \Drupal\Core\Form\FormInterface|string $form_arg
* A form object to use to build the form, or the unique string identifying * The value must be one of the following:
* the desired form. If $form_arg is a string and a function with that * - The name of a class that implements \Drupal\Core\Form\FormInterface.
* name exists, it is called to build the form array. * - An instance of a class that implements \Drupal\Core\Form\FormInterface.
* @param $form_state * @param $form_state
* The current state of the form. Most important is the * The current state of the form. Most important is the
* $form_state->getValues() collection, a tree of data used to simulate the * $form_state->getValues() collection, a tree of data used to simulate the

View file

@ -41,11 +41,11 @@ abstract class ContextAwarePluginBase extends ComponentContextAwarePluginBase im
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
* @return \Drupal\Core\Plugin\Context\ContextInterface
* The context object.
*
* This code is identical to the Component in order to pick up a different * This code is identical to the Component in order to pick up a different
* Context class. * Context class.
*
* @return \Drupal\Core\Plugin\Context\ContextInterface
* The context object.
*/ */
public function getContext($name) { public function getContext($name) {
// Check for a valid context value. // Check for a valid context value.

View file

@ -17,7 +17,7 @@ use Drupal\Core\Form\FormStateInterface;
* @code * @code
* $form['high_school']['tests_taken'] = array( * $form['high_school']['tests_taken'] = array(
* '#type' => 'checkboxes', * '#type' => 'checkboxes',
* '#options' => array('SAT' => $this->t('SAT'), 'ACT' => $this->t('ACT'))), * '#options' => array('SAT' => $this->t('SAT'), 'ACT' => $this->t('ACT')),
* '#title' => $this->t('What standardized tests did you take?'), * '#title' => $this->t('What standardized tests did you take?'),
* ... * ...
* ); * );

View file

@ -12,7 +12,7 @@ namespace Drupal\Core\Render\Element;
* $build['status_messages'] = [ * $build['status_messages'] = [
* '#type' => 'status_messages', * '#type' => 'status_messages',
* ]; * ];
* @end * @endcode
* *
* @RenderElement("status_messages") * @RenderElement("status_messages")
*/ */

View file

@ -64,7 +64,7 @@ interface RenderCacheInterface {
* this array. * this array.
* *
* @return bool|null * @return bool|null
* Returns FALSE if no cache item could be created, NULL otherwise. * Returns FALSE if no cache item could be created, NULL otherwise.
* *
* @see ::get() * @see ::get()
*/ */

View file

@ -2,10 +2,27 @@
namespace Drupal\Core\Routing; namespace Drupal\Core\Routing;
/**
* Rebuilds the route information and dumps it.
*
* Rebuilding the route information is the process of gathering all routing data
* from .routing.yml files, creating a
* \Symfony\Component\Routing\RouteCollection object out of it, and dispatching
* that object as a \Drupal\Core\Routing\RouteBuildEvent to all registered
* listeners. After that, the \Symfony\Component\Routing\RouteCollection object
* is used to dump the data. Examples of a dump include filling up the routing
* table, auto-generating Apache mod_rewrite rules, or auto-generating a PHP
* matcher class.
*
* @see \Drupal\Core\Routing\MatcherDumperInterface
* @see \Drupal\Core\Routing\RouteProviderInterface
*
* @ingroup routing
*/
interface RouteBuilderInterface { interface RouteBuilderInterface {
/** /**
* Rebuilds the route info and dumps to dumper. * Rebuilds the route information and dumps it.
* *
* @return bool * @return bool
* Returns TRUE if the rebuild succeeds, FALSE otherwise. * Returns TRUE if the rebuild succeeds, FALSE otherwise.
@ -13,7 +30,7 @@ interface RouteBuilderInterface {
public function rebuild(); public function rebuild();
/** /**
* Rebuilds the route info and dumps to dumper if necessary. * Rebuilds the route information if necessary, and dumps it.
* *
* @return bool * @return bool
* Returns TRUE if the rebuild occurs, FALSE otherwise. * Returns TRUE if the rebuild occurs, FALSE otherwise.

View file

@ -229,10 +229,8 @@ class UrlGenerator implements UrlGeneratorInterface {
// Add a query string if needed, including extra parameters. // Add a query string if needed, including extra parameters.
$query_params += array_diff_key($parameters, $variables, $defaults); $query_params += array_diff_key($parameters, $variables, $defaults);
if ($query_params && $query = http_build_query($query_params, '', '&')) { if ($query_params && $query = UrlHelper::buildQuery($query_params)) {
// "/" and "?" can be left decoded for better user experience, see $url .= '?' . $query;
// http://tools.ietf.org/html/rfc3986#section-3.4
$url .= '?' . strtr($query, array('%2F' => '/'));
} }
return $url; return $url;
@ -253,7 +251,7 @@ class UrlGenerator implements UrlGeneratorInterface {
* $parameters merged in. * $parameters merged in.
* *
* @return string * @return string
* The url path corresponding to the route, without the base path. * The url path corresponding to the route, without the base path.
*/ */
protected function getInternalPathFromRoute($name, SymfonyRoute $route, $parameters = array(), $query_params = array()) { protected function getInternalPathFromRoute($name, SymfonyRoute $route, $parameters = array(), $query_params = array()) {
// The Route has a cache of its own and is not recompiled as long as it does // The Route has a cache of its own and is not recompiled as long as it does

View file

@ -21,7 +21,7 @@ interface UrlGeneratorInterface extends VersatileGeneratorInterface {
* \Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate(). * \Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate().
* *
* @return string * @return string
* The internal Drupal path corresponding to the route. * The internal Drupal path corresponding to the route.
*/ */
public function getPathFromRoute($name, $parameters = array()); public function getPathFromRoute($name, $parameters = array());

View file

@ -64,7 +64,7 @@ interface PhpStreamWrapperInterface {
public function stream_cast($cast_as); public function stream_cast($cast_as);
/** /**
* @return void * Closes stream.
*/ */
public function stream_close(); public function stream_close();

View file

@ -82,7 +82,7 @@ class FileTranslation extends StaticTranslation {
* want to find translation files. * want to find translation files.
* *
* @return string * @return string
* String file pattern. * String file pattern.
*/ */
protected function getTranslationFilesPattern($langcode = NULL) { protected function getTranslationFilesPattern($langcode = NULL) {
// The file name matches: drupal-[release version].[language code].po // The file name matches: drupal-[release version].[language code].po

View file

@ -272,7 +272,11 @@ class Url {
if ($uri_parts === FALSE) { if ($uri_parts === FALSE) {
throw new \InvalidArgumentException("The URI '$uri' is malformed."); throw new \InvalidArgumentException("The URI '$uri' is malformed.");
} }
if (empty($uri_parts['scheme'])) { // We support protocol-relative URLs.
if (strpos($uri, '//') === 0) {
$uri_parts['scheme'] = '';
}
elseif (empty($uri_parts['scheme'])) {
throw new \InvalidArgumentException("The URI '$uri' is invalid. You must use a valid URI scheme."); throw new \InvalidArgumentException("The URI '$uri' is invalid. You must use a valid URI scheme.");
} }
$uri_parts += ['path' => '']; $uri_parts += ['path' => ''];

View file

@ -140,6 +140,7 @@ class LinkGenerator implements LinkGeneratorInterface {
// Allow other modules to modify the structure of the link. // Allow other modules to modify the structure of the link.
$this->moduleHandler->alter('link', $variables); $this->moduleHandler->alter('link', $variables);
$url = $variables['url'];
// Move attributes out of options since generateFromRoute() doesn't need // Move attributes out of options since generateFromRoute() doesn't need
// them. Include a placeholder for the href. // them. Include a placeholder for the href.

View file

@ -222,7 +222,7 @@
}); });
// Use jQuery UI Autocomplete on the textfield. // Use jQuery UI Autocomplete on the textfield.
$autocomplete.autocomplete(autocomplete.options) $autocomplete.autocomplete(autocomplete.options)
.each(function() { .each(function () {
$(this).data('ui-autocomplete')._renderItem = autocomplete.options.renderItem; $(this).data('ui-autocomplete')._renderItem = autocomplete.options.renderItem;
}); });
} }

View file

@ -1,8 +1,8 @@
<?php <?php
namespace Drupal\action\Tests; namespace Drupal\Tests\action\Functional;
use Drupal\simpletest\WebTestBase; use Drupal\Tests\BrowserTestBase;
/** /**
* Tests that uninstalling actions does not remove other module's actions. * Tests that uninstalling actions does not remove other module's actions.
@ -10,7 +10,7 @@ use Drupal\simpletest\WebTestBase;
* @group action * @group action
* @see \Drupal\action\Plugin\views\field\BulkForm * @see \Drupal\action\Plugin\views\field\BulkForm
*/ */
class ActionUninstallTest extends WebTestBase { class ActionUninstallTest extends BrowserTestBase {
/** /**
* Modules to install. * Modules to install.

View file

@ -1,8 +1,8 @@
<?php <?php
namespace Drupal\action\Tests; namespace Drupal\Tests\action\Functional;
use Drupal\simpletest\WebTestBase; use Drupal\Tests\BrowserTestBase;
use Drupal\views\Views; use Drupal\views\Views;
/** /**
@ -11,7 +11,7 @@ use Drupal\views\Views;
* @group action * @group action
* @see \Drupal\action\Plugin\views\field\BulkForm * @see \Drupal\action\Plugin\views\field\BulkForm
*/ */
class BulkFormTest extends WebTestBase { class BulkFormTest extends BrowserTestBase {
/** /**
* Modules to install. * Modules to install.
@ -123,7 +123,7 @@ class BulkFormTest extends WebTestBase {
// Check the default title. // Check the default title.
$this->drupalGet('test_bulk_form'); $this->drupalGet('test_bulk_form');
$result = $this->xpath('//label[@for="edit-action"]'); $result = $this->xpath('//label[@for="edit-action"]');
$this->assertEqual('With selection', (string) $result[0]); $this->assertEqual('With selection', $result[0]->getText());
// Setup up a different bulk form title. // Setup up a different bulk form title.
$view = Views::getView('test_bulk_form'); $view = Views::getView('test_bulk_form');
@ -133,7 +133,7 @@ class BulkFormTest extends WebTestBase {
$this->drupalGet('test_bulk_form'); $this->drupalGet('test_bulk_form');
$result = $this->xpath('//label[@for="edit-action"]'); $result = $this->xpath('//label[@for="edit-action"]');
$this->assertEqual('Test title', (string) $result[0]); $this->assertEqual('Test title', $result[0]->getText());
$this->drupalGet('test_bulk_form'); $this->drupalGet('test_bulk_form');
// Call the node delete action. // Call the node delete action.

View file

@ -1,9 +1,9 @@
<?php <?php
namespace Drupal\action\Tests; namespace Drupal\Tests\action\Functional;
use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Crypt;
use Drupal\simpletest\WebTestBase; use Drupal\Tests\BrowserTestBase;
/** /**
* Tests complex actions configuration by adding, editing, and deleting a * Tests complex actions configuration by adding, editing, and deleting a
@ -11,7 +11,7 @@ use Drupal\simpletest\WebTestBase;
* *
* @group action * @group action
*/ */
class ConfigurationTest extends WebTestBase { class ConfigurationTest extends BrowserTestBase {
/** /**
* Modules to install. * Modules to install.

View file

@ -20,7 +20,7 @@ class AggregatorItem extends DrupalSqlBase {
public function query() { public function query() {
return $this->select('aggregator_item', 'ai') return $this->select('aggregator_item', 'ai')
->fields('ai') ->fields('ai')
->orderBy('iid'); ->orderBy('ai.iid');
} }
/** /**

View file

@ -1,15 +1,15 @@
<?php <?php
namespace Drupal\ban\Tests; namespace Drupal\Tests\ban\Functional;
use Drupal\simpletest\WebTestBase; use Drupal\Tests\BrowserTestBase;
/** /**
* Tests IP address banning. * Tests IP address banning.
* *
* @group ban * @group ban
*/ */
class IpAddressBlockingTest extends WebTestBase { class IpAddressBlockingTest extends BrowserTestBase {
/** /**
* Modules to install. * Modules to install.
@ -61,7 +61,7 @@ class IpAddressBlockingTest extends WebTestBase {
// Pass an IP address as a URL parameter and submit it. // Pass an IP address as a URL parameter and submit it.
$submit_ip = '1.2.3.4'; $submit_ip = '1.2.3.4';
$this->drupalPostForm('admin/config/people/ban/' . $submit_ip, NULL, t('Add')); $this->drupalPostForm('admin/config/people/ban/' . $submit_ip, array(), t('Add'));
$ip = db_query("SELECT iid from {ban_ip} WHERE ip = :ip", array(':ip' => $submit_ip))->fetchField(); $ip = db_query("SELECT iid from {ban_ip} WHERE ip = :ip", array(':ip' => $submit_ip))->fetchField();
$this->assertTrue($ip, 'IP address found in database'); $this->assertTrue($ip, 'IP address found in database');
$this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $submit_ip)), 'IP address was banned.'); $this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $submit_ip)), 'IP address was banned.');

View file

@ -226,6 +226,13 @@ class BigPipe implements BigPipeInterface {
$preg_placeholder_strings = array_map($prepare_for_preg_split, array_keys($no_js_placeholders)); $preg_placeholder_strings = array_map($prepare_for_preg_split, array_keys($no_js_placeholders));
$fragments = preg_split('/' . implode('|', $preg_placeholder_strings) . '/', $html, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); $fragments = preg_split('/' . implode('|', $preg_placeholder_strings) . '/', $html, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
// Determine how many occurrences there are of each no-JS placeholder.
$placeholder_occurrences = array_count_values(array_intersect($fragments, array_keys($no_js_placeholders)));
// Set up a variable to store the content of placeholders that have multiple
// occurrences.
$multi_occurrence_placeholders_content = [];
foreach ($fragments as $fragment) { foreach ($fragments as $fragment) {
// If the fragment isn't one of the no-JS placeholders, it is the HTML in // If the fragment isn't one of the no-JS placeholders, it is the HTML in
// between placeholders and it must be printed & flushed immediately. The // between placeholders and it must be printed & flushed immediately. The
@ -236,6 +243,15 @@ class BigPipe implements BigPipeInterface {
continue; continue;
} }
// If there are multiple occurrences of this particular placeholder, and
// this is the second occurrence, we can skip all calculations and just
// send the same content.
if ($placeholder_occurrences[$fragment] > 1 && isset($multi_occurrence_placeholders_content[$fragment])) {
print $multi_occurrence_placeholders_content[$fragment];
flush();
continue;
}
$placeholder = $fragment; $placeholder = $fragment;
assert('isset($no_js_placeholders[$placeholder])'); assert('isset($no_js_placeholders[$placeholder])');
$token = Crypt::randomBytesBase64(55); $token = Crypt::randomBytesBase64(55);
@ -310,6 +326,13 @@ class BigPipe implements BigPipeInterface {
// they can be sent in ::sendPreBody(). // they can be sent in ::sendPreBody().
$cumulative_assets->setAlreadyLoadedLibraries(array_merge($cumulative_assets->getAlreadyLoadedLibraries(), $html_response->getAttachments()['library'])); $cumulative_assets->setAlreadyLoadedLibraries(array_merge($cumulative_assets->getAlreadyLoadedLibraries(), $html_response->getAttachments()['library']));
$cumulative_assets->setSettings($html_response->getAttachments()['drupalSettings']); $cumulative_assets->setSettings($html_response->getAttachments()['drupalSettings']);
// If there are multiple occurrences of this particular placeholder, track
// the content that was sent, so we can skip all calculations for the next
// occurrence.
if ($placeholder_occurrences[$fragment] > 1) {
$multi_occurrence_placeholders_content[$fragment] = $html_response->getContent();
}
} }
} }
@ -508,7 +531,9 @@ EOF;
* *
* @return array * @return array
* Indexed array; the order in which the BigPipe placeholders must be sent. * Indexed array; the order in which the BigPipe placeholders must be sent.
* Values are the BigPipe placeholder IDs. * Values are the BigPipe placeholder IDs. Note that only unique
* placeholders are kept: if the same placeholder occurs multiple times, we
* only keep the first occurrence.
*/ */
protected function getPlaceholderOrder($html) { protected function getPlaceholderOrder($html) {
$fragments = explode('<div data-big-pipe-placeholder-id="', $html); $fragments = explode('<div data-big-pipe-placeholder-id="', $html);
@ -521,7 +546,7 @@ EOF;
$order[] = $placeholder; $order[] = $placeholder;
} }
return $order; return array_unique($order);
} }
} }

View file

@ -269,6 +269,42 @@ class BigPipeTest extends WebTestBase {
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log'); unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
} }
/**
* Tests BigPipe with a multi-occurrence placeholder.
*/
public function testBigPipeMultiOccurrencePlaceholders() {
$this->drupalLogin($this->rootUser);
$this->assertSessionCookieExists(TRUE);
$this->assertBigPipeNoJsCookieExists(FALSE);
// By not calling performMetaRefresh() here, we simulate JavaScript being
// enabled, because as far as the BigPipe module is concerned, JavaScript is
// enabled in the browser as long as the BigPipe no-JS cookie is *not* set.
// @see setUp()
// @see performMetaRefresh()
$this->drupalGet(Url::fromRoute('big_pipe_test_multi_occurrence'));
$big_pipe_placeholder_id = 'callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args[0]&amp;token=a8c34b5e';
$expected_placeholder_replacement = '<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="' . $big_pipe_placeholder_id . '">';
$this->assertRaw('The count is 1.');
$this->assertNoRaw('The count is 2.');
$this->assertNoRaw('The count is 3.');
$raw_content = $this->getRawContent();
$this->assertTrue(substr_count($raw_content, $expected_placeholder_replacement) == 1, 'Only one placeholder replacement was found for the duplicate #lazy_builder arrays.');
// By calling performMetaRefresh() here, we simulate JavaScript being
// disabled, because as far as the BigPipe module is concerned, it is
// enabled in the browser when the BigPipe no-JS cookie is set.
// @see setUp()
// @see performMetaRefresh()
$this->performMetaRefresh();
$this->assertBigPipeNoJsCookieExists(TRUE);
$this->drupalGet(Url::fromRoute('big_pipe_test_multi_occurrence'));
$this->assertRaw('The count is 1.');
$this->assertNoRaw('The count is 2.');
$this->assertNoRaw('The count is 3.');
}
protected function assertBigPipeResponseHeadersPresent() { protected function assertBigPipeResponseHeadersPresent() {
$this->pass('Verifying BigPipe response headers…', 'Debug'); $this->pass('Verifying BigPipe response headers…', 'Debug');
$this->assertTrue(FALSE !== strpos($this->drupalGetHeader('Cache-Control'), 'private'), 'Cache-Control header set to "private".'); $this->assertTrue(FALSE !== strpos($this->drupalGetHeader('Cache-Control'), 'private'), 'Cache-Control header set to "private".');

View file

@ -15,3 +15,12 @@ no_big_pipe:
_no_big_pipe: TRUE _no_big_pipe: TRUE
requirements: requirements:
_access: 'TRUE' _access: 'TRUE'
big_pipe_test_multi_occurrence:
path: '/big_pipe_test_multi_occurrence'
defaults:
_controller: '\Drupal\big_pipe_test\BigPipeTestController::multiOccurrence'
_title: 'BigPipe test multiple occurrences of the same placeholder'
requirements:
_access: 'TRUE'

View file

@ -52,6 +52,30 @@ class BigPipeTestController {
return ['#markup' => '<p>Nope.</p>']; return ['#markup' => '<p>Nope.</p>'];
} }
/**
* A page with multiple occurrences of the same placeholder.
*
* @see \Drupal\big_pipe\Tests\BigPipeTest::testBigPipeMultipleOccurrencePlaceholders()
*
* @return array
*/
public function multiOccurrence() {
return [
'item1' => [
'#lazy_builder' => [static::class . '::counter', []],
'#create_placeholder' => TRUE,
],
'item2' => [
'#lazy_builder' => [static::class . '::counter', []],
'#create_placeholder' => TRUE,
],
'item3' => [
'#lazy_builder' => [static::class . '::counter', []],
'#create_placeholder' => TRUE,
],
];
}
/** /**
* #lazy_builder callback; builds <time> markup with current time. * #lazy_builder callback; builds <time> markup with current time.
* *
@ -98,4 +122,31 @@ class BigPipeTestController {
return ['#plain_text' => BigPipeTestSubscriber::CONTENT_TRIGGER_EXCEPTION]; return ['#plain_text' => BigPipeTestSubscriber::CONTENT_TRIGGER_EXCEPTION];
} }
/**
* #lazy_builder callback; returns the current count.
*
* @see \Drupal\big_pipe\Tests\BigPipeTest::testBigPipeMultipleOccurrencePlaceholders()
*
* @return array
* The render array.
*/
public static function counter() {
// Lazy builders are not allowed to build their own state like this function
// does, but in this case we're intentionally doing that for testing
// purposes: so we can ensure that each lazy builder is only ever called
// once with the same parameters.
static $count;
if (!isset($count)) {
$count = 0;
}
$count++;
return [
'#markup' => BigPipeMarkup::create("<p>The count is $count.</p>"),
'#cache' => ['max-age' => 0],
];
}
} }

View file

@ -0,0 +1,117 @@
<?php
namespace Drupal\Tests\big_pipe\FunctionalJavascript;
use Drupal\comment\CommentInterface;
use Drupal\comment\Entity\Comment;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\editor\Entity\Editor;
use Drupal\filter\Entity\FilterFormat;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\simpletest\ContentTypeCreationTrait;
use Drupal\simpletest\NodeCreationTrait;
/**
* BigPipe regression tests.
*
* @group big_pipe
*/
class BigPipeRegressionTest extends JavascriptTestBase {
use CommentTestTrait;
use ContentTypeCreationTrait;
use NodeCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'node',
'comment',
'big_pipe',
'history',
'editor',
'ckeditor',
'filter',
];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Use the big_pipe_test_theme theme.
$this->container->get('theme_installer')->install(['big_pipe_test_theme']);
$this->container->get('config.factory')->getEditable('system.theme')->set('default', 'big_pipe_test_theme')->save();
}
/**
* Ensure comment form works with history and big_pipe modules.
*
* @see https://www.drupal.org/node/2698811
*/
public function testCommentForm_2698811() {
// Ensure an `article` node type exists.
$this->createContentType(['type' => 'article']);
$this->addDefaultCommentField('node', 'article');
// Enable CKEditor.
$format = $this->randomMachineName();
FilterFormat::create([
'format' => $format,
'name' => $this->randomString(),
'weight' => 1,
'filters' => [],
])->save();
$settings['toolbar']['rows'] = [
[
[
'name' => 'Links',
'items' => [
'DrupalLink',
'DrupalUnlink',
],
],
],
];
$editor = Editor::create([
'format' => $format,
'editor' => 'ckeditor',
]);
$editor->setSettings($settings);
$editor->save();
$admin_user = $this->drupalCreateUser([
'access comments',
'post comments',
'use text format ' . $format,
]);
$this->drupalLogin($admin_user);
$node = $this->createNode([
'type' => 'article',
'comment' => CommentItemInterface::OPEN,
]);
// Create some comments.
foreach (range(1, 5) as $i) {
$comment = Comment::create([
'status' => CommentInterface::PUBLISHED,
'field_name' => 'comment',
'entity_type' => 'node',
'entity_id' => $node->id(),
]);
$comment->save();
}
$this->drupalGet($node->toUrl()->toString());
// Confirm that CKEditor loaded.
$javascript = <<<JS
(function(){
return Object.keys(CKEDITOR.instances).length > 0;
}());
JS;
$this->assertJsCondition($javascript);
}
}

View file

@ -0,0 +1,5 @@
name: 'BigPipe test theme'
type: theme
description: 'Theme for testing BigPipe edge cases.'
version: VERSION
core: 8.x

View file

@ -0,0 +1,13 @@
{#
/**
* @file
* Test that comments still work with the form above instead of below.
*
* @see \Drupal\Tests\big_pipe\FunctionalJavascript\BigPipeRegressionTest::testCommentForm_2698811()
*/
#}
<section{{ attributes }}>
{{ comment_form }}
{{ comments }}
</section>

View file

@ -19,6 +19,9 @@ process:
- module - module
- delta - delta
delimiter: _ delimiter: _
-
plugin: machine_name
field: id
plugin: plugin:
- -
plugin: static_map plugin: static_map

View file

@ -291,7 +291,6 @@ class Block extends ConfigEntityBase implements BlockInterface, EntityWithPlugin
* The condition plugin manager. * The condition plugin manager.
*/ */
protected function conditionPluginManager() { protected function conditionPluginManager() {
$this->conditionPluginManager;
if (!isset($this->conditionPluginManager)) { if (!isset($this->conditionPluginManager)) {
$this->conditionPluginManager = \Drupal::service('plugin.manager.condition'); $this->conditionPluginManager = \Drupal::service('plugin.manager.condition');
} }

View file

@ -43,6 +43,13 @@ class Block extends DrupalSqlBase {
*/ */
protected $blockRoleTable; protected $blockRoleTable;
/**
* Table listing user roles.
*
* @var string
*/
protected $userRoleTable;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -55,6 +62,9 @@ class Block extends DrupalSqlBase {
$this->blockTable = 'blocks'; $this->blockTable = 'blocks';
$this->blockRoleTable = 'blocks_roles'; $this->blockRoleTable = 'blocks_roles';
} }
// Drupal 6 & 7 both use the same name for the user roles table.
$this->userRoleTable = 'role';
return $this->select($this->blockTable, 'b')->fields('b'); return $this->select($this->blockTable, 'b')->fields('b');
} }
@ -106,11 +116,12 @@ class Block extends DrupalSqlBase {
$module = $row->getSourceProperty('module'); $module = $row->getSourceProperty('module');
$delta = $row->getSourceProperty('delta'); $delta = $row->getSourceProperty('delta');
$roles = $this->select($this->blockRoleTable, 'br') $query = $this->select($this->blockRoleTable, 'br')
->fields('br', array('rid')) ->fields('br', array('rid'))
->condition('module', $module) ->condition('module', $module)
->condition('delta', $delta) ->condition('delta', $delta);
->execute() $query->join($this->userRoleTable, 'ur', 'br.rid = ur.rid');
$roles = $query->execute()
->fetchCol(); ->fetchCol();
$row->setSourceProperty('roles', $roles); $row->setSourceProperty('roles', $roles);

View file

@ -108,7 +108,7 @@ class MigrateBlockTest extends MigrateDrupal7TestBase {
$this->assertEntity('bartik_system_main', 'system_main_block', [], '', 'content', 'bartik', 0, '', '0'); $this->assertEntity('bartik_system_main', 'system_main_block', [], '', 'content', 'bartik', 0, '', '0');
$this->assertEntity('bartik_search_form', 'search_form_block', [], '', 'sidebar_first', 'bartik', -1, '', '0'); $this->assertEntity('bartik_search_form', 'search_form_block', [], '', 'sidebar_first', 'bartik', -1, '', '0');
$this->assertEntity('bartik_user_login', 'user_login_block', [], '', 'sidebar_first', 'bartik', 0, '', '0'); $this->assertEntity('bartik_user_login', 'user_login_block', [], '', 'sidebar_first', 'bartik', 0, '', '0');
$this->assertEntity('bartik_system_powered-by', 'system_powered_by_block', [], '', 'footer', 'bartik', 10, '', '0'); $this->assertEntity('bartik_system_powered_by', 'system_powered_by_block', [], '', 'footer', 'bartik', 10, '', '0');
$this->assertEntity('seven_system_main', 'system_main_block', [], '', 'content', 'seven', 0, '', '0'); $this->assertEntity('seven_system_main', 'system_main_block', [], '', 'content', 'seven', 0, '', '0');
$this->assertEntity('seven_user_login', 'user_login_block', [], '', 'content', 'seven', 10, '', '0'); $this->assertEntity('seven_user_login', 'user_login_block', [], '', 'content', 'seven', 10, '', '0');

View file

@ -25,6 +25,39 @@ class BlockTest extends MigrateSqlSourceTestCase {
* Sample block instance query results from the source. * Sample block instance query results from the source.
*/ */
protected $expectedResults = array( protected $expectedResults = array(
array(
'bid' => 1,
'module' => 'block',
'delta' => '1',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'left',
'visibility' => 0,
'pages' => '',
'title' => 'Test Title 01',
'cache' => -1,
'roles' => [2]
),
array(
'bid' => 2,
'module' => 'block',
'delta' => '2',
'theme' => 'garland',
'status' => 1,
'weight' => 5,
'region' => 'right',
'visibility' => 0,
'pages' => '<front>',
'title' => 'Test Title 02',
'cache' => -1,
'roles' => [2]
),
);
/**
* Sample block table.
*/
protected $expectedBlocks = array(
array( array(
'bid' => 1, 'bid' => 1,
'module' => 'block', 'module' => 'block',
@ -62,14 +95,35 @@ class BlockTest extends MigrateSqlSourceTestCase {
'delta' => 1, 'delta' => 1,
'rid' => 2, 'rid' => 2,
), ),
array(
'module' => 'block',
'delta' => 2,
'rid' => 2,
),
array(
'module' => 'block',
'delta' => 2,
'rid' => 100,
),
);
/**
* Sample role table.
*/
protected $expectedRole = array(
array(
'rid' => 2,
'name' => 'authenticated user',
),
); );
/** /**
* Prepopulate database contents. * Prepopulate database contents.
*/ */
protected function setUp() { protected function setUp() {
$this->databaseContents['blocks'] = $this->expectedResults; $this->databaseContents['blocks'] = $this->expectedBlocks;
$this->databaseContents['blocks_roles'] = $this->expectedBlocksRoles; $this->databaseContents['blocks_roles'] = $this->expectedBlocksRoles;
$this->databaseContents['role'] = $this->expectedRole;
$this->databaseContents['system'] = array( $this->databaseContents['system'] = array(
array( array(
'filename' => 'modules/system/system.module', 'filename' => 'modules/system/system.module',

View file

@ -392,7 +392,7 @@ display:
plugin_id: string plugin_id: string
type: type:
id: type id: type
table: block_content table: block_content_field_data
field: type field: type
relationship: none relationship: none
group_type: group group_type: group

View file

@ -4,11 +4,8 @@ migration_tags:
- Drupal 6 - Drupal 6
source: source:
plugin: d6_box plugin: d6_box
constants:
type: basic
process: process:
id: bid id: bid
type: 'constants/type'
info: info info: info
'body/format': 'body/format':
plugin: migration plugin: migration
@ -17,6 +14,7 @@ process:
'body/value': body 'body/value': body
destination: destination:
plugin: entity:block_content plugin: entity:block_content
default_bundle: basic
no_stub: true no_stub: true
migration_dependencies: migration_dependencies:
required: required:

View file

@ -4,11 +4,8 @@ migration_tags:
- Drupal 7 - Drupal 7
source: source:
plugin: d7_block_custom plugin: d7_block_custom
constants:
type: basic
process: process:
id: bid id: bid
type: 'constants/type'
info: info info: info
'body/format': 'body/format':
plugin: migration plugin: migration
@ -17,6 +14,7 @@ process:
'body/value': body 'body/value': body
destination: destination:
plugin: entity:block_content plugin: entity:block_content
default_bundle: basic
no_stub: true no_stub: true
migration_dependencies: migration_dependencies:
required: required:

View file

@ -19,7 +19,7 @@ class Box extends DrupalSqlBase {
public function query() { public function query() {
$query = $this->select('boxes', 'b') $query = $this->select('boxes', 'b')
->fields('b', array('bid', 'body', 'info', 'format')); ->fields('b', array('bid', 'body', 'info', 'format'));
$query->orderBy('bid'); $query->orderBy('b.bid');
return $query; return $query;
} }

View file

@ -28,6 +28,10 @@ class BlockContentListViewsTest extends BlockContentTestBase {
// Test for the page title. // Test for the page title.
$this->assertTitle(t('Custom block library') . ' | Drupal'); $this->assertTitle(t('Custom block library') . ' | Drupal');
// Test for the exposed filters.
$this->assertFieldByName('info');
$this->assertFieldByName('type');
// Test for the table. // Test for the table.
$element = $this->xpath('//div[@class="layout-content"]//table'); $element = $this->xpath('//div[@class="layout-content"]//table');
$this->assertTrue($element, 'Views table found.'); $this->assertTrue($element, 'Views table found.');

View file

@ -18,6 +18,8 @@ interface BookManagerInterface {
* Since this can be the full tree including hidden items, the data returned * Since this can be the full tree including hidden items, the data returned
* may be used for generating an an admin interface or a select. * may be used for generating an an admin interface or a select.
* *
* Note: based on menu_tree_all_data().
*
* @param int $bid * @param int $bid
* The Book ID to find links for. * The Book ID to find links for.
* @param array|null $link * @param array|null $link
@ -31,8 +33,6 @@ interface BookManagerInterface {
* *
* @return array * @return array
* An tree of menu links in an array, in the order they should be rendered. * An tree of menu links in an array, in the order they should be rendered.
*
* Note: based on menu_tree_all_data().
*/ */
public function bookTreeAllData($bid, $link = NULL, $max_depth = NULL); public function bookTreeAllData($bid, $link = NULL, $max_depth = NULL);

View file

@ -23,7 +23,7 @@ class Book extends DrupalSqlBase {
for ($i = 1; $i <= 9; $i++) { for ($i = 1; $i <= 9; $i++) {
$field = "p$i"; $field = "p$i";
$ml_fields[] = $field; $ml_fields[] = $field;
$query->orderBy($field); $query->orderBy('ml.' . $field);
} }
$query->fields('ml', $ml_fields); $query->fields('ml', $ml_fields);
return $query; return $query;

View file

@ -30,7 +30,7 @@ class CommentType extends DrupalSqlBase {
return $this->select('field_config_instance', 'fci') return $this->select('field_config_instance', 'fci')
->distinct() ->distinct()
->fields('fci', array('bundle')) ->fields('fci', array('bundle'))
->condition('entity_type', 'comment'); ->condition('fci.entity_type', 'comment');
} }
/** /**

View file

@ -0,0 +1,14 @@
id: d6_i18n_system_maintenance
label: Maintenance page configuration
migration_tags:
- Drupal 6
source:
plugin: i18n_variable
variables:
- site_offline_message
process:
langcode: language
message: site_offline_message
destination:
plugin: config
config_name: system.maintenance

View file

@ -0,0 +1,38 @@
id: d6_i18n_system_site
label: Site configuration
migration_tags:
- Drupal 6
source:
plugin: i18n_variable
constants:
slash: '/'
variables:
- site_name
- site_mail
- site_slogan
- site_frontpage
- site_403
- site_404
process:
langcode: language
name: site_name
mail: site_mail
slogan: site_slogan
'page/front':
plugin: concat
source:
- constants/slash
- site_frontpage
'page/403':
plugin: concat
source:
- constants/slash
- site_403
'page/404':
plugin: concat
source:
- constants/slash
- site_404
destination:
plugin: config
config_name: system.site

View file

@ -0,0 +1,68 @@
id: d6_i18n_user_mail
label: User mail configuration
migration_tags:
- Drupal 6
source:
plugin: i18n_variable
variables:
- user_mail_status_activated_subject
- user_mail_status_activated_body
- user_mail_password_reset_subject
- user_mail_password_reset_body
- user_mail_status_deleted_subject
- user_mail_status_deleted_body
- user_mail_register_admin_created_subject
- user_mail_register_admin_created_body
- user_mail_register_no_approval_required_subject
- user_mail_register_no_approval_required_body
- user_mail_register_pending_approval_subject
- user_mail_register_pending_approval_body
- user_mail_status_blocked_subject
- user_mail_status_blocked_body
process:
langcode: language
'status_activated/subject':
plugin: convert_tokens
source: user_mail_status_activated_subject
'status_activated/body':
plugin: convert_tokens
source: user_mail_status_activated_body
'password_reset/subject':
plugin: convert_tokens
source: user_mail_password_reset_subject
'password_reset/body':
plugin: convert_tokens
source: user_mail_password_reset_body
'cancel_confirm/subject':
plugin: convert_tokens
source: user_mail_status_deleted_subject
'cancel_confirm/body':
plugin: convert_tokens
source: user_mail_status_deleted_body
'register_admin_created/subject':
plugin: convert_tokens
source: user_mail_register_admin_created_subject
'register_admin_created/body':
plugin: convert_tokens
source: user_mail_register_admin_created_body
'register_no_approval_required/subject':
plugin: convert_tokens
source: user_mail_register_no_approval_required_subject
'register_no_approval_required/body':
plugin: convert_tokens
source: user_mail_register_no_approval_required_body
'register_pending_approval/subject':
plugin: convert_tokens
source: user_mail_register_pending_approval_subject
'register_pending_approval/body':
plugin: convert_tokens
source: user_mail_register_pending_approval_body
'status_blocked/subject':
plugin: convert_tokens
source: user_mail_status_blocked_subject
'status_blocked/body':
plugin: convert_tokens
source: user_mail_status_blocked_body
destination:
plugin: config
config_name: user.mail

View file

@ -0,0 +1,29 @@
id: d6_i18n_user_settings
label: User configuration
migration_tags:
- Drupal 6
source:
plugin: i18n_variable
variables:
- user_mail_status_blocked_notify
- user_mail_status_activated_notify
- user_email_verification
- user_register
- anonymous
process:
langcode: language
'notify/status_blocked': user_mail_status_blocked_notify
'notify/status_activated': user_mail_status_activated_notify
verify_mail: user_email_verification
register:
plugin: static_map
source: user_register
default_value: visitors_admin_approval
map:
2: visitors_admin_approval
1: visitors
0: admin_only
anonymous: anonymous
destination:
plugin: config
config_name: user.settings

View file

@ -0,0 +1,32 @@
<?php
namespace Drupal\Tests\config_translation\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade i18n maintenance variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateI18nSystemMaintenanceTest extends MigrateDrupal6TestBase {
public static $modules = ['language', 'config_translation'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_i18n_system_maintenance');
}
/**
* Tests migration of system (maintenance) variables to system.maintenance.yml.
*/
public function testSystemMaintenance() {
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('fr', 'system.maintenance');
$this->assertIdentical('fr - Drupal is currently under maintenance. We should be back shortly. Thank you for your patience.', $config->get('message'));
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Drupal\Tests\config_translation\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade i18n_strings site variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateI18nSystemSiteTest extends MigrateDrupal6TestBase {
public static $modules = ['language', 'config_translation'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_i18n_system_site');
}
/**
* Tests migration of system (site) variables to system.site.yml.
*/
public function testSystemSite() {
$config_translation = \Drupal::service('language_manager')->getLanguageConfigOverride('fr', 'system.site');
$this->assertIdentical('fr site name', $config_translation->get('name'));
$this->assertIdentical('fr_site_mail@example.com', $config_translation->get('mail'));
$this->assertIdentical('fr Migrate rocks', $config_translation->get('slogan'));
$this->assertIdentical('/fr-user', $config_translation->get('page.403'));
$this->assertIdentical('/fr-page-not-found', $config_translation->get('page.404'));
$this->assertIdentical('/node', $config_translation->get('page.front'));
$this->assertIdentical(NULL, $config_translation->get('admin_compact_mode'));
$config_translation = \Drupal::service('language_manager')->getLanguageConfigOverride('zu', 'system.site');
$this->assertIdentical('zu - site_name', $config_translation->get('name'));
$this->assertIdentical('site_mail@example.com', $config_translation->get('mail'));
$this->assertIdentical('Migrate rocks', $config_translation->get('slogan'));
$this->assertIdentical('/zu-user', $config_translation->get('page.403'));
$this->assertIdentical('/zu-page-not-found', $config_translation->get('page.404'));
$this->assertIdentical('/node', $config_translation->get('page.front'));
$this->assertIdentical(NULL, $config_translation->get('admin_compact_mode'));
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace Drupal\Tests\config_translation\Kernel\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade i18n variables to user.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateI18nUserConfigsTest extends MigrateDrupal6TestBase {
use SchemaCheckTestTrait;
public static $modules = ['language', 'locale', 'config_translation'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('locale',
['locales_source', 'locales_target', 'locales_location']);
$this->executeMigrations(['d6_i18n_user_mail', 'd6_i18n_user_settings']);
}
/**
* Tests migration of i18n user variables to user.mail.yml.
*/
public function testUserMail() {
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('fr', 'user.mail');
$this->assertIdentical('fr - Account details for [user:name] at [site:name] (approved)', $config->get('status_activated.subject'));
$this->assertIdentical("fr - [user:name],\r\n\r\nYour account at [site:name] has been activated.\r\n\r\nYou may now log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\r\n\r\nOnce you have set your own password, you will be able to log in to [site:login-url] in the future using:\r\n\r\nusername: [user:name]\r\n", $config->get('status_activated.body'));
$this->assertIdentical('fr - Replacement login information for [user:name] at [site:name]', $config->get('password_reset.subject'));
$this->assertIdentical("fr - [user:name],\r\n\r\nA request to reset the password for your account has been made at [site:name].\r\n\r\nYou may now log in to [site:url-brief] by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.", $config->get('password_reset.body'));
$this->assertIdentical('fr - Account details for [user:name] at [site:name] (deleted)', $config->get('cancel_confirm.subject'));
$this->assertIdentical("fr - [user:name],\r\n\r\nYour account on [site:name] has been deleted.", $config->get('cancel_confirm.body'));
$this->assertIdentical('fr - An administrator created an account for you at [site:name]', $config->get('register_admin_created.subject'));
$this->assertIdentical("fr - [user:name],\r\n\r\nA site administrator at [site:name] has created an account for you. You may now log in to [site:login-url] using the following username and password:\r\n\r\nusername: [user:name]\r\npassword: \r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\r\n\r\n\r\n-- [site:name] team", $config->get('register_admin_created.body'));
$this->assertIdentical('fr - Account details for [user:name] at [site:name]', $config->get('register_no_approval_required.subject'));
$this->assertIdentical("fr - [user:name],\r\n\r\nThank you for registering at [site:name]. You may now log in to [site:login-url] using the following username and password:\r\n\r\nusername: [user:name]\r\npassword: \r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\r\n\r\n\r\n-- [site:name] team", $config->get('register_no_approval_required.body'));
$this->assertIdentical('fr - Account details for [user:name] at [site:name] (pending admin approval)', $config->get('register_pending_approval.subject'));
$this->assertIdentical("fr - [user:name],\r\n\r\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\r\n\r\n\r\n-- [site:name] team", $config->get('register_pending_approval.body'));
$this->assertIdentical('fr - Account details for [user:name] at [site:name] (blocked)', $config->get('status_blocked.subject'));
$this->assertIdentical("fr - [user:name],\r\n\r\nYour account on [site:name] has been blocked.", $config->get('status_blocked.body'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'user.mail', $config->get());
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('zu', 'user.mail');
$this->assertIdentical('zu - An administrator created an account for you at [site:name]', $config->get('register_admin_created.subject'));
$this->assertIdentical("zu - [user:name],\r\n\r\nA site administrator at [site:name] has created an account for you. You may now log in to [site:login-url] using the following username and password:\r\n\r\nusername: [user:name]\r\npassword: \r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\r\n\r\n\r\n-- [site:name] team", $config->get('register_admin_created.body'));
}
/**
* Tests migration of i18n user variables to user.settings.yml.
*/
public function testUserSettings() {
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('fr', 'user.settings');
$this->assertIdentical(1, $config->get('notify.status_blocked'));
$this->assertIdentical(0, $config->get('notify.status_activated'));
$this->assertIdentical(0, $config->get('verify_mail'));
$this->assertIdentical('admin_only', $config->get('register'));
$this->assertIdentical('fr Guest', $config->get('anonymous'));
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('zu', 'user.settings');
$this->assertIdentical(1, $config->get('notify.status_blocked'));
$this->assertIdentical(0, $config->get('notify.status_activated'));
$this->assertIdentical(0, $config->get('verify_mail'));
$this->assertIdentical('admin_only', $config->get('register'));
$this->assertIdentical('Guest', $config->get('anonymous'));
}
}

View file

@ -21,7 +21,7 @@ interface ContactFormInterface extends ConfigEntityInterface {
* Returns an auto-reply message to send to the message author. * Returns an auto-reply message to send to the message author.
* *
* @return string * @return string
* An auto-reply message * An auto-reply message
*/ */
public function getReply(); public function getReply();

View file

@ -29,7 +29,7 @@ class ContactCategory extends DrupalSqlBase {
'selected', 'selected',
) )
); );
$query->orderBy('cid'); $query->orderBy('c.cid');
return $query; return $query;
} }

View file

@ -18,7 +18,7 @@ class ContactSettings extends Variable {
protected function initializeIterator() { protected function initializeIterator() {
$default_category = $this->select('contact', 'c') $default_category = $this->select('contact', 'c')
->fields('c', ['cid']) ->fields('c', ['cid'])
->condition('selected', 1) ->condition('c.selected', 1)
->execute() ->execute()
->fetchField(); ->fetchField();
return new \ArrayIterator([$this->values() + ['default_category' => $default_category]]); return new \ArrayIterator([$this->values() + ['default_category' => $default_category]]);

View file

@ -77,8 +77,8 @@ class ContentTranslationController extends ControllerBase {
* The route match. * The route match.
* @param string $entity_type_id * @param string $entity_type_id
* (optional) The entity type ID. * (optional) The entity type ID.
* @return array Array of page elements to render. * @return array
* Array of page elements to render. * Array of page elements to render.
*/ */
public function overview(RouteMatchInterface $route_match, $entity_type_id = NULL) { public function overview(RouteMatchInterface $route_match, $entity_type_id = NULL) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */

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