Update to Drupal 8.0.6. For more information, see https://www.drupal.org/drupal-8.0.6-release-notes
This commit is contained in:
parent
4297c64508
commit
b11a755ba8
159 changed files with 2340 additions and 543 deletions
|
@ -349,4 +349,26 @@ class NestedArray {
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a nested array recursively.
|
||||
*
|
||||
* @param array $array
|
||||
* The filtered nested array.
|
||||
* @param callable|NULL $callable
|
||||
* The callable to apply for filtering.
|
||||
*
|
||||
* @return array
|
||||
* The filtered array.
|
||||
*/
|
||||
public static function filter(array $array, callable $callable = NULL) {
|
||||
$array = is_callable($callable) ? array_filter($array, $callable) : array_filter($array);
|
||||
foreach ($array as &$element) {
|
||||
if (is_array($element)) {
|
||||
$element = static::filter($element, $callable);
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,11 +10,10 @@ namespace Drupal\Component\Uuid;
|
|||
use Drupal\Component\Utility\Crypt;
|
||||
|
||||
/**
|
||||
* Generates a UUID v4 using PHP code.
|
||||
* Generates a UUID v4 (RFC 4122 section 4.4) using PHP code.
|
||||
*
|
||||
* Loosely based on Ruby's UUIDTools generate_random logic.
|
||||
*
|
||||
* @see http://uuidtools.rubyforge.org/api/classes/UUIDTools/UUID.html
|
||||
* @see http://www.rfc-editor.org/rfc/rfc4122.txt
|
||||
* @see http://www.rfc-editor.org/errata_search.php?rfc=4122&eid=3546
|
||||
*/
|
||||
class Php implements UuidInterface {
|
||||
|
||||
|
@ -22,27 +21,39 @@ class Php implements UuidInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function generate() {
|
||||
$hex = substr(hash('sha256', Crypt::randomBytes(16)), 0, 32);
|
||||
// Obtain a random string of 32 hex characters.
|
||||
$hex = bin2hex(Crypt::randomBytes(16));
|
||||
|
||||
// The field names refer to RFC 4122 section 4.1.2.
|
||||
// The variable names $time_low, $time_mid, $time_hi_and_version,
|
||||
// $clock_seq_hi_and_reserved, $clock_seq_low, and $node correlate to
|
||||
// the fields defined in RFC 4122 section 4.1.2.
|
||||
//
|
||||
// Use characters 0-11 to generate 32-bit $time_low and 16-bit $time_mid.
|
||||
$time_low = substr($hex, 0, 8);
|
||||
$time_mid = substr($hex, 8, 4);
|
||||
|
||||
$time_hi_and_version = base_convert(substr($hex, 12, 4), 16, 10);
|
||||
$time_hi_and_version &= 0x0FFF;
|
||||
$time_hi_and_version |= (4 << 12);
|
||||
// Use characters 12-15 to generate 16-bit $time_hi_and_version.
|
||||
// The 4 most significant bits are the version number (0100 == 0x4).
|
||||
// We simply skip character 12 from $hex, and concatenate the strings.
|
||||
$time_hi_and_version = '4' . substr($hex, 13, 3);
|
||||
|
||||
$clock_seq_hi_and_reserved = base_convert(substr($hex, 16, 4), 16, 10);
|
||||
$clock_seq_hi_and_reserved &= 0x3F;
|
||||
$clock_seq_hi_and_reserved |= 0x80;
|
||||
// Use characters 16-17 to generate 8-bit $clock_seq_hi_and_reserved.
|
||||
// The 2 most significant bits are set to one and zero respectively.
|
||||
$clock_seq_hi_and_reserved = base_convert(substr($hex, 16, 2), 16, 10);
|
||||
$clock_seq_hi_and_reserved &= 0b00111111;
|
||||
$clock_seq_hi_and_reserved |= 0b10000000;
|
||||
|
||||
$clock_seq_low = substr($hex, 20, 2);
|
||||
$nodes = substr($hex, 20);
|
||||
// Use characters 18-19 to generate 8-bit $clock_seq_low.
|
||||
$clock_seq_low = substr($hex, 18, 2);
|
||||
// Use characters 20-31 to generate 48-bit $node.
|
||||
$node = substr($hex, 20);
|
||||
|
||||
$uuid = sprintf('%s-%s-%04x-%02x%02x-%s',
|
||||
$time_low, $time_mid,
|
||||
$time_hi_and_version, $clock_seq_hi_and_reserved,
|
||||
$clock_seq_low, $nodes);
|
||||
// Re-combine as a UUID. $clock_seq_hi_and_reserved is still an integer.
|
||||
$uuid = sprintf('%s-%s-%s-%02x%s-%s',
|
||||
$time_low, $time_mid, $time_hi_and_version,
|
||||
$clock_seq_hi_and_reserved, $clock_seq_low,
|
||||
$node
|
||||
);
|
||||
|
||||
return $uuid;
|
||||
}
|
||||
|
|
|
@ -142,7 +142,9 @@ EOT;
|
|||
/**
|
||||
* Remove possibly problematic test files from vendored projects.
|
||||
*
|
||||
* @param \Composer\Script\Event $event
|
||||
* @param \Composer\Installer\PackageEvent $event
|
||||
* A PackageEvent object to get the configured composer vendor directories
|
||||
* from.
|
||||
*/
|
||||
public static function vendorTestCodeCleanup(PackageEvent $event) {
|
||||
$vendor_dir = $event->getComposer()->getConfig()->get('vendor-dir');
|
||||
|
@ -162,9 +164,6 @@ EOT;
|
|||
throw new \RuntimeException(sprintf("Failure removing directory '%s' in package '%s'.", $path, $package->getPrettyName()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new \RuntimeException(sprintf("The directory '%s' in package '%s' does not exist.", $path, $package->getPrettyName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,24 +190,35 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
});
|
||||
|
||||
$all_config = array_merge($existing_config, $list);
|
||||
$all_config = array_combine($all_config, $all_config);
|
||||
$config_to_create = $storage->readMultiple($list);
|
||||
// Check to see if the corresponding override storage has any overrides or
|
||||
// new configuration that can be installed.
|
||||
if ($profile_storage) {
|
||||
$config_to_create = $profile_storage->readMultiple($list) + $config_to_create;
|
||||
}
|
||||
// Sort $config_to_create in the order of the least dependent first.
|
||||
$dependency_manager = new ConfigDependencyManager();
|
||||
$dependency_manager->setData($config_to_create);
|
||||
$config_to_create = array_merge(array_flip($dependency_manager->sortAll()), $config_to_create);
|
||||
|
||||
foreach ($config_to_create as $config_name => $data) {
|
||||
// Exclude configuration where its dependencies cannot be met.
|
||||
if (!$this->validateDependencies($config_name, $data, $enabled_extensions, $all_config)) {
|
||||
unset($config_to_create[$config_name]);
|
||||
}
|
||||
// Exclude configuration that does not have a matching dependency.
|
||||
elseif (!empty($dependency)) {
|
||||
// Remove configuration where its dependencies cannot be met.
|
||||
$remove = !$this->validateDependencies($config_name, $data, $enabled_extensions, $all_config);
|
||||
// If $dependency is defined, remove configuration that does not have a
|
||||
// matching dependency.
|
||||
if (!$remove && !empty($dependency)) {
|
||||
// Create a light weight dependency object to check dependencies.
|
||||
$config_entity = new ConfigEntityDependency($config_name, $data);
|
||||
if (!$config_entity->hasDependency(key($dependency), reset($dependency))) {
|
||||
unset($config_to_create[$config_name]);
|
||||
}
|
||||
$remove = !$config_entity->hasDependency(key($dependency), reset($dependency));
|
||||
}
|
||||
|
||||
if ($remove) {
|
||||
// Remove from the list of configuration to create.
|
||||
unset($config_to_create[$config_name]);
|
||||
// Remove from the list of all configuration. This ensures that any
|
||||
// configuration that depends on this configuration is also removed.
|
||||
unset($all_config[$config_name]);
|
||||
}
|
||||
}
|
||||
if (!empty($config_to_create)) {
|
||||
|
|
|
@ -18,6 +18,8 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
|
|||
* Implement the container injection pattern of
|
||||
* \Drupal\Core\Entity\EntityHandlerInterface::createInstance() to obtain the
|
||||
* module handler service for your class.
|
||||
*
|
||||
* @ingroup entity_api
|
||||
*/
|
||||
abstract class EntityHandlerBase {
|
||||
use StringTranslationTrait;
|
||||
|
|
|
@ -14,6 +14,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
*
|
||||
* This interface can be implemented by entity handlers that require
|
||||
* dependency injection.
|
||||
*
|
||||
* @ingroup entity_api
|
||||
*/
|
||||
interface EntityHandlerInterface {
|
||||
|
||||
|
|
|
@ -277,8 +277,8 @@ use Drupal\node\Entity\NodeType;
|
|||
* content entity type that uses bundles, the 'bundle_label' annotation gives
|
||||
* the human-readable name to use for a bundle of this entity type (for
|
||||
* example, "Content type" for the Node entity).
|
||||
* - The annotation will refer to several controller classes, which you will
|
||||
* also need to define:
|
||||
* - The annotation will refer to several handler classes, which you will also
|
||||
* need to define:
|
||||
* - list_builder: Define a class that extends
|
||||
* \Drupal\Core\Config\Entity\ConfigEntityListBuilder (for configuration
|
||||
* entities) or \Drupal\Core\Entity\EntityListBuilder (for content
|
||||
|
@ -298,7 +298,7 @@ use Drupal\node\Entity\NodeType;
|
|||
* annotation has value TRUE), define a class that extends
|
||||
* \Drupal\content_translation\ContentTranslationHandler, to translate
|
||||
* the content. Configuration translation is handled automatically by the
|
||||
* Configuration Translation module, without the need of a controller class.
|
||||
* Configuration Translation module, without the need of a handler class.
|
||||
* - access: If your configuration entity has complex permissions, you might
|
||||
* need an access control handling, implementing
|
||||
* \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most entities
|
||||
|
@ -381,10 +381,10 @@ use Drupal\node\Entity\NodeType;
|
|||
* an object to the controller for the route.
|
||||
* - defaults: For entity form routes, use _entity_form rather than the generic
|
||||
* _controller or _form. The value is composed of the entity type machine name
|
||||
* and a form controller type from the entity annotation (see @ref define
|
||||
* above more more on controllers and annotation). So, in this example,
|
||||
* block.default refers to the 'default' form controller on the block entity
|
||||
* type, whose annotation contains:
|
||||
* and a form handler type from the entity annotation (see @ref define above
|
||||
* more more on handlers and annotation). So, in this example, block.default
|
||||
* refers to the 'default' form handler on the block entity type, whose
|
||||
* annotation contains:
|
||||
* @code
|
||||
* handlers = {
|
||||
* "form" = {
|
||||
|
|
|
@ -30,11 +30,6 @@ trait ConfigFormBaseTrait {
|
|||
/**
|
||||
* Retrieves a configuration object.
|
||||
*
|
||||
* Objects that use the trait need to implement the
|
||||
* \Drupal\Core\Form\ConfigFormBaseTrait::getEditableConfigNames() to declare
|
||||
* which configuration objects this method returns override free and mutable.
|
||||
* This ensures that overrides do not pollute saved configuration.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the configuration object to retrieve. The name corresponds to
|
||||
* a configuration file. For @code \Drupal::config('book.admin') @endcode,
|
||||
|
@ -42,7 +37,9 @@ trait ConfigFormBaseTrait {
|
|||
* configuration file.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Config|\Drupal\Core\Config\ImmutableConfig
|
||||
* A configuration object.
|
||||
* An editable configuration object if the given name is listed in the
|
||||
* getEditableConfigNames() method or an immutable configuration object if
|
||||
* not.
|
||||
*/
|
||||
protected function config($name) {
|
||||
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
* @param $MULTIPLE_PARAMS
|
||||
* Additional parameters specific to the batch. These are specified in the
|
||||
* array passed to batch_set().
|
||||
* @param $context
|
||||
* @param array|\ArrayAccess $context.
|
||||
* The batch context array, passed by reference. This contains the following
|
||||
* properties:
|
||||
* - 'finished': A float number between 0 and 1 informing the processing
|
||||
|
@ -51,6 +51,8 @@
|
|||
* all operations have finished, this is passed to callback_batch_finished()
|
||||
* where results may be referenced to display information to the end-user,
|
||||
* such as how many total items were processed.
|
||||
* It is discouraged to typehint this parameter as an array, to allow an
|
||||
* object implement \ArrayAccess to be passed.
|
||||
*/
|
||||
function callback_batch_operation($MULTIPLE_PARAMS, &$context) {
|
||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
|
|
|
@ -111,8 +111,8 @@ use Drupal\Core\Language\LanguageInterface;
|
|||
* @code
|
||||
* // PHP code
|
||||
* t('May', array(), array('context' => 'Long month name');
|
||||
* format_plural($count, '1 something', '@count somethings',
|
||||
* array(), array('context' => 'My context'));
|
||||
* \Drupal::translation()->formatPlural($count, '1 something',
|
||||
* '@count somethings', array(), array('context' => 'My context'));
|
||||
*
|
||||
* // JavaScript code
|
||||
* Drupal.t('May', {}, {'context': 'Long month name'});
|
||||
|
@ -147,7 +147,6 @@ use Drupal\Core\Language\LanguageInterface;
|
|||
*
|
||||
* @see transliteration
|
||||
* @see t()
|
||||
* @see format_plural()
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ class PhpStorageFactory {
|
|||
* An instantiated storage for the specified name.
|
||||
*/
|
||||
static function get($name) {
|
||||
$configuration = array();
|
||||
$overrides = Settings::get('php_storage');
|
||||
if (isset($overrides[$name])) {
|
||||
$configuration = $overrides[$name];
|
||||
|
@ -41,13 +42,11 @@ class PhpStorageFactory {
|
|||
elseif (isset($overrides['default'])) {
|
||||
$configuration = $overrides['default'];
|
||||
}
|
||||
else {
|
||||
$configuration = array(
|
||||
'class' => 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage',
|
||||
'secret' => Settings::getHashSalt(),
|
||||
);
|
||||
}
|
||||
// Make sure all the necessary configuration values are set.
|
||||
$class = isset($configuration['class']) ? $configuration['class'] : 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage';
|
||||
if (!isset($configuration['secret'])) {
|
||||
$configuration['secret'] = Settings::getHashSalt();
|
||||
}
|
||||
if (!isset($configuration['bin'])) {
|
||||
$configuration['bin'] = $name;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ use Drupal\Core\Template\Attribute;
|
|||
* Usage example:
|
||||
* @code
|
||||
* $build['hello'] = [
|
||||
* '#type' => 'html_tag'
|
||||
* '#type' => 'html_tag',
|
||||
* '#tag' => 'p',
|
||||
* '#value' => $this->t('Hello World'),
|
||||
* ];
|
||||
|
|
|
@ -259,6 +259,7 @@ class Tableselect extends Table {
|
|||
'#return_value' => $key,
|
||||
'#default_value' => isset($value[$key]) ? $key : NULL,
|
||||
'#attributes' => $element['#attributes'],
|
||||
'#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -136,7 +136,7 @@ class VerticalTabs extends RenderElement {
|
|||
$element[$name . '__active_tab'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#default_value' => $element['#default_tab'],
|
||||
'#attributes' => array('class' => array('vertical-tabs-active-tab')),
|
||||
'#attributes' => array('class' => array('vertical-tabs__active-tab')),
|
||||
);
|
||||
// Clean up the active tab value so it's not accidentally stored in
|
||||
// settings forms.
|
||||
|
|
|
@ -324,8 +324,11 @@
|
|||
* namespace Element, and generally extend the
|
||||
* \Drupal\Core\Render\Element\FormElement base class.
|
||||
* See the @link plugin_api Plugin API topic @endlink for general information
|
||||
* on plugins, and look for classes with the RenderElement or FormElement
|
||||
* annotation to discover what render elements are available.
|
||||
* on plugins. You can search for classes with the RenderElement or FormElement
|
||||
* annotation to discover what render elements are available. API reference
|
||||
* sites (such as https://api.drupal.org) generate lists of all existing
|
||||
* elements from these classes. Look for the Elements link in the API Navigation
|
||||
* block.
|
||||
*
|
||||
* Modules can define render elements by defining an element plugin.
|
||||
*
|
||||
|
|
|
@ -35,13 +35,6 @@ class PluralTranslatableMarkup extends TranslatableMarkup {
|
|||
*/
|
||||
protected $translatedString;
|
||||
|
||||
/**
|
||||
* A bool that statically caches whether locale_get_plural() exists.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $localeEnabled;
|
||||
|
||||
/**
|
||||
* Constructs a new PluralTranslatableMarkup object.
|
||||
*
|
||||
|
@ -157,10 +150,13 @@ class PluralTranslatableMarkup extends TranslatableMarkup {
|
|||
* @return int
|
||||
*/
|
||||
protected function getPluralIndex() {
|
||||
if (!isset(static::$localeEnabled)) {
|
||||
static::$localeEnabled = function_exists('locale_get_plural');
|
||||
}
|
||||
if (function_exists('locale_get_plural')) {
|
||||
// We have to test both if the function and the service exist since in
|
||||
// certain situations it is possible that locale code might be loaded but
|
||||
// the service does not exist. For example, where the parent test site has
|
||||
// locale installed but the child site does not.
|
||||
// @todo Refactor in https://www.drupal.org/node/2660338 so this code does
|
||||
// not depend on knowing that the Locale module exists.
|
||||
if (function_exists('locale_get_plural') && \Drupal::hasService('locale.plural.formula')) {
|
||||
return locale_get_plural($this->count, $this->getOption('langcode'));
|
||||
}
|
||||
return -1;
|
||||
|
|
|
@ -60,7 +60,9 @@ trait StringTranslationTrait {
|
|||
* - 'langcode' (defaults to the current language): A language code, to
|
||||
* translate to a language other than what is used to display the page.
|
||||
* - 'context' (defaults to the empty context): The context the source
|
||||
* string belongs to.
|
||||
* string belongs to. See the
|
||||
* @link i18n Internationalization topic @endlink for more information
|
||||
* about string contexts.
|
||||
*
|
||||
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* An object that, when cast to a string, returns the translated string.
|
||||
|
|
|
@ -38,7 +38,9 @@ interface TranslationInterface {
|
|||
* - 'langcode' (defaults to the current language): A language code, to
|
||||
* translate to a language other than what is used to display the page.
|
||||
* - 'context' (defaults to the empty context): The context the source
|
||||
* string belongs to.
|
||||
* string belongs to. See the
|
||||
* @link i18n Internationalization topic @endlink for more information
|
||||
* about string contexts.
|
||||
*
|
||||
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* An object that, when cast to a string, returns the translated string.
|
||||
|
|
161
core/lib/Drupal/Core/Test/AssertMailTrait.php
Normal file
161
core/lib/Drupal/Core/Test/AssertMailTrait.php
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Test\AssertMailTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Test;
|
||||
|
||||
/**
|
||||
* Provides methods for testing emails sent during test runs.
|
||||
*/
|
||||
trait AssertMailTrait {
|
||||
|
||||
/**
|
||||
* Gets an array containing all emails sent during this test case.
|
||||
*
|
||||
* @param array $filter
|
||||
* An array containing key/value pairs used to filter the emails that are
|
||||
* returned.
|
||||
*
|
||||
* @return array
|
||||
* An array containing email messages captured during the current test.
|
||||
*/
|
||||
protected function getMails(array $filter = []) {
|
||||
$captured_emails = $this->container->get('state')->get('system.test_mail_collector', []);
|
||||
$filtered_emails = [];
|
||||
|
||||
foreach ($captured_emails as $message) {
|
||||
foreach ($filter as $key => $value) {
|
||||
if (!isset($message[$key]) || $message[$key] != $value) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$filtered_emails[] = $message;
|
||||
}
|
||||
|
||||
return $filtered_emails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the most recently sent email message has the given value.
|
||||
*
|
||||
* The field in $name must have the content described in $value.
|
||||
*
|
||||
* @param string $name
|
||||
* Name of field or message property to assert. Examples: subject, body,
|
||||
* id, ...
|
||||
* @param string $value
|
||||
* Value of the field to assert.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion. Do not translate
|
||||
* messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
|
||||
* variables in the message text, not t(). If left blank, a default message
|
||||
* will be displayed.
|
||||
* @param string $group
|
||||
* (optional) The group this message is in, which is displayed in a column
|
||||
* in test output. Use 'Debug' to indicate this is debugging output. Do not
|
||||
* translate this string. Defaults to 'Email'; most tests do not override
|
||||
* this default.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE on pass, FALSE on fail.
|
||||
*/
|
||||
protected function assertMail($name, $value = '', $message = '', $group = 'Email') {
|
||||
$captured_emails = $this->container->get('state')->get('system.test_mail_collector') ?: [];
|
||||
$email = end($captured_emails);
|
||||
return $this->assertTrue($email && isset($email[$name]) && $email[$name] == $value, $message, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the most recently sent email message has the string in it.
|
||||
*
|
||||
* @param string $field_name
|
||||
* Name of field or message property to assert: subject, body, id, ...
|
||||
* @param string $string
|
||||
* String to search for.
|
||||
* @param int $email_depth
|
||||
* Number of emails to search for string, starting with most recent.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion. Do not translate
|
||||
* messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
|
||||
* variables in the message text, not t(). If left blank, a default message
|
||||
* will be displayed.
|
||||
* @param string $group
|
||||
* (optional) The group this message is in, which is displayed in a column
|
||||
* in test output. Use 'Debug' to indicate this is debugging output. Do not
|
||||
* translate this string. Defaults to 'Other'; most tests do not override
|
||||
* this default.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE on pass, FALSE on fail.
|
||||
*/
|
||||
protected function assertMailString($field_name, $string, $email_depth, $message = '', $group = 'Other') {
|
||||
$mails = $this->getMails();
|
||||
$string_found = FALSE;
|
||||
// Cast MarkupInterface objects to string.
|
||||
$string = (string) $string;
|
||||
for ($i = count($mails) - 1; $i >= count($mails) - $email_depth && $i >= 0; $i--) {
|
||||
$mail = $mails[$i];
|
||||
// Normalize whitespace, as we don't know what the mail system might have
|
||||
// done. Any run of whitespace becomes a single space.
|
||||
$normalized_mail = preg_replace('/\s+/', ' ', $mail[$field_name]);
|
||||
$normalized_string = preg_replace('/\s+/', ' ', $string);
|
||||
$string_found = (FALSE !== strpos($normalized_mail, $normalized_string));
|
||||
if ($string_found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$message) {
|
||||
$message = format_string('Expected text found in @field of email message: "@expected".', ['@field' => $field_name, '@expected' => $string]);
|
||||
}
|
||||
return $this->assertTrue($string_found, $message, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the most recently sent email message has the pattern in it.
|
||||
*
|
||||
* @param string $field_name
|
||||
* Name of field or message property to assert: subject, body, id, ...
|
||||
* @param string $regex
|
||||
* Pattern to search for.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion. Do not translate
|
||||
* messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
|
||||
* variables in the message text, not t(). If left blank, a default message
|
||||
* will be displayed.
|
||||
* @param string $group
|
||||
* (optional) The group this message is in, which is displayed in a column
|
||||
* in test output. Use 'Debug' to indicate this is debugging output. Do not
|
||||
* translate this string. Defaults to 'Other'; most tests do not override
|
||||
* this default.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE on pass, FALSE on fail.
|
||||
*/
|
||||
protected function assertMailPattern($field_name, $regex, $message = '', $group = 'Other') {
|
||||
$mails = $this->getMails();
|
||||
$mail = end($mails);
|
||||
$regex_found = preg_match("/$regex/", $mail[$field_name]);
|
||||
if (!$message) {
|
||||
$message = format_string('Expected text found in @field of email message: "@expected".', ['@field' => $field_name, '@expected' => $regex]);
|
||||
}
|
||||
return $this->assertTrue($regex_found, $message, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs to verbose the most recent $count emails sent.
|
||||
*
|
||||
* @param int $count
|
||||
* Optional number of emails to output.
|
||||
*/
|
||||
protected function verboseEmail($count = 1) {
|
||||
$mails = $this->getMails();
|
||||
for ($i = count($mails) - 1; $i >= count($mails) - $count && $i >= 0; $i--) {
|
||||
$mail = $mails[$i];
|
||||
$this->verbose('Email:<pre>' . print_r($mail, TRUE) . '</pre>');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
namespace Drupal\Core\Test\HttpClientMiddleware;
|
||||
|
||||
use Drupal\Core\Utility\Error;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Overrides the User-Agent HTTP header for outbound HTTP requests.
|
||||
|
@ -31,7 +33,29 @@ class TestHttpClientMiddleware {
|
|||
if ($test_prefix = drupal_valid_test_ua()) {
|
||||
$request = $request->withHeader('User-Agent', drupal_generate_test_ua($test_prefix));
|
||||
}
|
||||
return $handler($request, $options);
|
||||
return $handler($request, $options)
|
||||
->then(function (ResponseInterface $response) use ($request) {
|
||||
if (!drupal_valid_test_ua()) {
|
||||
return $response;
|
||||
}
|
||||
$headers = $response->getHeaders();
|
||||
foreach ($headers as $header_name => $header_values) {
|
||||
if (preg_match('/^X-Drupal-Assertion-[0-9]+$/', $header_name, $matches)) {
|
||||
foreach ($header_values as $header_value) {
|
||||
// Call \Drupal\simpletest\WebTestBase::error() with the parameters from
|
||||
// the header.
|
||||
$parameters = unserialize(urldecode($header_value));
|
||||
if (count($parameters) === 3) {
|
||||
throw new \Exception($parameters[1] . ': ' . $parameters[0] . "\n" . Error::formatBacktrace([$parameters[2]]));
|
||||
}
|
||||
else {
|
||||
throw new \Exception('Error thrown with the wrong amount of parameters.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,9 +13,8 @@ use Drupal\Core\Routing\RouteMatchInterface;
|
|||
* Defines an interface for classes which determine the active theme.
|
||||
*
|
||||
* To set the active theme, create a new service tagged with 'theme_negotiator'
|
||||
* (see user.services.yml for an example). The only method this service needs
|
||||
* to implement is determineActiveTheme. Return the name of the theme, or NULL
|
||||
* if other negotiators like the configured default one should kick in instead.
|
||||
* (see the theme.negotiator.admin_theme service in user.services.yml for an
|
||||
* example). Your service class needs to implement this interface.
|
||||
*
|
||||
* If you are setting a theme which is closely tied to the functionality of a
|
||||
* particular page or set of pages (such that the page might not function
|
||||
|
@ -46,7 +45,8 @@ interface ThemeNegotiatorInterface {
|
|||
* The current route match object.
|
||||
*
|
||||
* @return string|null
|
||||
* Returns the active theme name, else return NULL.
|
||||
* The name of the theme, or NULL if other negotiators, like the configured
|
||||
* default one, should be used instead.
|
||||
*/
|
||||
public function determineActiveTheme(RouteMatchInterface $route_match);
|
||||
|
||||
|
|
|
@ -25,6 +25,14 @@ namespace Drupal\Core\TypedData;
|
|||
*/
|
||||
interface ComplexDataInterface extends TraversableTypedDataInterface {
|
||||
|
||||
/**
|
||||
* Gets the data definition.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\ComplexDataDefinitionInterface
|
||||
* The data definition object describing the complex data.
|
||||
*/
|
||||
public function getDataDefinition();
|
||||
|
||||
/**
|
||||
* Gets a property object.
|
||||
*
|
||||
|
|
|
@ -22,6 +22,14 @@ namespace Drupal\Core\TypedData;
|
|||
*/
|
||||
interface ListInterface extends TraversableTypedDataInterface, \ArrayAccess, \Countable {
|
||||
|
||||
/**
|
||||
* Gets the data definition.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\ListDataDefinitionInterface
|
||||
* The data definition object describing the list.
|
||||
*/
|
||||
public function getDataDefinition();
|
||||
|
||||
/**
|
||||
* Determines whether the list contains any non-empty items.
|
||||
*
|
||||
|
|
Reference in a new issue