Update drupal modules

This commit is contained in:
Robert 2022-07-07 14:41:31 +02:00
parent 18683393be
commit d1f95f27f0
580 changed files with 39863 additions and 1002 deletions

View File

@ -74,7 +74,7 @@ INSTALLATION
* Install the Chaos Tool Suite module as you would normally install a
contributed Drupal module. Visit
https://www.drupal.org/docs/8/extending-drupal-8/installing-drupal-8-modules
https://www.drupal.org/docs/extending-drupal/installing-modules
for further information.

View File

@ -4,7 +4,7 @@ description: 'Provides a number of utility and helper APIs for Drupal developers
package: Chaos tool suite
core_version_requirement: ^8.8 || ^9
# Information added by Drupal.org packaging script on 2021-06-16
version: '8.x-3.7'
# Information added by Drupal.org packaging script on 2022-07-01
version: '8.x-3.8'
project: 'ctools'
datestamp: 1623822132
datestamp: 1656633726

View File

@ -5,8 +5,10 @@
* Provides utility and helper APIs for Drupal developers and site builders.
*/
use Drupal\Core\Url;
use Drupal\Core\Entity\Plugin\Condition\EntityBundle as CoreEntityBundle;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\ctools\Plugin\Condition\EntityBundle;
/**
* Implements hook_theme().
@ -47,7 +49,7 @@ function ctools_theme($existing, $type, $theme, $path) {
* @param $variables
*/
function template_preprocess_ctools_wizard_trail(&$variables) {
/** @var $wizard \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface */
/** @var \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface $wizard */
$wizard = $variables['wizard'];
$cached_values = $variables['cached_values'];
$trail = $variables['trail'];
@ -64,7 +66,7 @@ function template_preprocess_ctools_wizard_trail(&$variables) {
* @param $variables
*/
function template_preprocess_ctools_wizard_trail_links(&$variables) {
/** @var $wizard \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface */
/** @var \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface $wizard */
$wizard = $variables['wizard'];
$cached_values = $variables['cached_values'];
$trail = $variables['trail'];
@ -91,8 +93,16 @@ function ctools_condition_info_alter(&$definitions) {
if (isset($definitions['node_type']) && $definitions['node_type']['class'] == 'Drupal\node\Plugin\Condition\NodeType') {
$definitions['node_type']['class'] = 'Drupal\ctools\Plugin\Condition\NodeType';
}
// Replace all generic entity bundle conditions classes if they are unaltered,
// these exist in Drupal 9.3+.
foreach ($definitions as $id => $definition) {
if (strpos($id, 'entity_bundle:') === 0 && $definition['class'] == CoreEntityBundle::class) {
$definitions[$id]['class'] = EntityBundle::class;
}
}
}
/**
* Implements hook_help().
@ -115,7 +125,8 @@ function ctools_help($route_name, RouteMatchInterface $route_match) {
$output .= '<li>' . t('Modal dialog -- tool to make it simple to put a form in a modal dialog.') . '</li>';
$output .= '<li>' . t('Dependent -- a simple form widget to make form items appear and disappear based upon the selections in another item.') . '</li>';
$output .= '<li>' . t('Content -- pluggable content types used as panes in Panels and other modules like Dashboard.') . '</li>';
$output .= '<li>' . t('Form wizard -- an API to make multi-step forms much easier.') . '</li>';+ $output .= '<li>' . t('CSS tools -- tools to cache and sanitize CSS easily to make user-input CSS safe.') . '</li>';
$output .= '<li>' . t('Form wizard -- an API to make multi-step forms much easier.') . '</li>';
$output .= '<li>' . t('CSS tools -- tools to cache and sanitize CSS easily to make user-input CSS safe.') . '</li>';
$output .= '</ul>';
return $output;
}

View File

@ -6,7 +6,7 @@ core_version_requirement: ^8.8 || ^9
dependencies:
- ctools:ctools
# Information added by Drupal.org packaging script on 2021-06-16
version: '8.x-3.7'
# Information added by Drupal.org packaging script on 2022-07-01
version: '8.x-3.8'
project: 'ctools'
datestamp: 1623822132
datestamp: 1656633726

View File

@ -11,8 +11,7 @@
* In general, users should be using the core block types instead.
*/
function ctools_block_plugin_filter_block_alter(array &$definitions, array $extra, $consumer) {
$moduleHandler = \Drupal::service('module_handler');
if ($moduleHandler->moduleExists('layout_builder')) {
if ($consumer == 'layout_builder') {
foreach ($definitions as $label => $definition) {
if ($definition['provider'] == 'ctools_block') {
unset($definitions[$label]);

View File

@ -108,7 +108,7 @@ class EntityField extends BlockBase implements ContextAwarePluginInterface, Cont
$this->formatterManager = $formatter_manager;
// Get the entity type and field name from the plugin id.
list (, $entity_type_id, $field_name) = explode(':', $plugin_id);
[, $entity_type_id, $field_name] = explode(':', $plugin_id);
$this->entityTypeId = $entity_type_id;
$this->fieldName = $field_name;
@ -190,7 +190,7 @@ class EntityField extends BlockBase implements ContextAwarePluginInterface, Cont
return [
'formatter' => [
'label' => 'above',
'type' => isset($field_type_definition['default_formatter']) ? $field_type_definition['default_formatter'] : '',
'type' => $field_type_definition['default_formatter'] ?? '',
'settings' => [],
'third_party_settings' => [],
'weight' => 0,
@ -316,7 +316,12 @@ class EntityField extends BlockBase implements ContextAwarePluginInterface, Cont
*/
protected function getFieldStorageDefinition() {
if (empty($this->fieldStorageDefinition)) {
$field_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
// Some base fields have no storage.
$field_definitions = array_merge(
$this->entityFieldManager->getBaseFieldDefinitions($this->entityTypeId),
$this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId)
);
$this->fieldStorageDefinition = $field_definitions[$this->fieldName];
}
return $this->fieldStorageDefinition;

View File

@ -16,7 +16,13 @@ class EntityFieldDeriver extends EntityDeriverBase {
public function getDerivativeDefinitions($base_plugin_definition) {
$entity_type_labels = $this->entityTypeRepository->getEntityTypeLabels();
foreach ($this->entityFieldManager->getFieldMap() as $entity_type_id => $entity_field_map) {
foreach ($this->entityFieldManager->getFieldStorageDefinitions($entity_type_id) as $field_storage_definition) {
// Some base fields have no storage.
/** @var \Drupal\Core\Field\FieldDefinitionInterface[] $field_storage_definitions */
$field_storage_definitions = array_merge(
$this->entityFieldManager->getBaseFieldDefinitions($entity_type_id),
$this->entityFieldManager->getFieldStorageDefinitions($entity_type_id)
);
foreach ($field_storage_definitions as $field_storage_definition) {
$field_name = $field_storage_definition->getName();
// The blocks are based on fields. However, we are looping through field

View File

@ -12,7 +12,7 @@ dependencies:
- drupal:user
features: true
# Information added by Drupal.org packaging script on 2021-06-16
version: '8.x-3.7'
# Information added by Drupal.org packaging script on 2022-07-01
version: '8.x-3.8'
project: 'ctools'
datestamp: 1623822132
datestamp: 1656633726

View File

@ -14,7 +14,7 @@ class EntityFieldBlockTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block', 'ctools_block', 'ctools_block_field_test'];
protected static $modules = ['block', 'ctools_block', 'ctools_block_field_test'];
/**
* {@inheritdoc}

View File

@ -3,7 +3,7 @@ core_version_requirement: ^8.8 || ^9
type: module
description: 'Allows an entity type to borrow the fields and display configuration of another entity type.'
# Information added by Drupal.org packaging script on 2021-06-16
version: '8.x-3.7'
# Information added by Drupal.org packaging script on 2022-07-01
version: '8.x-3.8'
project: 'ctools'
datestamp: 1623822132
datestamp: 1656633726

View File

@ -7,7 +7,7 @@ dependencies:
- drupal:image
- drupal:text
# Information added by Drupal.org packaging script on 2021-06-16
version: '8.x-3.7'
# Information added by Drupal.org packaging script on 2022-07-01
version: '8.x-3.8'
project: 'ctools'
datestamp: 1623822132
datestamp: 1656633726

View File

@ -21,7 +21,7 @@ class DisplayTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
protected static $modules = [
'block',
'block_content',
'ctools_entity_mask',

View File

@ -16,7 +16,7 @@ class EntityMaskTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
protected static $modules = [
'block',
'block_content',
'ctools_entity_mask',

View File

@ -8,7 +8,7 @@ dependencies:
- drupal:block
- drupal:views
# Information added by Drupal.org packaging script on 2021-06-16
version: '8.x-3.7'
# Information added by Drupal.org packaging script on 2022-07-01
version: '8.x-3.8'
project: 'ctools'
datestamp: 1623822132
datestamp: 1656633726

View File

@ -2,14 +2,10 @@
namespace Drupal\ctools_views\Plugin\Display;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\Block\ViewsBlock;
use Drupal\views\Plugin\views\display\Block as CoreBlock;
use Drupal\views\Plugin\ViewsHandlerManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides a Block display plugin.
@ -32,44 +28,18 @@ class Block extends CoreBlock {
*/
protected $request;
/**
* Constructs a new Block instance.
*
* @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
* The entity manager.
* @param \Drupal\Core\Block\BlockManagerInterface $block_manager
* The block manager.
* @param \Drupal\views\Plugin\ViewsHandlerManager $filter_manager
* The views filter plugin manager.
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, BlockManagerInterface $block_manager, ViewsHandlerManager $filter_manager, Request $request) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $block_manager);
$this->filterManager = $filter_manager;
$this->request = $request;
}
/**
* {@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.block'),
$container->get('plugin.manager.views.filter'),
$container->get('request_stack')->getCurrentRequest()
);
/**
* @var \Drupal\ctools_views\Plugin\Display\Block
*/
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->filterManager = $container->get('plugin.manager.views.filter');
$instance->request = $container->get('request_stack')->getCurrentRequest();
return $instance;
}
/**
@ -141,7 +111,7 @@ class Block extends CoreBlock {
$form['override']['pager_offset'] = [
'#type' => 'number',
'#title' => $this->t('Pager offset'),
'#default_value' => isset($block_configuration['pager_offset']) ? $block_configuration['pager_offset'] : 0,
'#default_value' => $block_configuration['pager_offset'] ?? 0,
'#description' => $this->t('For example, set this to 3 and the first 3 items will not be displayed.'),
];
}
@ -157,7 +127,7 @@ class Block extends CoreBlock {
'#type' => 'radios',
'#title' => $this->t('Pager'),
'#options' => $pager_options,
'#default_value' => isset($block_configuration['pager']) ? $block_configuration['pager'] : 'view',
'#default_value' => $block_configuration['pager'] ?? 'view',
];
}
@ -245,7 +215,7 @@ class Block extends CoreBlock {
if (!empty($allow_settings['disable_filters'])) {
$items = [];
foreach ((array) $this->getOption('filters') as $filter_name => $item) {
$item['value'] = isset($block_configuration["filter"][$filter_name]['value']) ? $block_configuration["filter"][$filter_name]['value'] : '';
$item['value'] = $block_configuration["filter"][$filter_name]['value'] ?? '';
$items[$filter_name] = $item;
}
$this->setOption('filters', $items);
@ -390,7 +360,7 @@ class Block extends CoreBlock {
$allow_settings = array_filter($this->getOption('allow'));
$config = $block->getConfiguration();
list(, $display_id) = explode('-', $block->getDerivativeId(), 2);
[, $display_id] = explode('-', $block->getDerivativeId(), 2);
// Change pager offset settings based on block configuration.
if (!empty($allow_settings['offset']) && isset($config['pager_offset'])) {
@ -459,19 +429,6 @@ class Block extends CoreBlock {
return $config['value'][$filter['expose']['identifier']];
}
/**
* {@inheritdoc}
*/
public function usesExposed() {
$filters = $this->getHandlers('filter');
foreach ($filters as $filter) {
if ($filter->isExposed() && !empty($filter->exposedInfo())) {
return TRUE;
}
}
return FALSE;
}
/**
* Exposed widgets.
*
@ -501,8 +458,8 @@ class Block extends CoreBlock {
* Return the more weight
*/
public static function sortFieldsByWeight($a, $b) {
$a_weight = isset($a['weight']) ? $a['weight'] : 0;
$b_weight = isset($b['weight']) ? $b['weight'] : 0;
$a_weight = $a['weight'] ?? 0;
$b_weight = $b['weight'] ?? 0;
if ($a_weight == $b_weight) {
return 0;
}

View File

@ -12,7 +12,7 @@ dependencies:
- drupal:node
- drupal:taxonomy
# Information added by Drupal.org packaging script on 2021-06-16
version: '8.x-3.7'
# Information added by Drupal.org packaging script on 2022-07-01
version: '8.x-3.8'
project: 'ctools'
datestamp: 1623822132
datestamp: 1656633726

View File

@ -21,7 +21,7 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
*
* @var array
*/
public static $modules = ['ctools_views', 'ctools_views_test_views'];
protected static $modules = ['ctools_views', 'ctools_views_test_views'];
/**
* Views used by this test.
@ -65,7 +65,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit = [];
$edit['region'] = 'sidebar_first';
$edit['settings[override][items_per_page]'] = 0;
$this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme, $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme);
$this->submitForm($edit, $this->t('Save block'));
// Assert items per page default settings.
$this->drupalGet('<front>');
@ -78,7 +79,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit = [];
$edit['region'] = 'sidebar_first';
$edit['settings[override][items_per_page]'] = 2;
$this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager', $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager');
$this->submitForm($edit, $this->t('Save block'));
$block = $this->storage->load('views_block__ctools_views_test_view_block_pager');
$config = $block->getPlugin()->getConfiguration();
@ -110,7 +112,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit = [];
$edit['region'] = 'sidebar_first';
$edit['settings[override][items_per_page]'] = 0;
$this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme, $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme);
$this->submitForm($edit, $this->t('Save block'));
// Assert pager offset default settings.
$this->drupalGet('<front>');
@ -125,7 +128,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit['region'] = 'sidebar_first';
$edit['settings[override][items_per_page]'] = 0;
$edit['settings[override][pager_offset]'] = 1;
$this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager', $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager');
$this->submitForm($edit, $this->t('Save block'));
$block = $this->storage->load('views_block__ctools_views_test_view_block_pager');
$config = $block->getPlugin()->getConfiguration();
@ -156,7 +160,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit = [];
$edit['region'] = 'sidebar_first';
$edit['settings[override][items_per_page]'] = 0;
$this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme, $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme);
$this->submitForm($edit, $this->t('Save block'));
// Assert pager default settings.
$this->drupalGet('<front>');
@ -168,7 +173,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit['region'] = 'sidebar_first';
$edit['settings[override][items_per_page]'] = 0;
$edit['settings[override][pager]'] = 'some';
$this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager', $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager');
$this->submitForm($edit, $this->t('Save block'));
$block = $this->storage->load('views_block__ctools_views_test_view_block_pager');
$config = $block->getPlugin()->getConfiguration();
@ -184,7 +190,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit['region'] = 'sidebar_first';
$edit['settings[override][items_per_page]'] = 0;
$edit['settings[override][pager]'] = 'none';
$this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager', $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager');
$this->submitForm($edit, $this->t('Save block'));
$block = $this->storage->load('views_block__ctools_views_test_view_block_pager');
$config = $block->getPlugin()->getConfiguration();
@ -209,7 +216,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
// Add block to sidebar_first region with default settings.
$edit = [];
$edit['region'] = 'sidebar_first';
$this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_fields/' . $default_theme, $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_fields/' . $default_theme);
$this->submitForm($edit, $this->t('Save block'));
// Assert hide_fields default settings.
$this->drupalGet('<front>');
@ -219,7 +227,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit = [];
$edit['region'] = 'sidebar_first';
$edit['settings[override][order_fields][id][hide]'] = 1;
$this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_fields', $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/manage/views_block__ctools_views_test_view_block_fields');
$this->submitForm($edit, $this->t('Save block'));
$block = $this->storage->load('views_block__ctools_views_test_view_block_fields');
$config = $block->getPlugin()->getConfiguration();
@ -244,7 +253,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
// Add block to sidebar_first region with default settings.
$edit = [];
$edit['region'] = 'sidebar_first';
$this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_fields/' . $default_theme, $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_fields/' . $default_theme);
$this->submitForm($edit, $this->t('Save block'));
// Assert sort_fields default settings.
$this->drupalGet('<front>');
@ -261,7 +271,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit['settings[override][order_fields][created][weight]'] = -47;
$edit['settings[override][order_fields][id][weight]'] = -46;
$edit['settings[override][order_fields][name_1][weight]'] = -45;
$this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_fields', $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/manage/views_block__ctools_views_test_view_block_fields');
$this->submitForm($edit, $this->t('Save block'));
$block = $this->storage->load('views_block__ctools_views_test_view_block_fields');
$config = $block->getPlugin()->getConfiguration();
@ -298,7 +309,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
// Add block to sidebar_first region with default settings.
$edit = [];
$edit['region'] = 'sidebar_first';
$this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_filter/' . $default_theme, $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_filter/' . $default_theme);
$this->submitForm($edit, $this->t('Save block'));
// Assert disable_filters default settings.
$this->drupalGet('<front>');
@ -311,7 +323,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit['region'] = 'sidebar_first';
$edit['settings[override][filters][status][disable]'] = 1;
$edit['settings[override][filters][job][disable]'] = 1;
$this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_filter', $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/manage/views_block__ctools_views_test_view_block_filter');
$this->submitForm($edit, $this->t('Save block'));
$block = $this->storage->load('views_block__ctools_views_test_view_block_filter');
$config = $block->getPlugin()->getConfiguration();
@ -337,7 +350,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
// Add block to sidebar_first region with default settings.
$edit = [];
$edit['region'] = 'sidebar_first';
$this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_sort/' . $default_theme, $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_sort/' . $default_theme);
$this->submitForm($edit, $this->t('Save block'));
// Assert configure_sorts default settings.
$this->drupalGet('<front>');
@ -350,7 +364,8 @@ class CToolsViewsBasicViewBlockTest extends UITestBase {
$edit = [];
$edit['region'] = 'sidebar_first';
$edit['settings[override][sort][id][order]'] = 'DESC';
$this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_sort', $edit, $this->t('Save block'));
$this->drupalGet('admin/structure/block/manage/views_block__ctools_views_test_view_block_sort');
$this->submitForm($edit, $this->t('Save block'));
$block = $this->storage->load('views_block__ctools_views_test_view_block_sort');
$config = $block->getPlugin()->getConfiguration();

View File

@ -4,6 +4,14 @@ namespace Drupal\ctools\Access;
use Drupal\Core\Session\AccountInterface;
/**
* Ctools Access Interface.
*/
interface AccessInterface {
/**
* Provides the access method for accounts.
*/
public function access(AccountInterface $account);
}

View File

@ -10,7 +10,9 @@ use Drupal\ctools\Access\AccessInterface as CToolsAccessInterface;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Symfony\Component\Routing\Route;
/**
* Tempstore Access for ctools.
*/
class TempstoreAccess implements CoreAccessInterface {
/**
@ -20,17 +22,29 @@ class TempstoreAccess implements CoreAccessInterface {
*/
protected $tempstore;
/**
* Constructor for access to shared tempstore.
*/
public function __construct(SharedTempStoreFactory $tempstore) {
$this->tempstore = $tempstore;
}
/**
* Retreive the tempstore factory.
*/
protected function getTempstore() {
return $this->tempstore;
}
/**
* Access method to find if user has access to a particular tempstore.
*
* @param \Symfony\Component\Routing\Route $route
* @param \Drupal\Core\Routing\RouteMatchInterface $match
* @param \Drupal\Core\Session\AccountInterface $account
*
* @return \Drupal\Core\Access\AccessResultAllowed|\Drupal\Core\Access\AccessResultForbidden
*/
public function access(Route $route, RouteMatchInterface $match, AccountInterface $account) {
$tempstore_id = $match->getParameter('tempstore_id') ? $match->getParameter('tempstore_id') : $route->getDefault('tempstore_id');
$id = $match->getParameter($route->getRequirement('_ctools_access'));

View File

@ -4,10 +4,14 @@ namespace Drupal\ctools\Ajax;
use Drupal\Core\Ajax\OpenModalDialogCommand;
/**
*
*/
class OpenModalWizardCommand extends OpenModalDialogCommand {
/**
*
*/
public function __construct($object, $tempstore_id, array $parameters = [], array $dialog_options = [], $settings = NULL) {
// Instantiate the wizard class properly.
$parameters += [
@ -16,7 +20,7 @@ class OpenModalWizardCommand extends OpenModalDialogCommand {
'step' => NULL,
];
$form = \Drupal::service('ctools.wizard.factory')->getWizardForm($object, $parameters, TRUE);
$title = isset($form['#title']) ? $form['#title'] : '';
$title = $form['#title'] ?? '';
$content = $form;
parent::__construct($title, $content, $dialog_options, $settings);

View File

@ -2,15 +2,16 @@
namespace Drupal\ctools;
/**
* Interface for Constraint Conditions
*/
interface ConstraintConditionInterface {
/**
* Applies relevant constraints for this condition to the injected contexts.
*
* @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
*
* @return null
* Contexts to apply.
*/
public function applyConstraints(array $contexts = []);
@ -18,8 +19,7 @@ interface ConstraintConditionInterface {
* Removes constraints for this condition from the injected contexts.
*
* @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
*
* @return null
* Contexts to remove.
*/
public function removeConstraints(array $contexts = []);

View File

@ -17,6 +17,7 @@ class AutomaticContext extends Context {
* Returns TRUE if this context is automatic and always available.
*
* @return bool
* If the context is automatic or not.
*/
public function isAutomatic() {
return TRUE;

View File

@ -2,5 +2,7 @@
namespace Drupal\ctools;
/**
*
*/
class ContextNotFoundException extends \Exception {}

View File

@ -40,7 +40,7 @@ class WizardEntityFormController extends WizardFormController {
*/
protected function getFormArgument(RouteMatchInterface $route_match) {
$form_arg = $route_match->getRouteObject()->getDefault('_entity_wizard');
list($entity_type_id, $operation) = explode('.', $form_arg);
[$entity_type_id, $operation] = explode('.', $form_arg);
$definition = $this->entityTypeManager->getDefinition($entity_type_id);
$handlers = $definition->getHandlerClasses();
if (empty($handlers['wizard'][$operation])) {

View File

@ -24,7 +24,7 @@ class WizardFormController extends FormController {
/**
* Tempstore Factory for keeping track of values in each step of the wizard.
*
* @var \Drupal\Core\TempStore\SharedTempStoreFactory
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $tempstore;

View File

@ -6,6 +6,9 @@ use Drupal\Core\Block\BlockPluginInterface;
use Drupal\ctools\Plugin\BlockVariantInterface;
use Symfony\Component\EventDispatcher\Event;
/**
*
*/
class BlockVariantEvent extends Event {
/**

View File

@ -20,23 +20,31 @@ class WizardEvent extends Event {
*/
protected $values;
/**
*
*/
public function __construct(FormWizardInterface $wizard, $values) {
$this->wizard = $wizard;
$this->values = $values;
}
/**
*
*/
public function getWizard() {
return $this->wizard;
}
/**
*
*/
public function getValues() {
return $this->values;
}
/**
*
*/
public function setValues($values) {
$this->values = $values;
return $this;

View File

@ -14,6 +14,7 @@ trait AjaxFormTrait {
* Gets attributes for use with an AJAX modal.
*
* @return array
* The array of attributes.
*/
public static function getAjaxAttributes() {
return [
@ -29,6 +30,7 @@ trait AjaxFormTrait {
* Gets attributes for use with an add button AJAX modal.
*
* @return array
* The array of attributes.
*/
public static function getAjaxButtonAttributes() {
return NestedArray::mergeDeep(AjaxFormTrait::getAjaxAttributes(), [

View File

@ -47,7 +47,14 @@ abstract class ConditionConfigure extends FormBase {
return new static($container->get('tempstore.shared'), $container->get('plugin.manager.condition'));
}
/**
* Constructor for Condition Configuration.
*
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* The Tempstore Factory.
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
* The Plugin Manager.
*/
public function __construct(SharedTempStoreFactory $tempstore, PluginManagerInterface $manager) {
$this->tempstore = $tempstore;
$this->manager = $manager;
@ -76,7 +83,7 @@ abstract class ConditionConfigure extends FormBase {
$instance = $this->manager->createInstance($condition, []);
}
$form_state->setTemporaryValue('gathered_contexts', $this->getContexts($cached_values));
/** @var $instance \Drupal\Core\Condition\ConditionInterface */
/** @var \Drupal\Core\Condition\ConditionInterface $instance */
$form = $instance->buildConfigurationForm($form, $form_state);
if (isset($id)) {
// Conditionally set this form element so that we can update or add.
@ -104,17 +111,17 @@ abstract class ConditionConfigure extends FormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
/** @var $instance \Drupal\Core\Condition\ConditionInterface */
/** @var \Drupal\Core\Condition\ConditionInterface $instance */
$instance = $form_state->getValue('instance');
$instance->submitConfigurationForm($form, $form_state);
$conditions = $this->getConditions($cached_values);
if ($instance instanceof ContextAwarePluginInterface) {
/** @var $instance \Drupal\Core\Plugin\ContextAwarePluginInterface */
/** @var \Drupal\Core\Plugin\ContextAwarePluginInterface $instance */
$context_mapping = $form_state->hasValue('context_mapping') ? $form_state->getValue('context_mapping') : [];
$instance->setContextMapping($context_mapping);
}
if ($instance instanceof ConstraintConditionInterface) {
/** @var $instance \Drupal\ctools\ConstraintConditionInterface */
/** @var \Drupal\ctools\ConstraintConditionInterface $instance */
$instance->applyConstraints($this->getContexts($cached_values));
}
if ($form_state->hasValue('id')) {
@ -125,15 +132,25 @@ abstract class ConditionConfigure extends FormBase {
}
$cached_values = $this->setConditions($cached_values, $conditions);
$this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
$form_state->setRedirect($route_name, $route_parameters);
}
/**
* Ajax callback to save tempstore values.
*
* @param array $form
* The Drupal Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The Form state.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* Ajax values from tempstore.
*/
public function ajaxSave(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
$url = Url::fromRoute($route_name, $route_parameters);
$response->addCommand(new RedirectCommand($url->toString()));
$response->addCommand(new CloseModalDialogCommand());
@ -143,29 +160,34 @@ abstract class ConditionConfigure extends FormBase {
/**
* Document the route name and parameters for redirect after submission.
*
* @param $cached_values
* @param array $cached_values
* Cached values to get the route info.
*
* @return array
* In the format of
* return ['route.name', ['machine_name' => $this->machine_name, 'step' => 'step_name']];
* In the format of [
* 'route.name',
* ['machine_name' => $this->machine_name, 'step' => 'step_name']];
*/
abstract protected function getParentRouteInfo($cached_values);
/**
* Custom logic for retrieving the conditions array from cached_values.
*
* @param $cached_values
* @param array $cached_values
* Cached values to get contexts from.
*
* @return array
* The conditions attached to cached values.
*/
abstract protected function getConditions($cached_values);
abstract protected function getConditions(array $cached_values);
/**
* Custom logic for setting the conditions array in cached_values.
*
* @param $cached_values
* @param array $cached_values
* Cached values that will get set.
*
* @param $conditions
* @param mixed $conditions
* The conditions to set within the cached values.
*
* @return mixed
@ -176,10 +198,12 @@ abstract class ConditionConfigure extends FormBase {
/**
* Custom logic for retrieving the contexts array from cached_values.
*
* @param $cached_values
* @param array $cached_values
* Cached values to get contexts from.
*
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
* The contexts from cache.
*/
abstract protected function getContexts($cached_values);
abstract protected function getContexts(array $cached_values);
}

View File

@ -11,7 +11,9 @@ use Drupal\ctools\ConstraintConditionInterface;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Delete Condition Confirmation Form.
*/
abstract class ConditionDelete extends ConfirmFormBase {
/**
@ -46,7 +48,14 @@ abstract class ConditionDelete extends ConfirmFormBase {
return new static($container->get('tempstore.shared'), $container->get('plugin.manager.condition'));
}
/**
* Condition Delete Confirmation Form Constructor.
*
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* The Tempstore Factory.
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
* The Plugin Manager.
*/
public function __construct(SharedTempStoreFactory $tempstore, PluginManagerInterface $manager) {
$this->tempstore = $tempstore;
$this->manager = $manager;
@ -89,7 +98,7 @@ abstract class ConditionDelete extends ConfirmFormBase {
public function submitForm(array &$form, FormStateInterface $form_state) {
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
$conditions = $this->getConditions($cached_values);
/** @var $instance \Drupal\ctools\ConstraintConditionInterface */
/** @var \Drupal\ctools\ConstraintConditionInterface $instance */
$instance = $this->manager->createInstance($conditions[$this->id]['id'], $conditions[$this->id]);
if ($instance instanceof ConstraintConditionInterface) {
$instance->removeConstraints($this->getContexts($cached_values));
@ -97,11 +106,21 @@ abstract class ConditionDelete extends ConfirmFormBase {
unset($conditions[$this->id]);
$cached_values = $this->setConditions($cached_values, $conditions);
$this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
$form_state->setRedirect($route_name, $route_parameters);
}
/**
* Gets the delete question.
*
* @param $id
* Condition ID.
* @param $cached_values
* Cached Context values.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
* The confirmation delete question.
*/
public function getQuestion($id = NULL, $cached_values = NULL) {
$condition = $this->getConditions($cached_values)[$id];
return $this->t('Are you sure you want to delete the @label condition?', [
@ -150,7 +169,7 @@ abstract class ConditionDelete extends ConfirmFormBase {
*/
public function getCancelUrl() {
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
return new Url($route_name, $route_parameters);
}
@ -171,43 +190,47 @@ abstract class ConditionDelete extends ConfirmFormBase {
/**
* Document the route name and parameters for redirect after submission.
*
* @param $cached_values
* @param array $cached_values
* The cached context values.
*
* @return array
* In the format of
* return ['route.name', ['machine_name' => $this->machine_name, 'step' => 'step_name]];
*/
abstract protected function getParentRouteInfo($cached_values);
abstract protected function getParentRouteInfo(array $cached_values);
/**
* Custom logic for retrieving the conditions array from cached_values.
*
* @param $cached_values
* @param array $cached_values
* The cached context values.
*
* @return array
* The conditions.
*/
abstract protected function getConditions($cached_values);
abstract protected function getConditions(array $cached_values);
/**
* Custom logic for setting the conditions array in cached_values.
*
* @param $cached_values
*
* @param $conditions
* @param array $cached_values
* The cached context values.
* @param mixed $conditions
* The conditions to set within the cached values.
*
* @return mixed
* Return the $cached_values
*/
abstract protected function setConditions($cached_values, $conditions);
abstract protected function setConditions(array $cached_values, mixed $conditions);
/**
* Custom logic for retrieving the contexts array from cached_values.
*
* @param $cached_values
* @param array $cached_values
* The cached context values.
*
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
*/
abstract protected function getContexts($cached_values);
abstract protected function getContexts(array $cached_values);
}

View File

@ -16,27 +16,35 @@ use Drupal\Core\TempStore\SharedTempStoreFactory;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configure Context Form.
*/
abstract class ContextConfigure extends FormBase {
/**
* Tempstore Factory.
*
* @var \Drupal\Core\TempStore\SharedTempStoreFactory
*/
protected $tempstore;
/**
* Object EntityTypeManager.
* Entity Type Manager.
*
* @var Drupal\Core\Entity\EntityTypeManagerInterface
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The Tempstore ID.
*
* @var string
*/
protected $tempstore_id;
/**
* The form machine name.
*
* @var string
*/
protected $machine_name;
@ -51,7 +59,14 @@ abstract class ContextConfigure extends FormBase {
);
}
/**
* Configure Context Form constructor.
*
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* The tempstore factory.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(SharedTempStoreFactory $tempstore, EntityTypeManagerInterface $entity_type_manager) {
$this->tempstore = $tempstore;
$this->entityTypeManager = $entity_type_manager;
@ -120,7 +135,7 @@ abstract class ContextConfigure extends FormBase {
'#default_value' => $description,
];
if (strpos($data_type, 'entity:') === 0) {
list(, $entity_type) = explode(':', $data_type);
[, $entity_type] = explode(':', $data_type);
/** @var \Drupal\Core\Entity\Plugin\DataType\EntityAdapter $entity */
$entity = $edit ? $context->getContextValue() : NULL;
$form['context_value'] = [
@ -187,7 +202,7 @@ abstract class ContextConfigure extends FormBase {
}
// We're dealing with an entity and should make sure it's loaded.
if (strpos($context_definition->getDataType(), 'entity:') === 0) {
list(, $entity_type) = explode(':', $context_definition->getDataType());
[, $entity_type] = explode(':', $context_definition->getDataType());
if (is_numeric($form_state->getValue('context_value'))) {
$value = $this->entityTypeManager->getStorage($entity_type)->load($form_state->getValue('context_value'));
}
@ -200,15 +215,25 @@ abstract class ContextConfigure extends FormBase {
$cached_values = $this->addContext($cached_values, $form_state->getValue('machine_name'), $context);
$this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
$form_state->setRedirect($route_name, $route_parameters);
}
/**
* Ajax Save Method.
*
* @param array $form
* Drupal Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form State.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* The ajax data in the response.
*/
public function ajaxSave(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
$url = new Url($route_name, $route_parameters);
$response->addCommand(new RedirectCommand($url->toString()));
$response->addCommand(new CloseModalDialogCommand());

View File

@ -34,12 +34,19 @@ abstract class ContextDelete extends ConfirmFormBase {
*/
protected $context_id;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('tempstore.shared'));
}
/**
* Context Delete Constructor.
*
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* Tempstore Service.
*/
public function __construct(SharedTempStoreFactory $tempstore) {
$this->tempstore = $tempstore;
}
@ -75,12 +82,24 @@ abstract class ContextDelete extends ConfirmFormBase {
$form_state->setRedirectUrl($this->getCancelUrl());
}
/**
* Get a temp storage object.
*
* @return mixed
* The tempstore object.
*/
protected function getTempstore() {
return $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
}
/**
* Set the temp storage.
*
* @param array $cached_values
* Cached values to use in the tempstore.
*
* @throws \Drupal\Core\TempStore\TempStoreException
*/
protected function setTempstore($cached_values) {
$this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values);
}

View File

@ -12,7 +12,9 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
*
*/
abstract class ManageConditions extends FormBase {
/**
@ -32,7 +34,9 @@ abstract class ManageConditions extends FormBase {
*/
protected $machine_name;
/**
*
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.condition'),
@ -40,7 +44,9 @@ abstract class ManageConditions extends FormBase {
);
}
/**
*
*/
public function __construct(PluginManagerInterface $manager, FormBuilderInterface $form_builder) {
$this->manager = $manager;
$this->formBuilder = $form_builder;
@ -98,17 +104,19 @@ abstract class ManageConditions extends FormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
list(, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('conditions'));
[, $route_parameters] = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('conditions'));
$form_state->setRedirect($this->getAddRoute($cached_values), $route_parameters);
}
/**
*
*/
public function add(array &$form, FormStateInterface $form_state) {
$condition = $form_state->getValue('conditions');
$content = $this->formBuilder->getForm($this->getConditionClass(), $condition, $this->getTempstoreId(), $this->machine_name);
$content['#attached']['library'][] = 'core/drupal.dialog.ajax';
$cached_values = $form_state->getTemporaryValue('wizard');
list(, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('conditions'));
[, $route_parameters] = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('conditions'));
$route_name = $this->getAddRoute($cached_values);
$route_options = [
'query' => [
@ -130,9 +138,9 @@ abstract class ManageConditions extends FormBase {
public function renderRows($cached_values) {
$configured_conditions = [];
foreach ($this->getConditions($cached_values) as $row => $condition) {
/** @var $instance \Drupal\Core\Condition\ConditionInterface */
/** @var \Drupal\Core\Condition\ConditionInterface $instance */
$instance = $this->manager->createInstance($condition['id'], $condition);
list($route_name, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $cached_values['id'], $row);
[$route_name, $route_parameters] = $this->getOperationsRouteInfo($cached_values, $cached_values['id'], $row);
$build = [
'#type' => 'operations',
'#links' => $this->getOperations($route_name, $route_parameters),
@ -148,7 +156,9 @@ abstract class ManageConditions extends FormBase {
return $configured_conditions;
}
/**
*
*/
protected function getOperations($route_name_base, array $route_parameters = []) {
$operations['edit'] = [
'title' => $this->t('Edit'),

View File

@ -13,7 +13,9 @@ use Drupal\Core\Url;
use Drupal\ctools\TypedDataResolver;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Manage Context Form.
*/
abstract class ManageContext extends FormBase {
/**
@ -159,23 +161,33 @@ abstract class ManageContext extends FormBase {
public function submitForm(array &$form, FormStateInterface $form_state) {
if ($form_state->getTriggeringElement()['#name'] == 'add') {
$cached_values = $form_state->getTemporaryValue('wizard');
list(, $route_parameters) = $this->getContextOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('context'));
[, $route_parameters] = $this->getContextOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('context'));
$form_state->setRedirect($this->getContextAddRoute($cached_values), $route_parameters);
}
if ($form_state->getTriggeringElement()['#name'] == 'add_relationship') {
$cached_values = $form_state->getTemporaryValue('wizard');
list(, $route_parameters) = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('relationships'));
[, $route_parameters] = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('relationships'));
$form_state->setRedirect($this->getRelationshipAddRoute($cached_values), $route_parameters);
}
}
/**
* Add a context.
*
* @param array $form
* The Drupal Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* Form ajax repsonse.
*/
public function addContext(array &$form, FormStateInterface $form_state) {
$context = $form_state->getValue('context');
$content = $this->formBuilder->getForm($this->getContextClass(), $context, $this->getTempstoreId(), $this->machine_name);
$content['#attached']['library'][] = 'core/drupal.dialog.ajax';
$cached_values = $form_state->getTemporaryValue('wizard');
list(, $route_parameters) = $this->getContextOperationsRouteInfo($cached_values, $this->machine_name, $context);
$content = $this->formBuilder->getForm($this->getContextClass($cached_values), $context, $this->getTempstoreId(), $this->machine_name);
$content['#attached']['library'][] = 'core/drupal.dialog.ajax';
[, $route_parameters] = $this->getContextOperationsRouteInfo($cached_values, $this->machine_name, $context);
$route_name = $this->getContextAddRoute($cached_values);
$route_options = [
'query' => [
@ -189,13 +201,23 @@ abstract class ManageContext extends FormBase {
return $response;
}
/**
* Add relationship form.
*
* @param array $form
* The Drupal Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* Form ajax repsonse.
*/
public function addRelationship(array &$form, FormStateInterface $form_state) {
$relationship = $form_state->getValue('relationships');
$content = $this->formBuilder->getForm($this->getRelationshipClass(), $relationship, $this->getTempstoreId(), $this->machine_name);
$content['#attached']['library'][] = 'core/drupal.dialog.ajax';
$cached_values = $form_state->getTemporaryValue('wizard');
list(, $route_parameters) = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $relationship);
$content = $this->formBuilder->getForm($this->getRelationshipClass($cached_values), $relationship, $this->getTempstoreId(), $this->machine_name);
$content['#attached']['library'][] = 'core/drupal.dialog.ajax';
[, $route_parameters] = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $relationship);
$route_name = $this->getRelationshipAddRoute($cached_values);
$route_options = [
'query' => [
@ -209,22 +231,34 @@ abstract class ManageContext extends FormBase {
return $response;
}
protected function getAvailableRelationships($cached_values) {
/**
* Retrieve the available relationships.
*
* @param array $cached_values
* The cached context values.
*
* @return mixed
* The available relationships.
*/
protected function getAvailableRelationships(array $cached_values) {
/** @var \Drupal\ctools\TypedDataResolver $resolver */
$resolver = $this->typedDataResolver;
return $resolver->getTokensForContexts($this->getContexts($cached_values));
}
/**
* @param $cached_values
* Render the Rows.
*
* @param array $cached_values
* The cached context values.
*
* @return array
* The rendered rows.
*/
protected function renderRows($cached_values) {
protected function renderRows(array $cached_values) {
$contexts = [];
foreach ($this->getContexts($cached_values) as $row => $context) {
list($route_name, $route_parameters) = $this->getContextOperationsRouteInfo($cached_values, $this->machine_name, $row);
[$route_name, $route_parameters] = $this->getContextOperationsRouteInfo($cached_values, $this->machine_name, $row);
$build = [
'#type' => 'operations',
'#links' => $this->getOperations($cached_values, $row, $route_name, $route_parameters),
@ -242,14 +276,21 @@ abstract class ManageContext extends FormBase {
}
/**
* Get available Operations.
*
* @param array $cached_values
* The cached context values.
* @param string $row
* The row operations are being fetched from.
* @param string $route_name_base
* The route name.
* @param array $route_parameters
* Parameters for the route.
*
* @return mixed
* The operations.
*/
protected function getOperations($cached_values, $row, $route_name_base, array $route_parameters = []) {
protected function getOperations(array $cached_values, string $row, string $route_name_base, array $route_parameters = []) {
$operations = [];
if ($this->isEditableContext($cached_values, $row)) {
$operations['edit'] = [
@ -286,9 +327,13 @@ abstract class ManageContext extends FormBase {
* The ContextConfigure class is designed to be subclassed with custom
* route information to control the modal/redirect needs of your use case.
*
* @param mixed $cached_values
* Cached Relationship Class values.
*
* @return string
* The context class.
*/
abstract protected function getContextClass($cached_values);
abstract protected function getContextClass(mixed $cached_values);
/**
* Return a subclass of '\Drupal\ctools\Form\RelationshipConfigure'.
@ -296,28 +341,41 @@ abstract class ManageContext extends FormBase {
* The RelationshipConfigure class is designed to be subclassed with custom
* route information to control the modal/redirect needs of your use case.
*
* @param mixed $cached_values
* Cached Relationship Class values.
*
* @return string
* The relationship Class.
*/
abstract protected function getRelationshipClass($cached_values);
abstract protected function getRelationshipClass(mixed $cached_values);
/**
* The route to which context 'add' actions should submit.
*
* @param mixed $cached_values
* Cached Route info values.
*
* @return string
* The context add route.
*/
abstract protected function getContextAddRoute($cached_values);
abstract protected function getContextAddRoute(mixed $cached_values);
/**
* The route to which relationship 'add' actions should submit.
*
* @param mixed $cached_values
* Cached Route info values.
*
* @return string
* Relationship Add Route.
*/
abstract protected function getRelationshipAddRoute($cached_values);
abstract protected function getRelationshipAddRoute(mixed $cached_values);
/**
* Provide the tempstore id for your specified use case.
*
* @return string
* The tempstore ID.
*/
abstract protected function getTempstoreId();
@ -325,34 +383,51 @@ abstract class ManageContext extends FormBase {
* Returns the contexts already available in the wizard.
*
* @param mixed $cached_values
* Cached Contexts.
*
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
* The contexts.
*/
abstract protected function getContexts($cached_values);
/**
* Gets the Context Operations Route info.
*
* @param mixed $cached_values
* Cached Route info values.
* @param string $machine_name
* Relationship Machine Name.
* @param string $row
* Context Row.
*
* @return array
* The context operations.
*/
abstract protected function getContextOperationsRouteInfo($cached_values, $machine_name, $row);
/**
* Gets the Route info for Relationship Operations.
*
* @param mixed $cached_values
* Cached Route info values.
* @param string $machine_name
* Relationship Machine Name.
* @param string $row
* Context Row.
*
* @return array
* The operations allowed.
*/
abstract protected function getRelationshipOperationsRouteInfo($cached_values, $machine_name, $row);
/**
* @param mixed $cached_values
* Cached context values.
* @param string $row
* Context Row.
*
* @return bool
* If context is editable.
*/
abstract protected function isEditableContext($cached_values, $row);

View File

@ -119,7 +119,7 @@ abstract class ManageResolverRelationships extends FormBase {
public function submitForm(array &$form, FormStateInterface $form_state) {
if ($form_state->getTriggeringElement()['#name'] == 'add') {
$cached_values = $form_state->getTemporaryValue('wizard');
list(, $route_parameters) = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('relationships'));
[, $route_parameters] = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('relationships'));
$form_state->setRedirect($this->getAddRoute($cached_values), $route_parameters);
}
}
@ -137,7 +137,7 @@ abstract class ManageResolverRelationships extends FormBase {
$content = $this->formBuilder->getForm($this->getContextClass(), $relationship, $this->getTempstoreId(), $this->machine_name);
$content['#attached']['library'][] = 'core/drupal.dialog.ajax';
$cached_values = $form_state->getTemporaryValue('wizard');
list(, $route_parameters) = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $relationship);
[, $route_parameters] = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $relationship);
$route_name = $this->getAddRoute($cached_values);
$route_options = [
'query' => [
@ -175,7 +175,7 @@ abstract class ManageResolverRelationships extends FormBase {
protected function renderRows($cached_values) {
$contexts = [];
foreach ($this->getContexts($cached_values) as $row => $context) {
list($route_name, $route_parameters) = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $row);
[$route_name, $route_parameters] = $this->getRelationshipOperationsRouteInfo($cached_values, $this->machine_name, $row);
$build = [
'#type' => 'operations',
'#links' => $this->getOperations($cached_values, $row, $route_name, $route_parameters),

View File

@ -12,25 +12,35 @@ use Drupal\Core\TempStore\SharedTempStoreFactory;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configure Relationship Form.
*/
abstract class RelationshipConfigure extends FormBase {
/**
* Tempstore Factory.
*
* @var \Drupal\Core\TempStore\SharedTempStoreFactory
*/
protected $tempstore;
/**
* Typed Data Resolver Service.
*
* @var \Drupal\ctools\TypedDataResolver
*/
protected $resolver;
/**
* Tempstore ID.
*
* @var string
*/
protected $tempstore_id;
/**
* Relationship Machine Name.
*
* @var string
*/
protected $machine_name;
@ -42,7 +52,14 @@ abstract class RelationshipConfigure extends FormBase {
return new static($container->get('tempstore.shared'), $container->get('ctools.typed_data.resolver'));
}
/**
* Configure Relationship Form constructor.
*
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* Tempstore Service.
* @param \Drupal\ctools\TypedDataResolver $resolver
* Typed Data Resolver Service.
*/
public function __construct(SharedTempStoreFactory $tempstore, TypedDataResolver $resolver) {
$this->tempstore = $tempstore;
$this->resolver = $resolver;
@ -100,19 +117,24 @@ abstract class RelationshipConfigure extends FormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
list($route_name, $route_options) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_options] = $this->getParentRouteInfo($cached_values);
$form_state->setRedirect($route_name, $route_options);
}
/**
* Ajax Save Method.
*
* @param array $form
* Drupal Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form State.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* The ajax data in the response.
*/
public function ajaxSave(array &$form, FormStateInterface $form_state) {
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
$response = new AjaxResponse();
$url = Url::fromRoute($route_name, $route_parameters);
$response->addCommand(new RedirectCommand($url->toString()));
@ -124,33 +146,36 @@ abstract class RelationshipConfigure extends FormBase {
* Document the route name and parameters for redirect after submission.
*
* @param array $cached_values
* Cached Values get route info from.
*
* @return array In the format of
* In the format of
* return ['route.name', ['machine_name' => $this->machine_name, 'step' => 'step_name']];
* return ['route.name',
* ['machine_name' => $this->machine_name, 'step' => 'step_name']];
*/
abstract protected function getParentRouteInfo($cached_values);
abstract protected function getParentRouteInfo(array $cached_values);
/**
* Custom logic for setting the conditions array in cached_values.
*
* @param $cached_values
* @param array $cached_values
*
* @param $contexts
* @param mixed $contexts
* The conditions to set within the cached values.
*
* @return mixed
* Return the $cached_values
*/
abstract protected function setContexts($cached_values, $contexts);
abstract protected function setContexts(array $cached_values, mixed $contexts);
/**
* Custom logic for retrieving the contexts array from cached_values.
*
* @param $cached_values
* @param array $cached_values
* Cached Values contexts are fetched from.
*
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
*/
abstract protected function getContexts($cached_values);
abstract protected function getContexts(array $cached_values);
}

View File

@ -11,7 +11,9 @@ use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Form\FormBuilderInterface;
/**
* Required Context Form.
*/
abstract class RequiredContext extends FormBase {
/**
@ -41,7 +43,14 @@ abstract class RequiredContext extends FormBase {
);
}
/**
* Required Context Form constructor.
*
* @param \Drupal\Component\Plugin\PluginManagerInterface $typed_data_manager
* The Typed Data Manager.
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The Form Builder.
*/
public function __construct(PluginManagerInterface $typed_data_manager, FormBuilderInterface $form_builder) {
$this->typedDataManager = $typed_data_manager;
$this->formBuilder = $form_builder;
@ -98,7 +107,7 @@ abstract class RequiredContext extends FormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
list($route_name, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('contexts'));
[$route_name, $route_parameters] = $this->getOperationsRouteInfo($cached_values, $this->machine_name, $form_state->getValue('contexts'));
$form_state->setRedirect($route_name . '.edit', $route_parameters);
}
@ -120,15 +129,19 @@ abstract class RequiredContext extends FormBase {
}
/**
* Render The contexts in the form.
*
* @param $cached_values
* Cached context values.
*
* @return array
* The rendered contexts.
*/
public function renderContexts($cached_values) {
$configured_contexts = [];
foreach ($this->getContexts($cached_values) as $row => $context) {
list($plugin_id, $label, $machine_name, $description) = array_values($context);
list($route_name, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $cached_values['id'], $row);
[$plugin_id, $label, $machine_name, $description] = array_values($context);
[$route_name, $route_parameters] = $this->getOperationsRouteInfo($cached_values, $cached_values['id'], $row);
$build = [
'#type' => 'operations',
'#links' => $this->getOperations($route_name, $route_parameters),
@ -144,7 +157,17 @@ abstract class RequiredContext extends FormBase {
return $configured_contexts;
}
/**
* Retrieve Form Operations
*
* @param $route_name_base
* The base route name.
* @param array $route_parameters
* Route Parameters.
*
* @return array
* The available operations.
*/
protected function getOperations($route_name_base, array $route_parameters = []) {
$operations['edit'] = [
'title' => $this->t('Edit'),
@ -184,6 +207,7 @@ abstract class RequiredContext extends FormBase {
* information to control the modal/redirect needs of your use case.
*
* @return string
* The Context Class.
*/
abstract protected function getContextClass();
@ -191,6 +215,7 @@ abstract class RequiredContext extends FormBase {
* Provide the tempstore id for your specified use case.
*
* @return string
* The Tempstore ID.
*/
abstract protected function getTempstoreId();
@ -204,24 +229,28 @@ abstract class RequiredContext extends FormBase {
* this approach quite seamlessly.
*
* @param mixed $cached_values
*
* The Cached Values.
* @param string $machine_name
*
* The form machine name.
* @param string $row
* The form row to operate on.
*
* @return array
* In the format of
* return ['route.base.name', ['machine_name' => $machine_name, 'context' => $row]];
* return ['route.base.name',
* ['machine_name' => $machine_name, 'context' => $row]];
*/
abstract protected function getOperationsRouteInfo($cached_values, $machine_name, $row);
abstract protected function getOperationsRouteInfo(mixed $cached_values, string $machine_name, string $row);
/**
* Custom logic for retrieving the contexts array from cached_values.
*
* @param $cached_values
* @param array $cached_values
* The Cached Values.
*
* @return array
* The Contexts.
*/
abstract protected function getContexts($cached_values);
abstract protected function getContexts(array $cached_values);
}

View File

@ -15,21 +15,31 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class RequiredContextDelete extends ConfirmFormBase {
/**
* Creates a shared temporary storage for a collection.
*
* @var \Drupal\Core\TempStore\SharedTempStoreFactory
*/
protected $tempstore;
/**
* The temporary id storage.
*
* @var string
*/
// @codingStandardsIgnoreLine
protected $tempstore_id;
/**
* The machine name.
*
* @var string
*/
// @codingStandardsIgnoreLine
protected $machine_name;
/**
* The id.
*
* @var int
*/
protected $id;
@ -42,7 +52,10 @@ abstract class RequiredContextDelete extends ConfirmFormBase {
}
/**
* The constructor.
*
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* The shared temporary storage.
*/
public function __construct(SharedTempStoreFactory $tempstore) {
$this->tempstore = $tempstore;
@ -88,7 +101,7 @@ abstract class RequiredContextDelete extends ConfirmFormBase {
unset($contexts[$this->id]);
$cached_values = $this->setContexts($cached_values, $contexts);
$this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
$form_state->setRedirect($route_name, $route_parameters);
}
@ -140,7 +153,7 @@ abstract class RequiredContextDelete extends ConfirmFormBase {
*/
public function getCancelUrl() {
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
return new Url($route_name, $route_parameters);
}
@ -161,29 +174,35 @@ abstract class RequiredContextDelete extends ConfirmFormBase {
/**
* Document the route name and parameters for redirect after submission.
*
* @param $cached_values
* @param mixed $cached_values
* The cached values.
*
* @return array
* In the format of
* return ['route.name', ['machine_name' => $this->machine_name, 'step' => 'step_name]];
* return [
* 'route.name',
* ['machine_name' => $this->machine_name,'step' => 'step_name],
* ];
*/
abstract protected function getParentRouteInfo($cached_values);
/**
* Custom logic for retrieving the contexts array from cached_values.
*
* @param $cached_values
* @param mixed $cached_values
* The cache values.
*
* @return array
* Return an array.
*/
abstract protected function getContexts($cached_values);
/**
* Custom logic for setting the contexts array in cached_values.
*
* @param $cached_values
*
* @param $contexts
* @param mixed $cached_values
* The cache values.
* @param mixed $contexts
* The contexts to set within the cached values.
*
* @return mixed

View File

@ -11,7 +11,9 @@ use Drupal\Core\TempStore\SharedTempStoreFactory;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configure Relationships Resolver form.
*/
abstract class ResolverRelationshipConfigure extends FormBase {
/**
@ -36,7 +38,12 @@ abstract class ResolverRelationshipConfigure extends FormBase {
return new static($container->get('tempstore.shared'));
}
/**
* Configure Relationships Resolver form.
*
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* Tempstore Factory.
*/
public function __construct(SharedTempStoreFactory $tempstore) {
$this->tempstore = $tempstore;
}
@ -105,7 +112,14 @@ abstract class ResolverRelationshipConfigure extends FormBase {
return $form;
}
/**
* Configuration Form Validator.
*
* @param array $form
* The Drupal Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The Form State.
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$machine_name = $form_state->getValue('machine_name');
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
@ -136,15 +150,25 @@ abstract class ResolverRelationshipConfigure extends FormBase {
}
$cached_values = $this->setContexts($cached_values, $contexts);
$this->tempstore->get($this->tempstore_id)->set($this->machine_name, $cached_values);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
$form_state->setRedirect($route_name, $route_parameters);
}
/**
* Ajax Save Method.
*
* @param array $form
* Drupal Form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form State.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* The ajax data in the response.
*/
public function ajaxSave(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$cached_values = $this->tempstore->get($this->tempstore_id)->get($this->machine_name);
list($route_name, $route_parameters) = $this->getParentRouteInfo($cached_values);
[$route_name, $route_parameters] = $this->getParentRouteInfo($cached_values);
$url = Url::fromRoute($route_name, $route_parameters);
$response->addCommand(new RedirectCommand($url->toString()));
$response->addCommand(new CloseModalDialogCommand());

View File

@ -9,30 +9,42 @@ use Drupal\ctools\TypedDataResolver;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Resolver Relatinoship Delete Form.
*/
abstract class ResolverRelationshipDelete extends ConfirmFormBase {
/**
* Tempstore Factory.
*
* @var \Drupal\Core\TempStore\SharedTempStoreFactory
*/
protected $tempstore;
/**
* The resolver service.
*
* @var \Drupal\ctools\TypedDataResolver
*/
protected $resolver;
/**
* Tempstore ID.
*
* @var string
*/
protected $tempstore_id;
/**
* Machine name of the relationship.
*
* @var string
*/
protected $machine_name;
/**
* Resolver ID.
*
* @var string
*/
protected $id;
@ -45,6 +57,8 @@ abstract class ResolverRelationshipDelete extends ConfirmFormBase {
}
/**
* Resolver Relationship Delete Form Constructor.
*
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* The shared tempstore.
* @param \Drupal\ctools\TypedDataResolver $resolver
@ -120,6 +134,7 @@ abstract class ResolverRelationshipDelete extends ConfirmFormBase {
* The current wizard cached values.
*
* @return array
* Actions to call.
*/
protected function actions(array $form, FormStateInterface $form_state, $cached_values) {
return [
@ -144,7 +159,8 @@ abstract class ResolverRelationshipDelete extends ConfirmFormBase {
* The cached values.
*
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
* Contexts from the cached values.
*/
abstract public function getContexts($cached_values);
abstract public function getContexts(array $cached_values);
}

View File

@ -110,7 +110,7 @@ class TempstoreConverter implements ParamConverterInterface {
$tempstore_id = !empty($definition['tempstore_id']) ? $definition['tempstore_id'] : $defaults['tempstore_id'];
$machine_name = $this->convertVariable($value, $defaults);
list(, $parts) = explode(':', $definition['type'], 2);
[, $parts] = explode(':', $definition['type'], 2);
$parts = explode(':', $parts);
foreach ($parts as $key => $part) {
$parts[$key] = $this->convertVariable($part, $defaults);
@ -144,11 +144,11 @@ class TempstoreConverter implements ParamConverterInterface {
* @return mixed
* The value of a variable in defaults.
*/
protected function convertVariable($name, $defaults) {
protected function convertVariable($name, array $defaults) {
if (is_string($name) && strpos($name, '{') === 0) {
$length = strlen($name);
$name = substr($name, 1, $length - 2);
return isset($defaults[$name]) ? $defaults[$name] : NULL;
return $defaults[$name] ?? NULL;
}
return $name;
}

View File

@ -109,7 +109,7 @@ class EntityView extends BlockBase implements ContextAwarePluginInterface, Conta
return $return_as_object ? $parent_access : $parent_access->isAllowed();
}
/** @var $entity \Drupal\Core\Entity\EntityInterface */
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $this->getContextValue('entity');
return $entity->access('view', $account, $return_as_object);
}
@ -118,7 +118,7 @@ class EntityView extends BlockBase implements ContextAwarePluginInterface, Conta
* {@inheritdoc}
*/
public function build() {
/** @var $entity \Drupal\Core\Entity\EntityInterface */
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $this->getContextValue('entity');
$view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId());

View File

@ -30,16 +30,16 @@ class BlockPluginCollection extends DefaultLazyPluginCollection {
$region_assignments = [];
foreach ($this as $block_id => $block) {
$configuration = $block->getConfiguration();
$region = isset($configuration['region']) ? $configuration['region'] : NULL;
$region = $configuration['region'] ?? NULL;
$region_assignments[$region][$block_id] = $block;
}
foreach ($region_assignments as $region => $region_assignment) {
// @todo Determine the reason this needs error suppression.
@uasort($region_assignment, function (BlockPluginInterface $a, BlockPluginInterface $b) {
$a_config = $a->getConfiguration();
$a_weight = isset($a_config['weight']) ? $a_config['weight'] : 0;
$a_weight = $a_config['weight'] ?? 0;
$b_config = $b->getConfiguration();
$b_weight = isset($b_config['weight']) ? $b_config['weight'] : 0;
$b_weight = $b_config['weight'] ?? 0;
if ($a_weight == $b_weight) {
return strcmp($a->label(), $b->label());
}

View File

@ -92,7 +92,7 @@ trait BlockVariantTrait {
*/
public function getRegionAssignment($block_id) {
$configuration = $this->getBlock($block_id)->getConfiguration();
return isset($configuration['region']) ? $configuration['region'] : NULL;
return $configuration['region'] ?? NULL;
}
/**
@ -111,7 +111,7 @@ trait BlockVariantTrait {
*/
public function getRegionName($region) {
$regions = $this->getRegionNames();
return isset($regions[$region]) ? $regions[$region] : '';
return $regions[$region] ?? '';
}
/**

View File

@ -5,7 +5,9 @@ namespace Drupal\ctools\Plugin\Condition;
use Drupal\node\Plugin\Condition\NodeType as CoreNodeType;
use Drupal\ctools\ConstraintConditionInterface;
/**
*
*/
class NodeType extends CoreNodeType implements ConstraintConditionInterface {
/**

View File

@ -13,6 +13,13 @@ class EntityBundle extends EntityDeriverBase {
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
// Do not define any derivatives on Drupal 9.3+, instead, replace the core
// class in ctools_condition_info_alter().
if (\version_compare(\Drupal::VERSION, '9.3', '>')) {
return [];
}
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
if ($entity_type->hasKey('bundle')) {
$this->derivatives[$entity_type_id] = $base_plugin_definition;

View File

@ -4,7 +4,9 @@ namespace Drupal\ctools\Plugin\Deriver;
use Drupal\Core\TypedData\DataDefinitionInterface;
/**
*
*/
class TypedDataEntityRelationshipDeriver extends TypedDataRelationshipDeriver {
/**
@ -18,6 +20,12 @@ class TypedDataEntityRelationshipDeriver extends TypedDataRelationshipDeriver {
protected function generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, DataDefinitionInterface $base_definition, $property_name, DataDefinitionInterface $property_definition) {
if (method_exists($property_definition, 'getType') && $property_definition->getType() == 'entity_reference') {
parent::generateDerivativeDefinition($base_plugin_definition, $data_type_id, $data_type_definition, $base_definition, $property_name, $property_definition);
// Provide the entity type.
$derivative_id = $data_type_id . ':' . $property_name;
if (isset($this->derivatives[$derivative_id])) {
$this->derivatives[$derivative_id]['target_entity_type'] = $property_definition->getFieldStorageDefinition()->getPropertyDefinition('entity')->getConstraint('EntityType');
}
}
}

View File

@ -4,7 +4,9 @@ namespace Drupal\ctools\Plugin\Deriver;
use Drupal\Core\TypedData\DataDefinitionInterface;
/**
*
*/
class TypedDataLanguageRelationshipDeriver extends TypedDataRelationshipDeriver {
/**

View File

@ -15,7 +15,9 @@ use Drupal\Core\TypedData\TypedDataManagerInterface;
use Drupal\field\Entity\FieldConfig;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
*
*/
abstract class TypedDataPropertyDeriverBase extends DeriverBase implements ContainerDeriverInterface {
use StringTranslationTrait;

View File

@ -8,7 +8,9 @@ use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\field\FieldConfigInterface;
/**
*
*/
class TypedDataRelationshipDeriver extends TypedDataPropertyDeriverBase implements ContainerDeriverInterface {
/**
@ -32,7 +34,10 @@ class TypedDataRelationshipDeriver extends TypedDataPropertyDeriverBase implemen
'@property' => $property_definition->getLabel(),
'@base' => $data_type_definition['label'],
]);
$derivative['data_type'] = $property_definition->getFieldStorageDefinition()->getPropertyDefinition($property_definition->getFieldStorageDefinition()->getMainPropertyName())->getDataType();
$main_property = $property_definition->getFieldStorageDefinition()->getPropertyDefinition($property_definition->getFieldStorageDefinition()->getMainPropertyName());
if ($main_property) {
$derivative['data_type'] = $main_property->getDataType();
$derivative['property_name'] = $property_name;
if (strpos($base_data_type, 'entity:') === 0) {
$context_definition = new EntityContextDefinition($base_data_type, $this->typedDataManager->createDataDefinition($base_data_type));
@ -51,6 +56,7 @@ class TypedDataRelationshipDeriver extends TypedDataPropertyDeriverBase implemen
$this->derivatives[$base_data_type . ':' . $property_name] = $derivative;
}
}
// Individual fields can be on multiple bundles.
elseif ($property_definition instanceof FieldConfigInterface) {
// We should only end up in here on entity bundles.
@ -58,7 +64,7 @@ class TypedDataRelationshipDeriver extends TypedDataPropertyDeriverBase implemen
// Update label.
/** @var \Drupal\Core\StringTranslation\TranslatableMarkup $label */
$label = $derivative['label'];
list(,, $argument_name) = explode(':', $data_type_id);
[,, $argument_name] = explode(':', $data_type_id);
$arguments = $label->getArguments();
$arguments['@' . $argument_name] = $data_type_definition['label'];
$string_args = $arguments;

View File

@ -19,15 +19,17 @@ class TypedDataEntityRelationship extends TypedDataRelationship {
public function getRelationship() {
$plugin_definition = $this->getPluginDefinition();
$entity_type = $this->getData($this->getContext('base'))->getDataDefinition()->getSetting('target_type');
$context_definition = new EntityContextDefinition("entity:$entity_type", $plugin_definition['label']);
$context_definition = new EntityContextDefinition("entity:{$plugin_definition['target_entity_type']}", $plugin_definition['label']);
$context_value = NULL;
// If the 'base' context has a value, then get the property value to put on
// the context (otherwise, mapping hasn't occurred yet and we just want to
// return the context with the right definition and no value).
if ($this->getContext('base')->hasContextValue()) {
$context_value = $this->getData($this->getContext('base'))->entity;
$data = $this->getData($this->getContext('base'));
if ($data) {
$context_value = $data->entity;
}
}
$context_definition->setDefaultValue($context_value);

View File

@ -47,12 +47,16 @@ class TypedDataRelationship extends RelationshipBase {
return new Context($context_definition, $context_value);
}
/**
*
*/
public function getName() {
return $this->getPluginDefinition()['property_name'];
}
/**
*
*/
protected function getData(ContextInterface $context) {
/** @var \Drupal\Core\TypedData\ComplexDataInterface $base */
$base = $context->getContextValue();
@ -68,12 +72,16 @@ class TypedDataRelationship extends RelationshipBase {
return $data;
}
/**
*
*/
protected function getMainPropertyName(FieldItemInterface $data) {
return $data->getFieldDefinition()->getFieldStorageDefinition()->getMainPropertyName();
}
/**
*
*/
public function getRelationshipValue() {
$property = $this->getMainPropertyName();
/** @var \Drupal\Core\TypedData\ComplexDataInterface $data */

View File

@ -23,7 +23,7 @@ class VariantPluginCollection extends DefaultLazyPluginCollection {
*/
public function sort() {
// @todo Determine the reason this needs error suppression.
@uasort($this->instanceIDs, [$this, 'sortHelper']);
@uasort($this->instanceIds, [$this, 'sortHelper']);
return $this;
}

View File

@ -4,7 +4,9 @@ namespace Drupal\ctools\Testing;
use Drupal\Component\Render\FormattableMarkup;
/**
* Trait used for common entity creation methods.
*/
trait EntityCreationTrait {
/**
@ -33,9 +35,11 @@ trait EntityCreationTrait {
\Drupal::service('router.builder')->rebuild();
if ($this instanceof \PHPUnit_Framework_TestCase) {
$this->assertSame(SAVED_NEW, $status, (new FormattableMarkup('Created entity %id of type %type.', ['%id' => $entity->id(), '%type' => $entity_type]))->__toString());
// phpcs:ignore
$this->assertSame(SAVED_NEW, $status, (new FormattableMarkup('Created entity %id of type %type.', ['%id' => $entity->id(), '%type' => $entity_type]))->__toString()); //psp
}
else {
// phpcs:ignore
$this->assertEquals(SAVED_NEW, $status, (new FormattableMarkup('Created entity %id of type %type.', ['%id' => $entity->id(), '%type' => $entity_type]))->__toString());
}
@ -43,7 +47,10 @@ trait EntityCreationTrait {
}
/**
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
* Retrieves the Entity Type Manager for the Entity.
*
* @return \Drupal\Core\Entity\EntityTypeManager|\Drupal\Core\Entity\EntityTypeManagerInterface|object|null
* @throws \Exception
*/
protected function getEntityTypeManager() {
if (!isset($this->entityTypeManager)) {

View File

@ -14,7 +14,9 @@ use Drupal\Core\TypedData\ListDataDefinitionInterface;
use Drupal\Core\TypedData\ListInterface;
use Drupal\Core\TypedData\TypedDataManagerInterface;
/**
* Typed Data Resolver Service.
*/
class TypedDataResolver {
/**
@ -32,6 +34,8 @@ class TypedDataResolver {
protected $translation;
/**
* Typed Data Resolver Service constructor.
*
* @param \Drupal\Core\TypedData\TypedDataManagerInterface $manager
* The typed data manager.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
@ -59,7 +63,7 @@ class TypedDataResolver {
*
* @throws \Exception
*/
public function getContextFromProperty($property_path, ContextInterface $context) {
public function getContextFromProperty(string $property_path, ContextInterface $context) {
$value = NULL;
$data_definition = NULL;
if ($context->hasContextValue()) {
@ -147,7 +151,7 @@ class TypedDataResolver {
* TypedDataResolver which will convert it to an appropriate ContextInterface
* object.
*
* @param $token
* @param string $token
* A ":" delimited set of tokens representing
* @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
* The array of available contexts.
@ -157,13 +161,13 @@ class TypedDataResolver {
*
* @throws \Drupal\ctools\ContextNotFoundException
*/
public function convertTokenToContext($token, $contexts) {
public function convertTokenToContext(string $token, array $contexts) {
// If the requested token is already a context, just return it.
if (isset($contexts[$token])) {
return $contexts[$token];
}
else {
list($base, $property_path) = explode(':', $token, 2);
[$base, $property_path] = explode(':', $token, 2);
// A base must always be set. This method recursively calls itself
// setting bases for this reason.
if (!empty($contexts[$base])) {
@ -185,7 +189,7 @@ class TypedDataResolver {
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
* The administrative label of $token.
*/
public function getLabelByToken($token, $contexts) {
public function getLabelByToken(string $token, array $contexts) {
// @todo Optimize this by allowing to limit the desired token?
$tokens = $this->getTokensForContexts($contexts);
if (isset($tokens[$token])) {
@ -202,7 +206,7 @@ class TypedDataResolver {
* @return array
* An array of token keys and corresponding labels.
*/
public function getTokensForContexts($contexts) {
public function getTokensForContexts(array $contexts) {
$tokens = [];
foreach ($contexts as $context_id => $context) {
$data_definition = $context->getContextDefinition()->getDataDefinition();
@ -219,6 +223,7 @@ class TypedDataResolver {
* Returns tokens for a complex data definition.
*
* @param \Drupal\Core\TypedData\ComplexDataDefinitionInterface $complex_data_definition
* Complex Data Definition.
*
* @return array
* An array of token keys and corresponding labels.

View File

@ -6,9 +6,10 @@ use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\ctools\Event\WizardEvent;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
@ -24,7 +25,7 @@ abstract class EntityFormWizardBase extends FormWizardBase implements EntityForm
protected $entityTypeManager;
/**
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore
* Tempstore Factory for keeping track of values in each step of the
* wizard.
* @param \Drupal\Core\Form\FormBuilderInterface $builder
@ -35,16 +36,18 @@ abstract class EntityFormWizardBase extends FormWizardBase implements EntityForm
* The event dispatcher.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match object.
* @param $tempstore_id
* The shared temp store factory collection name.
* The private temp store factory collection name.
* @param null $machine_name
* The SharedTempStore key for our current wizard values.
* The PrivateTempStore key for our current wizard values.
* @param null $step
* The current active step of the wizard.
*/
public function __construct(SharedTempStoreFactory $tempstore, FormBuilderInterface $builder, ClassResolverInterface $class_resolver, EventDispatcherInterface $event_dispatcher, EntityTypeManagerInterface $entity_type_manager, RouteMatchInterface $route_match, $tempstore_id, $machine_name = NULL, $step = NULL) {
public function __construct(PrivateTempStoreFactory $tempstore, FormBuilderInterface $builder, ClassResolverInterface $class_resolver, EventDispatcherInterface $event_dispatcher, RouteMatchInterface $route_match, RendererInterface $renderer, $tempstore_id, EntityTypeManagerInterface $entity_type_manager, $machine_name = NULL, $step = NULL) {
$this->entityTypeManager = $entity_type_manager;
parent::__construct($tempstore, $builder, $class_resolver, $event_dispatcher, $route_match, $tempstore_id, $machine_name, $step);
parent::__construct($tempstore, $builder, $class_resolver, $event_dispatcher, $route_match, $renderer, $tempstore_id, $machine_name, $step);
}
/**
@ -52,11 +55,12 @@ abstract class EntityFormWizardBase extends FormWizardBase implements EntityForm
*/
public static function getParameters() {
$parameters = [
'tempstore' => \Drupal::service('tempstore.shared'),
'tempstore' => \Drupal::service('tempstore.private'),
'builder' => \Drupal::service('form_builder'),
'class_resolver' => \Drupal::service('class_resolver'),
'event_dispatcher' => \Drupal::service('event_dispatcher'),
'entity_type_manager' => \Drupal::service('entity_type.manager'),
'renderer' => \Drupal::service('renderer'),
];
// Keep the deprecated entity manager service as a parameter as well for
// BC, so that subclasses still work.

View File

@ -9,11 +9,12 @@ use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Drupal\ctools\Ajax\OpenModalWizardCommand;
use Drupal\ctools\Event\WizardEvent;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@ -25,7 +26,7 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
/**
* Tempstore Factory for keeping track of values in each step of the wizard.
*
* @var \Drupal\Core\TempStore\SharedTempStoreFactory
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $tempstore;
@ -51,14 +52,14 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
protected $dispatcher;
/**
* The shared temp store factory collection name.
* The private temp store factory collection name.
*
* @var string
*/
protected $tempstore_id;
/**
* The SharedTempStore key for our current wizard values.
* The PrivateTempStore key for our current wizard values.
*
* @var string|null
*/
@ -72,7 +73,14 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
protected $step;
/**
* @param \Drupal\Core\TempStore\SharedTempStoreFactory $tempstore
* Renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore
* Tempstore Factory for keeping track of values in each step of the
* wizard.
* @param \Drupal\Core\Form\FormBuilderInterface $builder
@ -82,18 +90,19 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
* @param $tempstore_id
* The shared temp store factory collection name.
* @param null $machine_name
* The SharedTempStore key for our current wizard values.
* @param null $step
* The private temp store factory collection name.
* @param string $machine_name
* The PrivateTempStore key for our current wizard values.
* @param string $step
* The current active step of the wizard.
*/
public function __construct(SharedTempStoreFactory $tempstore, FormBuilderInterface $builder, ClassResolverInterface $class_resolver, EventDispatcherInterface $event_dispatcher, RouteMatchInterface $route_match, $tempstore_id, $machine_name = NULL, $step = NULL) {
public function __construct(PrivateTempStoreFactory $tempstore, FormBuilderInterface $builder, ClassResolverInterface $class_resolver, EventDispatcherInterface $event_dispatcher, RouteMatchInterface $route_match, RendererInterface $renderer, $tempstore_id, $machine_name = NULL, $step = NULL) {
$this->tempstore = $tempstore;
$this->builder = $builder;
$this->classResolver = $class_resolver;
$this->dispatcher = $event_dispatcher;
$this->routeMatch = $route_match;
$this->renderer = $renderer;
$this->tempstore_id = $tempstore_id;
$this->machine_name = $machine_name;
$this->step = $step;
@ -104,10 +113,11 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
*/
public static function getParameters() {
return [
'tempstore' => \Drupal::service('tempstore.shared'),
'tempstore' => \Drupal::service('tempstore.private'),
'builder' => \Drupal::service('form_builder'),
'class_resolver' => \Drupal::service('class_resolver'),
'event_dispatcher' => \Drupal::service('event_dispatcher'),
'renderer' => \Drupal::service('renderer'),
];
}
@ -234,7 +244,7 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
$cached_values = $this->getTempstore()->get($this->getMachineName());
}
$operation = $this->getOperation($cached_values);
/* @var $operation \Drupal\Core\Form\FormInterface */
/** @var \Drupal\Core\Form\FormInterface $operation */
$operation = $this->classResolver->getInstanceFromDefinition($operation['form']);
return $operation->getFormId();
}
@ -247,7 +257,7 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
// Get the current form operation.
$operation = $this->getOperation($cached_values);
$form = $this->customizeForm($form, $form_state);
/* @var $formClass \Drupal\Core\Form\FormInterface */
/** @var \Drupal\Core\Form\FormInterface $formClass */
$formClass = $this->classResolver->getInstanceFromDefinition($operation['form']);
// Pass include any custom values for this operation.
if (!empty($operation['values'])) {
@ -336,8 +346,7 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
'#wizard' => $this,
'#cached_values' => $form_state->getTemporaryValue('wizard'),
];
// @todo properly inject the renderer.
$form['#prefix'] = \Drupal::service('renderer')->render($prefix);
$form['#prefix'] = $this->renderer->render($prefix);
return $form;
}
@ -364,6 +373,7 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
$after = array_slice($operations, array_search($step, $steps) + 1);
$actions = [
'#type' => 'actions',
'submit' => [
'#type' => 'submit',
'#value' => $this->t('Next'),
@ -438,7 +448,9 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
return $actions;
}
/**
*
*/
public function ajaxSubmit(array $form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
$response = new AjaxResponse();
@ -447,7 +459,9 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
return $response;
}
/**
*
*/
public function ajaxPrevious(array $form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
$response = new AjaxResponse();
@ -456,14 +470,18 @@ abstract class FormWizardBase extends FormBase implements FormWizardInterface {
return $response;
}
/**
*
*/
public function ajaxFinish(array $form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$response->addCommand(new CloseModalDialogCommand());
return $response;
}
/**
*
*/
public function getRouteName() {
return $this->routeMatch->getRouteName();
}

View File

@ -30,21 +30,21 @@ interface FormWizardInterface extends FormInterface {
public function initValues();
/**
* The shared temp store factory collection name.
* The private temp store factory collection name.
*
* @return string
*/
public function getTempstoreId();
/**
* The active SharedTempStore for this wizard.
* The active PrivateTempStore for this wizard.
*
* @return \Drupal\Core\TempStore\SharedTempStore
* @return \Drupal\Core\TempStore\PrivateTempStore
*/
public function getTempstore();
/**
* The SharedTempStore key for our current wizard values.
* The PrivateTempStore key for our current wizard values.
*
* @return null|string
*/
@ -173,13 +173,19 @@ interface FormWizardInterface extends FormInterface {
*/
public function finish(array &$form, FormStateInterface $form_state);
/**
*
*/
public function ajaxSubmit(array $form, FormStateInterface $form_state);
/**
*
*/
public function ajaxPrevious(array $form, FormStateInterface $form_state);
/**
*
*/
public function ajaxFinish(array $form, FormStateInterface $form_state);
}

View File

@ -93,7 +93,7 @@ class WizardFactory implements WizardFactoryInterface {
$arguments[] = $parameter->getDefaultValue();
}
}
/** @var $wizard \Drupal\ctools\Wizard\FormWizardInterface */
/** @var \Drupal\ctools\Wizard\FormWizardInterface $wizard */
$wizard = $reflection->newInstanceArgs($arguments);
return $wizard;
}

View File

@ -6,7 +6,7 @@ package: Testing
dependencies:
- ctools:ctools
# Information added by Drupal.org packaging script on 2021-06-16
version: '8.x-3.7'
# Information added by Drupal.org packaging script on 2022-07-01
version: '8.x-3.8'
project: 'ctools'
datestamp: 1623822132
datestamp: 1656633726

View File

@ -4,6 +4,9 @@ namespace Drupal\ctools_block_display_test\Plugin\DisplayVariant;
use Drupal\ctools\Plugin\DisplayVariant\BlockDisplayVariant as BaseBlockDisplayVariant;
/**
*
*/
class BlockDisplayVariant extends BaseBlockDisplayVariant {
/**

View File

@ -4,7 +4,7 @@ description: 'Provides testing for ctools wizard'
package: Testing
# version: 3.x
# Information added by Drupal.org packaging script on 2021-06-16
version: '8.x-3.7'
# Information added by Drupal.org packaging script on 2022-07-01
version: '8.x-3.8'
project: 'ctools'
datestamp: 1623822132
datestamp: 1656633726

View File

@ -4,7 +4,7 @@ namespace Drupal\ctools_wizard_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TempStore\SharedTempStoreFactory;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@ -15,17 +15,17 @@ class ExampleConfigEntityExternalForm extends FormBase {
/**
* Tempstore factory.
*
* @var \Drupal\Core\TempStore\SharedTempStoreFactory
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $tempstore;
/**
* Constructs a new ExampleConfigEntityExternalForm.
*
* @param \Drupal\ctools_wizard_test\Form\SharedTempStoreFactory $tempstore
* Creates a shared temporary storage for a collection.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore
* Creates a private temporary storage for a collection.
*/
public function __construct(SharedTempStoreFactory $tempstore) {
public function __construct(PrivateTempStoreFactory $tempstore) {
$this->tempstore = $tempstore;
}
@ -33,7 +33,7 @@ class ExampleConfigEntityExternalForm extends FormBase {
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('tempstore.shared'));
return new static($container->get('tempstore.private'));
}
/**
@ -48,7 +48,7 @@ class ExampleConfigEntityExternalForm extends FormBase {
*/
public function buildForm(array $form, FormStateInterface $form_state, $machine_name = '') {
$cached_values = $this->tempstore->get('ctools_wizard_test.config_entity')->get($machine_name);
/** @var $page \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity */
/** @var \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity $page */
$config_entity = $cached_values['ctools_wizard_test_config_entity'];
$form['blah'] = [

View File

@ -22,7 +22,7 @@ class ExampleConfigEntityGeneralForm extends FormBase {
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
/** @var $page \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity */
/** @var \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity $page */
$config_entity = $cached_values['ctools_wizard_test_config_entity'];
// The label and id will be added by the EntityFormWizardBase.
@ -34,7 +34,7 @@ class ExampleConfigEntityGeneralForm extends FormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
/** @var $page \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity */
/** @var \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity $page */
$config_entity = $cached_values['ctools_wizard_test_config_entity'];
$config_entity->set('id', $form_state->getValue('id'));

View File

@ -23,7 +23,7 @@ class ExampleConfigEntityOneForm extends FormBase {
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
/** @var $page \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity */
/** @var \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity $page */
$config_entity = $cached_values['ctools_wizard_test_config_entity'];
$form['one'] = [
@ -52,7 +52,7 @@ class ExampleConfigEntityOneForm extends FormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
/** @var $page \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity */
/** @var \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity $page */
$config_entity = $cached_values['ctools_wizard_test_config_entity'];
$config_entity->set('one', $form_state->getValue('one'));

View File

@ -22,7 +22,7 @@ class ExampleConfigEntityTwoForm extends FormBase {
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
/** @var $page \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity */
/** @var \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity $page */
$config_entity = $cached_values['ctools_wizard_test_config_entity'];
$form['two'] = [
@ -38,7 +38,7 @@ class ExampleConfigEntityTwoForm extends FormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$cached_values = $form_state->getTemporaryValue('wizard');
/** @var $page \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity */
/** @var \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity $page */
$config_entity = $cached_values['ctools_wizard_test_config_entity'];
$config_entity->set('two', $form_state->getValue('two'));

View File

@ -2,7 +2,9 @@
namespace Drupal\ctools_wizard_test\Wizard;
/**
*
*/
class EntityAddWizardTest extends EntityEditWizardTest {
/**

View File

@ -4,7 +4,9 @@ namespace Drupal\ctools_wizard_test\Wizard;
use Drupal\ctools\Wizard\EntityFormWizardBase;
/**
*
*/
class EntityEditWizardTest extends EntityFormWizardBase {
/**
@ -39,7 +41,7 @@ class EntityEditWizardTest extends EntityFormWizardBase {
* {@inheritdoc}
*/
public function getOperations($cached_values) {
/** @var $page \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity */
/** @var \Drupal\ctools_wizard_test\Entity\ExampleConfigEntity $page */
$config_entity = $cached_values['ctools_wizard_test_config_entity'];
$steps = [

View File

@ -5,7 +5,9 @@ namespace Drupal\ctools_wizard_test\Wizard;
use Drupal\Core\Form\FormStateInterface;
use Drupal\ctools\Wizard\FormWizardBase;
/**
*
*/
class WizardTest extends FormWizardBase {
/**

View File

@ -13,7 +13,7 @@ use Drupal\Tests\BrowserTestBase;
class CToolsWizardTest extends BrowserTestBase {
use StringTranslationTrait;
public static $modules = ['ctools', 'ctools_wizard_test'];
protected static $modules = ['ctools', 'ctools_wizard_test'];
/**
* {@inheritdoc}
@ -33,23 +33,24 @@ class CToolsWizardTest extends BrowserTestBase {
$edit = [
'one' => 'test',
];
$this->drupalPostForm('ctools/wizard', $edit, $this->t('Next'));
$this->drupalGet('ctools/wizard');
$this->submitForm($edit, $this->t('Next'));
// Redirected to the second step.
$this->assertSession()->pageTextContains('Form Two');
$this->assertSession()->pageTextContains('Dynamic value submitted: Xylophone');
// Check that $operations['two']['values'] worked.
$this->assertSession()->pageTextContains('Zebra');
// Hit previous to make sure our form value are preserved.
$this->drupalPostForm(NULL, [], $this->t('Previous'));
$this->submitForm([], $this->t('Previous'));
// Check the known form values.
$this->assertSession()->fieldValueEquals('one', 'test');
$this->assertSession()->pageTextContains('Xylophone');
// Goto next step again and finish this wizard.
$this->drupalPostForm(NULL, [], $this->t('Next'));
$this->submitForm([], $this->t('Next'));
$edit = [
'two' => 'Second test',
];
$this->drupalPostForm(NULL, $edit, $this->t('Finish'));
$this->submitForm($edit, $this->t('Finish'));
// Check that the wizard finished properly.
$this->assertSession()->pageTextContains('Value One: test');
$this->assertSession()->pageTextContains('Value Two: Second test');
@ -65,7 +66,8 @@ class CToolsWizardTest extends BrowserTestBase {
$edit = [
'one' => 'wrong',
];
$this->drupalPostForm('ctools/wizard', $edit, $this->t('Next'));
$this->drupalGet('ctools/wizard');
$this->submitForm($edit, $this->t('Next'));
// We're still on the first form and the error is present.
$this->assertSession()->pageTextContains('Form One');
$this->assertSession()->pageTextContains('Cannot set the value to "wrong".');
@ -73,13 +75,14 @@ class CToolsWizardTest extends BrowserTestBase {
$edit = [
'one' => 'magic',
];
$this->drupalPostForm('ctools/wizard', $edit, $this->t('Next'));
$this->drupalGet('ctools/wizard');
$this->submitForm($edit, $this->t('Next'));
// Redirected to the second step.
$this->assertSession()->pageTextContains('Form Two');
$edit = [
'two' => 'Second test',
];
$this->drupalPostForm(NULL, $edit, $this->t('Finish'));
$this->submitForm($edit, $this->t('Finish'));
// Check that the magic value triggered our submit callback.
$this->assertSession()->pageTextContains('Value One: Abraham');
$this->assertSession()->pageTextContains('Value Two: Second test');
@ -101,19 +104,19 @@ class CToolsWizardTest extends BrowserTestBase {
'id' => 'test123',
'label' => 'Test Config Entity 123',
];
$this->drupalPostForm(NULL, $edit, $this->t('Next'));
$this->submitForm($edit, $this->t('Next'));
// Submit the first step.
$edit = [
'one' => 'The first bit',
];
$this->drupalPostForm(NULL, $edit, $this->t('Next'));
$this->submitForm($edit, $this->t('Next'));
// Submit the second step.
$edit = [
'two' => 'The second bit',
];
$this->drupalPostForm(NULL, $edit, $this->t('Finish'));
$this->submitForm($edit, $this->t('Finish'));
// Now we should be looking at the list of entities.
$this->assertSession()->addressEquals('admin/structure/ctools_wizard_test_config_entity');
@ -130,12 +133,12 @@ class CToolsWizardTest extends BrowserTestBase {
$this->assertSession()->responseContains('Value from one: The first bit');
$this->drupalGet($previous);
// Change the value for 'one'.
$this->drupalPostForm(NULL, ['one' => 'New value'], $this->t('Next'));
$this->submitForm(['one' => 'New value'], $this->t('Next'));
$this->assertSession()->fieldValueEquals('two', 'The second bit');
$this->drupalPostForm(NULL, [], $this->t('Next'));
$this->submitForm([], $this->t('Next'));
// Make sure we get the additional step because the entity exists.
$this->assertSession()->pageTextContains('This step only shows if the entity is already existing!');
$this->drupalPostForm(NULL, [], $this->t('Finish'));
$this->submitForm([], $this->t('Finish'));
// Edit the entity again and make sure the change stuck.
$this->assertSession()->addressEquals('admin/structure/ctools_wizard_test_config_entity');

View File

@ -18,7 +18,7 @@ class BlockDisplayVariantTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['ctools', 'ctools_block_display_test', 'system', 'user'];
protected static $modules = ['ctools', 'ctools_block_display_test', 'system', 'user'];
/**
* Tests that events are fired when manipulating a block variant.

View File

@ -2,8 +2,7 @@
namespace Drupal\Tests\ctools\Kernel\Plugin\Block;
use Drupal\Core\Access\AccessResultForbidden;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\EntityContextDefinition;
use Drupal\ctools\Plugin\Block\EntityView;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\node\Traits\NodeCreationTrait;
@ -24,7 +23,7 @@ class EntityViewTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
protected static $modules = [
'block',
'ctools',
'filter',
@ -68,8 +67,8 @@ class EntityViewTest extends KernelTestBase {
],
];
$definition = [
'context' => [
'entity' => new ContextDefinition('entity:node', NULL, TRUE, FALSE, NULL, $node),
'context_definitions' => [
'entity' => new EntityContextDefinition('entity:node', NULL, TRUE, FALSE, NULL, $node),
],
'provider' => 'ctools',
];

View File

@ -43,20 +43,20 @@ class RelationshipManagerTest extends RelationshipsTestBase {
'node' => new Context($context_definition, $this->entities['node1']),
];
$definitions = $this->relationshipManager->getDefinitionsForContexts($contexts);
// $this->assertTrue(isset($definitions['typed_data_relationship:entity:node:body']));
$context_definition = new EntityContextDefinition('entity:node');
$contexts = [
'node' => new Context($context_definition, $this->entities['node2']),
];
$definitions = $this->relationshipManager->getDefinitionsForContexts($contexts);
$this->assertFalse(isset($definitions['typed_data_relationship:entity:node:body']));
$this->assertArrayNotHasKey('typed_data_relationship:entity:node:body', $definitions);
$context_definition = new EntityContextDefinition('entity:node');
$contexts = [
'node' => new Context($context_definition, $this->entities['node3']),
];
$definitions = $this->relationshipManager->getDefinitionsForContexts($contexts);
// $this->assertTrue(isset($definitions['typed_data_relationship:entity:node:body']));
}
}

View File

@ -5,7 +5,9 @@ namespace Drupal\Tests\ctools\Kernel;
use Drupal\ctools\Testing\EntityCreationTrait;
use Drupal\KernelTests\KernelTestBase;
/**
*
*/
abstract class RelationshipsTestBase extends KernelTestBase {
use EntityCreationTrait;
@ -24,7 +26,7 @@ abstract class RelationshipsTestBase extends KernelTestBase {
*
* @var array
*/
public static $modules = [
protected static $modules = [
'user',
'system',
'node',
@ -84,12 +86,17 @@ abstract class RelationshipsTestBase extends KernelTestBase {
'type' => 'foo',
'uid' => $user->id(),
]);
$node4 = $this->createEntity('node', [
'title' => 'Node 4',
'type' => 'foo',
])->set('uid', NULL);
$this->entities = [
'user' => $user,
'node1' => $node1,
'node2' => $node2,
'node3' => $node3,
'node4' => $node4,
];
}

View File

@ -16,7 +16,7 @@ class SerializableTempstoreTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['ctools', 'system', 'user'];
protected static $modules = ['ctools', 'system', 'user'];
/**
* {@inheritdoc}

View File

@ -41,6 +41,13 @@ class TypedDataEntityRelationshipPluginTest extends RelationshipsTestBase {
$relationship = $uid_plugin->getRelationship();
$this->assertTrue($relationship->getContextValue() instanceof User);
$this->assertSame('entity:user', $relationship->getContextDefinition()->getDataType());
/** @var \Drupal\ctools\Plugin\RelationshipInterface $uid_plugin */
$uid_plugin = $this->relationshipManager->createInstance('typed_data_entity_relationship:entity:node:uid');
$uid_plugin->setContextValue('base', $this->entities['node4']);
$relationship = $uid_plugin->getRelationship();
$this->assertFalse($relationship->hasContextValue());
$this->assertSame('entity:user', $relationship->getContextDefinition()->getDataType());
}
}

View File

@ -48,54 +48,54 @@ class TypedDataRelationshipPluginTest extends RelationshipsTestBase {
$nid_plugin->setContextValue('base', $this->entities['node1']);
$relationship = $nid_plugin->getRelationship();
$this->assertTrue($relationship instanceof ContextInterface);
$this->assertTrue($relationship->getContextDefinition()->getDataType() == 'integer');
$this->assertEquals('integer', $relationship->getContextDefinition()->getDataType());
$this->assertTrue($relationship->hasContextValue());
$this->assertTrue($relationship->getContextValue() == $this->entities['node1']->id());
$this->assertEquals($this->entities['node1']->id(), $relationship->getContextValue());
/** @var \Drupal\ctools\Plugin\RelationshipInterface $uuid_plugin */
$uuid_plugin = $this->relationshipManager->createInstance('typed_data_relationship:entity:node:uuid');
$uuid_plugin->setContextValue('base', $this->entities['node1']);
$relationship = $uuid_plugin->getRelationship();
$this->assertTrue($relationship instanceof ContextInterface);
$this->assertTrue($relationship->getContextDefinition()->getDataType() == 'string');
$this->assertEquals('string', $relationship->getContextDefinition()->getDataType());
$this->assertTrue($relationship->hasContextValue());
$this->assertTrue($relationship->getContextValue() == $this->entities['node1']->uuid());
$this->assertEquals($this->entities['node1']->uuid(), $relationship->getContextValue());
/** @var \Drupal\ctools\Plugin\RelationshipInterface $title_plugin */
$title_plugin = $this->relationshipManager->createInstance('typed_data_relationship:entity:node:title');
$title_plugin->setContextValue('base', $this->entities['node1']);
$relationship = $title_plugin->getRelationship();
$this->assertTrue($relationship instanceof ContextInterface);
$this->assertTrue($relationship->getContextDefinition()->getDataType() == 'string');
$this->assertEquals('string', $relationship->getContextDefinition()->getDataType());
$this->assertTrue($relationship->hasContextValue());
$this->assertTrue($relationship->getContextValue() == $this->entities['node1']->label());
$this->assertEquals($this->entities['node1']->label(), $relationship->getContextValue());
/** @var \Drupal\ctools\Plugin\RelationshipInterface $body_plugin */
$body_plugin = $this->relationshipManager->createInstance('typed_data_relationship:entity:node:body');
$body_plugin->setContextValue('base', $this->entities['node1']);
$relationship = $body_plugin->getRelationship();
$this->assertTrue($relationship instanceof ContextInterface);
$this->assertTrue($relationship->getContextDefinition()->getDataType() == 'string');
$this->assertEquals('string', $relationship->getContextDefinition()->getDataType());
$this->assertTrue($relationship->hasContextValue());
$this->assertTrue($relationship->getContextValue() == $this->entities['node1']->get('body')->first()->get('value')->getValue());
$this->assertEquals($this->entities['node1']->get('body')->first()->get('value')->getValue(), $relationship->getContextValue());
/** @var \Drupal\ctools\Plugin\RelationshipInterface $uid_plugin */
$uid_plugin = $this->relationshipManager->createInstance('typed_data_relationship:entity:node:uid');
$uid_plugin->setContextValue('base', $this->entities['node3']);
$relationship = $uid_plugin->getRelationship();
$this->assertTrue($relationship instanceof ContextInterface);
$this->assertTrue($relationship->getContextDefinition()->getDataType() == 'integer');
$this->assertEquals('integer', $relationship->getContextDefinition()->getDataType());
$this->assertTrue($relationship->hasContextValue());
$this->assertTrue($relationship->getContextValue() == $this->entities['node3']->getOwnerId());
$this->assertEquals($this->entities['node3']->getOwnerId(), $relationship->getContextValue());
/** @var \Drupal\ctools\Plugin\RelationshipInterface $mail_plugin */
$mail_plugin = $this->relationshipManager->createInstance('typed_data_relationship:entity:user:mail');
$mail_plugin->setContextValue('base', $this->entities['user']);
$relationship = $mail_plugin->getRelationship();
$this->assertTrue($relationship instanceof ContextInterface);
$this->assertTrue($relationship->getContextDefinition()->getDataType() == 'email');
$this->assertEquals('email', $relationship->getContextDefinition()->getDataType());
$this->assertTrue($relationship->hasContextValue());
$this->assertTrue($relationship->getContextValue() == $this->entities['user']->getEmail());
$this->assertEquals($this->entities['user']->getEmail(), $relationship->getContextValue());
}
}

View File

@ -22,7 +22,7 @@ class TypedDataResolverTest extends KernelTestBase {
*
* @var array
*/
public static $modules = ['user', 'system', 'entity_test', 'ctools'];
protected static $modules = ['user', 'system', 'entity_test', 'ctools'];
/**
* @var \Drupal\ctools\TypedDataResolver
@ -77,7 +77,7 @@ class TypedDataResolverTest extends KernelTestBase {
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity to test with.
* @param $property_path
* @param string $property_path
* The property path to look for.
* @param $expected_data_type
* The expected data type.
@ -85,7 +85,7 @@ class TypedDataResolverTest extends KernelTestBase {
* @return \Drupal\Core\Plugin\Context\ContextInterface
* The context with a value.
*/
protected function assertPropertyPath(ContentEntityInterface $entity, $property_path, $expected_data_type) {
protected function assertPropertyPath(ContentEntityInterface $entity, string $property_path, $expected_data_type) {
$typed_data_entity = $entity->getTypedData();
if (strpos($typed_data_entity->getDataDefinition()->getDataType(), 'entity:') === 0) {
$context_definition = new EntityContextDefinition($typed_data_entity->getDataDefinition()->getDataType());

View File

@ -45,7 +45,9 @@ class BlockDisplayVariantTest extends UnitTestCase {
return [];
}
/**
*
*/
public function getRegionNames() {
return [
'top' => 'Top',

View File

@ -35,6 +35,9 @@ class BlockVariantTraitTest extends UnitTestCase {
$this->assertSame($expected, $display_variant->getRegionAssignments());
}
/**
*
*/
public function providerTestGetRegionAssignments() {
return [
[
@ -75,7 +78,9 @@ class BlockVariantTraitTest extends UnitTestCase {
}
}
/**
*
*/
class TestBlockVariantTrait {
use BlockVariantTrait;

View File

@ -27,7 +27,7 @@ class VariantCollectionTraitTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
protected function setUp(): void {
protected function setUp() {
parent::setUp();
$container = new ContainerBuilder();
$this->manager = $this->prophesize(PluginManagerInterface::class);
@ -119,7 +119,7 @@ class VariantCollectionTraitTest extends UnitTestCase {
* @depends testAddVariant
*/
public function testGetVariant($data) {
list($trait_object, $uuid, $plugin) = $data;
[$trait_object, $uuid, $plugin] = $data;
$this->manager->createInstance()->shouldNotBeCalled();
$this->assertSame($plugin, $trait_object->getVariant($uuid));
@ -132,7 +132,7 @@ class VariantCollectionTraitTest extends UnitTestCase {
* @depends testGetVariant
*/
public function testRemoveVariant($data) {
list($trait_object, $uuid) = $data;
[$trait_object, $uuid] = $data;
$this->assertSame($trait_object, $trait_object->removeVariant($uuid));
$this->assertFalse($trait_object->getVariants()->has($uuid));
@ -145,7 +145,7 @@ class VariantCollectionTraitTest extends UnitTestCase {
* @depends testRemoveVariant
*/
public function testGetVariantException($data) {
list($trait_object, $uuid) = $data;
[$trait_object, $uuid] = $data;
// Attempt to retrieve a variant that has been removed.
$this->expectException('\Drupal\Component\Plugin\Exception\PluginNotFoundException');
$this->expectExceptionMessage("Plugin ID 'test-uuid' was not found.");
@ -153,7 +153,9 @@ class VariantCollectionTraitTest extends UnitTestCase {
}
}
/**
*
*/
class TestVariantCollectionTrait {
use VariantCollectionTrait;

View File

@ -0,0 +1,203 @@
CONTENTS OF THIS FILE
---------------------
* Requirements
* Recommended Modules
* Installation
* Configuration
* Features
* Extension modules
* FAQ
* Maintainers
INTRODUCTION
------------
The Facets module allows site builders to easily create and manage faceted
search interfaces.
REQUIREMENTS
------------
No other modules required; we're supporting Drupal Core's search as a source for
creating facets.
RECOMMENDED MODULES
-------------------
* Search API - https://www.drupal.org/project/search_api
INSTALLATION
------------
* Install as you would normally install a contributed Drupal module. Visit:
https://www.drupal.org/node/1897420 for further information.
CONFIGURATION
-------------
Before adding a facet, there should be a facet source. Facet sources can be:
- Drupal core's search.
- A view based on a Search API index with a page display.
- A page from the search_api_page module.
After adding one of those, you can add a facet on the facets configuration page:
/admin/config/search/facets, there's an `add facet` link, that links to:
admin/config/search/facets/add-facet. Use that page to add the facet by
selecting the correct facet source and field from that source.
If you're using Search API views, make sure to disable views cache when using
facets for that view.
KNOWN ISSUES
------------
When choosing the "Hard limit" option on a search_api_db backend, be aware that
the limitation is done internally after sorting on the number of results ("num")
first and then sorting by the raw value of the facet (e.g. entity-id) in the
second dimension. This can lead to edge cases when there is an equal amount of
results on facets that are exactly on the threshold of the hard limit. In this
case the raw facet value with the lower value is preferred:
| num | value | label |
|-----|-------|-------|
| 3 | 4 | Bar |
| 3 | 5 | Atom |
| 2 | 2 | Zero |
| 2 | 3 | Clown |
"Clown" will be cut off due to its higher internal value (entity-id). For
further details see: https://www.drupal.org/node/2834730
FEATURES
--------
If you are the developer of a search API backend implementation and want
to support facets with your service class, too, you'll have to support the
"search_api_facets" feature. In short, you'll just have to return facet terms
and counts according to the query's "search_api_facets" option, when executing
a query.
For the module to be able to tell that your server supports facets,
you will also have to change your service's supportsFeature() method to
something like the following:
```
public function getSupportedFeatures() {
return ['search_api_facets'];
}
```
If you don't do that, there's no way for the facet source to pick up facets.
The "search_api_facets" option looks as follows:
```
$query->setOption('search_api_facets', [
$facet_id => [
// The Search API field ID of the field to facet on.
'field' => (string),
// The maximum number of filters to retrieve for the facet.
'limit' => (int),
// The facet operator: "and" or "or".
'operator' => (string),
// The minimum count a filter/value must have to be returned.
'min_count' => (int),
// Whether to retrieve a facet for "missing" values.
'missing' => (bool),
],
// …
]);
```
The structure of the returned facets array should look like this:
```
$results->setExtraData('search_api_facets', [
$facet_id => [
[
'count' => (int),
'filter' => (string),
],
// …
],
// …
]);
```
A filter is a string with one of the following forms:
- `"VALUE"`: Filter by the literal value VALUE (always include the quotes, not
only for strings).
- `[VALUE1 VALUE2]`: Filter for a value between VALUE1 and VALUE2. Use
parentheses for excluding the border values and square brackets for including
them. An asterisk (*) can be used as a wildcard. E.g., (* 0) or [* 0) would be
a filter for all negative values.
- `!`: Filter for items without a value for this field (i.e., the "missing"
facet).
EXTENSION MODULES
-----------------
- https://www.drupal.org/project/entity_reference_facet_link
Provides a link to a facet through an entity reference field.
- https://www.drupal.org/project/facets_prefix_suffix
Provides a plugin to configure a prefix/suffix per result.
- https://www.drupal.org/project/facets_block
Provide the facets as a Drupal block.
- https://www.drupal.org/project/facets_taxonomy_path_processor
Sets taxonomy facet items active if present in route.
- https://www.drupal.org/project/facets_view_mode_processor
Provides a processor to render facet entity reference items as view modes.
- https://www.drupal.org/project/facets_range_input
Provides an input range form (min and max) as a processor and widget.
- https://www.drupal.org/project/facets_range_dropdowns
Provides an dropdown widget that works with the range processor.
FAQ
---
Q: Why do the facets disappear after a refresh?
A: We don't support cached views, change the view to disable caching.
Q: Why doesn't chosen (or similar JavaScript dropdown replacement) not work
with the dropdown widget?
A: Because the dropdown we create for the widget is created through JavaScript,
the chosen module (and others, probably) doesn't find the select element.
Though the library can be attached to the block in custom code, we haven't
done this in facets because we don't want to support all possible frameworks.
See https://www.drupal.org/node/2853121 for more information.
Q: Why are facets results links from another language showing in the facet
results?
A: Facets use the same limitations as the query object passed, so when using
views, add a filter to the view to limit to one language.
Otherwise, this is solved by adding a `hook_search_api_query_alter()` that
limits the results to the current language.
Q: I would like a prefix/suffix for facet result items.
A: If you just need to show text, use
https://www.drupal.org/project/facets_prefix_suffix.
However, if you need to include HTML you can use
hook_preprocess_facets_result_item().
Q: Why are results shown for inaccessible content?
A: If the "Content access" Search API processor is enabled but results still
aren't properly access-checked, you might need to write a custom processor to do
the access checks for you.
This should only happen if you're not using the default node access framework
provided by Core, though. You need to use a combination of hook_node_grants and
hook_node_access_records instead of hook_node_access.
MAINTAINERS
-----------
* Joris Vercammen (borisson_) - https://www.drupal.org/u/borisson_
* Jimmy Henderickx (StryKaizer) - https://www.drupal.org/u/strykaizer
* Nick Veenhof (Nick_vh) - https://www.drupal.org/u/nick_vh

View File

@ -0,0 +1,30 @@
{
"name": "drupal/facets",
"description": "The Facet module allows site builders to easily create and manage faceted search interfaces.",
"type": "drupal-module",
"homepage": "https://www.drupal.org/project/facets",
"authors": [
{
"name": "See all contributors",
"homepage": "https://www.drupal.org/node/2348769/committers"
}
],
"support": {
"issues": "https://www.drupal.org/project/issues/facets",
"irc": "irc://irc.freenode.org/drupal-search-api",
"source": "git://git.drupal.org/project/facets.git"
},
"license": "GPL-2.0+",
"require-dev": {
"drupal/search_api": "~1.21",
"drupal/jquery_ui_slider": "~1.1",
"drupal/jquery_ui_touch_punch": "~1.0"
},
"suggest": {
"drupal/jquery_ui_slider": "Required for the 'Facets Range Widget' module to work",
"drupal/jquery_ui_touch_punch": "Required for the 'Facets Range Widget' module to work"
},
"conflict": {
"drupal/search_api": "<1.14"
}
}

View File

@ -0,0 +1,7 @@
block.settings.facet_block:*:
type: block_settings
label: 'Facet rendered as block'
mapping:
block_id:
type: string
label: 'Block ID'

View File

@ -0,0 +1,106 @@
facets.facet.*:
type: config_entity
label : 'Facet'
mapping:
id:
type: string
label: 'ID'
name:
type: label
label: Name
weight:
type: integer
label: 'Weight'
min_count:
type: integer
label: 'Minimum count'
url_alias:
type: label
label: 'Name of facet as used in the URL'
facet_source_id:
type: string
label: 'Facet source id'
field_identifier:
type: string
label: 'Field identifier'
query_operator:
type: string
label: 'Query Operator'
hard_limit:
type: integer
label: 'Hard limit'
exclude:
type: boolean
label: 'Exclude'
use_hierarchy:
type: boolean
label: 'Use hierarchy'
keep_hierarchy_parents_active:
type: boolean
label: 'Keep hierarchy parents active'
hierarchy:
type: mapping
label: 'Hierarchy type'
mapping:
type:
type: string
label: 'Plugin id'
config:
type: facets.facet.[%parent.type]
label: 'Configuration'
expand_hierarchy:
type: boolean
label: 'Expand hierarchy'
enable_parent_when_child_gets_disabled:
type: boolean
label: 'Enable parent when child gets disabled'
widget:
type: mapping
label: 'Facet widget'
mapping:
type:
type: string
label: 'Plugin ID'
config:
type: facet.widget.config.[%parent.type]
label: 'Configuration'
empty_behavior:
type: mapping
label: 'Empty behavior'
mapping:
behavior:
type: string
label: 'The empty behavior identifier'
text_format:
type: string
label: 'Text format'
text:
type: text
label: 'Text'
only_visible_when_facet_source_is_visible:
type: boolean
label: 'Show this facet only when the facet source is visible.'
show_only_one_result:
type: boolean
label: 'Show only one result'
show_title:
type: boolean
label: 'Show title'
processor_configs:
type: sequence
label: 'Processor settings'
sequence:
type: mapping
label: 'A processor'
mapping:
processor_id:
type: string
label: 'The plugin ID of the processor'
weights:
type: sequence
label: 'The processor''s weights for the different processing stages'
sequence:
type: integer
label: 'The processor''s weight for this stage'
settings:
type: plugin.plugin_configuration.facets_processor.[%parent.processor_id]

View File

@ -0,0 +1,30 @@
facets.facet_source.*:
type: config_entity
label : 'Facet Source'
mapping:
id:
type: string
label: 'ID'
name:
type: label
label: Name'
filter_key:
type: string
label: 'Filter key'
url_processor:
type: string
label: 'Url processor'
breadcrumb:
type: mapping
labal: 'Breadcrumb'
mapping:
active:
type: boolean
label: 'Append active facets to breadcrumb'
before:
type: boolean
label: 'Show facet label before active facet'
group:
type: boolean
label: 'Group active items under same crumb'
third_party_settings: {}

View File

@ -0,0 +1,176 @@
plugin.plugin_configuration.facets_processor.*:
type: config_object
plugin.plugin_configuration.facets_processor.count_widget_widget_order:
type: mapping
label: 'Count widget order'
mapping:
sort:
type: string
label: sort order
plugin.plugin_configuration.facets_processor.display_value_widget_order:
type: mapping
label: 'Display value widget order'
mapping:
sort:
type: string
label: sort order
plugin.plugin_configuration.facets_processor.translate_entity:
type: mapping
label: 'Translate entity'
mapping:
sort:
type: boolean
label: translate entity
plugin.plugin_configuration.facets_processor.term_weight_widget_order:
type: mapping
label: 'Display term widget order'
mapping:
sort:
type: string
label: sort order
plugin.plugin_configuration.facets_processor.exclude_specified_items:
type: mapping
label: 'Exclude specified items'
mapping:
exclude:
type: string
label: Exclude
regex:
type: boolean
label: Regex
invert:
type: boolean
label: Invert
plugin.plugin_configuration.facets_processor.raw_value_widget_order:
type: mapping
label: 'Raw value widget order'
mapping:
sort:
type: string
label: sort order
plugin.plugin_configuration.facets_processor.active_widget_order:
type: mapping
label: 'Active widget order'
mapping:
sort:
type: string
label: sort order
plugin.plugin_configuration.facets_processor.count_widget_order:
type: mapping
label: 'Active widget order'
mapping:
sort:
type: string
label: sort order
plugin.plugin_configuration.facets_processor.count_limit:
type: mapping
label: 'Count limit widget'
mapping:
minimum_items:
type: integer
label: 'Mimimum amount of items to show.'
maximum_items:
type: integer
label: 'Maximum amount of items to show.'
plugin.plugin_configuration.facets_processor.boolean_item:
type: mapping
label: 'Boolean processor'
mapping:
on_value:
type: label
label: 'On value'
off_value:
type: label
label: 'Off value'
plugin.plugin_configuration.facets_processor.combine_processor:
type: sequence
label: 'Combine facets processor'
sequence:
type: mapping
label: Mapping for a processor
mapping:
combine:
type: boolean
label: 'Combine this facet'
mode:
type: string
label: 'Combination mode'
plugin.plugin_configuration.facets_processor.dependent_processor:
type: sequence
label: 'Dependent facet processor'
sequence:
type: mapping
label: Mapping for a processor
mapping:
enable:
type: boolean
label: 'Enable for this facet'
condition:
type: string
label: 'Type of condition'
values:
type: label
label: 'The value of the condition'
negate:
type: boolean
label: 'Should the condition be negated'
plugin.plugin_configuration.facets_processor.show_siblings_processor:
type: mapping
label: 'Show siblings processor'
mapping:
show_parent_siblings:
type: boolean
label: 'Show parents'
plugin.plugin_configuration.facets_processor.date_item:
type: mapping
label: 'Date item processor'
mapping:
date_display:
type: string
label: 'Date display'
granularity:
type: integer
label: 'Granularity'
date_format:
type: string
label: 'Date format'
hierarchy:
type: boolean
label: 'Hierarchy'
plugin.plugin_configuration.facets_processor.granularity_item:
type: mapping
label: 'Granular item processor'
mapping:
granularity:
type: integer
label: 'Granularity'
min_value:
type: integer
label: 'Minimum value'
max_value:
type: integer
label: 'Maximum value'
include_lower:
type: boolean
label: 'Include lower bounds'
include_upper:
type: boolean
label: 'Include upper bounds'
include_edges:
type: boolean
label: 'Include first lower and last upper bound'

View File

@ -0,0 +1,59 @@
# Default config schema that all widgets can extend.
facet.widget.default_config:
type: mapping
label: 'Default widget configuration'
mapping:
show_numbers:
type: boolean
label: 'Show counts'
# Default schema for facet widgets of unknown type.
facet.widget.config.*:
type: facet.widget.default_config
# Config schema for dropdown, you can find the implementation in
# Drupal\facets\Plugin\facets\widget\DropdownWidget. Options for this widget are
# "show counts" and "default option label".
facet.widget.config.dropdown:
type: facet.widget.default_config
label: 'Dropdown widget configuration'
mapping:
default_option_label:
type: label
label: 'Default option label'
# Config schema for links, you can find the implementation in
# Drupal\facets\Plugin\facets\widget\LinksWidget. Options for this widget are
# "soft limit" and "show counts".
facet.widget.config.links:
type: facet.widget.default_config
label: 'List of links widget configuration'
mapping:
soft_limit:
type: integer
label: 'Soft limit'
show_reset_link:
type: boolean
label: 'Show reset link'
reset_text:
type: label
label: 'Reset link text'
hide_reset_when_no_selection:
type: boolean
label: 'Hide reset link when no facet item is selected'
soft_limit_settings:
type: mapping
label: 'Soft limit settings'
mapping:
show_less_label:
type: label
label: 'Show less label'
show_more_label:
type: label
label: 'Show more label'
# Config schema for checkbox, you can find the implementation in
# Drupal\facets\Plugin\facets\widget\CheckboxWidget. Options for this widget are
# "soft limit" and "show counts".
facet.widget.config.checkbox:
type: facet.widget.config.links

View File

@ -0,0 +1,44 @@
/**
* @file
* Administration styles for the Facets module.
*/
/*
* Facets overview page
*/
.facets-groups-list {
margin-bottom: 2em;
}
.facets-groups-list tr {
border-bottom: none;
}
.facets-groups-list tr.facet-source {
border-top: 1px solid #e6e4df;
}
.facets-groups-list tr.facet:last-of-type {
border-bottom: 1px solid #e6e4df;
}
.facets-groups-list tr.facet-source .facets-type,
.facets-groups-list tr.facet-source .search-api-title {
font-weight: bold;
}
.facets-groups-list tr.facet .facets-summary-type,
.facets-groups-list tr.facet .facets-type {
padding-left: 3em;
}
/*
* Facets Display page
*/
.facets-processor-settings-sorting {
margin-bottom: -7px;
margin-left: 20px;
margin-top: -16px;
}
.facets-processor-settings-facet {
margin-left: 20px;
margin-bottom: 20px;
}

View File

@ -0,0 +1,3 @@
.facets-widget-dropdown label {
display: none;
}

View File

@ -0,0 +1,3 @@
.block-facets ul ul li {
margin-left: 10px;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* @file
* Hooks provided by the Facets module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Alter the Facets Query Type mapping.
*
* Modules may implement this hook to alter the mapping that defines how a
* certain data type should be handled in Search API based Facets.
*
* @param array $query_types
* The Search API backend info array, keyed by backend ID.
*
* @see \Drupal\facets\Plugin\facets\facet_source\SearchApiBaseFacetSource
*/
function hook_facets_search_api_query_type_mapping_alter($backend_plugin_id, array &$query_types) {
if ($backend_plugin_id == 'search_api_solr') {
$query_types['string'] = 'search_api_solr_string';
}
}

View File

@ -0,0 +1,14 @@
name: 'Facets'
type: module
description: 'Faceted search interfaces that can be used on Search API searchers.'
core_version_requirement: ^9.2 || ^10.0
package: Search
configure: entity.facets_facet.collection
test_dependencies:
- search_api:search_api
- drupal:views
# Information added by Drupal.org packaging script on 2022-04-04
version: '2.0.2'
project: 'facets'
datestamp: 1649070272

View File

@ -0,0 +1,218 @@
<?php
/**
* @file
* Update hooks for the facets module.
*/
use Drupal\facets\Entity\Facet;
use Drupal\facets\Entity\FacetSource;
use Drupal\block\Entity\Block;
/**
* Implements hook_update_dependencies().
*/
function facets_update_dependencies() {
$dependencies = [];
if (version_compare(\Drupal::VERSION, '8.6', '>=')
&& \Drupal::service('module_handler')->moduleExists('block_content')
) {
// block_content_update_8600() adds some fields to Blocks that makes
// facets_update_8006() fail if upgraded at the same time.
$dependencies['facets'][8006] = [
'block_content' => 8600,
];
}
return $dependencies;
}
/**
* Convert facets on Search Api facet sources to use the display plugin.
*/
function facets_update_8001() {
// We changed the way we work with search api facet sources, we're now using
// the SearchApiDisplay plugins that search api ships with. This consolidates
// the external points for facets, sorts, autocomplete and others. This
// refactor made us a better member of the Search API family. It also makes it
// easier for other modules that provide a display to support facets, for
// example, for the search_api_page module.
//
// This only works for the 3 default plugins that we previously shipped. So
// only views that have a page, block, or rest display. The id will get
// replaced from views_page:foo to search_api:views_page__foo.
$old_ids = ['views_page', 'views_block', 'views_rest'];
/** @var \Drupal\facets\FacetInterface[] $entities */
$entities = Facet::loadMultiple();
foreach ($entities as $entity) {
$facetSourceId = $entity->getFacetSourceId();
foreach ($old_ids as $id) {
if (strpos($facetSourceId, $id) !== FALSE) {
$new_id = str_replace($id . ':', 'search_api:' . $id . '__', $facetSourceId);
$entity->setFacetSourceId($new_id);
$entity->save();
}
}
}
/** @var \Drupal\facets\FacetSourceInterface[] $facetsources */
$facetsources = FacetSource::loadMultiple();
foreach ($facetsources as $facetsource) {
$as_array = $facetsource->toArray();
// Replace id and name to new naming scheme.
foreach ($old_ids as $id) {
if (strpos($as_array['id'], $id) !== FALSE) {
$as_array['id'] = str_replace($id . '__', 'search_api__' . $id . '__', $as_array['id']);
$as_array['name'] = str_replace($id . ':', 'search_api:' . $id . '__', $as_array['name']);
}
}
// Create new source.
unset($as_array['uuid']);
$existing = FacetSource::load($as_array['id']);
if (!$existing) {
FacetSource::create($as_array)->save();
// Delete old facet source.
$facetsource->delete();
}
}
}
/**
* Remove 'other_facet' plugin for older versions of facets.
*/
function facets_update_8002() {
$database = \Drupal::database();
$query = $database
->query("SELECT * FROM {config} WHERE data LIKE '%other_facet%'");
$results = $query->fetchAll();
foreach ($results as $result) {
$data = unserialize($result->data);
if (isset($data['visibility']['other_facet'])) {
unset($data['visibility']['other_facet']);
}
$database->update('config')
->fields([
'data' => serialize($data),
])
->condition('name', $result->name)
->execute();
}
}
/**
* WARNING: Facets core search support has been moved into a separate project.
*
* If you are using this feature, you need do download the "facets_core_search"
* module from drupal.org."
*/
function facets_update_8003() {
\Drupal::database()->delete('key_value')
->condition('collection', 'system.schema')
->condition('name', 'core_search_facets')
->execute();
}
/**
* Migrate facets with date widget to use date processor and links widget.
*/
function facets_update_8004() {
foreach (Facet::loadMultiple() as $facet) {
$widget = $facet->getWidget();
if ($widget['type'] === 'datebasic') {
// Set widget to use links instead.
$facet->setWidget('links', ['show_numbers' => $widget['config']['show_numbers']]);
// Migrate widget to processor settings and enable date_item processor.
$settings = [
'date_format' => $widget['config']['date_display'],
'granularity' => $widget['config']['granularity'],
'date_display' => 'actual_date',
];
if ($widget['config']['display_relative']) {
$settings['date_display'] = 'relative_date';
}
$facet->addProcessor([
'processor_id' => 'date_item',
'weights' => ['build' => 35],
'settings' => $settings,
]);
$facet->save();
}
}
}
/**
* Migrate facets with granular widget to use date processors + links widget.
*/
function facets_update_8005() {
foreach (Facet::loadMultiple() as $facet) {
$widget = $facet->getWidget();
if ($widget['type'] === 'numericgranular') {
// Set widget to use links instead.
$facet->setWidget('links', ['show_numbers' => $widget['config']['show_numbers']]);
// Migrate widget to processor settings and enable date_item processor.
$settings = [
'granularity' => $widget['config']['granularity'],
];
$facet->addProcessor([
'processor_id' => 'granularity_item',
'weights' => ['build' => 35],
'settings' => $settings,
]);
$facet->save();
}
}
}
/**
* Update facet blocks configuration with a block id used for AJAX support.
*/
function facets_update_8006() {
$query = \Drupal::entityQuery('block')
->condition('plugin', 'facet_block', 'STARTS_WITH')
->execute();
foreach ($query as $block_id) {
$block = Block::load($block_id);
$configuration = $block->get('settings');
$configuration['block_id'] = $block_id;
$block->set('settings', $configuration);
$block->save();
}
}
/**
* Resave facets for consistent configuration export.
*/
function facets_update_8007() {
// Moved to facets_update_8009().
}
/**
* Support different hierarchy plugin types.
*/
function facets_update_8008() {
$config_factory = \Drupal::configFactory();
foreach ($config_factory->listAll('facets.facet.') as $facet_config_name) {
$facet = $config_factory->getEditable($facet_config_name);
$facet->set('hierarchy', ['type' => 'taxonomy', 'config' => []]);
$facet->save(TRUE);
}
}
/**
* Resave facets for consistent configuration export.
*/
function facets_update_8009() {
$facets = Facet::loadMultiple();
foreach ($facets as $facet) {
$facet->save();
}
}

View File

@ -0,0 +1,82 @@
drupal.facets.index-active-formatters:
version: VERSION
js:
js/index-active-formatters.js: {}
dependencies:
- core/jquery
- core/drupal
- core/jquery.once
drupal.facets.edit-facet:
version: VERSION
js:
js/edit-facet.js: {}
dependencies:
- core/jquery
- core/drupal
drupal.facets.admin_css:
version: VERSION
css:
theme:
css/facets.admin.css: {}
widget:
version: VERSION
js:
js/base-widget.js: {}
dependencies:
- core/jquery
- core/drupal
- core/jquery.once
drupal.facets.link-widget:
version: VERSION
js:
js/link-widget.js: {}
dependencies:
- facets/widget
drupal.facets.checkbox-widget:
version: VERSION
js:
js/checkbox-widget.js: {}
dependencies:
- facets/widget
drupal.facets.hierarchical:
version: VERSION
css:
theme:
css/hierarchical.css: {}
drupal.facets.general:
version: VERSION
css:
theme:
css/general.css: {}
drupal.facets.dropdown-widget:
version: VERSION
js:
js/dropdown-widget.js: {}
dependencies:
- facets/widget
soft-limit:
version: VERSION
js:
js/soft-limit.js: {}
dependencies:
- core/jquery
- core/jquery.once
- core/drupal
- core/drupalSettings
drupal.facets.views-ajax:
js:
js/facets-views-ajax.js: {}
dependencies:
- facets/widget
- core/drupalSettings
- core/drupal.ajax

View File

@ -0,0 +1,5 @@
# See core/core.link_relation_types.yml
settings-form:
description: A form where a resource of this type can be edited. See customize-form.
clone-form:
description: A form where a resource of this type can be duplicated. See duplicate-form.

View File

@ -0,0 +1,5 @@
entity.facets_facet.add_form:
route_name: entity.facets_facet.add_form
title: 'Add facet'
appears_on:
- entity.facets_facet.collection

View File

@ -0,0 +1,4 @@
entity.facets_facet.edit_form:
title: 'Edit facet'
route_name: 'entity.facets_facet.edit_form'
group: facets_facet

View File

@ -0,0 +1,6 @@
entity.facets_facet.collection:
title: Facets
description: 'Create and configure facets and configure existing facet sources.'
route_name: entity.facets_facet.collection
weight: 30
parent: system.admin_config_search

Some files were not shown because too many files have changed in this diff Show More