upgrades core to 8.4.2

This commit is contained in:
Bachir Soussi Chiadmi
2017-11-14 16:09:54 +01:00
parent bd60eff9b3
commit c2b4e25be4
3504 changed files with 140306 additions and 38684 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ class Analyzer {
'#theme' => 'item_list',
'#items' => $messages,
];
$message = drupal_render($item_list);
$message = \Drupal::service('renderer')->render($item_list);
}
elseif ($messages) {
$message = array_shift($messages);
@@ -2,7 +2,6 @@
namespace Drupal\views\Annotation;
/**
* Defines a Plugin annotation object for views access plugins.
*
@@ -142,7 +142,7 @@ class ViewAjaxController implements ContainerInjectionInterface {
throw new NotFoundHttpException();
}
$view = $this->executableFactory->get($entity);
if ($view && $view->access($display_id) && $view->setDisplay($display_id) && $view->display_handler->getOption('use_ajax')) {
if ($view && $view->access($display_id) && $view->setDisplay($display_id) && $view->display_handler->ajaxEnabled()) {
$response->setView($view);
// Fix the current path for paging.
if (!empty($path)) {
@@ -181,7 +181,7 @@ class ViewAjaxController implements ContainerInjectionInterface {
$view->dom_id = $dom_id;
$context = new RenderContext();
$preview = $this->renderer->executeInRenderContext($context, function() use ($view, $display_id, $args) {
$preview = $this->renderer->executeInRenderContext($context, function () use ($view, $display_id, $args) {
return $view->preview($display_id, $args);
});
if (!$context->isEmpty()) {
@@ -28,15 +28,54 @@ class TranslationLanguageRenderer extends EntityTranslationRendererBase {
if (!$this->languageManager->isMultilingual() || !$this->entityType->hasKey('langcode')) {
return;
}
$langcode_key = $this->entityType->getKey('langcode');
$storage = \Drupal::entityManager()->getStorage($this->entityType->id());
if ($table = $storage->getTableMapping()->getFieldTableName($langcode_key)) {
$table_alias = $query->ensureTable($table, $relationship);
$langcode_table = $this->getLangcodeTable($query, $relationship);
if ($langcode_table) {
/** @var \Drupal\views\Plugin\views\query\Sql $query */
$table_alias = $query->ensureTable($langcode_table, $relationship);
$langcode_key = $this->entityType->getKey('langcode');
$this->langcodeAlias = $query->addField($table_alias, $langcode_key);
}
}
/**
* Returns the name of the table holding the "langcode" field.
*
* @param \Drupal\views\Plugin\views\query\QueryPluginBase $query
* The query being executed.
* @param string $relationship
* The relationship used by the entity type.
*
* @return string
* A table name.
*/
protected function getLangcodeTable(QueryPluginBase $query, $relationship) {
/** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */
$storage = \Drupal::entityTypeManager()->getStorage($this->entityType->id());
$langcode_key = $this->entityType->getKey('langcode');
$langcode_table = $storage->getTableMapping()->getFieldTableName($langcode_key);
// If the entity type is revisionable, we need to take into account views of
// entity revisions. Usually the view will use the entity data table as the
// query base table, however, in case of an entity revision view, we need to
// use the revision table or the revision data table, depending on which one
// is being used as query base table.
if ($this->entityType->isRevisionable()) {
$query_base_table = isset($query->relationships[$relationship]['base']) ?
$query->relationships[$relationship]['base'] :
$this->view->storage->get('base_table');
$revision_table = $storage->getRevisionTable();
$revision_data_table = $storage->getRevisionDataTable();
if ($query_base_table === $revision_table) {
$langcode_table = $revision_table;
}
elseif ($query_base_table === $revision_data_table) {
$langcode_table = $revision_data_table;
}
}
return $langcode_table;
}
/**
* {@inheritdoc}
*/
+105 -6
View File
@@ -5,8 +5,11 @@ namespace Drupal\views\Entity;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\views\Plugin\DependentWithRemovalPluginInterface;
use Drupal\views\Views;
use Drupal\views\ViewEntityInterface;
@@ -16,9 +19,6 @@ use Drupal\views\ViewEntityInterface;
* @ConfigEntityType(
* id = "view",
* label = @Translation("View", context = "View entity type"),
* handlers = {
* "access" = "Drupal\views\ViewAccessControlHandler"
* },
* admin_permission = "administer views",
* entity_keys = {
* "id" = "id",
@@ -290,10 +290,13 @@ class View extends ConfigEntityBase implements ViewEntityInterface {
public function preSave(EntityStorageInterface $storage) {
parent::preSave($storage);
$displays = $this->get('display');
$this->fixTableNames($displays);
// Sort the displays.
$display = $this->get('display');
ksort($display);
$this->set('display', ['default' => $display['default']] + $display);
ksort($displays);
$this->set('display', ['default' => $displays['default']] + $displays);
// @todo Check whether isSyncing is needed.
if (!$this->isSyncing()) {
@@ -301,6 +304,45 @@ class View extends ConfigEntityBase implements ViewEntityInterface {
}
}
/**
* Fixes table names for revision metadata fields of revisionable entities.
*
* Views for revisionable entity types using revision metadata fields might
* be using the wrong table to retrieve the fields after system_update_8300
* has moved them correctly to the revision table. This method updates the
* views to use the correct tables.
*
* @param array &$displays
* An array containing display handlers of a view.
*
* @deprecated in Drupal 8.3.0, will be removed in Drupal 9.0.0.
*/
private function fixTableNames(array &$displays) {
// Fix wrong table names for entity revision metadata fields.
foreach ($displays as $display => $display_data) {
if (isset($display_data['display_options']['fields'])) {
foreach ($display_data['display_options']['fields'] as $property_name => $property_data) {
if (isset($property_data['entity_type']) && isset($property_data['field']) && isset($property_data['table'])) {
$entity_type = $this->entityTypeManager()->getDefinition($property_data['entity_type']);
// We need to update the table name only for revisionable entity
// types, otherwise the view is already using the correct table.
if (($entity_type instanceof ContentEntityTypeInterface) && is_subclass_of($entity_type->getClass(), FieldableEntityInterface::class) && $entity_type->isRevisionable()) {
$revision_metadata_fields = $entity_type->getRevisionMetadataKeys();
// @see \Drupal\Core\Entity\Sql\SqlContentEntityStorage::initTableLayout()
$revision_table = $entity_type->getRevisionTable() ?: $entity_type->id() . '_revision';
// Check if this is a revision metadata field and if it uses the
// wrong table.
if (in_array($property_data['field'], $revision_metadata_fields) && $property_data['table'] != $revision_table) {
$displays[$display]['display_options']['fields'][$property_name]['table'] = $revision_table;
}
}
}
}
}
}
}
/**
* Fills in the cache metadata of this view.
*
@@ -468,4 +510,61 @@ class View extends ConfigEntityBase implements ViewEntityInterface {
\Drupal::service('cache_tags.invalidator')->invalidateTags($tags);
}
/**
* {@inheritdoc}
*/
public function onDependencyRemoval(array $dependencies) {
$changed = FALSE;
// Don't intervene if the views module is removed.
if (isset($dependencies['module']) && in_array('views', $dependencies['module'])) {
return FALSE;
}
// If the base table for the View is provided by a module being removed, we
// delete the View because this is not something that can be fixed manually.
$views_data = Views::viewsData();
$base_table = $this->get('base_table');
$base_table_data = $views_data->get($base_table);
if (!empty($base_table_data['table']['provider']) && in_array($base_table_data['table']['provider'], $dependencies['module'])) {
return FALSE;
}
$current_display = $this->getExecutable()->current_display;
$handler_types = Views::getHandlerTypes();
// Find all the handlers and check whether they want to do something on
// dependency removal.
foreach ($this->display as $display_id => $display_plugin_base) {
$this->getExecutable()->setDisplay($display_id);
$display = $this->getExecutable()->getDisplay();
foreach (array_keys($handler_types) as $handler_type) {
$handlers = $display->getHandlers($handler_type);
foreach ($handlers as $handler_id => $handler) {
if ($handler instanceof DependentWithRemovalPluginInterface) {
if ($handler->onDependencyRemoval($dependencies)) {
// Remove the handler and indicate we made changes.
unset($this->display[$display_id]['display_options'][$handler_types[$handler_type]['plural']][$handler_id]);
$changed = TRUE;
}
}
}
}
}
// Disable the View if we made changes.
// @todo https://www.drupal.org/node/2832558 Give better feedback for
// disabled config.
if ($changed) {
// Force a recalculation of the dependencies if we made changes.
$this->getExecutable()->current_display = NULL;
$this->calculateDependencies();
$this->disable();
}
$this->getExecutable()->setDisplay($current_display);
return $changed;
}
}
+8 -1
View File
@@ -236,6 +236,13 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
'type' => 'INNER',
];
}
// Add a filter for showing only the latest revisions of an entity.
$data[$revision_table]['latest_revision'] = [
'title' => $this->t('Is Latest Revision'),
'help' => $this->t('Restrict the view to only revisions that are the latest revision of their entity.'),
'filter' => ['id' => 'latest_revision'],
];
}
$this->addEntityLinks($data[$base_table]);
@@ -300,7 +307,7 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
// Add the entity type key to each table generated.
$entity_type_id = $this->entityType->id();
array_walk($data, function(&$table_data) use ($entity_type_id){
array_walk($data, function (&$table_data) use ($entity_type_id) {
$table_data['table']['entity type'] = $entity_type_id;
});
+4 -3
View File
@@ -2,6 +2,7 @@
namespace Drupal\views;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\HandlerBase;
@@ -268,8 +269,8 @@ class ManyToOneHelper {
$options['group'] = 0;
}
// add_condition determines whether a single expression is enough(FALSE) or the
// conditions should be added via an db_or()/db_and() (TRUE).
// If $add_condition is set to FALSE, a single expression is enough. If it
// is set to TRUE, conditions will be added.
$add_condition = TRUE;
if ($operator == 'not') {
$value = NULL;
@@ -326,7 +327,7 @@ class ManyToOneHelper {
if ($add_condition) {
$field = $this->handler->realField;
$clause = $operator == 'or' ? db_or() : db_and();
$clause = $operator == 'or' ? new Condition('OR') : new Condition('AND');
foreach ($this->handler->tableAliases as $value => $alias) {
$clause->condition("$alias.$field", $value);
}
@@ -1,6 +1,7 @@
<?php
namespace Drupal\views\Plugin\Block;
use Drupal\Core\Cache\Cache;
/**
@@ -24,9 +25,23 @@ class ViewsExposedFilterBlock extends ViewsBlockBase {
/**
* {@inheritdoc}
*
* @return array
* A renderable array representing the content of the block with additional
* context of current view and display ID.
*/
public function build() {
$output = $this->view->display_handler->viewExposedFormBlocks();
// Provide the context for block build and block view alter hooks.
// \Drupal\views\Plugin\Block\ViewsBlock::build() adds the same context in
// \Drupal\views\ViewExecutable::buildRenderable() using
// \Drupal\views\Plugin\views\display\DisplayPluginBase::buildRenderable().
if (is_array($output) && !empty($output)) {
$output += [
'#view' => $this->view,
'#display_id' => $this->displayID,
];
}
// Before returning the block output, convert it to a renderable array with
// contextual links.
@@ -0,0 +1,31 @@
<?php
namespace Drupal\views\Plugin;
/**
* Provides an interface for a plugin that has dependencies that can be removed.
*
* @ingroup views_plugins
*/
interface DependentWithRemovalPluginInterface {
/**
* Allows a plugin to define whether it should be removed.
*
* If this method returns TRUE then the plugin should be removed.
*
* @param array $dependencies
* An array of dependencies that will be deleted keyed by dependency type.
* Dependency types are, for example, entity, module and theme.
*
* @return bool
* TRUE if the plugin instance should be removed.
*
* @see \Drupal\Core\Config\Entity\ConfigDependencyManager
* @see \Drupal\Core\Config\ConfigEntityBase::preDelete()
* @see \Drupal\Core\Config\ConfigManager::uninstall()
* @see \Drupal\Core\Entity\EntityDisplayBase::onDependencyRemoval()
*/
public function onDependencyRemoval(array $dependencies);
}
@@ -2,17 +2,12 @@
namespace Drupal\views\Plugin\EntityReferenceSelection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\views\Views;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'selection' entity_reference.
@@ -24,66 +19,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* weight = 0
* )
*/
class ViewsSelection extends PluginBase implements SelectionInterface, ContainerFactoryPluginInterface {
class ViewsSelection extends SelectionPluginBase implements ContainerFactoryPluginInterface {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a new SelectionBase 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\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityManager = $entity_manager;
$this->moduleHandler = $module_handler;
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager'),
$container->get('module_handler'),
$container->get('current_user')
);
}
use SelectionTrait;
/**
* The loaded View object.
@@ -92,12 +30,26 @@ class ViewsSelection extends PluginBase implements SelectionInterface, Container
*/
protected $view;
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'view' => [
'view_name' => NULL,
'display_name' => NULL,
'arguments' => [],
],
] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$selection_handler_settings = $this->configuration['handler_settings'];
$view_settings = !empty($selection_handler_settings['view']) ? $selection_handler_settings['view'] : [];
$form = parent::buildConfigurationForm($form, $form_state);
$view_settings = $this->getConfiguration()['view'];
$displays = Views::getApplicableViews('entity_reference_display');
// Filter views that list the entity type we want, and group the separate
// displays by view.
@@ -156,16 +108,6 @@ class ViewsSelection extends PluginBase implements SelectionInterface, Container
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { }
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { }
/**
* Initializes a view.
*
@@ -184,9 +126,8 @@ class ViewsSelection extends PluginBase implements SelectionInterface, Container
* Return TRUE if the view was initialized, FALSE otherwise.
*/
protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $limit = 0, $ids = NULL) {
$handler_settings = $this->configuration['handler_settings'];
$view_name = $handler_settings['view']['view_name'];
$display_name = $handler_settings['view']['display_name'];
$view_name = $this->getConfiguration()['view']['view_name'];
$display_name = $this->getConfiguration()['view']['display_name'];
// Check that the view is valid and the display still exists.
$this->view = Views::getView($view_name);
@@ -211,9 +152,8 @@ class ViewsSelection extends PluginBase implements SelectionInterface, Container
* {@inheritdoc}
*/
public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
$handler_settings = $this->configuration['handler_settings'];
$display_name = $handler_settings['view']['display_name'];
$arguments = $handler_settings['view']['arguments'];
$display_name = $this->getConfiguration()['view']['display_name'];
$arguments = $this->getConfiguration()['view']['arguments'];
$result = [];
if ($this->initializeView($match, $match_operator, $limit)) {
// Get the results.
@@ -242,9 +182,8 @@ class ViewsSelection extends PluginBase implements SelectionInterface, Container
* {@inheritdoc}
*/
public function validateReferenceableEntities(array $ids) {
$handler_settings = $this->configuration['handler_settings'];
$display_name = $handler_settings['view']['display_name'];
$arguments = $handler_settings['view']['arguments'];
$display_name = $this->getConfiguration()['view']['display_name'];
$arguments = $this->getConfiguration()['view']['arguments'];
$result = [];
if ($this->initializeView(NULL, 'CONTAINS', 0, $ids)) {
// Get the results.
@@ -283,9 +222,4 @@ class ViewsSelection extends PluginBase implements SelectionInterface, Container
$form_state->setValueForElement($element, $value);
}
/**
* {@inheritdoc}
*/
public function entityQueryAlter(SelectInterface $query) { }
}
@@ -59,7 +59,7 @@ trait BrokenHandlerTrait {
}
}
$description_bottom = t('Enabling the appropriate module will may solve this issue. Otherwise, check to see if there is a module update available.');
$description_bottom = t('Enabling the appropriate module may solve this issue. Otherwise, check to see if there is a module update available.');
$form['description'] = [
'#type' => 'container',
@@ -214,16 +214,16 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
* Transform a string by a certain method.
*
* @param $string
* The input you want to transform.
* The input you want to transform.
* @param $option
* How do you want to transform it, possible values:
* - upper: Uppercase the string.
* - lower: lowercase the string.
* - ucfirst: Make the first char uppercase.
* - ucwords: Make each word in the string uppercase.
* How do you want to transform it, possible values:
* - upper: Uppercase the string.
* - lower: lowercase the string.
* - ucfirst: Make the first char uppercase.
* - ucwords: Make each word in the string uppercase.
*
* @return string
* The transformed string.
* The transformed string.
*/
protected function caseTransform($string, $option) {
switch ($option) {
@@ -350,80 +350,84 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
* If a handler has 'extra options' it will get a little settings widget and
* another form called extra_options.
*/
public function hasExtraOptions() { return FALSE; }
public function hasExtraOptions() {
return FALSE;
}
/**
* Provide defaults for the handler.
*/
public function defineExtraOptions(&$option) { }
public function defineExtraOptions(&$option) {}
/**
* Provide a form for setting options.
*/
public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) { }
public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Validate the options form.
*/
public function validateExtraOptionsForm($form, FormStateInterface $form_state) { }
public function validateExtraOptionsForm($form, FormStateInterface $form_state) {}
/**
* Perform any necessary changes to the form values prior to storage.
* There is no need for this function to actually store the data.
*/
public function submitExtraOptionsForm($form, FormStateInterface $form_state) { }
public function submitExtraOptionsForm($form, FormStateInterface $form_state) {}
/**
* Determine if a handler can be exposed.
*/
public function canExpose() { return FALSE; }
public function canExpose() {
return FALSE;
}
/**
* Set new exposed option defaults when exposed setting is flipped
* on.
*/
public function defaultExposeOptions() { }
public function defaultExposeOptions() {}
/**
* Get information about the exposed form for the form renderer.
*/
public function exposedInfo() { }
public function exposedInfo() {}
/**
* Render our chunk of the exposed handler form when selecting
*/
public function buildExposedForm(&$form, FormStateInterface $form_state) { }
public function buildExposedForm(&$form, FormStateInterface $form_state) {}
/**
* Validate the exposed handler form
*/
public function validateExposed(&$form, FormStateInterface $form_state) { }
public function validateExposed(&$form, FormStateInterface $form_state) {}
/**
* Submit the exposed handler form
*/
public function submitExposed(&$form, FormStateInterface $form_state) { }
public function submitExposed(&$form, FormStateInterface $form_state) {}
/**
* Form for exposed handler options.
*/
public function buildExposeForm(&$form, FormStateInterface $form_state) { }
public function buildExposeForm(&$form, FormStateInterface $form_state) {}
/**
* Validate the options form.
*/
public function validateExposeForm($form, FormStateInterface $form_state) { }
public function validateExposeForm($form, FormStateInterface $form_state) {}
/**
* Perform any necessary changes to the form exposes prior to storage.
* There is no need for this function to actually store the data.
*/
public function submitExposeForm($form, FormStateInterface $form_state) { }
public function submitExposeForm($form, FormStateInterface $form_state) {}
/**
* Shortcut to display the expose/hide button.
*/
public function showExposeButton(&$form, FormStateInterface $form_state) { }
public function showExposeButton(&$form, FormStateInterface $form_state) {}
/**
* Shortcut to display the exposed options form.
@@ -477,7 +481,7 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
/**
* {@inheritdoc}
*/
public function postExecute(&$values) { }
public function postExecute(&$values) {}
/**
* Provides a unique placeholders for handlers.
@@ -531,7 +535,7 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
/**
* {@inheritdoc}
*/
public function adminSummary() { }
public function adminSummary() {}
/**
* Determine if this item is 'exposed', meaning it provides form elements
@@ -546,24 +550,32 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
/**
* Returns TRUE if the exposed filter works like a grouped filter.
*/
public function isAGroup() { return FALSE; }
public function isAGroup() {
return FALSE;
}
/**
* Define if the exposed input has to be submitted multiple times.
* This is TRUE when exposed filters grouped are using checkboxes as
* widgets.
*/
public function multipleExposedInput() { return FALSE; }
public function multipleExposedInput() {
return FALSE;
}
/**
* Take input from exposed handlers and assign to this handler, if necessary.
*/
public function acceptExposedInput($input) { return TRUE; }
public function acceptExposedInput($input) {
return TRUE;
}
/**
* If set to remember exposed input in the session, store it there.
*/
public function storeExposedInput($input, $status) { return TRUE; }
public function storeExposedInput($input, $status) {
return TRUE;
}
/**
* {@inheritdoc}
@@ -587,7 +599,9 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
/**
* {@inheritdoc}
*/
public function validate() { return []; }
public function validate() {
return [];
}
/**
* {@inheritdoc}
@@ -155,7 +155,9 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
* @return array
* Returns the options of this handler/plugin.
*/
protected function defineOptions() { return []; }
protected function defineOptions() {
return [];
}
/**
* Fills up the options of the plugin with defaults.
@@ -271,17 +273,17 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
/**
* {@inheritdoc}
*/
public function validateOptionsForm(&$form, FormStateInterface $form_state) { }
public function validateOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
public function submitOptionsForm(&$form, FormStateInterface $form_state) { }
public function submitOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
public function query() { }
public function query() {}
/**
* {@inheritdoc}
@@ -293,7 +295,9 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
/**
* {@inheritdoc}
*/
public function validate() { return []; }
public function validate() {
return [];
}
/**
* {@inheritdoc}
@@ -351,7 +355,7 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
foreach ($tokens as $token => $replacement) {
// Twig wants a token replacement array stripped of curly-brackets.
// Some Views tokens come with curly-braces, others do not.
//@todo: https://www.drupal.org/node/2544392
// @todo: https://www.drupal.org/node/2544392
if (strpos($token, '{{') !== FALSE) {
// Twig wants a token replacement array stripped of curly-brackets.
$token = trim(str_replace(['{{', '}}'], '', $token));
@@ -107,7 +107,7 @@ abstract class AreaPluginBase extends HandlerBase {
* @return array
* In any case we need a valid Drupal render array to return.
*/
public abstract function render($empty = FALSE);
abstract public function render($empty = FALSE);
/**
* Does that area have nothing to show.
@@ -42,7 +42,7 @@ class HTTPStatusCode extends AreaPluginBase {
] + $options;
// Add the HTTP status code, so it's easier for people to find it.
array_walk($options, function($title, $code) use(&$options) {
array_walk($options, function ($title, $code) use (&$options) {
$options[$code] = $this->t('@code (@title)', ['@code' => $code, '@title' => $title]);
});
@@ -47,7 +47,7 @@ class Result extends AreaPluginBase {
'@page_count -- the total page count',
],
];
$list = drupal_render($item_list);
$list = \Drupal::service('renderer')->render($item_list);
$form['content'] = [
'#title' => $this->t('Display'),
'#type' => 'textarea',
@@ -325,7 +325,8 @@ abstract class ArgumentPluginBase extends HandlerBase implements CacheableDepend
'#suffix' => '</div>',
'#type' => 'item',
// Even if the plugin has no options add the key to the form_state.
'#input' => TRUE, // trick it into checking input to make #process run
// trick it into checking input to make #process run.
'#input' => TRUE,
'#states' => [
'visible' => [
':input[name="options[specify_validation]"]' => ['checked' => TRUE],
@@ -498,12 +499,14 @@ abstract class ArgumentPluginBase extends HandlerBase implements CacheableDepend
'method' => 'defaultDefault',
'form method' => 'defaultArgumentForm',
'has default argument' => TRUE,
'default only' => TRUE, // this can only be used for missing argument, not validation failure
// This can only be used for missing argument, not validation failure.
'default only' => TRUE,
],
'not found' => [
'title' => $this->t('Hide view'),
'method' => 'defaultNotFound',
'hard fail' => TRUE, // This is a hard fail condition
// This is a hard fail condition.
'hard fail' => TRUE,
],
'summary' => [
'title' => $this->t('Display a summary'),
@@ -669,7 +672,8 @@ abstract class ArgumentPluginBase extends HandlerBase implements CacheableDepend
'#suffix' => '</div>',
'#id' => 'edit-options-summary-options-' . $id,
'#type' => 'item',
'#input' => TRUE, // trick it into checking input to make #process run
// Trick it into checking input to make #process run.
'#input' => TRUE,
'#states' => [
'visible' => [
':input[name="options[default_action]"]' => ['value' => 'summary'],
@@ -24,7 +24,7 @@ class MonthDate extends Date {
*/
public function summaryName($data) {
$month = str_pad($data->{$this->name_alias}, 2, '0', STR_PAD_LEFT);
return format_date(strtotime("2005" . $month . "15" . " 00:00:00 UTC" ), 'custom', $this->format, 'UTC');
return format_date(strtotime("2005" . $month . "15" . " 00:00:00 UTC"), 'custom', $this->format, 'UTC');
}
/**
@@ -86,7 +86,7 @@ class NumericArgument extends ArgumentPluginBase {
/**
* Override for specific title lookups.
* @return array
* Returns all titles, if it's just one title it's an array with one entry.
* Returns all titles, if it's just one title it's an array with one entry.
*/
public function titleQuery() {
return $this->value;
@@ -42,7 +42,7 @@ abstract class ArgumentDefaultPluginBase extends PluginBase {
*
* This needs to be overridden by every default argument handler to properly do what is needed.
*/
public function getArgument() { }
public function getArgument() {}
/**
* Sets the parent argument this plugin is associated with.
@@ -58,28 +58,32 @@ abstract class ArgumentDefaultPluginBase extends PluginBase {
* Retrieve the options when this is a new access
* control plugin
*/
protected function defineOptions() { return []; }
protected function defineOptions() {
return [];
}
/**
* Provide the default form for setting options.
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) { }
public function buildOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Provide the default form form for validating options
*/
public function validateOptionsForm(&$form, FormStateInterface $form_state) { }
public function validateOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Provide the default form form for submitting options
*/
public function submitOptionsForm(&$form, FormStateInterface $form_state, &$options = []) { }
public function submitOptionsForm(&$form, FormStateInterface $form_state, &$options = []) {}
/**
* Determine if the administrator has the privileges to use this
* plugin
*/
public function access() { return TRUE; }
public function access() {
return TRUE;
}
/**
* If we don't have access to the form but are showing it anyway, ensure that
@@ -53,27 +53,31 @@ abstract class ArgumentValidatorPluginBase extends PluginBase {
/**
* Retrieves the options when this is a new access control plugin.
*/
protected function defineOptions() { return []; }
protected function defineOptions() {
return [];
}
/**
* Provides the default form for setting options.
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) { }
public function buildOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Provides the default form for validating options.
*/
public function validateOptionsForm(&$form, FormStateInterface $form_state) { }
public function validateOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Provides the default form for submitting options.
*/
public function submitOptionsForm(&$form, FormStateInterface $form_state, &$options = []) { }
public function submitOptionsForm(&$form, FormStateInterface $form_state, &$options = []) {}
/**
* Determines if the administrator has the privileges to use this plugin.
*/
public function access() { return TRUE; }
public function access() {
return TRUE;
}
/**
* Blocks user input when the form is shown but we don´t have access.
@@ -92,7 +96,9 @@ abstract class ArgumentValidatorPluginBase extends PluginBase {
/**
* Performs validation for a given argument.
*/
public function validateArgument($arg) { return TRUE; }
public function validateArgument($arg) {
return TRUE;
}
/**
* Processes the summary arguments for displaying.
@@ -102,7 +108,7 @@ abstract class ArgumentValidatorPluginBase extends PluginBase {
* for a faster query. But there are use cases where you want to use
* the old value again, for example the summary.
*/
public function processSummaryArguments(&$args) { }
public function processSummaryArguments(&$args) {}
/**
* Returns a context definition for this argument.
@@ -111,7 +117,7 @@ abstract class ArgumentValidatorPluginBase extends PluginBase {
* A context definition that represents the argument or NULL if that is
* not possible.
*/
public function getContextDefinition() { }
public function getContextDefinition() {}
}
@@ -171,7 +171,7 @@ abstract class CachePluginBase extends PluginBase {
* All of the cached result data will be available in $view->result, as well,
* so all ids used in the query should be discoverable.
*/
public function postRender(&$output) { }
public function postRender(&$output) {}
/**
* Calculates and sets a cache ID used for the result cache.
@@ -190,7 +190,7 @@ abstract class CachePluginBase extends PluginBase {
$query = clone $build_info[$index];
$query->preExecute();
$build_info[$index] = [
'query' => (string)$query,
'query' => (string) $query,
'arguments' => $query->getArguments(),
];
}
@@ -28,7 +28,9 @@ class DefaultDisplay extends DisplayPluginBase {
* Determine if this display is the 'default' display which contains
* fallback settings
*/
public function isDefaultDisplay() { return TRUE; }
public function isDefaultDisplay() {
return TRUE;
}
/**
* The default execute handler fully renders the view.
@@ -234,7 +234,9 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
/**
* {@inheritdoc}
*/
public function isDefaultDisplay() { return FALSE; }
public function isDefaultDisplay() {
return FALSE;
}
/**
* {@inheritdoc}
@@ -394,7 +396,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
/**
* {@inheritdoc}
*/
public function attachTo(ViewExecutable $view, $display_id, array &$build) { }
public function attachTo(ViewExecutable $view, $display_id, array &$build) {}
/**
* {@inheritdoc}
@@ -665,17 +667,23 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
/**
* {@inheritdoc}
*/
public function hasPath() { return FALSE; }
public function hasPath() {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function usesLinkDisplay() { return !$this->hasPath(); }
public function usesLinkDisplay() {
return !$this->hasPath();
}
/**
* {@inheritdoc}
*/
public function usesExposedFormInBlock() { return $this->hasPath(); }
public function usesExposedFormInBlock() {
return $this->hasPath();
}
/**
* {@inheritdoc}
@@ -772,7 +780,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
return $this->default_display->getOption($option);
}
if (array_key_exists($option, $this->options)) {
if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
return $this->options[$option];
}
}
@@ -2051,7 +2059,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
/**
* {@inheritdoc}
*/
public function renderFilters() { }
public function renderFilters() {}
/**
* {@inheritdoc}
@@ -2115,7 +2123,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
'#cache' => &$this->view->element['#cache'],
];
$this->applyDisplayCachablityMetadata($this->view->element);
$this->applyDisplayCacheabilityMetadata($this->view->element);
return $element;
}
@@ -2126,7 +2134,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
* @param array $element
* The render array with updated cacheability metadata.
*/
protected function applyDisplayCachablityMetadata(array &$element) {
protected function applyDisplayCacheabilityMetadata(array &$element) {
/** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */
$cache = $this->getPlugin('cache');
@@ -2138,6 +2146,22 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
->applyTo($element);
}
/**
* Applies the cacheability of the current display to the given render array.
*
* @param array $element
* The render array with updated cacheability metadata.
*
* @deprecated in Drupal 8.4.0, will be removed before Drupal 9.0. Use
* DisplayPluginBase::applyDisplayCacheabilityMetadata instead.
*
* @see \Drupal\views\Plugin\views\display\DisplayPluginBase::applyDisplayCacheabilityMetadata()
*/
protected function applyDisplayCachablityMetadata(array &$element) {
@trigger_error('The DisplayPluginBase::applyDisplayCachablityMetadata method is deprecated since version 8.4 and will be removed in 9.0. Use DisplayPluginBase::applyDisplayCacheabilityMetadata instead.', E_USER_DEPRECATED);
$this->applyDisplayCacheabilityMetadata($element);
}
/**
* {@inheritdoc}
*/
@@ -2309,7 +2333,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
/**
* {@inheritdoc}
*/
public function execute() { }
public function execute() {}
/**
* {@inheritdoc}
@@ -2330,7 +2354,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
// of cacheability metadata (e.g.: cache contexts), so they can bubble up.
// Thus, we add the cacheability metadata first, then modify / remove the
// cache keys depending on the $cache argument.
$this->applyDisplayCachablityMetadata($this->view->element);
$this->applyDisplayCacheabilityMetadata($this->view->element);
if ($cache) {
$this->view->element['#cache'] += ['keys' => []];
// Places like \Drupal\views\ViewExecutable::setCurrentPage() set up an
@@ -2,6 +2,8 @@
namespace Drupal\views\Plugin\views\display;
use Drupal\Core\Database\Query\Condition;
/**
* The plugin that handles an EntityReference display.
*
@@ -88,13 +90,16 @@ class EntityReference extends DisplayPluginBase {
}
/**
* {@inheritdoc}
* Builds the view result as a renderable array.
*
* @return array
* Renderable array or empty array.
*/
public function render() {
if (!empty($this->view->result) && $this->view->style_plugin->evenEmpty()) {
return $this->view->style_plugin->render($this->view->result);
}
return '';
return [];
}
/**
@@ -131,7 +136,7 @@ class EntityReference extends DisplayPluginBase {
}
// Multiple search fields are OR'd together.
$conditions = db_or();
$conditions = new Condition('OR');
// Build the condition using the selected search fields.
foreach ($style_options['options']['search_fields'] as $field_id) {
@@ -106,7 +106,7 @@ class Feed extends PathPluginBase implements ResponseDisplayPluginInterface {
public function render() {
$build = $this->view->style_plugin->render($this->view->result);
$this->applyDisplayCachablityMetadata($build);
$this->applyDisplayCacheabilityMetadata($build);
return $build;
}
@@ -36,45 +36,45 @@ abstract class DisplayExtenderPluginBase extends PluginBase {
/**
* Provide a form to edit options for this plugin.
*/
public function defineOptionsAlter(&$options) { }
public function defineOptionsAlter(&$options) {}
/**
* Provide a form to edit options for this plugin.
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) { }
public function buildOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Validate the options form.
*/
public function validateOptionsForm(&$form, FormStateInterface $form_state) { }
public function validateOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Handle any special handling on the validate form.
*/
public function submitOptionsForm(&$form, FormStateInterface $form_state) { }
public function submitOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Set up any variables on the view prior to execution.
*/
public function preExecute() { }
public function preExecute() {}
/**
* Inject anything into the query that the display_extender handler needs.
*/
public function query() { }
public function query() {}
/**
* Provide the default summary for options in the views UI.
*
* This output is returned as an array.
*/
public function optionsSummary(&$categories, &$options) { }
public function optionsSummary(&$categories, &$options) {}
/**
* Static member function to list which sections are defaultable
* and what items each section contains.
*/
public function defaultableSections(&$sections, $section = NULL) { }
public function defaultableSections(&$sections, $section = NULL) {}
}
@@ -179,22 +179,22 @@ abstract class ExposedFormPluginBase extends PluginBase implements CacheableDepe
/**
* {@inheritdoc}
*/
public function preRender($values) { }
public function preRender($values) {}
/**
* {@inheritdoc}
*/
public function postRender(&$output) { }
public function postRender(&$output) {}
/**
* {@inheritdoc}
*/
public function preExecute() { }
public function preExecute() {}
/**
* {@inheritdoc}
*/
public function postExecute() { }
public function postExecute() {}
/**
* {@inheritdoc}
@@ -119,7 +119,7 @@ class Date extends FieldPluginBase {
'#type' => 'select',
'#title' => $this->t('Timezone'),
'#description' => $this->t('Timezone to be used for date output.'),
'#options' => ['' => $this->t('- Default site/user timezone -')] + system_time_zones(FALSE),
'#options' => ['' => $this->t('- Default site/user timezone -')] + system_time_zones(FALSE, TRUE),
'#default_value' => $this->options['timezone'],
];
foreach (array_merge(['custom'], array_keys($date_formats)) as $timezone_date_formats) {
@@ -143,7 +143,9 @@ class Date extends FieldPluginBase {
if ($value) {
$timezone = !empty($this->options['timezone']) ? $this->options['timezone'] : NULL;
$time_diff = REQUEST_TIME - $value; // will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence)
// Will be positive for a datetime in the past (ago), and negative for a
// datetime in the future (hence).
$time_diff = REQUEST_TIME - $value;
switch ($format) {
case 'raw time ago':
return $this->dateFormatter->formatTimeDiffSince($value, ['granularity' => is_numeric($custom_format) ? $custom_format : 2]);
@@ -23,6 +23,7 @@ use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\views\FieldAPIHandlerTrait;
use Drupal\views\Entity\Render\EntityFieldRenderer;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\DependentWithRemovalPluginInterface;
use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -34,7 +35,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*
* @ViewsField("field")
*/
class EntityField extends FieldPluginBase implements CacheableDependencyInterface, MultiItemsFieldHandlerInterface {
class EntityField extends FieldPluginBase implements CacheableDependencyInterface, MultiItemsFieldHandlerInterface, DependentWithRemovalPluginInterface {
use FieldAPIHandlerTrait;
use PluginDependencyTrait;
@@ -315,24 +316,33 @@ class EntityField extends FieldPluginBase implements CacheableDependencyInterfac
}
/**
* Gets the field storage of the used field.
* Gets the field storage definition.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface
* The field storage definition used by this handler.
*/
protected function getFieldStorageDefinition() {
$entity_type_id = $this->definition['entity_type'];
$field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
$field_storage = NULL;
// @todo Unify 'entity field'/'field_name' instead of converting back and
// forth. https://www.drupal.org/node/2410779
if (isset($this->definition['field_name'])) {
$field_storage = $field_storage_definitions[$this->definition['field_name']];
if (isset($this->definition['field_name']) && isset($field_storage_definitions[$this->definition['field_name']])) {
return $field_storage_definitions[$this->definition['field_name']];
}
elseif (isset($this->definition['entity field'])) {
$field_storage = $field_storage_definitions[$this->definition['entity field']];
if (isset($this->definition['entity field']) && isset($field_storage_definitions[$this->definition['entity field']])) {
return $field_storage_definitions[$this->definition['entity field']];
}
// The list of field storage definitions above does not include computed
// base fields, so we need to explicitly fetch a list of all base fields in
// order to support them.
// @see \Drupal\Core\Entity\EntityFieldManager::getFieldStorageDefinitions()
$base_fields = $this->entityManager->getBaseFieldDefinitions($entity_type_id);
if (isset($this->definition['field_name']) && isset($base_fields[$this->definition['field_name']])) {
return $base_fields[$this->definition['field_name']]->getFieldStorageDefinition();
}
return $field_storage;
}
/**
@@ -1042,10 +1052,13 @@ class EntityField extends FieldPluginBase implements CacheableDependencyInterfac
*/
public function getValue(ResultRow $values, $field = NULL) {
$entity = $this->getEntity($values);
// Retrieve the translated object.
$translated_entity = $this->getEntityFieldRenderer()->getEntityTranslation($entity, $values);
// Some bundles might not have a specific field, in which case the entity
// (potentially a fake one) doesn't have it either.
/** @var \Drupal\Core\Field\FieldItemListInterface $field_item_list */
$field_item_list = isset($entity->{$this->definition['field_name']}) ? $entity->{$this->definition['field_name']} : NULL;
$field_item_list = isset($translated_entity->{$this->definition['field_name']}) ? $translated_entity->{$this->definition['field_name']} : NULL;
if (!isset($field_item_list)) {
// There isn't anything we can do without a valid field.
@@ -1077,4 +1090,29 @@ class EntityField extends FieldPluginBase implements CacheableDependencyInterfac
}
}
/**
* {@inheritdoc}
*/
public function onDependencyRemoval(array $dependencies) {
// See if this handler is responsible for any of the dependencies being
// removed. If this is the case, indicate that this handler needs to be
// removed from the View.
$remove = FALSE;
// Get all the current dependencies for this handler.
$current_dependencies = $this->calculateDependencies();
foreach ($current_dependencies as $group => $dependency_list) {
// Check if any of the handler dependencies match the dependencies being
// removed.
foreach ($dependency_list as $config_key) {
if (isset($dependencies[$group]) && array_key_exists($config_key, $dependencies[$group])) {
// This handlers dependency matches a dependency being removed,
// indicate that this handler needs to be removed.
$remove = TRUE;
break 2;
}
}
}
return $remove;
}
}
@@ -46,7 +46,7 @@ class EntityOperations extends FieldPluginBase {
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* The entity manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/
@@ -865,10 +865,10 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
$optgroup_arguments = (string) t('Arguments');
$optgroup_fields = (string) t('Fields');
foreach ($previous as $id => $label) {
$options[$optgroup_fields]["{{ $id }}"] = substr(strrchr($label, ":"), 2 );
$options[$optgroup_fields]["{{ $id }}"] = substr(strrchr($label, ":"), 2);
}
// Add the field to the list of options.
$options[$optgroup_fields]["{{ {$this->options['id']} }}"] = substr(strrchr($this->adminLabel(), ":"), 2 );
$options[$optgroup_fields]["{{ {$this->options['id']} }}"] = substr(strrchr($this->adminLabel(), ":"), 2);
foreach ($this->view->display_handler->getHandlers('argument') as $arg => $handler) {
$options[$optgroup_arguments]["{{ arguments.$arg }}"] = $this->t('@argument title', ['@argument' => $handler->adminLabel()]);
@@ -1105,7 +1105,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
/**
* {@inheritdoc}
*/
public function preRender(&$values) { }
public function preRender(&$values) {}
/**
* {@inheritdoc}
@@ -1713,14 +1713,14 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
* field ID is terms, then the tokens might be {{ terms__tid }} and
* {{ terms__name }}.
*/
protected function addSelfTokens(&$tokens, $item) { }
protected function addSelfTokens(&$tokens, $item) {}
/**
* Document any special tokens this field might use for itself.
*
* @see addSelfTokens()
*/
protected function documentSelfTokens(&$tokens) { }
protected function documentSelfTokens(&$tokens) {}
/**
* {@inheritdoc}
@@ -90,7 +90,7 @@ abstract class PrerenderList extends FieldPluginBase implements MultiItemsFieldH
'#list_type' => $this->options['type'],
];
}
return drupal_render($render);
return \Drupal::service('renderer')->render($render);
}
}
@@ -46,7 +46,7 @@ class RenderedEntity extends FieldPluginBase implements CacheableDependencyInter
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* The entity manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/
@@ -49,7 +49,6 @@ class BooleanOperator extends FilterPluginBase {
public $accept_null = FALSE;
/**
* {@inheritdoc}
*/
@@ -56,6 +56,8 @@ class Bundle extends InOperator {
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info_service
* The bundle info service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, EntityTypeBundleInfoInterface $bundle_info_service) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
@@ -166,8 +166,10 @@ class Date extends NumericFilter {
$b = intval(strtotime($this->value['max'], 0));
if ($this->value['type'] == 'offset') {
$a = '***CURRENT_TIME***' . sprintf('%+d', $a); // keep sign
$b = '***CURRENT_TIME***' . sprintf('%+d', $b); // keep sign
// Keep sign.
$a = '***CURRENT_TIME***' . sprintf('%+d', $a);
// Keep sign.
$b = '***CURRENT_TIME***' . sprintf('%+d', $b);
}
// This is safe because we are manually scrubbing the values.
// It is necessary to do it this way because $a and $b are formulas when using an offset.
@@ -178,7 +180,8 @@ class Date extends NumericFilter {
protected function opSimple($field) {
$value = intval(strtotime($this->value['value'], 0));
if (!empty($this->value['type']) && $this->value['type'] == 'offset') {
$value = '***CURRENT_TIME***' . sprintf('%+d', $value); // keep sign
// Keep sign.
$value = '***CURRENT_TIME***' . sprintf('%+d', $value);
}
// This is safe because we are manually scrubbing the value.
// It is necessary to do it this way because $value is a formula when using an offset.
@@ -131,9 +131,11 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
'required' => ['default' => FALSE],
'remember' => ['default' => FALSE],
'multiple' => ['default' => FALSE],
'remember_roles' => ['default' => [
RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID,
]],
'remember_roles' => [
'default' => [
RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID,
],
],
],
];
@@ -174,7 +176,9 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
/**
* Determine if a filter can be exposed.
*/
public function canExpose() { return TRUE; }
public function canExpose() {
return TRUE;
}
/**
* Determine if a filter can be converted into a group.
@@ -302,18 +306,20 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
* Provide a list of options for the default operator form.
* Should be overridden by classes that don't override operatorForm
*/
public function operatorOptions() { return []; }
public function operatorOptions() {
return [];
}
/**
* Validate the operator form.
*/
protected function operatorValidate($form, FormStateInterface $form_state) { }
protected function operatorValidate($form, FormStateInterface $form_state) {}
/**
* Perform any necessary changes to the form values prior to storage.
* There is no need for this function to actually store the data.
*/
public function operatorSubmit($form, FormStateInterface $form_state) { }
public function operatorSubmit($form, FormStateInterface $form_state) {}
/**
* Shortcut to display the value form.
@@ -341,13 +347,13 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
/**
* Validate the options form.
*/
protected function valueValidate($form, FormStateInterface $form_state) { }
protected function valueValidate($form, FormStateInterface $form_state) {}
/**
* Perform any necessary changes to the form values prior to storage.
* There is no need for this function to actually store the data.
*/
protected function valueSubmit($form, FormStateInterface $form_state) { }
protected function valueSubmit($form, FormStateInterface $form_state) {}
/**
* Shortcut to display the exposed options form.
@@ -989,7 +995,9 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
'#default_value' => $this->options['group_info']['remember'],
];
$groups = ['All' => $this->t('- Any -')]; // The string '- Any -' will not be rendered see @theme_views_ui_build_group_filter_form
// The string '- Any -' will not be rendered.
// @see theme_views_ui_build_group_filter_form()
$groups = ['All' => $this->t('- Any -')];
// Provide 3 options to start when we are in a new group.
if (count($this->options['group_info']['group_items']) == 0) {
@@ -1199,7 +1207,6 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
}
/**
* Sanitizes the HTML select element's options.
*
@@ -52,6 +52,8 @@ class GroupByNumeric extends NumericFilter {
return $this->getField(parent::adminLabel($short));
}
public function canGroup() { return FALSE; }
public function canGroup() {
return FALSE;
}
}
@@ -88,7 +88,8 @@ class InOperator extends FilterPluginBase {
'#type' => 'checkbox',
'#title' => $this->t('Limit list to selected items'),
'#description' => $this->t('If checked, the only items presented to the user will be the ones selected here.'),
'#default_value' => !empty($this->options['expose']['reduce']), // safety
// Safety.
'#default_value' => !empty($this->options['expose']['reduce']),
];
}
@@ -0,0 +1,113 @@
<?php
namespace Drupal\views\Plugin\views\filter;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\views\Plugin\ViewsHandlerManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Filter to show only the latest revision of an entity.
*
* @ingroup views_filter_handlers
*
* @ViewsFilter("latest_revision")
*/
class LatestRevision extends FilterPluginBase implements ContainerFactoryPluginInterface {
/**
* Entity Type Manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Views Handler Plugin Manager.
*
* @var \Drupal\views\Plugin\ViewsHandlerManager
*/
protected $joinHandler;
/**
* Constructs a new LatestRevision.
*
* @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\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity Type Manager Service.
* @param \Drupal\views\Plugin\ViewsHandlerManager $join_handler
* Views Handler Plugin Manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ViewsHandlerManager $join_handler) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
$this->joinHandler = $join_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration, $plugin_id, $plugin_definition,
$container->get('entity_type.manager'),
$container->get('plugin.manager.views.join')
);
}
/**
* {@inheritdoc}
*/
public function adminSummary() {
}
/**
* {@inheritdoc}
*/
protected function operatorForm(&$form, FormStateInterface $form_state) {
}
/**
* {@inheritdoc}
*/
public function canExpose() {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function query() {
/** @var \Drupal\views\Plugin\views\query\Sql $query */
$query = $this->query;
$query_base_table = $this->relationship ?: $this->view->storage->get('base_table');
$entity_type = $this->entityTypeManager->getDefinition($this->getEntityType());
$keys = $entity_type->getKeys();
$definition = [
'table' => $query_base_table,
'type' => 'LEFT',
'field' => $keys['id'],
'left_table' => $query_base_table,
'left_field' => $keys['id'],
'extra' => [
['left_field' => $keys['revision'], 'field' => $keys['revision'], 'operator' => '>'],
],
];
$join = $this->joinHandler->createInstance('standard', $definition);
$join_table_alias = $query->addTable($query_base_table, $this->relationship, $join);
$query->addWhere($this->options['group'], "$join_table_alias.{$keys['id']}", NULL, 'IS NULL');
}
}
@@ -129,6 +129,9 @@ class ManyToOne extends InOperator {
if (empty($this->value)) {
return;
}
// Form API returns unchecked options in the form of option_id => 0. This
// breaks the generated query for "is all of" filters so we remove them.
$this->value = array_filter($this->value, 'static::arrayFilterZero');
$this->helper->addFilter();
}
@@ -2,6 +2,7 @@
namespace Drupal\views\Plugin\views\filter;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Form\FormStateInterface;
/**
@@ -265,7 +266,7 @@ class StringFilter extends FilterPluginBase {
}
protected function opContainsWord($field) {
$where = $this->operator == 'word' ? db_or() : db_and();
$where = $this->operator == 'word' ? new Condition('OR') : new Condition('AND');
// Don't filter on empty strings.
if (empty($this->value)) {
@@ -0,0 +1,106 @@
<?php
namespace Drupal\views\Plugin\views\join;
use Drupal\Core\Database\Query\SelectInterface;
/**
* Implementation for the "field OR language" join.
*
* If the extra conditions contain either ".langcode" or ".bundle", they will be
* grouped and joined with OR instead of AND. The entire group will then be
* joined to the other conditions with AND.
*
* This is needed for configurable fields that are translatable on some bundles
* and untranslatable on others. The correct field values to fetch in this case
* have a langcode that matches the entity record *or* have a bundle on which
* the field is untranslatable. Thus, the entity base table (or data table, or
* revision data table, respectively) must join the field data table (or field
* revision table) on a matching langcode *or* a bundle where the field is
* untranslatable. The following example views data achieves this for a node
* field named 'field_tags' which is translatable on an 'article' node type, but
* not on the 'news' and 'page' node types:
*
* @code
* $data['node__field_tags']['table']['join']['node_field_data'] = [
* 'join_id' => 'field_or_language_join',
* 'table' => 'node__field_tags',
* 'left_field' => 'nid',
* 'field' => 'entity_id',
* 'extra' => [
* [
* 'field' => 'deleted',
* 'value' => 0,
* 'numeric' => TRUE,
* ],
* [
* 'left_field' => 'langcode',
* 'field' => 'langcode',
* ],
* [
* 'field' => 'bundle',
* 'value' => ['news', 'page'],
* ],
* ],
* ];
* @endcode
*
* The resulting join condition for this example would be the following:
*
* @code
* ON node__field_tags.deleted = 0
* AND (
* node_field_data.langcode = node__field_tags.langcode
* OR node__field.tags.bundle IN ['news', 'page']
* )
* @endcode
*
* @see views_field_default_views_data()
*
* @ingroup views_join_handlers
*
* @ViewsJoin("field_or_language_join")
*/
class FieldOrLanguageJoin extends JoinPluginBase {
/**
* {@inheritdoc}
*/
protected function joinAddExtra(&$arguments, &$condition, $table, SelectInterface $select_query, $left_table = NULL) {
if (empty($this->extra)) {
return;
}
if (is_array($this->extra)) {
$extras = [];
foreach ($this->extra as $extra) {
$extras[] = $this->buildExtra($extra, $arguments, $table, $select_query, $left_table);
}
// Remove and store the langcode OR bundle join condition extra.
$language_bundle_conditions = [];
foreach ($extras as $key => $extra) {
if (strpos($extra, '.langcode') !== FALSE || strpos($extra, '.bundle') !== FALSE) {
$language_bundle_conditions[] = $extra;
unset($extras[$key]);
}
}
if (count($extras) > 1) {
$condition .= ' AND (' . implode(' ' . $this->extraOperator . ' ', $extras) . ')';
}
elseif ($extras) {
$condition .= ' AND ' . array_shift($extras);
}
// Tack on the langcode OR bundle join condition extra.
if (!empty($language_bundle_conditions)) {
$condition .= ' AND (' . implode(' OR ', $language_bundle_conditions) . ')';
}
}
elseif (is_string($this->extra)) {
$condition .= " AND ($this->extra)";
}
}
}
@@ -2,6 +2,7 @@
namespace Drupal\views\Plugin\views\join;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Plugin\PluginBase;
/**
@@ -261,12 +262,13 @@ class JoinPluginBase extends PluginBase implements JoinPluginInterface {
}
if ($this->leftTable) {
$left = $view_query->getTableInfo($this->leftTable);
$left_field = "$left[alias].$this->leftField";
$left_table = $view_query->getTableInfo($this->leftTable);
$left_field = "$left_table[alias].$this->leftField";
}
else {
// This can be used if left_field is a formula or something. It should be used only *very* rarely.
$left_field = $this->leftField;
$left_table = NULL;
}
$condition = "$left_field = $table[alias].$this->field";
@@ -274,89 +276,123 @@ class JoinPluginBase extends PluginBase implements JoinPluginInterface {
// Tack on the extra.
if (isset($this->extra)) {
if (is_array($this->extra)) {
$extras = [];
foreach ($this->extra as $info) {
// Do not require 'value' to be set; allow for field syntax instead.
$info += [
'value' => NULL,
];
// Figure out the table name. Remember, only use aliases provided
// if at all possible.
$join_table = '';
if (!array_key_exists('table', $info)) {
$join_table = $table['alias'] . '.';
}
elseif (isset($info['table'])) {
// If we're aware of a table alias for this table, use the table
// alias instead of the table name.
if (isset($left) && $left['table'] == $info['table']) {
$join_table = $left['alias'] . '.';
}
else {
$join_table = $info['table'] . '.';
}
}
// Convert a single-valued array of values to the single-value case,
// and transform from IN() notation to = notation
if (is_array($info['value']) && count($info['value']) == 1) {
$info['value'] = array_shift($info['value']);
}
if (is_array($info['value'])) {
// We use an SA-CORE-2014-005 conformant placeholder for our array
// of values. Also, note that the 'IN' operator is implicit.
// @see https://www.drupal.org/node/2401615.
$operator = !empty($info['operator']) ? $info['operator'] : 'IN';
$placeholder = ':views_join_condition_' . $select_query->nextPlaceholder() . '[]';
$placeholder_sql = "( $placeholder )";
}
else {
// With a single value, the '=' operator is implicit.
$operator = !empty($info['operator']) ? $info['operator'] : '=';
$placeholder = $placeholder_sql = ':views_join_condition_' . $select_query->nextPlaceholder();
}
// Set 'field' as join table field if available or set 'left field' as
// join table field is not set.
if (isset($info['field'])) {
$join_table_field = "$join_table$info[field]";
// Allow the value to be set either with the 'value' element or
// with 'left_field'.
if (isset($info['left_field'])) {
$placeholder_sql = "$left[alias].$info[left_field]";
}
else {
$arguments[$placeholder] = $info['value'];
}
}
// Set 'left field' as join table field is not set.
else {
$join_table_field = "$left[alias].$info[left_field]";
$arguments[$placeholder] = $info['value'];
}
// Render out the SQL fragment with parameters.
$extras[] = "$join_table_field $operator $placeholder_sql";
}
if ($extras) {
if (count($extras) == 1) {
$condition .= ' AND ' . array_shift($extras);
}
else {
$condition .= ' AND (' . implode(' ' . $this->extraOperator . ' ', $extras) . ')';
}
}
}
elseif ($this->extra && is_string($this->extra)) {
$condition .= " AND ($this->extra)";
}
$this->joinAddExtra($arguments, $condition, $table, $select_query, $left_table);
}
$select_query->addJoin($this->type, $right_table, $table['alias'], $condition, $arguments);
}
/**
* Adds the extras to the join condition.
*
* @param array $arguments
* Array of query arguments.
* @param string $condition
* The condition to be built.
* @param array $table
* The right table.
* @param \Drupal\Core\Database\Query\SelectInterface $select_query
* The current select query being built.
* @param array $left_table
* The left table.
*/
protected function joinAddExtra(&$arguments, &$condition, $table, SelectInterface $select_query, $left_table = NULL) {
if (is_array($this->extra)) {
$extras = [];
foreach ($this->extra as $info) {
$extras[] = $this->buildExtra($info, $arguments, $table, $select_query, $left_table);
}
if ($extras) {
if (count($extras) == 1) {
$condition .= ' AND ' . array_shift($extras);
}
else {
$condition .= ' AND (' . implode(' ' . $this->extraOperator . ' ', $extras) . ')';
}
}
}
elseif ($this->extra && is_string($this->extra)) {
$condition .= " AND ($this->extra)";
}
}
/**
* Builds a single extra condition.
*
* @param array $info
* The extra information. See JoinPluginBase::$extra for details.
* @param array $arguments
* Array of query arguments.
* @param array $table
* The right table.
* @param \Drupal\Core\Database\Query\SelectInterface $select_query
* The current select query being built.
* @param array $left
* The left table.
*
* @return string
* The extra condition
*/
protected function buildExtra($info, &$arguments, $table, SelectInterface $select_query, $left) {
// Do not require 'value' to be set; allow for field syntax instead.
$info += [
'value' => NULL,
];
// Figure out the table name. Remember, only use aliases provided
// if at all possible.
$join_table = '';
if (!array_key_exists('table', $info)) {
$join_table = $table['alias'] . '.';
}
elseif (isset($info['table'])) {
// If we're aware of a table alias for this table, use the table
// alias instead of the table name.
if (isset($left) && $left['table'] == $info['table']) {
$join_table = $left['alias'] . '.';
}
else {
$join_table = $info['table'] . '.';
}
}
// Convert a single-valued array of values to the single-value case,
// and transform from IN() notation to = notation
if (is_array($info['value']) && count($info['value']) == 1) {
$info['value'] = array_shift($info['value']);
}
if (is_array($info['value'])) {
// We use an SA-CORE-2014-005 conformant placeholder for our array
// of values. Also, note that the 'IN' operator is implicit.
// @see https://www.drupal.org/node/2401615.
$operator = !empty($info['operator']) ? $info['operator'] : 'IN';
$placeholder = ':views_join_condition_' . $select_query->nextPlaceholder() . '[]';
$placeholder_sql = "( $placeholder )";
}
else {
// With a single value, the '=' operator is implicit.
$operator = !empty($info['operator']) ? $info['operator'] : '=';
$placeholder = $placeholder_sql = ':views_join_condition_' . $select_query->nextPlaceholder();
}
// Set 'field' as join table field if available or set 'left field' as
// join table field is not set.
if (isset($info['field'])) {
$join_table_field = "$join_table$info[field]";
// Allow the value to be set either with the 'value' element or
// with 'left_field'.
if (isset($info['left_field'])) {
$placeholder_sql = "$left[alias].$info[left_field]";
}
else {
$arguments[$placeholder] = $info['value'];
}
}
// Set 'left field' as join table field is not set.
else {
$join_table_field = "$left[alias].$info[left_field]";
$arguments[$placeholder] = $info['value'];
}
// Render out the SQL fragment with parameters.
return "$join_table_field $operator $placeholder_sql";
}
}
/**
* @}
*/
@@ -114,12 +114,12 @@ abstract class PagerPluginBase extends PluginBase {
/**
* Provide the default form form for validating options
*/
public function validateOptionsForm(&$form, FormStateInterface $form_state) { }
public function validateOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Provide the default form form for submitting options
*/
public function submitOptionsForm(&$form, FormStateInterface $form_state) { }
public function submitOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Return a string to display as the clickable title for the
@@ -156,6 +156,8 @@ abstract class PagerPluginBase extends PluginBase {
if (!empty($this->options['offset'])) {
$this->total_items -= $this->options['offset'];
}
// Prevent from being negative.
$this->total_items = max(0, $this->total_items);
return $this->total_items;
}
@@ -173,22 +175,22 @@ abstract class PagerPluginBase extends PluginBase {
*
* This is called during the build phase and can directly modify the query.
*/
public function query() { }
public function query() {}
/**
* Perform any needed actions just prior to the query executing.
*/
public function preExecute(&$query) { }
public function preExecute(&$query) {}
/**
* Perform any needed actions just after the query executing.
*/
public function postExecute(&$result) { }
public function postExecute(&$result) {}
/**
* Perform any needed actions just before rendering.
*/
public function preRender(&$result) { }
public function preRender(&$result) {}
/**
* Return the renderable array of the pager.
@@ -199,7 +201,7 @@ abstract class PagerPluginBase extends PluginBase {
* Any extra GET parameters that should be retained, such as exposed
* input.
*/
public function render($input) { }
public function render($input) {}
/**
* Determine if there are more records available.
@@ -211,11 +213,11 @@ abstract class PagerPluginBase extends PluginBase {
&& $this->total_items > (intval($this->current_page) + 1) * $this->getItemsPerPage();
}
public function exposedFormAlter(&$form, FormStateInterface $form_state) { }
public function exposedFormAlter(&$form, FormStateInterface $form_state) {}
public function exposedFormValidate(&$form, FormStateInterface $form_state) { }
public function exposedFormValidate(&$form, FormStateInterface $form_state) {}
public function exposedFormSubmit(&$form, FormStateInterface $form_state, &$exclude) { }
public function exposedFormSubmit(&$form, FormStateInterface $form_state, &$exclude) {}
public function usesExposed() {
return FALSE;
@@ -362,7 +362,7 @@ abstract class SqlBase extends PagerPluginBase implements CacheableDependencyInt
public function exposedFormValidate(&$form, FormStateInterface $form_state) {
if (!$form_state->isValueEmpty('offset') && trim($form_state->getValue('offset'))) {
if (!is_numeric($form_state->getValue('offset')) || $form_state->getValue('offset') < 0) {
$form_state->setErrorByName('offset', $this->t('Offset must be an number greater or equal than 0.'));
$form_state->setErrorByName('offset', $this->t('Offset must be a number greater than or equal to 0.'));
}
}
}
@@ -54,7 +54,7 @@ abstract class QueryPluginBase extends PluginBase implements CacheableDependency
* @param $get_count
* Provide a countquery if this is true, otherwise provide a normal query.
*/
public function query($get_count = FALSE) { }
public function query($get_count = FALSE) {}
/**
* Let modules modify the query just prior to finalizing it.
@@ -62,7 +62,7 @@ abstract class QueryPluginBase extends PluginBase implements CacheableDependency
* @param view $view
* The view which is executed.
*/
public function alter(ViewExecutable $view) { }
public function alter(ViewExecutable $view) {}
/**
* Builds the necessary info to execute the query.
@@ -70,7 +70,7 @@ abstract class QueryPluginBase extends PluginBase implements CacheableDependency
* @param view $view
* The view which is executed.
*/
public function build(ViewExecutable $view) { }
public function build(ViewExecutable $view) {}
/**
* Executes the query and fills the associated view object with according
@@ -85,7 +85,7 @@ abstract class QueryPluginBase extends PluginBase implements CacheableDependency
* @param view $view
* The view which is executed.
*/
public function execute(ViewExecutable $view) { }
public function execute(ViewExecutable $view) {}
/**
* Add a signature to the query, if such a thing is feasible.
@@ -96,18 +96,18 @@ abstract class QueryPluginBase extends PluginBase implements CacheableDependency
* @param view $view
* The view which is executed.
*/
public function addSignature(ViewExecutable $view) { }
public function addSignature(ViewExecutable $view) {}
/**
* Get aggregation info for group by queries.
*
* If NULL, aggregation is not allowed.
*/
public function getAggregationInfo() { }
public function getAggregationInfo() {}
public function validateOptionsForm(&$form, FormStateInterface $form_state) { }
public function validateOptionsForm(&$form, FormStateInterface $form_state) {}
public function submitOptionsForm(&$form, FormStateInterface $form_state) { }
public function submitOptionsForm(&$form, FormStateInterface $form_state) {}
public function summaryTitle() {
return $this->t('Settings');
@@ -5,6 +5,7 @@ namespace Drupal\views\Plugin\views\query;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
@@ -830,7 +831,7 @@ class Sql extends QueryPluginBase {
* @code
* $this->query->addWhere(
* $this->options['group'],
* db_or()
* (new Condition('OR'))
* ->condition($field, $value, 'NOT IN')
* ->condition($field, $value, 'IS NULL')
* );
@@ -1056,13 +1057,13 @@ class Sql extends QueryPluginBase {
$has_arguments = FALSE;
$has_filter = FALSE;
$main_group = db_and();
$filter_group = $this->groupOperator == 'OR' ? db_or() : db_and();
$main_group = new Condition('AND');
$filter_group = $this->groupOperator == 'OR' ? new Condition('OR') : new Condition('AND');
foreach ($this->$where as $group => $info) {
if (!empty($info['conditions'])) {
$sub_group = $info['type'] == 'OR' ? db_or() : db_and();
$sub_group = $info['type'] == 'OR' ? new Condition('OR') : new Condition('AND');
foreach ($info['conditions'] as $clause) {
if ($clause['operator'] == 'formula') {
$has_condition = TRUE;
@@ -1468,7 +1469,7 @@ class Sql extends QueryPluginBase {
// Setup the result row objects.
$view->result = iterator_to_array($result);
array_walk($view->result, function(ResultRow $row, $index) {
array_walk($view->result, function (ResultRow $row, $index) {
$row->index = $index;
});
@@ -178,7 +178,7 @@ class GroupwiseMax extends RelationshipPluginBase {
* - subquery_order: either ASC or DESC.
*
* @return string
* The subquery SQL string, ready for use in the main query.
* The subquery SQL string, ready for use in the main query.
*/
protected function leftQuery($options) {
// Either load another view, or create one on the fly.
@@ -37,7 +37,7 @@ class EntityReference extends Fields {
parent::buildOptionsForm($form, $form_state);
// Expand the description of the 'Inline field' checkboxes.
$form['inline']['#description'] .= '<br />' . $this->t("<strong>Note:</strong> In 'Entity Reference' displays, all fields will be displayed inline unless an explicit selection of inline fields is made here." );
$form['inline']['#description'] .= '<br />' . $this->t("<strong>Note:</strong> In 'Entity Reference' displays, all fields will be displayed inline unless an explicit selection of inline fields is made here.");
}
/**
@@ -122,13 +122,13 @@ abstract class RowPluginBase extends PluginBase {
/**
* Validate the options form.
*/
public function validateOptionsForm(&$form, FormStateInterface $form_state) { }
public function validateOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* Perform any necessary changes to the form values prior to storage.
* There is no need for this function to actually store the data.
*/
public function submitOptionsForm(&$form, FormStateInterface $form_state) { }
public function submitOptionsForm(&$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
@@ -151,7 +151,7 @@ abstract class RowPluginBase extends PluginBase {
* @param $result
* The full array of results from the query.
*/
public function preRender($result) { }
public function preRender($result) {}
/**
* Render a row object. This usually passes through to a theme template
@@ -28,7 +28,9 @@ abstract class SortPluginBase extends HandlerBase implements CacheableDependency
/**
* Determine if a sort can be exposed.
*/
public function canExpose() { return TRUE; }
public function canExpose() {
return TRUE;
}
/**
* Called to add the sort to a query.
@@ -175,9 +177,9 @@ abstract class SortPluginBase extends HandlerBase implements CacheableDependency
}
}
protected function sortValidate(&$form, FormStateInterface $form_state) { }
protected function sortValidate(&$form, FormStateInterface $form_state) {}
public function sortSubmit(&$form, FormStateInterface $form_state) { }
public function sortSubmit(&$form, FormStateInterface $form_state) {}
/**
* Provide a list of options for the default sort form.
@@ -385,7 +385,7 @@ abstract class StylePluginBase extends PluginBase {
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param string $type
* The display type, either block or page.
* The display type, either block or page.
*/
public function wizardForm(&$form, FormStateInterface $form_state, $type) {
}
@@ -413,13 +413,15 @@ abstract class StylePluginBase extends PluginBase {
* interfere with the sorts. If so it should build; if it returns
* any non-TRUE value, normal sorting will NOT be added to the query.
*/
public function buildSort() { return TRUE; }
public function buildSort() {
return TRUE;
}
/**
* Called by the view builder to let the style build a second set of
* sorts that will come after any other sorts in the view.
*/
public function buildSortPost() { }
public function buildSortPost() {}
/**
* Allow the style to do stuff before each row is rendered.
@@ -788,7 +790,7 @@ abstract class StylePluginBase extends PluginBase {
* @param $index
* The index count of the row.
* @param $field
* The id of the field.
* The id of the field.
*/
public function getFieldValue($index, $field) {
$this->view->row_index = $index;
@@ -160,7 +160,7 @@ class Table extends StylePluginBase implements CacheableDependencyInterface {
* display has listed due to access control or other changes.
*
* @return array
* An array of all the sanitized columns.
* An array of all the sanitized columns.
*/
public function sanitizeColumns($columns, $fields = NULL) {
$sanitized = [];
@@ -3,7 +3,7 @@
namespace Drupal\views\Tests;
use Drupal\Core\Cache\Cache;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\ViewExecutable;
use Symfony\Component\HttpFoundation\Request;
@@ -5,6 +5,11 @@ namespace Drupal\views\Tests;
use Drupal\Component\Render\MarkupInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Tests\Views\FieldTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\language\Entity\ContentLanguageSettings;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\views\Views;
/**
* Tests the Field Views data.
@@ -13,10 +18,27 @@ use Drupal\field\Tests\Views\FieldTestBase;
*/
class FieldApiDataTest extends FieldTestBase {
protected function setUp() {
parent::setUp();
/**
* {@inheritdoc}
*/
public static $modules = ['language'];
$field_names = $this->setUpFieldStorages(1);
/**
* {@inheritdoc}
*/
public static $testViews = ['test_field_config_translation_filter'];
/**
* The nodes used by the translation filter tests.
*
* @var \Drupal\node\NodeInterface[]
*/
protected $translationNodes;
protected function setUp() {
parent::setUp(FALSE);
$field_names = $this->setUpFieldStorages(4);
// Attach the field to nodes only.
$field = [
@@ -43,6 +65,109 @@ class FieldApiDataTest extends FieldTestBase {
];
$nodes[] = $this->drupalCreateNode($edit);
}
$bundles = [];
$bundles[] = $bundle = NodeType::create(['type' => 'bundle1']);
$bundle->save();
$bundles[] = $bundle = NodeType::create(['type' => 'bundle2']);
$bundle->save();
// Make the first field translatable on all bundles.
$field = FieldConfig::create([
'field_name' => $field_names[1],
'entity_type' => 'node',
'bundle' => $bundles[0]->id(),
'translatable' => TRUE,
]);
$field->save();
$field = FieldConfig::create([
'field_name' => $field_names[1],
'entity_type' => 'node',
'bundle' => $bundles[1]->id(),
'translatable' => TRUE,
]);
$field->save();
// Make the second field not translatable on any bundle.
$field = FieldConfig::create([
'field_name' => $field_names[2],
'entity_type' => 'node',
'bundle' => $bundles[0]->id(),
'translatable' => FALSE,
]);
$field->save();
$field = FieldConfig::create([
'field_name' => $field_names[2],
'entity_type' => 'node',
'bundle' => $bundles[1]->id(),
'translatable' => FALSE,
]);
$field->save();
// Make the last field translatable on some bundles.
$field = FieldConfig::create([
'field_name' => $field_names[3],
'entity_type' => 'node',
'bundle' => $bundles[0]->id(),
'translatable' => TRUE,
]);
$field->save();
$field = FieldConfig::create([
'field_name' => $field_names[3],
'entity_type' => 'node',
'bundle' => $bundles[1]->id(),
'translatable' => FALSE,
]);
$field->save();
// Create some example content.
ConfigurableLanguage::create([
'id' => 'es',
])->save();
ConfigurableLanguage::create([
'id' => 'fr',
])->save();
$config = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundles[0]->id());
$config->setDefaultLangcode('es')
->setLanguageAlterable(TRUE)
->save();
$config = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundles[1]->id());
$config->setDefaultLangcode('es')
->setLanguageAlterable(TRUE)
->save();
$node = Node::create([
'title' => 'Test title ' . $bundles[0]->id(),
'type' => $bundles[0]->id(),
'langcode' => 'es',
$field_names[1] => 'field name 1: es',
$field_names[2] => 'field name 2: es',
$field_names[3] => 'field name 3: es',
]);
$node->save();
$this->translationNodes[] = $node;
$translation = $node->addTranslation('fr');
$translation->{$field_names[1]}->value = 'field name 1: fr';
$translation->{$field_names[3]}->value = 'field name 3: fr';
$translation->title->value = $node->title->value;
$translation->save();
$node = Node::create([
'title' => 'Test title ' . $bundles[1]->id(),
'type' => $bundles[1]->id(),
'langcode' => 'es',
$field_names[1] => 'field name 1: es',
$field_names[2] => 'field name 2: es',
$field_names[3] => 'field name 3: es',
]);
$node->save();
$this->translationNodes[] = $node;
$translation = $node->addTranslation('fr');
$translation->{$field_names[1]}->value = 'field name 1: fr';
$translation->title->value = $node->title->value;
$translation->save();
}
/**
@@ -137,4 +262,118 @@ class FieldApiDataTest extends FieldTestBase {
return $data;
}
/**
* Tests filtering entries with different translatabilty.
*/
public function testEntityFieldFilter() {
$map = [
'nid' => 'nid',
'langcode' => 'langcode',
];
$view = Views::getView('test_field_config_translation_filter');
// Filter by 'field name 1: es'.
$view->setDisplay('embed_1');
$this->executeView($view);
$expected = [
[
'nid' => $this->translationNodes[0]->id(),
'langcode' => 'es',
],
[
'nid' => $this->translationNodes[1]->id(),
'langcode' => 'es',
],
];
$this->assertIdenticalResultset($view, $expected, $map);
$view->destroy();
// Filter by 'field name 1: fr'.
$view->setDisplay('embed_2');
$this->executeView($view);
$expected = [
[
'nid' => $this->translationNodes[0]->id(),
'langcode' => 'fr',
],
[
'nid' => $this->translationNodes[1]->id(),
'langcode' => 'fr',
],
];
$this->assertIdenticalResultset($view, $expected, $map);
$view->destroy();
// Filter by 'field name 2: es'.
$view->setDisplay('embed_3');
$this->executeView($view);
$expected = [
[
'nid' => $this->translationNodes[0]->id(),
'langcode' => 'es',
],
[
'nid' => $this->translationNodes[0]->id(),
'langcode' => 'fr',
],
[
'nid' => $this->translationNodes[1]->id(),
'langcode' => 'es',
],
[
'nid' => $this->translationNodes[1]->id(),
'langcode' => 'fr',
],
];
$this->assertIdenticalResultset($view, $expected, $map);
$view->destroy();
// Filter by 'field name 2: fr', which doesn't exist.
$view->setDisplay('embed_4');
$this->executeView($view);
$expected = [];
$this->assertIdenticalResultset($view, $expected, $map);
$view->destroy();
// Filter by 'field name 3: es'.
$view->setDisplay('embed_5');
$this->executeView($view);
$expected = [
[
'nid' => $this->translationNodes[0]->id(),
'langcode' => 'es',
],
[
'nid' => $this->translationNodes[1]->id(),
'langcode' => 'es',
],
// Why is this one returned?
[
'nid' => $this->translationNodes[1]->id(),
'langcode' => 'fr',
],
];
$this->assertIdenticalResultset($view, $expected, $map);
$view->destroy();
// Filter by 'field name 3: fr'.
$view->setDisplay('embed_6');
$this->executeView($view);
$expected = [
[
'nid' => $this->translationNodes[0]->id(),
'langcode' => 'fr',
],
];
$this->assertIdenticalResultset($view, $expected, $map);
$view->destroy();
}
}
@@ -1,6 +1,7 @@
<?php
namespace Drupal\views\Tests\Handler;
@trigger_error('\Drupal\views\Tests\Handler\HandlerTestBase is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\views\Functional\ViewTestBase', E_USER_DEPRECATED);
use Drupal\views\Tests\ViewTestBase;
@@ -45,10 +45,12 @@ class DisplayFeedTest extends PluginTestBase {
$node_title = 'This "cool" & "neat" article\'s title';
$node = $this->drupalCreateNode([
'title' => $node_title,
'body' => [0 => [
'value' => 'A paragraph',
'format' => filter_default_format(),
]],
'body' => [
0 => [
'value' => 'A paragraph',
'format' => filter_default_format(),
],
],
]);
// Test the site name setting.
@@ -103,10 +105,12 @@ class DisplayFeedTest extends PluginTestBase {
$node_title = 'This "cool" & "neat" article\'s title';
$this->drupalCreateNode([
'title' => $node_title,
'body' => [0 => [
'value' => 'A paragraph',
'format' => filter_default_format(),
]],
'body' => [
0 => [
'value' => 'A paragraph',
'format' => filter_default_format(),
],
],
]);
$this->drupalGet('test-feed-display-fields.xml');
@@ -1,6 +1,7 @@
<?php
namespace Drupal\views\Tests\Plugin;
@trigger_error('\Drupal\views\Tests\Plugin\PluginTestBase is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\views\Functional\ViewTestBase', E_USER_DEPRECATED);
use Drupal\views\Tests\ViewTestBase;
@@ -126,6 +126,8 @@ abstract class ViewKernelTestBase extends KernelTestBase {
/**
* Returns the schema definition.
*
* @internal
*/
protected function schemaDefinition() {
return ViewTestData::schemaDefinition();
@@ -1,6 +1,7 @@
<?php
namespace Drupal\views\Tests;
@trigger_error('\Drupal\views\Tests\ViewTestBase is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\views\Functional\ViewTestBase', E_USER_DEPRECATED);
use Drupal\Core\Database\Query\SelectInterface;
@@ -95,7 +96,7 @@ abstract class ViewTestBase extends WebTestBase {
*
* @param string $id
* The HTML ID of the button
* @param string $label
* @param string $expected_label
* The expected label for the button.
* @param string $message
* (optional) A custom message to display with the assertion. If no custom
@@ -131,6 +132,8 @@ abstract class ViewTestBase extends WebTestBase {
/**
* Returns the schema definition.
*
* @internal
*/
protected function schemaDefinition() {
return ViewTestData::schemaDefinition();
@@ -58,6 +58,8 @@ class ViewTestData {
/**
* Returns the schema definition.
*
* @internal
*/
public static function schemaDefinition() {
$schema['views_test_data'] = [
@@ -80,7 +82,8 @@ class ViewTestData {
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0],
'default' => 0,
],
'job' => [
'description' => "The person's job",
'type' => 'varchar',
@@ -1,6 +1,7 @@
<?php
namespace Drupal\views\Tests\Wizard;
@trigger_error('\Drupal\views\Tests\Wizard\WizardTestBase is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use \Drupal\Tests\views\Functional\Wizard\WizardTestBase', E_USER_DEPRECATED);
use Drupal\views\Tests\ViewTestBase;
+66 -38
View File
@@ -2,9 +2,9 @@
namespace Drupal\views;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Tags;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\views\Plugin\views\display\DisplayRouterInterface;
@@ -17,9 +17,14 @@ use Symfony\Component\Routing\Exception\RouteNotFoundException;
*
* 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.
*
* This class does not implement the Serializable interface since problems
* occurred when using the serialize method.
*
* @see https://www.drupal.org/node/2849674
* @see https://bugs.php.net/bug.php?id=66052
*/
class ViewExecutable implements \Serializable {
use DependencySerializationTrait;
class ViewExecutable {
/**
* The config entity in which the view is stored.
@@ -434,6 +439,13 @@ class ViewExecutable implements \Serializable {
*/
protected $baseEntityType;
/**
* Holds all necessary data for proper unserialization.
*
* @var array
*/
protected $serializationData;
/**
* Constructs a new ViewExecutable object.
*
@@ -788,7 +800,7 @@ class ViewExecutable implements \Serializable {
// Ensure the requested display exists.
if (!$this->displayHandlers->has($display_id)) {
debug(format_string('setDisplay() called with invalid display ID "@display".', ['@display' => $display_id]));
trigger_error(new FormattableMarkup('setDisplay() called with invalid display ID "@display".', ['@display' => $display_id]), E_USER_WARNING);
return FALSE;
}
@@ -1327,8 +1339,8 @@ class ViewExecutable implements \Serializable {
* @todo Some filter needs this function, even it is internal.
*
* @param string $key
* The type of handlers (filter etc.) which should be iterated over to
* build the relationship and query information.
* The type of handlers (filter etc.) which should be iterated over to build
* the relationship and query information.
*/
public function _build($key) {
$handlers = &$this->$key;
@@ -1746,7 +1758,7 @@ class ViewExecutable implements \Serializable {
// We can't use choose_display() here because that function
// calls this one.
$displays = (array)$displays;
$displays = (array) $displays;
foreach ($displays as $display_id) {
if ($this->displayHandlers->has($display_id)) {
if (($display = $this->displayHandlers->get($display_id)) && $display->access($account)) {
@@ -2466,52 +2478,68 @@ class ViewExecutable implements \Serializable {
}
/**
* {@inheritdoc}
* Magic method implementation to serialize the view executable.
*
* @return array
* The names of all variables that should be serialized.
*/
public function serialize() {
return serialize([
// Only serialize the storage entity ID.
$this->storage->id(),
$this->current_display,
$this->args,
$this->current_page,
$this->exposed_input,
$this->exposed_raw_input,
$this->exposed_data,
$this->dom_id,
$this->executed,
]);
public function __sleep() {
// Limit to only the required data which is needed to properly restore the
// state during unserialization.
$this->serializationData = [
'storage' => $this->storage->id(),
'views_data' => $this->viewsData->_serviceId,
'route_provider' => $this->routeProvider->_serviceId,
'current_display' => $this->current_display,
'args' => $this->args,
'current_page' => $this->current_page,
'exposed_input' => $this->exposed_input,
'exposed_raw_input' => $this->exposed_raw_input,
'exposed_data' => $this->exposed_data,
'dom_id' => $this->dom_id,
'executed' => $this->executed,
];
return ['serializationData'];
}
/**
* {@inheritdoc}
* Magic method implementation to unserialize the view executable.
*/
public function unserialize($serialized) {
list($storage, $current_display, $args, $current_page, $exposed_input, $exposed_raw_input, $exposed_data, $dom_id, $executed) = unserialize($serialized);
// There are cases, like in testing, where we don't have a container
public function __wakeup() {
// There are cases, like in testing where we don't have a container
// available.
if (\Drupal::hasContainer()) {
$this->setRequest(\Drupal::request());
if (\Drupal::hasContainer() && !empty($this->serializationData)) {
// Load and reference the storage.
$this->storage = \Drupal::entityTypeManager()->getStorage('view')
->load($this->serializationData['storage']);
$this->storage->set('executable', $this);
// Attach all necessary services.
$this->user = \Drupal::currentUser();
$this->viewsData = \Drupal::service($this->serializationData['views_data']);
$this->routeProvider = \Drupal::service($this->serializationData['route_provider']);
$this->storage = \Drupal::entityManager()->getStorage('view')->load($storage);
$this->setDisplay($current_display);
$this->setArguments($args);
$this->setCurrentPage($current_page);
$this->setExposedInput($exposed_input);
$this->exposed_data = $exposed_data;
$this->exposed_raw_input = $exposed_raw_input;
$this->dom_id = $dom_id;
// Restore the state of this executable.
if ($request = \Drupal::request()) {
$this->setRequest($request);
}
$this->setDisplay($this->serializationData['current_display']);
$this->setArguments($this->serializationData['args']);
$this->setCurrentPage($this->serializationData['current_page']);
$this->setExposedInput($this->serializationData['exposed_input']);
$this->exposed_data = $this->serializationData['exposed_data'];
$this->exposed_raw_input = $this->serializationData['exposed_raw_input'];
$this->dom_id = $this->serializationData['dom_id'];
$this->initHandlers();
// If the display was previously executed, execute it now.
if ($executed) {
if ($this->serializationData['executed']) {
$this->execute($this->current_display);
}
}
// Unset serializationData since it serves no further purpose.
unset($this->serializationData);
}
}
+3 -3
View File
@@ -283,8 +283,8 @@ class Views {
* Filters the views on status. Can either be 'all' (default), 'enabled' or
* 'disabled'
* @param mixed $exclude_view
* view or current display to exclude
* either a
* View or current display to exclude.
* Either a:
* - views object (containing $exclude_view->storage->name and $exclude_view->current_display)
* - views name as string: e.g. my_view
* - views name and display id (separated by ':'): e.g. my_view:default
@@ -512,7 +512,7 @@ class Views {
throw new \Exception('Invalid plugin type used. Valid types are "plugin" or "handler".');
}
return array_keys(array_filter(static::$plugins, function($plugin_type) use ($type) {
return array_keys(array_filter(static::$plugins, function ($plugin_type) use ($type) {
return $plugin_type == $type;
}));
}