You are here

PollViewForm.php in Poll 8

Namespace

Drupal\poll\Form

File

src/Form/PollViewForm.php
View source
<?php

namespace Drupal\poll\Form;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\poll\PollInterface;
use Symfony\Component\HttpFoundation\Request;

/**
 * Displays banned IP addresses.
 */
class PollViewForm extends FormBase implements BaseFormIdInterface {

  /**
   * The Poll of the form.
   *
   * @var \Drupal\poll\PollInterface
   */
  protected $poll;

  /**
   * {@inheritdoc}
   */
  public function getBaseFormId() {
    return 'poll_view_form';
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'poll_view_form_' . $this->poll
      ->id();
  }

  /**
   * Set the Poll of this form.
   *
   * @param \Drupal\poll\PollInterface $poll
   *   The poll that will be set in the form.
   */
  public function setPoll(PollInterface $poll) {
    $this->poll = $poll;
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL, $view_mode = 'full') {

    // Add the poll to the form.
    $form['poll']['#type'] = 'value';
    $form['poll']['#value'] = $this->poll;
    $form['#view_mode'] = $view_mode;
    if ($this
      ->showResults($this->poll, $form_state)) {

      // Check if the user already voted. The form is still being built but
      // the Vote button won't be added so the submit callbacks will not be
      // called. Directly check for the request method and use the raw user
      // input.
      if ($request
        ->isMethod('POST') && $this->poll
        ->hasUserVoted()) {
        $input = $form_state
          ->getUserInput();
        if (isset($input['op']) && $input['op'] == $this
          ->t('Vote')) {

          // If this happened, then the form submission was likely a cached page.
          // Force a session for this user so he can see the results.
          $this
            ->messenger()
            ->addError($this
            ->t('Your vote for this poll has already been submitted.'));
          $_SESSION['poll_vote'][$this->poll
            ->id()] = FALSE;
        }
      }
      $form['results'] = $this
        ->showPollResults($this->poll, $view_mode);

      // For all view modes except full and block (as block displays it as the
      // block title), display the question.
      if ($view_mode != 'full' && $view_mode != 'block') {
        $form['results']['#show_question'] = TRUE;
      }
    }
    else {
      $options = $this->poll
        ->getOptions();
      if ($options) {
        $form['choice'] = array(
          '#type' => 'radios',
          '#title' => t('Choices'),
          '#title_display' => 'invisible',
          '#options' => $options,
        );
      }
      $form['#theme'] = 'poll_vote';
      $form['#entity'] = $this->poll;
      $form['#action'] = $this->poll
        ->toUrl()
        ->setOption('query', \Drupal::destination()
        ->getAsArray())
        ->toString();

      // Set a flag to hide results which will be removed if we want to view
      // results when the form is rebuilt.
      $form_state
        ->set('show_results', FALSE);

      // For all view modes except full and block (as block displays it as the
      // block title), display the question.
      if ($view_mode != 'full' && $view_mode != 'block') {
        $form['#show_question'] = TRUE;
      }
    }
    $form['actions'] = $this
      ->actions($form, $form_state, $this->poll);
    $form['#cache'] = array(
      'tags' => $this->poll
        ->getCacheTags(),
    );
    return $form;
  }

