flexible_views-2.0.1/src/Plugin/views/filter/ColumnSelector.php

src/Plugin/views/filter/ColumnSelector.php
<?php

namespace Drupal\flexible_views\Plugin\views\filter;

use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Utility\TableSort;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Filter class which allows to show and hide columns.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("column_selector")
 */
class ColumnSelector extends FilterPluginBase {

  /**
   * {@inheritdoc}
   */
  public $no_operator = TRUE;

  /**
   * Module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandler
   */
  public $moduleHandler;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->moduleHandler = $container->get('module_handler');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
    parent::init($view, $display, $options);
    $this->valueTitle = t('Visible Column Selector.');
  }

  /**
   * {@inheritdoc}
   */
  public function defineOptions() {
    $options = parent::defineOptions();

    $options['wrap_with_details'] = ['default' => TRUE];
    $options['exposed'] = ['default' => TRUE];
    $options['expose']['contains']['label'] = ['default' => 'Column Options'];
    $options["expose"]["contains"]["identifier"] = ['default' => 'columns_selector'];

    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    parent::buildOptionsForm($form, $form_state);

    $form['wrap_with_details'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Wrap with details element'),
      '#description' => $this->t('Wrap the column selector section with a details element, so it can be collapsed.'),
      '#default_value' => $this->options['wrap_with_details'],
    ];

    // Disable the expose options - this filter needs to be exposed to work.
    $form["expose_button"]["checkbox"]["checkbox"]['#attributes']['disabled'] = 'disabled';

    // Disable the 'expose multiple' checkbox - it does not make sense.
    unset($form["expose"]["multiple"]);

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function adminSummary() {
    return $this->value;
  }

  /**
   * {@inheritdoc}
   */
  public function query() {
    $this->ensureMyTable();
  }

  /**
   * Maps query input from field_machine_name to label.
   *
   * @param array $query
   *   The inout query.
   * @param array $fields
   *   View Fields.
   *
   * @return array
   *   The mapped query.
   */
  public static function mapSelectedColumnsSubmit(array $query, array $fields) {
    $query_mapped = [];

    foreach ($query as $field_name) {
      if (isset($fields[$field_name])) {
        $query_mapped[$field_name] = $fields[$field_name]->options['label'];
      }
    }

    return $query_mapped;
  }

  /**
   * {@inheritdoc}
   */
  public function buildExposedForm(&$form, FormStateInterface $form_state) {
    parent::buildExposedForm($form, $form_state);

    if (empty($form_state->getStorage()['view'])) {
      return;
    }

    $form['#attached']['library'][] = 'flexible_views/column_selector';

    $fields = $form_state->getStorage()['view']->style_plugin->options['info'] ?? [];
    $fields_info = $form_state->getStorage()['view']->field;
    $query = TableSort::getQueryParameters(\Drupal::request());
    $view = $this->view;

    // Get the selected columns from the query of from the session storage.
    $display_id = ($view->display_handler->isDefaulted('filters')) ? 'default' : $view->current_display;
    $query_selected_columns = [];
    if (isset($query['selected_columns_submit_order']) && !empty($query['selected_columns_submit_order'])) {
      $query_selected_columns = $this::mapSelectedColumnsSubmit(Json::decode($query['selected_columns_submit_order']), $fields_info);
    }
    elseif (isset($_SESSION['views'][$view->storage->id()][$display_id]['selected_columns_submit_order']) && !empty($_SESSION['views'][$view->storage->id()][$display_id]['selected_columns_submit_order'])) {
      $query_selected_columns = $this::mapSelectedColumnsSubmit(Json::decode($_SESSION['views'][$view->storage->id()][$display_id]['selected_columns_submit_order']), $fields_info);
    }

    // If we have a query['selected_columns_submit_order'], use this for the
    // columns, otherwise render the default_visible columns and populate the
    // form elements.
    $options = [];
    $options_default_visible = [];

    // Integration with views_field_permissions module.
    $views_field_permissions = $this->moduleHandler->moduleExists('views_field_permissions');
    if ($views_field_permissions) {
      if (!$view->result) {
        $view->result = TRUE;
      }
      views_field_permissions_views_post_execute($view);
    }

    foreach ($fields_info as $field_name => $field_info) {
      // We dont want to process the node_bulk_form and the operations.
      if (!in_array($field_name, [
        'operations',
        'node_bulk_form',
        'views_bulk_operations_bulk_form',
      ])) {
        if (!$view->display_handler->getHandler('field', $field_name)) {
          $field_info->options['exclude'] = TRUE;
        }

        if (!$field_info->options['exclude']) {
          $options[$field_name] = $field_info->options['label'];
        }
        // Defaults.
        if (isset($fields[$field_name]['default_visible']) && $fields[$field_name]['default_visible']) {
          $options_default_visible[$field_name] = $field_info->options['label'];
        }
      }
    }

    $form['flexible_tables_fieldset'] = [
      '#type' => $this->options['wrap_with_details'] ? 'details' : 'container',
      '#open' => FALSE,
      '#title' => $this->options['expose']['label'],
      '#attributes' => [
        'style' => 'float: none;clear: both;',
      ],
    ];

    $form['flexible_tables_fieldset']['available_columns'] = [
      '#type' => 'select',
      '#title' => t('Available Columns'),
      '#options' => count($query_selected_columns) > 0 ? array_diff($options, $query_selected_columns) : array_diff($options, $options_default_visible),
      '#size' => count($options),
      '#multiple' => TRUE,
      '#attributes' => [
        'id' => 'flexible-table-available-columns',
      ],
      '#prefix' => $this->options['wrap_with_details'] ? '' : '<div class="details-wrapper fake-detail">',
      '#chosen' => FALSE,
    ];

    $move_left_right_buttons = <<<EOT
<div class="form-item move-buttons">
  <div class="move-right">→</div>
  <div class="move-left">←</div>
</div>
EOT;

    $form['flexible_tables_fieldset']['move_left_right_buttons']['#markup'] = $move_left_right_buttons;

    $form['flexible_tables_fieldset']['selected_columns'] = [
      '#type' => 'select',
      '#title' => t('Selected Columns'),
      '#options' => count($query_selected_columns) > 0 ? $query_selected_columns : $options_default_visible,
      '#size' => count($options),
      '#multiple' => TRUE,
      '#attributes' => [
        'id' => 'flexible-table-selected-columns',
      ],
      '#chosen' => FALSE,
    ];

    $move_top_down_buttons = <<<EOT
<div class="form-item move-buttons">
  <div class="move-top">↑</div>
  <div class="move-down">↓</div>
</div>
EOT;

    $form['flexible_tables_fieldset']['move_top_down_buttons']['#markup'] = $move_top_down_buttons;

    $form['flexible_tables_fieldset']['selected_columns_submit_order'] = [
      '#type' => 'textfield',
      '#default_value' => FALSE,
      '#size' => 60,
      '#maxlength' => 524288,
      '#required' => FALSE,
      '#attributes' => [
        'id' => 'flexible-table-selected-columns-submit-order',
      ],
      '#suffix' => $this->options['wrap_with_details'] ? '' : '</div>',
    ];

    // Add our submit routine to process.
    $form['#validate'][] = [$this, 'exposedFormValidate'];
  }

  /**
   * Reset the selected_columns to an empty array, we dont need this.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function exposedFormValidate(array &$form, FormStateInterface $form_state) {
    // Clear the values on available_columns and selected_columns, we dont need
    // these, and it's also confusing for the user.
    $input = $form_state->getUserInput();

    foreach ($input as $key => $item) {
      if (in_array($key, ['available_columns', 'selected_columns'])) {
        unset($input[$key]);
      }
    }

    $form_state->setUserInput($input);

    // Clear the validation error on the selected_columns field. We supply an
    // empty array [] as options, but the user can select something and this
    // results in a validation error.
    if (isset($form['filters']) && isset($form['filters']['children'])) {
      $parents = $this->getKey($form['filters'], ['filters']);
      if ($form_state->getError(NestedArray::getValue($form, $parents))) {
        $form_errors = $form_state->getErrors();
        // Clear the form errors.
        $form_state->clearErrors();

        // Remove the form error.
        unset($form_errors['selected_columns']);

        // Now loop through and re-apply the remaining form error messages.
        foreach ($form_errors as $name => $error_message) {
          $form_state->setErrorByName($name, $error_message);
        }
      }
      return;
    }

    // Do the same for a simpler structure.
    if ($form_state->getError($form['flexible_tables_fieldset']['selected_columns'])) {
      $form_errors = $form_state->getErrors();

      // Clear the form errors.
      $form_state->clearErrors();

      // Remove the field_mobile form error.
      unset($form_errors['selected_columns']);

      // Now loop through and re-apply the remaining form error messages.
      foreach ($form_errors as $name => $error_message) {
        $form_state->setErrorByName($name, $error_message);
      }
    }
  }

  /**
   * Helper method for getting form errors.
   *
   * @param array $subjectArray
   *   The form array to check.
   * @param array $chain
   *   The selector chain.
   *
   * @see https://www.drupal.org/project/flexible_views/issues/3247363
   *
   * @return array
   *   The filled selector chain.
   */
  public function getKey(array $subjectArray, array $chain) {
    $result = NestedArray::getValue($subjectArray, ['children']);
    foreach ($result as $key => $values) {
      if (strpos($key, 'container') !== FALSE) {
        array_push($chain, 'children', $key);
        break;
      }
      if ($key == 'flexible_tables_fieldset') {
        return $chain;
      }
    }
    return is_array($values) ? $this->getKey($values, $chain) : [];
  }

  /**
   * {@inheritdoc}
   */
  public function storeExposedInput($input, $status) {
    if (empty($this->options['exposed']) || empty($this->options['expose']['identifier'])) {
      return TRUE;
    }

    if (empty($this->options['expose']['remember'])) {
      return;
    }

    // Check if we store exposed value for current user.
    $user = \Drupal::currentUser();
    $allowed_rids = empty($this->options['expose']['remember_roles']) ? [] : array_filter($this->options['expose']['remember_roles']);
    $intersect_rids = array_intersect(array_keys($allowed_rids), $user->getRoles());
    if (empty($intersect_rids)) {
      return;
    }

    // Figure out which display id is responsible for the filters, so we
    // know where to look for session stored values.
    $display_id = ($this->view->display_handler->isDefaulted('filters')) ? 'default' : $this->view->current_display;

    // False means that we got a setting that means to recurse ourselves,
    // so we should erase whatever happened to be there.
    if (!$status && isset($_SESSION['views'][$this->view->storage->id()][$display_id])) {
      $session = &$_SESSION['views'][$this->view->storage->id()][$display_id];

      if (isset($session[$this->options['expose']['identifier']])) {
        unset($session[$this->options['expose']['identifier']]);
        // We use two form_elements different from the identifier.
        unset($session['selected_columns_submit_order']);
      }
    }

    if ($status) {
      if (!isset($_SESSION['views'][$this->view->storage->id()][$display_id])) {
        $_SESSION['views'][$this->view->storage->id()][$display_id] = [];
      }

      $session = &$_SESSION['views'][$this->view->storage->id()][$display_id];

      if (isset($input[$this->options['expose']['identifier']])) {
        $session[$this->options['expose']['identifier']] = $input[$this->options['expose']['identifier']];
      }

      $session['selected_columns_submit_order'] = $input['selected_columns_submit_order'];
    }
  }

  /**
   * {@inheritdoc}
   */
  public function canGroup() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function acceptExposedInput($input) {
    return TRUE;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc