update drupal

This commit is contained in:
Tessier
2020-10-15 11:59:37 +02:00
parent ebb5db14b5
commit d8b9162932
558 changed files with 5954 additions and 4475 deletions
@@ -624,11 +624,10 @@ class DateTimePlus {
$valid_date = FALSE;
$valid_time = TRUE;
// Check for a valid date using checkdate(). Only values that
// meet that test are valid.
if (array_key_exists('year', $array) && array_key_exists('month', $array) && array_key_exists('day', $array)) {
if (@checkdate($array['month'], $array['day'], $array['year'])) {
$valid_date = TRUE;
}
// meet that test are valid. An empty value, either a string or a 0, is not
// a valid value.
if (!empty($array['year']) && !empty($array['month']) && !empty($array['day'])) {
$valid_date = checkdate($array['month'], $array['day'], $array['year']);
}
// Testing for valid time is reversed. Missing time is OK,
// but incorrect values are not.
@@ -58,6 +58,21 @@ class PhpTransliteration implements TransliterationInterface {
*/
protected $genericMap = [];
/**
* Special characters for ::removeDiacritics().
*
* Characters which have accented variants but their base character
* transliterates to more than one ASCII character require special
* treatment: we want to remove their accent and use the un-
* transliterated base character.
*/
protected $fixTransliterateForRemoveDiacritics = [
'AE' => 'Æ',
'ae' => 'æ',
'ZH' => 'Ʒ',
'zh' => 'ʒ',
];
/**
* Constructs a transliteration object.
*
@@ -93,6 +108,9 @@ class PhpTransliteration implements TransliterationInterface {
if (strlen($to_add) === 1) {
$replacement = $to_add;
}
elseif (isset($this->fixTransliterateForRemoveDiacritics[$to_add])) {
$replacement = $this->fixTransliterateForRemoveDiacritics[$to_add];
}
}
$result .= $replacement;
@@ -35,7 +35,8 @@ class Bytes {
return round($size * pow(self::KILOBYTE, stripos('bkmgtpezy', $unit[0])));
}
else {
return round($size);
// Ensure size is a proper number type.
return round((float) $size);
}
}
@@ -30,8 +30,6 @@ class Image {
*
* @return bool
* TRUE if $dimensions was modified, FALSE otherwise.
*
* @see image_scale()
*/
public static function scaleDimensions(array &$dimensions, $width = NULL, $height = NULL, $upscale = FALSE) {
$aspect = $dimensions['height'] / $dimensions['width'];
@@ -537,16 +537,16 @@ EOD;
*/
public static function mimeHeaderDecode($header) {
$callback = function ($matches) {
$data = ($matches[2] == 'B') ? base64_decode($matches[3]) : str_replace('_', ' ', quoted_printable_decode($matches[3]));
$data = (strtolower($matches[2]) == 'b') ? base64_decode($matches[3]) : str_replace('_', ' ', quoted_printable_decode($matches[3]));
if (strtolower($matches[1]) != 'utf-8') {
$data = static::convertToUtf8($data, $matches[1]);
}
return $data;
};
// First step: encoded chunks followed by other encoded chunks (need to collapse whitespace)
$header = preg_replace_callback('/=\?([^?]+)\?(Q|B)\?([^?]+|\?(?!=))\?=\s+(?==\?)/', $callback, $header);
$header = preg_replace_callback('/=\?([^?]+)\?([Qq]|[Bb])\?([^?]+|\?(?!=))\?=\s+(?==\?)/', $callback, $header);
// Second step: remaining chunks (do not collapse whitespace)
return preg_replace_callback('/=\?([^?]+)\?(Q|B)\?([^?]+|\?(?!=))\?=/', $callback, $header);
return preg_replace_callback('/=\?([^?]+)\?([Qq]|[Bb])\?([^?]+|\?(?!=))\?=/', $callback, $header);
}
/**
@@ -345,7 +345,7 @@ class LibraryDiscoveryParser {
$library_file = $path . '/' . $extension . '.libraries.yml';
if (file_exists($this->root . '/' . $library_file)) {
try {
$libraries = Yaml::decode(file_get_contents($this->root . '/' . $library_file));
$libraries = Yaml::decode(file_get_contents($this->root . '/' . $library_file)) ?? [];
}
catch (InvalidDataTypeException $e) {
// Rethrow a more helpful exception to provide context.
+1 -262
View File
@@ -2,18 +2,10 @@
namespace Drupal\Core\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
use Drupal\Core\Plugin\ContextAwarePluginBase;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Plugin\PluginWithFormsInterface;
use Drupal\Core\Plugin\PluginWithFormsTrait;
use Drupal\Core\Render\PreviewFallbackInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Component\Transliteration\TransliterationInterface;
/**
* Defines a base block implementation that most blocks plugins will extend.
@@ -26,260 +18,7 @@ use Drupal\Component\Transliteration\TransliterationInterface;
*/
abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginInterface, PluginWithFormsInterface, PreviewFallbackInterface {
use BlockPluginTrait;
use ContextAwarePluginAssignmentTrait;
use MessengerTrait;
use PluginWithFormsTrait;
/**
* The transliteration service.
*
* @var \Drupal\Component\Transliteration\TransliterationInterface
*/
protected $transliteration;
/**
* {@inheritdoc}
*/
public function label() {
if (!empty($this->configuration['label'])) {
return $this->configuration['label'];
}
$definition = $this->getPluginDefinition();
// Cast the admin label to a string since it is an object.
// @see \Drupal\Core\StringTranslation\TranslatableMarkup
return (string) $definition['admin_label'];
}
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->setConfiguration($configuration);
}
/**
* {@inheritdoc}
*/
public function getConfiguration() {
return $this->configuration;
}
/**
* {@inheritdoc}
*/
public function setConfiguration(array $configuration) {
$this->configuration = NestedArray::mergeDeep(
$this->baseConfigurationDefaults(),
$this->defaultConfiguration(),
$configuration
);
}
/**
* Returns generic default configuration for block plugins.
*
* @return array
* An associative array with the default configuration.
*/
protected function baseConfigurationDefaults() {
return [
'id' => $this->getPluginId(),
'label' => '',
'provider' => $this->pluginDefinition['provider'],
'label_display' => static::BLOCK_LABEL_VISIBLE,
];
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [];
}
/**
* {@inheritdoc}
*/
public function setConfigurationValue($key, $value) {
$this->configuration[$key] = $value;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
return [];
}
/**
* {@inheritdoc}
*/
public function access(AccountInterface $account, $return_as_object = FALSE) {
$access = $this->blockAccess($account);
return $return_as_object ? $access : $access->isAllowed();
}
/**
* Indicates whether the block should be shown.
*
* Blocks with specific access checking should override this method rather
* than access(), in order to avoid repeating the handling of the
* $return_as_object argument.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The user session for which to check access.
*
* @return \Drupal\Core\Access\AccessResult
* The access result.
*
* @see self::access()
*/
protected function blockAccess(AccountInterface $account) {
// By default, the block is visible.
return AccessResult::allowed();
}
/**
* {@inheritdoc}
*
* Creates a generic configuration form for all block types. Individual
* block plugins can add elements to this form by overriding
* BlockBase::blockForm(). Most block plugins should not override this
* method unless they need to alter the generic form elements.
*
* @see \Drupal\Core\Block\BlockBase::blockForm()
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$definition = $this->getPluginDefinition();
$form['provider'] = [
'#type' => 'value',
'#value' => $definition['provider'],
];
$form['admin_label'] = [
'#type' => 'item',
'#title' => $this->t('Block description'),
'#plain_text' => $definition['admin_label'],
];
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Title'),
'#maxlength' => 255,
'#default_value' => $this->label(),
'#required' => TRUE,
];
$form['label_display'] = [
'#type' => 'checkbox',
'#title' => $this->t('Display title'),
'#default_value' => ($this->configuration['label_display'] === static::BLOCK_LABEL_VISIBLE),
'#return_value' => static::BLOCK_LABEL_VISIBLE,
];
// Add context mapping UI form elements.
$contexts = $form_state->getTemporaryValue('gathered_contexts') ?: [];
$form['context_mapping'] = $this->addContextAssignmentElement($this, $contexts);
// Add plugin-specific settings for this block type.
$form += $this->blockForm($form, $form_state);
return $form;
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
return [];
}
/**
* {@inheritdoc}
*
* Most block plugins should not override this method. To add validation
* for a specific block type, override BlockBase::blockValidate().
*
* @see \Drupal\Core\Block\BlockBase::blockValidate()
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
// Remove the admin_label form item element value so it will not persist.
$form_state->unsetValue('admin_label');
$this->blockValidate($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function blockValidate($form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*
* Most block plugins should not override this method. To add submission
* handling for a specific block type, override BlockBase::blockSubmit().
*
* @see \Drupal\Core\Block\BlockBase::blockSubmit()
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
// Process the block's submission handling if no errors occurred only.
if (!$form_state->getErrors()) {
$this->configuration['label'] = $form_state->getValue('label');
$this->configuration['label_display'] = $form_state->getValue('label_display');
$this->configuration['provider'] = $form_state->getValue('provider');
$this->blockSubmit($form, $form_state);
}
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
public function getMachineNameSuggestion() {
$definition = $this->getPluginDefinition();
$admin_label = $definition['admin_label'];
// @todo This is basically the same as what is done in
// \Drupal\system\MachineNameController::transliterate(), so it might make
// sense to provide a common service for the two.
$transliterated = $this->transliteration()->transliterate($admin_label, LanguageInterface::LANGCODE_DEFAULT, '_');
$transliterated = mb_strtolower($transliterated);
$transliterated = preg_replace('@[^a-z0-9_.]+@', '', $transliterated);
return $transliterated;
}
/**
* {@inheritdoc}
*/
public function getPreviewFallbackString() {
return $this->t('"@block" block', ['@block' => $this->label()]);
}
/**
* Wraps the transliteration service.
*
* @return \Drupal\Component\Transliteration\TransliterationInterface
*/
protected function transliteration() {
if (!$this->transliteration) {
$this->transliteration = \Drupal::transliteration();
}
return $this->transliteration;
}
/**
* Sets the transliteration service.
*
* @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration
* The transliteration service.
*/
public function setTransliteration(TransliterationInterface $transliteration) {
$this->transliteration = $transliteration;
}
}
@@ -0,0 +1,287 @@
<?php
namespace Drupal\Core\Block;
use Drupal\Component\Transliteration\TransliterationInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Plugin\PluginWithFormsTrait;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Provides the base implementation of a block plugin.
*
* @internal
* This trait is used internally by the block system. Block plugins should
* extend \Drupal\Core\Block\BlockBase.
*
* @see \Drupal\Core\Block\BlockBase
* @see \Drupal\Core\Block\BlockPluginInterface
*
* @ingroup block_api
*/
trait BlockPluginTrait {
use StringTranslationTrait;
use MessengerTrait;
use PluginWithFormsTrait;
/**
* The transliteration service.
*
* @var \Drupal\Component\Transliteration\TransliterationInterface
*/
protected $transliteration;
/**
* {@inheritdoc}
*/
public function label() {
if (!empty($this->configuration['label'])) {
return $this->configuration['label'];
}
$definition = $this->getPluginDefinition();
// Cast the admin label to a string since it is an object.
// @see \Drupal\Core\StringTranslation\TranslatableMarkup
return (string) $definition['admin_label'];
}
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->setConfiguration($configuration);
}
/**
* {@inheritdoc}
*/
public function getConfiguration() {
return $this->configuration;
}
/**
* {@inheritdoc}
*/
public function setConfiguration(array $configuration) {
$this->configuration = NestedArray::mergeDeep(
$this->baseConfigurationDefaults(),
$this->defaultConfiguration(),
$configuration
);
}
/**
* Returns generic default configuration for block plugins.
*
* @return array
* An associative array with the default configuration.
*/
protected function baseConfigurationDefaults() {
return [
'id' => $this->getPluginId(),
'label' => '',
'provider' => $this->pluginDefinition['provider'],
'label_display' => BlockPluginInterface::BLOCK_LABEL_VISIBLE,
];
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [];
}
/**
* {@inheritdoc}
*/
public function setConfigurationValue($key, $value) {
$this->configuration[$key] = $value;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
return [];
}
/**
* {@inheritdoc}
*/
public function access(AccountInterface $account, $return_as_object = FALSE) {
$access = $this->blockAccess($account);
return $return_as_object ? $access : $access->isAllowed();
}
/**
* Indicates whether the block should be shown.
*
* Blocks with specific access checking should override this method rather
* than access(), in order to avoid repeating the handling of the
* $return_as_object argument.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The user session for which to check access.
*
* @return \Drupal\Core\Access\AccessResult
* The access result.
*
* @see self::access()
*/
protected function blockAccess(AccountInterface $account) {
// By default, the block is visible.
return AccessResult::allowed();
}
/**
* {@inheritdoc}
*
* Creates a generic configuration form for all block types. Individual
* block plugins can add elements to this form by overriding
* BlockBase::blockForm(). Most block plugins should not override this
* method unless they need to alter the generic form elements.
*
* @see \Drupal\Core\Block\BlockBase::blockForm()
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$definition = $this->getPluginDefinition();
$form['provider'] = [
'#type' => 'value',
'#value' => $definition['provider'],
];
$form['admin_label'] = [
'#type' => 'item',
'#title' => $this->t('Block description'),
'#plain_text' => $definition['admin_label'],
];
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Title'),
'#maxlength' => 255,
'#default_value' => $this->label(),
'#required' => TRUE,
];
$form['label_display'] = [
'#type' => 'checkbox',
'#title' => $this->t('Display title'),
'#default_value' => ($this->configuration['label_display'] === BlockPluginInterface::BLOCK_LABEL_VISIBLE),
'#return_value' => BlockPluginInterface::BLOCK_LABEL_VISIBLE,
];
// Add context mapping UI form elements.
$contexts = $form_state->getTemporaryValue('gathered_contexts') ?: [];
$form['context_mapping'] = $this->addContextAssignmentElement($this, $contexts);
// Add plugin-specific settings for this block type.
$form += $this->blockForm($form, $form_state);
return $form;
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
return [];
}
/**
* {@inheritdoc}
*
* Most block plugins should not override this method. To add validation
* for a specific block type, override BlockBase::blockValidate().
*
* @see \Drupal\Core\Block\BlockBase::blockValidate()
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
// Remove the admin_label form item element value so it will not persist.
$form_state->unsetValue('admin_label');
$this->blockValidate($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function blockValidate($form, FormStateInterface $form_state) {
}
/**
* {@inheritdoc}
*
* Most block plugins should not override this method. To add submission
* handling for a specific block type, override BlockBase::blockSubmit().
*
* @see \Drupal\Core\Block\BlockBase::blockSubmit()
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
// Process the block's submission handling if no errors occurred only.
if (!$form_state->getErrors()) {
$this->configuration['label'] = $form_state->getValue('label');
$this->configuration['label_display'] = $form_state->getValue('label_display');
$this->configuration['provider'] = $form_state->getValue('provider');
$this->blockSubmit($form, $form_state);
}
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
}
/**
* {@inheritdoc}
*/
public function getMachineNameSuggestion() {
$definition = $this->getPluginDefinition();
$admin_label = $definition['admin_label'];
// @todo This is basically the same as what is done in
// \Drupal\system\MachineNameController::transliterate(), so it might make
// sense to provide a common service for the two.
$transliterated = $this->transliteration()->transliterate($admin_label, LanguageInterface::LANGCODE_DEFAULT, '_');
$transliterated = mb_strtolower($transliterated);
$transliterated = preg_replace('@[^a-z0-9_.]+@', '', $transliterated);
return $transliterated;
}
/**
* {@inheritdoc}
*/
public function getPreviewFallbackString() {
return $this->t('"@block" block', ['@block' => $this->label()]);
}
/**
* Wraps the transliteration service.
*
* @return \Drupal\Component\Transliteration\TransliterationInterface
*/
protected function transliteration() {
if (!$this->transliteration) {
$this->transliteration = \Drupal::transliteration();
}
return $this->transliteration;
}
/**
* Sets the transliteration service.
*
* @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration
* The transliteration service.
*/
public function setTransliteration(TransliterationInterface $transliteration) {
$this->transliteration = $transliteration;
}
}
@@ -2,8 +2,11 @@
namespace Drupal\Core\Block\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Block\BlockPluginTrait;
use Drupal\Core\Cache\CacheableDependencyTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginBase;
/**
* Defines a fallback plugin for missing block plugins.
@@ -14,7 +17,10 @@ use Drupal\Core\Form\FormStateInterface;
* category = @Translation("Block"),
* )
*/
class Broken extends BlockBase {
class Broken extends PluginBase implements BlockPluginInterface {
use BlockPluginTrait;
use CacheableDependencyTrait;
/**
* {@inheritdoc}
@@ -121,7 +121,7 @@ final class ConfigEvents {
* fire the event again to continue processing missing content dependencies.
*
* @see \Drupal\Core\Config\ConfigImporter::processMissingContent()
* @see \Drupal\Core\Config\MissingContentEvent
* @see \Drupal\Core\Config\Importer\MissingContentEvent
*/
const IMPORT_MISSING_CONTENT = 'config.importer.missing_content';
@@ -130,6 +130,11 @@ class Condition extends ConditionBase {
return TRUE;
}
}
// If the parent does not exist, it's safe to say the actual property
// we're checking for is also NULL.
elseif ($condition['operator'] === 'IS NULL') {
return TRUE;
}
}
// Only try to match a scalar if there are no remaining keys in
// $needs_matching as this indicates that we are looking for a specific
@@ -89,7 +89,14 @@ class Query extends QueryBase implements QueryInterface {
$direction = $sort['direction'] == 'ASC' ? -1 : 1;
$field = $sort['field'];
uasort($result, function ($a, $b) use ($field, $direction) {
return ($a[$field] <= $b[$field]) ? $direction : -$direction;
$properties = explode('.', $field);
foreach ($properties as $property) {
if (isset($a[$property]) || isset($b[$property])) {
$a = isset($a[$property]) ? $a[$property] : NULL;
$b = isset($b[$property]) ? $b[$property] : NULL;
}
}
return ($a <= $b) ? $direction : -$direction;
});
}
@@ -7,7 +7,7 @@ use Symfony\Component\EventDispatcher\Event;
/**
* Wraps a configuration event for event listeners.
*
* @see \Drupal\Core\Config\Config\ConfigEvents::IMPORT_MISSING_CONTENT
* @see \Drupal\Core\Config\ConfigEvents::IMPORT_MISSING_CONTENT
*/
class MissingContentEvent extends Event {
@@ -1501,8 +1501,8 @@ abstract class Connection {
/**
* Prepares a statement for execution and returns a statement object
*
* Emulated prepared statements does not communicate with the database server
* so this method does not check the statement.
* Emulated prepared statements do not communicate with the database server so
* this method does not check the statement.
*
* @param string $statement
* This must be a valid SQL statement for the target database server.
@@ -686,11 +686,11 @@ class Schema extends DatabaseSchema {
$condition->condition('column_name', $column);
$condition->compile($this->connection, $this);
// Don't use {} around information_schema.columns table.
return $this->connection->query("SELECT column_comment as column_comment FROM information_schema.columns WHERE " . (string) $condition, $condition->arguments())->fetchField();
return $this->connection->query("SELECT column_comment AS column_comment FROM information_schema.columns WHERE " . (string) $condition, $condition->arguments())->fetchField();
}
$condition->compile($this->connection, $this);
// Don't use {} around information_schema.tables table.
$comment = $this->connection->query("SELECT table_comment as table_comment FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchField();
$comment = $this->connection->query("SELECT table_comment AS table_comment FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchField();
// Work-around for MySQL 5.0 bug http://bugs.mysql.com/bug.php?id=11379
return preg_replace('/; InnoDB free:.*$/', '', $comment);
}
@@ -814,13 +814,16 @@ class Select extends Query implements SelectInterface {
$fields = [];
foreach ($this->tables as $alias => $table) {
if (!empty($table['all_fields'])) {
$fields[] = $this->connection->escapeTable($alias) . '.*';
$fields[] = $this->connection->escapeAlias($alias) . '.*';
}
}
foreach ($this->fields as $field) {
// Note that $field['table'] holds the table alias.
// @see \Drupal\Core\Database\Query\Select::addField
$table = isset($field['table']) ? $this->connection->escapeAlias($field['table']) . '.' : '';
// Always use the AS keyword for field aliases, as some
// databases require it (e.g., PostgreSQL).
$fields[] = (isset($field['table']) ? $this->connection->escapeTable($field['table']) . '.' : '') . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
$fields[] = $table . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
}
foreach ($this->expressions as $expression) {
$fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']);
@@ -852,7 +855,7 @@ class Select extends Query implements SelectInterface {
// Don't use the AS keyword for table aliases, as some
// databases don't support it (e.g., Oracle).
$query .= $table_string . ' ' . $this->connection->escapeTable($table['alias']);
$query .= $table_string . ' ' . $this->connection->escapeAlias($table['alias']);
if (!empty($table['condition'])) {
$query .= ' ON ' . (string) $table['condition'];
+1 -1
View File
@@ -200,7 +200,7 @@ abstract class Schema implements PlaceholderInterface {
// couldn't use \Drupal::database()->select() here because it would prefix
// information_schema.tables and the query would fail.
// Don't use {} around information_schema.tables table.
$results = $this->connection->query("SELECT table_name as table_name FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments());
$results = $this->connection->query("SELECT table_name AS table_name FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments());
foreach ($results as $table) {
// Take into account tables that have an individual prefix.
if (isset($individually_prefixed_tables[$table->table_name])) {
@@ -8,7 +8,7 @@ namespace Drupal\Core\DependencyInjection;
trait DeprecatedServicePropertyTrait {
/**
* Alows to access deprecated/removed properties.
* Allows to access deprecated/removed properties.
*
* This method must be public.
*/
@@ -70,7 +70,7 @@ interface ContentEntityFormInterface extends EntityFormInterface {
* For more information about entity validation, see
* https://www.drupal.org/node/2015613.
*
* @return \Drupal\Core\Entity\ContentEntityTypeInterface
* @return \Drupal\Core\Entity\ContentEntityInterface
* The built entity.
*/
public function validateForm(array &$form, FormStateInterface $form_state);
@@ -14,8 +14,54 @@ use Drupal\Core\Site\Settings;
/**
* Provides an entity autocomplete form element.
*
* The #default_value accepted by this element is either an entity object or an
* array of entity objects.
* The autocomplete form element allows users to select one or multiple
* entities, which can come from all or specific bundles of an entity type.
*
* Properties:
* - #target_type: (required) The ID of the target entity type.
* - #tags: (optional) TRUE if the element allows multiple selection. Defaults
* to FALSE.
* - #default_value: (optional) The default entity or an array of default
* entities, depending on the value of #tags.
* - #selection_handler: (optional) The plugin ID of the entity reference
* selection handler (a plugin of type EntityReferenceSelection). The default
* value is the lowest-weighted plugin that is compatible with #target_type.
* - #selection_settings: (optional) An array of settings for the selection
* handler. Settings for the default selection handler
* \Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection are:
* - target_bundles: Array of bundles to allow (omit to allow all bundles).
* - sort: Array with 'field' and 'direction' keys, determining how results
* will be sorted. Defaults to unsorted.
* - #autocreate: (optional) Array of settings used to auto-create entities
* that do not exist (omit to not auto-create entities). Elements:
* - bundle: (required) Bundle to use for auto-created entities.
* - uid: User ID to use as the author of auto-created entities. Defaults to
* the current user.
* - #process_default_value: (optional) Set to FALSE if the #default_value
* property is processed and access checked elsewhere (such as by a Field API
* widget). Defaults to TRUE.
* - #validate_reference: (optional) Set to FALSE if validation of the selected
* entities is performed elsewhere. Defaults to TRUE.
*
* Usage example:
* @code
* $form['my_element'] = [
* '#type' => 'entity_autocomplete',
* '#target_type' => 'node',
* '#tags' => TRUE,
* '#default_value' => $node,
* '#selection_handler' => 'default',
* '#selection_settings' => [
* 'target_bundles' => ['article', 'page'],
* ],
* '#autocreate' => [
* 'bundle' => 'article',
* 'uid' => <a valid user ID>,
* ],
* ];
* @endcode
*
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection
*
* @FormElement("entity_autocomplete")
*/
@@ -395,13 +395,16 @@ class DefaultSelection extends SelectionPluginBase implements ContainerFactoryPl
*/
public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
$bundle_key = $entity_type->getKey('bundle');
$label_key = $entity_type->getKey('label');
$entity = $this->entityTypeManager->getStorage($entity_type_id)->create([
$bundle_key => $bundle,
$label_key => $label,
]);
$values = [
$entity_type->getKey('label') => $label,
];
if ($bundle_key = $entity_type->getKey('bundle')) {
$values[$bundle_key] = $bundle;
}
$entity = $this->entityTypeManager->getStorage($entity_type_id)->create($values);
if ($entity instanceof EntityOwnerInterface) {
$entity->setOwnerId($uid);
@@ -846,8 +846,19 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
// Add the bundle column.
if ($bundle = $this->entityType->getKey('bundle')) {
if ($base_table) {
$select->join($base_table, 'base_table', "entity_table.{$this->entityType->getKey('id')} = %alias.{$this->entityType->getKey('id')}");
// The bundle field is not stored in the revision table, so we need to
// join the data (or base) table and retrieve it from there.
if ($base_table && $base_table !== $table_name) {
$join_condition = "entity_table.{$this->entityType->getKey('id')} = %alias.{$this->entityType->getKey('id')}";
// If the entity type is translatable, we also need to add the langcode
// to the join, otherwise we'll get duplicate rows for each language.
if ($this->entityType->isTranslatable()) {
$langcode = $this->entityType->getKey('langcode');
$join_condition .= " AND entity_table.{$langcode} = %alias.{$langcode}";
}
$select->join($base_table, 'base_table', $join_condition);
$select->addField('base_table', $bundle, 'bundle');
}
else {
+20 -2
View File
@@ -616,6 +616,18 @@ use Drupal\node\Entity\NodeType;
* are invoked are hook_entity_create_access() and
* hook_ENTITY_TYPE_create_access() instead.
*
* The access to an entity can be influenced in several ways:
* - To explicitly allow access, return an AccessResultInterface object with
* isAllowed() returning TRUE. Other modules can override this access by
* returning TRUE for isForbidden().
* - To explicitly forbid access, return an AccessResultInterface object with
* isForbidden() returning TRUE. Access will be forbidden even if your module
* (or another module) also returns TRUE for isNeutral() or isAllowed().
* - To neither allow nor explicitly forbid access, return an
* AccessResultInterface object with isNeutral() returning TRUE.
* - If your module does not return an AccessResultInterface object, neutral
* access will be assumed.
*
* The Node entity type has a complex system for determining access, which
* developers can interact with. This is described in the
* @link node_access Node access topic. @endlink
@@ -643,7 +655,10 @@ use Drupal\node\Entity\NodeType;
* @param string $operation
* The operation that is to be performed on $entity.
* @param \Drupal\Core\Session\AccountInterface $account
* The account trying to access the entity.
* The account trying to access the entity. Usually one of:
* - "view"
* - "update"
* - "delete"
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result. The final result is calculated by using
@@ -677,7 +692,10 @@ function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operat
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to check access to.
* @param string $operation
* The operation that is to be performed on $entity.
* The operation that is to be performed on $entity. Usually one of:
* - "view"
* - "update"
* - "delete"
* @param \Drupal\Core\Session\AccountInterface $account
* The account trying to access the entity.
*
@@ -42,6 +42,7 @@ class ExceptionLoggingSubscriber implements EventSubscriberInterface {
// why access was denied.
$exception = $event->getException();
$error = Error::decodeException($exception);
unset($error['@backtrace_string']);
$error['@uri'] = $event->getRequest()->getRequestUri();
$this->logger->get('access denied')->warning('Path: @uri. %type: @message in %function (line %line of %file).', $error);
}
@@ -49,7 +49,7 @@ class BooleanCheckboxWidget extends WidgetBase {
$summary = [];
$display_label = $this->getSetting('display_label');
$summary[] = t('Use field label: @display_label', ['@display_label' => ($display_label ? t('Yes') : 'No')]);
$summary[] = t('Use field label: @display_label', ['@display_label' => ($display_label ? t('Yes') : t('No'))]);
return $summary;
}
@@ -117,7 +117,7 @@ class EntityReferenceAutocompleteWidget extends WidgetBase {
'#placeholder' => $this->getSetting('placeholder'),
];
if ($this->getSelectionHandlerSetting('auto_create') && ($bundle = $this->getAutocreateBundle())) {
if ($bundle = $this->getAutocreateBundle()) {
$element['#autocreate'] = [
'bundle' => $bundle,
'uid' => ($entity instanceof EntityOwnerInterface) ? $entity->getOwnerId() : \Drupal::currentUser()->id(),
@@ -154,16 +154,23 @@ class EntityReferenceAutocompleteWidget extends WidgetBase {
* Returns the name of the bundle which will be used for autocreated entities.
*
* @return string
* The bundle name.
* The bundle name. If autocreate is not active, NULL will be returned.
*/
protected function getAutocreateBundle() {
$bundle = NULL;
if ($this->getSelectionHandlerSetting('auto_create') && $target_bundles = $this->getSelectionHandlerSetting('target_bundles')) {
if ($this->getSelectionHandlerSetting('auto_create')) {
$target_bundles = $this->getSelectionHandlerSetting('target_bundles');
// If there's no target bundle at all, use the target_type. It's the
// default for bundleless entity types.
if (empty($target_bundles)) {
$bundle = $this->getFieldSetting('target_type');
}
// If there's only one target bundle, use it.
if (count($target_bundles) == 1) {
elseif (count($target_bundles) == 1) {
$bundle = reset($target_bundles);
}
// Otherwise use the target bundle stored in selection handler settings.
// If there's more than one target bundle, use the autocreate bundle
// stored in selection handler settings.
elseif (!$bundle = $this->getSelectionHandlerSetting('auto_create_bundle')) {
// If no bundle has been set as auto create target means that there is
// an inconsistency in entity reference field settings.
@@ -861,7 +861,8 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
// https://www.drupal.org/node/2504709.
$parsed = UrlHelper::parse($request_uri);
unset($parsed['query'][static::AJAX_FORM_REQUEST], $parsed['query'][MainContentViewSubscriber::WRAPPER_FORMAT]);
return $parsed['path'] . ($parsed['query'] ? ('?' . UrlHelper::buildQuery($parsed['query'])) : '');
$action = $parsed['path'] . ($parsed['query'] ? ('?' . UrlHelper::buildQuery($parsed['query'])) : '');
return UrlHelper::filterBadProtocol($action);
}
/**
+2 -2
View File
@@ -66,8 +66,8 @@ class Link implements RenderableInterface {
/**
* Creates a Link object from a given Url object.
*
* @param string $text
* The text of the link.
* @param string|array|\Drupal\Component\Render\MarkupInterface $text
* The link text for the anchor tag as a translated string or render array.
* @param \Drupal\Core\Url $url
* The Url to create the link for.
*
@@ -10,13 +10,13 @@ use Drupal\Core\StringTranslation\TranslatableMarkup;
* Logging severity levels as defined in RFC 5424.
*
* The constant definitions of this class correspond to the logging severity
* levels defined in RFC 5424, section 4.1.1. PHP supplies predefined LOG_*
* levels defined in RFC 5424, section 6.2.1. PHP supplies predefined LOG_*
* constants for use in the syslog() function, but their values on Windows
* builds do not correspond to RFC 5424. The associated PHP bug report was
* closed with the comment, "And it's also not a bug, as Windows just have less
* log levels," and "So the behavior you're seeing is perfectly normal."
*
* @see http://tools.ietf.org/html/rfc5424
* @see https://tools.ietf.org/html/rfc5424#section-6.2.1
* @see http://bugs.php.net/bug.php?id=18090
* @see http://php.net/manual/function.syslog.php
* @see http://php.net/manual/network.constants.php
@@ -15,14 +15,14 @@ use Drupal\Core\Render\Element;
*
* Example usage:
* @code
* $form['email'] = array(
* $form['email'] = [
* '#type' => 'email',
* '#title' => $this->t('Email'),
* '#pattern' => '*@example.com',
* );
* @end
* ];
* @endcode
*
* @see \Drupal\Core\Render\Element\Render\Textfield
* @see \Drupal\Core\Render\Element\Textfield
*
* @FormElement("email")
*/
@@ -52,7 +52,7 @@ class StatusReport extends RenderElement {
// Order the grouped requirements by a set order.
$order = array_flip($element['#priorities']);
uksort($grouped_requirements, function ($a, $b) use ($order) {
return $order[$a] > $order[$b];
return $order[$a] <=> $order[$b];
});
$element['#grouped_requirements'] = $grouped_requirements;
@@ -414,13 +414,16 @@
* render array contained:
* @code
* $build['my_element'] = [
* '#attached' => ['placeholders' => ['@foo' => 'replacement']],
* '#markup' => ['Something about @foo'],
* '#markup' => 'Something about @foo',
* '#attached' => [
* 'placeholders' => [
* '@foo' => ['#markup' => 'replacement'],
* ],
* ];
* @endcode
* then #markup would end up containing 'Something about replacement'.
*
* Note that each placeholder value can itself be a render array, which will be
* Note that each placeholder value *must* itself be a render array. It will be
* rendered, and any cache tags generated during rendering will be added to the
* cache tags for the markup.
*
@@ -60,8 +60,8 @@ class PharExtensionInterceptor implements Assertable {
return FALSE;
}
// If the stream wrapper is registered by invoking a phar file that does
// not not have .phar extension then this should be allowed. For
// example, some CLI tools recommend removing the extension.
// not have .phar extension then this should be allowed. For example, some
// CLI tools recommend removing the extension.
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
// Find the last entry in the backtrace containing a 'file' key as
// sometimes the last caller is executed outside the scope of a file. For
@@ -26,7 +26,7 @@ class AttributeHelper {
* An Attribute object or an array of attributes.
*
* @return bool
* TRUE if the attibute exists, FALSE otherwise.
* TRUE if the attribute exists, FALSE otherwise.
*
* @throws \InvalidArgumentException
* When the input $collection is neither an Attribute object nor an array.
@@ -205,7 +205,7 @@ class PhpUnitTestRunner implements ContainerInjectionInterface {
*/
public function runTests($test_id, array $unescaped_test_classnames, &$status = NULL) {
$phpunit_file = $this->xmlLogFilePath($test_id);
// Store ouptut from our test run.
// Store output from our test run.
$output = [];
$this->runCommand($unescaped_test_classnames, $phpunit_file, $status, $output);
@@ -217,7 +217,7 @@ class PhpUnitTestRunner implements ContainerInjectionInterface {
'test_id' => $test_id,
'test_class' => implode(",", $unescaped_test_classnames),
'status' => TestStatus::label($status),
'message' => 'PHPunit Test failed to complete; Error: ' . implode("\n", $output),
'message' => 'PHPUnit Test failed to complete; Error: ' . implode("\n", $output),
'message_group' => 'Other',
'function' => implode(",", $unescaped_test_classnames),
'line' => '0',
@@ -169,7 +169,15 @@ class TypedDataManager extends DefaultPluginManager implements TypedDataManagerI
$parts[] = json_encode($settings);
}
// Property path for the requested data object.
$parts[] = $object->getPropertyPath() . '.' . $property_name;
$parts[] = $object->getPropertyPath();
// Only property instances of complex data types should be cached by the
// property name, as they represent different properties. Properties of list
// data types are the items of the list and the property name represents
// only the delta in that list and not an unique property, which is why all
// items should use the same prototype.
if ($object instanceof ComplexDataInterface) {
$parts[] = $property_name;
}
$key = implode(':', $parts);
// Create the prototype if needed.
@@ -27,16 +27,28 @@ class UpdateCompilerPass implements CompilerPassInterface {
do {
$has_changed = FALSE;
foreach ($container->getDefinitions() as $key => $definition) {
// Ensure all the definition's arguments are valid.
foreach ($definition->getArguments() as $argument) {
if ($argument instanceof Reference) {
$argument_id = (string) $argument;
if (!$container->has($argument_id) && $argument->getInvalidBehavior() === ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
// If the container does not have the argument and would throw an
// exception, remove the service.
if ($this->isArgumentMissingService($argument, $container)) {
$container->removeDefinition($key);
$container->log($this, sprintf('Removed service "%s"; reason: depends on non-existent service "%s".', $key, (string) $argument));
$has_changed = TRUE;
$process_aliases = TRUE;
// Process the next definition.
continue 2;
}
}
// Ensure all the method call arguments are valid.
foreach ($definition->getMethodCalls() as $call) {
foreach ($call[1] as $argument) {
if ($this->isArgumentMissingService($argument, $container)) {
$container->removeDefinition($key);
$container->log($this, sprintf('Removed service "%s"; reason: depends on non-existent service "%s".', $key, $argument_id));
$container->log($this, sprintf('Removed service "%s"; reason: method call "%s" depends on non-existent service "%s".', $key, $call[0], (string) $argument));
$has_changed = TRUE;
$process_aliases = TRUE;
// Process the next definition.
continue 3;
}
}
}
@@ -58,4 +70,26 @@ class UpdateCompilerPass implements CompilerPassInterface {
}
}
/**
* Checks if a reference argument is to a missing service.
*
* @param mixed $argument
* The argument to check.
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* The container.
*
* @return bool
* TRUE if the argument is a reference to a service that is missing from the
* container and the reference is required, FALSE if not.
*/
private function isArgumentMissingService($argument, ContainerBuilder $container) {
if ($argument instanceof Reference) {
$argument_id = (string) $argument;
if (!$container->has($argument_id) && $argument->getInvalidBehavior() === ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
return TRUE;
}
}
return FALSE;
}
}
+1 -1
View File
@@ -297,7 +297,7 @@ class Url implements TrustedCallbackInterface {
// Extract query parameters and fragment and merge them into $uri_options,
// but preserve the original $options for the fallback case.
$uri_options = $options;
if (isset($uri_parts['fragment'])) {
if (isset($uri_parts['fragment']) && $uri_parts['fragment'] !== '') {
$uri_options += ['fragment' => $uri_parts['fragment']];
unset($uri_parts['fragment']);
}
@@ -35,11 +35,11 @@ class ProjectInfo {
* @param bool $status
* Boolean that controls what status (enabled or uninstalled) to process out
* of the $list and add to the $projects array.
* @param array $additional_whitelist
* @param array $additional_elements
* (optional) Array of additional elements to be collected from the .info.yml
* file. Defaults to array().
*/
public function processInfoList(array &$projects, array $list, $project_type, $status, array $additional_whitelist = []) {
public function processInfoList(array &$projects, array $list, $project_type, $status, array $additional_elements = []) {
foreach ($list as $file) {
// Just projects with a matching status should be listed.
if ($file->status != $status) {
@@ -111,7 +111,7 @@ class ProjectInfo {
'name' => $project_name,
// Only save attributes from the .info.yml file we care about so we do
// not bloat our RAM usage needlessly.
'info' => $this->filterProjectInfo($file->info, $additional_whitelist),
'info' => $this->filterProjectInfo($file->info, $additional_elements),
'datestamp' => $file->info['datestamp'],
'includes' => [$file->getName() => $file->info['name']],
'project_type' => $project_display_type,
@@ -165,7 +165,7 @@ class ProjectInfo {
* @param array $info
* Array of .info.yml file data as returned by
* \Drupal\Core\Extension\InfoParser.
* @param $additional_whitelist
* @param $additional_elements
* (optional) Array of additional elements to be collected from the .info.yml
* file. Defaults to array().
*
@@ -174,8 +174,8 @@ class ProjectInfo {
*
* @see \Drupal\Core\Utility\ProjectInfo::processInfoList()
*/
public function filterProjectInfo($info, $additional_whitelist = []) {
$whitelist = [
public function filterProjectInfo($info, $additional_elements = []) {
$elements = [
'_info_file_ctime',
'datestamp',
'major',
@@ -185,8 +185,8 @@ class ProjectInfo {
'project status url',
'version',
];
$whitelist = array_merge($whitelist, $additional_whitelist);
return array_intersect_key($info, array_combine($whitelist, $whitelist));
$elements = array_merge($elements, $additional_elements);
return array_intersect_key($info, array_combine($elements, $elements));
}
}