You are here

function quiz_quiz_access in Quiz 6.x

Same name and namespace in other branches
  1. 8.6 quiz.module \quiz_quiz_access()
  2. 8.5 quiz.module \quiz_quiz_access()
  3. 7.5 quiz.module \quiz_quiz_access()

Implements hook_quiz_access().

Can a user take this quiz?

File

./quiz.module, line 271
Contains quiz.module

Code

function quiz_quiz_access(EntityInterface $entity, $operation, AccountInterface $account) {
  if ($operation == 'take') {
    $user_is_admin = $entity
      ->access('update');

    // Make sure this is available.
    if (!$entity
      ->get('quiz_date')
      ->isEmpty()) {

      // Compare current GMT time to the open and close dates (which should still
      // be in GMT time).
      $request_time = Drupal::time()
        ->getRequestTime();
      $quiz_date = $entity
        ->get('quiz_date')
        ->get(0)
        ->getValue();
      $quiz_open = $request_time >= strtotime($quiz_date['value']);
      $quiz_closed = $request_time >= strtotime($quiz_date['end_value']);
      if (!$quiz_open || $quiz_closed) {
        if ($user_is_admin) {
          $hooks['admin_ignore_date'] = [
            'success' => TRUE,
            'message' => (string) t('You are marked as an administrator or owner for this @quiz. While you can take this @quiz, the open/close times prohibit other users from taking this @quiz.', [
              '@quiz' => QuizUtil::getQuizName(),
            ]),
          ];
        }
        else {
          if ($quiz_closed) {
            return AccessResultForbidden::forbidden((string) t('This @quiz is closed.', [
              '@quiz' => QuizUtil::getQuizName(),
            ]));
          }
          if (!$quiz_open) {
            return AccessResultForbidden::forbidden((string) t('This @quiz is not yet open.', [
              '@quiz' => QuizUtil::getQuizName(),
            ]));
          }
        }
      }
    }

    // Check to see if this user is allowed to take the quiz again:
    if ($entity
      ->get('takes')
      ->getString() > 0) {
      $taken = Drupal::database()
        ->query('SELECT COUNT(*) AS takes FROM {quiz_result} WHERE uid = :uid AND qid = :qid', [
        ':uid' => $account
          ->id(),
        ':qid' => $entity
          ->id(),
      ])
        ->fetchField();
      $t = Drupal::translation();
      $allowed_times = $t
        ->formatPlural($entity
        ->get('takes')
        ->getString(), '1 time', '@count times');
      $taken_times = $t
        ->formatPlural($taken, '1 time', '@count times');

      // The user has already taken this quiz.
      if ($taken) {
        if (FALSE && $user_is_admin) {
          $hooks['owner_limit'] = [
            'success' => TRUE,
            'message' => (string) t('You have taken this @quiz already. You are marked as an owner or administrator for this quiz, so you can take this quiz as many times as you would like.', [
              '@quiz' => QuizUtil::getQuizName(),
            ]),
          ];
        }
        elseif ($taken >= $entity
          ->get('takes')
          ->getString()) {

          /* @var $quiz_session QuizSessionInterface */
          $quiz_session = Drupal::service('quiz.session');
          if ($entity->allow_resume->value && $entity
            ->getResumeableResult($account)) {

            // Quiz is resumable and there is an active attempt, so we should
            // allow them to finish it as it won't be creating a new attempt. This
            // is the blocker, so we do nothing here. The resume handles in the
            // take function.
          }
          elseif (!$quiz_session
            ->isTakingQuiz($entity)) {

            // If result is in session, don't check the attempt limit. @todo would
            // be to split up "take" into something like "start" and "continue" an
            // attempt.
            $hooks['attempt_limit'] = [
              'success' => FALSE,
              'message' => (string) t('You have already taken this @quiz @really. You may not take it again.', [
                '@quiz' => QuizUtil::getQuizName(),
                '@really' => $taken_times,
              ]),
            ];
          }
        }
        elseif ($entity->show_attempt_stats->value) {
          $hooks['attempt_limit'] = [
            'success' => TRUE,
            'message' => (string) t("You can only take this @quiz @allowed. You have taken it @really.", [
              '@quiz' => QuizUtil::getQuizName(),
              '@allowed' => $allowed_times,
              '@really' => $taken_times,
            ]),
            'weight' => -10,
          ];
        }
      }
    }

    // Check to see if the user is registered, and user alredy passed this quiz.
    if ($entity->show_passed->value && $account
      ->id() && $entity
      ->isPassed($account)) {
      $hooks['already_passed'] = [
        'success' => TRUE,
        'message' => (string) t('You have already passed this @quiz.', [
          '@quiz' => QuizUtil::getQuizName(),
        ]),
        'weight' => 10,
      ];
    }
    if (!empty($hooks)) {
      foreach ($hooks as $hook) {
        if (!$hook['success']) {
          return AccessResultForbidden::forbidden($hook['message'], [
            '@quiz' => QuizUtil::getQuizName(),
          ]);
        }
      }
    }
    if (!empty($hooks)) {
      foreach ($hooks as $hook) {
        if ($hook['success']) {
          if (Drupal::routeMatch()
            ->getRouteName() == 'entity.quiz.canonical') {

            // Only display if we are viewing the quiz.
            Drupal::messenger()
              ->addWarning($hook['message']);
          }
          return [
            AccessResultAllowed::allowed($hook['message'], [
              '@quiz' => QuizUtil::getQuizName(),
            ]),
          ];
        }
      }
    }

    // Check permission and node access.
    if (!Drupal::currentUser()
      ->hasPermission('access quiz') || !$entity
      ->access('view')) {
      return [
        AccessResultForbidden::forbidden((string) t('You are not allowed to take this @quiz.', [
          '@quiz' => QuizUtil::getQuizName(),
        ])),
      ];
    }
  }
}