diff --git a/search_api.links.action.yml b/search_api.links.action.yml index 7d85b50..5b99f87 100644 --- a/search_api.links.action.yml +++ b/search_api.links.action.yml @@ -13,8 +13,3 @@ search_api.execute_tasks: title: 'Execute pending tasks' appears_on: - search_api.overview -entity.search_api_index.add_fields: - route_name: entity.search_api_index.add_fields - title: 'Add fields' - appears_on: - - entity.search_api_index.fields diff --git a/src/Form/IndexAddFieldsForm.php b/src/Form/IndexAddFieldsForm.php index 724409f..9d76f8b 100644 --- a/src/Form/IndexAddFieldsForm.php +++ b/src/Form/IndexAddFieldsForm.php @@ -2,9 +2,11 @@ namespace Drupal\search_api\Form; +use Drupal\Component\Render\PlainTextOutput; use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Utility\Html; use Drupal\Core\Datetime\DateFormatterInterface; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityForm; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface; @@ -13,7 +15,6 @@ use Drupal\Core\TypedData\ComplexDataDefinitionInterface; use Drupal\Core\TypedData\DataReferenceDefinitionInterface; use Drupal\Core\Url; -use Drupal\search_api\Datasource\DatasourceInterface; use Drupal\search_api\Processor\ConfigurablePropertyInterface; use Drupal\search_api\Processor\ProcessorPropertyInterface; use Drupal\search_api\Utility\FieldsHelperInterface; @@ -108,7 +109,6 @@ public static function create(ContainerInterface $container) { return new static($entity_type_manager, $fields_helper, $renderer, $date_formatter, $parameters); } - /** * Retrieves a single page request parameter. * @@ -136,19 +136,43 @@ public function buildForm(array $form, FormStateInterface $form_state) { $this->checkEntityEditable($form, $index); + // Set title. Strip html to accommodate modal title. $args['%index'] = $index->label(); - $form['#title'] = $this->t('Add fields to index %index', $args); + $form['#title'] = PlainTextOutput::renderFromHtml($this->t('Add fields to index %index', $args)); - $form['properties'] = array( - '#theme' => 'search_api_form_item_list', - ); - $datasources = array( - '' => NULL, + // Main container. + $form['index-fields'] = [ + '#type' => 'container', + '#attributes' => ['id' => 'index-fields'], + ]; + + // We store the datasources we need to show in the form to accommodate the + // AJAX magic. + // Create hidden form field to store datasources. + $form['datasources'] = array( + '#type' => 'hidden', ); - $datasources += $this->entity->getDatasources(); - foreach ($datasources as $datasource) { - $form['properties'][] = $this->getDatasourceListItem($datasource); + + $datasources = $form_state->getValue('datasources'); + // First set some defaults when nothing is set. + if (empty($datasources)) { + // Get the datasources to add. + // Start with the general fields. + $datasources = ['general' => NULL]; + // Add entity datasource. + $entity_datasources = $this->entity->getDatasources(); + // For each datasource add id to form value. + foreach ($entity_datasources as $entity_datasource) { + $datasource_id = $entity_datasource->getPluginId(); + $datasources[$datasource_id] = NULL; + } } + $form['datasources']['#value'] = $datasources; + + // Create form elements for each of the datasources we want to show. + // This is a recursive function, allowing to show the nested elements. + $containers = $this->createFieldContainers($form['datasources']['#value']); + $form['index-fields']['properties'] = $containers; $form['actions'] = $this->actionsElement($form, $form_state); @@ -179,62 +203,95 @@ public function buildForm(array $form, FormStateInterface $form_state) { } /** - * Creates a list item for one datasource. + * Creates form elements for each field. * - * @param \Drupal\search_api\Datasource\DatasourceInterface|null $datasource - * The datasource, or NULL for general properties. + * @param array $fields + * A nested array which defines what fields to show. This info is + * stored in the form. * * @return array - * A render array representing the given datasource and, possibly, its - * attached properties. + * Form elements for the given fields.. */ - protected function getDatasourceListItem(DatasourceInterface $datasource = NULL) { - $item = array( - '#type' => 'container', - '#attributes' => array( - 'class' => array('container-inline'), - ), - ); - - $active = FALSE; - $datasource_id = $datasource ? $datasource->getPluginId() : ''; - $active_datasource = $this->getParameter('datasource'); - if (isset($active_datasource)) { - $active = $active_datasource == $datasource_id; - } - + protected function createFieldContainers(array $fields) { + $items = array(); $url = $this->entity->toUrl('add-fields'); - if ($active) { - $expand_link = array( - '#type' => 'link', - '#title' => '(-) ', - '#url' => $url, - ); - } - else { - $url->setOption('query', array('datasource' => $datasource_id)); - $expand_link = array( - '#type' => 'link', - '#title' => '(+) ', - '#url' => $url, - ); - } - $item['expand_link'] = $expand_link; - $label = $datasource ? Html::escape($datasource->label()) : $this->t('General'); - $item['label']['#markup'] = $label; + foreach (array_keys($fields) as $datasource_id) { + // Each element get' a container so we can add children for sub elements. + $item = [ + '#type' => 'container', + '#attributes' => [ + 'class' => ['container-inline'], + '#attributes' => ['id' => '' . $datasource_id], + ], + '#field-name' => $datasource_id, + ]; + + $item['test'] = [ + '#markup' => 'Show ' . $datasource_id . ' in ' . 'index-field-' . $datasource_id . ' container', + ]; + + $active = FALSE; + $active_datasource = $this->getParameter('datasource'); + if (isset($active_datasource)) { + $active = $active_datasource == $datasource_id; + } - if ($active) { - $properties = $this->entity->getPropertyDefinitions($datasource_id ?: NULL); + $properties = $this->entity->getPropertyDefinitions($datasource_id == 'general' ? NULL : $datasource_id); if ($properties) { - $active_property_path = $this->getParameter('property_path', ''); + $active_property_path = NULL; + if ($active) { + $active_property_path = $this->getParameter('property_path', ''); + } $base_url = clone $url; $base_url->setOption('query', array('datasource' => $datasource_id)); $item['properties'] = $this->getPropertiesList($properties, $active_property_path, $base_url); } + $items[] = $item; + + // Add child elements. + if (is_array($fields[$datasource_id])) { + $new_things = $this->createFieldContainers($fields[$datasource_id]); + foreach ($new_things as $new_thing) { + $items[] = $new_thing; + } + } } - return $item; + return $items; + } + + /** + * Handles form submissions for expanding a datasource. + */ + public function expandDatasourceFormSubmit(&$form, FormStateInterface &$form_state) { + // Find the triggering field. + $button = $form_state->getTriggeringElement(); + // Go one level up in the form, to the widgets container. + $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1)); + $field_name = $element['#field-name']; + + // Add new items to form state. + $datasources = $form_state->getValue('datasources'); + + $properties = $this->entity->getPropertyDefinitions($field_name == 'general' ? NULL : $field_name); + + foreach (array_keys($properties) as $property) { + $datasources[$field_name][$property] = NULL; + } + + $form_state->setValue('datasources', $datasources); + $form_state->setRebuild(); + } + + /** + * Returns new datasource structure. + */ + public function expandDatasourceFormAjax(array $form, FormStateInterface &$form_state) { + $form['messages']['status'] = [ + '#type' => 'status_messages', + ]; + return $form; } /** @@ -329,8 +386,8 @@ protected function getPropertiesList(array $properties, $active_property_path, U continue; } - $nested_list = array(); - $expand_link = array(); + $nested_list = FALSE; + $expand_link = FALSE; if ($nested_properties) { if ($key == $active_item) { $link_url = clone $base_url; @@ -340,6 +397,11 @@ protected function getPropertiesList(array $properties, $active_property_path, U '#type' => 'link', '#title' => '(-) ', '#url' => $link_url, + '#attributes' => [ + 'class' => [ + 'use-ajax', + ], + ], ); $nested_list = $this->getPropertiesList($nested_properties, $active_property_path, $base_url, $this_path, $label_prefix . $label . ' ยป '); @@ -352,6 +414,11 @@ protected function getPropertiesList(array $properties, $active_property_path, U '#type' => 'link', '#title' => '(+) ', '#url' => $link_url, + '#attributes' => [ + 'class' => [ + 'use-ajax', + ], + ], ); } } @@ -392,22 +459,6 @@ protected function getPropertiesList(array $properties, $active_property_path, U } /** - * {@inheritdoc} - */ - protected function actions(array $form, FormStateInterface $form_state) { - return array( - 'done' => array( - '#type' => 'link', - '#title' => $this->t('Done'), - '#url' => $this->entity->toUrl('fields'), - '#attributes' => array( - 'class' => array('button'), - ), - ), - ); - } - - /** * Form submission handler for adding a new field to the index. * * @param array $form diff --git a/src/Form/IndexFieldsForm.php b/src/Form/IndexFieldsForm.php index 1035e49..2fd0f33 100644 --- a/src/Form/IndexFieldsForm.php +++ b/src/Form/IndexFieldsForm.php @@ -2,6 +2,7 @@ namespace Drupal\search_api\Form; +use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\Html; use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Entity\EntityForm; @@ -119,6 +120,25 @@ public function buildForm(array $form, FormStateInterface $form_state) { $form['#title'] = $this->t('Manage fields for search index %label', array('%label' => $index->label())); $form['#tree'] = TRUE; + $form['add-field'] = [ + '#type' => 'link', + '#title' => $this->t('Add fields'), + '#url' => Url::fromRoute('entity.search_api_index.add_fields', ['search_api_index' => $index->id()]), + '#attributes' => [ + 'class' => [ + 'use-ajax', + 'button', + 'button-action', + 'button--primary', + 'button--small', + ], + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'width' => 700, + ]), + ], + ]; + $form['description']['#markup'] = $this->t('

The data type of a field determines how it can be used for searching and filtering. The boost is used to give additional weight to certain fields, for example titles or tags.

For information about the data types available for indexing, see the data types table at the bottom of the page.

', array('@url' => '#search-api-data-types-table')); if ($index->hasValidServer()) { $arguments = array(