summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordragos.dumitrescu2017-04-05 18:47:12 (GMT)
committerJoris Vercammen2017-04-05 18:47:12 (GMT)
commit706ce181d84903fd4f003771fc0027062a7d54ec (patch)
tree853a469c818d3ccbf1f75c4d7d4faabcbe8de85c
parentca4c9ccf826ddc441f267528f23afb8333ebd548 (diff)
Issue #2717537 by dragos-dumi, borisson_: Breadcrumbs support8.x-1.0-alpha9
-rw-r--r--config/schema/facets.facetsource.schema.yml10
-rw-r--r--facets.module127
-rw-r--r--src/Entity/FacetSource.php23
-rw-r--r--src/FacetSourceInterface.php16
-rw-r--r--src/Form/FacetSourceEditForm.php22
-rw-r--r--tests/src/Functional/BreadcrumbIntegrationTest.php112
-rw-r--r--tests/src/Functional/FacetSourceTest.php24
-rw-r--r--tests/src/Kernel/Entity/FacetSourceTest.php7
8 files changed, 340 insertions, 1 deletions
diff --git a/config/schema/facets.facetsource.schema.yml b/config/schema/facets.facetsource.schema.yml
index cadff0b..48b841f 100644
--- a/config/schema/facets.facetsource.schema.yml
+++ b/config/schema/facets.facetsource.schema.yml
@@ -17,4 +17,14 @@ facets.facet_source.*:
url_processor:
type: string
label: 'Url processor'
+ breadcrumb:
+ type: mapping
+ labal: 'Breadcrumb'
+ mapping:
+ active:
+ type: boolean
+ label: 'Append active facets to breadcrumb'
+ group:
+ type: boolean
+ label: 'Group active items under same crumb'
third_party_settings: {}
diff --git a/facets.module b/facets.module
index 1f20e42..e36f6fd 100644
--- a/facets.module
+++ b/facets.module
@@ -6,9 +6,12 @@
*/
use Drupal\Component\Utility\Html;
+use Drupal\Core\Breadcrumb\Breadcrumb;
+use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\facets\Entity\Facet;
use Drupal\facets\Entity\FacetSource;
+use Drupal\facets\FacetInterface;
use Drupal\search_api\Query\QueryInterface;
use Drupal\views\Entity\View;
use Drupal\Core\Entity\EntityInterface;
@@ -211,3 +214,127 @@ function facets_entity_predelete(EntityInterface $entity) {
function facets_preprocess_facets_item_list(&$variables) {
template_preprocess_item_list($variables);
}
+
+/**
+ * Implements hook_system_breadcrumb_alter().
+ */
+function facets_system_breadcrumb_alter(Breadcrumb &$breadcrumb, RouteMatchInterface $route_match, array $context) {
+ /** @var \Drupal\facets\FacetSource\FacetSourcePluginManager $facet_source_manager */
+ $facet_source_manager = \Drupal::service('plugin.manager.facets.facet_source');
+
+ /** @var \Drupal\facets\FacetManager\DefaultFacetManager $facet_manager */
+ $facet_manager = \Drupal::service('facets.manager');
+
+ /** @var \Drupal\Core\Entity\EntityTypeManager $entity_type_manager */
+ $entity_type_manager = \Drupal::service('entity_type.manager');
+
+ /** @var \Drupal\Core\Entity\EntityStorageInterface $facet_source_storage */
+ $facet_source_storage = $entity_type_manager->getStorage('facets_facet_source');
+
+ $facet_sources_definitions = $facet_source_manager->getDefinitions();
+
+ // No facet sources found, so don't do anything.
+ if (empty($facet_sources_definitions)) {
+ return;
+ }
+
+ foreach ($facet_sources_definitions as $definition) {
+ /* @var \Drupal\facets\FacetSource\FacetSourcePluginBase $facet_source_plugin */
+ $facet_source_plugin = $facet_source_manager->createInstance($definition['id']);
+
+ // If the current facet source is not being rendered, don't do anything with
+ // these facet sources.
+ if (!$facet_source_plugin->isRenderedInCurrentRequest()) {
+ continue;
+ }
+
+ $source_id = str_replace(':', '__', $definition['id']);
+ /** @var \Drupal\facets\FacetSourceInterface $facet_source */
+ $facet_source = $facet_source_storage->load($source_id);
+
+ // If the facet source is not loaded, or the facet source doesn't have
+ // breadcrumbs enabled, don't do anything.
+ if (!($facet_source && $facet_source->getBreadcrumbSettings()['active'])) {
+ continue;
+ }
+
+ // Add the required cacheability metadata.
+ $breadcrumb->addCacheContexts(['url']);
+ $breadcrumb->addCacheableDependency($facet_source);
+
+ // Process the facets if they are not already processed.
+ $facet_manager->processFacets();
+ $facets = $facet_manager->getFacetsByFacetSourceId($definition['id']);
+
+ // Sort facets by weight.
+ uasort($facets, function (FacetInterface $a, FacetInterface $b) {
+ if ($a->getWeight() == $b->getWeight()) {
+ return 0;
+ }
+ return ($a->getWeight() < $b->getWeight()) ? -1 : 1;
+ });
+
+ /** @var \Drupal\facets\UrlProcessor\UrlProcessorPluginManager $url_processor_manager */
+ $url_processor_manager = \Drupal::service('plugin.manager.facets.url_processor');
+
+ // Get active facets and results to use them at building the crumbs.
+ $active_results = [];
+ $active_facets = [];
+ foreach ($facets as $facet) {
+ if ($active_items = $facet->getActiveItems()) {
+ // Add the facet as a cacheable dependency.
+ $breadcrumb->addCacheableDependency($facet);
+ /** @var \Drupal\facets\UrlProcessor\UrlProcessorInterface $url_processor */
+ $url_processor = $url_processor_manager->createInstance($facet_source->getUrlProcessorName(), ['facet' => $facet]);
+ $facet_manager->build($facet);
+
+ foreach ($facet->getResults() as $result) {
+ if ($result->isActive()) {
+ // Clone the result so we can mark it as inactive to be added to the
+ // url parameters when calling buildUrls.
+ $cloned_result = clone $result;
+ $cloned_result->setActiveState(FALSE);
+ $active_results[$facet->getUrlAlias()][] = $cloned_result;
+ }
+ }
+ $url_processor->buildUrls($facet, $active_results[$facet->getUrlAlias()]);
+ $active_facets[$facet->getUrlAlias()] = $facet;
+ }
+ }
+
+ // TODO find a better way to construct the url for a crumb maybe url
+ // processor will have a function to get params for a result
+ // without all the other request parameters; with this we could implement:
+ // @see https://www.drupal.org/node/2861586
+ // TODO handle not grouped facets.
+
+ foreach ($active_results as $facet_alias => $facet_results) {
+ $facet_used_result[] = $facet_alias;
+ $facet_crumb_items = [];
+ reset($facet_results);
+ $facet_url = clone current($facet_results)->getUrl();
+
+ // Because we can't get the desired url trough a url processor method
+ // we iterate each result url and remove the facet params that haven't
+ // been used on previous crumbs
+ foreach ($facet_results as $res) {
+ $facet_url = $res->getUrl();
+ /** @var \Drupal\Core\Url $facet_url */
+ $query = $facet_url->getOption('query');
+ $source_filter = $query[$facet_source->getFilterKey()];
+ $source_filter = array_unique($source_filter);
+ $source_filter = array_filter($source_filter);
+ $matches = preg_grep('/^' . implode('|^', $facet_used_result) . '/', $source_filter);
+ $query[$facet_source->getFilterKey()] = $matches;
+ $facet_crumb_items[] = $res->getDisplayValue();
+ }
+
+ // Set the new query and create the link.
+ $facet_url->setOption('query', $query);
+ $crumb_text = $active_facets[$facet_alias]->label() . ': ' . implode(', ', $facet_crumb_items);
+ $link = Link::fromTextAndUrl($crumb_text, $facet_url);
+ $breadcrumb->addLink($link);
+ }
+ }
+
+}
diff --git a/src/Entity/FacetSource.php b/src/Entity/FacetSource.php
index 62b4ae9..9b22336 100644
--- a/src/Entity/FacetSource.php
+++ b/src/Entity/FacetSource.php
@@ -32,7 +32,8 @@ use Drupal\facets\FacetSourceInterface;
* "id",
* "name",
* "filter_key",
- * "url_processor"
+ * "url_processor",
+ * "breadcrumb"
* },
* links = {
* "canonical" = "/admin/config/search/facets/facet-sources/",
@@ -71,6 +72,13 @@ class FacetSource extends ConfigEntityBase implements FacetSourceInterface {
protected $url_processor = 'query_string';
/**
+ * The breadcrumb settings.
+ *
+ * @var array
+ */
+ protected $breadcrumb = [];
+
+ /**
* {@inheritdoc}
*/
public function getName() {
@@ -105,4 +113,17 @@ class FacetSource extends ConfigEntityBase implements FacetSourceInterface {
return $this->url_processor;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getBreadcrumbSettings() {
+ return $this->breadcrumb;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setBreadcrumbSettings(array $settings) {
+ $this->breadcrumb = $settings;
+ }
}
diff --git a/src/FacetSourceInterface.php b/src/FacetSourceInterface.php
index c78cb4d..81eaae7 100644
--- a/src/FacetSourceInterface.php
+++ b/src/FacetSourceInterface.php
@@ -49,4 +49,20 @@ interface FacetSourceInterface extends ConfigEntityInterface {
*/
public function getUrlProcessorName();
+ /**
+ * Returns an array with breadcrumb settings.
+ *
+ * @return array
+ * The breadcrumb settings.
+ */
+ public function getBreadcrumbSettings();
+
+ /**
+ * Sets breadcrumb settings.
+ *
+ * @param array $settings
+ * The breadcrumb settings.
+ */
+ public function setBreadcrumbSettings(array $settings);
+
}
diff --git a/src/Form/FacetSourceEditForm.php b/src/Form/FacetSourceEditForm.php
index 9aac428..8bc3072 100644
--- a/src/Form/FacetSourceEditForm.php
+++ b/src/Form/FacetSourceEditForm.php
@@ -89,6 +89,7 @@ class FacetSourceEditForm extends EntityForm {
/** @var \Drupal\facets\FacetSourceInterface $facet_source */
$facet_source = $this->getEntity();
+ $form['#tree'] = TRUE;
$form['filter_key'] = [
'#type' => 'textfield',
'#title' => $this->t('Filter key'),
@@ -116,6 +117,27 @@ class FacetSourceEditForm extends EntityForm {
'The URL Processor defines the url structure used for this facet source.') . '<br />- ' . implode('<br>- ', $url_processors_description),
];
+ $breadcrumb_settings = $facet_source->getBreadcrumbSettings();
+ $form['breadcrumb'] = [
+ '#type' => 'fieldset',
+ '#title' => $this->t('Breadcrumb'),
+ ];
+ $form['breadcrumb']['active'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Append active facets to breadcrumb'),
+ '#default_value' => isset($breadcrumb_settings['active']) ? $breadcrumb_settings['active'] : FALSE,
+ ];
+ $form['breadcrumb']['group'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Group active items under same crumb (not implemented yet - now always grouping)'),
+ '#default_value' => isset($breadcrumb_settings['group']) ? $breadcrumb_settings['group'] : FALSE,
+ '#states' => [
+ 'visible' => [
+ ':input[name="breadcrumb[active]"]' => ['checked' => TRUE],
+ ]
+ ]
+ ];
+
// The parent's form build method will add a save button.
return parent::buildForm($form, $form_state);
}
diff --git a/tests/src/Functional/BreadcrumbIntegrationTest.php b/tests/src/Functional/BreadcrumbIntegrationTest.php
new file mode 100644
index 0000000..5a24057
--- /dev/null
+++ b/tests/src/Functional/BreadcrumbIntegrationTest.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace Drupal\Tests\facets\Functional;
+
+use Drupal\Component\Utility\UrlHelper;
+
+/**
+ * Tests the overall functionality of the Facets admin UI.
+ *
+ * @group facets
+ */
+class BreadcrumbIntegrationTest extends FacetsTestBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = [
+ 'views',
+ 'node',
+ 'search_api',
+ 'facets',
+ 'block',
+ 'facets_search_api_dependency',
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setUp() {
+ parent::setUp();
+
+ $this->drupalLogin($this->adminUser);
+
+ $this->setUpExampleStructure();
+ $this->insertExampleContent();
+ self::assertEquals($this->indexItems($this->indexId), 5, '5 items were indexed.');
+
+ $block = [
+ 'region' => 'footer',
+ 'label' => 'Breadcrumbs',
+ 'provider' => 'system',
+ ];
+ $this->drupalPlaceBlock('system_breadcrumb_block', $block);
+ $this->resetAll();
+ }
+
+ /**
+ * Tests Breadcrumb integration with grouping.
+ */
+ public function testGroupingIntegration() {
+ $this->drupalGet('admin/config/search/facets');
+ $this->clickLink('Configure', 1);
+ $edit = [
+ 'filter_key' => 'f',
+ 'url_processor' => 'query_string',
+ 'breadcrumb[active]' => TRUE,
+ 'breadcrumb[group]' => TRUE,
+ ];
+ $this->drupalPostForm(NULL, $edit, 'Save');
+
+ $id = 'keywords';
+ $name = '#Keywords';
+ $this->createFacet($name, $id, 'keywords');
+ $this->resetAll();
+ $this->drupalGet('admin/config/search/facets/' . $id . '/edit');
+
+ $id = 'type';
+ $name = '#Type';
+ $this->createFacet($name, $id);
+ $this->resetAll();
+ $this->drupalGet('admin/config/search/facets/' . $id . '/edit');
+ $this->drupalPostForm(NULL, ['facet_settings[weight]' => '1'], 'Save');
+
+ // Breadcrumb should show #Keywords: orange > #Type: article, item
+
+ $initial_query = ['search_api_fulltext' => 'foo', 'test_param' => 1];
+ $this->drupalGet('search-api-test-fulltext', ['query' => $initial_query]);
+
+ $this->clickLink('item');
+ $this->clickLink('article');
+ $this->clickLink('orange');
+
+ $this->assertSession()->linkExists('#Keywords: orange');
+ $this->assertSession()->linkExists('#Type: article, item');
+
+ $this->clickLink('#Type: article, item');
+
+ $this->assertSession()->linkExists('#Keywords: orange');
+ $this->assertSession()->linkExists('#Type: article, item');
+ $this->checkFacetIsActive('orange');
+ $this->checkFacetIsActive('item');
+ $this->checkFacetIsActive('article');
+
+ $this->clickLink('#Keywords: orange');
+ $this->assertSession()->linkExists('#Keywords: orange');
+ $this->assertSession()->linkNotExists('#Type: article, item');
+ $this->checkFacetIsActive('orange');
+ $this->checkFacetIsNotActive('item');
+ $this->checkFacetIsNotActive('article');
+
+ // Check that the current url still has the initial parameters.
+ $curr_url = UrlHelper::parse($this->getUrl());
+ $this->assertArraySubset($initial_query, $curr_url['query']);
+ }
+
+ /**
+ * Tests Breadcrumb integration without grouping.
+ */
+// public function testNonGroupingIntegration() {
+ // TODO test it after we implement non grouping functionality.
+// }
+}
diff --git a/tests/src/Functional/FacetSourceTest.php b/tests/src/Functional/FacetSourceTest.php
index 55795c2..899bfcf 100644
--- a/tests/src/Functional/FacetSourceTest.php
+++ b/tests/src/Functional/FacetSourceTest.php
@@ -83,4 +83,28 @@ class FacetSourceTest extends FacetsTestBase {
$this->assertEquals('dummy_query', $elements[0]->getValue());
}
+ /**
+ * Tests editing the breadcrumb settings.
+ */
+ public function testEditBreadcrumbSettings() {
+ $this->assertSession()->fieldExists('breadcrumb[active]');
+ $this->assertSession()->fieldExists('breadcrumb[group]');
+ $this->assertSession()->checkboxNotChecked('breadcrumb[group]');
+ $this->assertSession()->checkboxNotChecked('breadcrumb[active]');
+ // Change the breadcrumb settings.
+ $edit = array(
+ 'breadcrumb[active]' => TRUE,
+ 'breadcrumb[group]' => TRUE,
+ );
+ $this->drupalPostForm(NULL, $edit, 'Save');
+ $this->assertSession()->statusCodeEquals(200);
+
+ $this->assertSession()->addressEquals('admin/config/search/facets');
+ $this->assertSession()->pageTextContains('Facet source search_api:views_block__search_api_test_view__block_1 has been saved.');
+ $this->clickLink('Configure');
+
+ // Test that saving worked and that the url processor has the new value.
+ $this->assertSession()->checkboxChecked('breadcrumb[group]');
+ $this->assertSession()->checkboxChecked('breadcrumb[active]');
+ }
}
diff --git a/tests/src/Kernel/Entity/FacetSourceTest.php b/tests/src/Kernel/Entity/FacetSourceTest.php
index 976f50a..0bfce32 100644
--- a/tests/src/Kernel/Entity/FacetSourceTest.php
+++ b/tests/src/Kernel/Entity/FacetSourceTest.php
@@ -60,6 +60,8 @@ class FacetSourceTest extends KernelTestBase {
* @covers ::getFilterKey
* @covers ::setUrlProcessor
* @covers ::getUrlProcessorName
+ * @covers ::setBreadcrumbSettings
+ * @covers ::getBreadcrumbSettings
*/
public function testGetterSetters() {
$fs = new FacetSource(['id' => 'llama'], 'facets_facet_source');
@@ -67,12 +69,17 @@ class FacetSourceTest extends KernelTestBase {
$this->assertNull($fs->getFilterKey());
$this->assertNull($fs->getName());
$this->assertEquals('query_string', $fs->getUrlProcessorName());
+ $this->assertEmpty($fs->getBreadcrumbSettings());
$fs->setFilterKey('ab');
$this->assertEquals('ab', $fs->getFilterKey());
$fs->setUrlProcessor('test');
$this->assertEquals('test', $fs->getUrlProcessorName());
+
+ $breadcrumb_settings = ['active' => 1, 'group' => 1];
+ $fs->setBreadcrumbSettings($breadcrumb_settings);
+ $this->assertEquals($breadcrumb_settings, $fs->getBreadcrumbSettings());
}
}