You are here

public function ViewsBulkOperationsBulkForm::viewsForm in Views Bulk Operations (VBO) 8.3

Same name and namespace in other branches
  1. 8 src/Plugin/views/field/ViewsBulkOperationsBulkForm.php \Drupal\views_bulk_operations\Plugin\views\field\ViewsBulkOperationsBulkForm::viewsForm()
  2. 8.2 src/Plugin/views/field/ViewsBulkOperationsBulkForm.php \Drupal\views_bulk_operations\Plugin\views\field\ViewsBulkOperationsBulkForm::viewsForm()
  3. 4.0.x src/Plugin/views/field/ViewsBulkOperationsBulkForm.php \Drupal\views_bulk_operations\Plugin\views\field\ViewsBulkOperationsBulkForm::viewsForm()

Form constructor for the bulk form.

Parameters

array $form: An associative array containing the structure of the form.

\Drupal\Core\Form\FormStateInterface $form_state: The current state of the form.

File

src/Plugin/views/field/ViewsBulkOperationsBulkForm.php, line 613

Class

ViewsBulkOperationsBulkForm
Defines the Views Bulk Operations field plugin.

Namespace

Drupal\views_bulk_operations\Plugin\views\field

Code

public function viewsForm(array &$form, FormStateInterface $form_state) {

  // Make sure we do not accidentally cache this form.
  // @todo Evaluate this again in https://www.drupal.org/node/2503009.
  $form['#cache']['max-age'] = 0;

  // Add VBO class to the form.
  $form['#attributes']['class'][] = 'vbo-view-form';

  // Add VBO front UI and tableselect libraries for table display style.
  if ($this->view->style_plugin instanceof Table) {
    $form['#attached']['library'][] = 'core/drupal.tableselect';
    $this->view->style_plugin->options['views_bulk_operations_enabled'] = TRUE;
  }
  $form['#attached']['library'][] = 'views_bulk_operations/frontUi';

  // Only add the bulk form options and buttons if
  // there are results and any actions are available.
  $action_options = $this
    ->getBulkOptions();
  if (!empty($this->view->result) && !empty($action_options)) {

    // Calculate bulk form keys and get labels for all rows.
    $bulk_form_keys = [];
    $entity_labels = [];
    $base_field = $this->view->storage
      ->get('base_field');
    foreach ($this->view->result as $row_index => $row) {
      if ($entity = $this
        ->getEntity($row)) {
        $bulk_form_keys[$row_index] = self::calculateEntityBulkFormKey($entity, $row->{$base_field});
        $entity_labels[$row_index] = $entity
          ->label();
      }
    }

    // Update and fetch tempstore data to be available from this point
    // as it's needed for proper functioning of further logic.
    // Update tempstore data with bulk form keys only when the form is
    // displayed, but not when the form is being built before submission
    // (data is subject to change - new entities added or deleted after
    // the form display). TODO: consider using $form_state->set() instead.
    if (empty($form_state
      ->getUserInput()['op'])) {
      $this
        ->updateTempstoreData($bulk_form_keys);
    }
    else {
      $this
        ->updateTempstoreData();
    }
    $form[$this->options['id']]['#tree'] = TRUE;

    // Get pager data if available.
    if (!empty($this->view->pager) && method_exists($this->view->pager, 'hasMoreRecords')) {
      $pagerData = [
        'current' => $this->view->pager
          ->getCurrentPage(),
        'more' => $this->view->pager
          ->hasMoreRecords(),
      ];
    }

    // Render checkboxes for all rows.
    $page_selected = [];
    foreach ($bulk_form_keys as $row_index => $bulk_form_key) {
      $checked = isset($this->tempStoreData['list'][$bulk_form_key]);
      if (!empty($this->tempStoreData['exclude_mode'])) {
        $checked = !$checked;
      }
      if ($checked) {
        $page_selected[] = $bulk_form_key;
      }
      $form[$this->options['id']][$row_index] = [
        '#type' => 'checkbox',
        '#title' => $entity_labels[$row_index],
        '#title_display' => 'invisible',
        '#default_value' => $checked,
        '#return_value' => $bulk_form_key,
      ];

      // We should use #value instead of #default_value to always apply
      // the plugin's own saved checkbox state (data being changed after form
      // submission results in wrong values applied by the FAPI),
      // however - automated tests fail if it's done this way.
      // We have to apply values conditionally for tests to pass.
      if (isset($element['#value']) && $element['#value'] != $checked) {
        $element['#value'] = $checked;
      }
    }

    // Ensure a consistent container for filters/operations
    // in the view header.
    $form['header'] = [
      '#type' => 'container',
      '#weight' => -100,
    ];

    // Build the bulk operations action widget for the header.
    // Allow themes to apply .container-inline on this separate container.
    $form['header'][$this->options['id']] = [
      '#type' => 'container',
      '#attributes' => [
        'id' => 'vbo-action-form-wrapper',
      ],
    ];

    // Display actions buttons or selector.
    if ($this->options['buttons']) {
      unset($form['actions']['submit']);
      foreach ($action_options as $id => $label) {
        $form['actions'][$id] = [
          '#type' => 'submit',
          '#value' => $label,
        ];
      }
    }
    else {

      // Replace the form submit button label.
      $form['actions']['submit']['#value'] = $this
        ->t('Apply to selected items');
      $form['header'][$this->options['id']]['action'] = [
        '#type' => 'select',
        '#title' => $this->options['action_title'],
        '#options' => [
          '' => $this
            ->t('-- Select action --'),
        ] + $action_options,
      ];
    }

    // Add AJAX functionality if actions are configurable through this form.
    if (empty($this->options['form_step'])) {
      $form['header'][$this->options['id']]['action']['#ajax'] = [
        'callback' => [
          __CLASS__,
          'viewsFormAjax',
        ],
        'wrapper' => 'vbo-action-configuration-wrapper',
      ];
      $form['header'][$this->options['id']]['configuration'] = [
        '#type' => 'container',
        '#attributes' => [
          'id' => 'vbo-action-configuration-wrapper',
        ],
      ];
      $action_id = $form_state
        ->getValue('action');
      if (!empty($action_id)) {
        $action = $this->actions[$action_id];
        if ($this
          ->isActionConfigurable($action)) {
          $actionObject = $this->actionManager
            ->createInstance($action_id);
          $form['header'][$this->options['id']]['configuration'] += $actionObject
            ->buildConfigurationForm($form['header'][$this->options['id']]['configuration'], $form_state);
          $form['header'][$this->options['id']]['configuration']['#config_included'] = TRUE;
        }
      }
    }

    // Selection info: displayed if exposed filters are set and selection
    // is not cleared when they change or "select all" element display
    // conditions are met. Also displayed by default if VBO field has such
    // configuration set.
    if ($this->options['force_selection_info']) {
      $display_select_all = TRUE;
    }
    else {
      $display_select_all = !$this->options['clear_on_exposed'] && !empty($this->view
        ->getExposedInput()) || isset($pagerData) && ($pagerData['more'] || $pagerData['current'] > 0);
    }
    if ($display_select_all) {
      $count = empty($this->tempStoreData['exclude_mode']) ? count($this->tempStoreData['list']) : $this->tempStoreData['total_results'] - count($this->tempStoreData['list']);
      $form['header'][$this->options['id']]['multipage'] = [
        '#type' => 'details',
        '#open' => FALSE,
        '#title' => $this
          ->formatPlural($count, 'Selected 1 item in this view', 'Selected @count items in this view'),
        '#attributes' => [
          // Add view_id and display_id to be available for
          // js multipage selector functionality.
          'data-view-id' => $this->tempStoreData['view_id'],
          'data-display-id' => $this->tempStoreData['display_id'],
          'class' => [
            'vbo-multipage-selector',
          ],
        ],
      ];

      // Get selection info elements.
      $form['header'][$this->options['id']]['multipage']['list'] = $this
        ->getMultipageList($this->tempStoreData);
      $form['header'][$this->options['id']]['multipage']['clear'] = [
        '#type' => 'submit',
        '#value' => $this
          ->t('Clear'),
        '#submit' => [
          [
            $this,
            'clearSelection',
          ],
        ],
        '#limit_validation_errors' => [],
      ];
    }

    // Select all results checkbox. Always display on non-table displays.
    if ($display_select_all || !$this->view->style_plugin instanceof Table) {
      $form['header'][$this->options['id']]['select_all'] = [
        '#type' => 'checkbox',
        '#title' => $this
          ->t('Select / deselect all results in this view (all pages, @count total)', [
          '@count' => $this->tempStoreData['total_results'],
        ]),
        '#attributes' => [
          'class' => [
            'vbo-select-all',
          ],
        ],
        '#default_value' => !empty($this->tempStoreData['exclude_mode']),
      ];
    }

    // Duplicate the form actions into the action container in the header.
    $form['header'][$this->options['id']]['actions'] = $form['actions'];
  }
  else {

    // Remove the default actions build array.
    unset($form['actions']);
  }
}