closedquestion-8.x-3.x-dev/src/Question/CqQuestionSequence.php

src/Question/CqQuestionSequence.php
<?php

namespace Drupal\closedquestion\Question;

use Drupal\closedquestion\Entity\ClosedQuestionInterface;
use Drupal\closedquestion\Question\Mapping\CqFeedback;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;

/**
 * Class CqQuestionSequence.
 *
 * Implementation of the Sequence question type. A Sequence is a set of
 * questions that behave as being one multi-part question.
 *
 * @package Drupal\closedquestion\Question
 */
class CqQuestionSequence extends CqQuestionAbstract implements CqListenerQuestionInterface {

  /**
   * The list of questions in the sequence.
   *
   * @var \Drupal\closedquestion\Question\CqQuestionInterface[]
   */
  private $subQuestions = array();

  /**
   * The index of the question in the sequence.
   *
   * @var int
   */
  private $currentIndex = 0;

  /**
   * The bit of html with the links to the previous and next questions.
   *
   * @var string
   */
  private $backNext = '';

  /**
   * Toggle to see if the back and next links have been generated yet.
   *
   * @var bool
   */
  private $backNextFixed = FALSE;

  /**
   * Constructs a Select&Order question object.
   *
   * @param CqUserAnswerInterface $userAnswer
   *   The CqUserAnswerInterface to use for storing the student's answer.
   * @param \Drupal\closedquestion\Entity\ClosedQuestionInterface $closedQuestion
   *   Closed question entity.
   */
  public function __construct(CqUserAnswerInterface $userAnswer, ClosedQuestionInterface $closedQuestion) {
    parent::__construct();
    $this->userAnswer = $userAnswer;
    $this->closedQuestion = $closedQuestion;
  }

  /**
   * Implements CqQuestionAbstract::getFeedbackItems()
   */
  public function getFeedbackItems() {
    $feedback = $this->subQuestions[$this->currentIndex]->getFeedbackItems();
    return array_merge($feedback, $this->fireGetExtraFeedbackItems($this, $this->getTries()));
  }

  /**
   * Overrides CqQuestionAbstract::getFeedbackFormItem()
   */
  public function getFeedbackFormItem() {
    return $this->subQuestions[$this->currentIndex]->getFeedbackFormItem();
  }

  /**
   * Implements CqQuestionAbstract::submitAnswer()
   */
  public function submitAnswer($form, FormStateInterface $form_state) {
    $this->subQuestions[$this->currentIndex]->submitAnswer($form, $form_state);
    $this->isCorrect(TRUE);
    $this->userAnswer->store();
    $this->fixBackNextLinks();
  }

  /**
   * Implements CqQuestionAbstract::checkCorrect()
   */
  public function checkCorrect() {
    $retval = TRUE;
    $tries = 0;
    foreach ($this->subQuestions as $question) {
      $tries += max(0, $question->onceCorrect() - 1);
      if (!$question->isCorrect()) {
        $retval = FALSE;
      }
    }
    $this->userAnswer->setTries($tries);
    return $retval;
  }

  /**
   * Implements CqQuestionAbstract::getOutput()
   */
  public function getOutput() {
    $this->initialise();
    if (!$this->backNextFixed) {
      $this->fixBackNextLinks();
    }
    $form['backnext'] = array(
      '#type' => 'item',
      '#markup' => $this->backNext,
      '#prefix' => '<h2>',
      '#suffix' => '</h2>',
    );
    if (isset($this->subQuestions[$this->currentIndex])) {
      $form['question'] = array(
        'cur_question' => $this->subQuestions[$this->currentIndex]->getOutput(),
        '#prefix' => $this->prefix,
        '#suffix' => $this->postfix,
      );
    }
    return $form;
  }