  /**
   * Ajax callback to replace the poll form.
   */
  public function ajaxReplaceForm(array $form, FormStateInterface $form_state) {

    // Embed status message into the form.
    $form = [
      'messages' => [
        '#type' => 'status_messages',
      ],
    ] + $form;

    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = \Drupal::service('renderer');

    // Render the form.
    $output = $renderer
      ->renderRoot($form);
    $response = new AjaxResponse();
    $response
      ->setAttachments($form['#attached']);

    // Replace the form completely and return it.
    return $response
      ->addCommand(new ReplaceCommand('.poll-view-form-' . $this->poll
      ->id(), $output));
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
  }
  public function showResults(PollInterface $poll, FormStateInterface $form_state) {
    $account = $this
      ->currentUser();
    switch (TRUE) {

      // The "View results" button, when available, has been clicked.
      case $form_state
        ->get('show_results'):
        return TRUE;

      // The poll is closed.
      case $poll
        ->isClosed():
        return TRUE;

      // Anonymous user is trying to view a poll they aren't allowed to vote in.
      case $account
        ->isAnonymous() && !$poll
        ->getAnonymousVoteAllow():
        return TRUE;

      // The user has already voted.
      case $poll
        ->hasUserVoted():
        return TRUE;
      default:
        return FALSE;
    }
  }
  protected function actions(array $form, FormStateInterface $form_state, $poll) {
    $actions = [];

    // Default ajax behavior, use the poll URL for faster submission, this
    // requires that we explicitly provide the ajax_form query argument too in
    // the separate options key, as that replaces all options of the Url object.
    $ajax = [
      'callback' => '::ajaxReplaceForm',
      'url' => $this->poll
        ->toUrl(),
      'options' => [
        'query' => [
          FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
          'view_mode' => $form['#view_mode'],
        ],
      ],
    ];
    if ($this
      ->showResults($poll, $form_state)) {

      // Allow user to cancel their vote.
      if ($this
        ->isCancelAllowed($poll)) {
        $actions['#type'] = 'actions';
        $actions['cancel']['#type'] = 'submit';
        $actions['cancel']['#button_type'] = 'primary';
        $actions['cancel']['#value'] = t('Cancel vote');
        $actions['cancel']['#submit'] = array(
          '::cancel',
        );
        $actions['cancel']['#ajax'] = $ajax;
        $actions['cancel']['#weight'] = '0';
      }
      if (!$poll
        ->hasUserVoted() && $poll
        ->isOpen() && $poll
        ->getAnonymousVoteAllow()) {
        $actions['#type'] = 'actions';
        $actions['back']['#type'] = 'submit';
        $actions['back']['#button_type'] = 'primary';
        $actions['back']['#value'] = t('View poll');
        $actions['back']['#submit'] = array(
          '::back',
        );
        $actions['back']['#ajax'] = $ajax;
        $actions['back']['#weight'] = '0';
      }
    }
    else {
      $actions['#type'] = 'actions';
      $actions['vote']['#type'] = 'submit';
      $actions['vote']['#button_type'] = 'primary';
      $actions['vote']['#value'] = t('Vote');
      $actions['vote']['#validate'] = array(
        '::validateVote',
      );
      $actions['vote']['#submit'] = array(
        '::save',
      );
      $actions['vote']['#ajax'] = $ajax;
      $actions['vote']['#weight'] = '0';

      // View results before voting.
      if ($poll->result_vote_allow->value || $this
        ->currentUser()
        ->hasPermission('view poll results')) {
        $actions['result']['#type'] = 'submit';
        $actions['result']['#button_type'] = 'primary';
        $actions['result']['#value'] = t('View results');
        $actions['result']['#submit'] = array(
          '::result',
        );
        $actions['result']['#ajax'] = $ajax;
        $actions['result']['#weight'] = '1';
      }
    }
    return $actions;
  }

  /**
   * Display a themed poll results.
   *
   * @param \Drupal\poll\PollInterface $poll
   *   The poll entity.
   * @param bool $block
   *   (optional) TRUE if a poll should be displayed in a block. Defaults to
   *   FALSE.
   *
   * @return array $output
   */
  function showPollResults(PollInterface $poll, $view_mode = 'default', $block = FALSE) {

    // Ensure that a page that shows poll results can not be cached.
    \Drupal::service('page_cache_kill_switch')
      ->trigger();
    $total_votes = 0;
    foreach ($poll
      ->getVotes() as $vote) {
      $total_votes += $vote;
    }
    $options = $poll
      ->getOptions();
    $poll_results = array();
    foreach ($poll
      ->getVotes() as $pid => $vote) {
      $percentage = round($vote * 100 / max($total_votes, 1));
      $display_votes = !$block ? ' (' . \Drupal::translation()
        ->formatPlural($vote, '1 vote', '@count votes') . ')' : '';
      $poll_results[] = array(
        '#theme' => 'poll_meter',
        '#choice' => $options[$pid],
        '#display_value' => t('@percentage%', array(
          '@percentage' => $percentage,
        )) . $display_votes,
        '#min' => 0,
        '#max' => $total_votes,
        '#value' => $vote,
        '#percentage' => $percentage,
        '#attributes' => array(
          'class' => array(
            'bar',
          ),
        ),
        '#poll' => $poll,
      );
    }

    /** @var \Drupal\poll\PollVoteStorageInterface $vote_storage */
    $vote_storage = \Drupal::service('poll_vote.storage');
    $user_vote = $vote_storage
      ->getUserVote($poll);
    $output = array(
      '#theme' => 'poll_results',
      '#raw_question' => $poll
        ->label(),
      '#results' => $poll_results,
      '#votes' => $total_votes,
      '#block' => $block,
      '#pid' => $poll
        ->id(),
      '#poll' => $poll,
      '#view_mode' => $view_mode,
      '#vote' => isset($user_vote['chid']) ? $user_vote['chid'] : NULL,
    );
    return $output;
  }

  /**
   * Cancel vote submit function.
   *
   * @param array $form
   *   The previous form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function cancel(array $form, FormStateInterface $form_state) {

    /** @var \Drupal\poll\PollVoteStorageInterface $vote_storage */
    $vote_storage = \Drupal::service('poll_vote.storage');
    $vote_storage
      ->cancelVote($this->poll, $this
      ->currentUser());
    \Drupal::logger('poll')
      ->notice('%user\'s vote in Poll #%poll deleted.', array(
      '%user' => $this
        ->currentUser()
        ->id(),
      '%poll' => $this->poll
        ->id(),
    ));
    $this
      ->messenger()
      ->addMessage($this
      ->t('Your vote was cancelled.'));

    // In case of an ajax submission, trigger a form rebuild so that we can
    // return an updated form through the ajax callback.
    if ($this
      ->getRequest()->query
      ->get('ajax_form')) {
      $form_state
        ->setRebuild(TRUE);
    }
  }

  /**
   * View vote results submit function.
   *
   * @param array $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   */
  public function result(array $form, FormStateInterface $form_state) {
    $form_state
      ->set('show_results', TRUE);
    $form_state
      ->setRebuild(TRUE);
  }

  /**
   * Back to poll view submit function.
   *
   * @param array $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   */
  public function back(array $form, FormStateInterface $form_state) {
    $form_state
      ->set('show_results', FALSE);
    $form_state
      ->setRebuild(TRUE);
  }

  /**
   * Save a user's vote submit function.
   *
   * @param array $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   */
  public function save(array $form, FormStateInterface $form_state) {
    $options = array();
    $options['chid'] = $form_state
      ->getValue('choice');
    $options['uid'] = $this
      ->currentUser()
      ->id();
    $options['pid'] = $form_state
      ->getValue('poll')
      ->id();
    $options['hostname'] = \Drupal::request()
      ->getClientIp();
    $options['timestamp'] = \Drupal::time()
      ->getRequestTime();

    // Save vote.

    /** @var \Drupal\poll\PollVoteStorage $vote_storage */
    $vote_storage = \Drupal::service('poll_vote.storage');
    $vote_storage
      ->saveVote($options);
    $this
      ->messenger()
      ->addMessage($this
      ->t('Your vote has been recorded.'));
    if ($this
      ->currentUser()
      ->isAnonymous()) {

      // The vote is recorded so the user gets the result view instead of the
      // voting form when viewing the poll. Saving a value in $_SESSION has the
      // convenient side effect of preventing the user from hitting the page
      // cache. When anonymous voting is allowed, the page cache should only
      // contain the voting form, not the results.
      $_SESSION['poll_vote'][$form_state
        ->getValue('poll')
        ->id()] = $form_state
        ->getValue('choice');
    }

    // In case of an ajax submission, trigger a form rebuild so that we can
    // return an updated form through the ajax callback.
    if ($this
      ->getRequest()->query
      ->get('ajax_form')) {
      $form_state
        ->setRebuild(TRUE);
    }

    // No explicit redirect, so that we stay on the current page, which might
    // be the poll form or another page that is displaying this poll, for
    // example as a block.
  }

  /**
   * Validates the vote action.
   *
   * @param array $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   */
  public function validateVote(array &$form, FormStateInterface $form_state) {
    if (!$form_state
      ->hasValue('choice')) {
      $form_state
        ->setErrorByName('choice', $this
        ->t('Your vote could not be recorded because you did not select any of the choices.'));
    }
  }

  /**
   * Checks if the current user is allowed to cancel on the given poll.
   *
   * @param \Drupal\poll\PollInterface $poll
   *
   * @return bool
   *   TRUE if the user can cancel.
   */
  protected function isCancelAllowed(PollInterface $poll) {

    // Allow access if the user has voted.
    return $poll
      ->hasUserVoted() && $poll
      ->getCancelVoteAllow() && $this
      ->currentUser()
      ->hasPermission('cancel own vote') && (\Drupal::currentUser()
      ->isAuthenticated() || !empty($_SESSION['poll_vote'][$poll
      ->id()])) && $poll
      ->isOpen();
  }

}

Classes

Namesort descending Description
PollViewForm Displays banned IP addresses.