diff --git a/core/includes/entity.inc b/core/includes/entity.inc index cc539dc..e010aad 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -57,16 +57,20 @@ function entity_get_bundles($entity_type = NULL) { * NULL. */ function entity_invoke_bundle_hook($hook, $entity_type, $bundle, $bundle_new = NULL) { - \Drupal::entityManager()->clearCachedBundles(); + $entity_manager = \Drupal::entityManager(); + + $entity_manager->clearCachedBundles(); // Notify the entity storage. $method = 'onBundle' . ucfirst($hook); - $storage = \Drupal::entityManager()->getStorage($entity_type); + $storage = $entity_manager->getStorage($entity_type); if (method_exists($storage, $method)) { $storage->$method($bundle, $bundle_new); } // Invoke hook_entity_bundle_*() hooks. \Drupal::moduleHandler()->invokeAll('entity_bundle_' . $hook, array($entity_type, $bundle, $bundle_new)); + // Clear the cached field definitions. + $entity_manager->clearCachedFieldDefinitions(); } /** diff --git a/core/includes/form.inc b/core/includes/form.inc index cc4bcdb..1610f80 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -18,24 +18,6 @@ use Symfony\Component\HttpFoundation\RedirectResponse; /** - * @defgroup forms Form builder functions - * @{ - * Functions that build an abstract representation of a HTML form. - * - * All modules should declare their form builder functions to be in this - * group and each builder function should reference its validate and submit - * functions using \@see. Conversely, validate and submit functions should - * reference the form builder function using \@see. For examples, of this see - * system_modules_uninstall() or user_pass(), the latter of which has the - * following in its doxygen documentation: - * - \@ingroup forms - * - \@see user_pass_validate() - * - \@see user_pass_submit() - * - * @} - */ - -/** * @defgroup form_api Form generation * @{ * Describes how to generate and manipulate forms and process form submissions. diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 18218af..4ba3914 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -2150,7 +2150,7 @@ function template_preprocess_page(&$variables) { if (!defined('MAINTENANCE_MODE')) { $variables['breadcrumb'] = array( '#theme' => 'breadcrumb', - '#breadcrumb' => \Drupal::service('breadcrumb')->build(\Drupal::request()->attributes->all()), + '#breadcrumb' => \Drupal::service('breadcrumb')->build(\Drupal::routeMatch()), ); } } diff --git a/core/lib/Drupal/Component/Plugin/Context/Context.php b/core/lib/Drupal/Component/Plugin/Context/Context.php index b117a72..f301583 100644 --- a/core/lib/Drupal/Component/Plugin/Context/Context.php +++ b/core/lib/Drupal/Component/Plugin/Context/Context.php @@ -26,14 +26,17 @@ class Context implements ContextInterface { /** * The definition to which a context must conform. * - * @var array + * @var \Drupal\Component\Plugin\Context\ContextDefinitionInterface */ protected $contextDefinition; /** * Sets the contextDefinition for us without needing to call the setter. + * + * @param \Drupal\Component\Plugin\Context\ContextDefinitionInterface $context_definition + * The context definition. */ - public function __construct(array $context_definition) { + public function __construct(ContextDefinitionInterface $context_definition) { $this->contextDefinition = $context_definition; } @@ -48,13 +51,22 @@ public function setContextValue($value) { * Implements \Drupal\Component\Plugin\Context\ContextInterface::getContextValue(). */ public function getContextValue() { + // Support optional contexts. + if (!isset($this->contextValue)) { + $definition = $this->getContextDefinition(); + if ($definition->isRequired()) { + $type = $definition->getDataType(); + throw new ContextException(sprintf("The %s context is required and not present.", $type)); + } + return NULL; + } return $this->contextValue; } /** - * Implements \Drupal\Component\Plugin\Context\ContextInterface::setContextDefinition(). + * {@inheritdoc} */ - public function setContextDefinition(array $context_definition) { + public function setContextDefinition(ContextDefinitionInterface $context_definition) { $this->contextDefinition = $context_definition; } diff --git a/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php b/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php new file mode 100644 index 0000000..3d2337b --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php @@ -0,0 +1,158 @@ +getPluginDefinition(); @@ -63,50 +63,47 @@ public function getContextDefinitions() { } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContextDefinition(). + * {@inheritdoc} */ public function getContextDefinition($name) { $definition = $this->getPluginDefinition(); if (empty($definition['context'][$name])) { - throw new PluginException("The $name context is not a valid context."); + throw new ContextException(sprintf("The %s context is not a valid context.", $name)); } return $definition['context'][$name]; } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContexts(). + * {@inheritdoc} */ public function getContexts() { - $definitions = $this->getContextDefinitions(); - if ($definitions && empty($this->context)) { - throw new PluginException("There are no set contexts."); + // Make sure all context objects are initialized. + foreach ($this->getContextDefinitions() as $name => $definition) { + $this->getContext($name); } - $contexts = array(); - foreach (array_keys($definitions) as $name) { - if (empty($this->context[$name])) { - throw new PluginException("The $name context is not yet set."); - } - $contexts[$name] = $this->context[$name]; - } - return $contexts; + return $this->context; } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContext(). + * {@inheritdoc} */ public function getContext($name) { - // Check for a valid context definition. - $this->getContextDefinition($name); // Check for a valid context value. if (!isset($this->context[$name])) { - throw new PluginException("The $name context is not yet set."); + $this->context[$name] = new Context($this->getContextDefinition($name)); } - return $this->context[$name]; } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContextValues(). + * {@inheritdoc} + */ + public function setContext($name, ContextInterface $context) { + $this->context[$name] = $context; + } + + /** + * {@inheritdoc} */ public function getContextValues() { $values = array(); @@ -117,36 +114,30 @@ public function getContextValues() { } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContextValue(). + * {@inheritdoc} */ public function getContextValue($name) { return $this->getContext($name)->getContextValue(); } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::setContextValue(). + * {@inheritdoc} */ public function setContextValue($name, $value) { - $context_definition = $this->getContextDefinition($name); - $this->context[$name] = new Context($context_definition); - $this->context[$name]->setContextValue($value); + $this->getContext($name)->setContextValue($value); return $this; } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::valdidateContexts(). + * {@inheritdoc} */ public function validateContexts() { $violations = new ConstraintViolationList(); // @todo: Implement symfony validator API to let the validator traverse // and set property paths accordingly. - foreach ($this->getContextDefinitions() as $name => $definition) { - // Validate any set values. - if (isset($this->context[$name])) { - $violations->addAll($this->context[$name]->validate()); - } - // @todo: If no value is set, make sure any mapping is validated. + foreach ($this->getContexts() as $context) { + $violations->addAll($context->validate()); } return $violations; } diff --git a/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php b/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php index f29119d..9cb56a1 100644 --- a/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php +++ b/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php @@ -7,7 +7,7 @@ namespace Drupal\Component\Plugin; -use Drupal\Component\Plugin\Exception\PluginException; +use \Drupal\Component\Plugin\Context\ContextInterface; /** * Interface for defining context aware plugins. @@ -36,7 +36,7 @@ public function getContextDefinitions(); * @throws \Drupal\Component\Plugin\Exception\PluginException * If the requested context is not defined. * - * @return array + * @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface. * The definition against which the context value must validate. */ public function getContextDefinition($name); @@ -90,6 +90,16 @@ public function getContextValues(); public function getContextValue($name); /** + * Set a context on this plugin. + * + * @param string $name + * The name of the context in the plugin configuration. + * @param \Drupal\Component\Plugin\Context\ContextInterface $context + * The context object to set. + */ + public function setContext($name, ContextInterface $context); + + /** * Sets the value for a defined context. * * @param string $name diff --git a/core/lib/Drupal/Core/Annotation/ContextDefinition.php b/core/lib/Drupal/Core/Annotation/ContextDefinition.php new file mode 100644 index 0000000..25bc272 --- /dev/null +++ b/core/lib/Drupal/Core/Annotation/ContextDefinition.php @@ -0,0 +1,110 @@ + TRUE, + 'multiple' => FALSE, + 'label' => NULL, + 'description' => NULL, + ); + if (isset($values['class']) && !in_array('Drupal\Core\Plugin\Context\ContextDefinitionInterface', class_implements($values['class']))) { + throw new \Exception('ContextDefinition class must implement \Drupal\Core\Plugin\Context\ContextDefinitionInterface.'); + } + $class = isset($values['class']) ? $values['class'] : 'Drupal\Core\Plugin\Context\ContextDefinition'; + $this->definition = new $class($values['value'], $values['label'], $values['required'], $values['multiple'], $values['description']); + } + + /** + * Returns the value of an annotation. + * + * @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface + */ + public function get() { + return $this->definition; + } + +} diff --git a/core/lib/Drupal/Core/Annotation/Mail.php b/core/lib/Drupal/Core/Annotation/Mail.php index abfddd3..4f0a11d 100644 --- a/core/lib/Drupal/Core/Annotation/Mail.php +++ b/core/lib/Drupal/Core/Annotation/Mail.php @@ -12,6 +12,14 @@ /** * Defines a Mail annotation object. * + * Plugin Namespace: Plugin\Mail + * + * For a working example, see \Drupal\Core\Mail\Plugin\Mail\PhpMail + * + * @see \Drupal\Core\Mail\MailInterface + * @see \Drupal\Core\Mail\MailManager + * @see plugin_api + * * @Annotation */ class Mail extends Plugin { diff --git a/core/lib/Drupal/Core/Archiver/Annotation/Archiver.php b/core/lib/Drupal/Core/Archiver/Annotation/Archiver.php index 553e507..9aff246 100644 --- a/core/lib/Drupal/Core/Archiver/Annotation/Archiver.php +++ b/core/lib/Drupal/Core/Archiver/Annotation/Archiver.php @@ -12,7 +12,13 @@ /** * Defines an archiver annotation object. * + * Plugin Namespace: Plugin\Archiver + * + * For a working example, see \Drupal\system\Plugin\Archiver\Zip + * * @see \Drupal\Core\Archiver\ArchiverManager + * @see \Drupal\Core\Archiver\ArchiverInterface + * @see plugin_api * @see hook_archiver_info_alter() * * @Annotation diff --git a/core/lib/Drupal/Core/Archiver/ArchiverInterface.php b/core/lib/Drupal/Core/Archiver/ArchiverInterface.php index 13d82dc..7d65487 100644 --- a/core/lib/Drupal/Core/Archiver/ArchiverInterface.php +++ b/core/lib/Drupal/Core/Archiver/ArchiverInterface.php @@ -9,6 +9,10 @@ /** * Defines the common interface for all Archiver classes. + * + * @see \Drupal\Core\Archiver\ArchiverManager + * @see \Drupal\Core\Archiver\Annotation\Archiver + * @see plugin_api */ interface ArchiverInterface { diff --git a/core/lib/Drupal/Core/Archiver/ArchiverManager.php b/core/lib/Drupal/Core/Archiver/ArchiverManager.php index e3dd4ab..b161bd9 100644 --- a/core/lib/Drupal/Core/Archiver/ArchiverManager.php +++ b/core/lib/Drupal/Core/Archiver/ArchiverManager.php @@ -12,7 +12,11 @@ use Drupal\Core\Plugin\DefaultPluginManager; /** - * Archiver plugin manager. + * Provides an Archiver plugin manager. + * + * @see \Drupal\Core\Archiver\Annotation\Archiver + * @see \Drupal\Core\Archiver\ArchiverInterface + * @see plugin_api */ class ArchiverManager extends DefaultPluginManager { diff --git a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php index fa7cb59..c808ce6 100644 --- a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php +++ b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Breadcrumb; +use Drupal\Core\Routing\RouteMatchInterface; + /** * Defines an interface for classes that build breadcrumbs. */ @@ -15,25 +17,25 @@ /** * Whether this breadcrumb builder should be used to build the breadcrumb. * - * @param array $attributes - * Attributes representing the current page. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The current route match. * * @return bool * TRUE if this builder should be used or FALSE to let other builders * decide. */ - public function applies(array $attributes); + public function applies(RouteMatchInterface $route_match); /** * Builds the breadcrumb. * - * @param array $attributes - * Attributes representing the current page. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The current route match. * * @return array * An array of HTML links for the breadcrumb. Returning an empty array will * suppress all breadcrumbs. */ - public function build(array $attributes); + public function build(RouteMatchInterface $route_match); } diff --git a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php index f2e138e..2c6ed49 100644 --- a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php +++ b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\String; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Routing\RouteMatchInterface; /** * Provides a breadcrumb manager. @@ -41,7 +42,7 @@ class BreadcrumbManager implements ChainBreadcrumbBuilderInterface { * * Set to NULL if the array needs to be re-calculated. * - * @var array|null + * @var \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface[]|null */ protected $sortedBuilders; @@ -67,25 +68,25 @@ public function addBuilder(BreadcrumbBuilderInterface $builder, $priority) { /** * {@inheritdoc} */ - public function applies(array $attributes) { + public function applies(RouteMatchInterface $route_match) { return TRUE; } /** * {@inheritdoc} */ - public function build(array $attributes) { + public function build(RouteMatchInterface $route_match) { $breadcrumb = array(); $context = array('builder' => NULL); // Call the build method of registered breadcrumb builders, // until one of them returns an array. foreach ($this->getSortedBuilders() as $builder) { - if (!$builder->applies($attributes)) { + if (!$builder->applies($route_match)) { // The builder does not apply, so we continue with the other builders. continue; } - $build = $builder->build($attributes); + $build = $builder->build($route_match); if (is_array($build)) { // The builder returned an array of breadcrumb links. @@ -98,7 +99,7 @@ public function build(array $attributes) { } } // Allow modules to alter the breadcrumb. - $this->moduleHandler->alter('system_breadcrumb', $breadcrumb, $attributes, $context); + $this->moduleHandler->alter('system_breadcrumb', $breadcrumb, $route_match, $context); // Fall back to an empty breadcrumb. return $breadcrumb; } @@ -106,7 +107,7 @@ public function build(array $attributes) { /** * Returns the sorted array of breadcrumb builders. * - * @return array + * @return \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface[] * An array of breadcrumb builder objects. */ protected function getSortedBuilders() { diff --git a/core/lib/Drupal/Core/Condition/Annotation/Condition.php b/core/lib/Drupal/Core/Condition/Annotation/Condition.php index 64b4946..55834d0 100644 --- a/core/lib/Drupal/Core/Condition/Annotation/Condition.php +++ b/core/lib/Drupal/Core/Condition/Annotation/Condition.php @@ -9,7 +9,21 @@ use Drupal\Component\Annotation\Plugin; /** - * Defines a condition annotation object. + * Defines a condition plugin annotation object. + * + * Condition plugins provide generalized conditions for use in other + * operations, such as conditional block placement. + * + * Plugin Namespace: Plugin\Condition + * + * For a working example, see \Drupal\user\Plugin\Condition\UserRole. + * + * @see \Drupal\Core\Condition\ConditionManager + * @see \Drupal\Core\Condition\ConditionInterface + * @see \Drupal\Core\Condition\ConditionPluginBase + * @see block_api + * + * @ingroup plugin_api * * @Annotation */ diff --git a/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php b/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php index 35587eb..528e662 100644 --- a/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php +++ b/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php @@ -7,7 +7,7 @@ namespace Drupal\Core\Condition; -use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\Component\Plugin\Exception\ContextException; /** * Resolves a set of conditions. @@ -30,7 +30,7 @@ protected function resolveConditions($conditions, $condition_logic) { try { $pass = $condition->execute(); } - catch (PluginException $e) { + catch (ContextException $e) { // If a condition is missing context, consider that a fail. $pass = FALSE; } diff --git a/core/lib/Drupal/Core/Condition/ConditionInterface.php b/core/lib/Drupal/Core/Condition/ConditionInterface.php index 7b1d412..fb23fe2 100644 --- a/core/lib/Drupal/Core/Condition/ConditionInterface.php +++ b/core/lib/Drupal/Core/Condition/ConditionInterface.php @@ -30,6 +30,11 @@ * * @see \Drupal\Core\TypedData\TypedDataManager::create() * @see \Drupal\Core\Executable\ExecutableInterface + * @see \Drupal\Core\Condition\ConditionManager + * @see \Drupal\Core\Condition\Annotation\Condition + * @see \Drupal\Core\Condition\ConditionPluginBase + * + * @ingroup plugin_api */ interface ConditionInterface extends ExecutableInterface, PluginFormInterface, ConfigurablePluginInterface, PluginInspectionInterface { diff --git a/core/lib/Drupal/Core/Condition/ConditionManager.php b/core/lib/Drupal/Core/Condition/ConditionManager.php index f6f7641..db0fa43 100644 --- a/core/lib/Drupal/Core/Condition/ConditionManager.php +++ b/core/lib/Drupal/Core/Condition/ConditionManager.php @@ -16,6 +16,12 @@ /** * A plugin manager for condition plugins. + * + * @see \Drupal\Core\Condition\Annotation\Condition + * @see \Drupal\Core\Condition\ConditionInterface + * @see \Drupal\Core\Condition\ConditionPluginBase + * + * @ingroup plugin_api */ class ConditionManager extends DefaultPluginManager implements ExecutableManagerInterface { @@ -44,6 +50,14 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac */ public function createInstance($plugin_id, array $configuration = array()) { $plugin = $this->factory->createInstance($plugin_id, $configuration); + + // If we receive any context values via config set it into the plugin. + if (!empty($configuration['context'])) { + foreach ($configuration['context'] as $name => $context) { + $plugin->setContextValue($name, $context); + } + } + return $plugin->setExecutableManager($this); } diff --git a/core/lib/Drupal/Core/Condition/ConditionPluginBase.php b/core/lib/Drupal/Core/Condition/ConditionPluginBase.php index 279ef0f..a36c770 100644 --- a/core/lib/Drupal/Core/Condition/ConditionPluginBase.php +++ b/core/lib/Drupal/Core/Condition/ConditionPluginBase.php @@ -11,12 +11,25 @@ /** * Provides a basis for fulfilling contexts for condition plugins. + * + * @see \Drupal\Core\Condition\Annotation\Condition + * @see \Drupal\Core\Condition\ConditionInterface + * @see \Drupal\Core\Condition\ConditionManager + * + * @ingroup plugin_api */ abstract class ConditionPluginBase extends ExecutablePluginBase implements ConditionInterface { /** * {@inheritdoc} */ + public static function contextDefinitions() { + return []; + } + + /** + * {@inheritdoc} + */ public function __construct(array $configuration, $plugin_id, $plugin_definition) { parent::__construct($configuration, $plugin_id, $plugin_definition); diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php index 8bf2c4e..bb3cc53 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -10,16 +10,15 @@ use Drupal\Component\Utility\String; use Drupal\Core\Database\Connection; use Drupal\Core\Database\Database; +use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException; use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Entity\Schema\ContentEntitySchemaHandler; use Drupal\Core\Entity\Sql\DefaultTableMapping; use Drupal\Core\Entity\Sql\SqlEntityStorageInterface; +use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\field\Entity\FieldConfig; -use Drupal\field\FieldConfigInterface; -use Drupal\field\FieldConfigUpdateForbiddenException; -use Drupal\field\FieldInstanceConfigInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -954,21 +953,22 @@ protected function doLoadFieldItems($entities, $age) { } // Collect impacted fields. - $fields = array(); + $storage_definitions = array(); $definitions = array(); foreach ($bundles as $bundle => $v) { $definitions[$bundle] = $this->entityManager->getFieldDefinitions($this->entityTypeId, $bundle); - foreach ($definitions[$bundle] as $field_name => $instance) { - if ($instance instanceof FieldInstanceConfigInterface) { - $fields[$field_name] = $instance->getFieldStorageDefinition(); + foreach ($definitions[$bundle] as $field_name => $field_definition) { + $storage_definition = $field_definition->getFieldStorageDefinition(); + if ($this->usesDedicatedTable($storage_definition)) { + $storage_definitions[$field_name] = $storage_definition; } } } // Load field data. $langcodes = array_keys(language_list(LanguageInterface::STATE_ALL)); - foreach ($fields as $field_name => $field) { - $table = $load_current ? static::_fieldTableName($field) : static::_fieldRevisionTableName($field); + foreach ($storage_definitions as $field_name => $storage_definition) { + $table = $load_current ? static::_fieldTableName($storage_definition) : static::_fieldRevisionTableName($storage_definition); // Ensure that only values having valid languages are retrieved. Since we // are loading values for multiple entities, we cannot limit the query to @@ -992,12 +992,12 @@ protected function doLoadFieldItems($entities, $age) { $delta_count[$row->entity_id][$row->langcode] = 0; } - if ($field->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field->getCardinality()) { + if ($storage_definition->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $storage_definition->getCardinality()) { $item = array(); // For each column declared by the field, populate the item from the // prefixed database column. - foreach ($field->getColumns() as $column => $attributes) { - $column_name = static::_fieldColumnName($field, $column); + foreach ($storage_definition->getColumns() as $column => $attributes) { + $column_name = static::_fieldColumnName($storage_definition, $column); // Unserialize the value if specified in the column schema. $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name; } @@ -1026,13 +1026,13 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) { $vid = $id; } - foreach ($this->entityManager->getFieldDefinitions($entity_type, $bundle) as $field_name => $instance) { - if (!($instance instanceof FieldInstanceConfigInterface)) { + foreach ($this->entityManager->getFieldDefinitions($entity_type, $bundle) as $field_name => $field_definition) { + $storage_definition = $field_definition->getFieldStorageDefinition(); + if (!$this->usesDedicatedTable($storage_definition)) { continue; } - $field = $instance->getFieldStorageDefinition(); - $table_name = static::_fieldTableName($field); - $revision_name = static::_fieldRevisionTableName($field); + $table_name = static::_fieldTableName($storage_definition); + $revision_name = static::_fieldRevisionTableName($storage_definition); // Delete and insert, rather than update, in case a value was added. if ($update) { @@ -1052,13 +1052,13 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) { // Prepare the multi-insert query. $do_insert = FALSE; $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode'); - foreach ($field->getColumns() as $column => $attributes) { - $columns[] = static::_fieldColumnName($field, $column); + foreach ($storage_definition->getColumns() as $column => $attributes) { + $columns[] = static::_fieldColumnName($storage_definition, $column); } $query = $this->database->insert($table_name)->fields($columns); $revision_query = $this->database->insert($revision_name)->fields($columns); - $langcodes = $field->isTranslatable() ? $translation_langcodes : array($default_langcode); + $langcodes = $field_definition->isTranslatable() ? $translation_langcodes : array($default_langcode); foreach ($langcodes as $langcode) { $delta_count = 0; $items = $entity->getTranslation($langcode)->get($field_name); @@ -1073,15 +1073,15 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) { 'delta' => $delta, 'langcode' => $langcode, ); - foreach ($field->getColumns() as $column => $attributes) { - $column_name = static::_fieldColumnName($field, $column); + foreach ($storage_definition->getColumns() as $column => $attributes) { + $column_name = static::_fieldColumnName($storage_definition, $column); // Serialize the value if specified in the column schema. $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column; } $query->values($record); $revision_query->values($record); - if ($field->getCardinality() != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $field->getCardinality()) { + if ($storage_definition->getCardinality() != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $storage_definition->getCardinality()) { break; } } @@ -1103,13 +1103,13 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) { * {@inheritdoc} */ protected function doDeleteFieldItems(EntityInterface $entity) { - foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $instance) { - if (!($instance instanceof FieldInstanceConfigInterface)) { + foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) { + $storage_definition = $field_definition->getFieldStorageDefinition(); + if (!$this->usesDedicatedTable($storage_definition)) { continue; } - $field = $instance->getFieldStorageDefinition(); - $table_name = static::_fieldTableName($field); - $revision_name = static::_fieldRevisionTableName($field); + $table_name = static::_fieldTableName($storage_definition); + $revision_name = static::_fieldRevisionTableName($storage_definition); $this->database->delete($table_name) ->condition('entity_id', $entity->id()) ->execute(); @@ -1125,11 +1125,12 @@ protected function doDeleteFieldItems(EntityInterface $entity) { protected function doDeleteFieldItemsRevision(EntityInterface $entity) { $vid = $entity->getRevisionId(); if (isset($vid)) { - foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $instance) { - if (!($instance instanceof FieldInstanceConfigInterface)) { + foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) { + $storage_definition = $field_definition->getFieldStorageDefinition(); + if (!$this->usesDedicatedTable($storage_definition)) { continue; } - $revision_name = static::_fieldRevisionTableName($instance->getFieldStorageDefinition()); + $revision_name = static::_fieldRevisionTableName($storage_definition); $this->database->delete($revision_name) ->condition('entity_id', $entity->id()) ->condition('revision_id', $vid) @@ -1139,10 +1140,25 @@ protected function doDeleteFieldItemsRevision(EntityInterface $entity) { } /** + * Returns whether the field uses a dedicated table for storage. + * + * @param FieldStorageDefinitionInterface $definition + * The field storage definition. + * + * @return bool + * Whether the field uses a dedicated table for storage. + */ + protected function usesDedicatedTable(FieldStorageDefinitionInterface $definition) { + // Everything that is not provided by the entity type is stored in a + // dedicated table. + return $definition->getProvider() != $this->entityType->getProvider() && !$definition->hasCustomStorage(); + } + + /** * {@inheritdoc} */ - public function onFieldCreate(FieldConfigInterface $field) { - $schema = $this->_fieldSqlSchema($field); + public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) { + $schema = $this->_fieldSqlSchema($storage_definition); foreach ($schema as $name => $table) { $this->database->schema()->createTable($name, $table); } @@ -1151,10 +1167,8 @@ public function onFieldCreate(FieldConfigInterface $field) { /** * {@inheritdoc} */ - public function onFieldUpdate(FieldConfigInterface $field) { - $original = $field->original; - - if (!$field->hasData()) { + public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) { + if (!$storage_definition->hasData()) { // There is no data. Re-create the tables completely. if ($this->database->supportsTransactionalDDL()) { @@ -1168,7 +1182,7 @@ public function onFieldUpdate(FieldConfigInterface $field) { foreach ($original_schema as $name => $table) { $this->database->schema()->dropTable($name, $table); } - $schema = $this->_fieldSqlSchema($field); + $schema = $this->_fieldSqlSchema($storage_definition); foreach ($schema as $name => $table) { $this->database->schema()->createTable($name, $table); } @@ -1190,8 +1204,8 @@ public function onFieldUpdate(FieldConfigInterface $field) { } } else { - if ($field->getColumns() != $original->getColumns()) { - throw new FieldConfigUpdateForbiddenException("The SQL storage cannot change the schema for an existing field with data."); + if ($storage_definition->getColumns() != $original->getColumns()) { + throw new FieldStorageDefinitionUpdateForbiddenException("The SQL storage cannot change the schema for an existing field with data."); } // There is data, so there are no column changes. Drop all the prior // indexes and create all the new ones, except for all the priors that @@ -1199,33 +1213,33 @@ public function onFieldUpdate(FieldConfigInterface $field) { $table = static::_fieldTableName($original); $revision_table = static::_fieldRevisionTableName($original); - $schema = $field->getSchema(); + $schema = $storage_definition->getSchema(); $original_schema = $original->getSchema(); foreach ($original_schema['indexes'] as $name => $columns) { if (!isset($schema['indexes'][$name]) || $columns != $schema['indexes'][$name]) { - $real_name = static::_fieldIndexName($field, $name); + $real_name = static::_fieldIndexName($storage_definition, $name); $this->database->schema()->dropIndex($table, $real_name); $this->database->schema()->dropIndex($revision_table, $real_name); } } - $table = static::_fieldTableName($field); - $revision_table = static::_fieldRevisionTableName($field); + $table = static::_fieldTableName($storage_definition); + $revision_table = static::_fieldRevisionTableName($storage_definition); foreach ($schema['indexes'] as $name => $columns) { if (!isset($original_schema['indexes'][$name]) || $columns != $original_schema['indexes'][$name]) { - $real_name = static::_fieldIndexName($field, $name); + $real_name = static::_fieldIndexName($storage_definition, $name); $real_columns = array(); foreach ($columns as $column_name) { // Indexes can be specified as either a column name or an array with // column name and length. Allow for either case. if (is_array($column_name)) { $real_columns[] = array( - static::_fieldColumnName($field, $column_name[0]), + static::_fieldColumnName($storage_definition, $column_name[0]), $column_name[1], ); } else { - $real_columns[] = static::_fieldColumnName($field, $column_name); + $real_columns[] = static::_fieldColumnName($storage_definition, $column_name); } } $this->database->schema()->addIndex($table, $real_name, $real_columns); @@ -1238,20 +1252,18 @@ public function onFieldUpdate(FieldConfigInterface $field) { /** * {@inheritdoc} */ - public function onFieldDelete(FieldConfigInterface $field) { + public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) { // Mark all data associated with the field for deletion. - $table = static::_fieldTableName($field); - $revision_table = static::_fieldRevisionTableName($field); + $table = static::_fieldTableName($storage_definition); + $revision_table = static::_fieldRevisionTableName($storage_definition); $this->database->update($table) ->fields(array('deleted' => 1)) ->execute(); // Move the table to a unique name while the table contents are being // deleted. - $deleted_field = clone $field; - $deleted_field->deleted = TRUE; - $new_table = static::_fieldTableName($deleted_field); - $revision_new_table = static::_fieldRevisionTableName($deleted_field); + $new_table = static::_fieldTableName($storage_definition, TRUE); + $revision_new_table = static::_fieldRevisionTableName($storage_definition, TRUE); $this->database->schema()->renameTable($table, $new_table); $this->database->schema()->renameTable($revision_table, $revision_new_table); } @@ -1259,17 +1271,19 @@ public function onFieldDelete(FieldConfigInterface $field) { /** * {@inheritdoc} */ - public function onInstanceDelete(FieldInstanceConfigInterface $instance) { - $field = $instance->getFieldStorageDefinition(); - $table_name = static::_fieldTableName($field); - $revision_name = static::_fieldRevisionTableName($field); + public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition) { + $storage_definition = $field_definition->getFieldStorageDefinition(); + $table_name = static::_fieldTableName($storage_definition); + $revision_name = static::_fieldRevisionTableName($storage_definition); + + // Mark field data as deleted. $this->database->update($table_name) ->fields(array('deleted' => 1)) - ->condition('bundle', $instance->bundle) + ->condition('bundle', $field_definition->getBundle()) ->execute(); $this->database->update($revision_name) ->fields(array('deleted' => 1)) - ->condition('bundle', $instance->bundle) + ->condition('bundle', $field_definition->getBundle()) ->execute(); } @@ -1277,14 +1291,20 @@ public function onInstanceDelete(FieldInstanceConfigInterface $instance) { * {@inheritdoc} */ public function onBundleRename($bundle, $bundle_new) { - // We need to account for deleted fields and instances. The method runs - // before the instance definitions are updated, so we need to fetch them - // using the old bundle name. - $instances = entity_load_multiple_by_properties('field_instance_config', array('entity_type' => $this->entityTypeId, 'bundle' => $bundle, 'include_deleted' => TRUE)); - foreach ($instances as $instance) { - $field = $instance->getFieldStorageDefinition(); - $table_name = static::_fieldTableName($field); - $revision_name = static::_fieldRevisionTableName($field); + // The method runs before the field definitions are updated, so we use the + // old bundle name. + $field_definitions = $this->entityManager->getFieldDefinitions($this->entityTypeId, $bundle); + // We need to handle deleted fields too. For now, this only makes sense for + // configurable fields, so we use the specific API. + // @todo Use the unified store of deleted field definitions instead in + // https://www.drupal.org/node/2282119 + $field_definitions += entity_load_multiple_by_properties('field_instance_config', array('entity_type' => $this->entityTypeId, 'bundle' => $bundle, 'deleted' => TRUE, 'include_deleted' => TRUE)); + + foreach ($field_definitions as $field_definition) { + $storage_definition = $field_definition->getFieldStorageDefinition(); + $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); + $table_name = static::_fieldTableName($storage_definition, $is_deleted); + $revision_name = static::_fieldRevisionTableName($storage_definition, $is_deleted); $this->database->update($table_name) ->fields(array('bundle' => $bundle_new)) ->condition('bundle', $bundle) @@ -1299,54 +1319,141 @@ public function onBundleRename($bundle, $bundle_new) { /** * {@inheritdoc} */ - protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceConfigInterface $instance) { - $field = $instance->getFieldStorageDefinition(); - $table_name = static::_fieldTableName($field); - $query = $this->database->select($table_name, 't', array('fetch' => \PDO::FETCH_ASSOC)) - ->condition('entity_id', $entity->id()) - ->orderBy('delta'); - foreach ($field->getColumns() as $column_name => $data) { - $query->addField('t', static::_fieldColumnName($field, $column_name), $column_name); - } - return $query->execute()->fetchAll(); + protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definition, $batch_size) { + // Check whether the whole field storage definition is gone, or just some + // bundle fields. + $storage_definition = $field_definition->getFieldStorageDefinition(); + $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); + $table_name = static::_fieldTableName($storage_definition, $is_deleted); + + // Get the entities which we want to purge first. + $entity_query = $this->database->select($table_name, 't', array('fetch' => \PDO::FETCH_ASSOC)); + $or = $entity_query->orConditionGroup(); + foreach ($storage_definition->getColumns() as $column_name => $data) { + $or->isNotNull(static::_fieldColumnName($storage_definition, $column_name)); + } + $entity_query + ->distinct(TRUE) + ->fields('t', array('entity_id')) + ->condition('bundle', $field_definition->getBundle()) + ->range(0, $batch_size); + + // Create a map of field data table column names to field column names. + $column_map = array(); + foreach ($storage_definition->getColumns() as $column_name => $data) { + $column_map[static::_fieldColumnName($storage_definition, $column_name)] = $column_name; + } + + $entities = array(); + $items_by_entity = array(); + foreach ($entity_query->execute() as $row) { + $item_query = $this->database->select($table_name, 't', array('fetch' => \PDO::FETCH_ASSOC)) + ->fields('t') + ->condition('entity_id', $row['entity_id']) + ->orderBy('delta'); + + foreach ($item_query->execute() as $item_row) { + if (!isset($entities[$item_row['revision_id']])) { + // Create entity with the right revision id and entity id combination. + $item_row['entity_type'] = $this->entityTypeId; + // @todo: Replace this by an entity object created via an entity + // factory, see https://drupal.org/node/1867228. + $entities[$item_row['revision_id']] = _field_create_entity_from_ids((object) $item_row); + } + $item = array(); + foreach ($column_map as $db_column => $field_column) { + $item[$field_column] = $item_row[$db_column]; + } + $items_by_entity[$item_row['revision_id']][] = $item; + } + } + + // Create field item objects and return. + foreach ($items_by_entity as $revision_id => $values) { + $items_by_entity[$revision_id] = \Drupal::typedDataManager()->create($field_definition, $values, $field_definition->getName(), $entities[$revision_id]); + } + return $items_by_entity; } /** * {@inheritdoc} */ - public function purgeFieldItems(EntityInterface $entity, FieldInstanceConfigInterface $instance) { - $field = $instance->getFieldStorageDefinition(); - $table_name = static::_fieldTableName($field); - $revision_name = static::_fieldRevisionTableName($field); + protected function purgeFieldItems(ContentEntityInterface $entity, FieldDefinitionInterface $field_definition) { + $storage_definition = $field_definition->getFieldStorageDefinition(); + $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); + $table_name = static::_fieldTableName($storage_definition, $is_deleted); + $revision_name = static::_fieldRevisionTableName($storage_definition, $is_deleted); + $revision_id = $this->entityType->isRevisionable() ? $entity->getRevisionId() : $entity->id(); $this->database->delete($table_name) - ->condition('entity_id', $entity->id()) + ->condition('revision_id', $revision_id) ->execute(); $this->database->delete($revision_name) - ->condition('entity_id', $entity->id()) + ->condition('revision_id', $revision_id) ->execute(); } /** * {@inheritdoc} */ - public function onFieldPurge(FieldConfigInterface $field) { - $table_name = static::_fieldTableName($field); - $revision_name = static::_fieldRevisionTableName($field); + public function finalizePurge(FieldStorageDefinitionInterface $storage_definition) { + $table_name = static::_fieldTableName($storage_definition, TRUE); + $revision_name = static::_fieldRevisionTableName($storage_definition, TRUE); $this->database->schema()->dropTable($table_name); $this->database->schema()->dropTable($revision_name); } /** + * {@inheritdoc} + */ + public function countFieldData($storage_definition, $as_bool = FALSE) { + $is_deleted = $this->storageDefinitionIsDeleted($storage_definition); + $table_name = static::_fieldTableName($storage_definition, $is_deleted); + + $query = $this->database->select($table_name, 't'); + $or = $query->orConditionGroup(); + foreach ($storage_definition->getColumns() as $column_name => $data) { + $or->isNotNull(static::_fieldColumnName($storage_definition, $column_name)); + } + $query + ->condition($or) + ->fields('t', array('entity_id')) + ->distinct(TRUE); + // If we are performing the query just to check if the field has data + // limit the number of rows. + if ($as_bool) { + $query->range(0, 1); + } + $count = $query->countQuery()->execute()->fetchField(); + return $as_bool ? (bool) $count : (int) $count; + } + + /** + * Returns whether the passed field has been already deleted. + * + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition + * The field storage definition. + * + * @return bool + * Whether the field has been already deleted. + */ + protected function storageDefinitionIsDeleted(FieldStorageDefinitionInterface $storage_definition) { + return !array_key_exists($storage_definition->getName(), $this->entityManager->getFieldStorageDefinitions($this->entityTypeId)); + } + + /** * Gets the SQL table schema. * * @private Calling this function circumvents the entity system and is * strongly discouraged. This function is not considered part of the public * API and modules relying on it might break even in minor releases. * - * @param \Drupal\field\FieldConfigInterface $field - * The field object + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition + * The field storage definition. * @param array $schema * The field schema array. Mandatory for upgrades, omit otherwise. + * @param bool $deleted + * (optional) Whether the schema of the table holding the values of a + * deleted field should be returned. * * @return array * The same as a hook_schema() implementation for the data and the @@ -1354,17 +1461,11 @@ public function onFieldPurge(FieldConfigInterface $field) { * * @see hook_schema() */ - public static function _fieldSqlSchema(FieldConfigInterface $field, array $schema = NULL) { - if ($field->deleted) { - $description_current = "Data storage for deleted field {$field->uuid()} ({$field->entity_type}, {$field->getName()})."; - $description_revision = "Revision archive storage for deleted field {$field->uuid()} ({$field->entity_type}, {$field->getName()})."; - } - else { - $description_current = "Data storage for {$field->entity_type} field {$field->getName()}."; - $description_revision = "Revision archive storage for {$field->entity_type} field {$field->getName()}."; - } + public static function _fieldSqlSchema(FieldStorageDefinitionInterface $storage_definition, array $schema = NULL, $deleted = FALSE) { + $description_current = "Data storage for {$storage_definition->getTargetEntityTypeId()} field {$storage_definition->getName()}."; + $description_revision = "Revision archive storage for {$storage_definition->getTargetEntityTypeId()} field {$storage_definition->getName()}."; - $entity_type_id = $field->entity_type; + $entity_type_id = $storage_definition->getTargetEntityTypeId(); $entity_manager = \Drupal::entityManager(); $entity_type = $entity_manager->getDefinition($entity_type_id); $definitions = $entity_manager->getBaseFieldDefinitions($entity_type_id); @@ -1452,47 +1553,47 @@ public static function _fieldSqlSchema(FieldConfigInterface $field, array $schem ); if (!$schema) { - $schema = $field->getSchema(); + $schema = $storage_definition->getSchema(); } // Add field columns. foreach ($schema['columns'] as $column_name => $attributes) { - $real_name = static::_fieldColumnName($field, $column_name); + $real_name = static::_fieldColumnName($storage_definition, $column_name); $current['fields'][$real_name] = $attributes; } // Add unique keys. foreach ($schema['unique keys'] as $unique_key_name => $columns) { - $real_name = static::_fieldIndexName($field, $unique_key_name); + $real_name = static::_fieldIndexName($storage_definition, $unique_key_name); foreach ($columns as $column_name) { - $current['unique keys'][$real_name][] = static::_fieldColumnName($field, $column_name); + $current['unique keys'][$real_name][] = static::_fieldColumnName($storage_definition, $column_name); } } // Add indexes. foreach ($schema['indexes'] as $index_name => $columns) { - $real_name = static::_fieldIndexName($field, $index_name); + $real_name = static::_fieldIndexName($storage_definition, $index_name); foreach ($columns as $column_name) { // Indexes can be specified as either a column name or an array with // column name and length. Allow for either case. if (is_array($column_name)) { $current['indexes'][$real_name][] = array( - static::_fieldColumnName($field, $column_name[0]), + static::_fieldColumnName($storage_definition, $column_name[0]), $column_name[1], ); } else { - $current['indexes'][$real_name][] = static::_fieldColumnName($field, $column_name); + $current['indexes'][$real_name][] = static::_fieldColumnName($storage_definition, $column_name); } } } // Add foreign keys. foreach ($schema['foreign keys'] as $specifier => $specification) { - $real_name = static::_fieldIndexName($field, $specifier); + $real_name = static::_fieldIndexName($storage_definition, $specifier); $current['foreign keys'][$real_name]['table'] = $specification['table']; foreach ($specification['columns'] as $column_name => $referenced) { - $sql_storage_column = static::_fieldColumnName($field, $column_name); + $sql_storage_column = static::_fieldColumnName($storage_definition, $column_name); $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced; } } @@ -1505,8 +1606,8 @@ public static function _fieldSqlSchema(FieldConfigInterface $field, array $schem $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to'; return array( - static::_fieldTableName($field) => $current, - static::_fieldRevisionTableName($field) => $revision, + static::_fieldTableName($storage_definition) => $current, + static::_fieldRevisionTableName($storage_definition) => $revision, ); } @@ -1520,23 +1621,26 @@ public static function _fieldSqlSchema(FieldConfigInterface $field, array $schem * support. Always call entity_load() before using the data found in the * table. * - * @param \Drupal\field\FieldConfigInterface $field - * The field object. + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition + * The field storage definition. + * @param bool $is_deleted + * (optional) Whether the table name holding the values of a deleted field + * should be returned. * * @return string * A string containing the generated name for the database table. - * */ - static public function _fieldTableName(FieldConfigInterface $field) { - if ($field->deleted) { + public static function _fieldTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) { + if ($is_deleted) { // When a field is a deleted, the table is renamed to // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with - // table names longer than 64 characters, we hash the uuid and return the - // first 10 characters so we end up with a short unique ID. - return "field_deleted_data_" . substr(hash('sha256', $field->uuid()), 0, 10); + // table names longer than 64 characters, we hash the unique storage + // identifier and return the first 10 characters so we end up with a short + // unique ID. + return "field_deleted_data_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10); } else { - return static::_generateFieldTableName($field, FALSE); + return static::_generateFieldTableName($storage_definition, FALSE); } } @@ -1550,22 +1654,26 @@ static public function _fieldTableName(FieldConfigInterface $field) { * support. Always call entity_load() before using the data found in the * table. * - * @param \Drupal\field\FieldConfigInterface $field - * The field object. + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition + * The field storage definition. + * @param bool $is_deleted + * (optional) Whether the table name holding the values of a deleted field + * should be returned. * * @return string * A string containing the generated name for the database table. */ - static public function _fieldRevisionTableName(FieldConfigInterface $field) { - if ($field->deleted) { + public static function _fieldRevisionTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) { + if ($is_deleted) { // When a field is a deleted, the table is renamed to // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with - // table names longer than 64 characters, we hash the uuid and return the - // first 10 characters so we end up with a short unique ID. - return "field_deleted_revision_" . substr(hash('sha256', $field->uuid()), 0, 10); + // table names longer than 64 characters, we hash the unique storage + // identifier and return the first 10 characters so we end up with a short + // unique ID. + return "field_deleted_revision_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10); } else { - return static::_generateFieldTableName($field, TRUE); + return static::_generateFieldTableName($storage_definition, TRUE); } } @@ -1575,17 +1683,17 @@ static public function _fieldRevisionTableName(FieldConfigInterface $field) { * The method accounts for a maximum table name length of 64 characters, and * takes care of disambiguation. * - * @param \Drupal\field\FieldConfigInterface $field - * The field object. + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition + * The field storage definition. * @param bool $revision * TRUE for revision table, FALSE otherwise. * * @return string * The final table name. */ - static protected function _generateFieldTableName(FieldConfigInterface $field, $revision) { + protected static function _generateFieldTableName(FieldStorageDefinitionInterface $storage_definition, $revision) { $separator = $revision ? '_revision__' : '__'; - $table_name = $field->entity_type . $separator . $field->name; + $table_name = $storage_definition->getTargetEntityTypeId() . $separator . $storage_definition->getName(); // Limit the string to 48 characters, keeping a 16 characters margin for db // prefixes. if (strlen($table_name) > 48) { @@ -1593,8 +1701,8 @@ static protected function _generateFieldTableName(FieldConfigInterface $field, $ // field UUID. $separator = $revision ? '_r__' : '__'; // Truncate to the same length for the current and revision tables. - $entity_type = substr($field->entity_type, 0, 34); - $field_hash = substr(hash('sha256', $field->uuid), 0, 10); + $entity_type = substr($storage_definition->getTargetEntityTypeId(), 0, 34); + $field_hash = substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10); $table_name = $entity_type . $separator . $field_hash; } return $table_name; @@ -1607,8 +1715,8 @@ static protected function _generateFieldTableName(FieldConfigInterface $field, $ * strongly discouraged. This function is not considered part of the public * API and modules relying on it might break even in minor releases. * - * @param \Drupal\field\FieldConfigInterface $field - * The field structure + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition + * The field storage definition. * @param string $index * The name of the index. * @@ -1616,8 +1724,8 @@ static protected function _generateFieldTableName(FieldConfigInterface $field, $ * A string containing a generated index name for a field data table that is * unique among all other fields. */ - static public function _fieldIndexName(FieldConfigInterface $field, $index) { - return $field->getName() . '_' . $index; + public static function _fieldIndexName(FieldStorageDefinitionInterface $storage_definition, $index) { + return $storage_definition->getName() . '_' . $index; } /** @@ -1630,8 +1738,8 @@ static public function _fieldIndexName(FieldConfigInterface $field, $index) { * support. Always call entity_load() before using the data found in the * table. * - * @param \Drupal\field\FieldConfigInterface $field - * The field object. + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition + * The field storage definition. * @param string $column * The name of the column. * @@ -1639,8 +1747,8 @@ static public function _fieldIndexName(FieldConfigInterface $field, $index) { * A string containing a generated column name for a field data table that is * unique among all other fields. */ - static public function _fieldColumnName(FieldConfigInterface $field, $column) { - return in_array($column, FieldConfig::getReservedColumns()) ? $column : $field->getName() . '_' . $column; + public static function _fieldColumnName(FieldStorageDefinitionInterface $storage_definition, $column) { + return in_array($column, FieldConfig::getReservedColumns()) ? $column : $storage_definition->getName() . '_' . $column; } } diff --git a/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php index 98dbd2f..12aeca3 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Entity; use Drupal\Core\Entity\Query\QueryException; -use Drupal\field\FieldInstanceConfigInterface; +use Drupal\Core\Field\FieldDefinitionInterface; /** * Defines a null entity storage. @@ -109,13 +109,14 @@ protected function doDeleteFieldItemsRevision(EntityInterface $entity) { /** * {@inheritdoc} */ - protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceConfigInterface $instance) { + protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definition, $batch_size) { + return array(); } /** * {@inheritdoc} */ - protected function purgeFieldItems(EntityInterface $entity, FieldInstanceConfigInterface $instance) { + protected function purgeFieldItems(ContentEntityInterface $entity, FieldDefinitionInterface $field_definition) { } /** @@ -130,4 +131,11 @@ protected function doSave($id, EntityInterface $entity) { protected function has($id, EntityInterface $entity) { } + /** + * {@inheritdoc} + */ + public function countFieldData($storage_definition, $as_bool = FALSE) { + return $as_bool ? FALSE : 0; + } + } diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php index 29c1de9..dad9674 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php @@ -8,8 +8,9 @@ namespace Drupal\Core\Entity; use Drupal\Component\Utility\String; +use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Cache\Cache; -use Drupal\field\FieldConfigInterface; use Drupal\field\FieldInstanceConfigInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -267,32 +268,32 @@ protected function deleteFieldItemsRevision(EntityInterface $entity) { /** * {@inheritdoc} */ - public function onFieldCreate(FieldConfigInterface $field) { } + public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) { } /** * {@inheritdoc} */ - public function onFieldUpdate(FieldConfigInterface $field) { } + public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) { } /** * {@inheritdoc} */ - public function onFieldDelete(FieldConfigInterface $field) { } + public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) { } /** * {@inheritdoc} */ - public function onInstanceCreate(FieldInstanceConfigInterface $instance) { } + public function onFieldDefinitionCreate(FieldDefinitionInterface $field_definition) { } /** * {@inheritdoc} */ - public function onInstanceUpdate(FieldInstanceConfigInterface $instance) { } + public function onFieldDefinitionUpdate(FieldDefinitionInterface $field_definition, FieldDefinitionInterface $original) { } /** * {@inheritdoc} */ - public function onInstanceDelete(FieldInstanceConfigInterface $instance) { } + public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition) { } /** * {@inheritdoc} @@ -312,45 +313,46 @@ public function onBundleDelete($bundle) { } /** * {@inheritdoc} */ - public function onFieldItemsPurge(EntityInterface $entity, FieldInstanceConfigInterface $instance) { - if ($values = $this->readFieldItemsToPurge($entity, $instance)) { - $items = \Drupal::typedDataManager()->create($instance, $values, $instance->getName(), $entity); + public function purgeFieldData(FieldDefinitionInterface $field_definition, $batch_size) { + $items_by_entity = $this->readFieldItemsToPurge($field_definition, $batch_size); + + foreach ($items_by_entity as $items) { $items->delete(); + $this->purgeFieldItems($items->getEntity(), $field_definition); } - $this->purgeFieldItems($entity, $instance); + return count($items_by_entity); } /** - * Reads values to be purged for a single field of a single entity. + * Reads values to be purged for a single field. * * This method is called during field data purge, on fields for which * onFieldDelete() or onFieldInstanceDelete() has previously run. * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity. - * @param \Drupal\field\FieldInstanceConfigInterface $instance - * The field instance. + * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition + * The field definition. + * @param $batch_size + * The maximum number of field data records to purge before returning. * - * @return array - * The field values, in their canonical array format (numerically indexed - * array of items, each item being a property/value array). + * @return \Drupal\Core\Field\FieldItemListInterface[] + * An array of field item lists, keyed by entity revision id. */ - abstract protected function readFieldItemsToPurge(EntityInterface $entity, FieldInstanceConfigInterface $instance); + abstract protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definition, $batch_size); /** - * Removes field data from storage during purge. + * Removes field items from storage per entity during purge. * - * @param EntityInterface $entity - * The entity whose values are being purged. - * @param FieldInstanceConfigInterface $instance + * @param ContentEntityInterface $entity + * The entity revision, whose values are being purged. + * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition * The field whose values are bing purged. */ - abstract protected function purgeFieldItems(EntityInterface $entity, FieldInstanceConfigInterface $instance); + abstract protected function purgeFieldItems(ContentEntityInterface $entity, FieldDefinitionInterface $field_definition); /** * {@inheritdoc} */ - public function onFieldPurge(FieldConfigInterface $field) { } + public function finalizePurge(FieldStorageDefinitionInterface $storage_definition) { } /** * Checks translation statuses and invoke the related hooks if needed. diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 5e2ad03..867654e 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -394,11 +394,13 @@ protected function buildBaseFieldDefinitions($entity_type_id) { } } - // Automatically set the field name for non-configurable fields. + // Automatically set the field name, target entity type and bundle + // for non-configurable fields. foreach ($base_field_definitions as $field_name => $base_field_definition) { if ($base_field_definition instanceof FieldDefinition) { $base_field_definition->setName($field_name); $base_field_definition->setTargetEntityTypeId($entity_type_id); + $base_field_definition->setBundle(NULL); } } @@ -491,11 +493,13 @@ protected function buildBundleFieldDefinitions($entity_type_id, $bundle, array $ } } - // Automatically set the field name for non-configurable fields. + // Automatically set the field name, target entity type and bundle + // for non-configurable fields. foreach ($bundle_field_definitions as $field_name => $field_definition) { if ($field_definition instanceof FieldDefinition) { $field_definition->setName($field_name); $field_definition->setTargetEntityTypeId($entity_type_id); + $field_definition->setBundle($bundle); } } diff --git a/core/lib/Drupal/Core/Entity/Exception/FieldStorageDefinitionUpdateForbiddenException.php b/core/lib/Drupal/Core/Entity/Exception/FieldStorageDefinitionUpdateForbiddenException.php new file mode 100644 index 0000000..54e2bff --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Exception/FieldStorageDefinitionUpdateForbiddenException.php @@ -0,0 +1,13 @@ + substr($specifier, 3), 'include_deleted' => TRUE))) { - $field = current($fields); - } - } - elseif (isset($field_storage_definitions[$specifier])) { + if (isset($field_storage_definitions[$specifier])) { $field = $field_storage_definitions[$specifier]; } else { diff --git a/core/lib/Drupal/Core/Field/FieldDefinition.php b/core/lib/Drupal/Core/Field/FieldDefinition.php index a0576f6..828ee70 100644 --- a/core/lib/Drupal/Core/Field/FieldDefinition.php +++ b/core/lib/Drupal/Core/Field/FieldDefinition.php @@ -509,8 +509,7 @@ public function getTargetEntityTypeId() { * @param string $entity_type_id * The name of the target entity type to set. * - * @return static - * The object itself for chaining. + * @return $this */ public function setTargetEntityTypeId($entity_type_id) { $this->definition['entity_type'] = $entity_type_id; @@ -520,6 +519,26 @@ public function setTargetEntityTypeId($entity_type_id) { /** * {@inheritdoc} */ + public function getBundle() { + return isset($this->definition['bundle']) ? $this->definition['bundle'] : NULL; + } + + /** + * Sets the bundle this field is defined for. + * + * @param string|null $bundle + * The bundle, or NULL if the field is not bundle-specific. + * + * @return $this + */ + public function setBundle($bundle) { + $this->definition['bundle'] = $bundle; + return $this; + } + + /** + * {@inheritdoc} + */ public function getSchema() { if (!isset($this->schema)) { // Get the schema from the field item class. @@ -576,7 +595,7 @@ public static function getReservedColumns() { * {@inheritdoc} */ public function hasCustomStorage() { - return !empty($this->definition['custom_storage']); + return !empty($this->definition['custom_storage']) || $this->isComputed(); } /** @@ -587,8 +606,14 @@ public function hasCustomStorage() { * TRUE otherwise. * * @return $this + * + * @throws \LogicException + * Thrown if custom storage is to be set to FALSE for a computed field. */ public function setCustomStorage($custom_storage) { + if (!$custom_storage && $this->isComputed()) { + throw new \LogicException("Entity storage cannot store a computed field."); + } $this->definition['custom_storage'] = $custom_storage; return $this; } @@ -600,4 +625,11 @@ public function getFieldStorageDefinition() { return $this; } + /** + * {@inheritdoc} + */ + public function getUniqueStorageIdentifier() { + return $this->getTargetEntityTypeId() . '-' . $this->getName(); + } + } diff --git a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php index 2933656..be7f0fe 100644 --- a/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php +++ b/core/lib/Drupal/Core/Field/FieldDefinitionInterface.php @@ -76,6 +76,15 @@ public function getName(); public function getType(); /** + * Gets the bundle the field is defined for. + * + * @return string|null + * The bundle the field is defined for, or NULL if it is a base field; i.e., + * it is not bundle-specific. + */ + public function getBundle(); + + /** * Returns whether the display for the field can be configured. * * @param string $display_context diff --git a/core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php b/core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php index 161b0b3..10f090e 100644 --- a/core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php +++ b/core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php @@ -297,4 +297,11 @@ public function getProvider(); */ public function hasCustomStorage(); + /** + * Returns a unique identifier for the field. + * + * @return string + */ + public function getUniqueStorageIdentifier(); + } diff --git a/core/lib/Drupal/Core/Mail/MailInterface.php b/core/lib/Drupal/Core/Mail/MailInterface.php index f78c312..64573ad 100644 --- a/core/lib/Drupal/Core/Mail/MailInterface.php +++ b/core/lib/Drupal/Core/Mail/MailInterface.php @@ -9,6 +9,10 @@ /** * Defines an interface for pluggable mail back-ends. + * + * @see \Drupal\Core\Annotation\Mail + * @see \Drupal\Core\Mail\MailManager + * @see plugin_api */ interface MailInterface { diff --git a/core/lib/Drupal/Core/Mail/MailManager.php b/core/lib/Drupal/Core/Mail/MailManager.php index 8fafc0c..0daaf3a 100644 --- a/core/lib/Drupal/Core/Mail/MailManager.php +++ b/core/lib/Drupal/Core/Mail/MailManager.php @@ -15,7 +15,11 @@ use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException; /** - * Mail plugin manager. + * Provides a Mail plugin manager. + * + * @see \Drupal\Core\Annotation\Mail + * @see \Drupal\Core\Mail\MailInterface + * @see plugin_api */ class MailManager extends DefaultPluginManager { diff --git a/core/lib/Drupal/Core/Plugin/Context/Context.php b/core/lib/Drupal/Core/Plugin/Context/Context.php index 2d79d6f..0366b71 100644 --- a/core/lib/Drupal/Core/Plugin/Context/Context.php +++ b/core/lib/Drupal/Core/Plugin/Context/Context.php @@ -8,84 +8,99 @@ namespace Drupal\Core\Plugin\Context; use Drupal\Component\Plugin\Context\Context as ComponentContext; -use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\Core\TypedData\DataDefinition; -use Drupal\Core\TypedData\ListInterface; +use Drupal\Component\Plugin\Exception\ContextException; +use Drupal\Component\Utility\String; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\TypedData\TypedDataInterface; +use Drupal\Core\TypedData\TypedDataTrait; /** * A Drupal specific context wrapper class. - * - * The validate method is specifically overridden in order to support typed - * data definitions instead of just class names in the contextual definitions - * of plugins that extend ContextualPluginBase. */ -class Context extends ComponentContext { +class Context extends ComponentContext implements ContextInterface { + + use TypedDataTrait; + + /** + * The data associated with the context. + * + * @var \Drupal\Core\TypedData\TypedDataInterface + */ + protected $contextData; + + /** + * The definition to which a context must conform. + * + * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface + */ + protected $contextDefinition; /** - * Overrides \Drupal\Component\Plugin\Context\Context::getContextValue(). + * {@inheritdoc} */ public function getContextValue() { - $typed_value = parent::getContextValue(); - // If the typed data is complex, pass it on as typed data. Else pass on its - // plain value, such that e.g. a string will be directly returned as PHP - // string. - $is_complex = $typed_value instanceof ComplexDataInterface; - if (!$is_complex && $typed_value instanceof ListInterface) { - $is_complex = $typed_value[0] instanceof ComplexDataInterface; + if (!isset($this->contextData)) { + $definition = $this->getContextDefinition(); + if ($definition->isRequired()) { + $type = $definition->getDataType(); + throw new ContextException(String::format("The @type context is required and not present.", array('@type' => $type))); + } + return NULL; } - if ($typed_value instanceof TypedDataInterface && !$is_complex) { - return $typed_value->getValue(); + // Special case entities. + // @todo: Remove once entities do not implemented TypedDataInterface. + if ($this->contextData instanceof ContentEntityInterface) { + return $this->contextData; } - return $typed_value; + return $this->contextData->getValue(); } /** - * Implements \Drupal\Component\Plugin\Context\ContextInterface::setContextValue(). + * {@inheritdoc} */ public function setContextValue($value) { - // Make sure the value set is a typed data object. - if (!empty($this->contextDefinition['type']) && !$value instanceof TypedDataInterface) { - $value = \Drupal::typedDataManager()->create(new DataDefinition($this->contextDefinition), $value); + if ($value instanceof TypedDataInterface) { + return $this->setContextData($value); + } + else { + return $this->setContextData($this->getTypedDataManager()->create($this->contextDefinition->getDataDefinition(), $value)); } - parent::setContextValue($value); - } - - /** - * Gets the context value as typed data object. - * - * parent::getContextValue() does not do all the processing required to - * return plain value of a TypedData object. This class overrides that method - * to return the appropriate values from TypedData objects, but the object - * itself can be useful as well, so this method is provided to allow for - * access to the TypedData object. Since parent::getContextValue() already - * does all the processing we need, we simply proxy to it here. - * - * @return \Drupal\Core\TypedData\TypedDataInterface - */ - public function getTypedContext() { - return parent::getContextValue(); } /** - * Implements \Drupal\Component\Plugin\Context\ContextInterface::getConstraints(). + * {@inheritdoc} */ public function getConstraints() { - if (!empty($this->contextDefinition['type'])) { - // If we do have typed data, leverage it for getting constraints. - return $this->getTypedContext()->getConstraints(); - } - return parent::getConstraints(); + return $this->contextDefinition->getConstraints(); } /** - * Overrides \Drupal\Component\Plugin\Context\Context::getConstraints(). + * {@inheritdoc} + */ + public function getContextData() { + return $this->contextData; + } + + /** + * {@inheritdoc} + */ + public function setContextData(TypedDataInterface $data) { + $this->contextData = $data; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getContextDefinition() { + return $this->contextDefinition; + } + + /** + * {@inheritdoc} */ public function validate() { - // If the context is typed data, defer to its validation. - if (!empty($this->contextDefinition['type'])) { - return $this->getTypedContext()->validate(); - } - return parent::validate(); + return $this->getContextData()->validate(); } + } diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php new file mode 100644 index 0000000..93a639f --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php @@ -0,0 +1,227 @@ +dataType = $data_type; + $this->label = $label; + $this->isRequired = $required; + $this->isMultiple = $multiple; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public function getDataType() { + return $this->dataType; + } + + /** + * {@inheritdoc} + */ + public function setDataType($data_type) { + $this->dataType = $data_type; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getLabel() { + return $this->label; + } + + /** + * {@inheritdoc} + */ + public function setLabel($label) { + $this->label = $label; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return $this->description; + } + + /** + * {@inheritdoc} + */ + public function setDescription($description) { + $this->description = $description; + return $this; + } + + /** + * {@inheritdoc} + */ + public function isMultiple() { + return $this->isMultiple; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($multiple = TRUE) { + $this->isMultiple = $multiple; + return $this; + } + + /** + * {@inheritdoc} + */ + public function isRequired() { + return $this->isRequired; + } + + /** + * {@inheritdoc} + */ + public function setRequired($required = TRUE) { + $this->isRequired = $required; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getConstraints() { + // @todo Apply defaults. + return $this->constraints; + } + + /** + * {@inheritdoc} + */ + public function getConstraint($constraint_name) { + $constraints = $this->getConstraints(); + return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL; + } + + /** + * {@inheritdoc} + */ + public function setConstraints(array $constraints) { + $this->constraints = $constraints; + return $this; + } + + /** + * {@inheritdoc} + */ + public function addConstraint($constraint_name, $options = NULL) { + $this->constraints[$constraint_name] = $options; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getDataDefinition() { + if ($this->isMultiple()) { + $definition = $this->getTypedDataManager()->createListDataDefinition($this->getDataType()); + } + else { + $definition = $this->getTypedDataManager()->createDataDefinition($this->getDataType()); + } + $definition->setLabel($this->getLabel()) + ->setDescription($this->getDescription()) + ->setRequired($this->isRequired()) + ->setConstraints($this->getConstraints()); + return $definition; + } + +} diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextDefinitionInterface.php b/core/lib/Drupal/Core/Plugin/Context/ContextDefinitionInterface.php new file mode 100644 index 0000000..3472c94 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinitionInterface.php @@ -0,0 +1,25 @@ +getContextDefinition()); diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextInterface.php b/core/lib/Drupal/Core/Plugin/Context/ContextInterface.php new file mode 100644 index 0000000..bb38617 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Context/ContextInterface.php @@ -0,0 +1,35 @@ + $value) { - $context_definition = $this->getContextDefinition($key); - $this->context[$key] = new Context($context_definition); - $this->context[$key]->setContextValue($value); + public function getContext($name) { + // Check for a valid context value. + if (!isset($this->context[$name])) { + $this->context[$name] = new Context($this->getContextDefinition($name)); } + return $this->context[$name]; } /** - * Override of \Drupal\Component\Plugin\ContextAwarePluginBase::setContextValue(). + * {@inheritdoc} */ - public function setContextValue($name, $value) { - $context_definition = $this->getContextDefinition($name); - // Use the Drupal specific context class. - $this->context[$name] = new Context($context_definition); - $this->context[$name]->setContextValue($value); - return $this; + public function setContext($name, ComponentContextInterface $context) { + // Check that the context passed is an instance of our extended interface. + if (!$context instanceof ContextInterface) { + throw new ContextException("Passed $name context must be an instance of \\Drupal\\Core\\Plugin\\Context\\ContextInterface"); + } + parent::setContext($name, $context); } } diff --git a/core/lib/Drupal/Core/TypedData/TypedDataTrait.php b/core/lib/Drupal/Core/TypedData/TypedDataTrait.php new file mode 100644 index 0000000..737a86a --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/TypedDataTrait.php @@ -0,0 +1,49 @@ +typedDataManager = $typed_data_manager; + return $this; + } + + /** + * Gets the typed data manager. + * + * @return \Drupal\Core\TypedData\TypedDataManager + * The typed data manager. + */ + public function getTypedDataManager() { + if (empty($this->typedDataManager)) { + $this->typedDataManager = \Drupal::typedDataManager(); + } + + return $this->typedDataManager; + } + +} diff --git a/core/modules/aggregator/src/Annotation/AggregatorFetcher.php b/core/modules/aggregator/src/Annotation/AggregatorFetcher.php index 0fbee2f..db66347 100644 --- a/core/modules/aggregator/src/Annotation/AggregatorFetcher.php +++ b/core/modules/aggregator/src/Annotation/AggregatorFetcher.php @@ -12,6 +12,15 @@ /** * Defines a Plugin annotation object for aggregator fetcher plugins. * + * Plugin Namespace: Plugin\aggregator\fetcher + * + * For a working example, see \Drupal\aggregator\Plugin\aggregator\fetcher\DefaultFetcher + * + * @see \Drupal\aggregator\Plugin\AggregatorPluginManager + * @see \Drupal\aggregator\Plugin\FetcherInterface + * @see \Drupal\aggregator\Plugin\AggregatorPluginSettingsBase + * @see plugin_api + * * @Annotation */ class AggregatorFetcher extends Plugin { diff --git a/core/modules/aggregator/src/Annotation/AggregatorParser.php b/core/modules/aggregator/src/Annotation/AggregatorParser.php index c36a5e8..45842b6 100644 --- a/core/modules/aggregator/src/Annotation/AggregatorParser.php +++ b/core/modules/aggregator/src/Annotation/AggregatorParser.php @@ -12,6 +12,15 @@ /** * Defines a Plugin annotation object for aggregator parser plugins. * + * Plugin Namespace: Plugin\aggregator\parser + * + * For a working example, see \Drupal\aggregator\Plugin\aggregator\parser\DefaultParser + * + * @see \Drupal\aggregator\Plugin\AggregatorPluginManager + * @see \Drupal\aggregator\Plugin\ParserInterface + * @see \Drupal\aggregator\Plugin\AggregatorPluginSettingsBase + * @see plugin_api + * * @Annotation */ class AggregatorParser extends Plugin { diff --git a/core/modules/aggregator/src/Annotation/AggregatorProcessor.php b/core/modules/aggregator/src/Annotation/AggregatorProcessor.php index 7ab4827..3aaba5b 100644 --- a/core/modules/aggregator/src/Annotation/AggregatorProcessor.php +++ b/core/modules/aggregator/src/Annotation/AggregatorProcessor.php @@ -12,6 +12,15 @@ /** * Defines a Plugin annotation object for aggregator processor plugins. * + * Plugin Namespace: Plugin\aggregator\processor + * + * For a working example, see \Drupal\aggregator\Plugin\aggregator\processor\DefaultProcessor + * + * @see \Drupal\aggregator\Plugin\AggregatorPluginManager + * @see \Drupal\aggregator\Plugin\ProcessorInterface + * @see \Drupal\aggregator\Plugin\AggregatorPluginSettingsBase + * @see plugin_api + * * @Annotation */ class AggregatorProcessor extends Plugin { diff --git a/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php b/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php index 84196cd..53c3acd 100644 --- a/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php +++ b/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php @@ -13,6 +13,15 @@ /** * Manages aggregator plugins. + * + * @see \Drupal\aggregator\Annotation\AggregatorParser + * @see \Drupal\aggregator\Annotation\AggregatorFetcher + * @see \Drupal\aggregator\Annotation\AggregatorProcessor + * @see \Drupal\aggregator\Plugin\AggregatorPluginSettingsBase + * @see \Drupal\aggregator\Plugin\FetcherInterface + * @see \Drupal\aggregator\Plugin\ProcessorInterface + * @see \Drupal\aggregator\Plugin\ParserInterface + * @see plugin_api */ class AggregatorPluginManager extends DefaultPluginManager { diff --git a/core/modules/aggregator/src/Plugin/AggregatorPluginSettingsBase.php b/core/modules/aggregator/src/Plugin/AggregatorPluginSettingsBase.php index 80493c0..ee94c7c 100644 --- a/core/modules/aggregator/src/Plugin/AggregatorPluginSettingsBase.php +++ b/core/modules/aggregator/src/Plugin/AggregatorPluginSettingsBase.php @@ -13,6 +13,15 @@ /** * Base class for aggregator plugins that implement settings forms. + * + * @see \Drupal\aggregator\Annotation\AggregatorParser + * @see \Drupal\aggregator\Annotation\AggregatorFetcher + * @see \Drupal\aggregator\Annotation\AggregatorProcessor + * @see \Drupal\aggregator\Plugin\AggregatorPluginManager + * @see \Drupal\aggregator\Plugin\FetcherInterface + * @see \Drupal\aggregator\Plugin\ProcessorInterface + * @see \Drupal\aggregator\Plugin\ParserInterface + * @see plugin_api */ abstract class AggregatorPluginSettingsBase extends PluginBase implements PluginFormInterface, ConfigurablePluginInterface { diff --git a/core/modules/aggregator/src/Plugin/FetcherInterface.php b/core/modules/aggregator/src/Plugin/FetcherInterface.php index 65567fa..be9734e 100644 --- a/core/modules/aggregator/src/Plugin/FetcherInterface.php +++ b/core/modules/aggregator/src/Plugin/FetcherInterface.php @@ -17,6 +17,11 @@ * active fetcher; second, it is converted to a common format by the active * parser; and finally, it is passed to all active processors, which manipulate * or store the data. + * + * @see \Drupal\aggregator\Annotation\AggregatorFetcher + * @see \Drupal\aggregator\Plugin\AggregatorPluginSettingsBase + * @see \Drupal\aggregator\Plugin\AggregatorPluginManager + * @see plugin_api */ interface FetcherInterface { diff --git a/core/modules/aggregator/src/Plugin/ParserInterface.php b/core/modules/aggregator/src/Plugin/ParserInterface.php index 8a4db68..bb065a1 100644 --- a/core/modules/aggregator/src/Plugin/ParserInterface.php +++ b/core/modules/aggregator/src/Plugin/ParserInterface.php @@ -18,6 +18,10 @@ * active parser; and finally, it is passed to all active processors which * manipulate or store the data. * + * @see \Drupal\aggregator\Annotation\AggregatorParser + * @see \Drupal\aggregator\Plugin\AggregatorPluginSettingsBase + * @see \Drupal\aggregator\Plugin\AggregatorPluginManager + * @see plugin_api */ interface ParserInterface { diff --git a/core/modules/aggregator/src/Plugin/ProcessorInterface.php b/core/modules/aggregator/src/Plugin/ProcessorInterface.php index 1b51e24..6315b4d 100644 --- a/core/modules/aggregator/src/Plugin/ProcessorInterface.php +++ b/core/modules/aggregator/src/Plugin/ProcessorInterface.php @@ -17,6 +17,11 @@ * active fetcher; second, it is converted to a common format by the active * parser; and finally, it is passed to all active processors that manipulate or * store the data. + * + * @see \Drupal\aggregator\Annotation\AggregatorProcessor + * @see \Drupal\aggregator\Plugin\AggregatorPluginSettingsBase + * @see \Drupal\aggregator\Plugin\AggregatorPluginManager + * @see plugin_api */ interface ProcessorInterface { diff --git a/core/modules/block/block.api.php b/core/modules/block/block.api.php index 9210f82..a0f79bf 100644 --- a/core/modules/block/block.api.php +++ b/core/modules/block/block.api.php @@ -6,6 +6,56 @@ */ /** + * @defgroup block_api Block API + * @{ + * Information about the classes and interfaces that make up the Block API. + * + * Blocks are a combination of a configuration entity and a plugin. The + * configuration entity stores placement information (theme, region, weight) and + * any other configuration that is specific to the block. The block plugin does + * the work of rendering the block's content for display. + * + * To define a block in a module you need to: + * - Define a Block plugin by creating a new class that implements the + * \Drupal\block\BlockPluginInterface, in namespace Plugin\Block under your + * module namespace. For more information about creating plugins, see the + * @link plugin_api Plugin API topic. @endlink + * - Usually you will want to extend the \Drupal\block\BlockBase class, which + * provides a common configuration form and utility methods for getting and + * setting configuration in the block configuration entity. + * - Block plugins use the annotations defined by + * \Drupal\block\Annotation\Block. See the + * @link annotation Annotations topic @endlink for more information about + * annotations. + * + * The Block API also makes use of Condition plugins, for conditional block + * placement. Condition plugins have interface + * \Drupal\Core\Condition\ConditionInterface, base class + * \Drupal\Core\Condition\ConditionPluginBase, and go in plugin namespace + * Plugin\Condition. Again, see the Plugin API and Annotations topics for + * details of how to create a plugin class and annotate it. + * + * There are also several block-related hooks, which allow you to affect + * the content and access permissions for blocks: + * - hook_block_view_alter() + * - hook_block_view_BASE_BLOCK_ID_alter() + * - hook_block_access() + * + * Further information and examples: + * - \Drupal\system\Plugin\Block\SystemPoweredByBlock provides a simple example + * of defining a block. + * - \Drupal\user\Plugin\Condition\UserRole is a straightforward example of a + * block placement condition plugin. + * - \Drupal\book\Plugin\Block\BookNavigationBlock is an example of a block with + * a custom configuration form. + * - For a more in-depth discussion of the Block API see + * https://drupal.org/developing/api/8/block_api + * - The Examples for Developers project also provides a Block example in + * https://drupal.org/project/examples. + * @} + */ + +/** * @addtogroup hooks * @{ */ @@ -35,6 +85,7 @@ * The block plugin instance. * * @see hook_block_view_BASE_BLOCK_ID_alter() + * @ingroup block_api */ function hook_block_view_alter(array &$build, \Drupal\block\BlockPluginInterface $block) { // Remove the contextual links on all blocks that provide them. @@ -62,6 +113,7 @@ function hook_block_view_alter(array &$build, \Drupal\block\BlockPluginInterface * The block plugin instance. * * @see hook_block_view_alter() + * @ingroup block_api */ function hook_block_view_BASE_BLOCK_ID_alter(array &$build, \Drupal\block\BlockPluginInterface $block) { // Change the title of the specific block. @@ -90,6 +142,7 @@ function hook_block_view_BASE_BLOCK_ID_alter(array &$build, \Drupal\block\BlockP * * @see \Drupal\Core\Entity\EntityAccessController::access() * @see \Drupal\block\BlockAccessController::checkAccess() + * @ingroup block_api */ function hook_block_access(\Drupal\block\Entity\Block $block, $operation, \Drupal\user\Entity\User $account, $langcode) { // Example code that would prevent displaying the 'Powered by Drupal' block in diff --git a/core/modules/block/src/BlockBase.php b/core/modules/block/src/BlockBase.php index 258d23d..813da27 100644 --- a/core/modules/block/src/BlockBase.php +++ b/core/modules/block/src/BlockBase.php @@ -13,12 +13,10 @@ use Drupal\Core\Condition\ConditionAccessResolverTrait; use Drupal\Core\Condition\ConditionPluginBag; use Drupal\Core\Plugin\ContextAwarePluginBase; -use Drupal\block\BlockInterface; use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Cache\Cache; -use Drupal\Core\Cache\CacheableInterface; use Drupal\Core\Session\AccountInterface; /** @@ -51,6 +49,13 @@ /** * {@inheritdoc} */ + public static function contextDefinitions() { + return []; + } + + /** + * {@inheritdoc} + */ public function label() { if (!empty($this->configuration['label'])) { return $this->configuration['label']; diff --git a/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php index 054d0de..4584172 100644 --- a/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php +++ b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php @@ -9,6 +9,7 @@ use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\StringTranslation\StringTranslationTrait; /** @@ -39,10 +40,7 @@ public function __construct(LanguageManagerInterface $language_manager) { * {@inheritdoc} */ protected function determineBlockContext() { - $context = new Context(array( - 'type' => 'language', - 'label' => $this->t('Current language'), - )); + $context = new Context(new ContextDefinition('language', $this->t('Current language'))); $context->setContextValue($this->languageManager->getCurrentLanguage()); $this->addContext('language', $context); } diff --git a/core/modules/block/src/EventSubscriber/CurrentUserContext.php b/core/modules/block/src/EventSubscriber/CurrentUserContext.php index 14565aa..2a5a19e 100644 --- a/core/modules/block/src/EventSubscriber/CurrentUserContext.php +++ b/core/modules/block/src/EventSubscriber/CurrentUserContext.php @@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; @@ -52,10 +53,7 @@ public function __construct(AccountInterface $account, EntityManagerInterface $e protected function determineBlockContext() { $current_user = $this->userStorage->load($this->account->id()); - $context = new Context(array( - 'type' => 'entity:user', - 'label' => $this->t('Current user'), - )); + $context = new Context(new ContextDefinition('entity:user', $this->t('Current user'))); $context->setContextValue($current_user); $this->addContext('current_user', $context); } diff --git a/core/modules/block/src/EventSubscriber/NodeRouteContext.php b/core/modules/block/src/EventSubscriber/NodeRouteContext.php index adadbe8..c1bd2bf 100644 --- a/core/modules/block/src/EventSubscriber/NodeRouteContext.php +++ b/core/modules/block/src/EventSubscriber/NodeRouteContext.php @@ -8,6 +8,7 @@ namespace Drupal\block\EventSubscriber; use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\node\Entity\Node; @@ -38,7 +39,7 @@ public function __construct(RouteMatchInterface $route_match) { */ protected function determineBlockContext() { if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) { - $context = new Context($route_contexts['node']); + $context = new Context(new ContextDefinition($route_contexts['node']['type'])); if ($node = $this->routeMatch->getParameter('node')) { $context->setContextValue($node); } @@ -46,7 +47,7 @@ protected function determineBlockContext() { } elseif ($this->routeMatch->getRouteName() == 'node.add') { $node_type = $this->routeMatch->getParameter('node_type'); - $context = new Context(array('type' => 'entity:node')); + $context = new Context(new ContextDefinition('entity:node')); $context->setContextValue(Node::create(array('type' => $node_type->id()))); $this->addContext('node', $context); } diff --git a/core/modules/book/src/BookBreadcrumbBuilder.php b/core/modules/book/src/BookBreadcrumbBuilder.php index 1f8ff83..e7d257a 100644 --- a/core/modules/book/src/BookBreadcrumbBuilder.php +++ b/core/modules/book/src/BookBreadcrumbBuilder.php @@ -10,6 +10,7 @@ use Drupal\Core\Access\AccessManager; use Drupal\Core\Breadcrumb\BreadcrumbBuilderBase; use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Drupal\node\NodeInterface; @@ -58,19 +59,18 @@ public function __construct(EntityManagerInterface $entity_manager, AccessManage /** * {@inheritdoc} */ - public function applies(array $attributes) { - return !empty($attributes['node']) - && ($attributes['node'] instanceof NodeInterface) - && !empty($attributes['node']->book); + public function applies(RouteMatchInterface $route_match) { + $node = $route_match->getParameter('node'); + return $node instanceof NodeInterface && !empty($node->book); } /** * {@inheritdoc} */ - public function build(array $attributes) { + public function build(RouteMatchInterface $route_match) { $book_nids = array(); $links = array($this->l($this->t('Home'), '')); - $book = $attributes['node']->book; + $book = $route_match->getParameter('node')->book; $depth = 1; // We skip the current node. while (!empty($book['p' . ($depth + 1)])) { diff --git a/core/modules/color/color.module b/core/modules/color/color.module index 335e0ad..15dfaf4 100644 --- a/core/modules/color/color.module +++ b/core/modules/color/color.module @@ -164,7 +164,6 @@ function color_get_palette($theme, $default = FALSE) { * * @see color_scheme_form_validate() * @see color_scheme_form_submit() - * @ingroup forms */ function color_scheme_form($complete_form, &$form_state, $theme) { $base = drupal_get_path('module', 'color'); diff --git a/core/modules/comment/src/CommentBreadcrumbBuilder.php b/core/modules/comment/src/CommentBreadcrumbBuilder.php index 9626963..43e1198 100644 --- a/core/modules/comment/src/CommentBreadcrumbBuilder.php +++ b/core/modules/comment/src/CommentBreadcrumbBuilder.php @@ -9,7 +9,7 @@ use Drupal\Core\Breadcrumb\BreadcrumbBuilderBase; use Drupal\Core\Entity\EntityManagerInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Drupal\Core\Routing\RouteMatchInterface; /** * Class to define the comment breadcrumb builder. @@ -36,23 +36,23 @@ public function __construct(EntityManagerInterface $entity_manager) { /** * {@inheritdoc} */ - public function applies(array $attributes) { - return isset($attributes[RouteObjectInterface::ROUTE_NAME]) && $attributes[RouteObjectInterface::ROUTE_NAME] == 'comment.reply' - && isset($attributes['entity_type']) - && isset($attributes['entity_id']) - && isset($attributes['field_name']); + public function applies(RouteMatchInterface $route_match) { + return $route_match->getRouteName() == 'comment.reply' + && $route_match->getParameter('entity_type') + && $route_match->getParameter('entity_id') + && $route_match->getParameter('field_name'); } /** * {@inheritdoc} */ - public function build(array $attributes) { + public function build(RouteMatchInterface $route_match) { $breadcrumb = array(); $breadcrumb[] = $this->l($this->t('Home'), ''); $entity = $this->entityManager - ->getStorage($attributes['entity_type']) - ->load($attributes['entity_id']); + ->getStorage($route_match->getParameter('entity_type')) + ->load($route_match->getParameter('entity_id')); $breadcrumb[] = \Drupal::linkGenerator()->generateFromUrl($entity->label(), $entity->urlInfo()); return $breadcrumb; } diff --git a/core/modules/comment/src/Tests/CommentFieldsTest.php b/core/modules/comment/src/Tests/CommentFieldsTest.php index 5dd3bb7..5bb4380 100644 --- a/core/modules/comment/src/Tests/CommentFieldsTest.php +++ b/core/modules/comment/src/Tests/CommentFieldsTest.php @@ -85,9 +85,6 @@ function testCommentInstallAfterContentModule() { // Purge field data now to allow comment module to be uninstalled once the // field has been deleted. field_purge_batch(10); - // Call again as field_purge_batch() won't remove both the instances and - // field in a single pass. - field_purge_batch(10); // Disable the comment module. $edit = array(); diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module index 6504b86..f95373c 100644 --- a/core/modules/config_translation/config_translation.module +++ b/core/modules/config_translation/config_translation.module @@ -18,16 +18,20 @@ function config_translation_help($route_name, Request $request) { case 'help.page.config_translation': $output = ''; $output .= '

' . t('About') . '

'; - $output .= '

' . t('The Configuration Translation module allows configurations to be translated into different languages. Views, your site name, contact module categories, vocabularies, menus, blocks, and so on are all stored within the unified configuration system and can be translated with this module. Content, such as nodes, taxonomy terms, custom blocks, and so on are translatable with the Content Translation module in Drupal core, while the built-in user interface (such as registration forms, content submission and administration interfaces) are translated with the Interface Translation module. Use these three modules effectively together to translate your whole site to different languages.') . '

'; + $output .= '

' . t('The Configuration Translation module allows you to translate configuration text; for example, the site name, vocabularies, menus, or date formats. Together with the modules Language, Content Translation, and Interface Translation, it allows you to build multilingual websites. For more information, see the online documentation for the Configuration Translation module.', array('!doc_url' => 'https://drupal.org/documentation/modules/config_translation', '!config' => \Drupal::url('help.page', array('name' => 'config')), '!language' => \Drupal::url('help.page', array('name' => 'language')), '!locale' => \Drupal::url('help.page', array('name' => 'locale')), '!content-translation' => (\Drupal::moduleHandler()->moduleExists('content_translation')) ? \Drupal::url('help.page', array('name' => 'content_translation')) : '#')) . '

'; $output .= '

' . t('Uses') . '

'; $output .= '
'; - $output .= '
' . t('Translating') . '
'; - $output .= '
' . t('To translate configuration items, select the translate tab when viewing the configuration, select the language for which you wish to provide translations and then enter the content.') . '
'; + $output .= '
' . t('Enabling translation') . '
'; + $output .= '
' . t('In order to translate configuration, the website must have at least two languages.', array('!url' => \Drupal::url('language.admin_overview'))) . '
'; + $output .= '
' . t('Translating configuration text') . '
'; + $output .= '
' . t('Users with the Translate user edited configuration permission can access the configuration translation overview, and manage translations for specific languages. The Configuration translation page shows a list of all configuration text that can be translated, either as individual items or as lists. After you click on Translate, you are provided with a list of all languages. You can add or edit a translation for a specific language. Users with specific configuration permissions can also edit the text for the site\'s default language. For some configuration text items (for example for the site information), the specific translation pages can also be accessed directly from their configuration pages.', array('!translation-page' => \Drupal::url('config_translation.mapper_list'))) . '
'; + $output .= '
' . t('Translating date formats') . '
'; + $output .= '
' . t('You can choose to translate date formats on the Configuration translation page. This allows you not only to translate the label text, but also to set a language-specific PHP date format.', array('!translation-page' => \Drupal::url('config_translation.mapper_list'))) . '
'; $output .= '
'; return $output; case 'config_translation.mapper_list': - $output = '

' . t('This page lists all configuration items on your site which have translatable text, like your site name, role names, etc.') . '

'; + $output = '

' . t('This page lists all configuration items on your site that have translatable text, like your site name, role names, etc.') . '

'; return $output; } } diff --git a/core/modules/editor/editor.admin.inc b/core/modules/editor/editor.admin.inc index 8ed24b8..69acabe 100644 --- a/core/modules/editor/editor.admin.inc +++ b/core/modules/editor/editor.admin.inc @@ -23,7 +23,6 @@ * The image upload settings form. * * @see \Drupal\editor\Form\EditorImageDialog - * @ingroup forms */ function editor_image_upload_settings_form(Editor $editor) { // Defaults. diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php index e8fb142..f364c51 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -6,7 +6,6 @@ */ use Drupal\Component\Utility\NestedArray; -use Drupal\field\FieldConfigUpdateForbiddenException; /** * @defgroup field_types Field Types API @@ -248,7 +247,7 @@ function hook_field_info_max_weight($entity_type, $bundle, $context, $context_mo * that cannot be updated. * * To forbid the update from occurring, throw a - * Drupal\field\FieldConfigUpdateForbiddenException. + * \Drupal\Core\Entity\Exception\StorageDefinitionUpdateForbiddenException. * * @param \Drupal\field\FieldConfigInterface $field * The field as it will be post-update. @@ -270,7 +269,7 @@ function hook_field_config_update_forbid(\Drupal\field\FieldConfigInterface $fie ->range(0, 1) ->execute(); if ($found) { - throw new FieldConfigUpdateForbiddenException("Cannot update a list field not to include keys with existing data"); + throw new \Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException("Cannot update a list field not to include keys with existing data"); } } } diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 4726101..4c087cf 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -198,20 +198,27 @@ function field_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundl } } - /** * Implements hook_entity_bundle_rename(). */ function field_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) { - $instances = entity_load_multiple_by_properties('field_instance_config', array('entity_type' => $entity_type, 'bundle' => $bundle_old)); + $instances = entity_load_multiple_by_properties('field_instance_config', array('entity_type' => $entity_type, 'bundle' => $bundle_old, 'include_deleted' => TRUE)); foreach ($instances as $instance) { - if ($instance->entity_type == $entity_type && $instance->bundle == $bundle_old) { - $id_new = $instance->entity_type . '.' . $bundle_new . '.' . $instance->field_name; - $instance->set('id', $id_new); - $instance->bundle = $bundle_new; + $id_new = $instance->entity_type . '.' . $bundle_new . '.' . $instance->field_name; + $instance->set('id', $id_new); + $instance->bundle = $bundle_new; + // Save non-deleted instances. + if (!$instance->isDeleted()) { $instance->allowBundleRename(); $instance->save(); } + // Update deleted instances directly in the state storage. + else { + $state = \Drupal::state(); + $deleted_instances = $state->get('field.instance.deleted') ?: array(); + $deleted_instances[$instance->uuid] = $instance->toArray(); + $state->set('field.instance.deleted', $deleted_instances); + } } } diff --git a/core/modules/field/field.purge.inc b/core/modules/field/field.purge.inc index 4471970..4a289cf 100644 --- a/core/modules/field/field.purge.inc +++ b/core/modules/field/field.purge.inc @@ -80,7 +80,6 @@ function field_purge_batch($batch_size, $field_uuid = NULL) { else { $instances = entity_load_multiple_by_properties('field_instance_config', array('deleted' => TRUE, 'include_deleted' => TRUE)); } - $factory = \Drupal::service('entity.query'); $info = \Drupal::entityManager()->getDefinitions(); foreach ($instances as $instance) { $entity_type = $instance->entity_type; @@ -92,36 +91,16 @@ function field_purge_batch($batch_size, $field_uuid = NULL) { continue; } - $ids = (object) array( - 'entity_type' => $entity_type, - 'bundle' => $instance->bundle, - ); - // Retrieve some entities. - $query = $factory->get($entity_type) - ->condition('id:' . $instance->getFieldStorageDefinition()->uuid() . '.deleted', 1) - ->range(0, $batch_size); - // If there's no bundle key, all results will have the same bundle. - if ($bundle_key = $info[$entity_type]->getKey('bundle')) { - $query->condition($bundle_key, $ids->bundle); - } - $results = $query->execute(); - if ($results) { - foreach ($results as $revision_id => $entity_id) { - $ids->revision_id = $revision_id; - $ids->entity_id = $entity_id; - $entity = _field_create_entity_from_ids($ids); - \Drupal::entityManager()->getStorage($entity_type)->onFieldItemsPurge($entity, $instance); - $batch_size--; - } - // Only delete up to the maximum number of records. - if ($batch_size == 0) { - break; - } - } - else { + $count_purged = \Drupal::entityManager()->getStorage($entity_type)->purgeFieldData($instance, $batch_size); + if ($count_purged < $batch_size || $count_purged == 0) { // No field data remains for the instance, so we can remove it. field_purge_instance($instance); } + $batch_size -= $count_purged; + // Only delete up to the maximum number of records. + if ($batch_size == 0) { + break; + } } // Retrieve all deleted fields. Any that have no instances can be purged. @@ -187,7 +166,7 @@ function field_purge_field($field) { $state->set('field.field.deleted', $deleted_fields); // Notify the storage layer. - \Drupal::entityManager()->getStorage($field->entity_type)->onFieldPurge($field); + \Drupal::entityManager()->getStorage($field->entity_type)->finalizePurge($field); // Invoke external hooks after the cache is cleared for API consistency. \Drupal::moduleHandler()->invokeAll('field_purge_field', array($field)); diff --git a/core/modules/field/src/ConfigImporterFieldPurger.php b/core/modules/field/src/ConfigImporterFieldPurger.php index db3d8b9..a33df39 100644 --- a/core/modules/field/src/ConfigImporterFieldPurger.php +++ b/core/modules/field/src/ConfigImporterFieldPurger.php @@ -75,7 +75,8 @@ protected static function initializeSandbox(array &$context, ConfigImporter $con $context['sandbox']['field']['steps_to_delete'] = 0; $fields = static::getFieldsToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete')); foreach ($fields as $field) { - $row_count = $field->entityCount(); + $row_count = \Drupal::entityManager()->getStorage($field->getTargetEntityTypeId()) + ->countFieldData($field); if ($row_count > 0) { // The number of steps to delete each field is determined by the // purge_batch_size setting. For example if the field has 9 rows and the @@ -84,8 +85,8 @@ protected static function initializeSandbox(array &$context, ConfigImporter $con $context['sandbox']['field']['steps_to_delete'] += $how_many_steps; } } - // Each field needs one last field_purge_batch() call to remove the last - // instance and the field itself. + // Each field possibly needs one last field_purge_batch() call to remove the + // last instance and the field itself. $context['sandbox']['field']['steps_to_delete'] += count($fields); $context['sandbox']['field']['current_progress'] = 0; diff --git a/core/modules/field/src/Entity/FieldConfig.php b/core/modules/field/src/Entity/FieldConfig.php index 10282ef..b7c9c5f 100644 --- a/core/modules/field/src/Entity/FieldConfig.php +++ b/core/modules/field/src/Entity/FieldConfig.php @@ -293,7 +293,7 @@ protected function preSaveNew(EntityStorageInterface $storage) { $this->settings += $field_type_manager->getDefaultSettings($this->type); // Notify the entity storage. - $entity_manager->getStorage($this->entity_type)->onFieldCreate($this); + $entity_manager->getStorage($this->entity_type)->onFieldStorageDefinitionCreate($this); } /** @@ -339,7 +339,7 @@ protected function preSaveUpdated(EntityStorageInterface $storage) { // Notify the storage. The controller can reject the definition // update as invalid by raising an exception, which stops execution before // the definition is written to config. - $entity_manager->getStorage($this->entity_type)->onFieldUpdate($this); + $entity_manager->getStorage($this->entity_type)->onFieldStorageDefinitionUpdate($this, $this->original); } /** @@ -408,7 +408,7 @@ public static function postDelete(EntityStorageInterface $storage, array $fields // Notify the storage. foreach ($fields as $field) { if (!$field->deleted) { - \Drupal::entityManager()->getStorage($field->entity_type)->onFieldDelete($field); + \Drupal::entityManager()->getStorage($field->entity_type)->onFieldStorageDefinitionDelete($field); $field->deleted = TRUE; } } @@ -631,59 +631,7 @@ public static function getReservedColumns() { * TRUE if the field has data for any entity; FALSE otherwise. */ public function hasData() { - return $this->entityCount(TRUE); - } - - /** - * Determines the number of entities that have field data. - * - * @param bool $as_bool - * (Optional) Optimises query for hasData(). Defaults to FALSE. - * - * @return bool|int - * The number of entities that have field data. If $as_bool parameter is - * TRUE then the value will either be TRUE or FALSE. - */ - public function entityCount($as_bool = FALSE) { - $count = 0; - $factory = \Drupal::service('entity.query'); - $entity_type = \Drupal::entityManager()->getDefinition($this->entity_type); - // Entity Query throws an exception if there is no base table. - if ($entity_type->getBaseTable()) { - if ($this->deleted) { - $query = $factory->get($this->entity_type) - ->condition('id:' . $this->uuid() . '.deleted', 1); - } - elseif ($this->getBundles()) { - $storage_details = $this->getSchema(); - $columns = array_keys($storage_details['columns']); - $query = $factory->get($this->entity_type); - $group = $query->orConditionGroup(); - foreach ($columns as $column) { - $group->exists($this->name . '.' . $column); - } - $query = $query->condition($group); - } - - if (isset($query)) { - $query - ->count() - ->accessCheck(FALSE); - // If we are performing the query just to check if the field has data - // limit the number of rows returned by the subquery. - if ($as_bool) { - $query->range(0, 1); - } - $count = $query->execute(); - } - } - - if ($as_bool) { - return (bool) $count; - } - else { - return (int) $count; - } + return \Drupal::entityManager()->getStorage($this->entity_type)->countFieldData($this, TRUE); } /** @@ -763,6 +711,13 @@ public function getMainPropertyName() { } /** + * {@inheritdoc} + */ + public function getUniqueStorageIdentifier() { + return $this->uuid(); + } + + /** * Helper to retrieve the field item class. */ protected function getFieldItemClass() { diff --git a/core/modules/field/src/Entity/FieldInstanceConfig.php b/core/modules/field/src/Entity/FieldInstanceConfig.php index ed1400b..84d3f1c 100644 --- a/core/modules/field/src/Entity/FieldInstanceConfig.php +++ b/core/modules/field/src/Entity/FieldInstanceConfig.php @@ -336,7 +336,7 @@ public function preSave(EntityStorageInterface $storage) { // Set the default instance settings. $this->settings += $field_type_manager->getDefaultInstanceSettings($field->type); // Notify the entity storage. - $entity_manager->getStorage($this->entity_type)->onInstanceCreate($this); + $entity_manager->getStorage($this->entity_type)->onFieldDefinitionCreate($this); } else { // Some updates are always disallowed. @@ -352,7 +352,7 @@ public function preSave(EntityStorageInterface $storage) { // Set the default instance settings. $this->settings += $field_type_manager->getDefaultInstanceSettings($field->type); // Notify the entity storage. - $entity_manager->getStorage($this->entity_type)->onInstanceUpdate($this); + $entity_manager->getStorage($this->entity_type)->onFieldDefinitionUpdate($this, $this->original); } if (!$this->isSyncing()) { // Ensure the correct dependencies are present. @@ -423,7 +423,7 @@ public static function postDelete(EntityStorageInterface $storage, array $instan // Notify the entity storage. foreach ($instances as $instance) { if (!$instance->deleted) { - \Drupal::entityManager()->getStorage($instance->entity_type)->onInstanceDelete($instance); + \Drupal::entityManager()->getStorage($instance->entity_type)->onFieldDefinitionDelete($instance); } } @@ -606,6 +606,13 @@ public function getDisplayOptions($display_context) { /** * {@inheritdoc} */ + public function getBundle() { + return $this->bundle; + } + + /** + * {@inheritdoc} + */ public function allowBundleRename() { $this->bundle_rename_allowed = TRUE; } @@ -745,4 +752,11 @@ public static function loadByName($entity_type_id, $bundle, $field_name) { return \Drupal::entityManager()->getStorage('field_instance_config')->load($entity_type_id . '.' . $bundle . '.' . $field_name); } + /** + * {@inheritdoc} + */ + public function getUniqueStorageIdentifier() { + return $this->getField()->getUniqueStorageIdentifier(); + } + } diff --git a/core/modules/field/src/FieldConfigUpdateForbiddenException.php b/core/modules/field/src/FieldConfigUpdateForbiddenException.php deleted file mode 100644 index 412e146..0000000 --- a/core/modules/field/src/FieldConfigUpdateForbiddenException.php +++ /dev/null @@ -1,13 +0,0 @@ -uuid() : $instance->id(); + $matching_instances[$key] = $instance; } return $matching_instances; diff --git a/core/modules/field/src/Tests/BulkDeleteTest.php b/core/modules/field/src/Tests/BulkDeleteTest.php index 41636e6..0535939 100644 --- a/core/modules/field/src/Tests/BulkDeleteTest.php +++ b/core/modules/field/src/Tests/BulkDeleteTest.php @@ -182,7 +182,7 @@ function testDeleteFieldInstance() { // The instance still exists, deleted. $instances = entity_load_multiple_by_properties('field_instance_config', array('field_id' => $field->uuid, 'deleted' => TRUE, 'include_deleted' => TRUE)); $this->assertEqual(count($instances), 1, 'There is one deleted instance'); - $instance = $instances[0]; + $instance = $instances[$instance->uuid]; $this->assertEqual($instance->bundle, $bundle, 'The deleted instance is for the correct bundle'); // Check that the actual stored content did not change during delete. @@ -306,9 +306,16 @@ function testPurgeField() { } $this->checkHooksInvocations($hooks, $actual_hooks); + // The instance still exists, deleted. + $instances = entity_load_multiple_by_properties('field_instance_config', array('uuid' => $instance->uuid, 'include_deleted' => TRUE)); + $this->assertTrue(isset($instances[$instance->uuid]) && $instances[$instance->uuid]->deleted, 'The instance exists and is deleted'); + // Purge again to purge the instance. field_purge_batch(0); + // The instance is gone. + $instances = entity_load_multiple_by_properties('field_instance_config', array('uuid' => $instance->uuid, 'include_deleted' => TRUE)); + $this->assertEqual(count($instances), 0, 'The instance is purged.'); // The field still exists, not deleted. $fields = entity_load_multiple_by_properties('field_config', array('uuid' => $field->uuid, 'include_deleted' => TRUE)); $this->assertTrue(isset($fields[$field->uuid]) && !$fields[$field->uuid]->deleted, 'The field exists and is not deleted'); @@ -334,14 +341,18 @@ function testPurgeField() { } $this->checkHooksInvocations($hooks, $actual_hooks); - // The field still exists, deleted. + // The field and instance still exist, deleted. + $instances = entity_load_multiple_by_properties('field_instance_config', array('uuid' => $instance->uuid, 'include_deleted' => TRUE)); + $this->assertTrue(isset($instances[$instance->uuid]) && $instances[$instance->uuid]->deleted, 'The instance exists and is deleted'); $fields = entity_load_multiple_by_properties('field_config', array('uuid' => $field->uuid, 'include_deleted' => TRUE)); $this->assertTrue(isset($fields[$field->uuid]) && $fields[$field->uuid]->deleted, 'The field exists and is deleted'); // Purge again to purge the instance and the field. field_purge_batch(0); - // The field is gone. + // The field and instance are gone. + $instances = entity_load_multiple_by_properties('field_instance_config', array('uuid' => $instance->uuid, 'include_deleted' => TRUE)); + $this->assertEqual(count($instances), 0, 'The instance is purged.'); $fields = entity_load_multiple_by_properties('field_config', array('uuid' => $field->uuid, 'include_deleted' => TRUE)); $this->assertEqual(count($fields), 0, 'The field is purged.'); } diff --git a/core/modules/field/src/Tests/CrudTest.php b/core/modules/field/src/Tests/CrudTest.php index 782897c..6a16101 100644 --- a/core/modules/field/src/Tests/CrudTest.php +++ b/core/modules/field/src/Tests/CrudTest.php @@ -8,6 +8,7 @@ namespace Drupal\field\Tests; use Drupal\Core\Entity\EntityStorageException; +use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException; use Drupal\field\Entity\FieldConfig; use Drupal\field\FieldException; @@ -440,7 +441,7 @@ function testUpdateFieldForbid() { $field->save(); $this->pass(t("A changeable setting can be updated.")); } - catch (FieldException $e) { + catch (FieldStorageDefinitionUpdateForbiddenException $e) { $this->fail(t("An unchangeable setting cannot be updated.")); } $field->settings['unchangeable']++; @@ -448,7 +449,7 @@ function testUpdateFieldForbid() { $field->save(); $this->fail(t("An unchangeable setting can be updated.")); } - catch (FieldException $e) { + catch (FieldStorageDefinitionUpdateForbiddenException $e) { $this->pass(t("An unchangeable setting cannot be updated.")); } } diff --git a/core/modules/field/src/Tests/FieldEntityCountTest.php b/core/modules/field/src/Tests/FieldDataCountTest.php similarity index 66% rename from core/modules/field/src/Tests/FieldEntityCountTest.php rename to core/modules/field/src/Tests/FieldDataCountTest.php index b55619f..3275b47 100644 --- a/core/modules/field/src/Tests/FieldEntityCountTest.php +++ b/core/modules/field/src/Tests/FieldDataCountTest.php @@ -2,7 +2,7 @@ /** * @file - * Contains \Drupal\field\Tests\FieldEntityCountTest. + * Contains \Drupal\field\Tests\FieldDataCountTest. */ namespace Drupal\field\Tests; @@ -10,22 +10,38 @@ use Drupal\Core\Entity\ContentEntityDatabaseStorage; /** - * Tests entityCount() and hasData() methods on FieldConfig entity. + * Tests counting field data records. * - * @see \Drupal\field\Entity\FieldConfig::entityCount() + * @see \Drupal\Core\Entity\FieldableEntityStorageInterface::countFieldData() * @see \Drupal\field\Entity\FieldConfig::hasData() */ -class FieldEntityCountTest extends FieldUnitTestBase { +class FieldDataCountTest extends FieldUnitTestBase { + /** + * @var \Drupal\Core\Entity\FieldableEntityStorageInterface + */ + protected $storage; + + /** + * {@inheritdoc} + */ public static function getInfo() { return array( - 'name' => 'Field config entityCount() and hasData() tests.', - 'description' => 'Tests entityCount() and hasData() methods on FieldConfig entity.', + 'name' => 'Field config hasData() tests.', + 'description' => 'Tests counting field data records and the hasData() method on FieldConfig entity.', 'group' => 'Field API', ); } /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + $this->storage = \Drupal::entityManager()->getStorage('entity_test'); + } + + /** * Tests entityCount() and hadData() methods. */ public function testEntityCountAndHasData() { @@ -45,7 +61,7 @@ public function testEntityCountAndHasData() { ))->save(); $this->assertIdentical($field->hasdata(), FALSE, 'There are no entities with field data.'); - $this->assertIdentical($field->entityCount(), 0, 'There are 0 entities with field data.'); + $this->assertIdentical($this->storage->countFieldData($field), 0, 'There are 0 entities with field data.'); // Create 1 entity without the field. $entity = entity_create('entity_test'); @@ -53,7 +69,7 @@ public function testEntityCountAndHasData() { $entity->save(); $this->assertIdentical($field->hasdata(), FALSE, 'There are no entities with field data.'); - $this->assertIdentical($field->entityCount(), 0, 'There are 0 entities with field data.'); + $this->assertIdentical($this->storage->countFieldData($field), 0, 'There are 0 entities with field data.'); // Create 12 entities to ensure that the purging works as expected. for ($i=0; $i < 12; $i++) { @@ -79,16 +95,16 @@ public function testEntityCountAndHasData() { } $this->assertIdentical($field->hasdata(), TRUE, 'There are entities with field data.'); - $this->assertEqual($field->entityCount(), 12, 'There are 12 entities with field data.'); + $this->assertEqual($this->storage->countFieldData($field), 12, 'There are 12 entities with field data.'); // Ensure the methods work on deleted fields. $field->delete(); $this->assertIdentical($field->hasdata(), TRUE, 'There are entities with deleted field data.'); - $this->assertEqual($field->entityCount(), 12, 'There are 12 entities with deleted field data.'); + $this->assertEqual($this->storage->countFieldData($field), 12, 'There are 12 entities with deleted field data.'); field_purge_batch(6); $this->assertIdentical($field->hasdata(), TRUE, 'There are entities with deleted field data.'); - $this->assertEqual($field->entityCount(), 6, 'There are 6 entities with deleted field data.'); + $this->assertEqual($this->storage->countFieldData($field), 6, 'There are 6 entities with deleted field data.'); } } diff --git a/core/modules/field/tests/modules/field_test/field_test.field.inc b/core/modules/field/tests/modules/field_test/field_test.field.inc index e43a7e7..93e090b 100644 --- a/core/modules/field/tests/modules/field_test/field_test.field.inc +++ b/core/modules/field/tests/modules/field_test/field_test.field.inc @@ -10,7 +10,7 @@ use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Session\AccountInterface; use Drupal\field\FieldConfigInterface; -use Drupal\field\FieldConfigUpdateForbiddenException; +use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException; /** * Implements hook_field_widget_info_alter(). @@ -24,7 +24,7 @@ function field_test_field_widget_info_alter(&$info) { */ function field_test_field_config_update_forbid(FieldConfigInterface $field, FieldConfigInterface $prior_field) { if ($field->getType() == 'test_field' && $field->getSetting('unchangeable') != $prior_field->getSetting('unchangeable')) { - throw new FieldConfigUpdateForbiddenException("field_test 'unchangeable' setting cannot be changed'"); + throw new FieldStorageDefinitionUpdateForbiddenException("field_test 'unchangeable' setting cannot be changed'"); } } diff --git a/core/modules/file/tests/file_module_test/file_module_test.module b/core/modules/file/tests/file_module_test/file_module_test.module index e9d6010..7c9e3a2 100644 --- a/core/modules/file/tests/file_module_test/file_module_test.module +++ b/core/modules/file/tests/file_module_test/file_module_test.module @@ -14,7 +14,6 @@ * * @see file_module_test_menu() * @see file_module_test_form_submit() - * @ingroup forms * * @deprecated Use \Drupal\file_module_test\Form\FileModuleTestForm::managedFileTest() */ diff --git a/core/modules/filter/config/install/filter.format.plain_text.yml b/core/modules/filter/config/install/filter.format.plain_text.yml index 5de8988..1f785fc 100644 --- a/core/modules/filter/config/install/filter.format.plain_text.yml +++ b/core/modules/filter/config/install/filter.format.plain_text.yml @@ -30,6 +30,6 @@ filters: id: filter_autop provider: filter status: true - weight: 0 + weight: 1 settings: { } langcode: en diff --git a/core/modules/filter/config/schema/filter.schema.yml b/core/modules/filter/config/schema/filter.schema.yml index 6a28b4e..82eda0d 100644 --- a/core/modules/filter/config/schema/filter.schema.yml +++ b/core/modules/filter/config/schema/filter.schema.yml @@ -38,9 +38,6 @@ filter.format.*: langcode: type: string label: 'Default language' - dependencies: - type: config_dependencies - label: 'Dependencies' filter_settings.*: type: sequence diff --git a/core/modules/filter/src/Annotation/Filter.php b/core/modules/filter/src/Annotation/Filter.php index 808357c..d866ba2 100644 --- a/core/modules/filter/src/Annotation/Filter.php +++ b/core/modules/filter/src/Annotation/Filter.php @@ -12,6 +12,15 @@ /** * Defines an filter annotation object. * + * Plugin Namespace: Plugin\Filter + * + * For a working example, see \Drupal\filter\Plugin\Filter\FilterHtml + * + * @see \Drupal\filter\FilterPluginManager + * @see \Drupal\filter\Plugin\FilterInterface + * @see \Drupal\filter\Plugin\FilterBase + * @see plugin_api + * * @Annotation */ class Filter extends Plugin { diff --git a/core/modules/filter/src/FilterPluginManager.php b/core/modules/filter/src/FilterPluginManager.php index de075af..77a3c71 100644 --- a/core/modules/filter/src/FilterPluginManager.php +++ b/core/modules/filter/src/FilterPluginManager.php @@ -15,6 +15,10 @@ * Manages text processing filters. * * @see hook_filter_info_alter() + * @see \Drupal\filter\Annotation\Filter + * @see \Drupal\filter\Plugin\FilterInterface + * @see \Drupal\filter\Plugin\FilterBase + * @see plugin_api */ class FilterPluginManager extends DefaultPluginManager { diff --git a/core/modules/filter/src/Plugin/FilterBase.php b/core/modules/filter/src/Plugin/FilterBase.php index 44421e1..cfed8ac 100644 --- a/core/modules/filter/src/Plugin/FilterBase.php +++ b/core/modules/filter/src/Plugin/FilterBase.php @@ -11,6 +11,11 @@ /** * Provides a base class for Filter plugins. + * + * @see \Drupal\filter\Annotation\Filter + * @see \Drupal\filter\FilterPluginManager + * @see \Drupal\filter\Plugin\FilterInterface + * @see plugin_api */ abstract class FilterBase extends PluginBase implements FilterInterface { diff --git a/core/modules/filter/src/Plugin/FilterInterface.php b/core/modules/filter/src/Plugin/FilterInterface.php index fea8559..dbbc490 100644 --- a/core/modules/filter/src/Plugin/FilterInterface.php +++ b/core/modules/filter/src/Plugin/FilterInterface.php @@ -74,7 +74,10 @@ * Most implementations want to extend the generic basic implementation for * filter plugins. * - * @see \Drupal\filter\Plugin\Filter\FilterBase + * @see \Drupal\filter\Annotation\Filter + * @see \Drupal\filter\FilterPluginManager + * @see \Drupal\filter\Plugin\FilterBase + * @see plugin_api */ interface FilterInterface extends ConfigurablePluginInterface, PluginInspectionInterface { diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 1f5ed4f..2b3d14f 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -108,10 +108,7 @@ function forum_uninstall() { } // Purge field data now to allow taxonomy and options module to be uninstalled - // if this is the only field remaining. We need to run it twice because - // field_purge_batch() will not remove the instance and the field in the same - // pass. - field_purge_batch(10); + // if this is the only field remaining. field_purge_batch(10); // Allow to delete a forum's node type. $locked = \Drupal::state()->get('node.type.locked'); diff --git a/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php b/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php index 5f7ae62..01efdf3 100644 --- a/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php +++ b/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php @@ -10,6 +10,7 @@ use Drupal\Core\Breadcrumb\BreadcrumbBuilderBase; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\forum\ForumManagerInterface; /** @@ -60,7 +61,7 @@ public function __construct(EntityManagerInterface $entity_manager, ConfigFactor /** * {@inheritdoc} */ - public function build(array $attributes) { + public function build(RouteMatchInterface $route_match) { $breadcrumb[] = $this->l($this->t('Home'), ''); $vocabulary = $this->entityManager diff --git a/core/modules/forum/src/Breadcrumb/ForumListingBreadcrumbBuilder.php b/core/modules/forum/src/Breadcrumb/ForumListingBreadcrumbBuilder.php index afc42f3..8f14417 100644 --- a/core/modules/forum/src/Breadcrumb/ForumListingBreadcrumbBuilder.php +++ b/core/modules/forum/src/Breadcrumb/ForumListingBreadcrumbBuilder.php @@ -7,7 +7,7 @@ namespace Drupal\forum\Breadcrumb; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Drupal\Core\Routing\RouteMatchInterface; /** * Provides a breadcrumb builder base class for forum listing pages. @@ -17,20 +17,18 @@ class ForumListingBreadcrumbBuilder extends ForumBreadcrumbBuilderBase { /** * {@inheritdoc} */ - public function applies(array $attributes) { - return !empty($attributes[RouteObjectInterface::ROUTE_NAME]) - && $attributes[RouteObjectInterface::ROUTE_NAME] == 'forum.page' - && isset($attributes['taxonomy_term']); + public function applies(RouteMatchInterface $route_match) { + return $route_match->getRouteName() == 'forum.page' && $route_match->getParameter('taxonomy_term'); } /** * {@inheritdoc} */ - public function build(array $attributes) { - $breadcrumb = parent::build($attributes); + public function build(RouteMatchInterface $route_match) { + $breadcrumb = parent::build($route_match); // Add all parent forums to breadcrumbs. - $term_id = $attributes['taxonomy_term']->id(); + $term_id = $route_match->getParameter('taxonomy_term')->id(); $parents = $this->forumManager->getParents($term_id); if ($parents) { foreach (array_reverse($parents) as $parent) { diff --git a/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php b/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php index 8d9a007..b4134d8 100644 --- a/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php +++ b/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php @@ -7,7 +7,7 @@ namespace Drupal\forum\Breadcrumb; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Drupal\Core\Routing\RouteMatchInterface; /** * Breadcrumb builder for forum nodes. @@ -17,20 +17,19 @@ class ForumNodeBreadcrumbBuilder extends ForumBreadcrumbBuilderBase { /** * {@inheritdoc} */ - public function applies(array $attributes) { - return !empty($attributes[RouteObjectInterface::ROUTE_NAME]) - && $attributes[RouteObjectInterface::ROUTE_NAME] == 'node.view' - && isset($attributes['node']) - && $this->forumManager->checkNodeType($attributes['node']); + public function applies(RouteMatchInterface $route_match) { + return $route_match->getRouteName() == 'node.view' + && $route_match->getParameter('node') + && $this->forumManager->checkNodeType($route_match->getParameter('node')); } /** * {@inheritdoc} */ - public function build(array $attributes) { - $breadcrumb = parent::build($attributes); + public function build(RouteMatchInterface $route_match) { + $breadcrumb = parent::build($route_match); - $parents = $this->forumManager->getParents($attributes['node']->forum_tid); + $parents = $this->forumManager->getParents($route_match->getParameter('node')->forum_tid); if ($parents) { $parents = array_reverse($parents); foreach ($parents as $parent) { diff --git a/core/modules/forum/tests/src/Breadcrumb/ForumBreadcrumbBuilderBaseTest.php b/core/modules/forum/tests/src/Breadcrumb/ForumBreadcrumbBuilderBaseTest.php index 8b71d69..33449a3 100644 --- a/core/modules/forum/tests/src/Breadcrumb/ForumBreadcrumbBuilderBaseTest.php +++ b/core/modules/forum/tests/src/Breadcrumb/ForumBreadcrumbBuilderBaseTest.php @@ -147,7 +147,7 @@ public function testBuild() { $property->setValue($breadcrumb_builder, $link_generator); // Our empty data set. - $attributes = array(); + $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); // Expected result set. $expected = array( @@ -156,7 +156,7 @@ public function testBuild() { ); // And finally, the test. - $this->assertSame($expected, $breadcrumb_builder->build($attributes)); + $this->assertSame($expected, $breadcrumb_builder->build($route_match)); } } diff --git a/core/modules/forum/tests/src/Breadcrumb/ForumListingBreadcrumbBuilderTest.php b/core/modules/forum/tests/src/Breadcrumb/ForumListingBreadcrumbBuilderTest.php index 7a58343..130545d 100644 --- a/core/modules/forum/tests/src/Breadcrumb/ForumListingBreadcrumbBuilderTest.php +++ b/core/modules/forum/tests/src/Breadcrumb/ForumListingBreadcrumbBuilderTest.php @@ -37,13 +37,15 @@ public static function getInfo() { * * @param bool $expected * ForumListingBreadcrumbBuilder::applies() expected result. - * @param array $attributes - * ForumListingBreadcrumbBuilder::applies() $attributes parameter. + * @param string|null $route_name + * (optional) A route name. + * @param array $parameter_map + * (optional) An array of parameter names and values. * * @dataProvider providerTestApplies * @covers ::applies */ - public function testApplies($expected, $attributes) { + public function testApplies($expected, $route_name = NULL, $parameter_map = array()) { // Make some test doubles. $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); $config_factory = $this->getConfigFactoryStub(array()); @@ -59,7 +61,15 @@ public function testApplies($expected, $attributes) { ->setMethods(NULL) ->getMock(); - $this->assertEquals($expected, $builder->applies($attributes)); + $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); + $route_match->expects($this->once()) + ->method('getRouteName') + ->will($this->returnValue($route_name)); + $route_match->expects($this->any()) + ->method('getParameter') + ->will($this->returnValueMap($parameter_map)); + + $this->assertEquals($expected, $builder->applies($route_match)); } /** @@ -79,33 +89,24 @@ public function providerTestApplies() { return array( array( FALSE, - array(), ), array( FALSE, - array( - RouteObjectInterface::ROUTE_NAME => 'NOT.forum.page', - ), + 'NOT.forum.page', ), array( FALSE, - array( - RouteObjectInterface::ROUTE_NAME => 'forum.page', - ), + 'forum.page', ), array( TRUE, - array( - RouteObjectInterface::ROUTE_NAME => 'forum.page', - 'taxonomy_term' => 'anything', - ), + 'forum.page', + array(array('taxonomy_term', 'anything')), ), array( TRUE, - array( - RouteObjectInterface::ROUTE_NAME => 'forum.page', - 'taxonomy_term' => $mock_term, - ), + 'forum.page', + array(array('taxonomy_term', $mock_term)), ), ); } @@ -212,9 +213,11 @@ public function testBuild() { ->will($this->returnValue('You_should_not_see_this')); // Our data set. - $attributes = array( - 'taxonomy_term' => $forum_listing, - ); + $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); + $route_match->expects($this->exactly(2)) + ->method('getParameter') + ->with('taxonomy_term') + ->will($this->returnValue($forum_listing)); // First test. $expected1 = array( @@ -222,7 +225,7 @@ public function testBuild() { 'Fora_is_the_plural_of_forum', 'Something', ); - $this->assertSame($expected1, $breadcrumb_builder->build($attributes)); + $this->assertSame($expected1, $breadcrumb_builder->build($route_match)); // Second test. $expected2 = array( @@ -231,7 +234,7 @@ public function testBuild() { 'Something else', 'Something', ); - $this->assertSame($expected2, $breadcrumb_builder->build($attributes)); + $this->assertSame($expected2, $breadcrumb_builder->build($route_match)); } } diff --git a/core/modules/forum/tests/src/Breadcrumb/ForumNodeBreadcrumbBuilderTest.php b/core/modules/forum/tests/src/Breadcrumb/ForumNodeBreadcrumbBuilderTest.php index d1019f5..b1726c4 100644 --- a/core/modules/forum/tests/src/Breadcrumb/ForumNodeBreadcrumbBuilderTest.php +++ b/core/modules/forum/tests/src/Breadcrumb/ForumNodeBreadcrumbBuilderTest.php @@ -37,13 +37,15 @@ public static function getInfo() { * * @param bool $expected * ForumNodeBreadcrumbBuilder::applies() expected result. - * @param array $attributes - * ForumNodeBreadcrumbBuilder::applies() $attributes parameter. + * @param string|null $route_name + * (optional) A route name. + * @param array $parameter_map + * (optional) An array of parameter names and values. * * @dataProvider providerTestApplies * @covers ::applies */ - public function testApplies($expected, $attributes) { + public function testApplies($expected, $route_name = NULL, $parameter_map = array()) { // Make some test doubles. $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); $config_factory = $this->getConfigFactoryStub(array()); @@ -65,7 +67,15 @@ public function testApplies($expected, $attributes) { ->setMethods(NULL) ->getMock(); - $this->assertEquals($expected, $builder->applies($attributes)); + $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); + $route_match->expects($this->once()) + ->method('getRouteName') + ->will($this->returnValue($route_name)); + $route_match->expects($this->any()) + ->method('getParameter') + ->will($this->returnValueMap($parameter_map)); + + $this->assertEquals($expected, $builder->applies($route_match)); } /** @@ -87,33 +97,24 @@ public function providerTestApplies() { return array( array( FALSE, - array(), ), array( FALSE, - array( - RouteObjectInterface::ROUTE_NAME => 'NOT.node.view', - ), + 'NOT.node.view', ), array( FALSE, - array( - RouteObjectInterface::ROUTE_NAME => 'node.view', - ), + 'node.view', ), array( FALSE, - array( - RouteObjectInterface::ROUTE_NAME => 'node.view', - 'node' => NULL, - ), + 'node.view', + array(array('node', NULL)), ), array( TRUE, - array( - RouteObjectInterface::ROUTE_NAME => 'node.view', - 'node' => $mock_node, - ), + 'node.view', + array(array('node', $mock_node)), ), ); } @@ -216,9 +217,11 @@ public function testBuild() { ->getMock(); // Our data set. - $attributes = array( - 'node' => $forum_node, - ); + $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); + $route_match->expects($this->exactly(2)) + ->method('getParameter') + ->with('node') + ->will($this->returnValue($forum_node)); // First test. $expected1 = array( @@ -226,7 +229,7 @@ public function testBuild() { 'Forums', 'Something', ); - $this->assertSame($expected1, $breadcrumb_builder->build($attributes)); + $this->assertSame($expected1, $breadcrumb_builder->build($route_match)); // Second test. $expected2 = array( @@ -235,7 +238,7 @@ public function testBuild() { 'Something else', 'Something', ); - $this->assertSame($expected2, $breadcrumb_builder->build($attributes)); + $this->assertSame($expected2, $breadcrumb_builder->build($route_match)); } } diff --git a/core/modules/language/src/Plugin/Condition/Language.php b/core/modules/language/src/Plugin/Condition/Language.php index 41dab71..46a99cb 100644 --- a/core/modules/language/src/Plugin/Condition/Language.php +++ b/core/modules/language/src/Plugin/Condition/Language.php @@ -17,11 +17,10 @@ * id = "language", * label = @Translation("Language"), * context = { - * "language" = { - * "type" = "language" - * } + * "language" = @ContextDefinition("language", label = @Translation("Language")) * } * ) + * */ class Language extends ConditionPluginBase { diff --git a/core/modules/locale/src/Form/ImportForm.php b/core/modules/locale/src/Form/ImportForm.php index 21b3150..f1ae6a1 100644 --- a/core/modules/locale/src/Form/ImportForm.php +++ b/core/modules/locale/src/Form/ImportForm.php @@ -70,8 +70,6 @@ public function getFormID() { /** * Form constructor for the translation import screen. - * - * @ingroup forms */ public function buildForm(array $form, array &$form_state) { $languages = $this->languageManager->getLanguages(); diff --git a/core/modules/menu_link/menu_link.module b/core/modules/menu_link/menu_link.module index ac3c408..23fc554 100644 --- a/core/modules/menu_link/menu_link.module +++ b/core/modules/menu_link/menu_link.module @@ -5,10 +5,10 @@ * Enables users to create menu links. */ +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Drupal\menu_link\Entity\MenuLink; use Drupal\menu_link\MenuLinkInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; function menu_link_help($route_name, Request $request) { @@ -213,11 +213,10 @@ function menu_link_maintain($module, $op, $link_path, $link_title = NULL) { /** * Implements hook_system_breadcrumb_alter(). */ -function menu_link_system_breadcrumb_alter(array &$breadcrumb, array $attributes, array $context) { +function menu_link_system_breadcrumb_alter(array &$breadcrumb, RouteMatchInterface $route_match, array $context) { // Custom breadcrumb behavior for editing menu links, we append a link to // the menu in which the link is found. - if (!empty($attributes[RouteObjectInterface::ROUTE_NAME]) && $attributes[RouteObjectInterface::ROUTE_NAME] == 'menu_ui.link_edit' && !empty($attributes['menu_link'])) { - $menu_link = $attributes['menu_link']; + if (($route_match->getRouteName() == 'menu_ui.link_edit') && $menu_link = $route_match->getParameter('menu_link')) { if (($menu_link instanceof MenuLinkInterface) && !$menu_link->isNew()) { // Add a link to the menu admin screen. $menu = entity_load('menu', $menu_link->menu_name); diff --git a/core/modules/node/src/Plugin/Condition/NodeType.php b/core/modules/node/src/Plugin/Condition/NodeType.php index 66e57e3..7973e69 100644 --- a/core/modules/node/src/Plugin/Condition/NodeType.php +++ b/core/modules/node/src/Plugin/Condition/NodeType.php @@ -16,11 +16,10 @@ * id = "node_type", * label = @Translation("Node Bundle"), * context = { - * "node" = { - * "type" = "entity:node" - * } + * "node" = @ContextDefinition("entity:node", label = @Translation("Node")) * } * ) + * */ class NodeType extends ConditionPluginBase { diff --git a/core/modules/options/options.module b/core/modules/options/options.module index 71bbf33..16c3498 100644 --- a/core/modules/options/options.module +++ b/core/modules/options/options.module @@ -8,7 +8,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\field\FieldConfigInterface; -use Drupal\field\FieldConfigUpdateForbiddenException; +use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException; use Symfony\Component\HttpFoundation\Request; /** @@ -100,7 +100,7 @@ function options_field_config_update_forbid(FieldConfigInterface $field, FieldCo $prior_allowed_values = $prior_field->getSetting('allowed_values'); $lost_keys = array_diff(array_keys($prior_allowed_values), array_keys($allowed_values)); if (_options_values_in_use($field->entity_type, $field->getName(), $lost_keys)) { - throw new FieldConfigUpdateForbiddenException(t('A list field (@field_name) with existing data cannot have its keys changed.', array('@field_name' => $field->getName()))); + throw new FieldStorageDefinitionUpdateForbiddenException(t('A list field (@field_name) with existing data cannot have its keys changed.', array('@field_name' => $field->getName()))); } } } diff --git a/core/modules/options/src/Tests/OptionsFieldTest.php b/core/modules/options/src/Tests/OptionsFieldTest.php index a1d2c14..ed775b4 100644 --- a/core/modules/options/src/Tests/OptionsFieldTest.php +++ b/core/modules/options/src/Tests/OptionsFieldTest.php @@ -7,7 +7,7 @@ namespace Drupal\options\Tests; -use Drupal\field\FieldException; +use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException; /** * Tests for the 'Options' field types. @@ -50,7 +50,7 @@ function testUpdateAllowedValues() { $this->field->save(); $this->fail(t('Cannot update a list field to not include keys with existing data.')); } - catch (FieldException $e) { + catch (FieldStorageDefinitionUpdateForbiddenException $e) { $this->pass(t('Cannot update a list field to not include keys with existing data.')); } // Empty the value, so that we can actually remove the option. diff --git a/core/modules/path/path.module b/core/modules/path/path.module index 50ba145..7419926 100644 --- a/core/modules/path/path.module +++ b/core/modules/path/path.module @@ -86,7 +86,8 @@ function path_entity_base_field_info(EntityTypeInterface $entity_type) { 'type' => 'path', 'weight' => 30, )) - ->setDisplayConfigurable('form', TRUE); + ->setDisplayConfigurable('form', TRUE) + ->setCustomStorage(TRUE); return $fields; } diff --git a/core/modules/rest/src/Annotation/RestResource.php b/core/modules/rest/src/Annotation/RestResource.php index c9ac9e9..42e63c9 100644 --- a/core/modules/rest/src/Annotation/RestResource.php +++ b/core/modules/rest/src/Annotation/RestResource.php @@ -12,6 +12,15 @@ /** * Defines a REST resource annotation object. * + * Plugin Namespace: Plugin\rest\resource + * + * For a working example, see \Drupal\rest\Plugin\rest\resource\DBLogResource + * + * @see \Drupal\rest\Plugin\Type\ResourcePluginManager + * @see \Drupal\rest\Plugin\ResourceBase + * @see \Drupal\rest\Plugin\ResourceInterface + * @see plugin_api + * * @Annotation */ class RestResource extends Plugin { diff --git a/core/modules/rest/src/Plugin/ResourceBase.php b/core/modules/rest/src/Plugin/ResourceBase.php index 05c0a2d..9b7f2d7 100644 --- a/core/modules/rest/src/Plugin/ResourceBase.php +++ b/core/modules/rest/src/Plugin/ResourceBase.php @@ -15,6 +15,11 @@ /** * Common base class for resource plugins. + * + * @see \Drupal\rest\Annotation\RestResource + * @see \Drupal\rest\Plugin\Type\ResourcePluginManager + * @see \Drupal\rest\Plugin\ResourceInterface + * @see plugin_api */ abstract class ResourceBase extends PluginBase implements ContainerFactoryPluginInterface, ResourceInterface { diff --git a/core/modules/rest/src/Plugin/ResourceInterface.php b/core/modules/rest/src/Plugin/ResourceInterface.php index 3f21f0d..d882c6f 100644 --- a/core/modules/rest/src/Plugin/ResourceInterface.php +++ b/core/modules/rest/src/Plugin/ResourceInterface.php @@ -11,6 +11,11 @@ /** * Specifies the publicly available methods of a resource plugin. + * + * @see \Drupal\rest\Annotation\RestResource + * @see \Drupal\rest\Plugin\Type\ResourcePluginManager + * @see \Drupal\rest\Plugin\ResourceBase + * @see plugin_api */ interface ResourceInterface extends PluginInspectionInterface { diff --git a/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php b/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php index cb87990..ec9cb9f 100644 --- a/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php +++ b/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php @@ -13,6 +13,11 @@ /** * Manages discovery and instantiation of resource plugins. + * + * @see \Drupal\rest\Annotation\RestResource + * @see \Drupal\rest\Plugin\ResourceBase + * @see \Drupal\rest\Plugin\ResourceInterface + * @see plugin_api */ class ResourcePluginManager extends DefaultPluginManager { diff --git a/core/modules/shortcut/shortcut.admin.inc b/core/modules/shortcut/shortcut.admin.inc index 52783d4..56e5e03 100644 --- a/core/modules/shortcut/shortcut.admin.inc +++ b/core/modules/shortcut/shortcut.admin.inc @@ -21,7 +21,6 @@ * @return * An array representing the form definition. * - * @ingroup forms * @see shortcut_set_switch_validate() * @see shortcut_set_switch_submit() * diff --git a/core/modules/system/core.api.php b/core/modules/system/core.api.php index c890e60..4b697a1 100644 --- a/core/modules/system/core.api.php +++ b/core/modules/system/core.api.php @@ -17,10 +17,10 @@ * * @section essentials Essential background concepts * - * - @link architecture Drupal's architecture @endlink * - @link oo_conventions Object-oriented conventions used in Drupal @endlink * - @link extending Extending and altering Drupal @endlink * - @link best_practices Security and best practices @endlink + * - @link info_types Types of information in Drupal @endlink * * @section interface User interface * @@ -67,39 +67,6 @@ */ /** - * @defgroup block_api Block API - * @{ - * Information about the classes and interfaces that make up the Block API. - * - * Blocks are a combination of a configuration entity and a plugin. The - * configuration entity stores placement information (theme, region, weight) and - * any other configuration that is specific to the block. The block plugin does - * the work of rendering the block's content for display. - * - * To define a block in a module you need to: - * - Define a Block plugin by creating a new class that implements the - * \Drupal\block\BlockPluginInterface. For more information about how block - * plugins are discovered see the @link plugin_api Plugin API topic @endlink. - * - Usually you will want to extend the \Drupal\block\BlockBase class, which - * provides a common configuration form and utility methods for getting and - * setting configuration in the block configuration entity. - * - Block plugins use the annotations defined by - * \Drupal\block\Annotation\Block. See the - * @link annotation Annotations topic @endlink for more information about - * annotations. - * - * Further information and examples: - * - \Drupal\system\Plugin\Block\SystemPoweredByBlock provides a simple example - * of defining a block. - * - \Drupal\book\Plugin\Block\BookNavigationBlock is an example of a block with - * a custom configuration form. - * - For a more in-depth discussion of the Block API see - * https://drupal.org/developing/api/8/block_api - * - The examples project also provides a Block example in - * https://drupal.org/project/examples. - */ - -/** * @defgroup third_party REST and Application Integration * @{ * Integrating third-party applications using REST and related operations. @@ -117,7 +84,7 @@ * Information about the State API. * * The State API is one of several methods in Drupal for storing information. - * See @link architecture Drupal's architecture topic @endlink for an + * See the @link info_types Information types topic @endlink for an * overview of the different types of information. * * The basic entry point into the State API is \Drupal::state(), which returns @@ -143,7 +110,7 @@ * Information about the Configuration API. * * The Configuration API is one of several methods in Drupal for storing - * information. See @link architecture Drupal's architecture topic @endlink for + * information. See the @link info_types Information types topic @endlink for * an overview of the different types of information. The sections below have * more information about the configuration API; see * https://drupal.org/developing/api/8/configuration for more details. @@ -164,6 +131,10 @@ * string, Boolean, etc.) and settings can also exist in a nested hierarchy, * known as a "mapping". * + * Configuration can also be overridden on a global, per-language, or + * per-module basis. See https://www.drupal.org/node/1928898 for more + * information. + * * @section sec_yaml Configuration YAML files * Whether or not configuration files are being used for the active * configuration storage on a particular site, configuration files are always @@ -275,8 +246,8 @@ * * Entities, in Drupal, are objects that are used for persistent storage of * content and configuration information. See the - * @link architecture Drupal's architecture topic @endlink for an overview of - * the different types of information, and the + * @link info_types Information types topic @endlink for an overview of the + * different types of information, and the * @link config_api Configuration API topic @endlink for more about the * configuration API. * @@ -441,23 +412,6 @@ */ /** - * @defgroup views_overview Views overview - * @{ - * Overview of the Views module API - * - * @todo write this - * - * Additional documentation paragraphs need to be written, and functions, - * classes, and interfaces need to be added to this topic. Should link to all - * or most of the existing Views topics, and maybe this should be moved into - * a different file? This topic should be an overview so that developers know - * which of the many Views classes and topics are important if they want to - * accomplish tasks that they may have. - * @} - */ - - -/** * @defgroup i18n Internationalization * @{ * Internationalization and translation @@ -993,18 +947,10 @@ */ /** - * @defgroup architecture Architecture overview + * @defgroup info_types Information types * @{ - * Overview of Drupal's architecture for developers. + * Types of information in Drupal. * - * @todo write this - * - * Additional documentation paragraphs need to be written, and functions, - * classes, and interfaces need to be added to this topic. - * - * Should include: modules, info.yml files, location of files, etc. - * - * @section Types of information in Drupal * Drupal has several distinct types of information, each with its own methods * for storage and retrieval: * - Content: Information meant to be displayed on your site: articles, basic @@ -1013,7 +959,10 @@ * - Session: Information about individual users' interactions with the site, * such as whether they are logged in. This is really "state" information, but * it is not stored the same way so it's a separate type here. Session - * information is managed ... + * information is managed via the session_manager service in Drupal, which + * implements \Drupal\Core\Session\SessionManagerInterface. See the + * @link container Services topic @endlink for more information about + * services. * - State: Information of a temporary nature, generally machine-generated and * not human-edited, about the current state of your site. Examples: the time * when Cron was last run, whether node access permissions need rebuilding, @@ -1024,9 +973,8 @@ * you have defined, etc. See * @link config_api the Configuration API topic @endlink for more information. * - * @todo Add something relevant to the list item about sessions. - * @todo Add some information about Settings, the key-value store in general, - * and maybe the cache to this list (not sure if cache belongs here?). + * @see cache + * @see i18n * @} */ @@ -1266,16 +1214,33 @@ * @{ * PSR-4, namespaces, class naming, and other conventions. * - * @todo write this - * - * Additional documentation paragraphs need to be written, and functions, - * classes, and interfaces need to be added to this topic. - * - * See https://drupal.org/node/608152 and links therein for references. This - * should be an overview and link to details. It needs to cover: PSR-*, - * namespaces, link to reference on OO, class naming conventions (base classes, - * etc.), and other things developers should know related to object-oriented - * coding. + * A lot of the PHP code in Drupal is object oriented (OO), making use of + * @link http://php.net/manual/language.oop5.php PHP classes, interfaces, and traits @endlink + * (which are loosely referred to as "classes" in the rest of this topic). The + * following conventions and standards apply to this version of Drupal: + * - Each class must be in its own file. + * - Classes must be namespaced. If a module defines a class, the namespace + * must start with \Drupal\module_name. If it is defined by Drupal Core for + * use across many modules, the namespace should be \Drupal\Core or + * \Drupal\Component, with the exception of the global class \Drupal. See + * https://www.drupal.org/node/1353118 for more about namespaces. + * - In order for the PSR-4-based class auto-loader to find the class, it must + * be located in a directory corresponding to the namespace. For + * module-defined classes, if the namespace is \Drupal\module_name\foo\bar, + * then the class goes under the main module directory in directory + * src/foo/bar. For Drupal-wide classes, if the namespace is + * \Drupal\Core\foo\bar, then it goes in directory + * core/lib/Drupal/Core/foo/bar. See https://www.drupal.org/node/2156625 for + * more information about PSR-4. + * - Some classes have annotations added to their documentation headers. See + * the @link annotation Annotation topic @endlink for more information. + * - Standard plugin discovery requires particular namespaces and annotation + * for most plugin classes. See the + * @link plugin_api Plugin API topic @endlink for more information. + * - There are project-wide coding standards for OO code, including naming: + * https://drupal.org/node/608152 + * - Documentation standards for classes are covered on: + * https://www.drupal.org/coding-standards/docs#classes * @} */ diff --git a/core/modules/system/src/Form/ThemeAdminForm.php b/core/modules/system/src/Form/ThemeAdminForm.php index 81aebc7..d836047 100644 --- a/core/modules/system/src/Form/ThemeAdminForm.php +++ b/core/modules/system/src/Form/ThemeAdminForm.php @@ -10,8 +10,6 @@ /** * Form to select the administration theme. - * - * @ingroup forms */ class ThemeAdminForm extends FormBase { diff --git a/core/modules/system/src/PathBasedBreadcrumbBuilder.php b/core/modules/system/src/PathBasedBreadcrumbBuilder.php index 79d7788..c5d5706 100644 --- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php +++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php @@ -13,6 +13,7 @@ use Drupal\Core\Access\AccessManager; use Drupal\Core\ParamConverter\ParamNotConvertedException; use Drupal\Core\PathProcessor\InboundPathProcessorInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Component\Utility\Unicode; use Symfony\Component\HttpFoundation\Request; @@ -107,14 +108,14 @@ public function __construct(RequestContext $context, AccessManager $access_manag /** * {@inheritdoc} */ - public function applies(array $attributes) { + public function applies(RouteMatchInterface $route_match) { return TRUE; } /** * {@inheritdoc} */ - public function build(array $attributes) { + public function build(RouteMatchInterface $route_match) { $links = array(); // General path-based breadcrumbs. Use the actual request path, prior to diff --git a/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php b/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php index 814fce5..2201ebb 100644 --- a/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php +++ b/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php @@ -8,6 +8,10 @@ namespace Drupal\system\Plugin\Block; use Drupal\block\BlockBase; +use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Routing\RouteMatchInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a block to display the breadcrumbs. @@ -17,15 +21,60 @@ * admin_label = @Translation("Breadcrumbs") * ) */ -class SystemBreadcrumbBlock extends BlockBase { +class SystemBreadcrumbBlock extends BlockBase implements ContainerFactoryPluginInterface { + + /** + * The breadcrumb manager. + * + * @var \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface + */ + protected $breadcrumbManager; + + /** + * The current route match. + * + * @var \Drupal\Core\Routing\RouteMatchInterface + */ + protected $routeMatch; + + /** + * Constructs a new SystemBreadcrumbBlock object. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface $breadcrumb_manager + * The breadcrumb manager. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The current route match. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, BreadcrumbBuilderInterface $breadcrumb_manager, RouteMatchInterface $route_match) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->breadcrumbManager = $breadcrumb_manager; + $this->routeMatch = $route_match; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('breadcrumb'), + $container->get('current_route_match') + ); + } /** * {@inheritdoc} */ public function build() { - $breadcrumb_manager = \Drupal::service('breadcrumb'); - $request = \Drupal::service('request'); - $breadcrumb = $breadcrumb_manager->build($request->attributes->all()); + $breadcrumb = $this->breadcrumbManager->build($this->routeMatch); if (!empty($breadcrumb)) { // $breadcrumb is expected to be an array of rendered breadcrumb links. return array( diff --git a/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php b/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php new file mode 100644 index 0000000..0023397 --- /dev/null +++ b/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php @@ -0,0 +1,131 @@ + 'Entity bundle fields', + 'description' => 'Tests providing a custom bundle field.', + 'group' => 'Entity API', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + $this->installSchema('user', array('users_data')); + $this->installSchema('system', array('router')); + $this->moduleHandler = $this->container->get('module_handler'); + $this->database = $this->container->get('database'); + } + + /** + * Tests the custom bundle field creation and deletion. + */ + public function testCustomBundleFieldCreateDelete() { + // Install the module which adds the field. + $this->moduleHandler->install(array('entity_bundle_field_test'), FALSE); + $definition = $this->entityManager->getFieldDefinitions('entity_test', 'custom')['custom_field']; + $this->assertNotNull($definition, 'Field definition found.'); + + // Make sure the table has been created. + $table = $this->entityManager->getStorage('entity_test')->_fieldTableName($definition); + $this->assertTrue($this->database->schema()->tableExists($table), 'Table created'); + $this->moduleHandler->uninstall(array('entity_bundle_field_test'), FALSE); + $this->assertFalse($this->database->schema()->tableExists($table), 'Table dropped'); + } + + /** + * Tests making use of a custom bundle field. + */ + public function testCustomBundleFieldUsage() { + // Check that an entity with bundle entity_test does not have the custom + // field. + $this->moduleHandler->install(array('entity_bundle_field_test'), FALSE); + $storage = $this->entityManager->getStorage('entity_test'); + $entity = $storage->create([ + 'type' => 'entity_test', + ]); + $this->assertFalse($entity->hasField('custom_field')); + + // Check that the custom bundle has the defined custom field and check + // saving and deleting of custom field data. + $entity = $storage->create([ + 'type' => 'custom', + ]); + $this->assertTrue($entity->hasField('custom_field')); + $entity->custom_field->value = 'swanky'; + $entity->save(); + $storage->resetCache(); + $entity = $storage->load($entity->id()); + $this->assertEqual($entity->custom_field->value, 'swanky', 'Entity was saved correct.y'); + + $entity->custom_field->value = 'cozy'; + $entity->save(); + $storage->resetCache(); + $entity = $storage->load($entity->id()); + $this->assertEqual($entity->custom_field->value, 'cozy', 'Entity was updated correctly.'); + + $entity->delete(); + $table = $storage->_fieldTableName($entity->getFieldDefinition('custom_field')); + $result = $this->database->select($table, 'f') + ->fields('f') + ->condition('f.entity_id', $entity->id()) + ->execute(); + $this->assertFalse($result->fetchAssoc(), 'Field data has been deleted'); + + // Create another entity to test that values are marked as deleted when a + // bundle is deleted. + $entity = $storage->create(['type' => 'custom', 'custom_field' => 'new']); + $entity->save(); + entity_test_delete_bundle('custom'); + + $table = $storage->_fieldTableName($entity->getFieldDefinition('custom_field')); + $result = $this->database->select($table, 'f') + ->condition('f.entity_id', $entity->id()) + ->condition('deleted', 1) + ->countQuery() + ->execute(); + $this->assertEqual(1, $result->fetchField(), 'Field data has been deleted'); + + // @todo Test field purge and table deletion once supported. + // $this->assertFalse($this->database->schema()->tableExists($table), 'Custom field table was deleted'); + } + +} diff --git a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php index e82129e..fcec7ca 100644 --- a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php +++ b/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php @@ -9,9 +9,8 @@ use Drupal\Core\Database\Database; use Drupal\Core\Entity\ContentEntityDatabaseStorage; -use Drupal\field\FieldException; +use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException; use Drupal\field\Entity\FieldConfig; -use Drupal\system\Tests\Entity\EntityUnitTestBase; /** * Tests field storage. @@ -322,7 +321,7 @@ function testUpdateFieldSchemaWithData() { $field->save(); $this->fail(t('Cannot update field schema with data.')); } - catch (FieldException $e) { + catch (FieldStorageDefinitionUpdateForbiddenException $e) { $this->pass(t('Cannot update field schema with data.')); } } @@ -560,9 +559,9 @@ public function testTableNames() { 'deleted' => TRUE, )); $expected = 'field_deleted_data_' . substr(hash('sha256', $field->uuid), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field), $expected); + $this->assertEqual(ContentEntityDatabaseStorage::_fieldTableName($field, TRUE), $expected); $expected = 'field_deleted_revision_' . substr(hash('sha256', $field->uuid), 0, 10); - $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field), $expected); + $this->assertEqual(ContentEntityDatabaseStorage::_fieldRevisionTableName($field, TRUE), $expected); } } diff --git a/core/modules/system/src/Tests/Plugin/ContextPluginTest.php b/core/modules/system/src/Tests/Plugin/ContextPluginTest.php index cd7dbfe..b52dd0f 100644 --- a/core/modules/system/src/Tests/Plugin/ContextPluginTest.php +++ b/core/modules/system/src/Tests/Plugin/ContextPluginTest.php @@ -7,14 +7,15 @@ namespace Drupal\system\Tests\Plugin; -use Drupal\simpletest\DrupalUnitTestBase; +use Drupal\Component\Plugin\Exception\ContextException; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\plugin_test\Plugin\MockBlockManager; -use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\simpletest\KernelTestBase; /** * Tests that context aware plugins function correctly. */ -class ContextPluginTest extends DrupalUnitTestBase { +class ContextPluginTest extends KernelTestBase { public static $modules = array('system', 'user', 'node', 'field', 'filter', 'text'); @@ -36,50 +37,31 @@ function testContext() { // Create a node, add it as context, catch the exception. $node = entity_create('node', array('title' => $name, 'type' => 'page')); - // Try to get a valid context that has not been set. + // Try to get context that is missing its definition. try { - $plugin->getContext('user'); + $plugin->getContextDefinition('not_exists'); $this->fail('The user context should not yet be set.'); } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The user context is not yet set.'); + catch (ContextException $e) { + $this->assertEqual($e->getMessage(), 'The not_exists context is not a valid context.'); } - // Try to get an invalid context. - try { - $plugin->getContext('node'); - $this->fail('The node context should not be a valid context.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The node context is not a valid context.'); - } + // Test the getContextDefinitions() method. + $user_context_definition = ContextDefinition::create('entity:user')->setLabel(t('User')); + $this->assertEqual($plugin->getContextDefinitions()['user']->getLabel(), $user_context_definition->getLabel()); + + // Test the getContextDefinition() method for a valid context. + $this->assertEqual($plugin->getContextDefinition('user')->getLabel(), $user_context_definition->getLabel()); + + // Try to get a context with valid definition. + $this->assertNotNull($plugin->getContext('user'), 'Succeeded to get a context with a valid definition.'); - // Try to get a valid context value that has not been set. + // Try to get a value of a valid context, while this value has not been set. try { $plugin->getContextValue('user'); - $this->fail('The user context should not yet be set.'); } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The user context is not yet set.'); - } - - // Try to call a method of the plugin that requires context before it has - // been set. - try { - $plugin->getTitle(); - $this->fail('The user context should not yet be set.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The user context is not yet set.'); - } - - // Try to get a context value that is not valid. - try { - $plugin->getContextValue('node'); - $this->fail('The node context should not be a valid context.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The node context is not a valid context.'); + catch(ContextException $e) { + $this->assertIdentical("The entity:user context is required and not present.", $e->getMessage(), 'Requesting a non-set value of a required context should throw a context exception.'); } // Try to pass the wrong class type as a context value. @@ -87,81 +69,22 @@ function testContext() { $violations = $plugin->validateContexts(); $this->assertTrue(!empty($violations), 'The provided context value does not pass validation.'); - // Set an appropriate context value appropriately and check to make sure - // its methods work as expected. + // Set an appropriate context value and check to make sure its methods work + // as expected. $user = entity_create('user', array('name' => $name)); $plugin->setContextValue('user', $user); + + $this->assertEqual($plugin->getContextValue('user')->getName(), $user->getName()); $this->assertEqual($user->label(), $plugin->getTitle()); - // Test the getContextDefinitions() method. - $this->assertIdentical($plugin->getContextDefinitions(), array('user' => array('class' => 'Drupal\user\UserInterface'))); - - // Test the getContextDefinition() method for a valid context. - $this->assertEqual($plugin->getContextDefinition('user'), array('class' => 'Drupal\user\UserInterface')); - - // Test the getContextDefinition() method for an invalid context. - try { - $plugin->getContextDefinition('node'); - $this->fail('The node context should not be a valid context.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The node context is not a valid context.'); - } - - // Test typed data context plugins. - $typed_data_plugin = $manager->createInstance('string_context'); - - // Try to get a valid context value that has not been set. - try { - $typed_data_plugin->getContextValue('string'); - $this->fail('The string context should not yet be set.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The string context is not yet set.'); - } - - // Try to call a method of the plugin that requires a context value before - // it has been set. - try { - $typed_data_plugin->getTitle(); - $this->fail('The string context should not yet be set.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The string context is not yet set.'); - } - - // Set the context value appropriately and check the title. - $typed_data_plugin->setContextValue('string', $name); - $this->assertEqual($name, $typed_data_plugin->getTitle()); + // Test Optional context handling. + $plugin = $manager->createInstance('user_name_optional'); + $this->assertNull($plugin->getContextValue('user'), 'Requesting a non-set value of a valid context should return NULL.'); // Test Complex compound context handling. $complex_plugin = $manager->createInstance('complex_context'); - - // With no contexts set, try to get the contexts. - try { - $complex_plugin->getContexts(); - $this->fail('There should not be any contexts set yet.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'There are no set contexts.'); - } - - // With no contexts set, try to get the context values. - $values = $complex_plugin->getContextValues(); - $this->assertIdentical(array_filter($values), array(), 'There are no set contexts.'); - - // Set the user context value. $complex_plugin->setContextValue('user', $user); - // With only the user context set, try to get the contexts. - try { - $complex_plugin->getContexts(); - $this->fail('The node context should not yet be set.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The node context is not yet set.'); - } - // With only the user context set, try to get the context values. $values = $complex_plugin->getContextValues(); $this->assertNull($values['node'], 'The node context is not yet set.'); diff --git a/core/modules/system/src/Tests/Plugin/DerivativeTest.php b/core/modules/system/src/Tests/Plugin/DerivativeTest.php index 8feeff6..0d634f5 100644 --- a/core/modules/system/src/Tests/Plugin/DerivativeTest.php +++ b/core/modules/system/src/Tests/Plugin/DerivativeTest.php @@ -25,11 +25,11 @@ public static function getInfo() { */ function testDerivativeDecorator() { // Ensure that getDefinitions() returns the expected definitions. - $this->assertIdentical($this->mockBlockManager->getDefinitions(), $this->mockBlockExpectedDefinitions); + $this->assertEqual($this->mockBlockManager->getDefinitions(), $this->mockBlockExpectedDefinitions); // Ensure that getDefinition() returns the expected definition. foreach ($this->mockBlockExpectedDefinitions as $id => $definition) { - $this->assertIdentical($this->mockBlockManager->getDefinition($id), $definition); + $this->assertEqual($this->mockBlockManager->getDefinition($id), $definition); } // Ensure that NULL is returned as the definition of a non-existing base diff --git a/core/modules/system/src/Tests/Plugin/PluginTestBase.php b/core/modules/system/src/Tests/Plugin/PluginTestBase.php index 3a92dbd..3508a05 100644 --- a/core/modules/system/src/Tests/Plugin/PluginTestBase.php +++ b/core/modules/system/src/Tests/Plugin/PluginTestBase.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Plugin; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\simpletest\UnitTestBase; use Drupal\plugin_test\Plugin\TestPluginManager; use Drupal\plugin_test\Plugin\MockBlockManager; @@ -82,22 +83,26 @@ public function setUp() { 'label' => 'User name', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', 'context' => array( - 'user' => array('class' => 'Drupal\user\UserInterface') + 'user' => new ContextDefinition('entity:user', 'User'), + ), + ), + 'user_name_optional' => array( + 'label' => 'User name optional', + 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', + 'context' => array( + 'user' => new ContextDefinition('entity:user', 'User', FALSE), ), ), 'string_context' => array( 'label' => 'String typed data', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\TypedDataStringBlock', - 'context' => array( - 'string' => array('type' => 'string'), - ), ), 'complex_context' => array( 'label' => 'Complex context', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockComplexContextBlock', 'context' => array( - 'user' => array('class' => 'Drupal\user\UserInterface'), - 'node' => array('class' => 'Drupal\node\NodeInterface'), + 'user' => new ContextDefinition('entity:user', 'User'), + 'node' => new ContextDefinition('entity:node', 'Node'), ), ), ); diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index a7a49ad..a1e73a6 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -895,16 +895,15 @@ function hook_module_implements_alter(&$implementations, $hook) { * @code * array('Home'); * @endcode - * @param array $attributes - * Attributes representing the current page, coming from - * \Drupal::request()->attributes. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The current route match. * @param array $context * May include the following key: * - builder: the instance of * \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface that constructed this * breadcrumb, or NULL if no builder acted based on the current attributes. */ -function hook_system_breadcrumb_alter(array &$breadcrumb, array $attributes, array $context) { +function hook_system_breadcrumb_alter(array &$breadcrumb, \Drupal\Core\Routing\RouteMatchInterface $route_match, array $context) { // Add an item to the end of the breadcrumb. $breadcrumb[] = Drupal::l(t('Text'), 'example_route_name'); } diff --git a/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.info.yml b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.info.yml new file mode 100644 index 0000000..6732090 --- /dev/null +++ b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.info.yml @@ -0,0 +1,8 @@ +name: 'Entity bundle field test module' +type: module +description: 'Provides a bundle field to the test entity.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - entity_test diff --git a/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.install b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.install new file mode 100644 index 0000000..6065425 --- /dev/null +++ b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.install @@ -0,0 +1,41 @@ +getFieldStorageDefinitions('entity_test')['custom_field']; + $manager->getStorage('entity_test')->onFieldStorageDefinitionCreate($definition); + + // Create the custom bundle and put our bundle field on it. + entity_test_create_bundle('custom'); + $definition = $manager->getFieldDefinitions('entity_test', 'custom')['custom_field']; + $manager->getStorage('entity_test')->onFieldDefinitionCreate($definition); +} + +/** + * Implements hook_uninstall(). + */ +function entity_bundle_field_test_uninstall() { + entity_bundle_field_test_is_uninstalling(TRUE); + $manager = \Drupal::entityManager(); + // Notify the entity storage that our field is gone. + $definition = $manager->getFieldDefinitions('entity_test', 'custom')['custom_field']; + $manager->getStorage('entity_test')->onFieldDefinitionDelete($definition); + $storage_definition = $manager->getFieldStorageDefinitions('entity_test')['custom_field']; + $manager->getStorage('entity_test')->onFieldStorageDefinitionDelete($storage_definition); + $manager->clearCachedFieldDefinitions(); + + do { + $count = $manager->getStorage('entity_test')->purgeFieldData($definition, 500); + } + while ($count != 0); + $manager->getStorage('entity_test')->finalizePurge($definition); +} diff --git a/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.module b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.module new file mode 100644 index 0000000..55f5bff --- /dev/null +++ b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.module @@ -0,0 +1,69 @@ +id() == 'entity_test' && !entity_bundle_field_test_is_uninstalling()) { + // @todo: Make use of a FieldStorageDefinition class instead of + // FieldDefinition as this should not implement FieldDefinitionInterface. + // See https://drupal.org/node/2280639. + $definitions['custom_field'] = FieldDefinition::create('string') + ->setName('custom_field') + ->setLabel(t('A custom field')) + ->setTargetEntityTypeId($entity_type->id()); + return $definitions; + } +} + +/** + * Implements hook_entity_bundle_field_info(). + */ +function entity_bundle_field_test_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) { + if ($entity_type->id() == 'entity_test' && $bundle == 'custom' && !entity_bundle_field_test_is_uninstalling()) { + $definitions['custom_field'] = FieldDefinition::create('string') + ->setName('custom_field') + ->setLabel(t('A custom field')); + return $definitions; + } +} + +/** + * Implements hook_entity_bundle_delete(). + */ +function entity_bundle_field_test_entity_bundle_delete($entity_type_id, $bundle) { + if ($entity_type_id == 'entity_test' && $bundle == 'custom') { + // Notify the entity storage that our field is gone. + $field_definition = FieldDefinition::create('string') + ->setTargetEntityTypeId($entity_type_id) + ->setBundle($bundle) + ->setName('custom_field') + ->setLabel(t('A custom field')); + \Drupal::entityManager()->getStorage('entity_test') + ->onFieldDefinitionDelete($field_definition); + } +} diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module index 26e7a50..2b0e441 100644 --- a/core/modules/system/tests/modules/form_test/form_test.module +++ b/core/modules/system/tests/modules/form_test/form_test.module @@ -1159,7 +1159,6 @@ function form_test_number($form, &$form_state, $element = 'number') { * Form constructor for testing #type 'range' elements. * * @see form_test_range_submit() - * @ingroup forms * * @deprecated Use \Drupal\form_test\testRange() */ @@ -1208,8 +1207,6 @@ function form_test_range($form, &$form_state) { /** * Form constructor for testing invalid #type 'range' elements. * - * @ingroup forms - * * @deprecated Use \Drupal\form_test\testRangeInvalid() */ function form_test_range_invalid($form, &$form_state) { @@ -1231,7 +1228,6 @@ function form_test_range_invalid($form, &$form_state) { * Form constructor for testing #type 'color' elements. * * @see form_test_color_submit() - * @ingroup forms * * @deprecated Use \Drupal\form_test\testColor() */ @@ -1331,7 +1327,6 @@ function form_test_checkboxes_radios($form, &$form_state, $customize = FALSE) { * Form constructor for testing #type 'email' elements. * * @see form_test_email_submit() - * @ingroup forms * * @deprecated Use \Drupal\form_test\testEmail() */ @@ -1359,7 +1354,6 @@ function form_test_email($form, &$form_state) { * Form constructor for testing #type 'url' elements. * * @see form_test_url_submit() - * @ingroup forms * * @deprecated Use \Drupal\form_test\testUrl() */ diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php index cd3d030..6de31cd 100644 --- a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php +++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php @@ -11,6 +11,7 @@ use Drupal\Component\Plugin\Discovery\StaticDiscovery; use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator; use Drupal\Component\Plugin\Factory\ReflectionFactory; +use Drupal\Core\Plugin\Context\ContextDefinition; /** * Defines a plugin manager used by Plugin API derivative unit tests. @@ -77,7 +78,16 @@ public function __construct() { 'label' => t('User name'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', 'context' => array( - 'user' => array('class' => 'Drupal\user\UserInterface') + 'user' => new ContextDefinition('entity:user', t('User')), + ), + )); + + // An optional context version of the previous block plugin. + $this->discovery->setDefinition('user_name_optional', array( + 'label' => t('User name optional'), + 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', + 'context' => array( + 'user' => new ContextDefinition('entity:user', t('User'), FALSE), ), )); @@ -85,9 +95,6 @@ public function __construct() { $this->discovery->setDefinition('string_context', array( 'label' => t('String typed data'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\TypedDataStringBlock', - 'context' => array( - 'string' => array('type' => 'string'), - ), )); // A complex context plugin that requires both a user and node for context. @@ -95,8 +102,8 @@ public function __construct() { 'label' => t('Complex context'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockComplexContextBlock', 'context' => array( - 'user' => array('class' => 'Drupal\user\UserInterface'), - 'node' => array('class' => 'Drupal\node\NodeInterface'), + 'user' => new ContextDefinition('entity:user', t('User')), + 'node' => new ContextDefinition('entity:node', t('Node')), ), )); diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockComplexContextBlock.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockComplexContextBlock.php index 1cb657a..8041c3a 100644 --- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockComplexContextBlock.php +++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockComplexContextBlock.php @@ -7,6 +7,7 @@ namespace Drupal\plugin_test\Plugin\plugin_test\mock_block; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Plugin\ContextAwarePluginBase; /** diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockUserNameBlock.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockUserNameBlock.php index 9150167..07c7a79 100644 --- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockUserNameBlock.php +++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockUserNameBlock.php @@ -7,6 +7,7 @@ namespace Drupal\plugin_test\Plugin\plugin_test\mock_block; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Plugin\ContextAwarePluginBase; /** diff --git a/core/modules/system/tests/src/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php b/core/modules/system/tests/src/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php index 65f2a62..b7a145b 100644 --- a/core/modules/system/tests/src/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php +++ b/core/modules/system/tests/src/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php @@ -140,7 +140,7 @@ public function testBuildOnFrontpage() { ->method('getPathInfo') ->will($this->returnValue('/')); - $links = $this->builder->build(array()); + $links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface')); $this->assertEquals(array(), $links); } @@ -156,7 +156,7 @@ public function testBuildWithOnePathElement() { $this->setupLinkGeneratorWithFrontpage(); - $links = $this->builder->build(array()); + $links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface')); $this->assertEquals(array(0 => 'Home'), $links); } @@ -198,7 +198,7 @@ public function testBuildWithTwoPathElements() { ->will($this->returnValue($link_front)); $this->setupAccessManagerWithTrue(); - $links = $this->builder->build(array()); + $links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface')); $this->assertEquals(array(0 => 'Home', 1 => $link_example), $links); } @@ -254,7 +254,7 @@ public function testBuildWithThreePathElements() { ->will($this->returnValue($link_front)); $this->setupAccessManagerWithTrue(); - $links = $this->builder->build(array()); + $links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface')); $this->assertEquals(array(0 => 'Home', 1 => $link_example, 2 => $link_example_bar), $links); } @@ -277,7 +277,7 @@ public function testBuildWithException($exception_class, $exception_argument) { ->will($this->throwException(new $exception_class($exception_argument))); $this->setupLinkGeneratorWithFrontpage(); - $links = $this->builder->build(array()); + $links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface')); // No path matched, though at least the frontpage is displayed. $this->assertEquals(array(0 => 'Home'), $links); @@ -319,7 +319,7 @@ public function testBuildWithNonProcessedPath() { ->will($this->returnValue(array())); $this->setupLinkGeneratorWithFrontpage(); - $links = $this->builder->build(array()); + $links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface')); // No path matched, though at least the frontpage is displayed. $this->assertEquals(array(0 => 'Home'), $links); @@ -331,7 +331,7 @@ public function testBuildWithNonProcessedPath() { * @covers ::applies() */ public function testApplies() { - $this->assertTrue($this->builder->applies(array())); + $this->assertTrue($this->builder->applies($this->getMock('Drupal\Core\Routing\RouteMatchInterface'))); } /** @@ -377,7 +377,7 @@ public function testBuildWithUserPath() { ->with($this->anything(), $route_1) ->will($this->returnValue('Admin')); - $links = $this->builder->build(array()); + $links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface')); $this->assertEquals(array(0 => 'Home', 1 => $link_user), $links); } diff --git a/core/modules/taxonomy/src/TermBreadcrumbBuilder.php b/core/modules/taxonomy/src/TermBreadcrumbBuilder.php index 6625d55..e4e1123 100644 --- a/core/modules/taxonomy/src/TermBreadcrumbBuilder.php +++ b/core/modules/taxonomy/src/TermBreadcrumbBuilder.php @@ -8,7 +8,7 @@ namespace Drupal\taxonomy; use Drupal\Core\Breadcrumb\BreadcrumbBuilderBase; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Drupal\Core\Routing\RouteMatchInterface; /** * Provides a custom taxonomy breadcrumb builder that uses the term hierarchy. @@ -18,17 +18,16 @@ class TermBreadcrumbBuilder extends BreadcrumbBuilderBase { /** * {@inheritdoc} */ - public function applies(array $attributes) { - return !empty($attributes[RouteObjectInterface::ROUTE_NAME]) - && ($attributes[RouteObjectInterface::ROUTE_NAME] == 'taxonomy.term_page') - && ($attributes['taxonomy_term'] instanceof TermInterface); + public function applies(RouteMatchInterface $route_match) { + return $route_match->getRouteName() == 'taxonomy.term_page' + && $route_match->getParameter('taxonomy_term') instanceof TermInterface; } /** * {@inheritdoc} */ - public function build(array $attributes) { - $term = $attributes['taxonomy_term']; + public function build(RouteMatchInterface $route_match) { + $term = $route_match->getParameter('taxonomy_term'); // @todo This overrides any other possible breadcrumb and is a pure // hard-coded presumption. Make this behavior configurable per // vocabulary or term. diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc index 38611bf..86872da 100644 --- a/core/modules/update/update.manager.inc +++ b/core/modules/update/update.manager.inc @@ -62,7 +62,6 @@ * @see update_manager_update_form_validate() * @see update_manager_update_form_submit() * @see update_menu() - * @ingroup forms * * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. Use * \Drupal\update\Form\UpdateForm::reportUpdate() or @@ -397,7 +396,6 @@ function update_manager_download_batch_finished($success, $results) { * * @see update_manager_update_ready_form_submit() * @see update_menu() - * @ingroup forms */ function update_manager_update_ready_form($form, &$form_state) { if (!_update_manager_check_backends($form, 'update')) { @@ -511,7 +509,6 @@ function update_manager_update_ready_form_submit($form, &$form_state) { * @see update_manager_install_form_validate() * @see update_manager_install_form_submit() * @see update_menu() - * @ingroup forms * * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. Use * \Drupal\update\Form\UpdateForm::reportInstall() or diff --git a/core/modules/user/src/Plugin/Condition/UserRole.php b/core/modules/user/src/Plugin/Condition/UserRole.php index cd38ee3..872b18a 100644 --- a/core/modules/user/src/Plugin/Condition/UserRole.php +++ b/core/modules/user/src/Plugin/Condition/UserRole.php @@ -8,6 +8,7 @@ namespace Drupal\user\Plugin\Condition; use Drupal\Core\Condition\ConditionPluginBase; +use Drupal\Core\Plugin\Context\ContextDefinition; /** * Provides a 'User Role' condition. @@ -16,11 +17,10 @@ * id = "user_role", * label = @Translation("User Role"), * context = { - * "user" = { - * "type" = "entity:user" - * } + * "user" = @ContextDefinition("entity:user", label = @Translation("User")) * } * ) + * */ class UserRole extends ConditionPluginBase { diff --git a/core/modules/views/includes/ajax.inc b/core/modules/views/includes/ajax.inc index 337524d..0bcb723 100644 --- a/core/modules/views/includes/ajax.inc +++ b/core/modules/views/includes/ajax.inc @@ -10,12 +10,6 @@ use Drupal\Core\Ajax\AjaxResponse; /** - * @defgroup views_ajax Views AJAX library - * @{ - * Handles the server side AJAX interactions of Views. - */ - -/** * Wrapper around drupal_build_form to handle some AJAX stuff automatically. * This makes some assumptions about the client. */ @@ -67,7 +61,3 @@ function views_ajax_form_wrapper($form_id, &$form_state) { return (!empty($form_state['title'])) ? array('#title' => $form_state['title'], '#markup' => $output) : $output; } - -/** - * @} - */ diff --git a/core/modules/views/src/Annotation/ViewsAccess.php b/core/modules/views/src/Annotation/ViewsAccess.php index 6917904..afa2419 100644 --- a/core/modules/views/src/Annotation/ViewsAccess.php +++ b/core/modules/views/src/Annotation/ViewsAccess.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views access plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\access\AccessPluginBase + * + * @ingroup views_access_plugins + * + * @Annotation */ class ViewsAccess extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsArea.php b/core/modules/views/src/Annotation/ViewsArea.php index 1248cda..0fb52a1 100644 --- a/core/modules/views/src/Annotation/ViewsArea.php +++ b/core/modules/views/src/Annotation/ViewsArea.php @@ -10,9 +10,11 @@ /** * Defines a Plugin annotation object for views area handlers. * - * @Annotation - * * @see \Drupal\views\Plugin\views\area\AreaPluginBase + * + * @ingroup views_area_handlers + * + * @Annotation */ class ViewsArea extends ViewsHandlerAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsArgument.php b/core/modules/views/src/Annotation/ViewsArgument.php index 206e00f..c02ae60 100644 --- a/core/modules/views/src/Annotation/ViewsArgument.php +++ b/core/modules/views/src/Annotation/ViewsArgument.php @@ -10,9 +10,11 @@ /** * Defines a Plugin annotation object for views argument handlers. * - * @Annotation - * * @see \Drupal\views\Plugin\views\argument\ArgumentPluginBase + * + * @ingroup views_argument_handlers + * + * @Annotation */ class ViewsArgument extends ViewsHandlerAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsArgumentDefault.php b/core/modules/views/src/Annotation/ViewsArgumentDefault.php index 2910a58..5dd3e26 100644 --- a/core/modules/views/src/Annotation/ViewsArgumentDefault.php +++ b/core/modules/views/src/Annotation/ViewsArgumentDefault.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views argument default plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase + * + * @ingroup views_argument_default_plugins + * + * @Annotation */ class ViewsArgumentDefault extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsArgumentValidator.php b/core/modules/views/src/Annotation/ViewsArgumentValidator.php index ae2d3f2..3868120 100644 --- a/core/modules/views/src/Annotation/ViewsArgumentValidator.php +++ b/core/modules/views/src/Annotation/ViewsArgumentValidator.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views argument default plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\argument_validator\ArgumentValidatorPluginBase + * + * @ingroup views_argument_validate_plugins + * + * @Annotation */ class ViewsArgumentValidator extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsCache.php b/core/modules/views/src/Annotation/ViewsCache.php index d092942..7098b23 100644 --- a/core/modules/views/src/Annotation/ViewsCache.php +++ b/core/modules/views/src/Annotation/ViewsCache.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views cache plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\cache\CachePluginBase + * + * @ingroup views_cache_plugins + * + * @Annotation */ class ViewsCache extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsDisplay.php b/core/modules/views/src/Annotation/ViewsDisplay.php index fc6652e..9fd0f0a 100644 --- a/core/modules/views/src/Annotation/ViewsDisplay.php +++ b/core/modules/views/src/Annotation/ViewsDisplay.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views display plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\display\DisplayPluginBase + * + * @ingroup views_display_plugins + * + * @Annotation */ class ViewsDisplay extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsDisplayExtender.php b/core/modules/views/src/Annotation/ViewsDisplayExtender.php index 5eceb64..122631a 100644 --- a/core/modules/views/src/Annotation/ViewsDisplayExtender.php +++ b/core/modules/views/src/Annotation/ViewsDisplayExtender.php @@ -10,9 +10,11 @@ /** * Defines a Plugin annotation object for views display extender plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase + * + * @ingroup views_display_extender_plugins + * + * @Annotation */ class ViewsDisplayExtender extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsExposedForm.php b/core/modules/views/src/Annotation/ViewsExposedForm.php index fd9376b..97ace2e 100644 --- a/core/modules/views/src/Annotation/ViewsExposedForm.php +++ b/core/modules/views/src/Annotation/ViewsExposedForm.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views exposed form plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase + * + * @ingroup views_exposed_form_plugins + * + * @Annotation */ class ViewsExposedForm extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsField.php b/core/modules/views/src/Annotation/ViewsField.php index e93c82c..6ec07c9 100644 --- a/core/modules/views/src/Annotation/ViewsField.php +++ b/core/modules/views/src/Annotation/ViewsField.php @@ -10,9 +10,11 @@ /** * Defines a Plugin annotation object for views field handlers. * - * @Annotation - * * @see \Drupal\views\Plugin\views\field\FieldPluginBase + * + * @ingroup views_field_handlers + * + * @Annotation */ class ViewsField extends ViewsHandlerAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsFilter.php b/core/modules/views/src/Annotation/ViewsFilter.php index 0ba4d84..1a60cd9 100644 --- a/core/modules/views/src/Annotation/ViewsFilter.php +++ b/core/modules/views/src/Annotation/ViewsFilter.php @@ -10,9 +10,11 @@ /** * Defines a Plugin annotation object for views filter handlers. * - * @Annotation - * * @see \Drupal\views\Plugin\views\filter\FilterPluginBase + * + * @ingroup views_filter_handlers + * + * @Annotation */ class ViewsFilter extends ViewsHandlerAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsJoin.php b/core/modules/views/src/Annotation/ViewsJoin.php index 919a8dd..ce15b69 100644 --- a/core/modules/views/src/Annotation/ViewsJoin.php +++ b/core/modules/views/src/Annotation/ViewsJoin.php @@ -10,9 +10,11 @@ /** * Defines a Plugin annotation object for views join plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\join\JoinPluginBase + * + * @ingroup views_join_handlers + * + * @Annotation */ class ViewsJoin extends ViewsHandlerAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsPager.php b/core/modules/views/src/Annotation/ViewsPager.php index 2466a57..ef8e43c 100644 --- a/core/modules/views/src/Annotation/ViewsPager.php +++ b/core/modules/views/src/Annotation/ViewsPager.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views pager plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\pager\PagerPluginBase + * + * @ingroup views_pager_plugins + * + * @Annotation */ class ViewsPager extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsQuery.php b/core/modules/views/src/Annotation/ViewsQuery.php index 5be9806..75f1536 100644 --- a/core/modules/views/src/Annotation/ViewsQuery.php +++ b/core/modules/views/src/Annotation/ViewsQuery.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views query plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\query\QueryPluginBase + * + * @ingroup views_query_plugins + * + * @Annotation */ class ViewsQuery extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsRelationship.php b/core/modules/views/src/Annotation/ViewsRelationship.php index 1f2753a..2e68ea6 100644 --- a/core/modules/views/src/Annotation/ViewsRelationship.php +++ b/core/modules/views/src/Annotation/ViewsRelationship.php @@ -10,9 +10,11 @@ /** * Defines a Plugin annotation object for views relationship handlers. * - * @Annotation - * * @see \Drupal\views\Plugin\views\relationship\RelationshipPluginBase + * + * @ingroup views_relationship_handlers + * + * @Annotation */ class ViewsRelationship extends ViewsHandlerAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsRow.php b/core/modules/views/src/Annotation/ViewsRow.php index 43d8eeb..d97d8f9 100644 --- a/core/modules/views/src/Annotation/ViewsRow.php +++ b/core/modules/views/src/Annotation/ViewsRow.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views row plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\row\RowPluginBase + * + * @ingroup views_row_plugins + * + * @Annotation */ class ViewsRow extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsSort.php b/core/modules/views/src/Annotation/ViewsSort.php index cdd28d2..a6f113b 100644 --- a/core/modules/views/src/Annotation/ViewsSort.php +++ b/core/modules/views/src/Annotation/ViewsSort.php @@ -10,9 +10,11 @@ /** * Defines a Plugin annotation object for views sort handlers. * - * @Annotation - * * @see \Drupal\views\Plugin\views\sort\SortPluginBase + * + * @ingroup views_sort_handlers + * + * @Annotation */ class ViewsSort extends ViewsHandlerAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsStyle.php b/core/modules/views/src/Annotation/ViewsStyle.php index 386f36e..7c1afc4 100644 --- a/core/modules/views/src/Annotation/ViewsStyle.php +++ b/core/modules/views/src/Annotation/ViewsStyle.php @@ -12,9 +12,11 @@ /** * Defines a Plugin annotation object for views style plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\style\StylePluginBase + * + * @ingroup views_style_plugins + * + * @Annotation */ class ViewsStyle extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Annotation/ViewsWizard.php b/core/modules/views/src/Annotation/ViewsWizard.php index 9486804..d06cee0 100644 --- a/core/modules/views/src/Annotation/ViewsWizard.php +++ b/core/modules/views/src/Annotation/ViewsWizard.php @@ -12,9 +12,12 @@ /** * Defines a Plugin annotation object for views wizard plugins. * - * @Annotation - * * @see \Drupal\views\Plugin\views\wizard\WizardPluginBase + * @see \Drupal\views\Plugin\views\wizard\WizardInterface + * + * @ingroup views_wizard_plugins + * + * @Annotation */ class ViewsWizard extends ViewsPluginAnnotationBase { diff --git a/core/modules/views/src/Plugin/ViewsPluginManager.php b/core/modules/views/src/Plugin/ViewsPluginManager.php index 386a4b3..ca98555 100644 --- a/core/modules/views/src/Plugin/ViewsPluginManager.php +++ b/core/modules/views/src/Plugin/ViewsPluginManager.php @@ -14,6 +14,8 @@ /** * Plugin type manager for all views plugins. + * + * @ingroup views_plugins */ class ViewsPluginManager extends DefaultPluginManager { diff --git a/core/modules/views/src/Plugin/views/HandlerBase.php b/core/modules/views/src/Plugin/views/HandlerBase.php index cbff847..979fa4b 100644 --- a/core/modules/views/src/Plugin/views/HandlerBase.php +++ b/core/modules/views/src/Plugin/views/HandlerBase.php @@ -21,6 +21,11 @@ use Drupal\views\Views; use Drupal\views\ViewsData; +/** + * Base class for Views handler plugins. + * + * @ingroup views_plugins + */ abstract class HandlerBase extends PluginBase { /** diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php index 086160d..f19520a 100644 --- a/core/modules/views/src/Plugin/views/PluginBase.php +++ b/core/modules/views/src/Plugin/views/PluginBase.php @@ -37,6 +37,8 @@ * root directory. * - module: machine name of the module. It must be present for any plugin that * wants to register a theme. + * + * @ingroup views_plugins */ abstract class PluginBase extends ComponentPluginBase implements ContainerFactoryPluginInterface { diff --git a/core/modules/views/src/Plugin/views/access/AccessPluginBase.php b/core/modules/views/src/Plugin/views/access/AccessPluginBase.php index 9d6a713..45f1ab0 100644 --- a/core/modules/views/src/Plugin/views/access/AccessPluginBase.php +++ b/core/modules/views/src/Plugin/views/access/AccessPluginBase.php @@ -14,10 +14,17 @@ /** * @defgroup views_access_plugins Views access plugins * @{ - * The base plugin to handle access to a view. + * Plugins to handle access checking for views. * - * Therefore it primarily has to implement the access and the alterRouteDefinition - * method. + * Access plugins are responsible for controlling access to the view. + * + * Access plugins extend \Drupal\views\Plugin\views\access\AccessPluginBase, + * implementing the access() and alterRouteDefinition() methods. They must be + * annotated with \Drupal\views\Annotation\ViewsAccess annotation, and they + * must be in namespace directory Plugin\views\access. + * + * @ingroup views_plugins + * @see plugin_api */ /** diff --git a/core/modules/views/src/Plugin/views/area/AreaPluginBase.php b/core/modules/views/src/Plugin/views/area/AreaPluginBase.php index 0255ecc..dcd3ad0 100644 --- a/core/modules/views/src/Plugin/views/area/AreaPluginBase.php +++ b/core/modules/views/src/Plugin/views/area/AreaPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\area\AreaPluginBase. + * Contains \Drupal\views\Plugin\views\area\AreaPluginBase. */ namespace Drupal\views\Plugin\views\area; @@ -12,16 +12,20 @@ use Drupal\views\Plugin\views\HandlerBase; /** - * @defgroup views_area_handlers Views area handlers + * @defgroup views_area_handlers Views area handler plugins * @{ - * Handlers to tell Views what can display in header, footer - * and empty text in a view. + * Plugins governing areas of views, such as header, footer, and empty text. + * + * Area handler plugins extend \Drupal\views\Plugin\views\area\AreaHandlerBase. + * They must be annotated with \Drupal\views\Annotation\ViewsArea annotation, + * and they must be in namespace directory Plugin\views\area. + * + * @ingroup views_plugins + * @see plugin_api */ /** - * Base class for area handlers. - * - * @ingroup views_area_handlers + * Base class for area handler plugins. */ abstract class AreaPluginBase extends HandlerBase { diff --git a/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php index e5857c5..6328269 100644 --- a/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php +++ b/core/modules/views/src/Plugin/views/argument/ArgumentPluginBase.php @@ -17,12 +17,23 @@ /** * @defgroup views_argument_handlers Views argument handlers - * Handlers to tell Views how to contextually filter queries. * @{ + * Handler plugins for Views contextual filters. + * + * Handler plugins help build the view query object. Views argument handlers + * are for contextual filtering. + * + * Views argument handlers extend + * \Drupal\views\Plugin\views\argument\ArgumentHandlerBase. They must be + * annotated with \Drupal\views\Annotation\ViewsArgument annotation, and they + * must be in namespace directory Plugin\views\argument. + * + * @ingroup views_plugins + * @see plugin_api */ /** - * Base class for arguments. + * Base class for argument (contextual filter) handler plugins. * * The basic argument works for very simple arguments such as nid and uid * @@ -42,8 +53,6 @@ * and may disappear or change. * - numeric: If set to TRUE this field is numeric and will use %d instead of * %s in queries. - * - * @ingroup views_argument_handlers */ abstract class ArgumentPluginBase extends HandlerBase { diff --git a/core/modules/views/src/Plugin/views/argument_default/ArgumentDefaultPluginBase.php b/core/modules/views/src/Plugin/views/argument_default/ArgumentDefaultPluginBase.php index 0065815..d478aa6 100644 --- a/core/modules/views/src/Plugin/views/argument_default/ArgumentDefaultPluginBase.php +++ b/core/modules/views/src/Plugin/views/argument_default/ArgumentDefaultPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase. + * Contains \Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase. */ namespace Drupal\views\Plugin\views\argument_default; @@ -14,7 +14,20 @@ /** * @defgroup views_argument_default_plugins Views argument default plugins * @{ - * Allow specialized methods of filling in arguments when they aren't provided. + * Plugins for argument defaults in Views. + * + * Argument default plugins provide default values for contextual filters. + * This is useful for blocks and other display types lacking a natural argument + * input. Examples are plugins to extract node and user IDs from the URL. + * + * Argument default plugins extend + * \Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase. They + * must be annotated with \Drupal\Views\Annotation\ViewsArgumentDefault + * annotation, and they must be in namespace directory + * Plugin\views\argument_default. + * + * @ingroup views_plugins + * @see plugin_api */ /** diff --git a/core/modules/views/src/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php b/core/modules/views/src/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php index af7f12c..b5c852a 100644 --- a/core/modules/views/src/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php +++ b/core/modules/views/src/Plugin/views/argument_validator/ArgumentValidatorPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\argument_validator\ArgumentValidatorPluginBase. + * Contains \Drupal\views\Plugin\views\argument_validator\ArgumentValidatorPluginBase. */ namespace Drupal\views\Plugin\views\argument_validator; @@ -14,7 +14,23 @@ /** * @defgroup views_argument_validate_plugins Views argument validate plugins * @{ - * Allow specialized methods of validating arguments. + * Plugins for validating views contextual filters. + * + * Views argument validator plugins validate contextual filters (arguments) on + * views. They can ensure arguments are valid, and even do transformations on + * the arguments. They can also provide replacement patterns for the view title. + * For example, the 'content' validator verifies verifies that the argument + * value corresponds to a node, loads that node, and provides the node title + * as a replacement pattern for the view title. + * + * Argument validator plugins extend + * \Drupal\views\Plugin\views\argument_validator\ArgumentValidatorPluginBase. + * They must be annotated with + * \Drupal\views\Plugin\Annotation\ViewsArgumentValidator annotation, and they + * must be in namespace directory Plugin\views\argument_validator. + * + * @ingroup views_plugins + * @see plugin_api */ /** diff --git a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php index 9d22eb4..08c07b7 100644 --- a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php +++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\cache\CachePluginBase. + * Contains \Drupal\views\Plugin\views\cache\CachePluginBase. */ namespace Drupal\views\Plugin\views\cache; @@ -14,10 +14,17 @@ /** * @defgroup views_cache_plugins Views cache plugins * @{ - * The base plugin to handler caching of a view. + * Plugins to handle the storage and loading of Views caches. * - * Cache plugins can handle both caching of just the database result and - * the rendered output of the view. + * Cache plugins control the storage and loading of caches in Views, for + * both result and render caching. + * + * Cache plugins extend \Drupal\views\Plugin\views\cache\CachePluginBase. + * They must be annotated with \Drupal\views\Annotation\ViewsCache + * annotation, and must be in namespace directory Plugin\views\cache. + * + * @ingroup views_plugins + * @see plugin_api */ /** diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php index d0e3250..288a84b 100644 --- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Contains Drupal\views\Plugin\views\display\DisplayPluginBase. + * Contains \Drupal\views\Plugin\views\display\DisplayPluginBase. */ namespace Drupal\views\Plugin\views\display; @@ -22,16 +22,26 @@ /** * @defgroup views_display_plugins Views display plugins * @{ - * Display plugins control how Views interact with the rest of Drupal. + * Plugins to handle the overall display of views. * - * They can handle creating Views from a Drupal page hook; they can - * handle creating Views from a Drupal block hook. They can also - * handle creating Views from an external module source. + * Display plugins are responsible for controlling where a view is rendered; + * that is, how it is exposed to other parts of Drupal. 'Page' and 'block' are + * the most commonly used display plugins. Each view also has a 'master' (or + * 'default') display that includes information shared between all its + * displays (see \Drupal\views\Plugin\views\display\DefaultDisplay). + * + * Display plugins extend \Drupal\views\Plugin\views\display\DisplayPluginBase. + * They must be annotated with \Drupal\views\Plugin\Annotation\ViewsDisplay + * annotation, and they must be in namespace directory Plugin\views\display. + * + * @ingroup views_plugins + * + * @see plugin_api + * @see views_display_extender_plugins */ /** - * The default display plugin handler. Display plugins handle options and - * basic mechanisms for different output methods. + * Base class for views display plugins. */ abstract class DisplayPluginBase extends PluginBase { diff --git a/core/modules/views/src/Plugin/views/display_extender/DefaultDisplayExtender.php b/core/modules/views/src/Plugin/views/display_extender/DefaultDisplayExtender.php index 234a0a0..3edbbf7 100644 --- a/core/modules/views/src/Plugin/views/display_extender/DefaultDisplayExtender.php +++ b/core/modules/views/src/Plugin/views/display_extender/DefaultDisplayExtender.php @@ -2,13 +2,15 @@ /** * @file - * Definition of Drupal\views\Plugin\views\display_extender\DefaultDisplayExtender. + * Contains \Drupal\views\Plugin\views\display_extender\DefaultDisplayExtender. */ namespace Drupal\views\Plugin\views\display_extender; /** - * @todo + * Default display extender plugin; does nothing. + * + * @ingroup views_display_extender_plugins * * @ViewsDisplayExtender( * id = "default", diff --git a/core/modules/views/src/Plugin/views/display_extender/DisplayExtenderPluginBase.php b/core/modules/views/src/Plugin/views/display_extender/DisplayExtenderPluginBase.php index 26c59f7..178014f 100644 --- a/core/modules/views/src/Plugin/views/display_extender/DisplayExtenderPluginBase.php +++ b/core/modules/views/src/Plugin/views/display_extender/DisplayExtenderPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase. + * Contains \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase. */ namespace Drupal\views\Plugin\views\display_extender; @@ -11,9 +11,30 @@ use Drupal\views\Plugin\views\PluginBase; /** - * @todo. + * @defgroup views_display_extender_plugins Views display extender plugins + * @{ + * Plugins that offer additional display options across display types. * - * @ingroup views_display_plugins + * Display extender plugins allow additional options or configuration to be + * added to views across all display types. For example, if you wanted to allow + * site users to add certain metadata to the rendered output of every view + * display regardless of display type, you could provide this option as a + * display extender. + * + * Display extender plugins extend + * \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase. + * They must be annotated with + * \Drupal\views\Plugin\Annotation\ViewsDisplayExtender annotation, and they + * must be in namespace directory Plugin\views\display_extender. + * + * @ingroup views_plugins + * + * @see plugin_api + * @see views_display_plugins + */ + +/** + * Base class for Views display extender plugins. */ abstract class DisplayExtenderPluginBase extends PluginBase { @@ -61,3 +82,7 @@ public function optionsSummary(&$categories, &$options) { } public function defaultableSections(&$sections, $section = NULL) { } } + +/** + * @} + */ diff --git a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php index 82e4184..8d98744 100644 --- a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php +++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase. + * Contains \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase. */ namespace Drupal\views\Plugin\views\exposed_form; @@ -16,13 +16,21 @@ /** * @defgroup views_exposed_form_plugins Views exposed form plugins * @{ - * Plugins that handle the validation/submission and rendering of exposed forms. + * Plugins that handle validation, submission, and rendering of exposed forms. * - * If needed, it is possible to use them to add additional form elements. + * Exposed forms are used for filters, sorts, and pager settings that are + * exposed to site visitors. Exposed form plugins handle the rendering, + * validation, and submission of exposed forms, and may add additional form + * elements. + * + * Exposed form plugins extend + * \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase. They must be + * annotated with \Drupal\views\Plugin\Annotation\ViewsExposedForm annotation, + * and they must be in namespace directory Plugin\views\exposed_form. */ /** - * The base plugin to handle exposed filter forms. + * Base class for Views exposed filter form plugins. */ abstract class ExposedFormPluginBase extends PluginBase { diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php index 5eb5765..565a53c 100644 --- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\field\FieldPluginBase. + * Contains \Drupal\views\Plugin\views\field\FieldPluginBase. */ namespace Drupal\views\Plugin\views\field; @@ -17,24 +17,33 @@ use Drupal\views\ViewExecutable; /** - * @defgroup views_field_handlers Views field handlers + * @defgroup views_field_handlers Views field handler plugins * @{ - * Handlers to tell Views how to build and display fields. + * Handler plugins for Views fields. * + * Field handlers handle both querying and display of fields in views. + * + * Field handler plugins extend + * \Drupal\views\Plugin\views\field\FieldHandlerBase. They must be + * annotated with \Drupal\views\Annotation\ViewsField annotation, and they + * must be in namespace directory Plugin\views\field. + * + * The following items can go into a hook_views_data() implementation in a + * field section to affect how the field handler will behave: + * - additional fields: An array of fields that should be added to the query. + * The array is in the form of: + * @code + * array('identifier' => array('table' => tablename, 'field' => fieldname)) + * @endcode + * As many fields as are necessary may be in this array. + * - click sortable: If TRUE (default), this field may be click sorted. + * + * @ingroup views_plugins + * @see plugin_api */ /** * Base field handler that has no options and renders an unformatted field. - * - * Definition terms: - * - additional fields: An array of fields that should be added to the query - * for some purpose. The array is in the form of: - * array('identifier' => array('table' => tablename, - * 'field' => fieldname); as many fields as are necessary - * may be in this array. - * - click sortable: If TRUE, this field may be click sorted. - * - * @ingroup views_field_handlers */ abstract class FieldPluginBase extends HandlerBase { diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php index 562ed68..30d702d 100644 --- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php +++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\filter\FilterPluginBase. + * Contains \Drupal\views\Plugin\views\filter\FilterPluginBase. */ namespace Drupal\views\Plugin\views\filter; @@ -14,30 +14,33 @@ use Drupal\views\ViewExecutable; /** - * @defgroup views_filter_handlers Views filter handlers + * @defgroup views_filter_handlers Views filter handler plugins * @{ - * Handlers to tell Views how to filter queries. + * Plugins that handle views filtering. * - * Definition items: + * Filter handler plugins extend + * \Drupal\views\Plugin\views\filter\FilterHandlerBase. They must be annotated + * with \Drupal\views\Annotation\ViewsFilter annotation, and they must be in + * namespace directory Plugin\views\filter. + * + * The following items can go into a hook_views_data() implementation in a + * filter section to affect how the filter handler will behave: * - allow empty: If true, the 'IS NULL' and 'IS NOT NULL' operators become * available as standard operators. * - * Object flags: - * You can set some specific behavior by setting up the following flags on - * your custom class. + * You can refine the behavior of filters by setting the following Boolean + * member variables to TRUE in your plugin class: + * - $alwaysMultiple: Disable the possibility of forcing a single value. + * - $no_operator: Disable the possibility of using operators. + * - $always_required: Disable the possibility of allowing an exposed input to + * be optional. * - * - alwaysMultiple: - * Disable the possibility to force a single value. - * - no_operator: - * Disable the possibility to use operators. - * - always_required: - * Disable the possibility to allow a exposed input to be optional. + * @ingroup views_plugins + * @see plugin_api */ /** - * Base class for filters. - * - * @ingroup views_filter_handlers + * Base class for Views filters handler plugins. */ abstract class FilterPluginBase extends HandlerBase { diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php index 64a03c5..c91cf1d 100644 --- a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php +++ b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php @@ -10,15 +10,22 @@ use Drupal\Core\Plugin\PluginBase; /** - * @defgroup views_join_handlers Views join handlers + * @defgroup views_join_handlers Views join handler plugins * @{ - * Handlers to tell Views how to join tables together. + * Handler plugins for Views table joins. * - * Here is an example how to join from table one to example two so it produces - * the following sql: + * Handler plugins help build the view query object. Join handler plugins + * handle table joins. + * + * Views join handlers extend \Drupal\views\Plugin\views\join\JoinPluginBase. + * They must be annotated with \Drupal\views\Annotation\ViewsJoin annotation, + * and they must be in namespace directory Plugin\views\join. + * + * Here is an example of how to join from table one to table two so it produces + * the following SQL: * @code * INNER JOIN {two} ON one.field_a = two.field_b - * @code. + * @endcode * The required php code for this kind of functionality is the following: * @code * $configuration = array( @@ -29,9 +36,9 @@ * 'operator' => '=' * ); * $join = Views::pluginManager('join')->createInstance('standard', $configuration); + * @endcode * - * To do complex joins: - * + * Here is an example of a more complex join: * @code * class JoinComplex extends JoinPluginBase { * public function buildJoin($select_query, $table, $view_query) { @@ -41,6 +48,9 @@ * } * } * @endcode + * + * @ingroup views_plugins + * @see plugin_api */ /** diff --git a/core/modules/views/src/Plugin/views/join/Standard.php b/core/modules/views/src/Plugin/views/join/Standard.php index a1d6325..21c6d87 100644 --- a/core/modules/views/src/Plugin/views/join/Standard.php +++ b/core/modules/views/src/Plugin/views/join/Standard.php @@ -10,6 +10,8 @@ /** * Default implementation of the join plugin. * + * @ingroup views_join_handlers + * * @ViewsJoin("standard") */ class Standard extends JoinPluginBase { diff --git a/core/modules/views/src/Plugin/views/join/Subquery.php b/core/modules/views/src/Plugin/views/join/Subquery.php index 2fb14e8..69b25f3 100644 --- a/core/modules/views/src/Plugin/views/join/Subquery.php +++ b/core/modules/views/src/Plugin/views/join/Subquery.php @@ -9,13 +9,17 @@ /** * Join handler for relationships that join with a subquery as the left field. - * E.g: - * LEFT JOIN node node_term_data ON ([YOUR SUBQUERY HERE]) = node_term_data.nid * - * Join definition: - * same as \Drupal\views\Plugin\views\join\JoinPluginBase, except: - * - left_query: The subquery to use in the left side of the join clause. + * For example: + * @code + * LEFT JOIN node node_term_data ON ([YOUR SUBQUERY HERE]) = node_term_data.nid + * @endcode * + * Join definition: same as \Drupal\views\Plugin\views\join\JoinPluginBase, + * except: + * - left_query: The subquery to use in the left side of the join clause. + * + * @ingroup views_join_handlers * @ViewsJoin("subquery") */ class Subquery extends JoinPluginBase { diff --git a/core/modules/views/src/Plugin/views/pager/PagerPluginBase.php b/core/modules/views/src/Plugin/views/pager/PagerPluginBase.php index d91e4bf..e056969 100644 --- a/core/modules/views/src/Plugin/views/pager/PagerPluginBase.php +++ b/core/modules/views/src/Plugin/views/pager/PagerPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\pager\PagerPluginBase. + * Contains \Drupal\views\Plugin\views\pager\PagerPluginBase. */ namespace Drupal\views\Plugin\views\pager; @@ -13,43 +13,22 @@ /** * @defgroup views_pager_plugins Views pager plugins * @{ - * The base plugin to handler pagers of a view. + * Plugins to handle paging in views. * - * The pager takes care about altering the query for its needs, altering some - * global information of pagers and finally rendering itself. + * Pager plugins take care of everything regarding pagers, including figuring + * out the total number of items to render, setting up the query for paging, + * and setting up the pager. + * + * Pager plugins extend \Drupal\views\Plugin\views\pager\PagerPluginBase. They + * must be annotated with \Drupal\views\Plugin\Annotation\ViewsPager annotation, + * and they must be in namespace directory Plugin\views\pager. + * + * @ingroup views_plugins + * @see plugin_api */ /** - * The base plugin to handle pager. - * - * Pager plugins take care of everything regarding pagers, including getting - * and setting the total number of items to render the pager and setting the - * global pager arrays. - * - * To define a pager type, extend this base class. The ViewsPluginManager (used - * to create views plugins objects) adds annotated discovery for pager plugins. - * Your pager plugin must have an annotation that includes the plugin's metadata, - * for example: - * @code - * @ Plugin( - * id = "demo_pager", - * title = @ Translation("Display a demonstration pager"), - * help = @ Translation("Demonstrate pagination of views items."), - * theme = "views_demo_pager" - * ) - * @endcode - * Remove spaces after @ in your actual plugin - these are put into this sample - * code so that it is not recognized as annotation. - * - * The plugin annotation contains these components: - * - id: The unique identifier of your pager plugin. - * - title: The "full" title for your pager type; used in the views UI. - * - short_title: (optional) The "short" title for your pager type; - * used in the views UI when specified. - * - help: (optional) A short help string; this is displayed in the views UI. - * - theme: The theme function used to render the pager's output. - * - * @see \Drupal\views\Plugin\ViewsPluginManager + * Base class for views pager plugins. */ abstract class PagerPluginBase extends PluginBase { diff --git a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php index 113e0cb..db95c61 100644 --- a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php +++ b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php @@ -13,7 +13,26 @@ use Drupal\views\Views; /** - * @todo. + * @defgroup views_query_plugins Views query plugins + * @{ + * Plugins for views queries. + * + * Query plugins generate and execute a built query object against a + * particular storage backend, converting the Views query object into an + * actual query. Although query plugins need not necessarily use SQL, most + * other handler plugins that affect the query (fields, filters, etc.) + * implicitly assume that the query is using SQL. + * + * Query plugins extend \Drupal\views\Plugin\views\query\QueryPluginBase. + * They must be annotated with \Drupal\views\Plugin\Annotation\ViewsQuery + * annotation, and they must be in namespace directory Plugin\views\query. + * + * @ingroup views_plugins + * @see plugin_api + */ + +/** + * Base plugin class for Views queries. */ abstract class QueryPluginBase extends PluginBase { @@ -267,3 +286,7 @@ public function getEntityTableInfo() { } } + +/** + * @} + */ diff --git a/core/modules/views/src/Plugin/views/query/Sql.php b/core/modules/views/src/Plugin/views/query/Sql.php index 323549c..fea3faf 100644 --- a/core/modules/views/src/Plugin/views/query/Sql.php +++ b/core/modules/views/src/Plugin/views/query/Sql.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\query\Sql. + * Contains \Drupal\views\Plugin\views\query\Sql. */ namespace Drupal\views\Plugin\views\query; @@ -17,7 +17,9 @@ use Drupal\views\Views; /** - * @todo. + * Views query plugin for an SQL query. + * + * @ingroup views_query_plugins * * @ViewsQuery( * id = "views_query", diff --git a/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php b/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php index ec40410..07f12ac 100644 --- a/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php +++ b/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\relationship\RelationshipPluginBase. + * Contains \Drupal\views\Plugin\views\relationship\RelationshipPluginBase. */ namespace Drupal\views\Plugin\views\relationship; @@ -16,7 +16,15 @@ /** * @defgroup views_relationship_handlers Views relationship handlers * @{ - * Handlers to tell Views how to create alternate relationships. + * Plugins for handling views relationships. + * + * Relationship handlers extend + * \Drupal\views\Plugin\views\relationship\RelationshipHandlerBase. They must + * be annotated with \Drupal\views\Annotation\ViewsRelationship annotation, + * and they must be in namespace directory Plugin\views\relationship. + * + * @ingroup views_plugins + * @see plugin_api */ /** diff --git a/core/modules/views/src/Plugin/views/row/RowPluginBase.php b/core/modules/views/src/Plugin/views/row/RowPluginBase.php index 864c455..5211136 100644 --- a/core/modules/views/src/Plugin/views/row/RowPluginBase.php +++ b/core/modules/views/src/Plugin/views/row/RowPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\row\RowPluginBase. + * Contains \Drupal\views\Plugin\views\row\RowPluginBase. */ namespace Drupal\views\Plugin\views\row; @@ -14,15 +14,31 @@ /** * @defgroup views_row_plugins Views row plugins * @{ - * Row plugins control how Views outputs an individual record. + * Plugins that control how Views outputs an individual record. * - * They are tightly coupled to style plugins, in that a style plugin is what - * calls the row plugin. + * Row plugins handle rendering each individual record from the view results. + * For instance, a row plugin could render fields, render an entire entity + * in a particular view mode, or render the raw data from the results. + * + * Row plugins are used by some (but not all) style plugins. They are not + * activated unless the style plugin sets them up. See the + * @link views_style_plugins Views style plugins topic @endlink for + * more information. + * + * Row plugins extend \Drupal\views\Plugin\views\row\RowPluginBase. They must + * be annotated with \Drupal\views\Plugin\Annotation\ViewsRow annotation, and + * they must be in namespace directory Plugin\views\row. + * + * @ingroup views_plugins + * @see plugin_api */ /** - * Default plugin to view a single row of a table. This is really just a wrapper around - * a theme function. + * Base class for Views row plugins. + * + * This is really just a wrapper around a theme hook. It renders a row + * of the result table by putting it into a render array with the set theme + * hook. */ abstract class RowPluginBase extends PluginBase { diff --git a/core/modules/views/src/Plugin/views/sort/SortPluginBase.php b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php index 74c9710..dd6e72e 100644 --- a/core/modules/views/src/Plugin/views/sort/SortPluginBase.php +++ b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\sort\SortPluginBase. + * Contains \Drupal\views\Plugin\views\sort\SortPluginBase. */ namespace Drupal\views\Plugin\views\sort; @@ -10,15 +10,20 @@ use Drupal\views\Plugin\views\HandlerBase; /** - * @defgroup views_sort_handlers Views sort handlers + * @defgroup views_sort_handlers Views sort handler plugins * @{ - * Handlers to tell Views how to sort queries. + * Plugins that handle sorting for Views. + * + * Sort handlers extend \Drupal\views\Plugin\views\sort:SortHandlerBase. They + * must be annotated with \Drupal\views\Annotation\ViewsSort annotation, and + * they must be in plugin directory Plugin\views\sort. + * + * @ingroup views_plugins + * @see plugin_api */ /** * Base sort handler that has no options and performs a simple sort. - * - * @ingroup views_sort_handlers */ abstract class SortPluginBase extends HandlerBase { diff --git a/core/modules/views/src/Plugin/views/style/StylePluginBase.php b/core/modules/views/src/Plugin/views/style/StylePluginBase.php index 64ebe6a..c02cb1c 100644 --- a/core/modules/views/src/Plugin/views/style/StylePluginBase.php +++ b/core/modules/views/src/Plugin/views/style/StylePluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\style\StylePluginBase. + * Contains \Drupal\views\Plugin\views\style\StylePluginBase. */ namespace Drupal\views\Plugin\views\style; @@ -15,17 +15,28 @@ /** * @defgroup views_style_plugins Views style plugins * @{ - * Style plugins control how a view is rendered. For example, they - * can choose to display a collection of fields, node_view() output, - * table output, or any kind of crazy output they want. + * Plugins that control how the collection of results is rendered in a view. * - * Many style plugins can have an optional 'row' plugin, that displays - * a single record. Not all style plugins can utilize this, so it is - * up to the plugin to set this up and call through to the row plugin. + * Style plugins control a view is displayed. For the most part, they are + * object wrappers around theme templates. Examples of styles include HTML + * lists, tables, full or teaser content views, etc. + * + * Many (but not all) style plugins have an optional row plugin, which + * displays a single record. Not all style plugins use row plugins, so it is + * up to the style plugin to set this up and call the row plugin. See the + * @link views_row_plugins Views row plugins topic @endlink for more + * information. + * + * Style plugins extend \Drupal\views\Plugin\views\style\StylePluginBase. They + * must be annotated with \Drupal\views\Plugin\Annotation\ViewsStyle + * annotation, and they must be in namespace directory Plugin\views\style. + * + * @ingroup views_plugins + * @see plugin_api */ /** - * Base class to define a style plugin handler. + * Base class for views style plugins. */ abstract class StylePluginBase extends PluginBase { diff --git a/core/modules/views/src/Plugin/views/wizard/Standard.php b/core/modules/views/src/Plugin/views/wizard/Standard.php index b0ede3f..6793a77 100644 --- a/core/modules/views/src/Plugin/views/wizard/Standard.php +++ b/core/modules/views/src/Plugin/views/wizard/Standard.php @@ -8,6 +8,10 @@ namespace Drupal\views\Plugin\views\wizard; /** + * Standard Views wizard plugin. + * + * @ingroup views_wizard_plugins + * * @ViewsWizard( * id = "standard", * derivative = "Drupal\views\Plugin\Derivative\DefaultWizardDeriver", diff --git a/core/modules/views/src/Plugin/views/wizard/WizardInterface.php b/core/modules/views/src/Plugin/views/wizard/WizardInterface.php index 4357d2e..6e1a548 100644 --- a/core/modules/views/src/Plugin/views/wizard/WizardInterface.php +++ b/core/modules/views/src/Plugin/views/wizard/WizardInterface.php @@ -2,13 +2,15 @@ /** * @file - * Definition of Drupal\views\Plugin\views\wizard\WizardInterface. + * Contains \Drupal\views\Plugin\views\wizard\WizardInterface. */ namespace Drupal\views\Plugin\views\wizard; /** * Defines a common interface for Views Wizard plugins. + * + * @ingroup views_wizard_plugins */ interface WizardInterface { diff --git a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php index 8752531..6d5e3fa 100644 --- a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php +++ b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\views\Plugin\views\wizard\WizardPluginBase. + * Contains \Drupal\views\Plugin\views\wizard\WizardPluginBase. */ namespace Drupal\views\Plugin\views\wizard; @@ -16,7 +16,21 @@ use Drupal\views\Plugin\views\wizard\WizardInterface; /** - * Provides the interface and base class for Views Wizard plugins. + * @defgroup views_wizard_plugins Views wizard plugins + * @{ + * Plugins for Views wizards. + * + * Wizard handlers implement \Drupal\views\Plugin\views\wizard\WizardInterface, + * and usually extend \Drupal\views\Plugin\views\wizard\WizardPluginBase. They + * must be annotated with \Drupal\views\Annotation\ViewsWizard annotation, + * and they must be in namespace directory Plugin\views\wizard. + * + * @ingroup views_plugins + * @see plugin_api + */ + +/** + * Base class for Views wizard plugins. * * This is a very generic Views Wizard class that can be constructed for any * base table. @@ -1191,3 +1205,7 @@ public function createView(array $form, array &$form_state) { } } + +/** + * @} + */ diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php index 72da90c..ad8e801 100644 --- a/core/modules/views/src/ViewExecutable.php +++ b/core/modules/views/src/ViewExecutable.php @@ -16,13 +16,8 @@ use Symfony\Component\HttpFoundation\Response; /** - * @defgroup views_objects Objects that represent a View or part of a view - * @{ - * These objects are the core of Views do the bulk of the direction and - * storing of data. All database activity is in these objects. - */ - -/** + * Represents a view as a whole. + * * An object to contain all of the data to generate a view, plus the member * functions to build the view query, execute the query and render the output. */ diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php index 57bea3d..578e376 100644 --- a/core/modules/views/views.api.php +++ b/core/modules/views/views.api.php @@ -8,84 +8,44 @@ use Drupal\Core\Language\LanguageInterface; /** + * @defgroup views_overview Views overview + * @{ + * Overview of the Views module API + * + * The Views module is a generalized query and display engine, which can be used + * to make views (formatted lists, grids, feeds, and other output) of items + * (often entities, but can be other types of data). Developers can interact + * with Views in several ways: + * - Provide plugins: Views plugins govern nearly every aspect of views, + * including querying (sorting, filtering, etc.) and display (at several + * levels of granularity, ranging from the entire view to the details of a + * field). See the @link views_plugins Views plugins topic @endlink for + * more information. + * - Provide data: Data types can be provided to Views by implementing + * hook_views_data(), and data types provided by other modules can be + * altered by implementing hook_views_data_alter(). + * - Implement hooks: A few operations in Views can be influenced by hooks. + * See the @link Views hooks topic @endlink for a list. + * - Theming: See the @link views_templates Views templates topic @endlink + * for more information. + * + * @see \Drupal\views\ViewExecutable + * @} + */ + +/** * @defgroup views_plugins Views plugins * * Views plugins are objects that are used to build and render the view. - * Plugins are registered by extending one of the Views base plugin classes - * and defining settings in the plugin annotation. For more information about - * plugins, see the @link plugin_api Plugin API topic. @endlink + * See individual views plugin topics for more information about the + * specifics of each plugin type, and the + * @link plugin_api Plugin API topic @endlink for more information about + * plugins in general. * - * Views has the following types of plugins: - * - Access: Access plugins are responsible for controlling access to the - * view. Views includes plugins for checking user roles and individual - * permissions. Access plugins extend - * \Drupal\views\Plugin\views\access\AccessPluginBase. - * - Argument default: Argument default plugins allow pluggable ways of - * providing default values for contextual filters. This is useful for - * blocks and other display types lacking a natural argument input. - * Examples are plugins to extract node and user IDs from the URL. Argument - * default plugins extend - * \Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase. - * - Argument validator: Validator plugins can ensure arguments are valid, - * and even do transformations on the arguments. They can also provide - * replacement patterns for the view title. For example, the 'content' - * validator verifies verifies that the argument value corresponds to a - * node, loads that node and provides the node title as a replacement - * pattern. Argument validator plugins extend - * \Drupal\views\Plugin\views\argument_validator\ArgumentValidatorPluginBase. - * - Cache: Cache plugins control the storage and loading of caches. - * Currently they can do both result and render caching. It might also be - * possible to cache the generated query. Cache plugins extend - * \Drupal\views\Plugin\views\cache\CachePluginBase. - * - Display: Display plugins are responsible for controlling where a View is - * rendered; that is, how it is exposed to other parts of Drupal. 'Page' - * and 'block' are the most commonly used display plugins. Each View also - * has a 'master' (or 'default') display that includes information shared - * between all its displays. (See - * \Drupal\views\Plugin\views\display\DefaultDisplay.) Display plugins extend - * \Drupal\views\Plugin\views\display\DisplayPluginBase. - * - Display extender: Display extender plugins allow additional options or - * configurations to added to views across all display types. For example, - * if you wanted to allow site users to add certain metadata to the rendered - * output of every view display regardless of display type, you could provide - * this option as a display extender. Display extender plugins extend - * \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase. - * - Exposed form: Exposed form plugins are responsible for building, - * rendering, and controlling exposed forms. Exposed form plugins extend - * \Drupal\views\Plugin\views\display\DisplayPluginBase. - * - Handlers: Handler plugins help build the view query object that the query - * plugin then executes to retrieve the data from the storage backend (see - * below). There are several types of handlers: - * - Area handlers: Extend \Drupal\views\Plugin\views\area\AreaHandlerBase - * - Argument handlers: Extend - * \Drupal\views\Plugin\views\argument\ArgumentHandlerBase - * - Field handlers: Extend \Drupal\views\Plugin\views\field\FieldHandlerBase - * - Filter handlers: Extend - * \Drupal\views\Plugin\views\filter\FilterHandlerBase - * - Relationship handlers: - * Extend \Drupal\views\Plugin\views\relationship\RelationshipHandlerBase - * - Sort handlers: Extend \Drupal\views\Plugin\views\sort:SortHandlerBase - * - Pager: Pager plugins take care of everything regarding pagers, including - * getting setting the total number of items to render the pager and - * setting the global pager arrays. Pager plugins extend - * \Drupal\views\Plugin\views\pager\PagerPluginBase. - * - Query: Query plugins generate and execute a built query object against a - * particular storage backend, converting the Views query object into an - * actual query. The only default implementation is SQL. (Note that most - * handler plugins currently rely on the SQL query plugin.) Query plugins - * extend \Drupal\views\Plugin\views\query\QueryPluginBase. - * - Row style: Row styles handle rendering each individual record from the - * main view table. The two default implementations render the entire entity - * (nodes only), or selected fields. Row style plugins extend - * \Drupal\views\Plugin\views\row\RowPluginBase). - * - Style: Style plugins control how a view is displayed. For the most part - * they are object wrappers around theme templates. Examples of styles - * include HTML lists, tables, etc. Style plugins extend - * \Drupal\views\Plugin\views\style\StylePluginBase. + * Some Views plugins are known as handlers. Handler plugins help build the + * view query object: filtering, contextual filtering, sorting, relationships, + * etc. * - * @todo Add an explanation for each type of handler. - * @todo Add @ingroup to all the base plugins for this group. - * @todo Add a separate @ingroup for all plugins? * @todo Document specific options on the appropriate plugin base classes. * @todo Add examples. * diff --git a/core/modules/filter/config/install/filter.format.plain_text.yml b/core/profiles/standard/config/install/filter.format.plain_text.yml similarity index 82% copy from core/modules/filter/config/install/filter.format.plain_text.yml copy to core/profiles/standard/config/install/filter.format.plain_text.yml index 5de8988..d6f3bad 100644 --- a/core/modules/filter/config/install/filter.format.plain_text.yml +++ b/core/profiles/standard/config/install/filter.format.plain_text.yml @@ -30,6 +30,15 @@ filters: id: filter_autop provider: filter status: true + weight: 1 + settings: { } + editor_file_reference: + id: editor_file_reference + provider: editor + status: false weight: 0 settings: { } langcode: en +dependencies: + module: + - editor diff --git a/core/profiles/standard/config/install/filter.format.restricted_html.yml b/core/profiles/standard/config/install/filter.format.restricted_html.yml index 95a20e5..fe22b49 100644 --- a/core/profiles/standard/config/install/filter.format.restricted_html.yml +++ b/core/profiles/standard/config/install/filter.format.restricted_html.yml @@ -33,4 +33,13 @@ filters: status: true weight: 10 settings: { } + editor_file_reference: + id: editor_file_reference + provider: editor + status: false + weight: 0 + settings: { } langcode: en +dependencies: + module: + - editor diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install index b9d1c02..c57259c 100644 --- a/core/profiles/standard/standard.install +++ b/core/profiles/standard/standard.install @@ -73,4 +73,10 @@ function standard_install() { // Enable the admin theme. \Drupal::config('node.settings')->set('use_admin_theme', '1')->save(); + + // Resave the plain_text formatter so that default filter plugins and + // dependencies are calculated correctly. This resolves an issue caused by the + // fact that filter is installed before editor but the standard profile also + // enables the file module. + entity_load('filter_format', 'plain_text')->save(); } diff --git a/core/tests/Drupal/Tests/Core/Breadcrumb/BreadcrumbManagerTest.php b/core/tests/Drupal/Tests/Core/Breadcrumb/BreadcrumbManagerTest.php index 49dcad8..75f9a80 100644 --- a/core/tests/Drupal/Tests/Core/Breadcrumb/BreadcrumbManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Breadcrumb/BreadcrumbManagerTest.php @@ -16,7 +16,7 @@ * @group Drupal * @group Breadcrumb * - * @covers \Drupal\Tests\Core\Breadcrumb\BreadcrumbManagerTest + * @coversDefaultClass \Drupal\Core\Breadcrumb\BreadcrumbManager */ class BreadcrumbManagerTest extends UnitTestCase { @@ -57,7 +57,7 @@ public static function getInfo() { * Tests the breadcrumb manager without any set breadcrumb. */ public function testBuildWithoutBuilder() { - $result = $this->breadcrumbManager->build(array()); + $result = $this->breadcrumbManager->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface')); $this->assertEquals(array(), $result); } @@ -68,8 +68,6 @@ public function testBuildWithSingleBuilder() { $builder = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface'); $breadcrumb = array('Test'); - $attributes = array('key' => 'value'); - $builder->expects($this->once()) ->method('applies') ->will($this->returnValue(TRUE)); @@ -78,13 +76,14 @@ public function testBuildWithSingleBuilder() { ->method('build') ->will($this->returnValue($breadcrumb)); + $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); $this->moduleHandler->expects($this->once()) ->method('alter') - ->with('system_breadcrumb', $breadcrumb, $attributes, array('builder' => $builder)); + ->with('system_breadcrumb', $breadcrumb, $route_match, array('builder' => $builder)); $this->breadcrumbManager->addBuilder($builder, 0); - $result = $this->breadcrumbManager->build($attributes); + $result = $this->breadcrumbManager->build($route_match); $this->assertEquals($breadcrumb, $result); } @@ -107,16 +106,16 @@ public function testBuildWithMultipleApplyingBuilders() { ->method('build') ->will($this->returnValue($breadcrumb2)); - $attributes = array('key' => 'value'); + $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); $this->moduleHandler->expects($this->once()) ->method('alter') - ->with('system_breadcrumb', $breadcrumb2, $attributes, array('builder' => $builder2)); + ->with('system_breadcrumb', $breadcrumb2, $route_match, array('builder' => $builder2)); $this->breadcrumbManager->addBuilder($builder1, 0); $this->breadcrumbManager->addBuilder($builder2, 10); - $result = $this->breadcrumbManager->build($attributes); + $result = $this->breadcrumbManager->build($route_match); $this->assertEquals($breadcrumb2, $result); } @@ -140,16 +139,16 @@ public function testBuildWithOneNotApplyingBuilders() { ->method('build') ->will($this->returnValue($breadcrumb2)); - $attributes = array('key' => 'value'); + $route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface'); $this->moduleHandler->expects($this->once()) ->method('alter') - ->with('system_breadcrumb', $breadcrumb2, $attributes, array('builder' => $builder2)); + ->with('system_breadcrumb', $breadcrumb2, $route_match, array('builder' => $builder2)); $this->breadcrumbManager->addBuilder($builder1, 10); $this->breadcrumbManager->addBuilder($builder2, 0); - $result = $this->breadcrumbManager->build($attributes); + $result = $this->breadcrumbManager->build($route_match); $this->assertEquals($breadcrumb2, $result); } @@ -168,7 +167,7 @@ public function testBuildWithInvalidBreadcrumbResult() { ->will($this->returnValue('invalid_result')); $this->breadcrumbManager->addBuilder($builder, 0); - $this->breadcrumbManager->build(array()); + $this->breadcrumbManager->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface')); } } diff --git a/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php b/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php index dc676c5..8081ea8 100644 --- a/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php +++ b/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php @@ -7,8 +7,8 @@ namespace Drupal\Tests\Core\Condition; +use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Core\Condition\ConditionAccessResolverTrait; -use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Tests\UnitTestCase; /** @@ -58,7 +58,7 @@ public function providerTestResolveConditions() { $condition_exception = $this->getMock('Drupal\Core\Condition\ConditionInterface'); $condition_exception->expects($this->any()) ->method('execute') - ->will($this->throwException(new PluginException())); + ->will($this->throwException(new ContextException())); $conditions = array(); $data[] = array($conditions, 'and', TRUE); diff --git a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php index 49efb66..c743200 100644 --- a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php @@ -957,14 +957,16 @@ public function testFieldSqlSchemaForEntityWithStringIdentifier() { ->will($this->returnValue($this->fieldDefinitions)); // Define a field definition for a test_field field. - $field = $this->getMock('\Drupal\field\FieldConfigInterface'); + $field = $this->getMock('\Drupal\Core\Field\FieldStorageDefinitionInterface'); $field->deleted = FALSE; - $field->entity_type = 'test_entity'; - $field->name = 'test_field'; $field->expects($this->any()) ->method('getName') - ->will($this->returnValue('test')); + ->will($this->returnValue('test_field')); + + $field->expects($this->any()) + ->method('getTargetEntityTypeId') + ->will($this->returnValue('test_entity')); $field_schema = array( 'columns' => array( diff --git a/robots.txt b/robots.txt index c5f0b5b..d2ee96f 100644 --- a/robots.txt +++ b/robots.txt @@ -11,7 +11,7 @@ # Ignored: http://example.com/site/robots.txt # # For more information about the robots.txt standard, see: -# http://www.robotstxt.org/wc/robots.html +# http://www.robotstxt.org/robotstxt.html # # For syntax checking, see: # http://www.frobee.com/robots-txt-check