  /**
   * Overrides CqQuestionAbstract::loadXml()
   */
  public function loadXml(\DOMNode $dom) {
    parent::loadXml($dom);


    $i = 0;
    foreach ($dom->childNodes as $child) {
      if (mb_strtolower($child->nodeName) == 'question') {
        $uac = new CqUserAnswerClient($this->userAnswer, $i);

        $question = _closedquestion_closedquestion_question_factory($child->getAttribute('type'), $uac, $this->closedQuestion);
        if ($question) {
          $question->loadXML($child);
          $this->subQuestions[] = & $question;
          $question->addListener($this);
          unset($question);
          $i++;
        }
        unset($uac);
      }
    }

    $answer = $this->userAnswer->getAnswer();
    if (isset($_REQUEST['CqQS_' . $this->closedQuestion->id() . '_Step'])) {
      $this->currentIndex = (int) $_REQUEST['CqQS_' . $this->closedQuestion->id() . '_Step'];
      $answer['ci'] = $this->currentIndex;
      $this->userAnswer->setAnswer($answer);
      $this->userAnswer->store();
    }
    else {
      $this->currentIndex = isset($answer['ci']) ? (int) $answer['ci'] : 0;
    }
    if ($this->currentIndex < 0 || $this->currentIndex >= count($this->subQuestions)) {
      $this->currentIndex = 0;
    }
  }

  /**
   * Implements CqQuestionAbstract::getForm()
   */
  public function getForm($formState) {
    $retval = $this->subQuestions[$this->currentIndex]->getForm($formState);
    return $retval;
  }

  /**
   * Overrides CqQuestionAbstract::reset()
   */
  public function reset() {
    $this->userAnswer->reset();
  }

  /**
   * Implements CqQuestionAbstract::getAllText()
   */
  public function getAllText() {
    $this->initialise();
    $retval = array();
    $nr = 0;
    foreach ($this->subQuestions as $question) {
      $nr++;
      $retval['items'][] = array(
        'title' => t('Sub-Question @nr', array('@nr' => $nr)),
        'question' => $question->getAllText(),
      );
    }
    $retval['#theme'] = 'closedquestion_question_sequence_text';
    return $retval;
  }

  /**
   * Creates the back and next links.
   */
  private function fixBackNextLinks() {
    $path = $this->usedPath;
    if ($path) {
      /** @var \Drupal\Core\Url|bool $url */
      $url = \Drupal::service('path-validator')->getUrlIfValid($path);
      $routeName = $url ? $url->getRouteName() : '<current>';
    }
    else {
      $routeName = '<current>';
    }
    $prevUrl = '';
    $nextUrl = '';
    if ($this->currentIndex > 0 && $this->subQuestions[$this->currentIndex - 1]->isCorrect()) {
      $prevUrl = Url::fromRoute(
        $routeName, [], [
          'query' => [
            'CqQS_' . $this->closedQuestion->id() . '_Step' => $this->currentIndex - 1,
          ],
        ]
      )->toString();
    }
    if ($this->currentIndex + 1 < count($this->subQuestions) && $this->subQuestions[$this->currentIndex]->isCorrect()) {
      $nextUrl = Url::fromRoute(
        $routeName, [], [
          'query' => [
            'CqQS_' . $this->closedQuestion->id() . '_Step' => $this->currentIndex + 1,
          ],
        ]
      )->toString();
    }
    $variables = array(
      '#theme' => 'closedquestion_sequence_back_next',
      '#index' => $this->currentIndex,
      '#total' => count($this->subQuestions),
      '#prev_url' => $prevUrl,
      '#next_url' => $nextUrl,
    );
    $this->backNext = $this->renderer->render($variables)->__toString();
    $this->backNextFixed = TRUE;
  }

  /**
   * Implements CqQuestionAbstract::FirstSolutionFound()
   */
  public function firstSolutionFound($tries) {
    // A sub question is answered correctly, but we don't need that info.
  }

  /**
   * Implements CqQuestionAbstract::getExtraFeedbackItems()
   */
  public function getExtraFeedbackItems($caller, $tries) {
    $retval = [];
    if (!empty($this->backNext)) {
      $fbItem = new CqFeedback();
      $fbItem->initWithValues($this->backNext, 0, 9999);
      $retval[] = $fbItem;
    }
    return array_merge($retval, $this->fireGetExtraFeedbackItems($this, $tries));
  }

}

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

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