add own logic

This commit is contained in:
rnsrk 2024-09-19 22:17:15 +02:00
commit 5cabe99d1e
64 changed files with 6031 additions and 0 deletions

View file

@ -0,0 +1,394 @@
<?php
namespace Drupal\advanced_search\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\search_api\Display\DisplayPluginManager;
use Drupal\views\Entity\View;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides an Islandora Advanced Search block.
*
* @Block(
* id = "advanced_search_block",
* deriver = "Drupal\advanced_search\Plugin\Block\AdvancedSearchBlockDeriver",
* admin_label = @Translation("Islandora Advanced Search"),
* category = @Translation("Islandora"),
* )
*/
class AdvancedSearchBlock extends BlockBase implements ContainerFactoryPluginInterface {
use ViewAndDisplayIdentifiersTrait;
// CSS classes used to bind table-drag behavior to.
const WEIGHT_FIELD_CLASS = 'field-weight';
const DISPLAY_FIELD_CLASS = 'field-display';
// Regions in the table which denote if a given field
// is visible in the Advanced Search Form or not.
const REGION_VISIBLE = 'visible';
const REGION_HIDDEN = 'hidden';
// Keys for settings.
const SETTING_FIELDS = 'fields';
const SETTING_CONTEXTUAL_FILTER = 'context_filter';
/**
* The display plugin manager.
*
* @var \Drupal\search_api\Display\DisplayPluginManager
*/
protected $displayPluginManager;
/**
* The clone of the current request object.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* The view this block affects.
*
* @var \Drupal\views\Entity\View
*/
protected $view;
/**
* The view display this block affects.
*
* @var array
*/
protected $display;
/**
* Form Builder.
*
* @var \Drupal\Core\Form\FormBuilderInterface
*/
protected $formBuilder;
/**
* Construct a AdvancedSearchBlock 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 string $plugin_definition
* The plugin implementation definition.
* @param \Drupal\search_api\Display\DisplayPluginManager $display_plugin_manager
* The display plugin manager.
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The form builder service used to build the search form.
* @param \Symfony\Component\HttpFoundation\Request $request
* A request object for the current request.
*/
final public function __construct(array $configuration, $plugin_id, $plugin_definition, DisplayPluginManager $display_plugin_manager, FormBuilderInterface $form_builder, Request $request) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->displayPluginManager = $display_plugin_manager;
[$view_id, $display_id] = preg_split('/__/', $this->getDerivativeId(), 2);
$this->view = View::Load($view_id);
$this->display = $this->view->getDisplay($display_id);
$this->formBuilder = $form_builder;
$this->request = clone $request;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('plugin.manager.search_api.display'),
$container->get('form_builder'),
$container->get('request_stack')->getMainRequest()
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
self::SETTING_FIELDS => [],
self::SETTING_CONTEXTUAL_FILTER => NULL,
];
}
/**
* Fields which can be enabled / disabled for display in the search form.
*
* @return \Drupal\search_api\Item\FieldInterface[]
* The $fields sorted by label.
*/
protected function getFields() {
$fields = $this->getIndex()->getFields();
// First pass sort on label, secondary sort will be used
// when looking at existing configuration for this block.
uasort($fields, function ($a, $b) {
return strcmp($a->getLabel(), $b->getLabel());
});
return $fields;
}
/**
* Get regions of table to display.
*
* @return array
* The properties of each region used for building the table of fields.
*/
protected function getRegions() {
// Classes for select fields like 'weight' and 'display' are hard-coded
// and used in js/islandora-advanced-search.admin.js.
return [
'visible' => [
'title' => $this->t('Visible'),
'invisible' => TRUE,
'message' => $this->t('No search field is visible.'),
'weight' => self::WEIGHT_FIELD_CLASS . '-visible',
'display' => self::DISPLAY_FIELD_CLASS . '-visible',
],
'hidden' => [
'title' => $this->t('Hidden'),
'invisible' => FALSE,
'message' => $this->t('No search field is hidden.'),
'weight' => self::WEIGHT_FIELD_CLASS . '-hidden',
'display' => self::DISPLAY_FIELD_CLASS . '-hidden',
],
];
}
/**
* Options for field display derived from the available regions.
*
* @return array
* Display select field options.
*/
protected function getDisplayOptions() {
$options = [];
foreach ($this->getRegions() as $region => $settings) {
$options[$region] = $settings['title'];
}
return $options;
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
// At most we will have one row per field.
$fields = $this->getFields();
$weight_delta = round(count($fields) / 2);
// Group each field into a region given our current configuration.
$visible_fields = $this->configuration[self::SETTING_FIELDS];
$regions = $this->getRegions();
$display_options = $this->getDisplayOptions();
// Field rows are grouped by the region in which they are displayed.
$field_rows = array_fill_keys(array_keys($regions), []);
foreach ($fields as $field) {
// If a field exists in the blocks configuration than it is 'visible' and
// its weight is equivalent to its order in the configuration,
// i.e. its index.
$identifier = $field->getFieldIdentifier();
$weight = array_search($identifier, $visible_fields);
$visible = $weight !== FALSE;
$region = $visible ? self::REGION_VISIBLE : self::REGION_HIDDEN;
$field_rows[$region][$identifier] = [
'#attributes' => [
'class' => ['draggable'],
],
'label' => ['#plain_text' => $field->getLabel()],
'identifier' => ['#plain_text' => $identifier],
'weight' => [
'#type' => 'weight',
'#title' => $this->t('Weight'),
'#title_display' => 'invisible',
'#default_value' => $visible ? $weight : 0,
'#delta' => $weight_delta,
'#attributes' => [
'class' => [self::WEIGHT_FIELD_CLASS, $regions[$region]['weight']],
],
],
'display' => [
'#type' => 'select',
'#title' => $this->t('Display'),
'#title_display' => 'invisible',
'#options' => $display_options,
'#default_value' => $region,
'#attributes' => [
'class' => [self::DISPLAY_FIELD_CLASS, $regions[$region]['display']],
],
],
];
}
// Sort the visible rows by their weight.
uasort($field_rows[self::REGION_VISIBLE], function ($a, $b) {
$a = $a['weight']['#default_value'];
$b = $b['weight']['#default_value'];
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
});
// Build Rows.
$rows = [];
$table_drag = [];
foreach ($regions as $region => $properties) {
$rows += [
// Conditionally display region title as a row.
"region-$region" => $properties['invisible'] ? NULL : [
'#attributes' => [
'class' => ['region-title', "region-title-$region"],
],
'label' => [
'#plain_text' => $properties['title'],
'#wrapper_attributes' => [
'colspan' => 4,
],
],
],
// Will dynamically display if the region has fields or not controlled
// by Drupal behaviors in js/islandora-advanced-search.admin.js.
"region-$region-message" => [
'#attributes' => [
'class' => [
'region-message',
"region-$region-message",
empty($field_rows[$region]) ? 'region-empty' : 'region-populated',
],
],
'message' => [
'#markup' => '<em>' . $properties['message'] . '</em>',
'#wrapper_attributes' => [
'colspan' => 4,
],
],
],
];
// Include field rows in this region.
$rows += $field_rows[$region];
// Configure order by weight field in region.
$table_drag[] = [
'action' => 'order',
'relationship' => 'sibling',
'group' => self::WEIGHT_FIELD_CLASS,
'subgroup' => $properties['weight'],
'source' => self::WEIGHT_FIELD_CLASS,
];
// Configure drag action for display field in region.
$table_drag[] = [
'action' => 'match',
'relationship' => 'sibling',
'group' => self::DISPLAY_FIELD_CLASS,
'subgroup' => $properties['display'],
'source' => self::DISPLAY_FIELD_CLASS,
];
}
$form[self::SETTING_FIELDS] = [
'#type' => 'table',
'#attributes' => [
// Identifier is hard-coded and used in
// js/islandora-advanced-search.admin.js.
'id' => 'advanced-search-fields',
],
'#header' => [
$this->t('Label'),
$this->t('Field'),
$this->t('Weight'),
$this->t('Display'),
],
'#empty' => $this->t('No search fields, please check search index configuration.'),
'#tabledrag' => $table_drag,
] + $rows;
// If there is contextual filters associated with the display that means
// we can filter on collection / sub-collection. Allow the user to choose
// which filters collections.
$id = NULL;
$field = NULL;
$options = [];
if (isset($this->display['display_options']['arguments'])) {
foreach ($this->display['display_options']['arguments'] as $context_filter) {
$id = $context_filter['id'];
$field = $context_filter['field'];
if (isset($fields[$field])) {
$options[$id] = $fields[$field]->getLabel() . ':' . $id;
}
}
}
if (count($options) > 0) {
$form[self::SETTING_CONTEXTUAL_FILTER] = [
'#type' => 'select',
'#title' => $this->t('Context Filter'),
'#description' => $this->t('If more than one <strong>Context Filter</strong> is defined, specify which is used to <strong>include</strong> only <strong>direct children</strong> of the Collection as it will disabled to allow recursive searching.'),
'#options' => $options,
'#default_value' => $this->configuration[self::SETTING_CONTEXTUAL_FILTER],
'#multiple' => FALSE,
'#required' => FALSE,
'#size' => count($options) + 1,
];
}
$form['#attributes']['class'][] = 'clearfix';
$form['#attached']['library'][] = 'advanced_search/advanced.search.admin';
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$fields = array_filter($values[self::SETTING_FIELDS], function ($field) {
return $field['display'] == 'visible';
});
uasort($fields, '\Drupal\Component\Utility\SortArray::sortByWeightElement');
$this->configuration[self::SETTING_FIELDS] = array_keys($fields);
if (isset($values[self::SETTING_CONTEXTUAL_FILTER])) {
$this->configuration[self::SETTING_CONTEXTUAL_FILTER] = $values[self::SETTING_CONTEXTUAL_FILTER];
}
}
/**
* {@inheritdoc}
*/
public function build() {
$fields = $this->getIndex()->getFields();
$configured_fields = [];
foreach ($this->configuration[self::SETTING_FIELDS] as $identifier) {
$configured_fields[$identifier] = $fields[$identifier];
}
return $this->formBuilder->getForm('Drupal\advanced_search\Form\AdvancedSearchForm', $this->view, $this->display, $configured_fields, $this->configuration[self::SETTING_CONTEXTUAL_FILTER]);
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
// The block cannot be cached, because it must always match the current
// search results.
return 0;
}
/**
* Get Search Index.
*/
protected function getIndex() {
$id = $this->getDerivativeId();
return $this->displayPluginManager->createInstance("views_{$this->display['display_plugin']}:{$id}")->getIndex();
}
}

View file

@ -0,0 +1,17 @@
<?php
namespace Drupal\advanced_search\Plugin\Block;
/**
* Deriver for AdvancedSearchBlock.
*/
class AdvancedSearchBlockDeriver extends SearchApiDisplayBlockDeriver {
/**
* {@inheritdoc}
*/
protected function label() {
return $this->t('Advanced Search');
}
}

View file

@ -0,0 +1,102 @@
<?php
namespace Drupal\advanced_search\Plugin\Block;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* This deriver creates a block for every search_api.display.
*/
abstract class SearchApiDisplayBlockDeriver implements ContainerDeriverInterface {
use StringTranslationTrait;
/**
* List of derivative definitions.
*
* @var array
*/
protected $derivatives = [];
/**
* The entity storage for the view.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $storage;
/**
* The display manager for the search_api.
*
* @var \Drupal\search_api\Display\DisplayPluginManager
*/
protected $displayPluginManager;
/**
* Label for the SearchApiDisplayBlockDriver.
*/
abstract protected function label();
/**
* The constructor.
*/
final public function __construct(ContainerInterface $container, $base_plugin_id) {}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
$deriver = new static($container, $base_plugin_id);
$deriver->storage = $container->get('entity_type.manager')->getStorage('view');
$deriver->displayPluginManager = $container->get('plugin.manager.search_api.display');
return $deriver;
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinition($derivative_id, $base_plugin_definition) {
$derivatives = $this->getDerivativeDefinitions($base_plugin_definition);
return $derivatives[$derivative_id] ?? NULL;
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
$base_plugin_id = $base_plugin_definition['id'];
if (!isset($this->derivatives[$base_plugin_id])) {
$plugin_derivatives = [];
foreach ($this->displayPluginManager->getDefinitions() as $display_definition) {
$view_id = $display_definition['view_id'];
$view_display = $display_definition['view_display'];
// The derived block needs both the view / display identifiers to
// construct the pager.
$machine_name = "{$view_id}__{$view_display}";
/** @var \Drupal\views\ViewEntityInterface $view */
$view = $this->storage->load($view_id);
$display = $view->getDisplay($view_display);
$plugin_derivatives[$machine_name] = [
'id' => $base_plugin_id . PluginBase::DERIVATIVE_SEPARATOR . $machine_name,
'label' => $this->label(),
'admin_label' => $this->t(':view: :label for :display', [
':view' => $view->label(),
':label' => $this->label(),
':display' => $display['display_title'],
]),
] + $base_plugin_definition;
}
$this->derivatives[$base_plugin_id] = $plugin_derivatives;
}
return $this->derivatives[$base_plugin_id];
}
}

View file

@ -0,0 +1,107 @@
<?php
namespace Drupal\advanced_search\Plugin\Block;
use Drupal\advanced_search\Form\SearchForm;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\advanced_search\Form\SettingsForm;
/**
* Provides a 'SearchBlock' block.
*
* @Block(
* id = "search_block",
* admin_label = @Translation("Search"),
* )
*/
class SearchBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$config = \Drupal::config(SettingsForm::CONFIG_NAME);
$form['search-attributes'] = [
'#type' => 'fieldset',
'#title' => $this->t('Configure Search Block'),
];
if (!$config->get(SettingsForm::SEARCH_ALL_FIELDS_FLAG)) {
$form['search-attributes'][SettingsForm::SEARCH_ALL_FIELDS_FLAG] = [
'#markup' => $this
->t('<strong>This block is required to enable searching all fields for the Advanced Search.
To proceed, please enable "Enable searching all fields" in
<a href="/admin/config/search/advanced" target="_blank">Advanced Seach Configuration</a></strong>.'),
];
}
else {
$views = \Drupal::EntityTypeManager()->getStorage('view')->loadMultiple();
$options = [];
foreach ($views as $view_name => $view) {
$displays = $view->get("display");
foreach ($displays as $display) {
if ($display['display_plugin'] === "page") {
$options["view.$view_name" . "." . $display['id']] = "view.$view_name" . "." . $display['id'];
}
}
}
$form['search-attributes']['view_machine_name'] = [
'#type' => 'select',
'#title' => $this->t('Select the machine name of Search Results Page:'),
'#default_value' => $this->configuration['search_view_machine_name'],
'#options' => $options,
];
$form['search-attributes']['search_textfield'] = [
'#type' => 'textfield',
'#title' => $this->t('Search Keyword Text field label:'),
'#default_value' => $this->configuration['search_textfield_label'],
'#maxlength' => 255,
];
$form['search-attributes']['search_placeholder_textfield'] = [
'#type' => 'textfield',
'#title' => $this->t('Search Keyword text field placeholder:'),
'#default_value' => $this->configuration['search_placeholder'],
'#maxlength' => 255,
];
$form['search-attributes']['search_submit'] = [
'#type' => 'textfield',
'#title' => $this->t('Search Button Label:'),
'#default_value' => $this->configuration['search_submit_label'],
'#maxlength' => 255,
];
}
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['block_id'] = $form_state->getBuildInfo()['callback_object']->getEntity()->id();
$this->configuration['search_view_machine_name'] = $form_state->getValues()['search-attributes']['view_machine_name'];
$this->configuration['search_textfield_label'] = $form_state->getValues()['search-attributes']['search_textfield'];
$this->configuration['search_placeholder'] = $form_state->getValues()['search-attributes']['search_placeholder_textfield'];
$this->configuration['search_submit_label'] = $form_state->getValues()['search-attributes']['search_submit'];
}
/**
* {@inheritdoc}
*/
public function build() {
$config = $this->getConfiguration();
$blockId = $config['block_id'];
$searchForm = new SearchForm($blockId);
return \Drupal::formBuilder()->getForm($searchForm);
}
}

View file

@ -0,0 +1,328 @@
<?php
namespace Drupal\advanced_search\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Url;
use Drupal\advanced_search\AdvancedSearchQuery;
use Drupal\views\Entity\View;
use Drupal\views\Plugin\views\pager\SqlBase;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Drupal\advanced_search\Form\SettingsForm;
/**
* Provides a 'AjaxViewBlock' block.
*
* @Block(
* id = "advanced_search_result_pager",
* deriver = "Drupal\advanced_search\Plugin\Block\SearchResultsPagerBlockDeriver",
* admin_label = @Translation("Search Results Pager"),
* category = @Translation("Islandora"),
* )
*/
class SearchResultsPagerBlock extends BlockBase implements ContainerFactoryPluginInterface {
use ViewAndDisplayIdentifiersTrait;
/**
* The clone of the current request object.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* Construct a FacetBlock 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 string $plugin_definition
* The plugin implementation definition.
* @param \Symfony\Component\HttpFoundation\Request $request
* A request object for the current request.
*/
final public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->request = clone $request;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('request_stack')->getMainRequest()
);
}
/**
* {@inheritdoc}
*/
public function build() {
$id = $this->getDerivativeId();
[$view_id, $display_id] = $this->getViewAndDisplayIdentifiers();
$view = View::Load($view_id);
$view_executable = $view->getExecutable();
$view_executable->setDisplay($display_id);
// Allow advanced search to alter the query.
$advanced_search_query = new AdvancedSearchQuery();
$advanced_search_query->alterView($this->request, $view_executable, $display_id);
$view_executable->execute();
$pager = $view_executable->getPager();
$exposed_input = $view_executable->getExposedInput();
$query_parameters = $this->request->query->all();
$build = [
'#attached' => [
'drupalSettings' => [
'advanced_search_pager_views_ajax' => [
$id => [
'view_id' => $view_id,
'current_display_id' => $display_id,
'ajax_path' => '/views/ajax',
],
],
],
],
'#attributes' => [
'class' => ['advanced_search_result_pager'],
'data-drupal-pager-id' => $id,
],
'result_summary' => $this->buildResultsSummary($view_executable),
'container' => [
'#prefix' => '<div class="pager__group">',
'#suffix' => '</div>',
'results_per_page_links' => $this->buildResultsPerPageLinks($pager, $query_parameters),
'display_links' => $this->buildDisplayLinks($query_parameters),
'sort_by' => $this->buildSortByForm($view_executable->sort, $query_parameters),
'pager' => array_merge($pager->render($exposed_input), ['#wrapper_attributes' => ['class' => ['container']]]),
],
];
return $build;
}
/**
* Build the results summary portion of the pager.
*
* @param Drupal\views\ViewExecutable $view_executable
* The view to build the summary for.
*
* @return array
* A renderable array that represents the current page, and number of
* results in the view.
*/
protected function buildResultsSummary(ViewExecutable $view_executable) {
$current_page = (int) $view_executable->getCurrentPage() + 1;
$per_page = (int) $view_executable->getItemsPerPage();
$total = $view_executable->total_rows ?? count($view_executable->result);
// If there is no result the "start" and "current_record_count" should be
// equal to 0. To have the same calculation logic, we use a "start offset"
// to handle all the cases.
$start_offset = empty($total) ? 0 : 1;
if ($per_page === 0) {
$start = $start_offset;
$end = $total;
}
else {
$total_count = $current_page * $per_page;
if ($total_count > $total) {
$total_count = $total;
}
$start = ($current_page - 1) * $per_page + $start_offset;
$end = $total_count;
}
if (!empty($total)) {
// Return as render array.
return [
'#prefix' => '<div class="pager__summary">',
'#suffix' => '</div>',
'#markup' => $this->t('Displaying @start - @end of @total', [
'@start' => $start,
'@end' => $end,
'@total' => $total,
]),
];
}
return [];
}
/**
* Build the results per page portion of the pager.
*
* @param Drupal\views\Plugin\views\pager\SqlBase $pager
* The pager for the view.
* @param array $query_parameters
* The query parameters used to change the number of results per page.
*
* @return array
* A renderable array representing the results per page portion of pager.
*/
protected function buildResultsPerPageLinks(SqlBase $pager, array $query_parameters) {
$active_items_per_page = $query_parameters['items_per_page'] ?? $pager->options['items_per_page'];
$items_per_page_options = array_map(function ($value) {
return trim($value);
}, explode(',', $pager->options['expose']['items_per_page_options']));
$items = [];
foreach ($items_per_page_options as $items_per_page) {
$url = Url::fromRoute('<current>', [], [
// When changing the number of items displayed always return the user
// to the first page.
'query' => array_merge($query_parameters, [
'items_per_page' => $items_per_page,
'page' => 0,
]),
'absolute' => TRUE,
]);
$active = $items_per_page == $active_items_per_page;
$items[] = [
'#type' => 'link',
'#url' => $url,
'#title' => $items_per_page,
'#attributes' => [
'aria-label' => $this->t("@item items per page", ["@item" => $items_per_page]),
'class' => $active ?
['pager__link', 'pager__link--is-active', 'pager__itemsperpage'] :
['pager__link', 'pager__itemsperpage'],
],
'#wrapper_attributes' => [
'class' => $active ? ['pager__item', 'is-active'] : ['pager__item'],
],
];
}
return [
'#theme' => 'item_list',
'#title' => $this->t('Results per page'),
'#list_type' => 'ul',
'#items' => $items,
'#attributes' => [],
'#wrapper_attributes' => ['class' => ['pager__results', 'container']],
];
}
/**
* Build the display links portion of the pager (list/grid).
*
* @param array $query_parameters
* The query parameters used to change the display format.
*
* @return array
* A renderable array representing the display links portion of pager.
*/
protected function buildDisplayLinks(array $query_parameters) {
$config = \Drupal::config(SettingsForm::CONFIG_NAME);
$display_options = [];
if ($config->get(SettingsForm::DISPLAY_LIST_FLAG) == 1) {
$display_options['list'] = [
'icon' => 'fa-list',
'title' => $this->t('List'),
];
}
if ($config->get(SettingsForm::DISPLAY_GRID_FLAG) == 1) {
$display_options['grid'] = [
'icon' => 'fa-th',
'title' => $this->t('Grid'),
];
}
$active_display = $query_parameters['display'] ?? $config->get(SettingsForm::DISPLAY_DEFAULT);
$items = [];
foreach ($display_options as $display => $options) {
$url = Url::fromRoute('<current>', [], [
'query' => array_merge($query_parameters, ['display' => $display]),
'absolute' => TRUE,
]);
$text = "<i class='fa {$options['icon']}' aria-hidden='true'>&nbsp;</i><span class='display-mode'>{$options['title']}</span>";
$active = $active_display == $display;
$items[] = [
'#type' => 'link',
'#url' => $url,
'#title' => Markup::create($text),
'#attributes' => [
'class' => $active ?
['pager__link', 'pager__link--is-active', 'pager__display'] :
['pager__link', 'pager__display'],
'aria-label' => $this->t("Display as @link", ["@link" => Markup::create($text)]),
],
'#wrapper_attributes' => [
'class' => $active ? ['pager__item', 'is-active'] : ['pager__item'],
],
];
}
return [
'#theme' => 'item_list',
'#list_type' => 'ul',
'#items' => $items,
'#attributes' => [],
'#wrapper_attributes' => ['class' => ['pager__display', 'container']],
];
}
/**
* Build the sort by portion of the pager.
*
* @param array $sort_criteria
* The search fields which can be sorted.
* @param array $query_parameters
* The query parameters used to change the display format.
*
* @return array
* A renderable array representing the sort by portion of pager.
*/
protected function buildSortByForm(array $sort_criteria, array $query_parameters) {
$default_order = $query_parameters['sort_order'] ?? 'DESC';
$default_sort_by = $query_parameters['sort_by'] ?? 'search_api_relevance';
$default_value = $default_sort_by . '_' . strtolower($default_order);
$options = [];
$options_attributes = [];
// Not sure if this will work without defining a sort per direction.
foreach ($sort_criteria as $sort) {
if ($sort->options['exposed'] == TRUE) {
$id = $sort->options['id'];
// Label should be translated via views already.
$label = $sort->options['expose']['label'];
$asc = "{$id}_asc";
$desc = "{$id}_desc";
$options[$asc] = "{$label}";
$options[$desc] = "{$label}";
$options_attributes[$asc] = [
'data-sort_by' => $id,
'data-sort_order' => 'ASC',
];
$options_attributes[$desc] = [
'data-sort_by' => $id,
'data-sort_order' => 'DESC',
];
}
}
return [
'#type' => 'select',
'#title' => 'Sort',
'#title_display' => 'invisible',
'#options' => $options,
'#options_attributes' => $options_attributes,
'#attributes' => ['autocomplete' => 'off', "aria-label" => "Sort By"],
'#wrapper_attributes' => ['class' => ['pager__sort', 'container']],
'#name' => 'order',
'#value' => $default_value,
];
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
// The block cannot be cached, because it must always match the current
// search results.
return 0;
}
}

View file

@ -0,0 +1,17 @@
<?php
namespace Drupal\advanced_search\Plugin\Block;
/**
* This deriver creates a block for every search_api.display.
*/
class SearchResultsPagerBlockDeriver extends SearchApiDisplayBlockDeriver {
/**
* {@inheritdoc}
*/
protected function label() {
return $this->t('Search Results Pager');
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\advanced_search\Plugin\Block;
/**
* Gets the view and display identifiers used to create this block.
*
* @see Drupal\Component\Plugin\Discovery\DiscoveryInterface
*/
trait ViewAndDisplayIdentifiersTrait {
/**
* {@inheritdoc}
*/
abstract public function getDerivativeId();
/**
* Gets the View and View Display identifiers used to derive this block.
*
* @return string[]
* Returns an array of two strings where the first is the View identifier
* and the second is the View Display identifier associated with the view
* used to derive this block.
*/
public function getViewAndDisplayIdentifiers() {
$id = $this->getDerivativeId();
return preg_split('/__/', $id, 2);
}
}