diff --git a/config/install/facetapi.facet_source.yml b/config/install/facetapi.facet_source.yml new file mode 100644 index 0000000..4aa101f --- /dev/null +++ b/config/install/facetapi.facet_source.yml @@ -0,0 +1 @@ +filter_key: f diff --git a/config/schema/facetapi.facet_source.schema.yml b/config/schema/facetapi.facet_source.schema.yml new file mode 100644 index 0000000..b9283fa --- /dev/null +++ b/config/schema/facetapi.facet_source.schema.yml @@ -0,0 +1,17 @@ +facetapi.facet_source: + type: config_object + label : 'Facet' + mapping: + filter_key: + type: string + label: 'Filter key' + overrides: + type: sequence + label: 'Overrides' + sequence: + type: mapping + label: 'Override' + mapping: + filter_key: + type: string + label: 'Filter key' diff --git a/facetapi.routing.yml b/facetapi.routing.yml index dec8f20..0bb1d0b 100644 --- a/facetapi.routing.yml +++ b/facetapi.routing.yml @@ -41,3 +41,11 @@ entity.facetapi_facet.display_form: _entity_form: 'facetapi_facet.display' requirements: _entity_access: 'facetapi_facet.edit' + +facetapi_facet.facetsource.config_form: + path: '/admin/config/search/facet-api/facet-source/{source_id}/edit' + defaults: + _controller: '\Drupal\facetapi\Controller\FacetSourceConfigController::facetSourceConfigForm' + _title: 'Edit facet source configuration' + requirements: + _entity_create_access: 'facetapi_facet' diff --git a/src/Controller/FacetSourceConfigController.php b/src/Controller/FacetSourceConfigController.php new file mode 100644 index 0000000..16e8f00 --- /dev/null +++ b/src/Controller/FacetSourceConfigController.php @@ -0,0 +1,48 @@ +createInstance($source_id); + } catch (PluginNotFoundException $e) { + // Return a renderable array with a plugin not found message. We add a + // max-age to make sure that this message is not found indefinitely. + return [ + '#markup' => $this->t('Plugin not found.'), + '#cache' => [ + 'max-age' => 5, + ] + ]; + } + + // Returns the render array of the FacetSourceConfigForm. + return $this->formBuilder()->getForm('\Drupal\facetapi\Form\FacetSourceConfigForm'); + } + +} diff --git a/src/FacetListBuilder.php b/src/FacetListBuilder.php index 43e801d..de19305 100644 --- a/src/FacetListBuilder.php +++ b/src/FacetListBuilder.php @@ -10,7 +10,7 @@ namespace Drupal\facetapi; use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\Config\Entity\ConfigEntityListBuilder; use Drupal\Core\Entity\EntityInterface; -use Drupal\facetapi\FacetSource\FacetSourceInterface; +use Drupal\Core\Link; /** * Builds a listing of facet entities. @@ -135,7 +135,9 @@ class FacetListBuilder extends ConfigEntityListBuilder { 'status' => array( 'data' => '', ), - 'operations' => array(), + 'operations' => array( + 'data' => Link::createFromRoute('Configure', 'facetapi_facet.facetsource.config_form', ['source_id' => $facet_source['id']])->toRenderable() + ), ), 'class' => array('facet-source'), ); diff --git a/src/Form/FacetSourceConfigForm.php b/src/Form/FacetSourceConfigForm.php new file mode 100644 index 0000000..491be51 --- /dev/null +++ b/src/Form/FacetSourceConfigForm.php @@ -0,0 +1,79 @@ +config('facetapi.facet_source'); + $facet_source_id = $this->getRequest()->get('source_id'); + $overridden_filter_key_config = $config->get('overrides.' . $facet_source_id . '.filter_key'); + + $form['description'] = [ + '#markup' => $this->t('Saving this form will create a configuration override for this specific facet source. + Unless you have to override facet source specific configuration, you can leave this to the default settings.') + ]; + + $form['facet_source_id'] = [ + '#type' => 'hidden', + '#value' => $facet_source_id, + ]; + + // Filter key setting. + $form['filter_key'] = [ + '#type' => 'textfield', + '#title' => $this->t('Filter key'), + '#size' => 20, + '#maxlength' => 255, + '#default_value' => (!is_null($overridden_filter_key_config) ? $overridden_filter_key_config : $config->get('filter_key')), + '#description' => $this->t('They key used for filtering in the URL, defaults to f. + You should change this to something else if you expect to have multiple facet sources on one page.'), + ]; + + // The parent's form build method will add a save button. + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // The parent's submitForm adds a message. + parent::submitForm($form, $form_state); + + // When saving the config, make sure it's saved in the + // 'overrides.facetsourceid.*' config name, so we can make sure that config + // is possible to be overridden per facet source. + $config = $this->config('facetapi.facet_source'); + $config->set('overrides.' . $form_state->getValue('facet_source_id') . '.filter_key', $form_state->getValue('filter_key')); + $config->save(); + } +} diff --git a/src/Plugin/facetapi/processor/QueryStringUrlProcessor.php b/src/Plugin/facetapi/processor/QueryStringUrlProcessor.php index 709e12f..0d1c310 100644 --- a/src/Plugin/facetapi/processor/QueryStringUrlProcessor.php +++ b/src/Plugin/facetapi/processor/QueryStringUrlProcessor.php @@ -7,6 +7,7 @@ namespace Drupal\facetapi\Plugin\facetapi\processor; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Url; use Drupal\facetapi\FacetInterface; use Drupal\facetapi\Processor\UrlProcessorPluginBase; @@ -40,8 +41,8 @@ class QueryStringUrlProcessor extends UrlProcessorPluginBase { /** * {@inheritdoc} */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request) { - parent::__construct($configuration, $plugin_id, $plugin_definition, $request); + public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request, ConfigFactoryInterface $config_factory) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $request, $config_factory); $this->initializeActiveFilters(); } diff --git a/src/Processor/UrlProcessorPluginBase.php b/src/Processor/UrlProcessorPluginBase.php index 7a3e94d..c9fd324 100644 --- a/src/Processor/UrlProcessorPluginBase.php +++ b/src/Processor/UrlProcessorPluginBase.php @@ -6,6 +6,7 @@ namespace Drupal\facetapi\Processor; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -41,10 +42,26 @@ abstract class UrlProcessorPluginBase extends ProcessorPluginBase implements Url * @param string $plugin_id * @param mixed $plugin_definition * @param \Symfony\Component\HttpFoundation\Request $request + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * An instance of the config factory */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request, ConfigFactoryInterface $config_factory) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->request = $request; + + $facetSourceId = $configuration['facet']->getFacetSourceId(); + + $facetSourceConfig = $config_factory->get('facetapi.facet_source'); + + // Set the filter key from the global config. + $this->filter_key = $facetSourceConfig->get('filter_key'); + + // Check if the filter key has been overridden in facet source specific + // config. + $override = $facetSourceConfig->get('overrides.' . $facetSourceId . '.filter_key'); + if (!is_null($override)) { + $this->filter_key = $override; + } } /** @@ -53,7 +70,11 @@ abstract class UrlProcessorPluginBase extends ProcessorPluginBase implements Url public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { /** @var Request $request */ $request = $container->get('request_stack')->getCurrentRequest(); - return new static($configuration, $plugin_id, $plugin_definition, $request); + + /** @var \Drupal\Core\Config\ConfigFactory $configFactory */ + $configFactory = $container->get('config.factory'); + + return new static($configuration, $plugin_id, $plugin_definition, $request, $configFactory); } } diff --git a/tests/src/Unit/Plugin/processor/QueryStringUrlProcessorTest.php b/tests/src/Unit/Plugin/processor/QueryStringUrlProcessorTest.php index ad41c24..1903ea6 100644 --- a/tests/src/Unit/Plugin/processor/QueryStringUrlProcessorTest.php +++ b/tests/src/Unit/Plugin/processor/QueryStringUrlProcessorTest.php @@ -35,6 +35,11 @@ class QueryStringUrlProcessorTest extends UnitTestCase { protected $original_results; /** + * A mocked config storage stub. + */ + protected $config; + + /** * Creates a new processor object for use in the tests. */ protected function setUp() { @@ -47,6 +52,8 @@ class QueryStringUrlProcessorTest extends UnitTestCase { new Result('duck', 'Duck', 15), new Result('alpaca', 'Alpaca', 25), ]; + + $this->config = $this->getConfigFactoryStub(['facetapi.facet_source' => ['filter_key' => 'f']]); } public function testSetSingleActiveItem() { @@ -57,7 +64,7 @@ class QueryStringUrlProcessorTest extends UnitTestCase { $request = new Request; $request->query->set('f', ['test:badger']); - $this->processor = new QueryStringUrlProcessor([], 'query_string', [], $request); + $this->processor = new QueryStringUrlProcessor(['facet' => $facet], 'query_string', [], $request, $this->config); $this->processor->preQuery($facet); $this->assertEquals(['badger'], $facet->getActiveItems()); @@ -71,9 +78,59 @@ class QueryStringUrlProcessorTest extends UnitTestCase { $request = new Request; $request->query->set('f', ['test:badger', 'test:mushroom', 'donkey:kong']); - $this->processor = new QueryStringUrlProcessor([], 'query_string', [], $request); + $this->processor = new QueryStringUrlProcessor(['facet' => $facet], 'query_string', [], $request, $this->config); + $this->processor->preQuery($facet); + + $this->assertEquals(['badger', 'mushroom'], $facet->getActiveItems()); + } + + public function testSetActiveWithConfigOverride() { + $facet = new Facet([], 'facet'); + $facet->setResults($this->original_results); + $facet->setFieldIdentifier('test'); + + $config = $this->getConfigFactoryStub(['facetapi.facet_source' => ['filter_key' => 'g']]); + + $request = new Request; + $request->query->set('f', ['test:badger', 'test:mushroom', 'donkey:kong']); + + $this->processor = new QueryStringUrlProcessor(['facet' => $facet], 'query_string', [], $request, $config); $this->processor->preQuery($facet); + + // Configuration was globally overridden but the request was still using the + // old 'f' parameter. This means there are no active items found in the + // request. + $this->assertEquals([], $facet->getActiveItems()); + + $request = new Request; + $request->query->set('g', ['test:badger', 'test:mushroom', 'donkey:kong']); + + $this->processor = new QueryStringUrlProcessor(['facet' => $facet], 'query_string', [], $request, $config); + $this->processor->preQuery($facet); + + // Now that the request also uses 'g' as query parameter, we expect the same + // response as ::testSetMultipleActiveItems expects. + $this->assertEquals(['badger', 'mushroom'], $facet->getActiveItems()); + } + + public function testFacetSourceOverride() { + $facet = new Facet([], 'facet'); + $facet->setResults($this->original_results); + $facet->setFieldIdentifier('test'); + $facet->setFacetSourceId('fs'); + + $config = $this->getConfigFactoryStub(['facetapi.facet_source' => ['filter_key' => 'f', 'overrides.fs.filter_key' => 'aa']]); + + $request = new Request; + $request->query->set('aa', ['test:badger', 'test:mushroom', 'donkey:kong']); + + $processor = new QueryStringUrlProcessor(['facet' => $facet], 'query_string', [], $request, $config); + $processor->preQuery($facet); + + // The request uses 'aa' in the parameter, this is the same as what was set + // in the config override and thus we should get badger and mushroom as + // active items. $this->assertEquals(['badger', 'mushroom'], $facet->getActiveItems()); } @@ -83,7 +140,7 @@ class QueryStringUrlProcessorTest extends UnitTestCase { $request = new Request; $request->query->set('f', []); - $this->processor = new QueryStringUrlProcessor([], 'query_string', [], $request); + $this->processor = new QueryStringUrlProcessor(['facet' => $facet], 'query_string', [], $request, $this->config); $results = $this->processor->build($facet, []); $this->assertEmpty($results); } @@ -97,7 +154,7 @@ class QueryStringUrlProcessorTest extends UnitTestCase { $this->setRouter(); - $this->processor = new QueryStringUrlProcessor([], 'query_string', [], $request); + $this->processor = new QueryStringUrlProcessor(['facet' => $facet], 'query_string', [], $request, $this->config); $results = $this->processor->build($facet, $this->original_results); /** @var \Drupal\facetapi\Result\ResultInterface $r */ @@ -119,7 +176,7 @@ class QueryStringUrlProcessorTest extends UnitTestCase { $this->setRouter(); - $this->processor = new QueryStringUrlProcessor([], 'query_string', [], $request); + $this->processor = new QueryStringUrlProcessor(['facet' => $facet], 'query_string', [], $request, $this->config); $results = $this->processor->build($facet, $original_results); /** @var \Drupal\facetapi\Result\ResultInterface $r */