You are here

class CourseObjectAccessControlHandler in Course 8.3

Same name and namespace in other branches
  1. 8.2 src/Access/CourseObjectAccessControlHandler.php \Drupal\course\Access\CourseObjectAccessControlHandler
  2. 3.x src/Access/CourseObjectAccessControlHandler.php \Drupal\course\Access\CourseObjectAccessControlHandler

Access controller for the Course entity.

Hierarchy

Expanded class hierarchy of CourseObjectAccessControlHandler

File

src/Access/CourseObjectAccessControlHandler.php, line 14

Namespace

Drupal\course\Access
View source
class CourseObjectAccessControlHandler extends Drupal\entity\UncacheableEntityAccessControlHandler {

  /**
   * Access functionality for course objects.
   *
   * Possible values for $op are 'see', 'view', 'take'.
   *
   * "see" means see it in a course outline. For example, a conditional survey
   * should not be seen in the course outline. A quiz at the end of the course,
   * should show up, but the user must not be able to take it.
   *
   * "view" means view and interact with the object, but nothing would be
   * recorded. For example, accessing a quiz but not being able to submit
   * responses.
   *
   * "take" means interact with the object in a way that records data.
   *
   * {@inheritdoc}
   */
  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
    if (!in_array($operation, array(
      'see',
      'take',
      'view',
    ), TRUE)) {

      // This isn't a supported course access operation, so defer to the parent.
      return parent::checkAccess($entity, $operation, $account);
    }
    $access = FALSE;
    if (!$account) {
      $account = Drupal::currentUser();
    }
    if (!$entity
      ->getOption('enabled') || $entity
      ->getOption('hidden')) {

      // Object is disabled or hidden so it should never be visible.
      return AccessResult::forbidden()
        ->resetCacheContexts();
    }
    switch ($operation) {
      case 'see':

        // User can see this object in the outline.
        $access = TRUE;
        break;
      case 'take':
      case 'view':
        if ($account
          ->isAnonymous()) {

          // Not logged in. Should never be accessible.
          return AccessResult::forbidden()
            ->resetCacheContexts();
        }

        // Stock access: check for completion of previous object.
        // Get a copy of the course, so we can run setActive() without changing
        // the global course.
        $course = clone $entity
          ->getCourse();
        $course
          ->setActive($entity
          ->getId());
        $courseObjects = $course
          ->getObjects();

        // Deny object access to non-enrolled users or users who cannot take
        // this course.
        $result = $entity
          ->getCourse()
          ->access('take', $account, TRUE);
        if (!$entity
          ->getCourse()
          ->isEnrolled($account) || $result
          ->isForbidden()) {
          return AccessResult::forbidden()
            ->resetCacheContexts();
        }
        else {
          if (reset($courseObjects)
            ->getId() == $entity
            ->getId()) {

            // User is enrolled. Grant access if first object.
            $access = TRUE;
          }
        }
        if (!$course
          ->getPrev()) {

          // There wasn't a previous object.
          $access = TRUE;
        }
        elseif (!$course
          ->getPrev()
          ->isRequired() || $course
          ->getPrev()
          ->isSkippable()) {

          // Previous step was not required, or was skippable.
          $access = TRUE;

          // But we need to see if at least one required step was completed (or the start of the course).
          $objects = array_reverse($course
            ->getObjects());
          $check = FALSE;
          foreach ($objects as $object) {
            if (!$object
              ->getOption('enabled')) {

              // Do not check this object.
              // Note that hidden objects are still counted when doing
              // fulfillment checks.
              continue;
            }
            if ($check) {
              if ($object
                ->isRequired() && !$object
                ->isSkippable()) {

                // Object is required.
                if (!$object
                  ->getFulfillment($account)
                  ->isComplete()) {

                  // Found a required object that was not complete.
                  $access = FALSE;
                  break;
                }
                else {

                  // The last required object was completed.
                  $access = TRUE;
                  break;
                }
              }
            }
            if ($object
              ->getId() == $entity
              ->getId()) {

              // We found the object we are trying to check access on.
              // Now we want to go backwards.
              $check = 1;
            }
          }
        }
        elseif ($course
          ->getPrev()
          ->getFulfillment($account)
          ->isComplete()) {

          // If last object was complete, and we are on the current object,
          // grant access.
          $access = TRUE;
        }
    }

    // Plugin access.
    // @todo D8 fix

    /* @var $pluginManager CourseObjectAccessPluginManager */
    $pluginManager = Drupal::service('plugin.manager.course.object.access');
    $plugins = $pluginManager
      ->getDefinitions();
    foreach ($plugins as $key => $plugin) {
      $accessPlugin = $pluginManager
        ->createInstance($key);
      $accessPlugin
        ->setCourseObject($entity);
      $accessPlugin
        ->setType($key);

      // Run access check.
      $ret = $accessPlugin
        ->{$operation}($account);
      if ($ret === FALSE) {

        // If previous access was granted, revoke it.
        $access = $ret;
      }
    }
    return $access ? AccessResult::allowed() : AccessResult::forbidden()
      ->resetCacheContexts();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CourseObjectAccessControlHandler::checkAccess protected function Access functionality for course objects. Overrides EntityAccessControlHandlerBase::checkAccess
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
EntityAccessControlHandler::$accessCache protected property Stores calculated access check results.
EntityAccessControlHandler::$entityType protected property Information about the entity type.
EntityAccessControlHandler::$entityTypeId protected property The entity type ID of the access control handler instance.
EntityAccessControlHandler::$viewLabelOperation protected property Allows to grant access to just the labels. 5
EntityAccessControlHandler::access public function Checks access to an operation on a given entity or entity translation. Overrides EntityAccessControlHandlerInterface::access 1
EntityAccessControlHandler::checkFieldAccess protected function Default field access as determined by this access control handler. 4
EntityAccessControlHandler::createAccess public function Checks access to create an entity. Overrides EntityAccessControlHandlerInterface::createAccess 1
EntityAccessControlHandler::fieldAccess public function Checks access to an operation on a given entity field. Overrides EntityAccessControlHandlerInterface::fieldAccess
EntityAccessControlHandler::getCache protected function Tries to retrieve a previously cached access value from the static cache.
EntityAccessControlHandler::prepareUser protected function Loads the current account object, if it does not exist yet.
EntityAccessControlHandler::processAccessHookResults protected function We grant access to the entity if both of these conditions are met:
EntityAccessControlHandler::resetCache public function Clears all cached access checks. Overrides EntityAccessControlHandlerInterface::resetCache
EntityAccessControlHandler::setCache protected function Statically caches whether the given user has access.
EntityAccessControlHandlerBase::checkCreateAccess protected function Performs create access checks. Overrides EntityAccessControlHandler::checkCreateAccess
EntityAccessControlHandlerBase::checkEntityPermissions protected function Checks the entity operation and bundle permissions.
EntityHandlerBase::$moduleHandler protected property The module handler to invoke hooks on. 2
EntityHandlerBase::moduleHandler protected function Gets the module handler. 2
EntityHandlerBase::setModuleHandler public function Sets the module handler for this handler.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UncacheableEntityAccessControlHandler::checkEntityOwnerPermissions protected function Checks the entity operation and bundle permissions, with owners. Overrides EntityAccessControlHandlerBase::checkEntityOwnerPermissions
UncacheableEntityAccessControlHandler::__construct public function Constructs an access control handler instance. Overrides EntityAccessControlHandler::__construct