Skip to content
......@@ -7,12 +7,10 @@
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\search_api\Query\QueryInterface;
use Drupal\facets\FacetInterface;
/**
* Implements hook_help().
*/
function facets_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
// Main module help for the facets module.
......
facets_processor:
label: Facets processor
plugin_manager_service_id: plugin.manager.facets.processor
plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
facets_url_processor:
label: Facets URL processor
plugin_manager_service_id: plugin.manager.facets.url_processor
plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
facets_facet_source:
label: Facets source
plugin_manager_service_id: plugin.manager.facets.facet_source
plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
facets_widget:
label: Facets widget
plugin_manager_service_id: plugin.manager.facets.widget
plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
facets_query_type:
label: Facets query type
plugin_manager_service_id: plugin.manager.facets.query_type
plugin_definition_decorator_class: \Drupal\plugin\PluginDefinition\ArrayPluginDefinitionDecorator
facets.overview:
path: '/admin/config/search/facets'
defaults:
_title: 'Facet API'
_title: 'Facets'
_entity_list: 'facets_facet'
requirements:
_entity_create_access: 'facets_facet'
......@@ -41,3 +41,11 @@ entity.facets_facet.display_form:
_entity_form: 'facets_facet.display'
requirements:
_entity_access: 'facets_facet.edit'
entity.facets_facet_source.edit_form:
path: '/admin/config/search/facets/facet-sources/{source_id}/edit'
defaults:
_controller: '\Drupal\facets\Controller\FacetSourceController::facetSourceConfigForm'
_title: 'Edit facet source configuration'
requirements:
_entity_create_access: 'facets_facet'
......@@ -11,6 +11,9 @@ services:
plugin.manager.facets.processor:
class: Drupal\facets\Processor\ProcessorPluginManager
arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@string_translation']
plugin.manager.facets.url_processor:
class: Drupal\facets\UrlProcessor\UrlProcessorPluginManager
parent: default_plugin_manager
facets.manager:
class: Drupal\facets\FacetManager\DefaultFacetManager
arguments:
......@@ -19,9 +22,3 @@ services:
- '@plugin.manager.facets.facet_source'
- '@plugin.manager.facets.processor'
- '@entity_type.manager'
facets.facet_context:
class: Drupal\facets\ContextProvider\FacetContextProvider
arguments: ['@entity_type.manager']
tags:
- { name: context_provider }
/**
* @file
* Attaches show/hide functionality to checkboxes in the "Processor" tab.
*/
(function ($) {
"use strict";
Drupal.behaviors.facetsIndexFormatter = {
attach: function (context, settings) {
$('.search-api-status-wrapper input.form-checkbox', context).each(function () {
var $checkbox = $(this);
var processor_id = $checkbox.data('id');
var $rows = $('.search-api-processor-weight--' + processor_id, context);
var tab = $('.search-api-processor-settings-' + processor_id, context).data('verticalTab');
// Bind a click handler to this checkbox to conditionally show and hide
// the processor's table row and vertical tab pane.
$checkbox.on('click.searchApiUpdate', function () {
if ($checkbox.is(':checked')) {
$rows.show();
if (tab) {
tab.tabShow().updateSummary();
}
}
else {
$rows.hide();
if (tab) {
tab.tabHide().updateSummary();
}
}
});
// Attach summary for configurable items (only for screen-readers).
if (tab) {
tab.details.drupalSetSummary(function () {
return $checkbox.is(':checked') ? Drupal.t('Enabled') : Drupal.t('Disabled');
});
}
// Trigger our bound click handler to update elements to initial state.
$checkbox.triggerHandler('click.searchApiUpdate');
});
}
};
})(jQuery);
......@@ -2,7 +2,7 @@
/**
* @file
* Contains \Drupal\facets\Annotation\FacetsFacet.
* Contains \Drupal\facets\Annotation\FacetsFacetSource.
*/
namespace Drupal\facets\Annotation;
......@@ -13,7 +13,7 @@ use Drupal\Component\Annotation\Plugin;
* Defines a Facets facet source annotation.
*
* @see \Drupal\facets\FacetSource\FacetSourcePluginManager
* @see \Drupal\facets\FacetSource\FacetSourceInterface
* @see \Drupal\facets\FacetSource\FacetSourcePluginInterface
* @see \Drupal\facets\FacetSource\FacetSourcePluginBase
* @see plugin_api
*
......@@ -29,7 +29,7 @@ class FacetsFacetSource extends Plugin {
public $id;
/**
* The human-readable name of the facet soruce plugin.
* The human-readable name of the facet source plugin.
*
* @ingroup plugin_translatable
*
......
<?php
/**
* @file
* Contains \Drupal\facets\Annotation\FacetsUrlProcessor.
*/
namespace Drupal\facets\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a Facets URL Processor annotation.
*
* @see \Drupal\facets\Processor\ProcessorPluginManager
* @see plugin_api
*
* @ingroup plugin_api
*
* @Annotation
*/
class FacetsUrlProcessor extends Plugin {
/**
* The URL processor plugin id.
*
* @var string
*/
public $id;
/**
* The human-readable name of the URL processor plugin.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $label;
/**
* The URL processor description.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $description;
}
<?php
/**
* @file
* Contains \Drupal\facets\ContextProvider\FacetContextProvider.
*/
namespace Drupal\facets\ContextProvider;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* A provider for the core context system for facets.
*
* This provider is a provider for core's context system, it makes integration
* with blocks, panels and other layout systems easy.
*/
class FacetContextProvider implements ContextProviderInterface {
use StringTranslationTrait;
protected $facetStorage;
/**
* Creates a new instance of the context provider.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->facetStorage = $entity_type_manager->getStorage('facets_facet');
}
/**
* {@inheritdoc}
*/
public function getRuntimeContexts(array $unqualified_context_ids = []) {
$ids = $this->facetStorage->getQuery()
->condition('uuid', $unqualified_context_ids, 'IN')
->execute();
$contexts = [];
foreach ($this->facetStorage->loadMultiple($ids) as $facet) {
$context = new Context(new ContextDefinition('entity:facets_facet'), $facet);
$contexts[$facet->uuid()] = $context;
}
return $contexts;
}
/**
* {@inheritdoc}
*/
public function getAvailableContexts() {
$facets = $this->facetStorage->loadMultiple();
$contexts = [];
/** @var \Drupal\facets\FacetInterface $facet */
foreach ($facets as $facet) {
$context = new Context(
new ContextDefinition('entity:facets_facet', $facet->label()),
$facet
);
$contexts[$facet->uuid()] = $context;
}
return $contexts;
}
}
<?php
/**
* @file
* Contains \Drupal\facets\Controller\FacetSourceController.
*/
namespace Drupal\facets\Controller;
use Drupal\Core\Controller\ControllerBase;
/**
* Provides route responses for facet source configuration.
*/
class FacetSourceController extends ControllerBase {
/**
* Configuration for the facet source.
*
* @param string $source_id
* The plugin id.
*
* @return array
* A renderable array containing the form.
*/
public function facetSourceConfigForm($source_id) {
// Returns the render array of the FacetSourceConfigForm.
return $this->formBuilder()->getForm('\Drupal\facets\Form\FacetSourceEditForm');
}
}
......@@ -11,7 +11,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\facets\FacetInterface;
/**
* Defines the search index configuration entity.
* Defines the facet configuration entity.
*
* @ConfigEntityType(
* id = "facets_facet",
......@@ -37,13 +37,18 @@ use Drupal\facets\FacetInterface;
* config_export = {
* "id",
* "name",
* "url_alias",
* "field_identifier",
* "query_type_name",
* "facet_source_id",
* "widget",
* "widget_configs",
* "options",
* "query_operator",
* "exclude",
* "only_visible_when_facet_source_is_visible",
* "processor_configs",
* "empty_behavior",
* "facet_configs",
* },
* links = {
* "canonical" = "/admin/config/search/facets",
......@@ -57,28 +62,35 @@ use Drupal\facets\FacetInterface;
class Facet extends ConfigEntityBase implements FacetInterface {
/**
* The ID of the index.
* The ID of the facet.
*
* @var string
*/
protected $id;
/**
* A name to be displayed for the index.
* A name to be displayed for the facet.
*
* @var string
*/
protected $name;
/**
* A string describing the index.
* The name for the parameter when used in the URL.
*
* @var string
*/
protected $url_alias;
/**
* A string describing the facet.
*
* @var string
*/
protected $description;
/**
* A string describing the widget.
* The plugin name of the widget.
*
* @var string
*/
......@@ -87,18 +99,23 @@ class Facet extends ConfigEntityBase implements FacetInterface {
/**
* Configuration for the widget. This is a key-value stored array.
*
* @var string
* @var array
*/
protected $widget_configs;
protected $widget_configs = [];
/**
* An array of options configuring this index.
* The operator to hand over to the query, currently AND | OR.
*
* @var array
* @var string
*/
protected $query_operator;
/**
* A boolean flag indicating if search should exclude selected facets.
*
* @see getOptions()
* @var bool
*/
protected $options = array();
protected $exclude;
/**
* The field identifier.
......@@ -131,7 +148,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
/**
* The facet source belonging to this facet.
*
* @var \Drupal\facets\FacetSource\FacetSourceInterface
* @var \Drupal\facets\FacetSource\FacetSourcePluginInterface
*
* @see getFacetSource()
*/
......@@ -151,6 +168,18 @@ class Facet extends ConfigEntityBase implements FacetInterface {
*/
protected $results = [];
/**
* The results.
*
* @var \Drupal\facets\Result\ResultInterface[]
*/
protected $unfiltered_results = [];
/**
* An array of active values.
*
* @var string[]
*/
protected $active_values = [];
/**
......@@ -169,6 +198,20 @@ class Facet extends ConfigEntityBase implements FacetInterface {
*/
protected $processors;
/**
* Configuration for the processors. This is an array of arrays.
*
* @var array
*/
protected $processor_configs = [];
/**
* Additional facet configurations.
*
* @var array
*/
protected $facet_configs = [];
/**
* Is the facet only visible when the facet source is only visible.
*
......@@ -179,6 +222,13 @@ class Facet extends ConfigEntityBase implements FacetInterface {
*/
protected $only_visible_when_facet_source_is_visible;
/**
* The no-result configuration.
*
* @var string[];
*/
protected $empty_behavior;
/**
* The widget plugin manager.
*
......@@ -186,6 +236,14 @@ class Facet extends ConfigEntityBase implements FacetInterface {
*/
protected $widget_plugin_manager;
/**
* The facet source config object.
*
* @var \Drupal\Facets\FacetSourceInterface
* The facet source config object.
*/
protected $facetSourceConfig;
/**
* {@inheritdoc}
*/
......@@ -194,7 +252,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
}
/**
* Gets the widget plugin manager.
* Returns the widget plugin manager.
*
* @return \Drupal\facets\Widget\WidgetPluginManager
* The widget plugin manager.
......@@ -208,8 +266,9 @@ class Facet extends ConfigEntityBase implements FacetInterface {
/**
* {@inheritdoc}
*/
public function id() {
return $this->id;
protected function urlRouteParameters($rel) {
$parameters = parent::urlRouteParameters($rel);
return $parameters;
}
/**
......@@ -227,6 +286,13 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this;
}
/**
* {@inheritdoc}
*/
public function getQueryTypes() {
return $this->query_type_name;
}
/**
* {@inheritdoc}
*/
......@@ -234,6 +300,48 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this->widget;
}
/**
* Retrieves all processors supported by this facet.
*
* @return \Drupal\facets\Processor\ProcessorInterface[]
* The loaded processors, keyed by processor ID.
*/
protected function loadProcessors() {
if (!isset($this->processors)) {
/* @var $processor_plugin_manager \Drupal\facets\Processor\ProcessorPluginManager */
$processor_plugin_manager = \Drupal::service('plugin.manager.facets.processor');
$processor_settings = $this->getProcessorConfigs();
foreach ($processor_plugin_manager->getDefinitions() as $name => $processor_definition) {
if (class_exists($processor_definition['class']) && empty($this->processors[$name])) {
// Create our settings for this processor.
$settings = empty($processor_settings[$name]['settings']) ? [] : $processor_settings[$name]['settings'];
$settings['facet'] = $this;
/* @var $processor \Drupal\facets\Processor\ProcessorInterface */
$processor = $processor_plugin_manager->createInstance($name, $settings);
$this->processors[$name] = $processor;
}
elseif (!class_exists($processor_definition['class'])) {
\Drupal::logger('facets')
->warning('Processor @id specifies a non-existing @class.', array(
'@id' => $name,
'@class' => $processor_definition['class'],
));
}
}
}
return $this->processors;
}
/**
* {@inheritdoc}
*/
public function getProcessorConfigs() {
return !empty($this->processor_configs) ? $this->processor_configs : [];
}
/**
* {@inheritdoc}
*/
......@@ -252,56 +360,54 @@ class Facet extends ConfigEntityBase implements FacetInterface {
/**
* {@inheritdoc}
*/
public function getFieldAlias() {
// For now, create the field alias based on the field identifier.
$field_alias = preg_replace('/[:\/]+/', '_', $this->field_identifier);
return $field_alias;
public function setQueryOperator($operator = '') {
return $this->query_operator = $operator;
}
/**
* {@inheritdoc}
*/
public function setActiveItem($value) {
if (!in_array($value, $this->active_values)) {
$this->active_values[] = $value;
}
public function getQueryOperator() {
return $this->query_operator ?: 'OR';
}
/**
* {@inheritdoc}
*/
public function getActiveItems() {
return $this->active_values;
public function setExclude($exclude) {
return $this->exclude = $exclude;
}
/**
* {@inheritdoc}
*/
public function getOption($name, $default = NULL) {
return isset($this->options[$name]) ? $this->options[$name] : $default;
public function getExclude() {
return $this->exclude;
}
/**
* {@inheritdoc}
*/
public function getOptions() {
return $this->options;
public function getFieldAlias() {
// For now, create the field alias based on the field identifier.
$field_alias = preg_replace('/[:\/]+/', '_', $this->field_identifier);
return $field_alias;
}
/**
* {@inheritdoc}
*/
public function setOption($name, $option) {
$this->options[$name] = $option;
return $this;
public function setActiveItem($value) {
if (!in_array($value, $this->active_values)) {
$this->active_values[] = $value;
}
}
/**
* {@inheritdoc}
*/
public function setOptions(array $options) {
$this->options = $options;
return $this;
public function getActiveItems() {
return $this->active_values;
}
/**
......@@ -319,13 +425,6 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this;
}
/**
* {@inheritdoc}
*/
public function getQueryTypes() {
return $this->query_type_name;
}
/**
* {@inheritdoc}
*/
......@@ -341,6 +440,20 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this->name;
}
/**
* {@inheritdoc}
*/
public function getUrlAlias() {
return $this->url_alias;
}
/**
* {@inheritdoc}
*/
public function setUrlAlias($url_alias) {
$this->url_alias = $url_alias;
}
/**
* {@inheritdoc}
*/
......@@ -357,7 +470,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
if (!$this->facet_source_instance && $this->facet_source_id) {
/* @var $facet_source_plugin_manager \Drupal\facets\FacetSource\FacetSourcePluginManager */
$facet_source_plugin_manager = \Drupal::service('plugin.manager.facets.facet_source');
$this->facet_source_instance = $facet_source_plugin_manager->createInstance($this->facet_source_id);
$this->facet_source_instance = $facet_source_plugin_manager->createInstance($this->facet_source_id, ['facet' => $this]);
}
return $this->facet_source_instance;
......@@ -371,42 +484,37 @@ class Facet extends ConfigEntityBase implements FacetInterface {
}
/**
* Retrieves all processors supported by this facet.
*
* @return \Drupal\facets\Processor\ProcessorInterface[]
* The loaded processors, keyed by processor ID.
* {@inheritdoc}
*/
protected function loadProcessors() {
if (!isset($this->processors)) {
/* @var $processor_plugin_manager \Drupal\facets\Processor\ProcessorPluginManager */
$processor_plugin_manager = \Drupal::service('plugin.manager.facets.processor');
$processor_settings = $this->getOption('processors', []);
public function getFacetSourceConfig() {
// Return the facet source config object, if it's already set on the facet.
if ($this->facetSourceConfig instanceof FacetSource) {
return $this->facetSourceConfig;
}
foreach ($processor_plugin_manager->getDefinitions() as $name => $processor_definition) {
if (class_exists($processor_definition['class']) && empty($this->processors[$name])) {
// Create our settings for this processor.
$settings = empty($processor_settings[$name]['settings']) ? [] : $processor_settings[$name]['settings'];
$settings['facet'] = $this;
$storage = \Drupal::entityTypeManager()->getStorage('facets_facet_source');
$source_id = str_replace(':', '__', $this->facet_source_id);
/* @var $processor \Drupal\facets\Processor\ProcessorInterface */
$processor = $processor_plugin_manager->createInstance($name, $settings);
$this->processors[$name] = $processor;
}
elseif (!class_exists($processor_definition['class'])) {
\Drupal::logger('facets')->warning('Processor @id specifies a non-existing @class.', array('@id' => $name, '@class' => $processor_definition['class']));
}
}
// Load and return the facet source config object from the storage.
$facet_source = $storage->load($source_id);
if ($facet_source instanceof FacetSource) {
$this->facetSourceConfig = $facet_source;
return $this->facetSourceConfig;
}
return $this->processors;
}
// We didn't have a facet source config entity yet for this facet source
// plugin, so we create it on the fly.
$facet_source = new FacetSource(
[
'id' => $source_id,
'name' => $this->facet_source_id,
],
'facets_facet_source'
);
$facet_source->save();
/**
* {@inheritdoc}
*/
protected function urlRouteParameters($rel) {
$parameters = parent::urlRouteParameters($rel);
return $parameters;
$this->facetSourceConfig = $facet_source;
return $this->facetSourceConfig;
}
/**
......@@ -432,6 +540,20 @@ class Facet extends ConfigEntityBase implements FacetInterface {
}
}
/**
* {@inheritdoc}
*/
public function setUnfilteredResults(array $all_results = []) {
$this->unfiltered_results = $all_results;
}
/**
* {@inheritdoc}
*/
public function getUnfilteredResults() {
return $this->unfiltered_results;
}
/**
* {@inheritdoc}
*/
......@@ -458,12 +580,16 @@ class Facet extends ConfigEntityBase implements FacetInterface {
// Create our settings for this facet source..
$config = isset($this->facetSourcePlugins[$name]) ? $this->facetSourcePlugins[$name] : [];
/* @var $facet_source \Drupal\facets\FacetSource\FacetSourceInterface */
/* @var $facet_source \Drupal\facets\FacetSource\FacetSourcePluginInterface */
$facet_source = $facet_source_plugin_manager->createInstance($name, $config);
$this->facetSourcePlugins[$name] = $facet_source;
}
elseif (!class_exists($facet_source_definition['class'])) {
\Drupal::logger('facets')->warning('Facet Source @id specifies a non-existing @class.', ['@id' => $name, '@class' => $facet_source_definition['class']]);
\Drupal::logger('facets')
->warning('Facet Source @id specifies a non-existing @class.', [
'@id' => $name,
'@class' => $facet_source_definition['class'],
]);
}
}
}
......@@ -483,9 +609,9 @@ class Facet extends ConfigEntityBase implements FacetInterface {
$processors = $this->loadProcessors();
// Filter processors by status if required. Enabled processors are those
// which have settings in the "processors" option.
// which have settings in the processor_configs.
if ($only_enabled) {
$processors_settings = $this->getOption('processors', array());
$processors_settings = $this->getProcessorConfigs();
$processors = array_intersect_key($processors, $processors_settings);
}
......@@ -497,7 +623,7 @@ class Facet extends ConfigEntityBase implements FacetInterface {
*/
public function getProcessorsByStage($stage, $only_enabled = TRUE) {
$processors = $this->loadProcessors();
$processor_settings = $this->getOption('processors', array());
$processor_settings = $this->getProcessorConfigs();
$processor_weights = array();
// Get a list of all processors meeting the criteria (stage and, optionally,
......@@ -538,4 +664,66 @@ class Facet extends ConfigEntityBase implements FacetInterface {
return $this->only_visible_when_facet_source_is_visible;
}
/**
* {@inheritdoc}
*/
public function addProcessor(array $processor) {
$this->processor_configs[$processor['processor_id']] = [
'processor_id' => $processor['processor_id'],
'weights' => $processor['weights'],
'settings' => $processor['settings'],
];
// Sort the processors so we won't have unnecessary changes.
ksort($this->processor_configs);
}
/**
* {@inheritdoc}
*/
public function removeProcessor($processor_id) {
unset($this->processor_configs[$processor_id]);
}
/**
* {@inheritdoc}
*/
public function getEmptyBehavior() {
return $this->empty_behavior;
}
/**
* {@inheritdoc}
*/
public function setEmptyBehavior(array $empty_behavior) {
$this->empty_behavior = $empty_behavior;
}
/**
* {@inheritdoc}
*/
public function setWidgetConfigs(array $widget_configs) {
$this->widget_configs = $widget_configs;
}
/**
* {@inheritdoc}
*/
public function getWidgetConfigs() {
return $this->widget_configs;
}
/**
* {@inheritdoc}
*/
public function setFacetConfigs(array $facet_configs) {
$this->facet_configs = $facet_configs;
}
/**
* {@inheritdoc}
*/
public function getFacetConfigs() {
return $this->facet_configs;
}
}
<?php
/**
* @file
* Contains \Drupal\facets\Entity\FacetSource.
*/
namespace Drupal\facets\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\facets\FacetSourceInterface;
/**
* Defines the facet source configuration entity.
*
* @ConfigEntityType(
* id = "facets_facet_source",
* label = @Translation("Facet source"),
* handlers = {
* "storage" = "Drupal\Core\Config\Entity\ConfigEntityStorage",
* "list_builder" = "Drupal\facets\FacetListBuilder",
* "form" = {
* "default" = "Drupal\facets\Form\FacetSourceEditForm",
* "edit" = "Drupal\facets\Form\FacetSourceEditForm",
* "display" = "Drupal\facets\Form\FacetSourceDisplayForm",
* "delete" = "Drupal\facets\Form\FacetSourceDeleteConfirmForm",
* },
* },
* admin_permission = "administer facets",
* config_prefix = "facet_source",
* entity_keys = {
* "id" = "id",
* "label" = "name",
* "uuid" = "uuid"
* },
* config_export = {
* "id",
* "name",
* "filter_key",
* "url_processor"
* },
* links = {
* "canonical" = "/admin/config/search/facets/facet-sources/",
* "edit-form" = "/admin/config/search/facets/facet-sources/{facets_facet_source}/edit"
* }
* )
*/
class FacetSource extends ConfigEntityBase implements FacetSourceInterface {
/**
* The ID of the facet source.
*
* @var string
*/
protected $id;
/**
* A name to be displayed for the facet source.
*
* @var string
*/
protected $name;
/**
* The key, used for filters in the query string.
*
* @var string
*/
protected $filter_key;
/**
* The url processor name.
*
* @var string
*/
protected $url_processor = 'query_string';
/**
* {@inheritdoc}
*/
public function getName() {
return $this->name;
}
/**
* {@inheritdoc}
*/
public function setFilterKey($filter_key) {
$this->filter_key = $filter_key;
}
/**
* {@inheritdoc}
*/
public function getFilterKey() {
return $this->filter_key;
}
/**
* {@inheritdoc}
*/
public function setUrlProcessor($processor_name) {
$this->url_processor = $processor_name;
}
/**
* {@inheritdoc}
*/
public function getUrlProcessorName() {
return $this->url_processor;
}
}
......@@ -33,7 +33,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function getWidget();
/**
* Get field identifier.
* Returns field identifier.
*
* @return string
* The field identifier of this facet.
......@@ -41,7 +41,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function getFieldIdentifier();
/**
* Set field identifier.
* Sets field identifier.
*
* @param string $field_identifier
* The field identifier of this facet.
......@@ -52,7 +52,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function setFieldIdentifier($field_identifier);
/**
* Get the field alias used to identify the facet in the url.
* Returns the field alias used to identify the facet in the url.
*
* @return string
* The field alias for the facet.
......@@ -60,7 +60,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function getFieldAlias();
/**
* Get the field name of the facet as used in the index.
* Returns the field name of the facet as used in the index.
*
* @TODO: Check if fieldIdentifier can be used as well!
*
......@@ -69,6 +69,22 @@ interface FacetInterface extends ConfigEntityInterface {
*/
public function getName();
/**
* Returns the name of the facet for use in the URL.
*
* @return string
* The name of the facet for use in the URL.
*/
public function getUrlAlias();
/**
* Sets the name of the facet for use in the URL.
*
* @param string $url_alias
* The name of the facet for use in the URL.
*/
public function setUrlAlias($url_alias);
/**
* Sets an item with value to active.
*
......@@ -78,7 +94,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function setActiveItem($value);
/**
* Get all the active items in the facet.
* Returns all the active items in the facet.
*
* @return mixed
* An array containing all active items.
......@@ -97,7 +113,7 @@ interface FacetInterface extends ConfigEntityInterface {
public function isActiveValue($value);
/**
* Get the result for the facet.
* Returns the result for the facet.
*
* @return \Drupal\facets\Result\ResultInterface[] $results
* The results of the facet.
......@@ -112,69 +128,61 @@ interface FacetInterface extends ConfigEntityInterface {
*/
public function setResults(array $results);
/**
* Get the query type instance.
* Sets an array of unfiltered results.
*
* @return string
* The query type plugin being used.
* These unfiltered results are used to set the correct count of the actual
* facet results when using the OR query operator. They are not results value
* objects like those in ::$results.
*
* @param array $all_results
* Unfiltered results.
*/
public function getQueryType();
public function setUnfilteredResults(array $all_results = []);
/**
* Get the plugin name for the url processor.
* Returns an array of unfiltered results.
*
* @return string
* The id of the url processor.
* @return array
* Unfiltered results.
*/
public function getUrlProcessorName();
public function getUnfilteredResults();
/**
* Retrieves an option.
*
* @param string $name
* The name of an option.
* @param mixed $default
* The value return if the option wasn't set.
* Returns the query type instance.
*
* @return mixed
* The value of the option.
*
* @see getOptions()
* @return string
* The query type plugin being used.
*/
public function getOption($name, $default = NULL);
public function getQueryType();
/**
* Retrieves an array of all options.
* Returns the query operator.
*
* @return array
* An associative array of option values, keyed by the option name.
* @return string
* The query operator being used.
*/
public function getOptions();
public function getQueryOperator();
/**
* Sets an option.
* Returns the value of the exclude boolean.
*
* @param string $name
* The name of an option.
* @param mixed $option
* The new option.
* This will return true when the current facet's value should be exclusive
* from the search rather than inclusive.
* When this returns TRUE, the operator will be "<>" instead of "=".
*
* @return $this
* Returns self.
* @return bool
* A boolean flag indicating if search should exlude selected facets
*/
public function setOption($name, $option);
public function getExclude();
/**
* Sets the index's options.
*
* @param array $options
* The new index options.
* Returns the plugin name for the url processor.
*
* @return $this
* Returns self.
* @return string
* The id of the url processor.
*/
public function setOptions(array $options);
public function getUrlProcessorName();
/**
* Sets a string representation of the Facet source plugin.
......@@ -189,6 +197,22 @@ interface FacetInterface extends ConfigEntityInterface {
*/
public function setFacetSourceId($facet_source_id);
/**
* Sets the query operator.
*
* @param string $operator
* The query operator being used.
*/
public function setQueryOperator($operator);
/**
* Sets the exclude.
*
* @param bool $exclude
* A boolean flag indicating if search should exclude selected facets
*/
public function setExclude($exclude);
/**
* Returns the Facet source id.
*
......@@ -200,18 +224,26 @@ interface FacetInterface extends ConfigEntityInterface {
/**
* Returns the plugin instance of a facet source.
*
* @return \Drupal\facets\FacetSource\FacetSourceInterface
* @return \Drupal\facets\FacetSource\FacetSourcePluginInterface
* The plugin instance for the facet source.
*/
public function getFacetSource();
/**
* Load the facet sources for this facet.
* Returns the facet source configuration object.
*
* @return \Drupal\facets\FacetSourceInterface
* A facet source configuration object.
*/
public function getFacetSourceConfig();
/**
* Loads the facet sources for this facet.
*
* @param bool|TRUE $only_enabled
* Only return enabled facet sources.
*
* @return \Drupal\facets\FacetSource\FacetSourceInterface[]
* @return \Drupal\facets\FacetSource\FacetSourcePluginInterface[]
* An array of facet sources.
*/
public function getFacetSources($only_enabled = TRUE);
......@@ -244,6 +276,14 @@ interface FacetInterface extends ConfigEntityInterface {
*/
public function getProcessorsByStage($stage, $only_enabled = TRUE);
/**
* Retrieves this facets's processor configs.
*
* @return array
* An array of processors and their configs.
*/
public function getProcessorConfigs();
/**
* Sets the "only visible when facet source is visible" boolean flag.
*
......@@ -264,4 +304,68 @@ interface FacetInterface extends ConfigEntityInterface {
*/
public function getOnlyVisibleWhenFacetSourceIsVisible();
/**
* Adds a processor for this facet.
*
* @param array $processor
* An array definition for a processor.
*/
public function addProcessor(array $processor);
/**
* Removes a processor for this facet.
*
* @param string $processor_id
* The plugin id of the processor.
*/
public function removeProcessor($processor_id);
/**
* Defines the no-results behavior.
*
* @param array $behavior
* The definition of the behavior.
*/
public function setEmptyBehavior(array $behavior);
/**
* Returns the defined no-results behavior or NULL if none defined.
*
* @return array|NULL
* The behavior definition or NULL.
*/
public function getEmptyBehavior();
/**
* Returns the configuration of the selected widget.
*
* @return array
* The configuration settings for the widget.
*/
public function getWidgetConfigs();
/**
* Sets the configuration for the widget of this facet.
*
* @param array $widget_config
* The configuration settings for the widget.
*/
public function setWidgetConfigs(array $widget_config);
/**
* Returns any additional configuration for this facet, not defined above.
*
* @return array
* An array of additional configuration for the facet.
*/
public function getFacetConfigs();
/**
* Defines any additional configuration for this facet not defined above.
*
* @param array $facet_config
* An array of additional configuration for the facet.
*/
public function setFacetConfigs(array $facet_config);
}
......@@ -10,6 +10,7 @@ namespace Drupal\facets;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Link;
/**
* Builds a listing of facet entities.
......@@ -119,7 +120,7 @@ class FacetListBuilder extends ConfigEntityListBuilder {
}
/**
* {@inheritdoc}
* Builds an array of facet sources for display in the overview.
*/
public function buildFacetSourceRow(array $facet_source = []) {
return array(
......@@ -134,7 +135,13 @@ class FacetListBuilder extends ConfigEntityListBuilder {
'status' => array(
'data' => '',
),
'operations' => array(),
'operations' => array(
'data' => Link::createFromRoute(
$this->t('Configure'),
'entity.facets_facet_source.edit_form',
['source_id' => $facet_source['id']]
)->toRenderable(),
),
),
'class' => array('facet-source'),
);
......@@ -153,7 +160,7 @@ class FacetListBuilder extends ConfigEntityListBuilder {
'#markup' => $this->t(
'You currently have no facet sources defined. You should start by adding a facet source before creating facets.<br />
An example of a facet source is a view based on Search API or a Search API page.
Other modules can also implement a facet source by providing a plugin that implements the FacetSourceInterface.'
Other modules can also implement a facet source by providing a plugin that implements the FacetSourcePluginInterface.'
),
];
}
......
......@@ -53,6 +53,13 @@ class DefaultFacetManager {
*/
protected $processorPluginManager;
/**
* The widget plugin manager.
*
* @var \Drupal\facets\Widget\WidgetPluginManager
*/
protected $widgetPluginManager;
/**
* An array of facets that are being rendered.
*
......@@ -96,20 +103,16 @@ class DefaultFacetManager {
*
* @var string
*
* @see \Drupal\facets\FacetSource\FacetSourceInterface
* @see \Drupal\facets\FacetSource\FacetSourcePluginInterface
*/
protected $facetSourceId;
/**
* Set the search id.
* The entity storage for facets.
*
* @param string $facet_source_id
* The id of the facet source.
* @var \Drupal\Core\Entity\EntityStorageInterface|object
*/
public function setFacetSourceId($facet_source_id) {
$this->facetSourceId = $facet_source_id;
}
protected $facetStorage;
/**
* Constructs a new instance of the DefaultFacetManager.
*
......@@ -125,18 +128,27 @@ class DefaultFacetManager {
* The entity type plugin manager.
*/
public function __construct(QueryTypePluginManager $query_type_plugin_manager, WidgetPluginManager $widget_plugin_manager, FacetSourcePluginManager $facet_source_manager, ProcessorPluginManager $processor_plugin_manager, EntityTypeManager $entity_type_manager) {
$this->queryTypePluginManager = $query_type_plugin_manager;
$this->widget_plugin_manager = $widget_plugin_manager;
$this->widgetPluginManager = $widget_plugin_manager;
$this->facetSourcePluginManager = $facet_source_manager;
$this->processorPluginManager = $processor_plugin_manager;
$this->facet_storage = $entity_type_manager->getStorage('facets_facet');
$this->facetStorage = $entity_type_manager->getStorage('facets_facet');
// Immediately initialize the facets. This can be done directly because the
// only thing needed is the url.
$this->initFacets();
}
/**
* Sets the search id.
*
* @param string $facet_source_id
* The id of the facet source.
*/
public function setFacetSourceId($facet_source_id) {
$this->facetSourceId = $facet_source_id;
}
/**
* Allows the backend to add facet queries to its native query object.
*
......@@ -153,8 +165,14 @@ class DefaultFacetManager {
// Make sure we don't alter queries for facets with a different source.
if ($facet->getFacetSourceId() == $this->facetSourceId) {
/** @var \Drupal\facets\QueryType\QueryTypeInterface $query_type_plugin */
$query_type_plugin = $this->queryTypePluginManager->createInstance($facet->getQueryType(), ['query' => $query, 'facet' => $facet]);
$query_type_plugin->execute();
$query_type_plugin = $this->queryTypePluginManager
->createInstance($facet->getQueryType(), ['query' => $query, 'facet' => $facet]);
$unfiltered_results = $query_type_plugin->execute();
// Save unfiltered results in facet.
if (!is_null($unfiltered_results)) {
$facet->setUnfilteredResults($unfiltered_results);
}
}
}
}
......@@ -166,11 +184,11 @@ class DefaultFacetManager {
* An array of enabled facets.
*/
public function getEnabledFacets() {
return $this->facet_storage->loadMultiple();
return $this->facetStorage->loadMultiple();
}
/**
* Get the ID of the facet source.
* Returns the ID of the facet source.
*
* @return string
* The id of the facet source.
......@@ -197,7 +215,7 @@ class DefaultFacetManager {
}
/**
* Initialize enabled facets.
* Initializes enabled facets.
*
* In this method all pre-query processors get called and their contents are
* executed.
......@@ -207,23 +225,20 @@ class DefaultFacetManager {
$this->facets = $this->getEnabledFacets();
foreach ($this->facets as $facet) {
foreach ($facet->getProcessors() as $processor) {
$processor_definition = $processor->getPluginDefinition();
if (is_array($processor_definition['stages']) && array_key_exists(ProcessorInterface::STAGE_PRE_QUERY, $processor_definition['stages'])) {
/** @var PreQueryProcessorInterface $pre_query_processor */
$pre_query_processor = $this->processorPluginManager->createInstance($processor->getPluginDefinition()['id']);
if (!$pre_query_processor instanceof PreQueryProcessorInterface) {
throw new InvalidProcessorException(new FormattableMarkup("The processor @processor has a pre_query definition but doesn't implement the required PreQueryProcessorInterface interface", ['@processor' => $processor_configuration['processor_id']]));
}
$pre_query_processor->preQuery($facet);
foreach ($facet->getProcessorsByStage(ProcessorInterface::STAGE_PRE_QUERY) as $processor) {
/** @var PreQueryProcessorInterface $pre_query_processor */
$pre_query_processor = $this->processorPluginManager->createInstance($processor->getPluginDefinition()['id'], ['facet' => $facet]);
if (!$pre_query_processor instanceof PreQueryProcessorInterface) {
throw new InvalidProcessorException(new FormattableMarkup("The processor @processor has a pre_query definition but doesn't implement the required PreQueryProcessorInterface interface", ['@processor' => $processor_configuration['processor_id']]));
}
$pre_query_processor->preQuery($facet);
}
}
}
}
/**
* Build a facet and returns it's render array.
* Builds a facet and returns it as a renderable array.
*
* This method delegates to the relevant plugins to render a facet, it calls
* out to a widget plugin to do the actual rendering when results are found.
......@@ -271,23 +286,20 @@ class DefaultFacetManager {
// @see \Drupal\facets\Processor\WidgetOrderProcessorInterface.
$results = $facet->getResults();
foreach ($facet->getProcessors() as $processor) {
$processor_definition = $this->processorPluginManager->getDefinition($processor->getPluginDefinition()['id']);
if (is_array($processor_definition['stages']) && array_key_exists(ProcessorInterface::STAGE_BUILD, $processor_definition['stages'])) {
/** @var BuildProcessorInterface $build_processor */
$build_processor = $this->processorPluginManager->createInstance($processor->getPluginDefinition()['id']);
if (!$build_processor instanceof BuildProcessorInterface) {
throw new InvalidProcessorException(new FormattableMarkup("The processor @processor has a build definition but doesn't implement the required BuildProcessorInterface interface", ['@processor' => $processor['processor_id']]));
}
$results = $build_processor->build($facet, $results);
foreach ($facet->getProcessorsByStage(ProcessorInterface::STAGE_BUILD) as $processor) {
/** @var BuildProcessorInterface $build_processor */
$build_processor = $this->processorPluginManager->createInstance($processor->getPluginDefinition()['id'], ['facet' => $facet]);
if (!$build_processor instanceof BuildProcessorInterface) {
throw new InvalidProcessorException(new FormattableMarkup("The processor @processor has a build definition but doesn't implement the required BuildProcessorInterface interface", ['@processor' => $processor['processor_id']]));
}
$results = $build_processor->build($facet, $results);
}
$facet->setResults($results);
// No results behavior handling. Return a custom text or false depending on
// settings.
if (empty($facet->getResults())) {
$empty_behavior = $facet->getOption('empty_behavior');
$empty_behavior = $facet->getEmptyBehavior();
if ($empty_behavior['behavior'] == 'text') {
return ['#markup' => $empty_behavior['text']];
}
......@@ -298,7 +310,7 @@ class DefaultFacetManager {
// Let the widget plugin render the facet.
/** @var \Drupal\facets\Widget\WidgetInterface $widget */
$widget = $this->widget_plugin_manager->createInstance($facet->getWidget());
$widget = $this->widgetPluginManager->createInstance($facet->getWidget());
return $widget->build($facet);
}
......@@ -308,10 +320,29 @@ class DefaultFacetManager {
*/
public function updateResults() {
// Get an instance of the facet source.
/** @var \drupal\facets\FacetSource\FacetSourceInterface $facet_source_plugin */
/** @var \drupal\facets\FacetSource\FacetSourcePluginInterface $facet_source_plugin */
$facet_source_plugin = $this->facetSourcePluginManager->createInstance($this->facetSourceId);
$facet_source_plugin->fillFacetsWithResults($this->facets);
}
/**
* Returns one of the processed facets.
*
* Returns one of the processed facets, this is a facet with filled results.
* Keep in mind that if you want to have the facet's build processor executed,
* there needs to be an extra call to the FacetManager::build with the facet
* returned here as argument.
*
* @param string $facet_id
* The id of the facet.
*
* @return \Drupal\facets\FacetInterface|NULL
* The updated facet if it exists, NULL otherwise.
*/
public function returnProcessedFacet($facet_id) {
$this->processFacets();
return $this->facets[$facet_id];
}
}
......@@ -82,7 +82,6 @@ abstract class FacetSourceDeriverBase implements ContainerDeriverInterface {
return isset($derivatives[$derivative_id]) ? $derivatives[$derivative_id] : NULL;
}
/**
* Compares two plugin definitions according to their labels.
*
......
......@@ -11,6 +11,8 @@ use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Facets\FacetInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines a base class from which other facet sources may extend.
......@@ -23,10 +25,10 @@ use Drupal\Facets\FacetInterface;
*
* @see \Drupal\facets\Annotation\FacetsFacetSource
* @see \Drupal\facets\FacetSource\FacetSourcePluginManager
* @see \Drupal\facets\FacetSource\FacetSourceInterface
* @see \Drupal\facets\FacetSource\FacetSourcePluginInterface
* @see plugin_api
*/
abstract class FacetSourcePluginBase extends PluginBase implements FacetSourceInterface, ContainerFactoryPluginInterface {
abstract class FacetSourcePluginBase extends PluginBase implements FacetSourcePluginInterface, ContainerFactoryPluginInterface {
/**
* The plugin manager.
......@@ -42,17 +44,23 @@ abstract class FacetSourcePluginBase extends PluginBase implements FacetSourceIn
*/
protected $keys;
/**
* The facet we're editing for.
*
* @var \Drupal\facets\FacetInterface
*/
protected $facet;
/**
* {@inheritdoc}
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
$query_type_plugin_manager
) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, $query_type_plugin_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->queryTypePluginManager = $query_type_plugin_manager;
if (isset($configuration['facet'])) {
$this->facet = $configuration['facet'];
}
}
/**
......@@ -102,4 +110,20 @@ abstract class FacetSourcePluginBase extends PluginBase implements FacetSourceIn
return $this->keys;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$facet_source_id = $this->facet->getFacetSourceId();
$field_identifier = $form_state->getValue('facet_source_configs')[$facet_source_id]['field_identifier'];
$this->facet->setFieldIdentifier($field_identifier);
}
}
......@@ -2,12 +2,12 @@
/**
* @file
* Contains \Drupal\facets\FacetSource\FacetSourceInterface.
* Contains \Drupal\facets\FacetSource\FacetSourcePluginInterface.
*/
namespace Drupal\facets\FacetSource;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\facets\FacetInterface;
/**
......@@ -15,28 +15,14 @@ use Drupal\facets\FacetInterface;
*
* A facet source is used to abstract the data source where facets can be added
* to. A good example of this is a search api view. There are other possible
* facet data sources, these all implement the FacetSourceInterface.
* facet data sources, these all implement the FacetSourcePluginInterface.
*
* @see plugin_api
*/
interface FacetSourceInterface {
interface FacetSourcePluginInterface extends PluginFormInterface {
/**
* Adds a configuration form for this facet source.
*
* @param array $form
* The configuration form definition.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current form state.
* @param \Drupal\facets\FacetInterface $facet
* The facet being edited.
* @param \Drupal\facets\FacetSource\FacetSourceInterface $facet_source
* The facet source being edited.
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state, FacetInterface $facet, FacetSourceInterface $facet_source);
/**
* Fill in facet data in to the configured facets.
* Fills the facet entities with results from the facet source.
*
* @param \Drupal\facets\FacetInterface[] $facets
* The configured facets.
......@@ -44,15 +30,7 @@ interface FacetSourceInterface {
public function fillFacetsWithResults($facets);
/**
* Returns the path where a facet should link to.
*
* @return string
* The path of the facet.
*/
public function getPath();
/**
* Get the allowed query types for a given facet for the facet source.
* Returns the allowed query types for a given facet for the facet source.
*
* @param \Drupal\facets\FacetInterface $facet
* The facet we should get query types for.
......@@ -65,6 +43,14 @@ interface FacetSourceInterface {
*/
public function getQueryTypesForFacet(FacetInterface $facet);
/**
* Returns the path where a facet should link to.
*
* @return string
* The path of the facet.
*/
public function getPath();
/**
* Returns true if the Facet source is being rendered in the current request.
*
......@@ -100,7 +86,7 @@ interface FacetSourceInterface {
public function setSearchKeys($keys);
/**
* Gets the search keys, or query text, submitted by the user.
* Returns the search keys, or query text, submitted by the user.
*
* @return string
* The search keys, or query text, submitted by the user.
......
......@@ -21,18 +21,10 @@ use Drupal\Core\Plugin\DefaultPluginManager;
class FacetSourcePluginManager extends DefaultPluginManager {
/**
* Constructs a FacetSourcePluginManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* {@inheritdoc}
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/facets/facet_source', $namespaces, $module_handler, 'Drupal\facets\FacetSource\FacetSourceInterface', 'Drupal\facets\Annotation\FacetsFacetSource');
parent::__construct('Plugin/facets/facet_source', $namespaces, $module_handler, 'Drupal\facets\FacetSource\FacetSourcePluginInterface', 'Drupal\facets\Annotation\FacetsFacetSource');
}
}
<?php
/**
* @file
* Contains Drupal\facets\FacetSourceInterface.
*/
namespace Drupal\facets;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* The facet source entity.
*/
interface FacetSourceInterface extends ConfigEntityInterface {
/**
* Returns the label of the facet source.
*
* @return string
* The facet name.
*/
public function getName();
/**
* Returns the filter key for this facet source.
*
* @return string
* The filter key.
*/
public function getFilterKey();
/**
* Sets the filter key for this facet source.
*
* @param string $filter_key
* The filter key.
*/
public function setFilterKey($filter_key);
/**
* Sets the processor name to be used.
*
* @param string $processor_name
* Plugin name of the url processor.
*/
public function setUrlProcessor($processor_name);
/**
* Returns a string version of the url processor.
*
* @return string
* The url processor to be used as a string.
*/
public function getUrlProcessorName();
}
......@@ -14,6 +14,7 @@ use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Form\FormStateInterface;
use Drupal\facets\Processor\ProcessorInterface;
use Drupal\facets\Processor\ProcessorPluginManager;
use Drupal\facets\UrlProcessor\UrlProcessorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\facets\Widget\WidgetPluginManager;
use Drupal\facets\Processor\WidgetOrderProcessorInterface;
......@@ -194,6 +195,7 @@ class FacetDisplayForm extends EntityForm {
else {
$all_processors = $form_state->get('processors');
}
$enabled_processors = $facet->getProcessors(TRUE);
$stages = $this->processorPluginManager->getProcessingStages();
$processors_by_stage = array();
......@@ -201,10 +203,8 @@ class FacetDisplayForm extends EntityForm {
$processors_by_stage[$stage] = $facet->getProcessorsByStage($stage, FALSE);
}
$processor_settings = $facet->getOption('processors');
$form['#tree'] = TRUE;
$form['#attached']['library'][] = 'search_api/drupal.search_api.index-active-formatters';
$form['#attached']['library'][] = 'facets/drupal.facets.index-active-formatters';
$form['#title'] = $this->t('Manage processors for facet %label', array('%label' => $facet->label()));
// Add the list of all other processors with checkboxes to enable/disable
......@@ -219,12 +219,12 @@ class FacetDisplayForm extends EntityForm {
),
);
foreach ($all_processors as $processor_id => $processor) {
if (!($processor instanceof WidgetOrderProcessorInterface)) {
if (!($processor instanceof WidgetOrderProcessorInterface) && !($processor instanceof UrlProcessorInterface)) {
$clean_css_id = Html::cleanCssIdentifier($processor_id);
$form['facet_settings'][$processor_id]['status'] = array(
'#type' => 'checkbox',
'#title' => (string) $processor->getPluginDefinition()['label'],
'#default_value' => $processor->isLocked() || !empty($processor_settings[$processor_id]),
'#default_value' => $processor->isLocked() || !empty($enabled_processors[$processor_id]),
'#description' => $processor->getDescription(),
'#attributes' => array(
'class' => array(
......@@ -280,7 +280,7 @@ class FacetDisplayForm extends EntityForm {
$form['facet_sorting'][$processor_id]['status'] = array(
'#type' => 'checkbox',
'#title' => (string) $processor->getPluginDefinition()['label'],
'#default_value' => $processor->isLocked() || !empty($processor_settings[$processor_id]),
'#default_value' => $processor->isLocked() || !empty($enabled_processors[$processor_id]),
'#description' => $processor->getDescription(),
'#attributes' => array(
'class' => array(
......@@ -326,8 +326,7 @@ class FacetDisplayForm extends EntityForm {
'#default_value' => $facet->getOnlyVisibleWhenFacetSourceIsVisible(),
];
// Behavior for empty facets.
$empty_behavior_config = $facet->getOption('empty_behavior');
$empty_behavior_config = $facet->getEmptyBehavior();
$form['facet_settings']['empty_behavior'] = [
'#type' => 'radios',
'#title' => t('Empty facet behavior'),
......@@ -352,6 +351,21 @@ class FacetDisplayForm extends EntityForm {
'#default_value' => isset($empty_behavior_config['text_format']) ? $empty_behavior_config['text'] : '',
];
$form['facet_settings']['query_operator'] = [
'#type' => 'radios',
'#title' => $this->t('Operator'),
'#options' => ['OR' => $this->t('OR'), 'AND' => $this->t('AND')],
'#description' => $this->t('AND filters are exclusive and narrow the result set. OR filters are inclusive and widen the result set.'),
'#default_value' => $facet->getQueryOperator(),
];
$form['facet_settings']['exclude'] = [
'#type' => 'checkbox',
'#title' => $this->t('Exclude'),
'#description' => $this->t('Make the search exclude selected facets, instead of restricting it to them.'),
'#default_value' => $facet->getExclude(),
];
$form['weights'] = array(
'#type' => 'details',
'#title' => t('Advanced settings'),
......@@ -384,6 +398,8 @@ class FacetDisplayForm extends EntityForm {
);
}
$processor_settings = $facet->getProcessorConfigs();
// Fill in the containers previously created with the processors that are
// enabled on the facet.
foreach ($processors_by_stage as $stage => $processors) {
......@@ -469,7 +485,6 @@ class FacetDisplayForm extends EntityForm {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$new_settings = array();
// Store processor settings.
// @todo Go through all available processors, enable/disable with method on
......@@ -482,16 +497,16 @@ class FacetDisplayForm extends EntityForm {
foreach ($processors as $processor_id => $processor) {
$form_container_key = $processor instanceof WidgetOrderProcessorInterface ? 'facet_sorting' : 'facet_settings';
if (empty($values[$form_container_key][$processor_id]['status'])) {
$facet->removeProcessor($processor_id);
continue;
}
$new_settings[$processor_id] = array(
$new_settings = array(
'processor_id' => $processor_id,
'weights' => array(),
'settings' => array(),
);
$processor_values = $values[$form_container_key][$processor_id];
if (!empty($processor_values['weights'])) {
$new_settings[$processor_id]['weights'] = $processor_values['weights'];
if (!empty($values['processors'][$processor_id]['weights'])) {
$new_settings['weights'] = $values['processors'][$processor_id]['weights'];
}
if (isset($form[$form_container_key][$processor_id]['settings'])) {
$processor_form_state = new SubFormState(
......@@ -499,16 +514,14 @@ class FacetDisplayForm extends EntityForm {
array($form_container_key, $processor_id, 'settings')
);
$processor->submitConfigurationForm($form[$form_container_key][$processor_id]['settings'], $processor_form_state, $facet);
$new_settings[$processor_id]['settings'] = $processor->getConfiguration();
$new_settings['settings'] = $processor->getConfiguration();
}
$facet->addProcessor($new_settings);
}
// Sort the processors so we won't have unnecessary changes.
ksort($new_settings);
$facet->setOption('processors', $new_settings);
$facet->setWidget($form_state->getValue('widget'));
$facet->set('widget_configs', $form_state->getValue('widget_configs'));
$facet->set('only_visible_when_facet_source_is_visible', $form_state->getValue(['facet_settings', 'only_visible_when_facet_source_is_visible']));
$facet->setWidgetConfigs($form_state->getValue('widget_configs'));
$facet->setOnlyVisibleWhenFacetSourceIsVisible($form_state->getValue(['facet_settings', 'only_visible_when_facet_source_is_visible']));
$empty_behavior_config = [];
$empty_behavior = $form_state->getValue(['facet_settings', 'empty_behavior']);
......@@ -527,14 +540,18 @@ class FacetDisplayForm extends EntityForm {
'value',
]);
}
$facet->setOption('empty_behavior', $empty_behavior_config);
$facet->setEmptyBehavior($empty_behavior_config);
$facet->setQueryOperator($form_state->getValue(['facet_settings', 'query_operator']));
$facet->setExclude($form_state->getValue(['facet_settings', 'exclude']));
$facet->save();
drupal_set_message(t('Facet %name has been updated.', ['%name' => $facet->getName()]));
}
/**
* Form submission handler for the widget subform.
* Handles form submissions for the widget subform.
*/
public function submitAjaxWidgetConfigForm($form, FormStateInterface $form_state) {
$form_state->setRebuild();
......