Update to drupal 8.0.0-rc1. For more information, see https://www.drupal.org/node/2582663
This commit is contained in:
parent
eb34d130a8
commit
f32e58e4b1
8476 changed files with 211648 additions and 170042 deletions
52
core/lib/Drupal/Core/Ajax/BaseCommand.php
Normal file
52
core/lib/Drupal/Core/Ajax/BaseCommand.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Ajax\BaseCommand.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Ajax;
|
||||
|
||||
/**
|
||||
* Base command that only exists to simplify AJAX commands.
|
||||
*/
|
||||
class BaseCommand implements CommandInterface {
|
||||
|
||||
/**
|
||||
* The name of the command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $command;
|
||||
|
||||
/**
|
||||
* The data to pass on to the client side.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Constructs a BaseCommand object.
|
||||
*
|
||||
* @param string $command
|
||||
* The name of the command.
|
||||
* @param string $data
|
||||
* The data to pass on to the client side.
|
||||
*/
|
||||
public function __construct($command, $data) {
|
||||
$this->command = $command;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
return array(
|
||||
'command' => $this->command,
|
||||
'data' => $this->data,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -29,7 +29,7 @@ trait CommandWithAttachedAssetsTrait {
|
|||
* If content is a render array, it may contain attached assets to be
|
||||
* processed.
|
||||
*
|
||||
* @return string|\Drupal\Component\Utility\SafeStringInterface
|
||||
* @return string|\Drupal\Component\Render\MarkupInterface
|
||||
* HTML rendered content.
|
||||
*/
|
||||
protected function getRenderedContent() {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
namespace Drupal\Core\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
use Drupal\Core\StringTranslation\TranslationWrapper;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
|
||||
/**
|
||||
* @defgroup plugin_context Annotation for context definition
|
||||
|
@ -113,7 +113,7 @@ class ContextDefinition extends Plugin {
|
|||
// used in the classes they pass to.
|
||||
foreach (['label', 'description'] as $key) {
|
||||
// @todo Remove this workaround in https://www.drupal.org/node/2362727.
|
||||
if (isset($values[$key]) && $values[$key] instanceof TranslationWrapper) {
|
||||
if (isset($values[$key]) && $values[$key] instanceof TranslatableMarkup) {
|
||||
$values[$key] = (string) $values[$key]->get();
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
namespace Drupal\Core\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\AnnotationBase;
|
||||
use Drupal\Core\StringTranslation\TranslationWrapper;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
|
||||
/**
|
||||
* @defgroup plugin_translatable Annotation for translatable text
|
||||
|
@ -60,7 +60,7 @@ class Translation extends AnnotationBase {
|
|||
/**
|
||||
* The string translation object.
|
||||
*
|
||||
* @var \Drupal\Core\StringTranslation\TranslationWrapper
|
||||
* @var \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
*/
|
||||
protected $translation;
|
||||
|
||||
|
@ -86,7 +86,7 @@ class Translation extends AnnotationBase {
|
|||
'context' => $values['context'],
|
||||
);
|
||||
}
|
||||
$this->translation = new TranslationWrapper($string, $arguments, $options);
|
||||
$this->translation = new TranslatableMarkup($string, $arguments, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -166,6 +166,7 @@ class AssetResolver implements AssetResolverInterface {
|
|||
uasort($css, 'static::sort');
|
||||
|
||||
// Allow themes to remove CSS files by CSS files full path and file name.
|
||||
// @todo Remove in Drupal 9.0.x.
|
||||
if ($stylesheet_remove = $theme_info->getStyleSheetsRemove()) {
|
||||
foreach ($css as $key => $options) {
|
||||
if (isset($stylesheet_remove[$key])) {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Asset\Exception\InvalidLibrariesExtendSpecificationException.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Asset\Exception;
|
||||
|
||||
/**
|
||||
* Defines a custom exception for an invalid libraries-extend specification.
|
||||
*/
|
||||
class InvalidLibrariesExtendSpecificationException extends \RuntimeException {
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Asset\Exception\InvalidLibrariesOverrideSpecificationException.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Asset\Exception;
|
||||
|
||||
/**
|
||||
* Defines a custom exception if a definition refers to a non-existent library.
|
||||
*/
|
||||
class InvalidLibrariesOverrideSpecificationException extends \RuntimeException {
|
||||
|
||||
}
|
|
@ -9,8 +9,6 @@ namespace Drupal\Core\Asset;
|
|||
|
||||
use Drupal\Core\Cache\CacheCollectorInterface;
|
||||
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
|
||||
/**
|
||||
* Discovers available asset libraries in Drupal.
|
||||
|
@ -87,6 +85,8 @@ class LibraryDiscovery implements LibraryDiscoveryInterface {
|
|||
*/
|
||||
public function clearCachedDefinitions() {
|
||||
$this->cacheTagInvalidator->invalidateTags(['library_info']);
|
||||
$this->libraryDefinitions = [];
|
||||
$this->collector->clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
namespace Drupal\Core\Asset;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Asset\Exception\InvalidLibrariesExtendSpecificationException;
|
||||
use Drupal\Core\Asset\Exception\InvalidLibrariesOverrideSpecificationException;
|
||||
use Drupal\Core\Cache\CacheCollector;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
|
@ -79,9 +82,94 @@ class LibraryDiscoveryCollector extends CacheCollector {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function resolveCacheMiss($key) {
|
||||
$this->storage[$key] = $this->discoveryParser->buildByExtension($key);
|
||||
$this->storage[$key] = $this->getLibraryDefinitions($key);
|
||||
$this->persist($key);
|
||||
|
||||
return $this->storage[$key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the library definitions for a given extension.
|
||||
*
|
||||
* This also implements libraries-overrides for entire libraries that have
|
||||
* been specified by the LibraryDiscoveryParser.
|
||||
*
|
||||
* @param string $extension
|
||||
* The name of the extension for which library definitions will be returned.
|
||||
*
|
||||
* @return array
|
||||
* The library definitions for $extension with overrides applied.
|
||||
*
|
||||
* @throws \Drupal\Core\Asset\Exception\InvalidLibrariesOverrideSpecificationException
|
||||
*/
|
||||
protected function getLibraryDefinitions($extension) {
|
||||
$libraries = $this->discoveryParser->buildByExtension($extension);
|
||||
foreach ($libraries as $name => $definition) {
|
||||
// Handle libraries that are marked for override or removal.
|
||||
// @see \Drupal\Core\Asset\LibraryDiscoveryParser::applyLibrariesOverride()
|
||||
if (isset($definition['override'])) {
|
||||
if ($definition['override'] === FALSE) {
|
||||
// Remove the library definition if FALSE is given.
|
||||
unset($libraries[$name]);
|
||||
}
|
||||
else {
|
||||
// Otherwise replace with existing library definition if it exists.
|
||||
// Throw an exception if it doesn't.
|
||||
list($replacement_extension, $replacement_name) = explode('/', $definition['override']);
|
||||
$replacement_definition = $this->get($replacement_extension);
|
||||
if (isset($replacement_definition[$replacement_name])) {
|
||||
$libraries[$name] = $replacement_definition[$replacement_name];
|
||||
}
|
||||
else {
|
||||
throw new InvalidLibrariesOverrideSpecificationException(sprintf('The specified library %s does not exist.', $definition['override']));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If libraries are not overridden, then apply libraries-extend.
|
||||
$libraries[$name] = $this->applyLibrariesExtend($extension, $name, $definition);
|
||||
}
|
||||
}
|
||||
return $libraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the libraries-extend specified by the active theme.
|
||||
*
|
||||
* This extends the library definitions with the those specified by the
|
||||
* libraries-extend specifications for the active theme.
|
||||
*
|
||||
* @param string $extension
|
||||
* The name of the extension for which library definitions will be extended.
|
||||
* @param string $library_name
|
||||
* The name of the library whose definitions is to be extended.
|
||||
* @param $library_definition
|
||||
* The library definition to be extended.
|
||||
*
|
||||
* @return array
|
||||
* The library definition extended as specified by libraries-extend.
|
||||
*
|
||||
* @throws \Drupal\Core\Asset\Exception\InvalidLibrariesExtendSpecificationException
|
||||
*/
|
||||
protected function applyLibrariesExtend($extension, $library_name, $library_definition) {
|
||||
$libraries_extend = $this->themeManager->getActiveTheme()->getLibrariesExtend();
|
||||
if (!empty($libraries_extend["$extension/$library_name"])) {
|
||||
foreach ($libraries_extend["$extension/$library_name"] as $library_extend_name) {
|
||||
if (!is_string($library_extend_name)) {
|
||||
// Only string library names are allowed.
|
||||
throw new InvalidLibrariesExtendSpecificationException('The libraries-extend specification for each library must be a list of strings.');
|
||||
}
|
||||
list($new_extension, $new_library_name) = explode('/', $library_extend_name, 2);
|
||||
$new_libraries = $this->get($new_extension);
|
||||
if (isset($new_libraries[$new_library_name])) {
|
||||
$library_definition = NestedArray::mergeDeep($library_definition, $new_libraries[$new_library_name]);
|
||||
}
|
||||
else {
|
||||
throw new InvalidLibrariesExtendSpecificationException(sprintf('The specified library "%s" does not exist.', $library_extend_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $library_definition;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
namespace Drupal\Core\Asset;
|
||||
|
||||
use Drupal\Core\Asset\Exception\IncompleteLibraryDefinitionException;
|
||||
use Drupal\Core\Asset\Exception\InvalidLibrariesOverrideSpecificationException;
|
||||
use Drupal\Core\Asset\Exception\InvalidLibraryFileException;
|
||||
use Drupal\Core\Asset\Exception\LibraryDefinitionMissingLicenseException;
|
||||
use Drupal\Core\Extension\Extension;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
|
||||
|
@ -88,6 +90,7 @@ class LibraryDiscoveryParser {
|
|||
}
|
||||
|
||||
$libraries = $this->parseLibraryInfo($extension, $path);
|
||||
$libraries = $this->applyLibrariesOverride($libraries, $extension);
|
||||
|
||||
foreach ($libraries as $id => &$library) {
|
||||
if (!isset($library['js']) && !isset($library['css']) && !isset($library['drupalSettings'])) {
|
||||
|
@ -185,6 +188,13 @@ class LibraryDiscoveryParser {
|
|||
elseif ($this->fileValidUri($source)) {
|
||||
$options['data'] = $source;
|
||||
}
|
||||
// A regular URI (e.g., http://example.com/example.js) without
|
||||
// 'external' explicitly specified, which may happen if, e.g.
|
||||
// libraries-override is used.
|
||||
elseif ($this->isValidUri($source)) {
|
||||
$options['type'] = 'external';
|
||||
$options['data'] = $source;
|
||||
}
|
||||
// By default, file paths are relative to the registering extension.
|
||||
else {
|
||||
$options['data'] = $path . '/' . $source;
|
||||
|
@ -313,6 +323,70 @@ class LibraryDiscoveryParser {
|
|||
return $libraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply libraries overrides specified for the current active theme.
|
||||
*
|
||||
* @param array $libraries
|
||||
* The libraries definitions.
|
||||
* @param string $extension
|
||||
* The extension in which these libraries are defined.
|
||||
*
|
||||
* @return array
|
||||
* The modified libraries definitions.
|
||||
*/
|
||||
protected function applyLibrariesOverride($libraries, $extension) {
|
||||
$active_theme = $this->themeManager->getActiveTheme();
|
||||
// ActiveTheme::getLibrariesOverride() returns libraries-overrides for the
|
||||
// current theme as well as all its base themes.
|
||||
$all_libraries_overrides = $active_theme->getLibrariesOverride();
|
||||
foreach ($all_libraries_overrides as $theme_path => $libraries_overrides) {
|
||||
foreach ($libraries as $library_name => $library) {
|
||||
// Process libraries overrides.
|
||||
if (isset($libraries_overrides["$extension/$library_name"])) {
|
||||
// Active theme defines an override for this library.
|
||||
$override_definition = $libraries_overrides["$extension/$library_name"];
|
||||
if (is_string($override_definition) || $override_definition === FALSE) {
|
||||
// A string or boolean definition implies an override (or removal)
|
||||
// for the whole library. Use the override key to specify that this
|
||||
// library will be overridden when it is called.
|
||||
// @see \Drupal\Core\Asset\LibraryDiscovery::getLibraryByName()
|
||||
if ($override_definition) {
|
||||
$libraries[$library_name]['override'] = $override_definition;
|
||||
}
|
||||
else {
|
||||
$libraries[$library_name]['override'] = FALSE;
|
||||
}
|
||||
}
|
||||
elseif (is_array($override_definition)) {
|
||||
// An array definition implies an override for an asset within this
|
||||
// library.
|
||||
foreach ($override_definition as $sub_key => $value) {
|
||||
// Throw an exception if the asset is not properly specified.
|
||||
if (!is_array($value)) {
|
||||
throw new InvalidLibrariesOverrideSpecificationException(sprintf('Library asset %s is not correctly specified. It should be in the form "extension/library_name/sub_key/path/to/asset.js".', "$extension/$library_name/$sub_key"));
|
||||
}
|
||||
if ($sub_key === 'drupalSettings') {
|
||||
// drupalSettings may not be overridden.
|
||||
throw new InvalidLibrariesOverrideSpecificationException(sprintf('drupalSettings may not be overridden in libraries-override. Trying to override %s. Use hook_library_info_alter() instead.', "$extension/$library_name/$sub_key"));
|
||||
}
|
||||
elseif ($sub_key === 'css') {
|
||||
// SMACSS category should be incorporated into the asset name.
|
||||
foreach ($value as $category => $overrides) {
|
||||
$this->setOverrideValue($libraries[$library_name], [$sub_key, $category], $overrides, $theme_path);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->setOverrideValue($libraries[$library_name], [$sub_key], $value, $theme_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $libraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps drupal_get_path().
|
||||
*/
|
||||
|
@ -327,4 +401,67 @@ class LibraryDiscoveryParser {
|
|||
return file_valid_uri($source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the supplied string is a valid URI.
|
||||
*/
|
||||
protected function isValidUri($string) {
|
||||
return count(explode('://', $string)) === 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the specified library asset.
|
||||
*
|
||||
* @param array $library
|
||||
* The containing library definition.
|
||||
* @param array $sub_key
|
||||
* An array containing the sub-keys specifying the library asset, e.g.
|
||||
* @code['js']@endcode or @code['css', 'component']@endcode
|
||||
* @param array $overrides
|
||||
* Specifies the overrides, this is an array where the key is the asset to
|
||||
* be overridden while the value is overriding asset.
|
||||
*/
|
||||
protected function setOverrideValue(array &$library, array $sub_key, array $overrides, $theme_path) {
|
||||
foreach ($overrides as $original => $replacement) {
|
||||
// Get the attributes of the asset to be overridden. If the key does
|
||||
// not exist, then throw an exception.
|
||||
$key_exists = NULL;
|
||||
$parents = array_merge($sub_key, [$original]);
|
||||
// Save the attributes of the library asset to be overridden.
|
||||
$attributes = NestedArray::getValue($library, $parents, $key_exists);
|
||||
if ($key_exists) {
|
||||
// Remove asset to be overridden.
|
||||
NestedArray::unsetValue($library, $parents);
|
||||
// No need to replace if FALSE is specified, since that is a removal.
|
||||
if ($replacement) {
|
||||
// Ensure the replacement path is relative to drupal root.
|
||||
$replacement = $this->resolveThemeAssetPath($theme_path, $replacement);
|
||||
$new_parents = array_merge($sub_key, [$replacement]);
|
||||
// Replace with an override if specified.
|
||||
NestedArray::setValue($library, $new_parents, $attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a full path is returned for an overriding theme asset.
|
||||
*
|
||||
* @param string $theme_path
|
||||
* The theme or base theme.
|
||||
* @param string $overriding_asset
|
||||
* The overriding library asset.
|
||||
*
|
||||
* @return string
|
||||
* A fully resolved theme asset path relative to the Drupal directory.
|
||||
*/
|
||||
protected function resolveThemeAssetPath($theme_path, $overriding_asset) {
|
||||
if ($overriding_asset[0] !== '/' && !$this->isValidUri($overriding_asset)) {
|
||||
// The destination is not an absolute path and it's not a URI (e.g.
|
||||
// public://generated_js/example.js or http://example.com/js/my_js.js), so
|
||||
// it's relative to the theme.
|
||||
return '/' . $theme_path . '/' . $overriding_asset;
|
||||
}
|
||||
return $overriding_asset;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,13 +9,12 @@ namespace Drupal\Core\Block;
|
|||
|
||||
use Drupal\block\BlockInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginBase;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Component\Transliteration\TransliterationInterface;
|
||||
|
||||
|
@ -30,6 +29,8 @@ use Drupal\Component\Transliteration\TransliterationInterface;
|
|||
*/
|
||||
abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginInterface {
|
||||
|
||||
use ContextAwarePluginAssignmentTrait;
|
||||
|
||||
/**
|
||||
* The transliteration service.
|
||||
*
|
||||
|
@ -47,7 +48,7 @@ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginIn
|
|||
|
||||
$definition = $this->getPluginDefinition();
|
||||
// Cast the admin label to a string since it is an object.
|
||||
// @see \Drupal\Core\StringTranslation\TranslationWrapper
|
||||
// @see \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
return (string) $definition['admin_label'];
|
||||
}
|
||||
|
||||
|
@ -89,10 +90,6 @@ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginIn
|
|||
'label' => '',
|
||||
'provider' => $this->pluginDefinition['provider'],
|
||||
'label_display' => BlockInterface::BLOCK_LABEL_VISIBLE,
|
||||
'cache' => array(
|
||||
// Blocks are cacheable by default.
|
||||
'max_age' => Cache::PERMANENT,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -180,24 +177,10 @@ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginIn
|
|||
'#default_value' => ($this->configuration['label_display'] === BlockInterface::BLOCK_LABEL_VISIBLE),
|
||||
'#return_value' => BlockInterface::BLOCK_LABEL_VISIBLE,
|
||||
);
|
||||
// Identical options to the ones for page caching.
|
||||
// @see \Drupal\system\Form\PerformanceForm::buildForm()
|
||||
$period = array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400);
|
||||
$period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($period, $period));
|
||||
$period[0] = '<' . $this->t('no caching') . '>';
|
||||
$period[\Drupal\Core\Cache\Cache::PERMANENT] = $this->t('Forever');
|
||||
$form['cache'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Cache settings'),
|
||||
);
|
||||
$form['cache']['max_age'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Maximum age'),
|
||||
'#description' => $this->t('The maximum time this block may be cached.'),
|
||||
'#default_value' => $this->configuration['cache']['max_age'],
|
||||
'#options' => $period,
|
||||
);
|
||||
|
||||
// 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;
|
||||
|
@ -244,7 +227,6 @@ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginIn
|
|||
$this->configuration['label'] = $form_state->getValue('label');
|
||||
$this->configuration['label_display'] = $form_state->getValue('label_display');
|
||||
$this->configuration['provider'] = $form_state->getValue('provider');
|
||||
$this->configuration['cache'] = $form_state->getValue('cache');
|
||||
$this->blockSubmit($form, $form_state);
|
||||
}
|
||||
}
|
||||
|
@ -272,16 +254,6 @@ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginIn
|
|||
return $transliterated;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
$max_age = parent::getCacheMaxAge();
|
||||
// @todo Configurability of this will be removed in
|
||||
// https://www.drupal.org/node/2458763.
|
||||
return Cache::mergeMaxAges($max_age, (int) $this->configuration['cache']['max_age']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the transliteration service.
|
||||
*
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Drupal\Core\Block;
|
|||
/**
|
||||
* The interface for "main page content" blocks.
|
||||
*
|
||||
* A main page content block represents the content returns by the controller.
|
||||
* A main page content block represents the content returned by the controller.
|
||||
*
|
||||
* @ingroup block_api
|
||||
*/
|
||||
|
|
55
core/lib/Drupal/Core/Block/Plugin/Block/PageTitleBlock.php
Normal file
55
core/lib/Drupal/Core/Block/Plugin/Block/PageTitleBlock.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Plugin\Block\PageTitleBlock.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Block\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Block\TitleBlockPluginInterface;
|
||||
|
||||
/**
|
||||
* Provides a block to display the page title.
|
||||
*
|
||||
* @Block(
|
||||
* id = "page_title_block",
|
||||
* admin_label = @Translation("Page title"),
|
||||
* )
|
||||
*/
|
||||
class PageTitleBlock extends BlockBase implements TitleBlockPluginInterface {
|
||||
|
||||
/**
|
||||
* The page title: a string (plain title) or a render array (formatted title).
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
protected $title = '';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return ['label_display' => FALSE];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return [
|
||||
'#type' => 'page_title',
|
||||
'#title' => $this->title,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
30
core/lib/Drupal/Core/Block/TitleBlockPluginInterface.php
Normal file
30
core/lib/Drupal/Core/Block/TitleBlockPluginInterface.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Block\TitleBlockPluginInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Block;
|
||||
|
||||
/**
|
||||
* The interface for "title" blocks.
|
||||
*
|
||||
* A title block shows the title returned by the controller.
|
||||
*
|
||||
* @ingroup block_api
|
||||
*
|
||||
* @see \Drupal\Core\Render\Element\PageTitle
|
||||
*/
|
||||
interface TitleBlockPluginInterface extends BlockPluginInterface {
|
||||
|
||||
/**
|
||||
* Sets the title.
|
||||
*
|
||||
* @param string|array $title
|
||||
* The page title: either a string for plain titles or a render array for
|
||||
* formatted titles.
|
||||
*/
|
||||
public function setTitle($title);
|
||||
|
||||
}
|
|
@ -7,15 +7,17 @@
|
|||
|
||||
namespace Drupal\Core\Breadcrumb;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\Core\Render\RenderableInterface;
|
||||
|
||||
/**
|
||||
* Used to return generated breadcrumbs with associated cacheability metadata.
|
||||
*
|
||||
* @todo implement RenderableInterface once https://www.drupal.org/node/2529560 lands.
|
||||
*/
|
||||
class Breadcrumb extends CacheableMetadata {
|
||||
class Breadcrumb implements RenderableInterface, RefinableCacheableDependencyInterface {
|
||||
|
||||
use RefinableCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* An ordered list of links for the breadcrumb.
|
||||
|
@ -68,4 +70,24 @@ class Breadcrumb extends CacheableMetadata {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toRenderable() {
|
||||
$build = [
|
||||
'#cache' => [
|
||||
'contexts' => $this->cacheContexts,
|
||||
'tags' => $this->cacheTags,
|
||||
'max-age' => $this->cacheMaxAge,
|
||||
],
|
||||
];
|
||||
if (!empty($this->links)) {
|
||||
$build += [
|
||||
'#theme' => 'breadcrumb',
|
||||
'#links' => $this->links,
|
||||
];
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ class ApcuBackend implements CacheBackendInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {
|
||||
Cache::validateTags($tags);
|
||||
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache tags must be strings.');
|
||||
$tags = array_unique($tags);
|
||||
$cache = new \stdClass();
|
||||
$cache->cid = $cid;
|
||||
|
|
|
@ -34,7 +34,7 @@ class Cache {
|
|||
*/
|
||||
public static function mergeContexts(array $a = [], array $b = []) {
|
||||
$cache_contexts = array_unique(array_merge($a, $b));
|
||||
\Drupal::service('cache_contexts_manager')->validateTokens($cache_contexts);
|
||||
assert('\Drupal::service(\'cache_contexts_manager\')->assertValidTokens($cache_contexts)');
|
||||
sort($cache_contexts);
|
||||
return $cache_contexts;
|
||||
}
|
||||
|
@ -59,8 +59,9 @@ class Cache {
|
|||
* The merged array of cache tags.
|
||||
*/
|
||||
public static function mergeTags(array $a = [], array $b = []) {
|
||||
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($a) && \Drupal\Component\Assertion\Inspector::assertAllStrings($b)', 'Cache tags must be valid strings');
|
||||
|
||||
$cache_tags = array_unique(array_merge($a, $b));
|
||||
static::validateTags($cache_tags);
|
||||
sort($cache_tags);
|
||||
return $cache_tags;
|
||||
}
|
||||
|
@ -99,6 +100,9 @@ class Cache {
|
|||
* @param string[] $tags
|
||||
* An array of cache tags.
|
||||
*
|
||||
* @deprecated
|
||||
* Use assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)');
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public static function validateTags(array $tags) {
|
||||
|
|
|
@ -115,7 +115,7 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn
|
|||
* (optional) The tags to specify for the cache item.
|
||||
*/
|
||||
public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, array $tags = array()) {
|
||||
Cache::validateTags($tags);
|
||||
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache tags must be strings.');
|
||||
$this->cid = $cid;
|
||||
$this->cache = $cache;
|
||||
$this->tags = $tags;
|
||||
|
|
|
@ -27,8 +27,7 @@ class CacheTagsInvalidator implements CacheTagsInvalidatorInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
// Validate the tags.
|
||||
Cache::validateTags($tags);
|
||||
assert('Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache tags must be strings.');
|
||||
|
||||
// Notify all added cache tags invalidators.
|
||||
foreach ($this->invalidators as $invalidator) {
|
||||
|
|
|
@ -103,11 +103,9 @@ class CacheContextsManager {
|
|||
* The ContextCacheKeys object containing the converted cache keys and
|
||||
* cacheability metadata.
|
||||
*
|
||||
* @throws \LogicException
|
||||
* Thrown if any of the context tokens or parameters are not valid.
|
||||
*/
|
||||
public function convertTokensToKeys(array $context_tokens) {
|
||||
$this->validateTokens($context_tokens);
|
||||
assert('$this->assertValidTokens($context_tokens)');
|
||||
$cacheable_metadata = new CacheableMetadata();
|
||||
$optimized_tokens = $this->optimizeTokens($context_tokens);
|
||||
// Iterate over cache contexts that have been optimized away and get their
|
||||
|
@ -299,4 +297,33 @@ class CacheContextsManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the context tokens are valid
|
||||
*
|
||||
* Similar to ::validateTokens, this method returns boolean TRUE when the
|
||||
* context tokens are valid, and FALSE when they are not instead of returning
|
||||
* NULL when they are valid and throwing a \LogicException when they are not.
|
||||
* This function should be used with the assert() statement.
|
||||
*
|
||||
* @param mixed $context_tokens
|
||||
* Variable to be examined - should be array of context_tokens.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if context_tokens is an array of valid tokens.
|
||||
*/
|
||||
public function assertValidTokens($context_tokens) {
|
||||
if (!is_array($context_tokens)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->validateTokens($context_tokens);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ class DatabaseBackend implements CacheBackendInterface {
|
|||
'tags' => array(),
|
||||
);
|
||||
|
||||
Cache::validateTags($item['tags']);
|
||||
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($item[\'tags\'])', 'Cache Tags must be strings.');
|
||||
$item['tags'] = array_unique($item['tags']);
|
||||
// Sort the cache tags so that they are stored consistently in the DB.
|
||||
sort($item['tags']);
|
||||
|
|
|
@ -107,7 +107,7 @@ class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterf
|
|||
* Implements Drupal\Core\Cache\CacheBackendInterface::set().
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
|
||||
Cache::validateTags($tags);
|
||||
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache Tags must be strings.');
|
||||
$tags = array_unique($tags);
|
||||
// Sort the cache tags so that they are stored consistently in the database.
|
||||
sort($tags);
|
||||
|
|
|
@ -148,7 +148,7 @@ class PhpBackend implements CacheBackendInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
|
||||
Cache::validateTags($tags);
|
||||
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache Tags must be strings.');
|
||||
$item = (object) array(
|
||||
'cid' => $cid,
|
||||
'data' => $data,
|
||||
|
|
|
@ -33,6 +33,27 @@ trait RefinableCacheableDependencyTrait {
|
|||
*/
|
||||
protected $cacheMaxAge = Cache::PERMANENT;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return $this->cacheTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return $this->cacheContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return $this->cacheMaxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
65
core/lib/Drupal/Core/Command/DbCommandBase.php
Normal file
65
core/lib/Drupal/Core/Command/DbCommandBase.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Command\DbCommandBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Command;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Base command that abstracts handling of database connection arguments.
|
||||
*/
|
||||
class DbCommandBase extends Command {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this->addOption('database', NULL, InputOption::VALUE_OPTIONAL, 'The database connection name to use.', 'default')
|
||||
->addOption('database-url', 'db-url', InputOption::VALUE_OPTIONAL, 'A database url to parse and use as the database connection.')
|
||||
->addOption('prefix', NULL, InputOption::VALUE_OPTIONAL, 'Override or set the table prefix used in the database connection.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse input options decide on a database.
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* Input object.
|
||||
* @return \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected function getDatabaseConnection(InputInterface $input) {
|
||||
// Load connection from a url.
|
||||
if ($input->getOption('database-url')) {
|
||||
// @todo this could probably be refactored to not use a global connection.
|
||||
// Ensure database connection isn't set.
|
||||
if (Database::getConnectionInfo('db-tools')) {
|
||||
throw new \RuntimeException('Database "db-tools" is already defined. Cannot define database provided.');
|
||||
}
|
||||
$info = Database::convertDbUrlToConnectionInfo($input->getOption('database-url'), \Drupal::root());
|
||||
Database::addConnectionInfo('db-tools', 'default', $info);
|
||||
$key = 'db-tools';
|
||||
}
|
||||
else {
|
||||
$key = $input->getOption('database');
|
||||
}
|
||||
|
||||
// If they supplied a prefix, replace it in the connection information.
|
||||
$prefix = $input->getOption('prefix');
|
||||
if ($prefix) {
|
||||
$info = Database::getConnectionInfo($key)['default'];
|
||||
$info['prefix']['default'] = $prefix;
|
||||
|
||||
Database::removeConnection($key);
|
||||
Database::addConnectionInfo($key, 'default', $info);
|
||||
}
|
||||
|
||||
return Database::getConnection('default', $key);
|
||||
}
|
||||
|
||||
}
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\Command;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
|
@ -17,34 +15,6 @@ use Symfony\Component\Console\Input\InputInterface;
|
|||
*/
|
||||
class DbDumpApplication extends Application {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Construct the application.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
function __construct(Connection $connection, ModuleHandlerInterface $module_handler) {
|
||||
$this->connection = $connection;
|
||||
$this->moduleHandler = $module_handler;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -58,7 +28,7 @@ class DbDumpApplication extends Application {
|
|||
protected function getDefaultCommands() {
|
||||
// Even though this is a single command, keep the HelpCommand (--help).
|
||||
$default_commands = parent::getDefaultCommands();
|
||||
$default_commands[] = new DbDumpCommand($this->connection, $this->moduleHandler);
|
||||
$default_commands[] = new DbDumpCommand();
|
||||
return $default_commands;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,8 @@ namespace Drupal\Core\Command;
|
|||
|
||||
use Drupal\Component\Utility\Variable;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
|
@ -30,21 +29,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||
*
|
||||
* @see \Drupal\Core\Command\DbDumpApplication
|
||||
*/
|
||||
class DbDumpCommand extends Command {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection $connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
class DbDumpCommand extends DbCommandBase {
|
||||
|
||||
/**
|
||||
* An array of table patterns to exclude completely.
|
||||
|
@ -55,81 +40,78 @@ class DbDumpCommand extends Command {
|
|||
*/
|
||||
protected $excludeTables = ['simpletest.+'];
|
||||
|
||||
/**
|
||||
* Table patterns for which to only dump the schema, no data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $schemaOnly = ['cache.*', 'sessions', 'watchdog'];
|
||||
|
||||
/**
|
||||
* Construct the database dump command.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler to use.
|
||||
*/
|
||||
function __construct(Connection $connection, ModuleHandlerInterface $module_handler) {
|
||||
// Check this is MySQL.
|
||||
if ($connection->databaseType() !== 'mysql') {
|
||||
throw new \RuntimeException('This script can only be used with MySQL database backends.');
|
||||
}
|
||||
|
||||
$this->connection = $connection;
|
||||
$this->moduleHandler = $module_handler;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this->setName('dump-database-d8-mysql')
|
||||
->setDescription('Dump the current database to a generation script');
|
||||
->setDescription('Dump the current database to a generation script')
|
||||
->addOption('schema-only', NULL, InputOption::VALUE_OPTIONAL, 'A comma separated list of tables to only export the schema without data.', 'cache.*,sessions,watchdog');
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$connection = $this->getDatabaseConnection($input);
|
||||
|
||||
// If not explicitly set, disable ANSI which will break generated php.
|
||||
if ($input->hasParameterOption(['--ansi']) !== TRUE) {
|
||||
$output->setDecorated(FALSE);
|
||||
}
|
||||
|
||||
$output->writeln($this->generateScript());
|
||||
$schema_tables = $input->getOption('schema-only');
|
||||
$schema_tables = explode(',', $schema_tables);
|
||||
|
||||
$output->writeln($this->generateScript($connection, $schema_tables), OutputInterface::OUTPUT_RAW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the database script.
|
||||
*
|
||||
* @return string
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @param array $schema_only
|
||||
* Table patterns for which to only dump the schema, no data.
|
||||
* @return string The PHP script.
|
||||
* The PHP script.
|
||||
*/
|
||||
protected function generateScript() {
|
||||
protected function generateScript(Connection $connection, array $schema_only = []) {
|
||||
$tables = '';
|
||||
foreach ($this->getTables() as $table) {
|
||||
$schema = $this->getTableSchema($table);
|
||||
$data = $this->getTableData($table);
|
||||
|
||||
$schema_only_patterns = [];
|
||||
foreach ($schema_only as $match) {
|
||||
$schema_only_patterns[] = '/^' . $match . '$/';
|
||||
}
|
||||
|
||||
foreach ($this->getTables($connection) as $table) {
|
||||
$schema = $this->getTableSchema($connection, $table);
|
||||
// Check for schema only.
|
||||
if (empty($schema_only_patterns) || preg_replace($schema_only_patterns, '', $table)) {
|
||||
$data = $this->getTableData($connection, $table);
|
||||
}
|
||||
else {
|
||||
$data = [];
|
||||
}
|
||||
$tables .= $this->getTableScript($table, $schema, $data);
|
||||
}
|
||||
$script = $this->getTemplate();
|
||||
// Substitute in the tables.
|
||||
$script = str_replace('{{TABLES}}', trim($tables), $script);
|
||||
// Modules.
|
||||
$script = str_replace('{{MODULES}}', $this->getModulesScript(), $script);
|
||||
return trim($script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of tables, not including those set to be excluded.
|
||||
*
|
||||
* @return array
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @return array An array of table names.
|
||||
* An array of table names.
|
||||
*/
|
||||
protected function getTables() {
|
||||
$tables = array_values($this->connection->schema()->findTables('%'));
|
||||
protected function getTables(Connection $connection) {
|
||||
$tables = array_values($connection->schema()->findTables('%'));
|
||||
|
||||
foreach ($tables as $key => $table) {
|
||||
// Remove any explicitly excluded tables.
|
||||
|
@ -146,6 +128,8 @@ class DbDumpCommand extends Command {
|
|||
/**
|
||||
* Returns a schema array for a given table.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @param string $table
|
||||
* The table name.
|
||||
*
|
||||
|
@ -154,14 +138,19 @@ class DbDumpCommand extends Command {
|
|||
*
|
||||
* @todo This implementation is hard-coded for MySQL.
|
||||
*/
|
||||
protected function getTableSchema($table) {
|
||||
$query = $this->connection->query("SHOW FULL COLUMNS FROM {" . $table . "}");
|
||||
protected function getTableSchema(Connection $connection, $table) {
|
||||
// Check this is MySQL.
|
||||
if ($connection->databaseType() !== 'mysql') {
|
||||
throw new \RuntimeException('This script can only be used with MySQL database backends.');
|
||||
}
|
||||
|
||||
$query = $connection->query("SHOW FULL COLUMNS FROM {" . $table . "}");
|
||||
$definition = [];
|
||||
while (($row = $query->fetchAssoc()) !== FALSE) {
|
||||
$name = $row['Field'];
|
||||
// Parse out the field type and meta information.
|
||||
preg_match('@([a-z]+)(?:\((\d+)(?:,(\d+))?\))?\s*(unsigned)?@', $row['Type'], $matches);
|
||||
$type = $this->fieldTypeMap($matches[1]);
|
||||
$type = $this->fieldTypeMap($connection, $matches[1]);
|
||||
if ($row['Extra'] === 'auto_increment') {
|
||||
// If this is an auto increment, then the type is 'serial'.
|
||||
$type = 'serial';
|
||||
|
@ -170,7 +159,7 @@ class DbDumpCommand extends Command {
|
|||
'type' => $type,
|
||||
'not null' => $row['Null'] === 'NO',
|
||||
];
|
||||
if ($size = $this->fieldSizeMap($matches[1])) {
|
||||
if ($size = $this->fieldSizeMap($connection, $matches[1])) {
|
||||
$definition['fields'][$name]['size'] = $size;
|
||||
}
|
||||
if (isset($matches[2]) && $type === 'numeric') {
|
||||
|
@ -216,10 +205,10 @@ class DbDumpCommand extends Command {
|
|||
}
|
||||
|
||||
// Set primary key, unique keys, and indexes.
|
||||
$this->getTableIndexes($table, $definition);
|
||||
$this->getTableIndexes($connection, $table, $definition);
|
||||
|
||||
// Set table collation.
|
||||
$this->getTableCollation($table, $definition);
|
||||
$this->getTableCollation($connection, $table, $definition);
|
||||
|
||||
return $definition;
|
||||
}
|
||||
|
@ -227,15 +216,17 @@ class DbDumpCommand extends Command {
|
|||
/**
|
||||
* Adds primary key, unique keys, and index information to the schema.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @param string $table
|
||||
* The table to find indexes for.
|
||||
* @param array &$definition
|
||||
* The schema definition to modify.
|
||||
*/
|
||||
protected function getTableIndexes($table, &$definition) {
|
||||
protected function getTableIndexes(Connection $connection, $table, &$definition) {
|
||||
// Note, this query doesn't support ordering, so that is worked around
|
||||
// below by keying the array on Seq_in_index.
|
||||
$query = $this->connection->query("SHOW INDEX FROM {" . $table . "}");
|
||||
$query = $connection->query("SHOW INDEX FROM {" . $table . "}");
|
||||
while (($row = $query->fetchAssoc()) !== FALSE) {
|
||||
$index_name = $row['Key_name'];
|
||||
$column = $row['Column_name'];
|
||||
|
@ -262,13 +253,15 @@ class DbDumpCommand extends Command {
|
|||
/**
|
||||
* Set the table collation.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @param string $table
|
||||
* The table to find indexes for.
|
||||
* @param array &$definition
|
||||
* The schema definition to modify.
|
||||
*/
|
||||
protected function getTableCollation($table, &$definition) {
|
||||
$query = $this->connection->query("SHOW TABLE STATUS LIKE '{" . $table . "}'");
|
||||
protected function getTableCollation(Connection $connection, $table, &$definition) {
|
||||
$query = $connection->query("SHOW TABLE STATUS LIKE '{" . $table . "}'");
|
||||
$data = $query->fetchAssoc();
|
||||
|
||||
// Set `mysql_character_set`. This will be ignored by other backends.
|
||||
|
@ -280,21 +273,17 @@ class DbDumpCommand extends Command {
|
|||
*
|
||||
* If a table is set to be schema only, and empty array is returned.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @param string $table
|
||||
* The table to query.
|
||||
*
|
||||
* @return array
|
||||
* The data from the table as an array.
|
||||
*/
|
||||
protected function getTableData($table) {
|
||||
// Check for schema only.
|
||||
foreach ($this->schemaOnly as $schema_only) {
|
||||
if (preg_match('/^' . $schema_only . '$/', $table)) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
$order = $this->getFieldOrder($table);
|
||||
$query = $this->connection->query("SELECT * FROM {" . $table . "} " . $order );
|
||||
protected function getTableData(Connection $connection, $table) {
|
||||
$order = $this->getFieldOrder($connection, $table);
|
||||
$query = $connection->query("SELECT * FROM {" . $table . "} " . $order);
|
||||
$results = [];
|
||||
while (($row = $query->fetchAssoc()) !== FALSE) {
|
||||
$results[] = $row;
|
||||
|
@ -305,6 +294,8 @@ class DbDumpCommand extends Command {
|
|||
/**
|
||||
* Given a database field type, return a Drupal type.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @param string $type
|
||||
* The MySQL field type.
|
||||
*
|
||||
|
@ -312,9 +303,9 @@ class DbDumpCommand extends Command {
|
|||
* The Drupal schema field type. If there is no mapping, the original field
|
||||
* type is returned.
|
||||
*/
|
||||
protected function fieldTypeMap($type) {
|
||||
protected function fieldTypeMap(Connection $connection, $type) {
|
||||
// Convert everything to lowercase.
|
||||
$map = array_map('strtolower', $this->connection->schema()->getFieldTypeMap());
|
||||
$map = array_map('strtolower', $connection->schema()->getFieldTypeMap());
|
||||
$map = array_flip($map);
|
||||
|
||||
// The MySql map contains type:size. Remove the size part.
|
||||
|
@ -324,15 +315,17 @@ class DbDumpCommand extends Command {
|
|||
/**
|
||||
* Given a database field type, return a Drupal size.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @param string $type
|
||||
* The MySQL field type.
|
||||
*
|
||||
* @return string
|
||||
* The Drupal schema field size.
|
||||
*/
|
||||
protected function fieldSizeMap($type) {
|
||||
protected function fieldSizeMap(Connection $connection, $type) {
|
||||
// Convert everything to lowercase.
|
||||
$map = array_map('strtolower', $this->connection->schema()->getFieldTypeMap());
|
||||
$map = array_map('strtolower', $connection->schema()->getFieldTypeMap());
|
||||
$map = array_flip($map);
|
||||
|
||||
$schema_type = explode(':', $map[$type])[0];
|
||||
|
@ -346,24 +339,26 @@ class DbDumpCommand extends Command {
|
|||
/**
|
||||
* Gets field ordering for a given table.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection to use.
|
||||
* @param string $table
|
||||
* The table name.
|
||||
*
|
||||
* @return string
|
||||
* The order string to append to the query.
|
||||
*/
|
||||
protected function getFieldOrder($table) {
|
||||
protected function getFieldOrder(Connection $connection, $table) {
|
||||
// @todo this is MySQL only since there are no Database API functions for
|
||||
// table column data.
|
||||
// @todo this code is duplicated in `core/scripts/migrate-db.sh`.
|
||||
$connection_info = $this->connection->getConnectionOptions();
|
||||
$connection_info = $connection->getConnectionOptions();
|
||||
// Order by primary keys.
|
||||
$order = '';
|
||||
$query = "SELECT `COLUMN_NAME` FROM `information_schema`.`COLUMNS`
|
||||
WHERE (`TABLE_SCHEMA` = '" . $connection_info['database'] . "')
|
||||
AND (`TABLE_NAME` = '{" . $table . "}') AND (`COLUMN_KEY` = 'PRI')
|
||||
ORDER BY COLUMN_NAME";
|
||||
$results = $this->connection->query($query);
|
||||
$results = $connection->query($query);
|
||||
while (($row = $results->fetchAssoc()) !== FALSE) {
|
||||
$order .= $row['COLUMN_NAME'] . ', ';
|
||||
}
|
||||
|
@ -384,12 +379,9 @@ class DbDumpCommand extends Command {
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Filled installation of Drupal 8.0, for test purposes.
|
||||
* A database agnostic dump for testing purposes.
|
||||
*
|
||||
* This file was generated by the dump-database-d8.php script, from an
|
||||
* installation of Drupal 8. It has the following modules installed:
|
||||
*
|
||||
{{MODULES}}
|
||||
* This file was generated by the Drupal 8.0 db-tools.php script.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
@ -431,20 +423,4 @@ ENDOFSCRIPT;
|
|||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of modules enabled for insertion into the script docblock.
|
||||
*
|
||||
* @return string
|
||||
* The formatted list of enabled modules.
|
||||
*/
|
||||
protected function getModulesScript() {
|
||||
$output = '';
|
||||
$modules = $this->moduleHandler->getModuleList();
|
||||
ksort($modules);
|
||||
foreach ($modules as $module => $filename) {
|
||||
$output .= " * - $module\n";
|
||||
}
|
||||
return rtrim($output, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
75
core/lib/Drupal/Core/Command/DbImportCommand.php
Normal file
75
core/lib/Drupal/Core/Command/DbImportCommand.php
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Command\DbDumpCommand.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Command;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\SchemaObjectExistsException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Provides a command to import the current database from a script.
|
||||
*
|
||||
* This script runs on databases exported using using one of the database dump
|
||||
* commands and imports it into the current database connection.
|
||||
*
|
||||
* @see \Drupal\Core\Command\DbImportApplication
|
||||
*/
|
||||
class DbImportCommand extends DbCommandBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
parent::configure();
|
||||
$this->setName('import')
|
||||
->setDescription('Import database from a generation script.')
|
||||
->addArgument('script', InputOption::VALUE_REQUIRED, 'Import script');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$script = $input->getArgument('script');
|
||||
if (!is_file($script)) {
|
||||
$output->writeln('File must exist.');
|
||||
return;
|
||||
}
|
||||
|
||||
$connection = $this->getDatabaseConnection($input);
|
||||
$this->runScript($connection, $script);
|
||||
$output->writeln('Import completed successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the database script.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* Connection used by the script when included.
|
||||
* @param string $script
|
||||
* Path to dump script.
|
||||
*/
|
||||
protected function runScript(Connection $connection, $script) {
|
||||
$old_key = Database::setActiveConnection($connection->getKey());
|
||||
|
||||
if (substr($script, -3) == '.gz') {
|
||||
$script = "compress.zlib://$script";
|
||||
}
|
||||
try {
|
||||
require $script;
|
||||
}
|
||||
catch (SchemaObjectExistsException $e) {
|
||||
throw new \RuntimeException('An existing Drupal installation exists at this location. Try removing all tables or changing the database prefix in your settings.php file.');
|
||||
}
|
||||
Database::setActiveConnection($old_key);
|
||||
}
|
||||
|
||||
}
|
34
core/lib/Drupal/Core/Command/DbToolsApplication.php
Normal file
34
core/lib/Drupal/Core/Command/DbToolsApplication.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Command\DbToolsApplication.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Command;
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
/**
|
||||
* Provides a command to import a database generation script.
|
||||
*/
|
||||
class DbToolsApplication extends Application {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct('Database Tools', '8.0.x');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultCommands() {
|
||||
$default_commands = parent::getDefaultCommands();
|
||||
$default_commands[] = new DbDumpCommand();
|
||||
$default_commands[] = new DbImportCommand();
|
||||
return $default_commands;
|
||||
}
|
||||
|
||||
}
|
|
@ -48,7 +48,15 @@ class BootstrapConfigStorageFactory {
|
|||
/**
|
||||
* Returns a File-based configuration storage implementation.
|
||||
*
|
||||
* If there is no active configuration directory calling this method will
|
||||
* result in an error.
|
||||
*
|
||||
* @return \Drupal\Core\Config\FileStorage
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x and will be removed before 9.0.0. Drupal core
|
||||
* no longer creates an active directory.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getFileStorage() {
|
||||
return new FileStorage(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY));
|
||||
|
|
|
@ -108,8 +108,8 @@ class Config extends StorableConfigBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setData(array $data, $validate_keys = TRUE) {
|
||||
parent::setData($data, $validate_keys);
|
||||
public function setData(array $data) {
|
||||
parent::setData($data);
|
||||
$this->resetOverriddenData();
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
|
||||
|
@ -153,10 +154,6 @@ abstract class ConfigBase implements RefinableCacheableDependencyInterface {
|
|||
*
|
||||
* @param array $data
|
||||
* The new configuration data.
|
||||
* @param bool $validate_keys
|
||||
* (optional) Whether the data should be verified for valid keys. Set to
|
||||
* FALSE if the $data is known to be valid already (for example, being
|
||||
* loaded from the config storage).
|
||||
*
|
||||
* @return $this
|
||||
* The configuration object.
|
||||
|
@ -164,10 +161,9 @@ abstract class ConfigBase implements RefinableCacheableDependencyInterface {
|
|||
* @throws \Drupal\Core\Config\ConfigValueException
|
||||
* If any key in $data in any depth contains a dot.
|
||||
*/
|
||||
public function setData(array $data, $validate_keys = TRUE) {
|
||||
if ($validate_keys) {
|
||||
$this->validateKeys($data);
|
||||
}
|
||||
public function setData(array $data) {
|
||||
$data = $this->castSafeStrings($data);
|
||||
$this->validateKeys($data);
|
||||
$this->data = $data;
|
||||
return $this;
|
||||
}
|
||||
|
@ -187,6 +183,7 @@ abstract class ConfigBase implements RefinableCacheableDependencyInterface {
|
|||
* If $value is an array and any of its keys in any depth contains a dot.
|
||||
*/
|
||||
public function set($key, $value) {
|
||||
$value = $this->castSafeStrings($value);
|
||||
// The dot/period is a reserved character; it may appear between keys, but
|
||||
// not within keys.
|
||||
if (is_array($value)) {
|
||||
|
@ -280,4 +277,27 @@ abstract class ConfigBase implements RefinableCacheableDependencyInterface {
|
|||
return $this->cacheMaxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts any objects that implement MarkupInterface to string.
|
||||
*
|
||||
* @param mixed $data
|
||||
* The configuration data.
|
||||
*
|
||||
* @return mixed
|
||||
* The data with any safe strings cast to string.
|
||||
*/
|
||||
protected function castSafeStrings($data) {
|
||||
if ($data instanceof MarkupInterface) {
|
||||
$data = (string) $data;
|
||||
}
|
||||
else if (is_array($data)) {
|
||||
array_walk_recursive($data, function (&$value) {
|
||||
if ($value instanceof MarkupInterface) {
|
||||
$value = (string) $value;
|
||||
}
|
||||
});
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -367,8 +367,8 @@ class ConfigImporter {
|
|||
$current_extensions = $this->storageComparer->getTargetStorage()->read('core.extension');
|
||||
$new_extensions = $this->storageComparer->getSourceStorage()->read('core.extension');
|
||||
|
||||
// If there is no extension information in staging then exit. This is
|
||||
// probably due to an empty staging directory.
|
||||
// If there is no extension information in sync then exit. This is probably
|
||||
// due to an empty sync directory.
|
||||
if (!$new_extensions) {
|
||||
return;
|
||||
}
|
||||
|
@ -718,11 +718,11 @@ class ConfigImporter {
|
|||
$old_entity_type_id = $this->configManager->getEntityTypeIdByName($names['old_name']);
|
||||
$new_entity_type_id = $this->configManager->getEntityTypeIdByName($names['new_name']);
|
||||
if ($old_entity_type_id != $new_entity_type_id) {
|
||||
$this->logError($this->t('Entity type mismatch on rename. !old_type not equal to !new_type for existing configuration !old_name and staged configuration !new_name.', array('old_type' => $old_entity_type_id, 'new_type' => $new_entity_type_id, 'old_name' => $names['old_name'], 'new_name' => $names['new_name'])));
|
||||
$this->logError($this->t('Entity type mismatch on rename. @old_type not equal to @new_type for existing configuration @old_name and staged configuration @new_name.', array('@old_type' => $old_entity_type_id, '@new_type' => $new_entity_type_id, '@old_name' => $names['old_name'], '@new_name' => $names['new_name'])));
|
||||
}
|
||||
// Has to be a configuration entity.
|
||||
if (!$old_entity_type_id) {
|
||||
$this->logError($this->t('Rename operation for simple configuration. Existing configuration !old_name and staged configuration !new_name.', array('old_name' => $names['old_name'], 'new_name' => $names['new_name'])));
|
||||
$this->logError($this->t('Rename operation for simple configuration. Existing configuration @old_name and staged configuration @new_name.', array('@old_name' => $names['old_name'], '@new_name' => $names['new_name'])));
|
||||
}
|
||||
}
|
||||
$this->eventDispatcher->dispatch(ConfigEvents::IMPORT_VALIDATE, new ConfigImporterEvent($this));
|
||||
|
@ -762,7 +762,7 @@ class ConfigImporter {
|
|||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->logError($this->t('Unexpected error during import with operation @op for @name: !message', array('@op' => $op, '@name' => $name, '!message' => $e->getMessage())));
|
||||
$this->logError($this->t('Unexpected error during import with operation @op for @name: @message', array('@op' => $op, '@name' => $name, '@message' => $e->getMessage())));
|
||||
// Error for that operation was logged, mark it as processed so that
|
||||
// the import can continue.
|
||||
$this->setProcessedConfiguration($collection, $op, $name);
|
||||
|
@ -780,7 +780,7 @@ class ConfigImporter {
|
|||
* The name of the extension to process.
|
||||
*/
|
||||
protected function processExtension($type, $op, $name) {
|
||||
// Set the config installer to use the staging directory instead of the
|
||||
// Set the config installer to use the sync directory instead of the
|
||||
// extensions own default config directories.
|
||||
\Drupal::service('config.installer')
|
||||
->setSyncing(TRUE)
|
||||
|
|
|
@ -155,11 +155,15 @@ class ConfigManager implements ConfigManagerInterface {
|
|||
// Check for new or removed files.
|
||||
if ($source_data === array('false')) {
|
||||
// Added file.
|
||||
$source_data = array($this->t('File added'));
|
||||
// Cast the result of t() to a string, as the diff engine doesn't know
|
||||
// about objects.
|
||||
$source_data = array((string) $this->t('File added'));
|
||||
}
|
||||
if ($target_data === array('false')) {
|
||||
// Deleted file.
|
||||
$target_data = array($this->t('File removed'));
|
||||
// Cast the result of t() to a string, as the diff engine doesn't know
|
||||
// about objects.
|
||||
$target_data = array((string) $this->t('File removed'));
|
||||
}
|
||||
|
||||
return new Diff($source_data, $target_data);
|
||||
|
@ -316,7 +320,8 @@ class ConfigManager implements ConfigManagerInterface {
|
|||
}
|
||||
if ($this->callOnDependencyRemoval($dependent, $original_dependencies, $type, $names)) {
|
||||
// Recalculate dependencies and update the dependency graph data.
|
||||
$dependency_manager->updateData($dependent->getConfigDependencyName(), $dependent->calculateDependencies());
|
||||
$dependent->calculateDependencies();
|
||||
$dependency_manager->updateData($dependent->getConfigDependencyName(), $dependent->getDependencies());
|
||||
// Based on the updated data rebuild the list of dependents.
|
||||
$dependents = $this->findConfigEntityDependentsAsEntities($type, $names, $dependency_manager);
|
||||
// Ensure that the dependency has actually been fixed. It is possible
|
||||
|
@ -454,6 +459,9 @@ class ConfigManager implements ConfigManagerInterface {
|
|||
if (isset($config_data['dependencies']['content'])) {
|
||||
$content_dependencies = array_merge($content_dependencies, $config_data['dependencies']['content']);
|
||||
}
|
||||
if (isset($config_data['dependencies']['enforced']['content'])) {
|
||||
$content_dependencies = array_merge($content_dependencies, $config_data['dependencies']['enforced']['content']);
|
||||
}
|
||||
}
|
||||
foreach (array_unique($content_dependencies) as $content_dependency) {
|
||||
// Format of the dependency is entity_type:bundle:uuid.
|
||||
|
|
|
@ -53,8 +53,7 @@ trait ConfigDependencyDeleteFormTrait {
|
|||
'#type' => 'details',
|
||||
'#title' => $this->t('Configuration updates'),
|
||||
'#description' => $this->t('The listed configuration will be updated.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#open' => TRUE,
|
||||
'#access' => FALSE,
|
||||
);
|
||||
|
||||
|
@ -72,7 +71,7 @@ trait ConfigDependencyDeleteFormTrait {
|
|||
'#items' => array(),
|
||||
);
|
||||
}
|
||||
$form['entity_updates'][$entity_type_id]['#items'][] = $entity->label() ?: $entity->id();
|
||||
$form['entity_updates'][$entity_type_id]['#items'][$entity->id()] = $entity->label() ?: $entity->id();
|
||||
}
|
||||
if (!empty($dependent_entities['update'])) {
|
||||
$form['entity_updates']['#access'] = TRUE;
|
||||
|
@ -83,7 +82,7 @@ trait ConfigDependencyDeleteFormTrait {
|
|||
foreach ($entity_types as $entity_type_id => $label) {
|
||||
$form['entity_updates'][$entity_type_id]['#weight'] = $weight;
|
||||
// Sort the list of entity labels alphabetically.
|
||||
sort($form['entity_updates'][$entity_type_id]['#items'], SORT_FLAG_CASE);
|
||||
ksort($form['entity_updates'][$entity_type_id]['#items'], SORT_FLAG_CASE);
|
||||
$weight++;
|
||||
}
|
||||
}
|
||||
|
@ -92,8 +91,7 @@ trait ConfigDependencyDeleteFormTrait {
|
|||
'#type' => 'details',
|
||||
'#title' => $this->t('Configuration deletions'),
|
||||
'#description' => $this->t('The listed configuration will be deleted.'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#open' => TRUE,
|
||||
'#access' => FALSE,
|
||||
);
|
||||
|
||||
|
@ -110,7 +108,7 @@ trait ConfigDependencyDeleteFormTrait {
|
|||
'#items' => array(),
|
||||
);
|
||||
}
|
||||
$form['entity_deletes'][$entity_type_id]['#items'][] = $entity->label() ?: $entity->id();
|
||||
$form['entity_deletes'][$entity_type_id]['#items'][$entity->id()] = $entity->label() ?: $entity->id();
|
||||
}
|
||||
if (!empty($dependent_entities['delete'])) {
|
||||
$form['entity_deletes']['#access'] = TRUE;
|
||||
|
@ -119,10 +117,12 @@ trait ConfigDependencyDeleteFormTrait {
|
|||
asort($entity_types, SORT_FLAG_CASE);
|
||||
$weight = 0;
|
||||
foreach ($entity_types as $entity_type_id => $label) {
|
||||
$form['entity_deletes'][$entity_type_id]['#weight'] = $weight;
|
||||
// Sort the list of entity labels alphabetically.
|
||||
sort($form['entity_deletes'][$entity_type_id]['#items'], SORT_FLAG_CASE);
|
||||
$weight++;
|
||||
if (isset($form['entity_deletes'][$entity_type_id])) {
|
||||
$form['entity_deletes'][$entity_type_id]['#weight'] = $weight;
|
||||
// Sort the list of entity labels alphabetically.
|
||||
ksort($form['entity_deletes'][$entity_type_id]['#items'], SORT_FLAG_CASE);
|
||||
$weight++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,11 +58,11 @@ use Drupal\Component\Utility\SortArray;
|
|||
* Configuration entity classes usually extend
|
||||
* \Drupal\Core\Config\Entity\ConfigEntityBase. The base class provides a
|
||||
* generic implementation of the calculateDependencies() method that can
|
||||
* discover dependencies due to enforced dependencies, plugins, and third party
|
||||
* settings. If the configuration entity has dependencies that cannot be
|
||||
* discovered by the base class's implementation, then it needs to implement
|
||||
* discover dependencies due to plugins, and third party settings. If the
|
||||
* configuration entity has dependencies that cannot be discovered by the base
|
||||
* class's implementation, then it needs to implement
|
||||
* \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies() to
|
||||
* calculate (and return) the dependencies. In this method, use
|
||||
* calculate the dependencies. In this method, use
|
||||
* \Drupal\Core\Config\Entity\ConfigEntityBase::addDependency() to add
|
||||
* dependencies. Implementations should call the base class implementation to
|
||||
* inherit the generic functionality.
|
||||
|
@ -85,9 +85,9 @@ use Drupal\Component\Utility\SortArray;
|
|||
* configuration object so that they can be checked without the module that
|
||||
* provides the configuration entity class being installed. This is important
|
||||
* for configuration synchronization, which needs to be able to validate
|
||||
* configuration in the staging directory before the synchronization has
|
||||
* occurred. Also, if you have a configuration entity object and you want to
|
||||
* get the current dependencies without recalculation, you can use
|
||||
* configuration in the sync directory before the synchronization has occurred.
|
||||
* Also, if you have a configuration entity object and you want to get the
|
||||
* current dependencies (without recalculation), you can use
|
||||
* \Drupal\Core\Config\Entity\ConfigEntityInterface::getDependencies().
|
||||
*
|
||||
* When uninstalling a module or a theme, configuration entities that are
|
||||
|
@ -115,6 +115,7 @@ use Drupal\Component\Utility\SortArray;
|
|||
* module dependency in the sub-module only.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies()
|
||||
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::getDependencies()
|
||||
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::onDependencyRemoval()
|
||||
* @see \Drupal\Core\Config\Entity\ConfigEntityBase::addDependency()
|
||||
* @see \Drupal\Core\Config\ConfigInstallerInterface::installDefaultConfig()
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Config\Entity;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Config\ConfigException;
|
||||
use Drupal\Core\Config\Schema\SchemaIncompleteException;
|
||||
|
@ -341,7 +342,7 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
|
|||
throw new ConfigDuplicateUUIDException("Attempt to save a configuration entity '{$this->id()}' with UUID '{$this->uuid()}' when this entity already exists with UUID '{$original->uuid()}'");
|
||||
}
|
||||
}
|
||||
if (!$this->isSyncing() && !$this->trustedData) {
|
||||
if (!$this->isSyncing()) {
|
||||
// Ensure the correct dependencies are present. If the configuration is
|
||||
// being written during a configuration synchronization then there is no
|
||||
// need to recalculate the dependencies.
|
||||
|
@ -353,16 +354,9 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
// Dependencies should be recalculated on every save. This ensures stale
|
||||
// dependencies are never saved.
|
||||
if (isset($this->dependencies['enforced'])) {
|
||||
$dependencies = $this->dependencies['enforced'];
|
||||
$this->dependencies = $dependencies;
|
||||
$this->dependencies['enforced'] = $dependencies;
|
||||
}
|
||||
else {
|
||||
$this->dependencies = array();
|
||||
}
|
||||
// All dependencies should be recalculated on every save apart from enforced
|
||||
// dependencies. This ensures stale dependencies are never saved.
|
||||
$this->dependencies = array_intersect_key($this->dependencies, ['enforced' => '']);
|
||||
if ($this instanceof EntityWithPluginCollectionInterface) {
|
||||
// Configuration entities need to depend on the providers of any plugins
|
||||
// that they store the configuration for.
|
||||
|
@ -379,13 +373,15 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
|
|||
$this->addDependency('module', $provider);
|
||||
}
|
||||
}
|
||||
return $this->dependencies;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function urlInfo($rel = 'edit-form', array $options = []) {
|
||||
// Unless language was already provided, avoid setting an explicit language.
|
||||
$options += ['language' => NULL];
|
||||
return parent::urlInfo($rel, $options);
|
||||
}
|
||||
|
||||
|
@ -436,7 +432,14 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDependencies() {
|
||||
return $this->dependencies;
|
||||
$dependencies = $this->dependencies;
|
||||
if (isset($dependencies['enforced'])) {
|
||||
// Merge the enforced dependencies into the list of dependencies.
|
||||
$enforced_dependencies = $dependencies['enforced'];
|
||||
unset($dependencies['enforced']);
|
||||
$dependencies = NestedArray::mergeDeep($dependencies, $enforced_dependencies);
|
||||
}
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Config\Entity;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\Core\Config\ConfigNameException;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
|
||||
/**
|
||||
|
@ -19,31 +18,6 @@ use Drupal\Core\Entity\EntityStorageInterface;
|
|||
*/
|
||||
abstract class ConfigEntityBundleBase extends ConfigEntityBase {
|
||||
|
||||
/**
|
||||
* Renames displays when a bundle is renamed.
|
||||
*/
|
||||
protected function renameDisplays() {
|
||||
// Rename entity displays.
|
||||
if ($this->getOriginalId() !== $this->id()) {
|
||||
foreach ($this->loadDisplays('entity_view_display') as $display) {
|
||||
$new_id = $this->getEntityType()->getBundleOf() . '.' . $this->id() . '.' . $display->getMode();
|
||||
$display->set('id', $new_id);
|
||||
$display->setTargetBundle($this->id());
|
||||
$display->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Rename entity form displays.
|
||||
if ($this->getOriginalId() !== $this->id()) {
|
||||
foreach ($this->loadDisplays('entity_form_display') as $form_display) {
|
||||
$new_id = $this->getEntityType()->getBundleOf() . '.' . $this->id() . '.' . $form_display->getMode();
|
||||
$form_display->set('id', $new_id);
|
||||
$form_display->setTargetBundle($this->id());
|
||||
$form_display->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes display if a bundle is deleted.
|
||||
*/
|
||||
|
@ -80,12 +54,6 @@ abstract class ConfigEntityBundleBase extends ConfigEntityBase {
|
|||
}
|
||||
// Entity bundle field definitions may depend on bundle settings.
|
||||
$entity_manager->clearCachedFieldDefinitions();
|
||||
|
||||
if ($this->getOriginalId() != $this->id()) {
|
||||
// If the entity was renamed, update the displays.
|
||||
$this->renameDisplays();
|
||||
$entity_manager->onBundleRename($this->getOriginalId(), $this->id(), $bundle_of);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,6 +69,33 @@ abstract class ConfigEntityBundleBase extends ConfigEntityBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts on an entity before the presave hook is invoked.
|
||||
*
|
||||
* Used before the entity is saved and before invoking the presave hook.
|
||||
*
|
||||
* Ensure that config entities which are bundles of other entities cannot have
|
||||
* their ID changed.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage object.
|
||||
*
|
||||
* @throws \Drupal\Core\Config\ConfigNameException
|
||||
* Thrown when attempting to rename a bundle entity.
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
parent::preSave($storage);
|
||||
|
||||
// Only handle renames, not creations.
|
||||
if (!$this->isNew() && $this->getOriginalId() !== $this->id()) {
|
||||
$bundle_type = $this->getEntityType();
|
||||
$bundle_of = $bundle_type->getBundleOf();
|
||||
if (!empty($bundle_of)) {
|
||||
throw new ConfigNameException("The machine name of the '{$bundle_type->getLabel()}' bundle cannot be changed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns view or form displays for this bundle.
|
||||
*
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\Core\Config\Entity;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
|
||||
/**
|
||||
* Provides a value object to discover configuration dependencies.
|
||||
*
|
||||
|
@ -26,7 +28,7 @@ class ConfigEntityDependency {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dependencies;
|
||||
protected $dependencies = [];
|
||||
|
||||
/**
|
||||
* Constructs the configuration entity dependency from the entity values.
|
||||
|
@ -36,13 +38,16 @@ class ConfigEntityDependency {
|
|||
* @param array $values
|
||||
* (optional) The configuration entity's values.
|
||||
*/
|
||||
public function __construct($name, $values = array()) {
|
||||
public function __construct($name, $values = []) {
|
||||
$this->name = $name;
|
||||
if (isset($values['dependencies'])) {
|
||||
$this->dependencies = $values['dependencies'];
|
||||
if (isset($values['dependencies']) && isset($values['dependencies']['enforced'])) {
|
||||
// Merge the enforced dependencies into the list of dependencies.
|
||||
$enforced_dependencies = $values['dependencies']['enforced'];
|
||||
unset($values['dependencies']['enforced']);
|
||||
$this->dependencies = NestedArray::mergeDeep($values['dependencies'], $enforced_dependencies);
|
||||
}
|
||||
else {
|
||||
$this->dependencies = array();
|
||||
elseif (isset($values['dependencies'])) {
|
||||
$this->dependencies = $values['dependencies'];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -144,8 +144,7 @@ interface ConfigEntityInterface extends EntityInterface, ThirdPartySettingsInter
|
|||
/**
|
||||
* Calculates dependencies and stores them in the dependency property.
|
||||
*
|
||||
* @return array
|
||||
* An array of dependencies grouped by type (module, theme, entity).
|
||||
* @return $this
|
||||
*
|
||||
* @see \Drupal\Core\Config\Entity\ConfigDependencyManager
|
||||
*/
|
||||
|
|
|
@ -16,18 +16,21 @@ class FileStorageFactory {
|
|||
* Returns a FileStorage object working with the active config directory.
|
||||
*
|
||||
* @return \Drupal\Core\Config\FileStorage FileStorage
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x and will be removed before 9.0.0. Drupal core
|
||||
* no longer creates an active directory.
|
||||
*/
|
||||
static function getActive() {
|
||||
return new FileStorage(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a FileStorage object working with the staging config directory.
|
||||
* Returns a FileStorage object working with the sync config directory.
|
||||
*
|
||||
* @return \Drupal\Core\Config\FileStorage FileStorage
|
||||
*/
|
||||
static function getStaging() {
|
||||
return new FileStorage(config_get_config_directory(CONFIG_STAGING_DIRECTORY));
|
||||
static function getSync() {
|
||||
return new FileStorage(config_get_config_directory(CONFIG_SYNC_DIRECTORY));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,27 +7,10 @@
|
|||
|
||||
namespace Drupal\Core\Config\Schema;
|
||||
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\TypedData\TypedData;
|
||||
|
||||
/**
|
||||
* Defines a generic configuration element that contains multiple properties.
|
||||
*/
|
||||
abstract class ArrayElement extends TypedData implements \IteratorAggregate, TypedConfigInterface {
|
||||
|
||||
/**
|
||||
* The typed config manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
*/
|
||||
protected $typedConfig;
|
||||
|
||||
/**
|
||||
* The configuration value.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
abstract class ArrayElement extends Element implements \IteratorAggregate, TypedConfigInterface {
|
||||
|
||||
/**
|
||||
* Parsed elements.
|
||||
|
@ -152,7 +135,7 @@ abstract class ArrayElement extends TypedData implements \IteratorAggregate, Typ
|
|||
* @return \Drupal\Core\TypedData\TypedDataInterface
|
||||
*/
|
||||
protected function createElement($definition, $value, $key) {
|
||||
return $this->typedConfig->create($definition, $value, $key, $this);
|
||||
return $this->getTypedDataManager()->create($definition, $value, $key, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,20 +153,17 @@ abstract class ArrayElement extends TypedData implements \IteratorAggregate, Typ
|
|||
* @return \Drupal\Core\TypedData\DataDefinitionInterface
|
||||
*/
|
||||
protected function buildDataDefinition($definition, $value, $key) {
|
||||
return $this->typedConfig->buildDataDefinition($definition, $value, $key, $this);
|
||||
return $this->getTypedDataManager()->buildDataDefinition($definition, $value, $key, $this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the typed config manager on the instance.
|
||||
* Determines if this element allows NULL as a value.
|
||||
*
|
||||
* This must be called immediately after construction to enable
|
||||
* self::parseElement() and self::buildDataDefinition() to work.
|
||||
*
|
||||
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
|
||||
* @return bool
|
||||
* TRUE if NULL is a valid value, FALSE otherwise.
|
||||
*/
|
||||
public function setTypedConfig(TypedConfigManagerInterface $typed_config) {
|
||||
$this->typedConfig = $typed_config;
|
||||
public function isNullable() {
|
||||
return isset($this->definition['nullable']) && $this->definition['nullable'] == TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
namespace Drupal\Core\Config\Schema;
|
||||
|
||||
use Drupal\Core\Config\TypedConfigManagerInterface;
|
||||
use Drupal\Core\TypedData\TypedData;
|
||||
use Drupal\Core\TypedData\TypedDataManagerInterface;
|
||||
|
||||
/**
|
||||
* Defines a generic configuration element.
|
||||
|
@ -21,4 +23,41 @@ abstract class Element extends TypedData {
|
|||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Gets the typed configuration manager.
|
||||
*
|
||||
* Overrides \Drupal\Core\TypedData\TypedDataTrait::getTypedDataManager() to
|
||||
* ensure the typed configuration manager is returned.
|
||||
*
|
||||
* @return \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
* The typed configuration manager.
|
||||
*/
|
||||
public function getTypedDataManager() {
|
||||
if (empty($this->typedDataManager)) {
|
||||
$this->setTypedDataManager(\Drupal::service('config.typed'));
|
||||
}
|
||||
|
||||
return $this->typedDataManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the typed config manager.
|
||||
*
|
||||
* Overrides \Drupal\Core\TypedData\TypedDataTrait::setTypedDataManager() to
|
||||
* ensure that only typed configuration manager can be used.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
|
||||
* The typed config manager. This must be an instance of
|
||||
* \Drupal\Core\Config\TypedConfigManagerInterface. If it is not, then this
|
||||
* method will error when assertions are enabled. We can not narrow the
|
||||
* typehint as this will cause PHP errors.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTypedDataManager(TypedDataManagerInterface $typed_data_manager) {
|
||||
assert($typed_data_manager instanceof TypedConfigManagerInterface, '$typed_data_manager should be an instance of \Drupal\Core\Config\TypedConfigManagerInterface.');
|
||||
$this->typedDataManager = $typed_data_manager;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -106,9 +106,13 @@ trait SchemaCheckTrait {
|
|||
(($type == 'double' || $type == 'integer') && $element instanceof FloatInterface) ||
|
||||
($type == 'boolean' && $element instanceof BooleanInterface) ||
|
||||
($type == 'string' && $element instanceof StringInterface) ||
|
||||
// Null values are allowed for all types.
|
||||
// Null values are allowed for all primitive types.
|
||||
($value === NULL);
|
||||
}
|
||||
// Array elements can also opt-in for allowing a NULL value.
|
||||
elseif ($element instanceof ArrayElement && $element->isNullable() && $value === NULL) {
|
||||
$success = TRUE;
|
||||
}
|
||||
$class = get_class($element);
|
||||
if (!$success) {
|
||||
return array($error_key => "variable type is $type but applied schema class is $class");
|
||||
|
|
|
@ -98,7 +98,7 @@ abstract class StorableConfigBase extends ConfigBase {
|
|||
*/
|
||||
public function initWithData(array $data) {
|
||||
$this->isNew = FALSE;
|
||||
$this->setData($data, FALSE);
|
||||
$this->data = $data;
|
||||
$this->originalData = $this->data;
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -71,13 +71,7 @@ class TypedConfigManager extends TypedDataManager implements TypedConfigManagerI
|
|||
|
||||
|
||||
/**
|
||||
* Gets typed configuration data.
|
||||
*
|
||||
* @param string $name
|
||||
* Configuration object name.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Schema\TypedConfigInterface
|
||||
* Typed configuration data.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($name) {
|
||||
$data = $this->configStorage->read($name);
|
||||
|
@ -282,8 +276,12 @@ class TypedConfigManager extends TypedDataManager implements TypedConfigManagerI
|
|||
return $value;
|
||||
}
|
||||
elseif (!$parts) {
|
||||
$value = $data[$name];
|
||||
if (is_bool($value)) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
// If no more parts left, this is the final property.
|
||||
return (string)$data[$name];
|
||||
return (string) $value;
|
||||
}
|
||||
else {
|
||||
// Get nested value and continue processing.
|
||||
|
@ -338,17 +336,4 @@ class TypedConfigManager extends TypedDataManager implements TypedConfigManagerI
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createInstance($data_type, array $configuration = array()) {
|
||||
$instance = parent::createInstance($data_type, $configuration);
|
||||
// Enable elements to construct their own definitions using the typed config
|
||||
// manager.
|
||||
if ($instance instanceof ArrayElement) {
|
||||
$instance->setTypedConfig($this);
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\TypedDataManagerInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for managing config schema type plugins.
|
||||
|
@ -19,7 +17,7 @@ use Drupal\Core\TypedData\DataDefinitionInterface;
|
|||
* @see hook_config_schema_info_alter()
|
||||
* @see https://www.drupal.org/node/1905070
|
||||
*/
|
||||
Interface TypedConfigManagerInterface extends PluginManagerInterface, CachedDiscoveryInterface {
|
||||
Interface TypedConfigManagerInterface extends TypedDataManagerInterface {
|
||||
|
||||
/**
|
||||
* Gets typed configuration data.
|
||||
|
@ -32,48 +30,6 @@ Interface TypedConfigManagerInterface extends PluginManagerInterface, CachedDisc
|
|||
*/
|
||||
public function get($name);
|
||||
|
||||
/**
|
||||
* Instantiates a typed configuration object.
|
||||
*
|
||||
* @param string $data_type
|
||||
* The data type, for which a typed configuration object should be
|
||||
* instantiated.
|
||||
* @param array $configuration
|
||||
* The plugin configuration array, i.e. an array with the following keys:
|
||||
* - data definition: The data definition object, i.e. an instance of
|
||||
* \Drupal\Core\TypedData\DataDefinitionInterface.
|
||||
* - name: (optional) If a property or list item is to be created, the name
|
||||
* of the property or the delta of the list item.
|
||||
* - parent: (optional) If a property or list item is to be created, the
|
||||
* parent typed data object implementing either the ListInterface or the
|
||||
* ComplexDataInterface.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Schema\Element
|
||||
* The instantiated typed configuration object.
|
||||
*/
|
||||
public function createInstance($data_type, array $configuration = array());
|
||||
|
||||
/**
|
||||
* Creates a new typed configuration object instance.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
|
||||
* The data definition of the typed data object.
|
||||
* @param mixed $value
|
||||
* The data value. If set, it has to match one of the supported
|
||||
* data type format as documented for the data type classes.
|
||||
* @param string $name
|
||||
* (optional) If a property or list item is to be created, the name of the
|
||||
* property or the delta of the list item.
|
||||
* @param mixed $parent
|
||||
* (optional) If a property or list item is to be created, the parent typed
|
||||
* data object implementing either the ListInterface or the
|
||||
* ComplexDataInterface.
|
||||
*
|
||||
* @return \Drupal\Core\Config\Schema\Element
|
||||
* The instantiated typed data object.
|
||||
*/
|
||||
public function create(DataDefinitionInterface $definition, $value, $name = NULL, $parent = NULL);
|
||||
|
||||
/**
|
||||
* Creates a new data definition object from a type definition array and
|
||||
* actual configuration data. Since type definitions may contain variables
|
||||
|
|
|
@ -33,7 +33,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
*
|
||||
* @see \Drupal\Core\DependencyInjection\ContainerInjectionInterface
|
||||
*
|
||||
* @ingroup menu
|
||||
* @ingroup routing
|
||||
*/
|
||||
abstract class ControllerBase implements ContainerInjectionInterface {
|
||||
|
||||
|
|
|
@ -17,18 +17,20 @@ interface TitleResolverInterface {
|
|||
/**
|
||||
* Returns a static or dynamic title for the route.
|
||||
*
|
||||
* The returned title string must be safe to output in HTML. For example, an
|
||||
* implementation should call \Drupal\Component\Utility\SafeMarkup::checkPlain()
|
||||
* or \Drupal\Component\Utility\Xss::filterAdmin() on the string, or use
|
||||
* appropriate placeholders to sanitize dynamic content inside a localized
|
||||
* string before returning it. The title may contain HTML such as EM tags.
|
||||
* If the returned title can contain HTML that should not be escaped it should
|
||||
* return a render array, for example:
|
||||
* @code
|
||||
* ['#markup' => 'title', '#allowed_tags' => ['em']]
|
||||
* @endcode
|
||||
* If the method returns a string and it is not marked safe then it will be
|
||||
* auto-escaped.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request object passed to the title callback.
|
||||
* @param \Symfony\Component\Routing\Route $route
|
||||
* The route information of the route to fetch the title.
|
||||
*
|
||||
* @return string|null
|
||||
* @return array|string|null
|
||||
* The title for the route.
|
||||
*/
|
||||
public function getTitle(Request $request, Route $route);
|
||||
|
|
|
@ -24,7 +24,7 @@ use Drupal\Core\DependencyInjection\ServiceProviderInterface;
|
|||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\TaggedHandlersPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\RegisterEventSubscribersPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass;
|
||||
use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass;
|
||||
use Drupal\Core\Plugin\PluginManagerPass;
|
||||
|
@ -82,7 +82,7 @@ class CoreServiceProvider implements ServiceProviderInterface {
|
|||
$container->addCompilerPass(new TwigExtensionPass());
|
||||
|
||||
// Add a compiler pass for registering event subscribers.
|
||||
$container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
$container->addCompilerPass(new RegisterEventSubscribersPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||
|
||||
$container->addCompilerPass(new RegisterAccessChecksPass());
|
||||
$container->addCompilerPass(new RegisterLazyRouteEnhancers());
|
||||
|
|
|
@ -239,6 +239,12 @@ abstract class Connection {
|
|||
* further up the call chain can take an appropriate action. To suppress
|
||||
* that behavior and simply return NULL on failure, set this option to
|
||||
* FALSE.
|
||||
* - allow_delimiter_in_query: By default, queries which have the ; delimiter
|
||||
* any place in them will cause an exception. This reduces the chance of SQL
|
||||
* injection attacks that terminate the original query and add one or more
|
||||
* additional queries (such as inserting new user accounts). In rare cases,
|
||||
* such as creating an SQL function, a ; is needed and can be allowed by
|
||||
* changing this option to TRUE.
|
||||
*
|
||||
* @return array
|
||||
* An array of default query options.
|
||||
|
@ -249,6 +255,7 @@ abstract class Connection {
|
|||
'fetch' => \PDO::FETCH_OBJ,
|
||||
'return' => Database::RETURN_STATEMENT,
|
||||
'throw_exception' => TRUE,
|
||||
'allow_delimiter_in_query' => FALSE,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -491,7 +498,7 @@ abstract class Connection {
|
|||
return '';
|
||||
|
||||
// Flatten the array of comments.
|
||||
$comment = implode('; ', $comments);
|
||||
$comment = implode('. ', $comments);
|
||||
|
||||
// Sanitize the comment string so as to avoid SQL injection attacks.
|
||||
return '/* ' . $this->filterComment($comment) . ' */ ';
|
||||
|
@ -516,7 +523,7 @@ abstract class Connection {
|
|||
*
|
||||
* Would result in the following SQL statement being generated:
|
||||
* @code
|
||||
* "/ * Exploit * / DROP TABLE node; -- * / UPDATE example SET field2=..."
|
||||
* "/ * Exploit * / DROP TABLE node. -- * / UPDATE example SET field2=..."
|
||||
* @endcode
|
||||
*
|
||||
* Unless the comment is sanitised first, the SQL server would drop the
|
||||
|
@ -529,7 +536,8 @@ abstract class Connection {
|
|||
* A sanitized version of the query comment string.
|
||||
*/
|
||||
protected function filterComment($comment = '') {
|
||||
return strtr($comment, ['*' => ' * ']);
|
||||
// Change semicolons to period to avoid triggering multi-statement check.
|
||||
return strtr($comment, ['*' => ' * ', ';' => '.']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -593,6 +601,16 @@ abstract class Connection {
|
|||
}
|
||||
else {
|
||||
$this->expandArguments($query, $args);
|
||||
// To protect against SQL injection, Drupal only supports executing one
|
||||
// statement at a time. Thus, the presence of a SQL delimiter (the
|
||||
// semicolon) is not allowed unless the option is set. Allowing
|
||||
// semicolons should only be needed for special cases like defining a
|
||||
// function or stored procedure in SQL. Trim any trailing delimiter to
|
||||
// minimize false positives.
|
||||
$query = rtrim($query, "; \t\n\r\0\x0B");
|
||||
if (strpos($query, ';') !== FALSE && empty($options['allow_delimiter_in_query'])) {
|
||||
throw new \InvalidArgumentException('; is not supported in SQL strings. Use only one statement at a time.');
|
||||
}
|
||||
$stmt = $this->prepareQuery($query);
|
||||
$stmt->execute($args, $options);
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ class Tasks extends InstallTasks {
|
|||
catch (\Exception $e) {
|
||||
// Detect utf8mb4 incompability.
|
||||
if ($e->getCode() == Connection::UNSUPPORTED_CHARSET) {
|
||||
$this->fail(t('Your MySQL server and PHP MySQL driver must support utf8mb4 character encoding. Make sure to use a database system that supports this (such as MySQL/MariaDB/Percona 5.5.3 and up), and that the utf8mb4 character set is compiled in. See the <a href="@documentation" target="_blank">MySQL documentation</a> for more information.', array('@documentation' => 'https://dev.mysql.com/doc/refman/5.0/en/cannot-initialize-character-set.html')));
|
||||
$this->fail(t('Your MySQL server and PHP MySQL driver must support utf8mb4 character encoding. Make sure to use a database system that supports this (such as MySQL/MariaDB/Percona 5.5.3 and up), and that the utf8mb4 character set is compiled in. See the <a href=":documentation" target="_blank">MySQL documentation</a> for more information.', array(':documentation' => 'https://dev.mysql.com/doc/refman/5.0/en/cannot-initialize-character-set.html')));
|
||||
$info = Database::getConnectionInfo();
|
||||
$info_copy = $info;
|
||||
// Set a flag to fall back to utf8. Note: this flag should only be
|
||||
|
@ -164,13 +164,13 @@ class Tasks extends InstallTasks {
|
|||
// The mysqlnd driver supports utf8mb4 starting at version 5.0.9.
|
||||
$version = preg_replace('/^\D+([\d.]+).*/', '$1', $version);
|
||||
if (version_compare($version, self::MYSQLND_MINIMUM_VERSION, '<')) {
|
||||
$this->fail(t("The MySQLnd driver version %version is less than the minimum required version. Upgrade to MySQLnd version %mysqlnd_minimum_version or up, or alternatively switch mysql drivers to libmysqlclient version %libmysqlclient_minimum_version or up.", array('%version' => Database::getConnection()->version(), '%mysqlnd_minimum_version' => self::MYSQLND_MINIMUM_VERSION, '%libmysqlclient_minimum_version' => self::LIBMYSQLCLIENT_MINIMUM_VERSION)));
|
||||
$this->fail(t("The MySQLnd driver version %version is less than the minimum required version. Upgrade to MySQLnd version %mysqlnd_minimum_version or up, or alternatively switch mysql drivers to libmysqlclient version %libmysqlclient_minimum_version or up.", array('%version' => $version, '%mysqlnd_minimum_version' => self::MYSQLND_MINIMUM_VERSION, '%libmysqlclient_minimum_version' => self::LIBMYSQLCLIENT_MINIMUM_VERSION)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The libmysqlclient driver supports utf8mb4 starting at version 5.5.3.
|
||||
if (version_compare($version, self::LIBMYSQLCLIENT_MINIMUM_VERSION, '<')) {
|
||||
$this->fail(t("The libmysqlclient driver version %version is less than the minimum required version. Upgrade to libmysqlclient version %libmysqlclient_minimum_version or up, or alternatively switch mysql drivers to MySQLnd version %mysqlnd_minimum_version or up.", array('%version' => Database::getConnection()->version(), '%libmysqlclient_minimum_version' => self::LIBMYSQLCLIENT_MINIMUM_VERSION, '%mysqlnd_minimum_version' => self::MYSQLND_MINIMUM_VERSION)));
|
||||
$this->fail(t("The libmysqlclient driver version %version is less than the minimum required version. Upgrade to libmysqlclient version %libmysqlclient_minimum_version or up, or alternatively switch mysql drivers to MySQLnd version %mysqlnd_minimum_version or up.", array('%version' => $version, '%libmysqlclient_minimum_version' => self::LIBMYSQLCLIENT_MINIMUM_VERSION, '%mysqlnd_minimum_version' => self::MYSQLND_MINIMUM_VERSION)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -539,7 +539,8 @@ class Schema extends DatabaseSchema {
|
|||
// Add table prefixes before truncating.
|
||||
$comment = Unicode::truncate($this->connection->prefixTables($comment), $length, TRUE, TRUE);
|
||||
}
|
||||
|
||||
// Remove semicolons to avoid triggering multi-statement check.
|
||||
$comment = strtr($comment, array(';' => '.'));
|
||||
return $this->connection->quote($comment);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,10 @@ class Tasks extends InstallTasks {
|
|||
'function' => 'checkBinaryOutput',
|
||||
'arguments' => array(),
|
||||
);
|
||||
$this->tasks[] = array(
|
||||
'function' => 'checkStandardConformingStrings',
|
||||
'arguments' => array(),
|
||||
);
|
||||
$this->tasks[] = array(
|
||||
'function' => 'initializeDatabase',
|
||||
'arguments' => array(),
|
||||
|
@ -118,10 +122,9 @@ class Tasks extends InstallTasks {
|
|||
$this->pass(t('Database is encoded in UTF-8'));
|
||||
}
|
||||
else {
|
||||
$this->fail(t('The %driver database must use %encoding encoding to work with Drupal. Recreate the database with %encoding encoding. See !link for more details.', array(
|
||||
$this->fail(t('The %driver database must use %encoding encoding to work with Drupal. Recreate the database with %encoding encoding. See <a href="INSTALL.pgsql.txt">INSTALL.pgsql.txt</a> for more details.', array(
|
||||
'%encoding' => 'UTF8',
|
||||
'%driver' => $this->name(),
|
||||
'!link' => '<a href="INSTALL.pgsql.txt">INSTALL.pgsql.txt</a>'
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -168,9 +171,9 @@ class Tasks extends InstallTasks {
|
|||
'%setting' => 'bytea_output',
|
||||
'%current_value' => 'hex',
|
||||
'%needed_value' => 'escape',
|
||||
'!query' => "<code>" . $query . "</code>",
|
||||
'@query' => $query,
|
||||
);
|
||||
$this->fail(t("The %setting setting is currently set to '%current_value', but needs to be '%needed_value'. Change this by running the following query: !query", $replacements));
|
||||
$this->fail(t("The %setting setting is currently set to '%current_value', but needs to be '%needed_value'. Change this by running the following query: <code>@query</code>", $replacements));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,6 +187,58 @@ class Tasks extends InstallTasks {
|
|||
return ($bytea_output == 'escape');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures standard_conforming_strings setting is 'on'.
|
||||
*
|
||||
* When standard_conforming_strings setting is 'on' string literals ('...')
|
||||
* treat backslashes literally, as specified in the SQL standard. This allows
|
||||
* Drupal to convert between bytea, text and varchar columns.
|
||||
*/
|
||||
public function checkStandardConformingStrings() {
|
||||
$database_connection = Database::getConnection();
|
||||
if (!$this->checkStandardConformingStringsSuccess()) {
|
||||
// First try to alter the database. If it fails, raise an error telling
|
||||
// the user to do it themselves.
|
||||
$connection_options = $database_connection->getConnectionOptions();
|
||||
// It is safe to include the database name directly here, because this
|
||||
// code is only called when a connection to the database is already
|
||||
// established, thus the database name is guaranteed to be a correct
|
||||
// value.
|
||||
$query = "ALTER DATABASE \"" . $connection_options['database'] . "\" SET standard_conforming_strings = 'on';";
|
||||
try {
|
||||
$database_connection->query($query);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Ignore possible errors when the user doesn't have the necessary
|
||||
// privileges to ALTER the database.
|
||||
}
|
||||
|
||||
// Close the database connection so that the configuration parameter
|
||||
// is applied to the current connection.
|
||||
Database::closeConnection();
|
||||
|
||||
// Recheck, if it fails, finally just rely on the end user to do the
|
||||
// right thing.
|
||||
if (!$this->checkStandardConformingStringsSuccess()) {
|
||||
$replacements = array(
|
||||
'%setting' => 'standard_conforming_strings',
|
||||
'%current_value' => 'off',
|
||||
'%needed_value' => 'on',
|
||||
'@query' => $query,
|
||||
);
|
||||
$this->fail(t("The %setting setting is currently set to '%current_value', but needs to be '%needed_value'. Change this by running the following query: <code>@query</code>", $replacements));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the standard_conforming_strings setting.
|
||||
*/
|
||||
protected function checkStandardConformingStringsSuccess() {
|
||||
$standard_conforming_strings = Database::getConnection()->query("SHOW standard_conforming_strings")->fetchField();
|
||||
return ($standard_conforming_strings == 'on');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make PostgreSQL Drupal friendly.
|
||||
*/
|
||||
|
@ -195,18 +250,23 @@ class Tasks extends InstallTasks {
|
|||
// At the same time checking for the existence of the function fixes
|
||||
// concurrency issues, when both try to update at the same time.
|
||||
try {
|
||||
$connection = Database::getConnection();
|
||||
// Don't use {} around pg_proc table.
|
||||
if (!db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'")->fetchField()) {
|
||||
db_query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
|
||||
if (!$connection->query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'rand'")->fetchField()) {
|
||||
$connection->query('CREATE OR REPLACE FUNCTION "rand"() RETURNS float AS
|
||||
\'SELECT random();\'
|
||||
LANGUAGE \'sql\''
|
||||
LANGUAGE \'sql\'',
|
||||
[],
|
||||
[ 'allow_delimiter_in_query' => TRUE ]
|
||||
);
|
||||
}
|
||||
|
||||
if (!db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'substring_index'")->fetchField()) {
|
||||
db_query('CREATE OR REPLACE FUNCTION "substring_index"(text, text, integer) RETURNS text AS
|
||||
if (!$connection->query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'substring_index'")->fetchField()) {
|
||||
$connection->query('CREATE OR REPLACE FUNCTION "substring_index"(text, text, integer) RETURNS text AS
|
||||
\'SELECT array_to_string((string_to_array($1, $2)) [1:$3], $2);\'
|
||||
LANGUAGE \'sql\''
|
||||
LANGUAGE \'sql\'',
|
||||
[],
|
||||
[ 'allow_delimiter_in_query' => TRUE ]
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -726,17 +726,23 @@ class Schema extends DatabaseSchema {
|
|||
// Usually, we do this via a simple typecast 'USING fieldname::type'. But
|
||||
// the typecast does not work for conversions to bytea.
|
||||
// @see http://www.postgresql.org/docs/current/static/datatype-binary.html
|
||||
$table_information = $this->queryTableInformation($table);
|
||||
$is_bytea = !empty($table_information->blob_fields[$field]);
|
||||
if ($spec['pgsql_type'] != 'bytea') {
|
||||
$this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $field_def . ' USING "' . $field . '"::' . $field_def);
|
||||
if ($is_bytea) {
|
||||
$this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $field_def . ' USING convert_from("' . $field . '"' . ", 'UTF8')");
|
||||
}
|
||||
else {
|
||||
$this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $field_def . ' USING "' . $field . '"::' . $field_def);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Do not attempt to convert a field that is bytea already.
|
||||
$table_information = $this->queryTableInformation($table);
|
||||
if (!in_array($field, $table_information->blob_fields)) {
|
||||
if (!$is_bytea) {
|
||||
// Convert to a bytea type by using the SQL replace() function to
|
||||
// convert any single backslashes in the field content to double
|
||||
// backslashes ('\' to '\\').
|
||||
$this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $field_def . ' USING decode(replace("' . $field . '"' . ", '\\', '\\\\'), 'escape');");
|
||||
$this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $field_def . ' USING decode(replace("' . $field . '"' . ", E'\\\\', E'\\\\\\\\'), 'escape');");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ class Schema extends DatabaseSchema {
|
|||
*/
|
||||
public function createTableSql($name, $table) {
|
||||
$sql = array();
|
||||
$sql[] = "CREATE TABLE {" . $name . "} (\n" . $this->createColumnsSql($name, $table) . "\n);\n";
|
||||
$sql[] = "CREATE TABLE {" . $name . "} (\n" . $this->createColumnsSql($name, $table) . "\n)\n";
|
||||
return array_merge($sql, $this->createIndexSql($name, $table));
|
||||
}
|
||||
|
||||
|
@ -60,12 +60,12 @@ class Schema extends DatabaseSchema {
|
|||
$info = $this->getPrefixInfo($tablename);
|
||||
if (!empty($schema['unique keys'])) {
|
||||
foreach ($schema['unique keys'] as $key => $fields) {
|
||||
$sql[] = 'CREATE UNIQUE INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $key . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . "); \n";
|
||||
$sql[] = 'CREATE UNIQUE INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $key . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . ")\n";
|
||||
}
|
||||
}
|
||||
if (!empty($schema['indexes'])) {
|
||||
foreach ($schema['indexes'] as $key => $fields) {
|
||||
$sql[] = 'CREATE INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $key . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . "); \n";
|
||||
$sql[] = 'CREATE INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $key . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . ")\n";
|
||||
}
|
||||
}
|
||||
return $sql;
|
||||
|
|
|
@ -188,6 +188,25 @@ class Condition implements ConditionInterface, \Countable {
|
|||
'operator' => $condition['operator'],
|
||||
'use_value' => TRUE,
|
||||
);
|
||||
// Remove potentially dangerous characters.
|
||||
// If something passed in an invalid character stop early, so we
|
||||
// don't rely on a broken SQL statement when we would just replace
|
||||
// those characters.
|
||||
if (stripos($condition['operator'], 'UNION') !== FALSE || strpbrk($condition['operator'], '[-\'"();') !== FALSE) {
|
||||
$this->changed = TRUE;
|
||||
$this->arguments = [];
|
||||
// Provide a string which will result into an empty query result.
|
||||
$this->stringVersion = '( AND 1 = 0 )';
|
||||
|
||||
// Conceptually throwing an exception caused by user input is bad
|
||||
// as you result into a WSOD, which depending on your webserver
|
||||
// configuration can result into the assumption that your site is
|
||||
// broken.
|
||||
// On top of that the database API relies on __toString() which
|
||||
// does not allow to throw exceptions.
|
||||
trigger_error('Invalid characters in query operator: ' . $condition['operator'], E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
$operator = $connection->mapConditionOperator($condition['operator']);
|
||||
if (!isset($operator)) {
|
||||
$operator = $this->mapConditionOperator($condition['operator']);
|
||||
|
|
|
@ -45,95 +45,140 @@ class SelectExtender implements SelectInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\PlaceholderInterface::uniqueIdentifier().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uniqueIdentifier() {
|
||||
return $this->uniqueIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Database\Query\PlaceholderInterface::nextPlaceholder().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function nextPlaceholder() {
|
||||
return $this->placeholder++;
|
||||
}
|
||||
|
||||
/* Implementations of Drupal\Core\Database\Query\AlterableInterface. */
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTag($tag) {
|
||||
$this->query->addTag($tag);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasTag($tag) {
|
||||
return $this->query->hasTag($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasAllTags() {
|
||||
return call_user_func_array(array($this->query, 'hasAllTags'), func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasAnyTag() {
|
||||
return call_user_func_array(array($this->query, 'hasAnyTag'), func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addMetaData($key, $object) {
|
||||
$this->query->addMetaData($key, $object);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetaData($key) {
|
||||
return $this->query->getMetaData($key);
|
||||
}
|
||||
|
||||
/* Implementations of Drupal\Core\Database\Query\ConditionInterface for the WHERE clause. */
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function condition($field, $value = NULL, $operator = '=') {
|
||||
$this->query->condition($field, $value, $operator);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &conditions() {
|
||||
return $this->query->conditions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function arguments() {
|
||||
return $this->query->arguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function where($snippet, $args = array()) {
|
||||
$this->query->where($snippet, $args);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile(Connection $connection, PlaceholderInterface $queryPlaceholder) {
|
||||
return $this->query->compile($connection, $queryPlaceholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compiled() {
|
||||
return $this->query->compiled();
|
||||
}
|
||||
|
||||
/* Implementations of Drupal\Core\Database\Query\ConditionInterface for the HAVING clause. */
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function havingCondition($field, $value = NULL, $operator = '=') {
|
||||
$this->query->havingCondition($field, $value, $operator);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &havingConditions() {
|
||||
return $this->query->havingConditions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function havingArguments() {
|
||||
return $this->query->havingArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function having($snippet, $args = array()) {
|
||||
$this->query->having($snippet, $args);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function havingCompile(Connection $connection) {
|
||||
return $this->query->havingCompile($connection);
|
||||
}
|
||||
|
@ -170,8 +215,9 @@ class SelectExtender implements SelectInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/* Implementations of Drupal\Core\Database\Query\ExtendableInterface. */
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extend($extender_name) {
|
||||
$class = $this->connection->getDriverClass($extender_name);
|
||||
return new $class($this, $this->connection);
|
||||
|
@ -179,26 +225,44 @@ class SelectExtender implements SelectInterface {
|
|||
|
||||
/* Alter accessors to expose the query data to alter hooks. */
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getFields() {
|
||||
return $this->query->getFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getExpressions() {
|
||||
return $this->query->getExpressions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getOrderBy() {
|
||||
return $this->query->getOrderBy();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getGroupBy() {
|
||||
return $this->query->getGroupBy();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getTables() {
|
||||
return $this->query->getTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function &getUnion() {
|
||||
return $this->query->getUnion();
|
||||
}
|
||||
|
@ -218,14 +282,23 @@ class SelectExtender implements SelectInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArguments(PlaceholderInterface $queryPlaceholder = NULL) {
|
||||
return $this->query->getArguments($queryPlaceholder);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isPrepared() {
|
||||
return $this->query->isPrepared();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preExecute(SelectInterface $query = NULL) {
|
||||
// If no query object is passed in, use $this.
|
||||
if (!isset($query)) {
|
||||
|
@ -235,6 +308,9 @@ class SelectExtender implements SelectInterface {
|
|||
return $this->query->preExecute($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute() {
|
||||
// By calling preExecute() here, we force it to preprocess the extender
|
||||
// object rather than just the base query object. That means
|
||||
|
@ -246,102 +322,168 @@ class SelectExtender implements SelectInterface {
|
|||
return $this->query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function distinct($distinct = TRUE) {
|
||||
$this->query->distinct($distinct);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addField($table_alias, $field, $alias = NULL) {
|
||||
return $this->query->addField($table_alias, $field, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields($table_alias, array $fields = array()) {
|
||||
$this->query->fields($table_alias, $fields);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addExpression($expression, $alias = NULL, $arguments = array()) {
|
||||
return $this->query->addExpression($expression, $alias, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function join($table, $alias = NULL, $condition = NULL, $arguments = array()) {
|
||||
return $this->query->join($table, $alias, $condition, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function innerJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
|
||||
return $this->query->innerJoin($table, $alias, $condition, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function leftJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
|
||||
return $this->query->leftJoin($table, $alias, $condition, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = array()) {
|
||||
return $this->query->rightJoin($table, $alias, $condition, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addJoin($type, $table, $alias = NULL, $condition = NULL, $arguments = array()) {
|
||||
return $this->query->addJoin($type, $table, $alias, $condition, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function orderBy($field, $direction = 'ASC') {
|
||||
$this->query->orderBy($field, $direction);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function orderRandom() {
|
||||
$this->query->orderRandom();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function range($start = NULL, $length = NULL) {
|
||||
$this->query->range($start, $length);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function union(SelectInterface $query, $type = '') {
|
||||
$this->query->union($query, $type);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function groupBy($field) {
|
||||
$this->query->groupBy($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function forUpdate($set = TRUE) {
|
||||
$this->query->forUpdate($set);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countQuery() {
|
||||
return $this->query->countQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function isNull($field) {
|
||||
$this->query->isNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function isNotNull($field) {
|
||||
$this->query->isNotNull($field);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function exists(SelectInterface $select) {
|
||||
$this->query->exists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function notExists(SelectInterface $select) {
|
||||
$this->query->notExists($select);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString() {
|
||||
return (string) $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __clone() {
|
||||
$this->uniqueIdentifier = uniqid('', TRUE);
|
||||
|
||||
|
|
|
@ -648,4 +648,12 @@ interface SelectInterface extends ConditionInterface, AlterableInterface, Extend
|
|||
*/
|
||||
public function forUpdate($set = TRUE);
|
||||
|
||||
/**
|
||||
* Returns a string representation of how the query will be executed in SQL.
|
||||
*
|
||||
* @return string
|
||||
* The Select Query object expressed as a string.
|
||||
*/
|
||||
public function __toString();
|
||||
|
||||
}
|
||||
|
|
|
@ -640,6 +640,8 @@ abstract class Schema implements PlaceholderInterface {
|
|||
* The prepared comment.
|
||||
*/
|
||||
public function prepareComment($comment, $length = NULL) {
|
||||
// Remove semicolons to avoid triggering multi-statement check.
|
||||
$comment = strtr($comment, [';' => '.']);
|
||||
return $this->connection->quote($comment);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\Datetime;
|
||||
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
|
@ -22,7 +21,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
|||
*
|
||||
* @ingroup i18n
|
||||
*/
|
||||
class DateFormatter {
|
||||
class DateFormatter implements DateFormatterInterface {
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
|
@ -105,33 +104,7 @@ class DateFormatter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Formats a date, using a date type or a custom date format string.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* A UNIX timestamp to format.
|
||||
* @param string $type
|
||||
* (optional) The format to use, one of:
|
||||
* - One of the built-in formats: 'short', 'medium',
|
||||
* 'long', 'html_datetime', 'html_date', 'html_time',
|
||||
* 'html_yearless_date', 'html_week', 'html_month', 'html_year'.
|
||||
* - The name of a date type defined by a date format config entity.
|
||||
* - The machine name of an administrator-defined date format.
|
||||
* - 'custom', to use $format.
|
||||
* Defaults to 'medium'.
|
||||
* @param string $format
|
||||
* (optional) If $type is 'custom', a PHP date format string suitable for
|
||||
* input to date(). Use a backslash to escape ordinary text, so it does not
|
||||
* get interpreted as date format characters.
|
||||
* @param string|null $timezone
|
||||
* (optional) Time zone identifier, as described at
|
||||
* http://php.net/manual/timezones.php Defaults to the time zone used to
|
||||
* display the page.
|
||||
* @param string|null $langcode
|
||||
* (optional) Language code to translate to. NULL (default) means to use
|
||||
* the user interface language for the page.
|
||||
*
|
||||
* @return string
|
||||
* A translated date string in the requested format.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function format($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
|
||||
if (!isset($timezone)) {
|
||||
|
@ -168,32 +141,11 @@ class DateFormatter {
|
|||
$settings = array(
|
||||
'langcode' => $langcode,
|
||||
);
|
||||
return Xss::filter($date->format($format, $settings));
|
||||
return $date->format($format, $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a time interval with the requested granularity.
|
||||
*
|
||||
* Note that for intervals over 30 days, the output is approximate: a "month"
|
||||
* is always exactly 30 days, and a "year" is always 365 days. It is not
|
||||
* possible to make a more exact representation, given that there is only one
|
||||
* input in seconds. If you are formatting an interval between two specific
|
||||
* timestamps, use \Drupal\Core\Datetime\DateFormatter::formatDiff() instead.
|
||||
*
|
||||
* @param int $interval
|
||||
* The length of the interval in seconds.
|
||||
* @param int $granularity
|
||||
* (optional) How many different units to display in the string (2 by
|
||||
* default).
|
||||
* @param string|null $langcode
|
||||
* (optional) langcode: The language code for the language used to format
|
||||
* the date. Defaults to NULL, which results in the user interface language
|
||||
* for the page being used.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the interval.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatDiff()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formatInterval($interval, $granularity = 2, $langcode = NULL) {
|
||||
$output = '';
|
||||
|
@ -218,21 +170,7 @@ class DateFormatter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Provides values for all date formatting characters for a given timestamp.
|
||||
*
|
||||
* @param string|null $langcode
|
||||
* (optional) Language code of the date format, if different from the site
|
||||
* default language.
|
||||
* @param int|null $timestamp
|
||||
* (optional) The Unix timestamp to format, defaults to current time.
|
||||
* @param string|null $timezone
|
||||
* (optional) The timezone to use, if different from the site's default
|
||||
* timezone.
|
||||
*
|
||||
* @return array
|
||||
* An array of formatted date values, indexed by the date format character.
|
||||
*
|
||||
* @see date()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSampleDateFormats($langcode = NULL, $timestamp = NULL, $timezone = NULL) {
|
||||
$timestamp = $timestamp ?: time();
|
||||
|
@ -245,30 +183,7 @@ class DateFormatter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Formats the time difference from the current request time to a timestamp.
|
||||
*
|
||||
* @param $timestamp
|
||||
* A UNIX timestamp to compare against the current request time.
|
||||
* @param array $options
|
||||
* (optional) An associative array with additional options. The following
|
||||
* keys can be used:
|
||||
* - granularity: An integer value that signals how many different units to
|
||||
* display in the string. Defaults to 2.
|
||||
* - langcode: The language code for the language used to format the date.
|
||||
* Defaults to NULL, which results in the user interface language for the
|
||||
* page being used.
|
||||
* - strict: A Boolean value indicating whether or not the timestamp can be
|
||||
* before the current request time. If TRUE (default) and $timestamp is
|
||||
* before the current request time, the result string will be "0 seconds".
|
||||
* If FALSE and $timestamp is before the current request time, the result
|
||||
* string will be the formatted time difference.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the difference between the given
|
||||
* timestamp and the current request time. This interval is always positive.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatDiff()
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatTimeDiffSince()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formatTimeDiffUntil($timestamp, $options = array()) {
|
||||
$request_time = $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME');
|
||||
|
@ -276,30 +191,7 @@ class DateFormatter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Formats the time difference from a timestamp to the current request time.
|
||||
*
|
||||
* @param $timestamp
|
||||
* A UNIX timestamp to compare against the current request time.
|
||||
* @param array $options
|
||||
* (optional) An associative array with additional options. The following
|
||||
* keys can be used:
|
||||
* - granularity: An integer value that signals how many different units to
|
||||
* display in the string. Defaults to 2.
|
||||
* - langcode: The language code for the language used to format the date.
|
||||
* Defaults to NULL, which results in the user interface language for the
|
||||
* page being used.
|
||||
* - strict: A Boolean value indicating whether or not the timestamp can be
|
||||
* after the current request time. If TRUE (default) and $timestamp is
|
||||
* after the current request time, the result string will be "0 seconds".
|
||||
* If FALSE and $timestamp is after the current request time, the result
|
||||
* string will be the formatted time difference.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the difference between the given
|
||||
* timestamp and the current request time. This interval is always positive.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatDiff()
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatTimeDiffUntil()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formatTimeDiffSince($timestamp, $options = array()) {
|
||||
$request_time = $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME');
|
||||
|
@ -307,32 +199,7 @@ class DateFormatter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Formats a time interval between two timestamps.
|
||||
*
|
||||
* @param int $from
|
||||
* A UNIX timestamp, defining the from date and time.
|
||||
* @param int $to
|
||||
* A UNIX timestamp, defining the to date and time.
|
||||
* @param array $options
|
||||
* (optional) An associative array with additional options. The following
|
||||
* keys can be used:
|
||||
* - granularity: An integer value that signals how many different units to
|
||||
* display in the string. Defaults to 2.
|
||||
* - langcode: The language code for the language used to format the date.
|
||||
* Defaults to NULL, which results in the user interface language for the
|
||||
* page being used.
|
||||
* - strict: A Boolean value indicating whether or not the $from timestamp
|
||||
* can be after the $to timestamp. If TRUE (default) and $from is after
|
||||
* $to, the result string will be "0 seconds". If FALSE and $from is
|
||||
* after $to, the result string will be the formatted time difference.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the interval. This interval is
|
||||
* always positive.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatInterval()
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatTimeDiffSince()
|
||||
* @see \Drupal\Core\Datetime\DateFormatter::formatTimeDiffUntil()
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formatDiff($from, $to, $options = array()) {
|
||||
|
||||
|
|
178
core/lib/Drupal/Core/Datetime/DateFormatterInterface.php
Normal file
178
core/lib/Drupal/Core/Datetime/DateFormatterInterface.php
Normal file
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Datetime\DateFormatterInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Datetime;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a date formatter.
|
||||
*/
|
||||
interface DateFormatterInterface {
|
||||
|
||||
/**
|
||||
* Formats a date, using a date type or a custom date format string.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* A UNIX timestamp to format.
|
||||
* @param string $type
|
||||
* (optional) The format to use, one of:
|
||||
* - One of the built-in formats: 'short', 'medium',
|
||||
* 'long', 'html_datetime', 'html_date', 'html_time',
|
||||
* 'html_yearless_date', 'html_week', 'html_month', 'html_year'.
|
||||
* - The name of a date type defined by a date format config entity.
|
||||
* - The machine name of an administrator-defined date format.
|
||||
* - 'custom', to use $format.
|
||||
* Defaults to 'medium'.
|
||||
* @param string $format
|
||||
* (optional) If $type is 'custom', a PHP date format string suitable for
|
||||
* input to date(). Use a backslash to escape ordinary text, so it does not
|
||||
* get interpreted as date format characters.
|
||||
* @param string|null $timezone
|
||||
* (optional) Time zone identifier, as described at
|
||||
* http://php.net/manual/timezones.php Defaults to the time zone used to
|
||||
* display the page.
|
||||
* @param string|null $langcode
|
||||
* (optional) Language code to translate to. NULL (default) means to use
|
||||
* the user interface language for the page.
|
||||
*
|
||||
* @return string
|
||||
* A translated date string in the requested format. Since the format may
|
||||
* contain user input, this value should be escaped when output.
|
||||
*/
|
||||
public function format($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL);
|
||||
|
||||
/**
|
||||
* Formats a time interval with the requested granularity.
|
||||
*
|
||||
* Note that for intervals over 30 days, the output is approximate: a "month"
|
||||
* is always exactly 30 days, and a "year" is always 365 days. It is not
|
||||
* possible to make a more exact representation, given that there is only one
|
||||
* input in seconds. If you are formatting an interval between two specific
|
||||
* timestamps, use \Drupal\Core\Datetime\DateFormatter::formatDiff() instead.
|
||||
*
|
||||
* @param int $interval
|
||||
* The length of the interval in seconds.
|
||||
* @param int $granularity
|
||||
* (optional) How many different units to display in the string (2 by
|
||||
* default).
|
||||
* @param string|null $langcode
|
||||
* (optional) langcode: The language code for the language used to format
|
||||
* the date. Defaults to NULL, which results in the user interface language
|
||||
* for the page being used.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the interval.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatterInterface::formatDiff()
|
||||
*/
|
||||
public function formatInterval($interval, $granularity = 2, $langcode = NULL);
|
||||
|
||||
/**
|
||||
* Provides values for all date formatting characters for a given timestamp.
|
||||
*
|
||||
* @param string|null $langcode
|
||||
* (optional) Language code of the date format, if different from the site
|
||||
* default language.
|
||||
* @param int|null $timestamp
|
||||
* (optional) The Unix timestamp to format, defaults to current time.
|
||||
* @param string|null $timezone
|
||||
* (optional) The timezone to use, if different from the site's default
|
||||
* timezone.
|
||||
*
|
||||
* @return array
|
||||
* An array of formatted date values, indexed by the date format character.
|
||||
*
|
||||
* @see date()
|
||||
*/
|
||||
public function getSampleDateFormats($langcode = NULL, $timestamp = NULL, $timezone = NULL);
|
||||
|
||||
/**
|
||||
* Formats the time difference from the current request time to a timestamp.
|
||||
*
|
||||
* @param $timestamp
|
||||
* A UNIX timestamp to compare against the current request time.
|
||||
* @param array $options
|
||||
* (optional) An associative array with additional options. The following
|
||||
* keys can be used:
|
||||
* - granularity: An integer value that signals how many different units to
|
||||
* display in the string. Defaults to 2.
|
||||
* - langcode: The language code for the language used to format the date.
|
||||
* Defaults to NULL, which results in the user interface language for the
|
||||
* page being used.
|
||||
* - strict: A Boolean value indicating whether or not the timestamp can be
|
||||
* before the current request time. If TRUE (default) and $timestamp is
|
||||
* before the current request time, the result string will be "0 seconds".
|
||||
* If FALSE and $timestamp is before the current request time, the result
|
||||
* string will be the formatted time difference.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the difference between the given
|
||||
* timestamp and the current request time. This interval is always positive.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatterInterface::formatDiff()
|
||||
* @see \Drupal\Core\Datetime\DateFormatterInterface::formatTimeDiffSince()
|
||||
*/
|
||||
public function formatTimeDiffUntil($timestamp, $options = array());
|
||||
|
||||
/**
|
||||
* Formats the time difference from a timestamp to the current request time.
|
||||
*
|
||||
* @param $timestamp
|
||||
* A UNIX timestamp to compare against the current request time.
|
||||
* @param array $options
|
||||
* (optional) An associative array with additional options. The following
|
||||
* keys can be used:
|
||||
* - granularity: An integer value that signals how many different units to
|
||||
* display in the string. Defaults to 2.
|
||||
* - langcode: The language code for the language used to format the date.
|
||||
* Defaults to NULL, which results in the user interface language for the
|
||||
* page being used.
|
||||
* - strict: A Boolean value indicating whether or not the timestamp can be
|
||||
* after the current request time. If TRUE (default) and $timestamp is
|
||||
* after the current request time, the result string will be "0 seconds".
|
||||
* If FALSE and $timestamp is after the current request time, the result
|
||||
* string will be the formatted time difference.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the difference between the given
|
||||
* timestamp and the current request time. This interval is always positive.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatterInterface::formatDiff()
|
||||
* @see \Drupal\Core\Datetime\DateFormatterInterface::formatTimeDiffUntil()
|
||||
*/
|
||||
public function formatTimeDiffSince($timestamp, $options = array());
|
||||
|
||||
/**
|
||||
* Formats a time interval between two timestamps.
|
||||
*
|
||||
* @param int $from
|
||||
* A UNIX timestamp, defining the from date and time.
|
||||
* @param int $to
|
||||
* A UNIX timestamp, defining the to date and time.
|
||||
* @param array $options
|
||||
* (optional) An associative array with additional options. The following
|
||||
* keys can be used:
|
||||
* - granularity: An integer value that signals how many different units to
|
||||
* display in the string. Defaults to 2.
|
||||
* - langcode: The language code for the language used to format the date.
|
||||
* Defaults to NULL, which results in the user interface language for the
|
||||
* page being used.
|
||||
* - strict: A Boolean value indicating whether or not the $from timestamp
|
||||
* can be after the $to timestamp. If TRUE (default) and $from is after
|
||||
* $to, the result string will be "0 seconds". If FALSE and $from is
|
||||
* after $to, the result string will be the formatted time difference.
|
||||
*
|
||||
* @return string
|
||||
* A translated string representation of the interval. This interval is
|
||||
* always positive.
|
||||
*
|
||||
* @see \Drupal\Core\Datetime\DateFormatterInterface::formatInterval()
|
||||
* @see \Drupal\Core\Datetime\DateFormatterInterface::formatTimeDiffSince()
|
||||
* @see \Drupal\Core\Datetime\DateFormatterInterface::formatTimeDiffUntil()
|
||||
*/
|
||||
public function formatDiff($from, $to, $options = array());
|
||||
|
||||
}
|
|
@ -90,7 +90,8 @@ class DrupalDateTime extends DateTimePlus {
|
|||
* the result of the format() method. Defaults to NULL.
|
||||
*
|
||||
* @return string
|
||||
* The formatted value of the date.
|
||||
* The formatted value of the date. Since the format may contain user input,
|
||||
* this value should be escaped when output.
|
||||
*/
|
||||
public function format($format, $settings = array()) {
|
||||
$langcode = !empty($settings['langcode']) ? $settings['langcode'] : $this->langcode;
|
||||
|
|
|
@ -241,7 +241,7 @@ class Datetime extends DateElementBase {
|
|||
// placeholders are invalid for HTML5 date and datetime, so an example
|
||||
// format is appended to the title to appear in tooltips.
|
||||
$extra_attributes = array(
|
||||
'title' => t('Date (e.g. !format)', array('!format' => static::formatExample($date_format))),
|
||||
'title' => t('Date (e.g. @format)', array('@format' => static::formatExample($date_format))),
|
||||
'type' => $element['#date_date_element'],
|
||||
);
|
||||
|
||||
|
@ -288,7 +288,7 @@ class Datetime extends DateElementBase {
|
|||
|
||||
// Adds the HTML5 attributes.
|
||||
$extra_attributes = array(
|
||||
'title' => t('Time (e.g. !format)', array('!format' => static::formatExample($time_format))),
|
||||
'title' => t('Time (e.g. @format)', array('@format' => static::formatExample($time_format))),
|
||||
'type' => $element['#date_time_element'],
|
||||
'step' => $element['#date_increment'],
|
||||
);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass.
|
||||
* Contains \Drupal\Core\DependencyInjection\Compiler\RegisterEventSubscribersPass.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\DependencyInjection\Compiler;
|
||||
|
@ -10,7 +10,10 @@ namespace Drupal\Core\DependencyInjection\Compiler;
|
|||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
class RegisterKernelListenersPass implements CompilerPassInterface {
|
||||
/**
|
||||
* Registers all event subscribers to the event dispatcher.
|
||||
*/
|
||||
class RegisterEventSubscribersPass implements CompilerPassInterface {
|
||||
public function process(ContainerBuilder $container) {
|
||||
if (!$container->hasDefinition('event_dispatcher')) {
|
||||
return;
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Display\ContextAwareVariantInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Display;
|
||||
|
||||
/**
|
||||
* Provides an interface for variant plugins that are context-aware.
|
||||
*/
|
||||
interface ContextAwareVariantInterface extends VariantInterface {
|
||||
|
||||
/**
|
||||
* Gets the values for all defined contexts.
|
||||
*
|
||||
* @return \Drupal\Component\Plugin\Context\ContextInterface[]
|
||||
* An array of set contexts, keyed by context name.
|
||||
*/
|
||||
public function getContexts();
|
||||
|
||||
/**
|
||||
* Sets the context values for this display variant.
|
||||
*
|
||||
* @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
|
||||
* An array of contexts, keyed by context name.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContexts(array $contexts);
|
||||
|
||||
}
|
|
@ -36,4 +36,15 @@ interface PageVariantInterface extends VariantInterface {
|
|||
*/
|
||||
public function setMainContent(array $main_content);
|
||||
|
||||
/**
|
||||
* Sets the title for the page being rendered.
|
||||
*
|
||||
* @param string|array $title
|
||||
* The page title: either a string for plain titles or a render array for
|
||||
* formatted titles.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTitle($title);
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Display;
|
||||
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\Core\Plugin\PluginDependencyTrait;
|
||||
|
@ -23,6 +24,7 @@ use Drupal\Core\Session\AccountInterface;
|
|||
abstract class VariantBase extends PluginBase implements VariantInterface {
|
||||
|
||||
use PluginDependencyTrait;
|
||||
use RefinableCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\Core\Display;
|
|||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
|
@ -20,7 +21,7 @@ use Drupal\Core\Session\AccountInterface;
|
|||
* @see \Drupal\Core\Display\VariantManager
|
||||
* @see plugin_api
|
||||
*/
|
||||
interface VariantInterface extends PluginInspectionInterface, ConfigurablePluginInterface, PluginFormInterface {
|
||||
interface VariantInterface extends PluginInspectionInterface, ConfigurablePluginInterface, PluginFormInterface, RefinableCacheableDependencyInterface {
|
||||
|
||||
/**
|
||||
* Returns the user-facing display variant label.
|
||||
|
@ -79,6 +80,10 @@ interface VariantInterface extends PluginInspectionInterface, ConfigurablePlugin
|
|||
/**
|
||||
* Builds and returns the renderable array for the display variant.
|
||||
*
|
||||
* The variant can contain cacheability metadata for the configuration that
|
||||
* was passed in setConfiguration(). In the build() method, this should be
|
||||
* added to the render array that is returned.
|
||||
*
|
||||
* @return array
|
||||
* A render array for the display variant.
|
||||
*/
|
||||
|
|
|
@ -992,47 +992,32 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
* @todo D8: Eliminate this entirely in favor of Request object.
|
||||
*/
|
||||
protected function initializeRequestGlobals(Request $request) {
|
||||
// Provided by settings.php.
|
||||
global $base_url;
|
||||
// Set and derived from $base_url by this function.
|
||||
global $base_path, $base_root;
|
||||
global $base_secure_url, $base_insecure_url;
|
||||
|
||||
// @todo Refactor with the Symfony Request object.
|
||||
if (isset($base_url)) {
|
||||
// Parse fixed base URL from settings.php.
|
||||
$parts = parse_url($base_url);
|
||||
if (!isset($parts['path'])) {
|
||||
$parts['path'] = '';
|
||||
}
|
||||
$base_path = $parts['path'] . '/';
|
||||
// Build $base_root (everything until first slash after "scheme://").
|
||||
$base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
|
||||
}
|
||||
else {
|
||||
// Create base URL.
|
||||
$base_root = $request->getSchemeAndHttpHost();
|
||||
// Create base URL.
|
||||
$base_root = $request->getSchemeAndHttpHost();
|
||||
$base_url = $base_root;
|
||||
|
||||
$base_url = $base_root;
|
||||
|
||||
// For a request URI of '/index.php/foo', $_SERVER['SCRIPT_NAME'] is
|
||||
// '/index.php', whereas $_SERVER['PHP_SELF'] is '/index.php/foo'.
|
||||
if ($dir = rtrim(dirname($request->server->get('SCRIPT_NAME')), '\/')) {
|
||||
// Remove "core" directory if present, allowing install.php,
|
||||
// authorize.php, and others to auto-detect a base path.
|
||||
$core_position = strrpos($dir, '/core');
|
||||
if ($core_position !== FALSE && strlen($dir) - 5 == $core_position) {
|
||||
$base_path = substr($dir, 0, $core_position);
|
||||
}
|
||||
else {
|
||||
$base_path = $dir;
|
||||
}
|
||||
$base_url .= $base_path;
|
||||
$base_path .= '/';
|
||||
// For a request URI of '/index.php/foo', $_SERVER['SCRIPT_NAME'] is
|
||||
// '/index.php', whereas $_SERVER['PHP_SELF'] is '/index.php/foo'.
|
||||
if ($dir = rtrim(dirname($request->server->get('SCRIPT_NAME')), '\/')) {
|
||||
// Remove "core" directory if present, allowing install.php,
|
||||
// authorize.php, and others to auto-detect a base path.
|
||||
$core_position = strrpos($dir, '/core');
|
||||
if ($core_position !== FALSE && strlen($dir) - 5 == $core_position) {
|
||||
$base_path = substr($dir, 0, $core_position);
|
||||
}
|
||||
else {
|
||||
$base_path = '/';
|
||||
$base_path = $dir;
|
||||
}
|
||||
$base_url .= $base_path;
|
||||
$base_path .= '/';
|
||||
}
|
||||
else {
|
||||
$base_path = '/';
|
||||
}
|
||||
$base_secure_url = str_replace('http://', 'https://', $base_url);
|
||||
$base_insecure_url = str_replace('https://', 'http://', $base_url);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\Annotation;
|
||||
use Drupal\Core\StringTranslation\TranslationWrapper;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
|
||||
/**
|
||||
* Defines a config entity type annotation object.
|
||||
|
@ -37,7 +37,7 @@ class ConfigEntityType extends EntityType {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function get() {
|
||||
$this->definition['group_label'] = new TranslationWrapper('Configuration', array(), array('context' => 'Entity type group'));
|
||||
$this->definition['group_label'] = new TranslatableMarkup('Configuration', array(), array('context' => 'Entity type group'));
|
||||
|
||||
return parent::get();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\Annotation;
|
||||
use Drupal\Core\StringTranslation\TranslationWrapper;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
|
||||
/**
|
||||
* Defines a content entity type annotation object.
|
||||
|
@ -37,7 +37,7 @@ class ContentEntityType extends EntityType {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function get() {
|
||||
$this->definition['group_label'] = new TranslationWrapper('Content', array(), array('context' => 'Entity type group'));
|
||||
$this->definition['group_label'] = new TranslatableMarkup('Content', array(), array('context' => 'Entity type group'));
|
||||
|
||||
return parent::get();
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ use Drupal\Component\Annotation\Plugin;
|
|||
*
|
||||
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
|
||||
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
|
||||
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase
|
||||
* @see \Drupal\Core\Entity\Plugin\Derivative\SelectionBase
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
|
|
41
core/lib/Drupal/Core/Entity/BundleEntityFormBase.php
Normal file
41
core/lib/Drupal/Core/Entity/BundleEntityFormBase.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\BundleEntityFormBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity;
|
||||
|
||||
/**
|
||||
* Class BundleEntityFormBase is a base form for bundle config entities.
|
||||
*/
|
||||
class BundleEntityFormBase extends EntityForm {
|
||||
|
||||
/**
|
||||
* Protects the bundle entity's ID property's form element against changes.
|
||||
*
|
||||
* This method is assumed to be called on a completely built entity form,
|
||||
* including a form element for the bundle config entity's ID property.
|
||||
*
|
||||
* @param array $form
|
||||
* The completely built entity bundle form array.
|
||||
*
|
||||
* @return array
|
||||
* The updated entity bundle form array.
|
||||
*/
|
||||
protected function protectBundleIdElement(array $form) {
|
||||
$entity = $this->getEntity();
|
||||
$id_key = $entity->getEntityType()->getKey('id');
|
||||
assert('isset($form[$id_key])');
|
||||
$element = &$form[$id_key];
|
||||
|
||||
// Make sure the element is not accidentally re-enabled if it has already
|
||||
// been disabled.
|
||||
if (empty($element['#disabled'])) {
|
||||
$element['#disabled'] = !$entity->isNew();
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
}
|
|
@ -70,7 +70,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
/**
|
||||
* Local cache for the available language objects.
|
||||
*
|
||||
* @var array
|
||||
* @var \Drupal\Core\Language\LanguageInterface[]
|
||||
*/
|
||||
protected $languages;
|
||||
|
||||
|
@ -138,7 +138,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
protected $isDefaultRevision = TRUE;
|
||||
|
||||
/**
|
||||
* Holds translatable entity keys such as the ID, bundle and revision ID.
|
||||
* Holds untranslatable entity keys such as the ID, bundle, and revision ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
|
@ -593,7 +593,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
}
|
||||
return $this->entityManager()
|
||||
->getAccessControlHandler($this->entityTypeId)
|
||||
->access($this, $operation, $this->activeLangcode, $account, $return_as_object);
|
||||
->access($this, $operation, $account, $return_as_object);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -737,22 +737,11 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
if (isset($this->translations[$langcode]['entity'])) {
|
||||
$translation = $this->translations[$langcode]['entity'];
|
||||
}
|
||||
else {
|
||||
if (isset($this->translations[$langcode])) {
|
||||
$translation = $this->initializeTranslation($langcode);
|
||||
$this->translations[$langcode]['entity'] = $translation;
|
||||
}
|
||||
else {
|
||||
// If we were given a valid language and there is no translation for it,
|
||||
// we return a new one.
|
||||
$this->getLanguages();
|
||||
if (isset($this->languages[$langcode])) {
|
||||
// If the entity or the requested language is not a configured
|
||||
// language, we fall back to the entity itself, since in this case it
|
||||
// cannot have translations.
|
||||
$translation = !$this->languages[$this->defaultLangcode]->isLocked() && !$this->languages[$langcode]->isLocked() ? $this->addTranslation($langcode) : $this;
|
||||
}
|
||||
}
|
||||
// Otherwise if an existing translation language was specified we need to
|
||||
// instantiate the related translation.
|
||||
elseif (isset($this->translations[$langcode])) {
|
||||
$translation = $this->initializeTranslation($langcode);
|
||||
$this->translations[$langcode]['entity'] = $translation;
|
||||
}
|
||||
|
||||
if (empty($translation)) {
|
||||
|
@ -823,10 +812,15 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTranslation($langcode, array $values = array()) {
|
||||
// Make sure we do not attempt to create a translation if an invalid
|
||||
// language is specified or the entity cannot be translated.
|
||||
$this->getLanguages();
|
||||
if (!isset($this->languages[$langcode]) || $this->hasTranslation($langcode)) {
|
||||
if (!isset($this->languages[$langcode]) || $this->hasTranslation($langcode) || $this->languages[$langcode]->isLocked()) {
|
||||
throw new \InvalidArgumentException("Invalid translation language ($langcode) specified.");
|
||||
}
|
||||
if ($this->languages[$this->defaultLangcode]->isLocked()) {
|
||||
throw new \InvalidArgumentException("The entity cannot be translated since it is language neutral ({$this->defaultLangcode}).");
|
||||
}
|
||||
|
||||
// Instantiate a new empty entity so default values will be populated in the
|
||||
// specified language.
|
||||
|
|
|
@ -62,6 +62,15 @@ class ContentEntityForm extends EntityForm implements ContentEntityFormInterface
|
|||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
// Update the changed timestamp of the entity.
|
||||
$this->updateChangedTime($this->entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -164,7 +173,7 @@ class ContentEntityForm extends EntityForm implements ContentEntityFormInterface
|
|||
// language.
|
||||
$this->initFormLangcodes($form_state);
|
||||
$langcode = $this->getFormLangcode($form_state);
|
||||
$this->entity = $this->entity->getTranslation($langcode);
|
||||
$this->entity = $this->entity->hasTranslation($langcode) ? $this->entity->getTranslation($langcode) : $this->entity->addTranslation($langcode);
|
||||
|
||||
$form_display = EntityFormDisplay::collectRenderDisplay($this->entity, $this->getOperation());
|
||||
$this->setFormDisplay($form_display, $form_state);
|
||||
|
@ -269,4 +278,18 @@ class ContentEntityForm extends EntityForm implements ContentEntityFormInterface
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the changed time of the entity.
|
||||
*
|
||||
* Applies only if the entity implements the EntityChangedInterface.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity updated with the submitted values.
|
||||
*/
|
||||
public function updateChangedTime(EntityInterface $entity) {
|
||||
if ($entity->getEntityType()->isSubclassOf(EntityChangedInterface::class)) {
|
||||
$entity->setChangedTime(REQUEST_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -336,7 +336,7 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Dyn
|
|||
$this->invokeHook('translation_insert', $entity->getTranslation($langcode));
|
||||
}
|
||||
elseif (!isset($translations[$langcode]) && isset($original_translations[$langcode])) {
|
||||
$this->invokeHook('translation_delete', $entity->getTranslation($langcode));
|
||||
$this->invokeHook('translation_delete', $entity->original->getTranslation($langcode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,17 +91,14 @@ class EntityViewController implements ContainerInjectionInterface {
|
|||
* @param string $view_mode
|
||||
* (optional) The view mode that should be used to display the entity.
|
||||
* Defaults to 'full'.
|
||||
* @param string $langcode
|
||||
* (optional) For which language the entity should be rendered, defaults to
|
||||
* the current content language.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by drupal_render().
|
||||
*/
|
||||
public function view(EntityInterface $_entity, $view_mode = 'full', $langcode = NULL) {
|
||||
public function view(EntityInterface $_entity, $view_mode = 'full') {
|
||||
$page = $this->entityManager
|
||||
->getViewBuilder($_entity->getEntityTypeId())
|
||||
->view($_entity, $view_mode, $langcode);
|
||||
->view($_entity, $view_mode);
|
||||
|
||||
$page['#pre_render'][] = [$this, 'buildTitle'];
|
||||
$page['#entity_type'] = $_entity->getEntityTypeId();
|
||||
|
|
|
@ -155,7 +155,7 @@ class EntityAutocomplete extends Textfield {
|
|||
if ($match === NULL) {
|
||||
// Try to get a match from the input string when the user didn't use
|
||||
// the autocomplete but filled in a value manually.
|
||||
$match = $handler->validateAutocompleteInput($input, $element, $form_state, $complete_form, !$autocreate);
|
||||
$match = static::matchEntityByTitle($handler, $input, $element, $form_state, !$autocreate);
|
||||
}
|
||||
|
||||
if ($match !== NULL) {
|
||||
|
@ -202,6 +202,60 @@ class EntityAutocomplete extends Textfield {
|
|||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an entity from an autocomplete input without an explicit ID.
|
||||
*
|
||||
* The method will return an entity ID if one single entity unambuguously
|
||||
* matches the incoming input, and sill assign form errors otherwise.
|
||||
*
|
||||
* @param string $input
|
||||
* Single string from autocomplete element.
|
||||
* @param array $element
|
||||
* The form element to set a form error.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
* @param bool $strict
|
||||
* Whether to trigger a form error if an element from $input (eg. an entity)
|
||||
* is not found.
|
||||
*
|
||||
* @return integer|null
|
||||
* Value of a matching entity ID, or NULL if none.
|
||||
*/
|
||||
protected static function matchEntityByTitle($handler, $input, &$element, FormStateInterface $form_state, $strict) {
|
||||
$entities_by_bundle = $handler->getReferenceableEntities($input, '=', 6);
|
||||
$entities = array_reduce($entities_by_bundle, function ($flattened, $bundle_entities) {
|
||||
return $flattened + $bundle_entities;
|
||||
}, []);
|
||||
$params = array(
|
||||
'%value' => $input,
|
||||
'@value' => $input,
|
||||
);
|
||||
if (empty($entities)) {
|
||||
if ($strict) {
|
||||
// Error if there are no entities available for a required field.
|
||||
$form_state->setError($element, t('There are no entities matching "%value".', $params));
|
||||
}
|
||||
}
|
||||
elseif (count($entities) > 5) {
|
||||
$params['@id'] = key($entities);
|
||||
// Error if there are more than 5 matching entities.
|
||||
$form_state->setError($element, t('Many entities are called %value. Specify the one you want by appending the id in parentheses, like "@value (@id)".', $params));
|
||||
}
|
||||
elseif (count($entities) > 1) {
|
||||
// More helpful error if there are only a few matching entities.
|
||||
$multiples = array();
|
||||
foreach ($entities as $id => $name) {
|
||||
$multiples[] = $name . ' (' . $id . ')';
|
||||
}
|
||||
$params['@id'] = $id;
|
||||
$form_state->setError($element, t('Multiple entities match this reference; "%multiple". Specify the one you want by appending the id in parentheses, like "@value (@id)".', array('%multiple' => implode('", "', $multiples))));
|
||||
}
|
||||
else {
|
||||
// Take the one and only matching entity.
|
||||
return key($entities);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of entity objects into a string of entity labels.
|
||||
*
|
||||
|
|
|
@ -311,9 +311,9 @@ abstract class Entity implements EntityInterface {
|
|||
->getAccessControlHandler($this->entityTypeId)
|
||||
->createAccess($this->bundle(), $account, [], $return_as_object);
|
||||
}
|
||||
return $this->entityManager()
|
||||
return $this->entityManager()
|
||||
->getAccessControlHandler($this->entityTypeId)
|
||||
->access($this, $operation, LanguageInterface::LANGCODE_DEFAULT, $account, $return_as_object);
|
||||
->access($this, $operation, $account, $return_as_object);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -181,6 +181,7 @@ class EntityViewDisplay extends EntityDisplayBase implements EntityViewDisplayIn
|
|||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
// Reset the render cache for the target entity type.
|
||||
parent::postSave($storage, $update);
|
||||
if (\Drupal::entityManager()->hasHandler($this->targetEntityType, 'view_builder')) {
|
||||
\Drupal::entityManager()->getViewBuilder($this->targetEntityType)->resetCache();
|
||||
}
|
||||
|
@ -248,7 +249,7 @@ class EntityViewDisplay extends EntityDisplayBase implements EntityViewDisplayIn
|
|||
$items = $grouped_items[$id];
|
||||
/** @var \Drupal\Core\Access\AccessResultInterface $field_access */
|
||||
$field_access = $items->access('view', NULL, TRUE);
|
||||
$build_list[$id][$name] = $field_access->isAllowed() ? $formatter->view($items) : [];
|
||||
$build_list[$id][$name] = $field_access->isAllowed() ? $formatter->view($items, $entity->language()->getId()) : [];
|
||||
// Apply the field access cacheability metadata to the render array.
|
||||
$this->renderer->addCacheableDependency($build_list[$id][$name], $field_access);
|
||||
}
|
||||
|
|
|
@ -53,8 +53,9 @@ class EntityAccessControlHandler extends EntityHandlerBase implements EntityAcce
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access(EntityInterface $entity, $operation, $langcode = LanguageInterface::LANGCODE_DEFAULT, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
public function access(EntityInterface $entity, $operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
$account = $this->prepareUser($account);
|
||||
$langcode = $entity->language()->getId();
|
||||
|
||||
if (($return = $this->getCache($entity->uuid(), $operation, $langcode, $account)) !== NULL) {
|
||||
// Cache hit, no work necessary.
|
||||
|
@ -71,8 +72,8 @@ class EntityAccessControlHandler extends EntityHandlerBase implements EntityAcce
|
|||
// - No modules say to deny access.
|
||||
// - At least one module says to grant access.
|
||||
$access = array_merge(
|
||||
$this->moduleHandler()->invokeAll('entity_access', array($entity, $operation, $account, $langcode)),
|
||||
$this->moduleHandler()->invokeAll($entity->getEntityTypeId() . '_access', array($entity, $operation, $account, $langcode))
|
||||
$this->moduleHandler()->invokeAll('entity_access', [$entity, $operation, $account]),
|
||||
$this->moduleHandler()->invokeAll($entity->getEntityTypeId() . '_access', [$entity, $operation, $account])
|
||||
);
|
||||
|
||||
$return = $this->processAccessHookResults($access);
|
||||
|
@ -80,7 +81,7 @@ class EntityAccessControlHandler extends EntityHandlerBase implements EntityAcce
|
|||
// Also execute the default access check except when the access result is
|
||||
// already forbidden, as in that case, it can not be anything else.
|
||||
if (!$return->isForbidden()) {
|
||||
$return = $return->orIf($this->checkAccess($entity, $operation, $langcode, $account));
|
||||
$return = $return->orIf($this->checkAccess($entity, $operation, $account));
|
||||
}
|
||||
$result = $this->setCache($return, $entity->uuid(), $operation, $langcode, $account);
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
|
@ -124,15 +125,13 @@ class EntityAccessControlHandler extends EntityHandlerBase implements EntityAcce
|
|||
* The entity for which to check access.
|
||||
* @param string $operation
|
||||
* The entity operation. Usually one of 'view', 'update' or 'delete'.
|
||||
* @param string $langcode
|
||||
* The language code for which to check access.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to check access.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
|
||||
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
if ($operation == 'delete' && $entity->isNew()) {
|
||||
return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
|
||||
}
|
||||
|
|
|
@ -29,9 +29,6 @@ interface EntityAccessControlHandlerInterface {
|
|||
* @param string $operation
|
||||
* The operation access should be checked for.
|
||||
* Usually one of "view", "update" or "delete".
|
||||
* @param string $langcode
|
||||
* (optional) The language code for which to check access. Defaults to
|
||||
* LanguageInterface::LANGCODE_DEFAULT.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* (optional) The user session for which to check access, or NULL to check
|
||||
* access for the current user. Defaults to NULL.
|
||||
|
@ -45,7 +42,7 @@ interface EntityAccessControlHandlerInterface {
|
|||
* returned, i.e. TRUE means access is explicitly allowed, FALSE means
|
||||
* access is either explicitly forbidden or "no opinion".
|
||||
*/
|
||||
public function access(EntityInterface $entity, $operation, $langcode = LanguageInterface::LANGCODE_DEFAULT, AccountInterface $account = NULL, $return_as_object = FALSE);
|
||||
public function access(EntityInterface $entity, $operation, AccountInterface $account = NULL, $return_as_object = FALSE);
|
||||
|
||||
/**
|
||||
* Checks access to create an entity.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
namespace Drupal\Core\Entity;
|
||||
|
||||
/**
|
||||
* An interface for reacting to entity bundle creation, deletion, and renames.
|
||||
* An interface for reacting to entity bundle creation and deletion.
|
||||
*
|
||||
* @todo Convert to Symfony events: https://www.drupal.org/node/2332935
|
||||
*/
|
||||
|
@ -24,20 +24,6 @@ interface EntityBundleListenerInterface {
|
|||
*/
|
||||
public function onBundleCreate($bundle, $entity_type_id);
|
||||
|
||||
/**
|
||||
* Reacts to a bundle being renamed.
|
||||
*
|
||||
* This method runs before fields are updated with the new bundle name.
|
||||
*
|
||||
* @param string $bundle
|
||||
* The name of the bundle being renamed.
|
||||
* @param string $bundle_new
|
||||
* The new name of the bundle.
|
||||
* @param string $entity_type_id
|
||||
* The entity type to which the bundle is bound; e.g. 'node' or 'user'.
|
||||
*/
|
||||
public function onBundleRename($bundle, $bundle_new, $entity_type_id);
|
||||
|
||||
/**
|
||||
* Reacts to a bundle being deleted.
|
||||
*
|
||||
|
|
|
@ -29,6 +29,16 @@ interface EntityChangedInterface {
|
|||
*/
|
||||
public function getChangedTime();
|
||||
|
||||
/**
|
||||
* Sets the timestamp of the last entity change for the current translation.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* The timestamp of the last entity save operation.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setChangedTime($timestamp);
|
||||
|
||||
/**
|
||||
* Gets the timestamp of the last entity change across all translations.
|
||||
*
|
||||
|
|
|
@ -28,4 +28,26 @@ trait EntityChangedTrait {
|
|||
return $changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timestamp of the last entity change for the current translation.
|
||||
*
|
||||
* @return int
|
||||
* The timestamp of the last entity save operation.
|
||||
*/
|
||||
public function getChangedTime() {
|
||||
return $this->get('changed')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timestamp of the last entity change for the current translation.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* The timestamp of the last entity save operation.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setChangedTime($timestamp) {
|
||||
$this->set('changed', $timestamp);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class EntityDeleteForm extends EntityConfirmFormBase {
|
|||
if (!($entity instanceof ConfigEntityInterface)) {
|
||||
return $form;
|
||||
}
|
||||
$this->addDependencyListsToForm($form, $entity->getConfigDependencyKey(), [$entity->getConfigDependencyName()], $this->getConfigManager(), $this->entityManager);
|
||||
$this->addDependencyListsToForm($form, $entity->getConfigDependencyKey(), $this->getConfigNamesToDelete($entity), $this->getConfigManager(), $this->entityManager);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
@ -49,4 +49,17 @@ class EntityDeleteForm extends EntityConfirmFormBase {
|
|||
return \Drupal::service('config.manager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config names to delete for the deletion confirmation form.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
|
||||
* The entity being deleted.
|
||||
*
|
||||
* @return string[]
|
||||
* A list of configuration names that will be deleted by this form.
|
||||
*/
|
||||
protected function getConfigNamesToDelete(ConfigEntityInterface $entity) {
|
||||
return [$entity->getConfigDependencyName()];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -278,7 +278,7 @@ abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDispl
|
|||
$mode_entity = $this->entityManager()->getStorage('entity_' . $this->displayContext . '_mode')->load($target_entity_type->id() . '.' . $this->mode);
|
||||
$this->addDependency('config', $mode_entity->getConfigDependencyName());
|
||||
}
|
||||
return $this->dependencies;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -428,18 +428,87 @@ abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDispl
|
|||
}
|
||||
}
|
||||
foreach ($this->getComponents() as $name => $component) {
|
||||
if (isset($component['type']) && $definition = $this->pluginManager->getDefinition($component['type'], FALSE)) {
|
||||
if (in_array($definition['provider'], $dependencies['module'])) {
|
||||
if ($renderer = $this->getRenderer($name)) {
|
||||
if (in_array($renderer->getPluginDefinition()['provider'], $dependencies['module'])) {
|
||||
// Revert to the defaults if the plugin that supplies the widget or
|
||||
// formatter depends on a module that is being uninstalled.
|
||||
$this->setComponent($name);
|
||||
$changed = TRUE;
|
||||
}
|
||||
|
||||
// Give this component the opportunity to react on dependency removal.
|
||||
$component_removed_dependencies = $this->getPluginRemovedDependencies($renderer->calculateDependencies(), $dependencies);
|
||||
if ($component_removed_dependencies) {
|
||||
if ($renderer->onDependencyRemoval($component_removed_dependencies)) {
|
||||
// Update component settings to reflect changes.
|
||||
$component['settings'] = $renderer->getSettings();
|
||||
$component['third_party_settings'] = [];
|
||||
foreach ($renderer->getThirdPartyProviders() as $module) {
|
||||
$component['third_party_settings'][$module] = $renderer->getThirdPartySettings($module);
|
||||
}
|
||||
$this->setComponent($name, $component);
|
||||
$changed = TRUE;
|
||||
}
|
||||
// If there are unresolved deleted dependencies left, disable this
|
||||
// component to avoid the removal of the entire display entity.
|
||||
if ($this->getPluginRemovedDependencies($renderer->calculateDependencies(), $dependencies)) {
|
||||
$this->removeComponent($name);
|
||||
$arguments = [
|
||||
'@display' => (string) $this->getEntityType()->getLabel(),
|
||||
'@id' => $this->id(),
|
||||
'@name' => $name,
|
||||
];
|
||||
$this->getLogger()->warning("@display '@id': Component '@name' was disabled because its settings depend on removed dependencies.", $arguments);
|
||||
$changed = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin dependencies being removed.
|
||||
*
|
||||
* The function recursively computes the intersection between all plugin
|
||||
* dependencies and all removed dependencies.
|
||||
*
|
||||
* Note: The two arguments do not have the same structure.
|
||||
*
|
||||
* @param array[] $plugin_dependencies
|
||||
* A list of dependencies having the same structure as the return value of
|
||||
* ConfigEntityInterface::calculateDependencies().
|
||||
* @param array[] $removed_dependencies
|
||||
* A list of dependencies having the same structure as the input argument of
|
||||
* ConfigEntityInterface::onDependencyRemoval().
|
||||
*
|
||||
* @return array
|
||||
* A recursively computed intersection.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies()
|
||||
* @see \Drupal\Core\Config\Entity\ConfigEntityInterface::onDependencyRemoval()
|
||||
*/
|
||||
protected function getPluginRemovedDependencies(array $plugin_dependencies, array $removed_dependencies) {
|
||||
$intersect = [];
|
||||
foreach ($plugin_dependencies as $type => $dependencies) {
|
||||
if ($removed_dependencies[$type]) {
|
||||
// Config and content entities have the dependency names as keys while
|
||||
// module and theme dependencies are indexed arrays of dependency names.
|
||||
// @see \Drupal\Core\Config\ConfigManager::callOnDependencyRemoval()
|
||||
if (in_array($type, ['config', 'content'])) {
|
||||
$removed = array_intersect_key($removed_dependencies[$type], array_flip($dependencies));
|
||||
}
|
||||
else {
|
||||
$removed = array_values(array_intersect($removed_dependencies[$type], $dependencies));
|
||||
}
|
||||
if ($removed) {
|
||||
$intersect[$type] = $removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $intersect;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -471,4 +540,14 @@ abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDispl
|
|||
$this->__construct($values, $this->entityTypeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the 'system' channel logger service.
|
||||
*
|
||||
* @return \Psr\Log\LoggerInterface
|
||||
* The 'system' channel logger.
|
||||
*/
|
||||
protected function getLogger() {
|
||||
return \Drupal::logger('system');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ abstract class EntityDisplayModeBase extends ConfigEntityBase implements EntityD
|
|||
parent::calculateDependencies();
|
||||
$target_entity_type = \Drupal::entityManager()->getDefinition($this->targetEntityType);
|
||||
$this->addDependency('module', $target_entity_type->getProvider());
|
||||
return $this->dependencies;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -240,10 +240,19 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
|
|||
/**
|
||||
* Acts on an entity before the presave hook is invoked.
|
||||
*
|
||||
* Used before the entity is saved and before invoking the presave hook.
|
||||
* Used before the entity is saved and before invoking the presave hook. Note
|
||||
* that in case of translatable content entities this callback is only fired
|
||||
* on their current translation. It is up to the developer to iterate
|
||||
* over all translations if needed. This is different from its counterpart in
|
||||
* the Field API, FieldItemListInterface::preSave(), which is fired on all
|
||||
* field translations automatically.
|
||||
* @todo Adjust existing implementations and the documentation according to
|
||||
* https://www.drupal.org/node/2577609 to have a consistent API.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage object.
|
||||
*
|
||||
* @see \Drupal\Core\Field\FieldItemListInterface::preSave()
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage);
|
||||
|
||||
|
@ -251,7 +260,9 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
|
|||
* Acts on a saved entity before the insert or update hook is invoked.
|
||||
*
|
||||
* Used after the entity is saved, but before invoking the insert or update
|
||||
* hook.
|
||||
* hook. Note that in case of translatable content entities this callback is
|
||||
* only fired on their current translation. It is up to the developer to
|
||||
* iterate over all translations if needed.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage object.
|
||||
|
|
|
@ -228,6 +228,7 @@ class EntityListBuilder extends EntityHandlerBase implements EntityListBuilderIn
|
|||
'#empty' => $this->t('There is no @label yet.', array('@label' => $this->entityType->getLabel())),
|
||||
'#cache' => [
|
||||
'contexts' => $this->entityType->getListCacheContexts(),
|
||||
'tags' => $this->entityType->getListCacheTags(),
|
||||
],
|
||||
);
|
||||
foreach ($this->load() as $entity) {
|
||||
|
|
|
@ -32,7 +32,7 @@ use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
|
|||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\Core\TypedData\TranslatableInterface;
|
||||
use Drupal\Core\TypedData\TypedDataManager;
|
||||
use Drupal\Core\TypedData\TypedDataManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
@ -111,7 +111,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
|||
/**
|
||||
* The typed data manager.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataManager
|
||||
* @var \Drupal\Core\TypedData\TypedDataManagerInterface
|
||||
*/
|
||||
protected $typedDataManager;
|
||||
|
||||
|
@ -193,14 +193,14 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
|||
* The string translationManager.
|
||||
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
|
||||
* The class resolver.
|
||||
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
|
||||
* @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
|
||||
* The typed data manager.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
|
||||
* The keyvalue factory.
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||
* The event dispatcher.
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManager $typed_data_manager, KeyValueFactoryInterface $key_value_factory, EventDispatcherInterface $event_dispatcher) {
|
||||
public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManagerInterface $typed_data_manager, KeyValueFactoryInterface $key_value_factory, EventDispatcherInterface $event_dispatcher) {
|
||||
parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface');
|
||||
|
||||
$this->setCacheBackend($cache, 'entity_type', array('entity_types'));
|
||||
|
@ -945,7 +945,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
|||
|
||||
foreach ($definitions as $entity_type_id => $definition) {
|
||||
if ($group) {
|
||||
$options[$definition->getGroupLabel()][$entity_type_id] = $definition->getLabel();
|
||||
$options[(string) $definition->getGroupLabel()][$entity_type_id] = $definition->getLabel();
|
||||
}
|
||||
else {
|
||||
$options[$entity_type_id] = $definition->getLabel();
|
||||
|
@ -960,7 +960,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
|||
|
||||
// Make sure that the 'Content' group is situated at the top.
|
||||
$content = $this->t('Content', array(), array('context' => 'Entity type group'));
|
||||
$options = array($content => $options[$content]) + $options;
|
||||
$options = array((string) $content => $options[(string) $content]) + $options;
|
||||
}
|
||||
|
||||
return $options;
|
||||
|
@ -1088,15 +1088,29 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getViewModeOptions($entity_type, $include_disabled = FALSE) {
|
||||
return $this->getDisplayModeOptions('view_mode', $entity_type, $include_disabled);
|
||||
public function getViewModeOptions($entity_type_id) {
|
||||
return $this->getDisplayModeOptions('view_mode', $entity_type_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormModeOptions($entity_type, $include_disabled = FALSE) {
|
||||
return $this->getDisplayModeOptions('form_mode', $entity_type, $include_disabled);
|
||||
public function getFormModeOptions($entity_type_id) {
|
||||
return $this->getDisplayModeOptions('form_mode', $entity_type_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getViewModeOptionsByBundle($entity_type_id, $bundle) {
|
||||
return $this->getDisplayModeOptionsByBundle('view_mode', $entity_type_id, $bundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormModeOptionsByBundle($entity_type_id, $bundle) {
|
||||
return $this->getDisplayModeOptionsByBundle('form_mode', $entity_type_id, $bundle);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1106,19 +1120,55 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
|||
* The display type to be retrieved. It can be "view_mode" or "form_mode".
|
||||
* @param string $entity_type_id
|
||||
* The entity type whose display mode options should be returned.
|
||||
* @param bool $include_disabled
|
||||
* Force to include disabled display modes. Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of display mode labels, keyed by the display mode ID.
|
||||
*/
|
||||
protected function getDisplayModeOptions($display_type, $entity_type_id, $include_disabled = FALSE) {
|
||||
protected function getDisplayModeOptions($display_type, $entity_type_id) {
|
||||
$options = array('default' => t('Default'));
|
||||
foreach ($this->getDisplayModesByEntityType($display_type, $entity_type_id) as $mode => $settings) {
|
||||
if (!empty($settings['status']) || $include_disabled) {
|
||||
$options[$mode] = $settings['label'];
|
||||
$options[$mode] = $settings['label'];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of display mode options by bundle.
|
||||
*
|
||||
* @param $display_type
|
||||
* The display type to be retrieved. It can be "view_mode" or "form_mode".
|
||||
* @param string $entity_type_id
|
||||
* The entity type whose display mode options should be returned.
|
||||
* @param string $bundle
|
||||
* The name of the bundle.
|
||||
*
|
||||
* @return array
|
||||
* An array of display mode labels, keyed by the display mode ID.
|
||||
*/
|
||||
protected function getDisplayModeOptionsByBundle($display_type, $entity_type_id, $bundle) {
|
||||
// Collect all the entity's display modes.
|
||||
$options = $this->getDisplayModeOptions($display_type, $entity_type_id);
|
||||
|
||||
// Filter out modes for which the entity display is disabled
|
||||
// (or non-existent).
|
||||
$load_ids = array();
|
||||
// Get the list of available entity displays for the current bundle.
|
||||
foreach (array_keys($options) as $mode) {
|
||||
$load_ids[] = $entity_type_id . '.' . $bundle . '.' . $mode;
|
||||
}
|
||||
|
||||
// Load the corresponding displays.
|
||||
$displays = $this->getStorage($display_type == 'form_mode' ? 'entity_form_display' : 'entity_view_display')
|
||||
->loadMultiple($load_ids);
|
||||
|
||||
// Unset the display modes that are not active or do not exist.
|
||||
foreach (array_keys($options) as $mode) {
|
||||
$display_id = $entity_type_id . '.' . $bundle . '.' . $mode;
|
||||
if (!isset($displays[$display_id]) || !$displays[$display_id]->status()) {
|
||||
unset($options[$mode]);
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
@ -1317,31 +1367,6 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
|||
$this->moduleHandler->invokeAll('entity_bundle_create', array($entity_type_id, $bundle));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onBundleRename($bundle_old, $bundle_new, $entity_type_id) {
|
||||
$this->clearCachedBundles();
|
||||
// Notify the entity storage.
|
||||
$storage = $this->getStorage($entity_type_id);
|
||||
if ($storage instanceof EntityBundleListenerInterface) {
|
||||
$storage->onBundleRename($bundle_old, $bundle_new, $entity_type_id);
|
||||
}
|
||||
|
||||
// Rename existing base field bundle overrides.
|
||||
$overrides = $this->getStorage('base_field_override')->loadByProperties(array('entity_type' => $entity_type_id, 'bundle' => $bundle_old));
|
||||
foreach ($overrides as $override) {
|
||||
$override->set('id', $entity_type_id . '.' . $bundle_new . '.' . $override->getName());
|
||||
$override->set('bundle', $bundle_new);
|
||||
$override->allowBundleRename();
|
||||
$override->save();
|
||||
}
|
||||
|
||||
// Invoke hook_entity_bundle_rename() hook.
|
||||
$this->moduleHandler->invokeAll('entity_bundle_rename', array($entity_type_id, $bundle_old, $bundle_new));
|
||||
$this->clearCachedFieldDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -252,9 +252,9 @@ interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListe
|
|||
* Creates a new handler instance for a entity type and handler type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type for this controller.
|
||||
* The entity type for this handler.
|
||||
* @param string $handler_type
|
||||
* The controller type to create an instance for.
|
||||
* The handler type to create an instance for.
|
||||
*
|
||||
* @return object
|
||||
* A handler instance.
|
||||
|
@ -432,26 +432,48 @@ interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListe
|
|||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type whose view mode options should be returned.
|
||||
* @param bool $include_disabled
|
||||
* Force to include disabled view modes. Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of view mode labels, keyed by the display mode ID.
|
||||
*/
|
||||
public function getViewModeOptions($entity_type_id, $include_disabled = FALSE);
|
||||
public function getViewModeOptions($entity_type_id);
|
||||
|
||||
/**
|
||||
* Gets an array of form mode options.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type whose form mode options should be returned.
|
||||
* @param bool $include_disabled
|
||||
* Force to include disabled form modes. Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of form mode labels, keyed by the display mode ID.
|
||||
*/
|
||||
public function getFormModeOptions($entity_type_id, $include_disabled = FALSE);
|
||||
public function getFormModeOptions($entity_type_id);
|
||||
|
||||
/**
|
||||
* Returns an array of view mode options by bundle.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type whose view mode options should be returned.
|
||||
* @param string $bundle
|
||||
* The name of the bundle.
|
||||
*
|
||||
* @return array
|
||||
* An array of view mode labels, keyed by the display mode ID.
|
||||
*/
|
||||
public function getViewModeOptionsByBundle($entity_type_id, $bundle);
|
||||
|
||||
/**
|
||||
* Returns an array of form mode options by bundle.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type whose form mode options should be returned.
|
||||
* @param string $bundle
|
||||
* The name of the bundle.
|
||||
*
|
||||
* @return array
|
||||
* An array of form mode labels, keyed by the display mode ID.
|
||||
*/
|
||||
public function getFormModeOptionsByBundle($entity_type_id, $bundle);
|
||||
|
||||
/**
|
||||
* Loads an entity by UUID.
|
||||
|
|
|
@ -8,16 +8,13 @@
|
|||
namespace Drupal\Core\Entity\EntityReferenceSelection;
|
||||
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
|
||||
/**
|
||||
* Interface definition for Entity Reference Selection plugins.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase
|
||||
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
|
||||
* @see \Drupal\Core\Entity\Annotation\EntityReferenceSelection
|
||||
* @see \Drupal\Core\Entity\Plugin\Derivative\SelectionBase
|
||||
* @see plugin_api
|
||||
*/
|
||||
interface SelectionInterface extends PluginFormInterface {
|
||||
|
@ -27,7 +24,7 @@ interface SelectionInterface extends PluginFormInterface {
|
|||
*
|
||||
* @return array
|
||||
* A nested array of entities, the first level is keyed by the
|
||||
* entity bundle, which contains an array of entity labels (safe HTML),
|
||||
* entity bundle, which contains an array of entity labels (escaped),
|
||||
* keyed by the entity ID.
|
||||
*/
|
||||
public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0);
|
||||
|
@ -48,28 +45,6 @@ interface SelectionInterface extends PluginFormInterface {
|
|||
*/
|
||||
public function validateReferenceableEntities(array $ids);
|
||||
|
||||
/**
|
||||
* Validates input from an autocomplete widget that has no ID.
|
||||
*
|
||||
* @param string $input
|
||||
* Single string from autocomplete widget.
|
||||
* @param array $element
|
||||
* The form element to set a form error.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current form state.
|
||||
* @param array $form
|
||||
* The form.
|
||||
* @param bool $strict
|
||||
* Whether to trigger a form error if an element from $input (eg. an entity)
|
||||
* is not found. Defaults to TRUE.
|
||||
*
|
||||
* @return integer|null
|
||||
* Value of a matching entity ID, or NULL if none.
|
||||
*
|
||||
* @see \Drupal\entity_reference\Plugin\Field\FieldWidget::elementValidate()
|
||||
*/
|
||||
public function validateAutocompleteInput($input, &$element, FormStateInterface $form_state, $form, $strict = TRUE);
|
||||
|
||||
/**
|
||||
* Allows the selection to alter the SelectQuery generated by EntityFieldQuery.
|
||||
*
|
||||
|
|
|
@ -19,8 +19,6 @@ use Drupal\Core\Plugin\DefaultPluginManager;
|
|||
*
|
||||
* @see \Drupal\Core\Entity\Annotation\EntityReferenceSelection
|
||||
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
|
||||
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase
|
||||
* @see \Drupal\Core\Entity\Plugin\Derivative\SelectionBase
|
||||
* @see plugin_api
|
||||
*/
|
||||
class SelectionPluginManager extends DefaultPluginManager implements SelectionPluginManagerInterface, FallbackPluginManagerInterface {
|
||||
|
|
|
@ -351,20 +351,26 @@ abstract class EntityStorageBase extends EntityHandlerBase implements EntityStor
|
|||
return;
|
||||
}
|
||||
|
||||
// Ensure that the entities are keyed by ID.
|
||||
$keyed_entities = [];
|
||||
foreach ($entities as $entity) {
|
||||
$keyed_entities[$entity->id()] = $entity;
|
||||
}
|
||||
|
||||
// Allow code to run before deleting.
|
||||
$entity_class = $this->entityClass;
|
||||
$entity_class::preDelete($this, $entities);
|
||||
foreach ($entities as $entity) {
|
||||
$entity_class::preDelete($this, $keyed_entities);
|
||||
foreach ($keyed_entities as $entity) {
|
||||
$this->invokeHook('predelete', $entity);
|
||||
}
|
||||
|
||||
// Perform the delete and reset the static cache for the deleted entities.
|
||||
$this->doDelete($entities);
|
||||
$this->resetCache(array_keys($entities));
|
||||
$this->doDelete($keyed_entities);
|
||||
$this->resetCache(array_keys($keyed_entities));
|
||||
|
||||
// Allow code to run after deleting.
|
||||
$entity_class::postDelete($this, $entities);
|
||||
foreach ($entities as $entity) {
|
||||
$entity_class::postDelete($this, $keyed_entities);
|
||||
foreach ($keyed_entities as $entity) {
|
||||
$this->invokeHook('delete', $entity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,7 +110,15 @@ class EntityType implements EntityTypeInterface {
|
|||
/**
|
||||
* The name of a callback that returns the label of the entity.
|
||||
*
|
||||
* @var string|null
|
||||
* @var callable|null
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x-dev and will be removed before Drupal 9.0.0.
|
||||
* Use Drupal\Core\Entity\EntityInterface::label() for complex label
|
||||
* generation as needed.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::label()
|
||||
*
|
||||
* @todo Remove usages of label_callback https://www.drupal.org/node/2450793.
|
||||
*/
|
||||
protected $label_callback = NULL;
|
||||
|
||||
|
@ -688,7 +696,7 @@ class EntityType implements EntityTypeInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return (string) $this->label;
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -725,7 +733,7 @@ class EntityType implements EntityTypeInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getGroupLabel() {
|
||||
return !empty($this->group_label) ? (string) $this->group_label : $this->t('Other', array(), array('context' => 'Entity type group'));
|
||||
return !empty($this->group_label) ? $this->group_label : $this->t('Other', array(), array('context' => 'Entity type group'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\Core\Entity;
|
||||
|
||||
use Drupal\Component\Plugin\Definition\PluginDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for an entity type and its metadata.
|
||||
*
|
||||
|
@ -15,7 +17,7 @@ namespace Drupal\Core\Entity;
|
|||
* implemented to alter existing data and fill-in defaults. Module-specific
|
||||
* properties should be documented in the hook implementations defining them.
|
||||
*/
|
||||
interface EntityTypeInterface {
|
||||
interface EntityTypeInterface extends PluginDefinitionInterface {
|
||||
|
||||
/**
|
||||
* The maximum length of ID, in characters.
|
||||
|
@ -66,14 +68,6 @@ interface EntityTypeInterface {
|
|||
*/
|
||||
public function getProvider();
|
||||
|
||||
/**
|
||||
* Gets the name of the entity type class.
|
||||
*
|
||||
* @return string
|
||||
* The name of the entity type class.
|
||||
*/
|
||||
public function getClass();
|
||||
|
||||
/**
|
||||
* Gets the name of the original entity type class.
|
||||
*
|
||||
|
@ -108,8 +102,8 @@ interface EntityTypeInterface {
|
|||
* - label: (optional) The name of the property that contains the entity
|
||||
* label. For example, if the entity's label is located in
|
||||
* $entity->subject, then 'subject' should be specified here. If complex
|
||||
* logic is required to build the label, a 'label_callback' should be
|
||||
* defined instead (see the $label_callback block above for details).
|
||||
* logic is required to build the label,
|
||||
* \Drupal\Core\Entity\EntityInterface::label() should be used.
|
||||
* - langcode: (optional) The name of the property that contains the
|
||||
* language code. For instance, if the entity's language is located in
|
||||
* $entity->langcode, then 'langcode' should be specified here.
|
||||
|
@ -170,16 +164,6 @@ interface EntityTypeInterface {
|
|||
*/
|
||||
public function isPersistentlyCacheable();
|
||||
|
||||
/**
|
||||
* Sets the name of the entity type class.
|
||||
*
|
||||
* @param string $class
|
||||
* The name of the entity type class.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setClass($class);
|
||||
|
||||
/**
|
||||
* Determines if there is a handler for a given type.
|
||||
*
|
||||
|
@ -493,15 +477,23 @@ interface EntityTypeInterface {
|
|||
* entity label is the main string associated with an entity; for example, the
|
||||
* title of a node or the subject of a comment. If there is an entity object
|
||||
* property that defines the label, use the 'label' element of the
|
||||
* 'entity_keys' return value component to provide this information (see
|
||||
* below). If more complex logic is needed to determine the label of an
|
||||
* entity, you can instead specify a callback function here, which will be
|
||||
* called to determine the entity label. See also the
|
||||
* \Drupal\Core\Entity\EntityInterface::label() method, which implements this
|
||||
* logic.
|
||||
* 'entity_keys' return value component to provide this information. If more
|
||||
* complex logic is needed to determine the label of an entity, you can
|
||||
* instead specify a callback function here, which will be called to determine
|
||||
* the entity label.
|
||||
*
|
||||
* @return callable|null
|
||||
* The callback, or NULL if none exists.
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x-dev and will be removed before Drupal 9.0.0.
|
||||
* Use Drupal\Core\Entity\EntityInterface::label() for complex label
|
||||
* generation as needed.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::label()
|
||||
* @see \Drupal\Core\Entity\EntityTypeInterface::setLabelCallback()
|
||||
* @see \Drupal\Core\Entity\EntityTypeInterface::hasLabelCallback()
|
||||
*
|
||||
* @todo Remove usages of label_callback https://www.drupal.org/node/2450793.
|
||||
*/
|
||||
public function getLabelCallback();
|
||||
|
||||
|
@ -512,6 +504,13 @@ interface EntityTypeInterface {
|
|||
* A callable that returns the label of the entity.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x-dev and will be removed before Drupal 9.0.0.
|
||||
* Use EntityInterface::label() for complex label generation as needed.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::label()
|
||||
* @see \Drupal\Core\Entity\EntityTypeInterface::getLabelCallback()
|
||||
* @see \Drupal\Core\Entity\EntityTypeInterface::hasLabelCallback()
|
||||
*/
|
||||
public function setLabelCallback($callback);
|
||||
|
||||
|
@ -519,6 +518,13 @@ interface EntityTypeInterface {
|
|||
* Indicates if a label callback exists.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @deprecated in Drupal 8.0.x-dev and will be removed before Drupal 9.0.0.
|
||||
* Use EntityInterface::label() for complex label generation as needed.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityInterface::label()
|
||||
* @see \Drupal\Core\Entity\EntityTypeInterface::getLabelCallback()
|
||||
* @see \Drupal\Core\Entity\EntityTypeInterface::setLabelCallback()
|
||||
*/
|
||||
public function hasLabelCallback();
|
||||
|
||||
|
@ -645,6 +651,20 @@ interface EntityTypeInterface {
|
|||
*/
|
||||
public function setUriCallback($callback);
|
||||
|
||||
/**
|
||||
* Gets the machine name of the entity type group.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGroup();
|
||||
|
||||
/**
|
||||
* Gets the human-readable name of the entity type group.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGroupLabel();
|
||||
|
||||
/**
|
||||
* The list cache contexts associated with this entity type.
|
||||
*
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue