Skip to content
Commits on Source (93)
......@@ -35,8 +35,8 @@ env:
- PATH="$PATH:$HOME/.composer/vendor/bin"
# Configuration variables.
- DRUPAL_TI_MODULE_NAME="facetapi"
- DRUPAL_TI_SIMPLETEST_GROUP="facetapi"
- DRUPAL_TI_MODULE_NAME="facets"
- DRUPAL_TI_SIMPLETEST_GROUP="facets"
# Define runners and environment vars to include before and after the
# main runners / environment vars.
......
......@@ -4,9 +4,7 @@ CONTENTS OF THIS FILE
* Requirements
* Installation
* Configuration
* Troubleshooting
* FAQ
* Maintainers
INTRODUCTION
------------
......@@ -14,8 +12,9 @@ Todo
REQUIREMENTS
------------
No other modules required, though the module is useless without an implementing
module like search api.
No other modules required, we're supporting drupal core as a source for creating
facets. Though we recommend using Search API, as that integration is better
tested.
INSTALLATION
------------
......@@ -25,17 +24,14 @@ INSTALLATION
CONFIGURATION
-------------
Todo
Before adding a facet, there should be a facet source. Facet sources can be:
- Drupal core's search.
- A view based on a Search API index with a page display.
- A page from the search_api_page module.
TROUBLESHOOTING
---------------
Todo
After adding one of those, you can add a facet on the facets configuration page:
/admin/config/search/facets
FAQ
---
Todo
MAINTAINERS
-----------
Current maintainers:
* Todo
facetapi.facet.*:
facets.facet.*:
type: config_entity
label : 'Facet'
mapping:
......@@ -14,6 +14,9 @@ facetapi.facet.*:
status:
type: boolean
label: 'Status'
url_alias:
type: string
label: 'Name of facet as used in the URL'
facet_source_id:
type: string
label: 'Facet source id'
......@@ -23,9 +26,28 @@ facetapi.facet.*:
query_type_name:
type: string
label: 'Query Type Name'
query_operator:
type: string
label: 'Query Operator'
exclude:
type: boolean
label: 'Exclude'
widget:
type: string
label: 'Field identifier'
label: 'Widget identifier'
empty_behavior:
type: mapping
label: 'Empty behavior'
mapping:
behavior:
type: string
label: 'The empty behavior identifier'
text_format:
type: string
label: 'Text format'
text:
type: string
label: 'Text'
widget_configs:
type: sequence
label: 'Widget plugin configurations'
......@@ -52,4 +74,18 @@ facetapi.facet.*:
type: integer
label: 'The processor''s weight for this stage'
settings:
type: facetapi.processor.plugin.[%parent.processor_id]
type: plugin.plugin_configuration.facets_processor.[%parent.processor_id]
facet_configs:
type: sequence
label: 'Facet plugin-specific options'
sequence:
type: plugin.plugin_configuration.facets_facet_options.[%key]
label: 'Facet plugin options'
condition.plugin.other_facet:
type: condition.plugin
mapping:
facet_value:
type: string
facets:
type: string
facets.facet_source.*:
type: config_entity
label : 'Facet Source'
mapping:
uuid:
type: string
label: 'UUID'
id:
type: string
label: 'ID'
name:
type: label
label: Name'
filter_key:
type: string
label: 'Filter key'
url_processor:
type: string
label: 'Url processor'
facetapi.processor.plugin.count_widget_order:
plugin.plugin_configuration.facets_processor.count_widget_widget_order:
type: mapping
label: 'Count widget order'
mapping:
......@@ -6,7 +6,7 @@ facetapi.processor.plugin.count_widget_order:
type: string
label: sort order
facetapi.processor.plugin.display_value_widget_order:
plugin.plugin_configuration.facets_processor.display_value_widget_order:
type: mapping
label: 'Display value widget order'
mapping:
......@@ -14,7 +14,7 @@ facetapi.processor.plugin.display_value_widget_order:
type: string
label: sort order
facetapi.processor.plugin.raw_value_widget_order:
plugin.plugin_configuration.facets_processor.raw_value_widget_order:
type: mapping
label: 'Raw value widget order'
mapping:
......@@ -22,7 +22,7 @@ facetapi.processor.plugin.raw_value_widget_order:
type: string
label: sort order
facetapi.processor.plugin.active_widget_order:
plugin.plugin_configuration.facets_processor.active_widget_order:
type: mapping
label: 'Active widget order'
mapping:
......@@ -30,10 +30,19 @@ facetapi.processor.plugin.active_widget_order:
type: string
label: sort order
facetapi.processor.plugin.minimum_count:
plugin.plugin_configuration.facets_processor.count_limit:
type: mapping
label: 'Minimum count widget'
label: 'Count limit widget'
mapping:
minimum_items:
type: integer
label: 'Mimimum amount of items to show.'
maximum_items:
type: integer
label: 'Maximum amount of items to show.'
plugin.plugin_configuration.facets_processor.url_processor_handler:
type: config_object
plugin.plugin_configuration.facets_processor.hide_non_narrowing_result_processor:
type: config_object
<?php
/**
* @file
* Hooks provided by the core_search_facets module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Adds field types as possible options for facets.
*
* @param array $allowed_field_types
* The field types.
*
* @return array
* Array that contains the field types.
*/
function hook_facets_core_allowed_field_types(array $allowed_field_types) {
$allowed_field_types[] = 'float';
return $allowed_field_types;
}
/**
* @} End of "addtogroup hooks".
*/
name: 'Core Search Facet API'
name: 'Core Search Facets'
type: module
description: 'Facet API integration for Core.'
description: 'Facets integration for Core.'
core: 8.x
package: Search
dependencies:
- facetapi
- facets
......@@ -2,7 +2,7 @@
/**
* @file
* Contains core_search_facetapi.module
* Contains core_search_facets.module
*/
use Drupal\Core\Database\Query\AlterableInterface;
......@@ -12,13 +12,13 @@ use Drupal\Core\Database\Query\AlterableInterface;
*
* @param \Drupal\Core\Database\Query\AlterableInterface $query
*/
function core_search_facetapi_query_search_node_search_alter(AlterableInterface $query) {
function core_search_facets_query_search_node_search_alter(AlterableInterface $query) {
// Obtain the Facet Source id for the current search.
$request = \Drupal::requestStack()->getMasterRequest();
$search_page = $request->attributes->get('entity');
/** @var \Drupal\facetapi\FacetManager\DefaultFacetManager $facet_manager */
$facet_manager = \Drupal::service('facetapi.manager');
/** @var \Drupal\facets\FacetManager\DefaultFacetManager $facet_manager */
$facet_manager = \Drupal::service('facets.manager');
$search_id = 'core_node_search:' . $search_page->id();
// Add the active filters.
......@@ -36,11 +36,22 @@ function core_search_facetapi_query_search_node_search_alter(AlterableInterface
* @see \Drupal\search\Annotation\SearchPlugin
* @see \Drupal\search\SearchPluginManager
*/
function core_search_facetapi_search_plugin_alter(array &$definitions) {
function core_search_facets_search_plugin_alter(array &$definitions) {
// Replace the Search Plugin class to alter the search form on a different
// way because we don't need content types or languages as advanced search
// filters.
// @TODO wait until we can add configuration to a facet source.
if (isset($definitions['node_search'])) {
$definitions['node_search']['class'] = 'Drupal\core_search_facetapi\Plugin\Search\NodeSearchFacets';
//$definitions['node_search']['class'] = 'Drupal\core_search_facets\Plugin\Search\NodeSearchFacets';
}
}
/**
* Implements hook_facets_core_allowed_field_types().
*/
function core_search_facets_facets_core_allowed_field_types(array $allowed_field_types) {
$allowed_field_types[] = 'taxonomy_term';
$allowed_field_types[] = 'integer';
return $allowed_field_types;
}
......@@ -2,17 +2,17 @@
/**
* @file
* Contains \Drupal\core_search_facetapi\FacetapiQuery.
* Contains \Drupal\core_search_facets\FacetsQuery.
*/
namespace Drupal\core_search_facetapi;
namespace Drupal\core_search_facets;
use Drupal\search\SearchQuery;
/**
* Extension of the SearchQuery class.
*/
class FacetapiQuery extends SearchQuery {
class FacetsQuery extends SearchQuery {
/**
* Stores joined tables.
......@@ -24,11 +24,8 @@ class FacetapiQuery extends SearchQuery {
*
* @param array $query_info
* An associative array of query information.
* @param $table_alias
* @param string $table_alias
* The alias of the table being joined.
*
* @return FacetapiQuery
* An instance of this class.
*/
public function addFacetJoin(array $query_info, $table_alias) {
if (isset($query_info['joins'][$table_alias])) {
......@@ -46,7 +43,7 @@ class FacetapiQuery extends SearchQuery {
* @param array $query_info
* An associative array of query information.
*
* @return FacetapiQuery
* @return FacetsQuery
* An instance of this class.
*/
public function addFacetField(array $query_info) {
......@@ -75,22 +72,20 @@ class FacetapiQuery extends SearchQuery {
$this->join('search_total', 't', 'i.word = t.word');
$this
->condition('i.type', $this->type)
// @TODO needs review. Adding n.uid,n.type and others to avoid "Syntax error or access violation: 1055"
->groupBy('i.langcode')
->groupBy('n.uid')
->groupBy('n.type')
->groupBy('value')
->groupBy('i.type')
->groupBy('i.sid')
->having('COUNT(*) >= :matches', array(':matches' => $this->matches));
// For complex search queries, add the LIKE conditions.
if (!$this->simple) {
$this->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type');
$this->condition($this->conditions);
}
/*if (!$this->simple) {
$this->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type');
$this->condition($this->conditions);
}*/
// Add conditions to query.
$this->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type');
$this->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type AND i.langcode = d.langcode');
$this->condition($this->conditions);
// Add tag and useful metadata.
......@@ -120,5 +115,5 @@ class FacetapiQuery extends SearchQuery {
public function getSearchExpression() {
return $this->searchExpression;
}
}
}
......@@ -2,35 +2,35 @@
/**
* @file
* Contains \Drupal\core_search_facetapi\Plugin\FacetSourceInterface.
* Contains \Drupal\core_search_facets\Plugin\CoreSearchFacetSourceInterface.
*/
namespace Drupal\core_search_facetapi\Plugin;
namespace Drupal\core_search_facets\Plugin;
use Drupal\facetapi\FacetInterface;
use Drupal\facets\FacetInterface;
/**
*
* Additional interface for core facet sources.
*
* 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.
*/
interface CoreSearchFacetSourceInterface {
/**
* Sets the facet query object.
*
* @return \Drupal\core_search_facetapi\FacetapiQuery
* @return \Drupal\core_search_facets\FacetsQuery
* The facet query object.
*/
public function getFacetQueryExtender();
/**
* Returns the query info for this facet field.
*
* @param \Drupal\facetapi\FacetInterface $facet
* The facet definition as returned by facetapi_facet_load().
* @param \Drupal\facets\FacetInterface $facet
* The facet definition as returned by facets_facet_load().
*
* @return array
* An associative array containing:
......@@ -47,4 +47,3 @@ interface CoreSearchFacetSourceInterface {
public function getQueryInfo(FacetInterface $facet);
}
......@@ -2,22 +2,24 @@
/**
* @file
* Contains \Drupal\core_search_facetapi\Plugin\Search\NodeSearchFacets.
* Contains \Drupal\core_search_facets\Plugin\Search\NodeSearchFacets.
*/
namespace Drupal\core_search_facetapi\Plugin\Search;
namespace Drupal\core_search_facets\Plugin\Search;
use Drupal\Core\Config\Config;
use Drupal\Core\Database\Driver\mysql\Connection;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\facets\FacetSource\FacetSourcePluginManager;
use Drupal\node\Plugin\Search\NodeSearch;
use Drupal\Core\Render\RendererInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Handles searching for node entities using the Search module index.
......@@ -34,19 +36,19 @@ class NodeSearchFacets extends NodeSearch {
$plugin_id,
$plugin_definition,
Connection $database,
EntityManagerInterface $entity_manager,
EntityTypeManagerInterface $entity_manager,
ModuleHandlerInterface $module_handler,
Config $search_settings,
LanguageManagerInterface $language_manager,
RendererInterface $renderer,
AccountInterface $account = NULL,
$facet_source_plugin_manager,
$request_stack) {
FacetSourcePluginManager $facet_source_plugin_manager,
RequestStack $request_stack,
AccountInterface $account = NULL) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $database, $entity_manager, $module_handler, $search_settings, $language_manager, $renderer, $account);
/** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */
if ($search_page = $request_stack->getMasterRequest()->attributes->get('entity')) {
/** @var \Drupal\facetapi\FacetSource\FacetSourcePluginManager $facet_source_plugin_manager */
/** @var \Drupal\facets\FacetSource\FacetSourcePluginManager $facet_source_plugin_manager */
$this->facetSource = $facet_source_plugin_manager->createInstance('core_node_search:' . $search_page->id());
}
}
......@@ -60,14 +62,14 @@ class NodeSearchFacets extends NodeSearch {
$plugin_id,
$plugin_definition,
$container->get('database'),
$container->get('entity.manager'),
$container->get('entity_type.manager'),
$container->get('module_handler'),
$container->get('config.factory')->get('search.settings'),
$container->get('language_manager'),
$container->get('renderer'),
$container->get('current_user'),
$container->get('plugin.manager.facetapi.facet_source'),
$container->get('request_stack')
$container->get('plugin.manager.facets.facet_source'),
$container->get('request_stack'),
$container->get('current_user')
);
}
......@@ -80,7 +82,7 @@ class NodeSearchFacets extends NodeSearch {
$used_advanced = !empty($parameters[self::ADVANCED_FORM]);
if ($used_advanced) {
$f = isset($parameters['f']) ? (array) $parameters['f'] : array();
$defaults = $this->parseAdvancedDefaults($f, $keys);
$defaults = $this->parseAdvancedDefaults($f, $keys);
}
else {
$defaults = array('keys' => $keys);
......@@ -96,6 +98,7 @@ class NodeSearchFacets extends NodeSearch {
'#access' => $this->account && $this->account->hasPermission('use advanced search'),
'#open' => $used_advanced,
);
$form['advanced']['keywords-fieldset'] = array(
'#type' => 'fieldset',
'#title' => t('Keywords'),
......@@ -188,4 +191,3 @@ class NodeSearchFacets extends NodeSearch {
}
}
......@@ -2,28 +2,31 @@
/**
* @file
* Contains \Drupal\core_search_facetapi\Plugin\facet_api\facet_source\CoreNodeSearchFacetSource
* Contains \Drupal\core_search_facets\Plugin\facets\facet_source\CoreNodeSearchFacetSource.
*/
namespace Drupal\core_search_facetapi\Plugin\facetapi\facet_source;
namespace Drupal\core_search_facets\Plugin\facets\facet_source;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\core_search_facetapi\Plugin\CoreSearchFacetSourceInterface;
use Drupal\facetapi\FacetInterface;
use Drupal\facetapi\FacetSource\FacetSourceInterface;
use Drupal\facetapi\FacetSource\FacetSourcePluginBase;
use Drupal\core_search_facets\Plugin\CoreSearchFacetSourceInterface;
use Drupal\facets\FacetInterface;
use Drupal\facets\FacetSource\FacetSourcePluginBase;
use Drupal\facets\QueryType\QueryTypePluginManager;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\search\SearchPageInterface;
use Drupal\search\SearchPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Represents a facet source which represents the search api views.
*
* @FacetApiFacetSource(
* @FacetsFacetSource(
* id = "core_node_search",
* deriver = "Drupal\core_search_facetapi\Plugin\facetapi\facet_source\CoreNodeSearchFacetSourceDeriver"
* deriver = "Drupal\core_search_facets\Plugin\facets\facet_source\CoreNodeSearchFacetSourceDeriver"
* )
*/
class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSearchFacetSourceInterface {
......@@ -52,6 +55,11 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
*/
protected $configFactory;
/**
* The plugin manager for core search plugins.
*
* @var \Drupal\search\SearchPluginManager
*/
protected $searchManager;
/**
......@@ -62,7 +70,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, $query_type_plugin_manager, $search_manager, RequestStack $request_stack) {
public function __construct(array $configuration, $plugin_id, array $plugin_definition, QueryTypePluginManager $query_type_plugin_manager, SearchPluginManager $search_manager, RequestStack $request_stack) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $query_type_plugin_manager);
$this->searchManager = $search_manager;
$this->setSearchKeys($request_stack->getMasterRequest()->query->get('keys'));
......@@ -79,7 +87,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
$configuration,
$plugin_id,
$plugin_definition,
$container->get('plugin.manager.facetapi.query_type'),
$container->get('plugin.manager.facets.query_type'),
$container->get('plugin.manager.search'),
$request_stack
);
......@@ -95,7 +103,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
$view->execute();
return $view->getDisplay()->getOption('path');*/
return;
return '';
}
/**
......@@ -119,22 +127,35 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
* {@inheritdoc}
*/
public function getQueryTypesForFacet(FacetInterface $facet) {
// Get our FacetApi Field Identifier.
$field_id = $facet->getFieldIdentifier();
// Verify if the field exists. Otherwise the type will be a column
// (type,uid...) from the node and we can use the field identifier directly.
if ($field = FieldStorageConfig::loadByName('node', $facet->getFieldIdentifier())) {
$field_type = $field->getType();
}
else {
$field_type = $facet->getFieldIdentifier();
}
return $this->getQueryTypesForDataType($field_id);
return $this->getQueryTypesForFieldType($field_type);
}
/**
* @param $field_id
* Get the query types for a field type.
*
* @param string $field_type
* The field type.
*
* @return array
* An array of query types.
*/
public function getQueryTypesForDataType($field_id) {
protected function getQueryTypesForFieldType($field_type) {
$query_types = [];
switch ($field_id) {
switch ($field_type) {
case 'type':
case 'uid':
case 'langcode':
case 'integer':
case 'entity_reference':
$query_types['string'] = 'core_node_search_string';
break;
}
......@@ -146,7 +167,6 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
* {@inheritdoc}
*/
public function isRenderedInCurrentRequest() {
// @TODO Avoid the use of \Duupal so maybe inject?
$request = \Drupal::requestStack()->getMasterRequest();
$search_page = $request->attributes->get('entity');
if ($search_page instanceof SearchPageInterface) {
......@@ -163,7 +183,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state, FacetInterface $facet, FacetSourceInterface $facet_source) {
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['field_identifier'] = [
'#type' => 'select',
......@@ -171,7 +191,7 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
'#title' => $this->t('Facet field'),
'#description' => $this->t('Choose the indexed field.'),
'#required' => TRUE,
'#default_value' => $facet->getFieldIdentifier(),
'#default_value' => $this->facet->getFieldIdentifier(),
];
return $form;
......@@ -181,32 +201,57 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
* {@inheritdoc}
*/
public function getFields() {
$default_fields = [
// Default fields.
$facet_fields = $this->getDefaultFields();
// Get the allowed field types.
$allowed_field_types = \Drupal::moduleHandler()->invokeAll('facets_core_allowed_field_types', array($field_types = []));
// Get the current field instances and detect if the field type is allowed.
$fields = FieldConfig::loadMultiple();
/** @var \Drupal\Field\FieldConfigInterface $field */
foreach ($fields as $field) {
// Verify if the target type is allowed for entity reference fields,
// otherwise verify the field type(i.e. integer, float...).
$target_is_allowed = in_array($field->getFieldStorageDefinition()->getSetting('target_type'), $allowed_field_types);
$field_is_allowed = in_array($field->getFieldStorageDefinition()->getType(), $allowed_field_types);
if ($target_is_allowed || $field_is_allowed) {
/** @var \Drupal\field\Entity\FieldConfig $field */
if (!array_key_exists($field->getName(), $facet_fields)) {
$facet_fields[$field->getName()] = $this->t('@label', ['@label' => $field->getLabel()]);
}
}
}
return $facet_fields;
}
/**
* Getter for default node fields.
*
* @return array
* An array containing the default fields enabled on a node.
*/
protected function getDefaultFields() {
return [
'type' => $this->t('Content Type'),
'uid' => $this->t('Author'),
'langcode' => $this->t('Language'),
];
return $default_fields;
}
/**
* {@inheritdoc}
*/
public function getFacetQueryExtender() {
//if (!$this->facetQueryExtender) {
//$this->facetQueryExtender = db_select('search_index', 'i', array('target' => 'replica'))->extend('Drupal\search\ViewsSearchQuery');
//$this->searchQuery->searchExpression($input, $this->searchType);
//$this->searchQuery->publicParseSearchExpression();
$this->facetQueryExtender = db_select('search_index', 'i', array('target' => 'replica'))->extend('Drupal\core_search_facetapi\FacetapiQuery');
$this->facetQueryExtender->join('node_field_data', 'n', 'n.nid = i.sid');
$this->facetQueryExtender
//->condition('n.status', 1)
->addTag('node_access')
->searchExpression($this->keys, 'node_search');
//}
if (!$this->facetQueryExtender) {
$this->facetQueryExtender = db_select('search_index', 'i', array('target' => 'replica'))->extend('Drupal\core_search_facets\FacetsQuery');
$this->facetQueryExtender->join('node_field_data', 'n', 'n.nid = i.sid');
$this->facetQueryExtender
// ->condition('n.status', 1).
->addTag('node_access')
->searchExpression($this->keys, 'node_search');
}
return $this->facetQueryExtender;
}
......@@ -214,13 +259,16 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
* {@inheritdoc}
*/
public function getQueryInfo(FacetInterface $facet) {
//if (!$facet['field api name']) {
// We add the language code of the indexed item to the result of the query.
// So in this case we need to use the search_index table alias (i) for the
// langcode field. Otherwise we will have same nid for multiples languages
// as result. For more details you can take a look at
// NodeSearch::findResults().
$table_alias = $facet->getFieldIdentifier() == 'langcode' ? 'i' : 'n';
$query_info = [];
$field_name = $facet->getFieldIdentifier();
$default_fields = $this->getDefaultFields();
if (array_key_exists($facet->getFieldIdentifier(), $default_fields)) {
// We add the language code of the indexed item to the result of the
// query. So in this case we need to use the search_index table alias (i)
// for the langcode field. Otherwise we will have same nid for multiple
// languages as result. For more details see NodeSearch::findResults().
// @TODO review if I can refactor this.
$table_alias = $facet->getFieldIdentifier() == 'langcode' ? 'i' : 'n';
$query_info = [
'fields' => [
$table_alias . '.' . $facet->getFieldIdentifier() => [
......@@ -229,32 +277,36 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
],
],
];
//}
/*else {
$query_info = array();
}
else {
// Gets field info, finds table name and field name.
$field = field_info_field($facet['field api name']);
$table = _field_sql_storage_tablename($field);
// Iterates over columns, adds fields to query info.
foreach ($field['columns'] as $column_name => $attributes) {
$column = _field_sql_storage_columnname($field['field_name'], $column_name);
$query_info['fields'][$table . '.' . $column] = array(
'table_alias' => $table,
'field' => $column,
);
$table = "node__{$field_name}";
// The column name will be different depending on the field type, it's
// always the fields machine name, suffixed with '_value'. Entity
// reference fields change that suffix into '_target_id'.
$field_config = FieldStorageConfig::loadByName('node', $facet->getFieldIdentifier());
$field_type = $field_config->getType();
if ($field_type == 'entity_reference') {
$column = $facet->getFieldIdentifier() . '_target_id';
}
else {
$column = $facet->getFieldIdentifier() . '_value';
}
$query_info['fields'][$field_name . '.' . $column] = array(
'table_alias' => $table,
'field' => $column,
);
// Adds the join on the node table.
$query_info['joins'] = array(
$table => array(
'table' => $table,
'alias' => $table,
'condition' => "n.vid = $table.revision_id",
'condition' => "n.vid = $table.revision_id AND i.langcode = $table.langcode",
),
);
}*/
}
// Returns query info, makes sure all keys are present.
return $query_info + [
......@@ -264,12 +316,14 @@ class CoreNodeSearchFacetSource extends FacetSourcePluginBase implements CoreSea
}
/**
* Checks if the search has facets.
*
* @TODO move to the Base class???
*/
public function hasFacets() {
$manager = \Drupal::service('entity_type.manager')->getStorage('facetapi_facet');
$manager = \Drupal::service('entity_type.manager')->getStorage('facets_facet');
$facets = $manager->loadMultiple();
foreach($facets as $facet) {
foreach ($facets as $facet) {
if ($facet->getFacetSourceId() == $this->getPluginId()) {
return TRUE;
}
......
......@@ -2,26 +2,44 @@
/**
* @file
* Contains \Drupal\core_search_facetapi\Plugin\facet_api\facet_source\CoreNodeSearchFacetSourceDeriver.
* Contains \Drupal\core_search_facets\Plugin\facets\facet_source\CoreNodeSearchFacetSourceDeriver.
*/
namespace Drupal\core_search_facetapi\Plugin\facetapi\facet_source;
namespace Drupal\core_search_facets\Plugin\facets\facet_source;
use Drupal\Component\Plugin\PluginBase;
use Drupal\facetapi\FacetSource\FacetSourceDeriverBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\facets\FacetSource\FacetSourceDeriverBase;
use Drupal\search\SearchPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Derives a facet source plugin definition for every search api view.
*
* @see \Drupal\facetapi\Plugin\facetapi\facet_source\SearchApiViewsPage
* @see \Drupal\facets\Plugin\facets\facet_source\SearchApiViewsPage
*/
class CoreNodeSearchFacetSourceDeriver extends FacetSourceDeriverBase {
/**
* The plugin manager for core search plugins.
*
* @var \Drupal\search\SearchPluginManager
*/
protected $searchManager;
public function __construct(ContainerInterface $container, $base_plugin_id, $search_manager) {
/**
* Creates an instance of the deriver.
*
* @param string $base_plugin_id
* The plugin ID.
* @param \Drupal\search\SearchPluginManager $search_manager
* The plugin manager for core search plugins.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity manager.
*/
public function __construct($base_plugin_id, SearchPluginManager $search_manager, EntityTypeManagerInterface $entity_type_manager) {
$this->searchManager = $search_manager;
$this->entityTypeManager = $entity_type_manager;
}
/**
......@@ -29,10 +47,10 @@ class CoreNodeSearchFacetSourceDeriver extends FacetSourceDeriverBase {
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container,
$base_plugin_id,
$container->get('plugin.manager.search')
);
$container->get('plugin.manager.search'),
$container->get('entity_type.manager')
);
}
/**
......@@ -43,18 +61,18 @@ class CoreNodeSearchFacetSourceDeriver extends FacetSourceDeriverBase {
if (!isset($this->derivatives[$base_plugin_id])) {
$plugin_derivatives = [];
// @TODO inject entity plugin manager.
$pages = \Drupal::entityManager()->getListBuilder('search_page')->load();
foreach($pages as $machine_name => $page) {
/** @var \Drupal\search\Entity\SearchPage $page * */
$pages = $this->entityTypeManager->getStorage('search_page')->loadMultiple();
foreach ($pages as $machine_name => $page) {
/* @var \Drupal\search\Entity\SearchPage $page * */
if ($page->get('plugin') == 'node_search') {
// Detect if the plugin has "faceted" definition.
$plugin_derivatives[$machine_name] = [
'id' => $base_plugin_id . PluginBase::DERIVATIVE_SEPARATOR . $machine_name,
'label' => $this->t('Core Search Page: %page_name', ['%page_name' => $page->get('label')]),
'description' => $this->t('Provides a facet source.'),
] + $base_plugin_definition;
'id' => $base_plugin_id . PluginBase::DERIVATIVE_SEPARATOR . $machine_name,
'label' => $this->t('Core Search Page: %page_name', ['%page_name' => $page->get('label')]),
'description' => $this->t('Provides a facet source.'),
] + $base_plugin_definition;
}
uasort($plugin_derivatives, array($this, 'compareDerivatives'));
......
......@@ -2,17 +2,18 @@
/**
* @file
* Contains \Drupal\core_search_facetapi\Plugin\facetapi\query_type\CoreNodeSearchString.
* Contains \Drupal\core_search_facets\Plugin\facets\query_type\CoreNodeSearchString.
*/
namespace Drupal\core_search_facetapi\Plugin\facetapi\query_type;
namespace Drupal\core_search_facets\Plugin\facets\query_type;
use Drupal\facetapi\QueryType\QueryTypePluginBase;
use Drupal\facetapi\Result\Result;
use Drupal\facets\QueryType\QueryTypePluginBase;
use Drupal\facets\Result\Result;
/**
* A string query type for core.
*
* @FacetApiQueryType(
* @FacetsQueryType(
* id = "core_node_search_string",
* label = @Translation("String"),
* )
......@@ -20,7 +21,7 @@ use Drupal\facetapi\Result\Result;
class CoreNodeSearchString extends QueryTypePluginBase {
/**
* Holds the backend's native query object.
* The backend's native query object.
*
* @var \Drupal\search_api\Query\QueryInterface
*/
......@@ -30,12 +31,11 @@ class CoreNodeSearchString extends QueryTypePluginBase {
* {@inheritdoc}
*/
public function execute() {
/** @var \Drupal\core_search_facetapi\Plugin\CoreSearchFacetSourceInterface $facetSource */
$facetSource = $this->facet->getFacetSource();
$query_info = $facetSource->getQueryInfo($this->facet);
/** @var \Drupal\core_search_facetapi\FacetapiQuery $facet_query */
$facet_query = $facetSource->getFacetQueryExtender();
/** @var \Drupal\core_search_facets\Plugin\CoreSearchFacetSourceInterface $facet_source */
$facet_source = $this->facet->getFacetSource();
$query_info = $facet_source->getQueryInfo($this->facet);
/** @var \Drupal\core_search_facets\FacetsQuery $facet_query */
$facet_query = $facet_source->getFacetQueryExtender();
$tables_joined = [];
// Add the filter to the query if there are active values.
......@@ -45,16 +45,16 @@ class CoreNodeSearchString extends QueryTypePluginBase {
foreach ($query_info['fields'] as $field_info) {
// Adds join to the facet query.
/*$facet_query->addFacetJoin($query_info, $field_info['table_alias']);
$facet_query->addFacetJoin($query_info, $field_info['table_alias']);
// Adds adds join to search query, makes sure it is only added once.
// Adds join to search query, makes sure it is only added once.
if (isset($query_info['joins'][$field_info['table_alias']])) {
if (!isset($tables_joined[$field_info['table_alias']])) {
$tables_joined[$field_info['table_alias']] = TRUE;
$join_info = $query_info['joins'][$field_info['table_alias']];
$this->query->join($join_info['table'], $join_info['alias'], $join_info['condition']);
}
}*/
}
// Adds facet conditions to the queries.
$field = $field_info['table_alias'] . '.' . $field_info['field'];
......@@ -68,14 +68,17 @@ class CoreNodeSearchString extends QueryTypePluginBase {
* {@inheritdoc}
*/
public function build() {
/** @var \Drupal\core_search_facetapi\Plugin\CoreSearchFacetSourceInterface $facetSource */
$facetSource = $this->facet->getFacetSource();
$query_info = $facetSource->getQueryInfo($this->facet);
/* @var \Drupal\core_search_facets\Plugin\CoreSearchFacetSourceInterface $facet_source */
$facet_source = $this->facet->getFacetSource();
$query_info = $facet_source->getQueryInfo($this->facet);
/** @var \Drupal\core_search_facetapi\FacetapiQuery $facet_query */
$facet_query = $facetSource->getFacetQueryExtender();
/** @var \Drupal\core_search_facets\FacetsQuery $facet_query */
$facet_query = $facet_source->getFacetQueryExtender();
$facet_query->addFacetField($query_info);
foreach ($query_info['joins'] as $table_alias => $join_info) {
$facet_query->addFacetJoin($query_info, $table_alias);
}
// Only build results if a search is executed.
if ($facet_query->getSearchExpression()) {
......@@ -94,4 +97,3 @@ class CoreNodeSearchString extends QueryTypePluginBase {
}
}
<?php
/**
* @file
* Contains \Drupal\core_search_facets\Tests\HooksTest.
*/
namespace Drupal\core_search_facets\Tests;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests integration of hooks.
*
* @group core_search_facets
*/
class HooksTest extends WebTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'node',
'search',
'core_search_facets_test_hooks',
'field',
];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Create a field of type float.
FieldStorageConfig::create(
[
'field_name' => 'float',
'entity_type' => 'node',
'type' => 'float',
]
)->save();
// Create an instance of the float field on the "page" content type.
FieldConfig::create(
[
'field_name' => 'float',
'entity_type' => 'node',
'bundle' => 'page',
'label' => 'Float Field Label',
]
)->save();
// Log in, so we can test all the things.
$this->drupalLogin($this->adminUser);
}
/**
* Tests various that all hooks fire correctly.
*/
public function testHooks() {
// Verify that hook_facets_core_allowed_field_types was triggered.
$facet_add_page = 'admin/config/search/facets/add-facet';
$this->drupalGet($facet_add_page);
$this->assertResponse(200);
// Select the node_search facet source.
$this->drupalGet($facet_add_page);
$this->drupalPostForm(
NULL,
['facet_source_id' => 'core_node_search:node_search'],
$this->t('Configure facet source')
);
// The field appears as expected.
$this->assertText('Float Field Label', 'Float Field appears as expected');
}
}
<?php
/**
* @file
* Contains \Drupal\core_search_facets\Tests\IntegrationTest.
*/
namespace Drupal\core_search_facets\Tests;
use Drupal\core_search_facets\Tests\WebTestBase as CoreSearchFacetsWebTestBase;
/**
* Tests the admin UI with the core search facet source.
*
* @group core_search_facets
*/
class IntegrationTest extends CoreSearchFacetsWebTestBase {
/**
* The block entities used by this test.
*
* @var \Drupal\block\BlockInterface[]
*/
protected $blocks;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->drupalLogin($this->adminUser);
// Index the content.
\Drupal::service('plugin.manager.search')->createInstance('node_search')->updateIndex();
search_update_totals();
// Make absolutely sure the ::$blocks variable doesn't pass information
// along between tests.
$this->blocks = NULL;
}
/**
* Tests various operations via the Facets' admin UI.
*/
public function testFramework() {
$facet_name = "Test Facet name";
$facet_id = 'test_facet_name';
// Check if the overview is empty.
$this->checkEmptyOverview();
// Add a new facet and edit it.
$this->addFacet($facet_name);
$this->editFacet($facet_name);
// Create and place a block for "Test Facet name" facet.
$this->createFacetBlock($facet_id);
// Verify that the facet results are correct.
$this->drupalGet('search/node', ['query' => ['keys' => 'test']]);
$this->assertLink('page');
$this->assertLink('article');
// Verify that facet blocks appear as expected.
$this->assertFacetBlocksAppear();
$this->setShowAmountOfResults($facet_name, TRUE);
// Verify that the number of results per item.
$this->drupalGet('search/node', ['query' => ['keys' => 'test']]);
$this->assertLink('page (10)');
$this->assertLink('article (10)');
// Do not show the block on empty behaviors.
// Truncate the search_index table because, for the moment, we don't have
// the possibility to clear the index from the API.
// See https://www.drupal.org/node/326062
\Drupal::database()->truncate('search_index')->execute();
// Verify that no facet blocks appear. Empty behavior "None" is selected by
// default.
$this->drupalGet('search/node', ['query' => ['keys' => 'test']]);
$this->assertNoFacetBlocksAppear();
// Verify that the "empty_text" appears as expected.
$this->setEmptyBehaviorFacetText($facet_name);
$this->drupalGet('search/node', ['query' => ['keys' => 'test']]);
$this->assertRaw('block-test-facet-name');
$this->assertRaw('No results found for this block!');
// Delete the block.
$this->deleteBlock($facet_id);
// Delete the facet and make sure the overview is empty again.
$this->deleteUnusedFacet($facet_name);
$this->checkEmptyOverview();
}
/**
* Configures the possibility to show the amount of results for facet blocks.
*
* @param string $facet_name
* The name of the facet.
* @param bool|TRUE $show
* Boolean to determine if we want to show the amount of results.
*/
protected function setShowAmountOfResults($facet_name, $show = TRUE) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_display_page = '/admin/config/search/facets/' . $facet_id . '/display';
// Go to the facet edit page and make sure "edit facet %facet" is present.
$this->drupalGet($facet_display_page);
$this->assertResponse(200);
// Configure the text for empty results behavior.
$edit = [
'widget_configs[show_numbers]' => $show,
];
$this->drupalPostForm(NULL, $edit, $this->t('Save'));
}
/**
* Deletes a facet block by id.
*
* @param string $id
* The id of the block.
*/
protected function deleteBlock($id) {
$this->drupalGet('admin/structure/block/manage/' . $this->blocks[$id]->id(), array('query' => array('destination' => 'admin')));
$this->clickLink(t('Delete'));
$this->drupalPostForm(NULL, array(), t('Delete'));
$this->assertRaw(t('The block %name has been deleted.', array('%name' => $this->blocks[$id]->label())));
}
/**
* Asserts that a facet block does not appear.
*/
protected function assertNoFacetBlocksAppear() {
foreach ($this->blocks as $block) {
$this->assertNoBlockAppears($block);
}
}
/**
* Asserts that a facet block appears.
*/
protected function assertFacetBlocksAppear() {
foreach ($this->blocks as $block) {
$this->assertBlockAppears($block);
}
}
/**
* Creates a facet block by id.
*
* @param string $id
* The id of the block.
*/
protected function createFacetBlock($id) {
$block = [
'plugin_id' => 'facet_block:' . $id,
'settings' => [
'region' => 'footer',
'id' => str_replace('_', '-', $id),
],
];
$this->blocks[$id] = $this->drupalPlaceBlock($block['plugin_id'], $block['settings']);
}
/**
* Configures empty behavior option to show a text on empty results.
*
* @param string $facet_name
* The name of the facet.
*/
protected function setEmptyBehaviorFacetText($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_display_page = '/admin/config/search/facets/' . $facet_id . '/display';
// Go to the facet edit page and make sure "edit facet %facet" is present.
$this->drupalGet($facet_display_page);
$this->assertResponse(200);
// Configure the text for empty results behavior.
$edit = [
'facet_settings[empty_behavior]' => 'text',
'facet_settings[empty_behavior_container][empty_behavior_text][value]' => 'No results found for this block!',
];
$this->drupalPostForm(NULL, $edit, $this->t('Save'));
}
/**
* Configures a facet to only be visible when accessing to the facet source.
*
* @param string $facet_name
* The name of the facet.
*/
protected function setOptionShowOnlyWhenFacetSourceVisible($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_display_page = '/admin/config/search/facets/' . $facet_id . '/display';
$this->drupalGet($facet_display_page);
$this->assertResponse(200);
$edit = [
'facet_settings[only_visible_when_facet_source_is_visible]' => TRUE,
'widget' => 'links',
'widget_configs[show_numbers]' => '0',
];
$this->drupalPostForm(NULL, $edit, $this->t('Save'));
}
/**
* Get the facet overview page and make sure the overview is empty.
*/
protected function checkEmptyOverview() {
$facet_overview = '/admin/config/search/facets';
$this->drupalGet($facet_overview);
$this->assertResponse(200);
// The list overview has Field: field_name as description. This tests on the
// absence of that.
$this->assertNoText('Field:');
}
/**
* Tests adding a facet trough the interface.
*
* @param string $facet_name
* The name of the facet.
*/
protected function addFacet($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
// Go to the Add facet page and make sure that returns a 200.
$facet_add_page = '/admin/config/search/facets/add-facet';
$this->drupalGet($facet_add_page);
$this->assertResponse(200);
$form_values = [
'name' => '',
'id' => $facet_id,
'status' => 1,
'url_alias' => $facet_id,
];
// Try filling out the form, but without having filled in a name for the
// facet to test for form errors.
$this->drupalPostForm($facet_add_page, $form_values, $this->t('Save'));
$this->assertText($this->t('Facet name field is required.'));
$this->assertText($this->t('Facet source field is required.'));
// Make sure that when filling out the name, the form error disappears.
$form_values['name'] = $facet_name;
$this->drupalPostForm(NULL, $form_values, $this->t('Save'));
$this->assertNoText($this->t('Facet name field is required.'));
// Configure the facet source by selecting one of the search api views.
$this->drupalGet($facet_add_page);
$this->drupalPostForm(NULL, ['facet_source_id' => 'core_node_search:node_search'], $this->t('Configure facet source'));
// The facet field is still required.
$this->drupalPostForm(NULL, $form_values, $this->t('Save'));
$this->assertText($this->t('Facet field field is required.'));
// Fill in all fields and make sure the 'field is required' message is no
// longer shown.
$facet_source_form = [
'facet_source_configs[core_node_search:node_search][field_identifier]' => 'type',
];
$this->drupalPostForm(NULL, $form_values + $facet_source_form, $this->t('Save'));
$this->assertNoText('field is required.');
// Make sure that the redirection to the display page is correct.
$this->assertRaw(t('Facet %name has been created.', ['%name' => $facet_name]));
$this->assertUrl('admin/config/search/facets/' . $facet_id . '/display');
$this->drupalGet('admin/config/search/facets');
}
/**
* Tests editing of a facet through the UI.
*
* @param string $facet_name
* The name of the facet.
*/
public function editFacet($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_edit_page = '/admin/config/search/facets/' . $facet_id . '/edit';
// Go to the facet edit page and make sure "edit facet %facet" is present.
$this->drupalGet($facet_edit_page);
$this->assertResponse(200);
$this->assertRaw($this->t('Edit facet @facet', ['@facet' => $facet_name]));
// Change the facet name to add in "-2" to test editing of a facet works.
$form_values = ['name' => $facet_name . ' - 2'];
$this->drupalPostForm($facet_edit_page, $form_values, $this->t('Save'));
// Make sure that the redirection back to the overview was successful and
// the edited facet is shown on the overview page.
$this->assertRaw(t('Facet %name has been updated.', ['%name' => $facet_name . ' - 2']));
// Make sure the "-2" suffix is still on the facet when editing a facet.
$this->drupalGet($facet_edit_page);
$this->assertRaw($this->t('Edit facet @facet', ['@facet' => $facet_name . ' - 2']));
// Edit the form and change the facet's name back to the initial name.
$form_values = ['name' => $facet_name];
$this->drupalPostForm($facet_edit_page, $form_values, $this->t('Save'));
// Make sure that the redirection back to the overview was successful and
// the edited facet is shown on the overview page.
$this->assertRaw(t('Facet %name has been updated.', ['%name' => $facet_name]));
}
/**
* This deletes an unused facet through the UI.
*
* @param string $facet_name
* The name of the facet.
*/
protected function deleteUsedFacet($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_delete_page = '/admin/config/search/facets/' . $facet_id . '/delete';
// Go to the facet delete page and make the warning is shown.
$this->drupalGet($facet_delete_page);
$this->assertResponse(200);
// Check that the facet by testing for the message and the absence of the
// facet name on the overview.
$this->assertRaw($this->t('The facet is currently used in a block and thus can\'t be removed. Remove the block first.'));
}
/**
* This deletes a facet through the UI.
*
* @param string $facet_name
* The name of the facet.
*/
protected function deleteUnusedFacet($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_delete_page = '/admin/config/search/facets/' . $facet_id . '/delete';
// Go to the facet delete page and make the warning is shown.
$this->drupalGet($facet_delete_page);
$this->assertResponse(200);
$this->assertText($this->t('This action cannot be undone'));
// Actually submit the confirmation form.
$this->drupalPostForm(NULL, [], $this->t('Delete'));
// Check that the facet by testing for the message and the absence of the
// facet name on the overview.
$this->assertRaw($this->t('The facet %facet has been deleted.', ['%facet' => $facet_name]));
// Refresh the page because on the previous page the $facet_name is still
// visible (in the message).
$facet_overview = '/admin/config/search/facets';
$this->drupalGet($facet_overview);
$this->assertResponse(200);
$this->assertNoText($facet_name);
}
/**
* Convert facet name to machine name.
*
* @param string $facet_name
* The name of the facet.
*
* @return string
* The facet name changed to a machine name.
*/
protected function convertNameToMachineName($facet_name) {
return preg_replace('@[^a-zA-Z0-9_]+@', '_', strtolower($facet_name));
}
/**
* Go to the Delete Facet Page using the facet name.
*
* @param string $facet_name
* The name of the facet.
*/
protected function goToDeleteFacetPage($facet_name) {
$facet_id = $this->convertNameToMachineName($facet_name);
$facet_delete_page = '/admin/config/search/facets/' . $facet_id . '/delete';
// Go to the facet delete page and make the warning is shown.
$this->drupalGet($facet_delete_page);
$this->assertResponse(200);
}
}
<?php
/**
* @file
* Contains \Drupal\core_search_facets\Tests\WebTestBase.
*/
namespace Drupal\core_search_facets\Tests;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\simpletest\WebTestBase as SimpletestWebTestBase;
/**
* Provides the base class for web tests for Core Search Facets.
*/
abstract class WebTestBase extends SimpletestWebTestBase {
use StringTranslationTrait;
/**
* Modules to enable for this test.
*
* @var string[]
*/
public static $modules = [
'field',
'search',
'entity_test',
'views',
'node',
'facets',
'block',
'core_search_facets',
];
/**
* An admin user used for this test.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $adminUser;
/**
* A user without Search / Facet admin permission.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $unauthorizedUser;
/**
* The anonymous user used for this test.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $anonymousUser;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Create content types.
$this->drupalCreateContentType(['type' => 'page']);
$this->drupalCreateContentType(['type' => 'article']);
// Adding 10 pages.
for ($i = 0; $i < 10; $i++) {
$this->drupalCreateNode(array(
'title' => 'foo bar' . $i,
'body' => 'test page' . $i,
'type' => 'page',
));
}
// Adding 10 articles.
for ($i = 0; $i < 10; $i++) {
$this->drupalCreateNode(array(
'title' => 'foo baz' . $i,
'body' => 'test article' . $i,
'type' => 'article',
));
}
// Create the users used for the tests.
$this->adminUser = $this->drupalCreateUser([
'administer search',
'administer facets',
'access administration pages',
'administer nodes',
'access content overview',
'administer content types',
'administer blocks',
'search content',
]);
}
}
name: 'Core Search Facets Hooks Test'
type: module
description: 'Support module for core_search_facets tests, tests all the hooks.'
package: Testing
dependencies:
- facets
- core_search_facets
core: 8.x
hidden: true
<?php
/**
* @file
* Tests all the hooks defined by the core_search_facets module.
*/
/**
* Implements hook_facets_core_allowed_field_types().
*/
function core_search_facets_test_hooks_facets_core_allowed_field_types(array $allowed_field_types) {
$allowed_field_types[] = 'float';
return $allowed_field_types;
}
/**
* @file
* Administration styles for the Facet API module.
* Administration styles for the Facets module.
*/
/*
* Facet API overview page
* Facets overview page
*/
#facetapi-groups-list {
.facets-groups-list {
margin-bottom: 2em;
}
#facetapi-groups-list tr {
.facets-groups-list tr {
border-bottom: none;
}
#facetapi-groups-list tr.facet-source {
.facets-groups-list tr.facet-source {
border-top: 1px solid #e6e4df;
}
#facetapi-groups-list tr.facet:last-of-type {
.facets-groups-list tr.facet:last-of-type {
border-bottom: 1px solid #e6e4df;
}
#facetapi-groups-list tr.facet-source .facetapi-type,
#facetapi-groups-list tr.facet-source .search-api-title {
.facets-groups-list tr.facet-source .facets-type,
.facets-groups-list tr.facet-source .search-api-title {
font-weight: bold;
}
#facetapi-groups-list tr.facet .facetapi-type {
.facets-groups-list tr.facet .facets-type {
padding-left: 3em;
}
/*
* Facet API Display page
* Facets Display page
*/
.facetapi-processor-settings-sorting {
.facets-processor-settings-sorting {
margin-bottom: -7px;
margin-left: 20px;
margin-top: -16px;
}
.facetapi-processor-settings-facet {
.facets-processor-settings-facet {
margin-left: 20px;
margin-bottom: 20px;
}
\ No newline at end of file
}