Commit b3e1b546 authored by borisson_'s avatar borisson_ Committed by borisson_

Issue #2596447 by borisson_: Port facet dependencies

parent 58d008e6
......@@ -320,4 +320,22 @@ class DefaultFacetManager {
$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];
}
}
<?php
/**
* @file
* Contains \Drupal\facets\Plugin\Condition\OtherFacet.
*/
namespace Drupal\facets\Plugin\Condition;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Block\BlockManager;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\facets\FacetManager\DefaultFacetManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides an 'other facet' condition.
*
* This adds a condition plugin to make sure that facets can depend on other
* facet's or their values. The facet value is a freeform textfield and works on
* both raw and display values of the results.
*
* @Condition(
* id = "other_facet",
* label = @Translation("Other facet"),
* )
*/
class OtherFacet extends ConditionPluginBase implements ContainerFactoryPluginInterface {
/**
* The facet entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $facetStorage;
/**
* The block plugin manager.
*
* @var \Drupal\Core\Block\BlockManager
*/
protected $blockManager;
/**
* The user that's currently logged in.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* The facet manager service.
*
* @var \Drupal\facets\FacetManager\DefaultFacetManager
*/
protected $facetManager;
/**
* Creates a new instance of the condition.
*
* @param \Drupal\Core\Entity\EntityStorageInterface $entity_storage
* The entity storage.
* @param \Drupal\Core\Block\BlockManager $block_manager
* The block plugin manager.
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
* The currently logged in user.
* @param \Drupal\facets\FacetManager\DefaultFacetManager $facet_manager
* The default facet manager class.
* @param array $configuration
* The plugin configuration, i.e. an array with configuration values keyed
* by configuration option name. The special key 'context' may be used to
* initialize the defined contexts by setting it to an array of context
* values keyed by context names.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
*/
public function __construct(EntityStorageInterface $entity_storage, BlockManager $block_manager, AccountProxyInterface $current_user, DefaultFacetManager $facet_manager,array $configuration, $plugin_id, $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->facetStorage = $entity_storage;
$this->blockManager = $block_manager;
$this->currentUser = $current_user;
$this->facetManager = $facet_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$container->get('entity_type.manager')->getStorage('facets_facet'),
$container->get('plugin.manager.block'),
$container->get('current_user'),
$container->get('facets.manager'),
$configuration,
$plugin_id,
$plugin_definition
);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$options = [];
// Loop over all defined blocks and filter them by provider, this builds an
// array of blocks that are provided by the facets module.
foreach ($this->blockManager->getDefinitions() as $definition) {
if ($definition['provider'] == 'facets') {
$options[$definition['id']] = $definition['label'];
}
}
$form['facets'] = [
'#title' => $this->t('Other facet blocks'),
'#type' => 'radios',
'#options' => $options,
'#default_value' => $this->configuration['facets'],
];
$form['facet_value'] = [
'#title' => $this->t('Facet value'),
'#description' => $this->t('Only applies when a facet is already selected.'),
'#type' => 'textfield',
'#default_value' => $this->configuration['facet_value'],
];
return parent::buildConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['facets'] = $form_state->getValue('facets');
$this->configuration['facet_value'] = $form_state->getValue('facet_value');
parent::submitConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function summary() {
return $this->t(
'The facet is @facet also rendered on the same page.',
['@facet' => $this->configuration['facets']]
);
}
/**
* {@inheritdoc}
*/
public function evaluate() {
$allowed_facet_value = $this->configuration['facet_value'];
$allowed_facets = $this->configuration['facets'];
// Return as early as possible when there are no settings for allowed
// facets.
if (empty($allowed_facets)) {
return TRUE;
}
/** @var \Drupal\facets\Plugin\Block\FacetBlock $block_plugin */
$block_plugin = $this->blockManager->createInstance($allowed_facets);
// Allowed facet value is not set, so we only have to check if the block is
// shown here by running the access method on the block plugin with the
// currently logged in user.
if (empty($allowed_facet_value)) {
return $block_plugin->access($this->currentUser);
}
// The block plugin id is saved in the schema: BasePluginID:FacetID. This
// means we can explode the ID on ':' and the facet id is in the last part
// of that result.
$block_plugin_id = $block_plugin->getPluginId();
$facet_id = explode(PluginBase::DERIVATIVE_SEPARATOR, $block_plugin_id)[1];
/** @var \Drupal\facets\FacetInterface $facet */
$facet = $this->facetStorage->load($facet_id);
$this->facetManager->setFacetSourceId($facet->getFacetSourceId());
$facet = $this->facetManager->returnProcessedFacet($facet_id);
foreach ($facet->getResults() as $result) {
$is_value = $result->getRawValue() == $allowed_facet_value || $result->getDisplayValue() == $allowed_facet_value;
if ($is_value && $result->isActive()) {
return TRUE;
}
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$config = ['facets' => FALSE, 'facet_value' => FALSE];
return $config + parent::defaultConfiguration();
}
}
......@@ -35,6 +35,10 @@ class IntegrationTest extends FacetWebTestBase {
$this->setUpExampleStructure();
$this->insertExampleContent();
$this->assertEqual($this->indexItems($this->indexId), 5, '5 items were indexed.');
// Make absolutely sure the ::$blocks variable doesn't pass information
// along between tests.
$this->blocks = NULL;
}
/**
......@@ -203,6 +207,88 @@ class IntegrationTest extends FacetWebTestBase {
$this->assertUrl($url);
}
/**
* Tests facet dependencies.
*/
public function testFacetDependencies() {
$facet_name = "DependableFacet";
$facet_id = 'dependablefacet';
$this->addFacet($facet_name);
$depending_facet_name = "DependingFacet";
$depending_facet_id = "dependingfacet";
$this->addFacet($depending_facet_name, 'keywords');
// Create both facets as blocks and add them on the page.
$this->createFacetBlock($facet_id);
$this->createFacetBlock($depending_facet_id);
// Go the the view and test that both facets are shown. Item and article
// come from the DependableFacet, orange and grape come from DependingFacet.
$this->drupalGet('search-api-test-fulltext');
$this->assertLink('grape');
$this->assertLink('orange');
$this->assertLink('item');
$this->assertLink('article');
$this->assertFacetBlocksAppear();
// Change the visiblity settings of the DependingFacet.
$this->drupalGet('admin/structure/block/manage/dependingfacet');
$edit = [
'visibility[other_facet][facets]' => 'facet_block:dependablefacet',
'visibility[other_facet][facet_value]' => 'item',
];
$this->drupalPostForm(NULL, $edit, $this->t('Save block'));
$this->assertText('The block configuration has been saved.');
// Go to the view and test that only the types are shown.
$this->drupalGet('search-api-test-fulltext');
$this->assertNoLink('grape');
$this->assertNoLink('orange');
$this->assertLink('item');
$this->assertLink('article');
// Click on the item, and test that this shows the keywords.
$this->clickLink('item');
$this->assertLink('grape');
$this->assertLink('orange');
// Go back to the view, click on article and test that the keywords are
// hidden.
$this->drupalGet('search-api-test-fulltext');
$this->clickLink('article');
$this->assertNoLink('grape');
$this->assertNoLink('orange');
// Change the visibility settings to negate the previous settings.
$this->drupalGet('admin/structure/block/manage/dependingfacet');
$edit = [
'visibility[other_facet][facets]' => 'facet_block:dependablefacet',
'visibility[other_facet][facet_value]' => 'item',
'visibility[other_facet][negate]' => TRUE,
];
$this->drupalPostForm(NULL, $edit, $this->t('Save block'));
// Go the the view and test only the type facet is shown.
$this->drupalGet('search-api-test-fulltext');
$this->assertLink('item');
$this->assertLink('article');
$this->assertLink('grape');
$this->assertLink('orange');
// Click on the article, and test that this shows the keywords.
$this->clickLink('article');
$this->assertLink('grape');
$this->assertLink('orange');
// Go back to the view, click on item and test that the keywords are
// hidden.
$this->drupalGet('search-api-test-fulltext');
$this->clickLink('item');
$this->assertNoLink('grape');
$this->assertNoLink('orange');
}
/**
* Deletes a facet block by id.
*
......@@ -315,7 +401,7 @@ class IntegrationTest extends FacetWebTestBase {
* @param string $facet_name
* The name of the facet.
*/
protected function addFacet($facet_name) {
protected function addFacet($facet_name, $facet_type = 'type') {
$facet_id = $this->convertNameToMachineName($facet_name);
// Go to the Add facet page and make sure that returns a 200.
......@@ -353,7 +439,7 @@ class IntegrationTest extends FacetWebTestBase {
// longer shown.
$facet_source_form = [
'facet_source_id' => 'search_api_views:search_api_test_view:page_1',
'facet_source_configs[search_api_views:search_api_test_view:page_1][field_identifier]' => 'type',
'facet_source_configs[search_api_views:search_api_test_view:page_1][field_identifier]' => $facet_type,
];
$this->drupalPostForm(NULL, $form_values + $facet_source_form, $this->t('Save'));
$this->assertNoText('field is required.');
......@@ -365,7 +451,6 @@ class IntegrationTest extends FacetWebTestBase {
$this->drupalGet('admin/config/search/facets');
}
/**
* Tests editing of a facet through the UI.
*
......
<?php
/**
* @file
* Contains \Drupal\Tests\facets\Plugin\Condition\OtherFacetTest.
*/
namespace Drupal\Tests\facets\Unit\Plugin\Condition;
use Drupal\facets\Entity\Facet;
use Drupal\facets\Plugin\Condition\OtherFacet;
use Drupal\facets\Result\Result;
use Drupal\Tests\UnitTestCase;
/**
* Unit test for the 'other facet' condition plugin.
*
* @group facets
*/
class OtherFacetTest extends UnitTestCase {
/**
* Tests what happens when no values are passed on to the plugin.
*/
public function testNoValue() {
$storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
$manager = $this->getMockBuilder('\Drupal\Core\Block\BlockManager')
->disableOriginalConstructor()
->getMock();
$user = $this->getMock('\Drupal\Core\Session\AccountProxyInterface');
$facet_manager = $this->getMockBuilder('\Drupal\facets\FacetManager\DefaultFacetManager')
->disableOriginalConstructor()
->getMock();
$sut = new OtherFacet($storage, $manager, $user, $facet_manager, [], 'other_facet', '');
$evaluation = $sut->evaluate();
$this->assertTrue($evaluation);
}
/**
* Tests the return value of the plugin for a displayed facet.
*/
public function testDisplayedFacet() {
$block = $this->getMockBuilder('\Drupal\facets\Plugin\Block\FacetBlock')
->disableOriginalConstructor()
->getMock();
$block->expects($this->exactly(1))
->method('access')
->willReturn(TRUE);
$storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
$manager = $this->getMockBuilder('\Drupal\Core\Block\BlockManager')
->disableOriginalConstructor()
->getMock();
$manager->expects($this->exactly(1))
->method('createInstance')
->willReturn($block);
$user = $this->getMock('\Drupal\Core\Session\AccountProxyInterface');
$facet_manager = $this->getMockBuilder('\Drupal\facets\FacetManager\DefaultFacetManager')
->disableOriginalConstructor()
->getMock();
$sut = new OtherFacet($storage, $manager, $user, $facet_manager, ['facets' => 'test'], 'other_facet', '');
$evaluation = $sut->evaluate();
$this->assertTrue($evaluation);
}
/**
* Tests the return value of the plugin for a hidden facet.
*/
public function testHiddenFacet() {
$block = $this->getMockBuilder('\Drupal\facets\Plugin\Block\FacetBlock')
->disableOriginalConstructor()
->getMock();
$block->expects($this->exactly(1))
->method('access')
->willReturn(FALSE);
$storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
$manager = $this->getMockBuilder('\Drupal\Core\Block\BlockManager')
->disableOriginalConstructor()
->getMock();
$manager->expects($this->exactly(1))
->method('createInstance')
->willReturn($block);
$user = $this->getMock('\Drupal\Core\Session\AccountProxyInterface');
$facet_manager = $this->getMockBuilder('\Drupal\facets\FacetManager\DefaultFacetManager')
->disableOriginalConstructor()
->getMock();
$sut = new OtherFacet($storage, $manager, $user, $facet_manager, ['facets' => 'test'], 'other_facet', '');
$evaluation = $sut->evaluate();
$this->assertFalse($evaluation);
}
/**
* Tests the return value of the plugin for an active facet value.
*/
public function testActiveFacetValue() {
$facet = new Facet([], 'facets_facet');
/** @var \Drupal\facets\Result\ResultInterface[] $results */
$results = [
new Result('llama', 'Llama', 1),
new Result('kitten', 'Kitten', 5),
new Result('puppy', 'Puppy', 3),
];
$results[0]->setActiveState(TRUE);
$facet->setResults($results);
$block = $this->getMockBuilder('\Drupal\facets\Plugin\Block\FacetBlock')
->disableOriginalConstructor()
->getMock();
$block->expects($this->exactly(0))->method('access');
$block->expects($this->exactly(1))
->method('getPluginId')
->willReturn('block:id');
$storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
$storage->expects($this->exactly(1))
->method('load')
->with('id')
->willReturn($facet);
$manager = $this->getMockBuilder('\Drupal\Core\Block\BlockManager')
->disableOriginalConstructor()
->getMock();
$manager->expects($this->exactly(1))
->method('createInstance')
->willReturn($block);
$user = $this->getMock('\Drupal\Core\Session\AccountProxyInterface');
$facet_manager = $this->getMockBuilder('\Drupal\facets\FacetManager\DefaultFacetManager')
->disableOriginalConstructor()
->getMock();
$facet_manager->expects($this->exactly(1))
->method('returnProcessedFacet')
->with('id')
->willReturn($facet);
$configuration = ['facets' => 'test', 'facet_value' => 'llama'];
$sut = new OtherFacet($storage, $manager, $user, $facet_manager, $configuration, 'other_facet', '');
$evaluation = $sut->evaluate();
$this->assertTrue($evaluation);
}
/**
* Tests the return value of the plugin for an inactive facet value.
*/
public function testInactiveFacetValue() {
$facet = new Facet([], 'facets_facet');
/** @var \Drupal\facets\Result\ResultInterface[] $results */
$results = [
new Result('llama', 'Llama', 1),
new Result('kitten', 'Kitten', 5),
new Result('puppy', 'Puppy', 3),
];
$results[1]->setActiveState(TRUE);
$facet->setResults($results);
$block = $this->getMockBuilder('\Drupal\facets\Plugin\Block\FacetBlock')
->disableOriginalConstructor()
->getMock();
$block->expects($this->exactly(0))->method('access');
$block->expects($this->exactly(1))
->method('getPluginId')
->willReturn('block:id');
$storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
$storage->expects($this->exactly(1))
->method('load')
->with('id')
->willReturn($facet);
$manager = $this->getMockBuilder('\Drupal\Core\Block\BlockManager')
->disableOriginalConstructor()
->getMock();
$manager->expects($this->exactly(1))
->method('createInstance')
->willReturn($block);
$user = $this->getMock('\Drupal\Core\Session\AccountProxyInterface');
$facet_manager = $this->getMockBuilder('\Drupal\facets\FacetManager\DefaultFacetManager')
->disableOriginalConstructor()
->getMock();
$facet_manager->expects($this->exactly(1))
->method('returnProcessedFacet')
->with('id')
->willReturn($facet);
$configuration = ['facets' => 'test', 'facet_value' => 'llama'];
$sut = new OtherFacet($storage, $manager, $user, $facet_manager, $configuration, 'other_facet', '');
$evaluation = $sut->evaluate();
$this->assertFalse($evaluation);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment