You are here

public function QuizQuestionAnsweringForm::buildForm in Quiz 6.x

Same name and namespace in other branches
  1. 8.6 src/Form/QuizQuestionAnsweringForm.php \Drupal\quiz\Form\QuizQuestionAnsweringForm::buildForm()
  2. 8.5 src/Form/QuizQuestionAnsweringForm.php \Drupal\quiz\Form\QuizQuestionAnsweringForm::buildForm()

Get the form to show to the quiz taker.

_state

Parameters

array $form:

array $questions: A list of question nodes to get answers from.

int $result_id: The result ID for this attempt.

Return value

array An renderable FAPI array.

Overrides FormInterface::buildForm

File

src/Form/QuizQuestionAnsweringForm.php, line 28

Class

QuizQuestionAnsweringForm
The form used to deliver questions to users and capture their responses.

Namespace

Drupal\quiz\Form

Code

public function buildForm(array $form, FormStateInterface $form_state) : array {

  /* @var $questions \Drupal\quiz\Entity\QuizQuestion[] */
  $questions = $form_state
    ->getBuildInfo()['args'][0];

  /* @var $quiz_session \Drupal\quiz\Services\QuizSessionInterface */
  $quiz_session = \Drupal::service('quiz.session');

  /* @var $quiz_result \Drupal\quiz\Entity\QuizResult */
  $quiz_result = $form_state
    ->getBuildInfo()['args'][1];
  $quiz = \Drupal::entityTypeManager()
    ->getStorage('quiz')
    ->loadRevision($quiz_result
    ->get('vid')
    ->getString());

  // Store quiz and result in the form.
  $form['#quiz'] = $quiz;
  $form['#quiz_result'] = $quiz_result;
  if (!is_array($questions)) {

    // One single question (or page?)
    if ($questions
      ->bundle() == 'page') {
      foreach ($quiz_result
        ->getLayout() as $qra) {
        if ($qra
          ->get('question_id')
          ->getString() == $questions
          ->id()) {

          // Found the page in the layout.
          // Add the page as the first "question".
          $questions = [
            $questions,
          ];
          foreach ($quiz_result
            ->getLayout() as $qra2) {

            // Get all page children.
            if ($qra2->qqr_pid == $qra->qqr_id) {

              // This question belongs in the requested page.
              $questions[] = $qra2
                ->getQuizQuestion();
            }
          }
          break;
        }
      }
      unset($qra, $qra2);
    }
    else {

      // Make this a page of 1 question.
      $questions = [
        $questions
          ->id() => $questions,
      ];
    }
  }
  $form['#attributes']['class'] = [
    'answering-form',
  ];
  $form['#tree'] = TRUE;

  // Mark this as the current question.
  $quiz_result
    ->setQuestion(reset($questions)
    ->getResponse($quiz_result)
    ->get('number')->value);
  foreach ($questions as $question) {
    $class = Html::getClass('quiz-question-' . $question
      ->bundle());

    // Element for a single question.
    // @todo Instead of doing this load I think we can refer back to the layout.
    $qra = $question
      ->getResponse($quiz_result);
    $element = $question
      ->getAnsweringForm($form_state, $qra);

    // Render the question using the "question" view mode.
    $build = \Drupal::entityTypeManager()
      ->getViewBuilder('quiz_question')
      ->view($question, 'question');
    $header_markup = NULL;
    if (!$qra
      ->get('display_number')
      ->isEmpty()) {
      $header_markup = [
        '#markup' => "<h2>" . t("Question @question", [
          '@question' => $qra
            ->get('display_number')
            ->getString(),
        ]) . "</h2>",
      ];
    }
    $form['question'][$question
      ->id()] = [
      '#attributes' => [
        'class' => [
          $class,
        ],
      ],
      '#type' => 'container',
      'header' => $header_markup,
      'question' => $build,
      'answer' => $element,
    ];
    $form['question'][$question
      ->id()]['answer']['#quiz_result_answer'] = $qra;
    $blank_and_change = $qra
      ->get('is_skipped')
      ->getString() && $quiz
      ->get('allow_change_blank')
      ->getString();
    if (!$quiz
      ->get('allow_change')
      ->getString() && !$qra
      ->get('answer_timestamp')
      ->isEmpty()) {
      if ($blank_and_change) {

        // Allow it.
      }
      else {

        // This question was already answered, or answering blank question is
        // disabled.
        $form['question'][$question
          ->id()]['#disabled'] = TRUE;
        if (empty($form_state
          ->getUserInput())) {

          // Only show message if not submitting.
          \Drupal::messenger()
            ->addWarning(t('Changing answers is disabled.'));
        }
      }
    }
    if ($quiz
      ->get('mark_doubtful')
      ->getString() && $question
      ->isQuestion()) {
      $form['question'][$question
        ->id()]['is_doubtful'] = [
        '#type' => 'checkbox',
        '#title' => t('Doubtful?'),
        '#default_value' => $qra
          ->get('is_doubtful')
          ->getString(),
      ];
    }
  }
  $is_last = $quiz
    ->isLastQuestion();
  $form['navigation']['#type'] = 'actions';
  $form['navigation']['submit_hidden'] = [
    '#weight' => -9999,
    '#type' => 'submit',
    '#value' => $is_last ? t('Finish') : t('Next'),
    '#attributes' => [
      'style' => 'display: none',
    ],
  ];
  if ($quiz
    ->get('backwards_navigation')
    ->getString() && $quiz_session
    ->getCurrentQuestion($quiz) != 1) {

    // Backwards navigation enabled, and we are looking at not the first
    // question. @todo detect when on the first page.
    $form['navigation']['back'] = [
      '#weight' => 10,
      '#type' => 'submit',
      '#value' => t('Back'),
      '#submit' => [
        '::submitBack',
      ],
      '#limit_validation_errors' => [],
    ];
    if ($is_last) {
      $form['navigation']['#last'] = TRUE;
      $form['navigation']['last_text'] = [
        '#weight' => 0,
        '#markup' => '<p><em>' . t('This is the last question. Press Finish to deliver your answers') . '</em></p>',
      ];
    }
  }
  $form['navigation']['submit'] = [
    '#weight' => 30,
    '#type' => 'submit',
    '#value' => $is_last ? t('Finish') : t('Next'),
    '#ajax' => [],
  ];
  if ($is_last && $quiz
    ->get('backwards_navigation')
    ->getString() && !$quiz
    ->get('repeat_until_correct')
    ->getString()) {

    // Display a confirmation dialogue if this is the last question and a user
    // is able to navigate backwards but not forced to answer correctly.
    $form['#attributes']['class'][] = 'quiz-answer-confirm';
    $form['#attributes']['data-confirm-message'] = t("By proceeding you won't be able to go back and edit your answers.");
    $form['#attached']['library'][] = 'quiz/confirm';
  }
  if ($quiz
    ->get('allow_skipping')
    ->getString()) {
    $form['navigation']['skip'] = [
      '#weight' => 20,
      '#type' => 'submit',
      '#value' => $is_last ? t('Leave blank and finish') : t('Leave blank'),
      '#access' => $question
        ->isGraded(),
      '#submit' => [
        '::submitBlank',
      ],
      '#limit_validation_errors' => [],
    ];
  }
  return $form;
}