You are here

abstract class CourseObject in Course 8.2

Same name in this branch
  1. 8.2 src/Annotation/CourseObject.php \Drupal\course\Annotation\CourseObject
  2. 8.2 src/Entity/CourseObject.php \Drupal\course\Entity\CourseObject
Same name and namespace in other branches
  1. 8.3 src/Entity/CourseObject.php \Drupal\course\Entity\CourseObject
  2. 3.x src/Entity/CourseObject.php \Drupal\course\Entity\CourseObject

Parent abstract base class of all course objects.

Represents a course object in the database.

Also holds a fulfillment record if a user is given.

Plugin annotation


@ContentEntityType(
  id = "course_object",
  label = @Translation("Course object"),
  label_collection = @Translation("Course objects"),
  label_singular = @Translation("Course object"),
  label_plural = @Translation("Course objects"),
  label_count = @PluralTranslation(
    singular = "@count course object",
    plural = "@count course object fulfillments",
  ),
  admin_permission = "administer course",
  permission_granularity = "bundle",
  bundle_label = @Translation("Course object type"),
  bundle_entity_type = "course_object_type",
  base_table = "course_object",
  fieldable = TRUE,
  field_ui_base_route = "entity.course_object_type.edit_form",
  show_revision_ui = TRUE,
  revision_table = "course_object_revision",
  revision_data_table = "course_object_field_revision",
  entity_keys = {
    "id" = "coid",
    "uid" = "uid",
    "bundle" = "object_type",
    "label" = "title"
  },
  revision_metadata_keys = {
    "revision_user" = "revision_uid",
    "revision_created" = "revision_timestamp",
    "revision_log_message" = "revision_log"
  },
  handlers =  {
    "access" = "Drupal\course\Access\CourseObjectAccessControlHandler",
    "permission_provider" = "Drupal\entity\UncacheableEntityPermissionProvider",
    "storage" = "Drupal\course\Storage\CourseObjectStorage",
    "view_builder" = "Drupal\course\View\CourseObjectViewBuilder",
    "views_data" = "Drupal\views\EntityViewsData",
    "form" = {
      "default" = "Drupal\Core\Entity\ContentEntityForm",
      "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
    },
  }
)

Hierarchy

Expanded class hierarchy of CourseObject

19 files declare their use of CourseObject
course.module in ./course.module
course.module Core functionality for Courses.
Course.php in src/Entity/Course.php
CourseObjectAccessGrade.php in src/Plugin/course/CourseObjectAccess/CourseObjectAccessGrade.php
CourseObjectBookTestCase.php in modules/course_book/tests/src/Functional/CourseObjectBookTestCase.php
CourseObjectBroken.php in src/Object/CourseObjectBroken.php

... See full list

File

src/Entity/CourseObject.php, line 72

Namespace

Drupal\course\Entity
View source
abstract class CourseObject extends CourseHandler {
  protected $accessMessages = array();

  /**
   * Override navigation links.
   *
   * @return array
   *   An array of navigation links. Keyed values will override matching items
   *   in Course::getNavigation().
   */
  public function overrideNavigation() {
    return array();
  }

  /**
   * Specify whether fulfillment uses asynchronous polling.
   *
   * @return bool
   *   Whether this object uses polling. Defaults to FALSE.
   */
  public function hasPolling() {
    return FALSE;
  }

  /**
   * Overrides a course outline list item.
   *
   * @param array $item
   *   A course outline list item. The structure mirrors an array element from
   *   the $items param from theme_item_list().
   */
  public function overrideOutlineListItem(&$item) {
  }
  public function isActive() {
    return $this
      ->getCourse()
      ->current()
      ->getId() == $this
      ->getId();
  }

  /**
   * Define configuration elements and their defaults.
   *
   * Extended classes should call parent::optionsDefinition first to get the
   * parent's configuration.
   */
  public function optionsDefinition() {
    $defaults = parent::optionsDefinition();
    $defaults += array(
      'uniqid' => NULL,
      'nid' => NULL,
      'title' => NULL,
      'enabled' => 1,
      'hidden' => 0,
      'required' => 1,
      'skippable' => 0,
      'delete' => 0,
      'delete_instance' => 0,
      'grade_include' => 0,
      'instance' => NULL,
      'plugins' => array(),
      'duration' => NULL,
      'skippable' => 0,
      'use_node_title' => 1,
    );
    return $defaults;
  }

  /**
   * Default options form for all course objects.
   */
  public function optionsForm(&$form, &$form_state) {
    parent::optionsForm($form, $form_state);
    $config = $this
      ->getOptions();
    $form['header']['#markup'] = t("<h2>Settings for %t</h2>", array(
      '%t' => $this
        ->getTitle(),
    ));
    $form['uniqid'] = array(
      '#type' => 'value',
      '#value' => $this
        ->getId(),
    );
    $form['course_tabs']['#type'] = 'vertical_tabs';
    $form['course_tabs']['#default_tab'] = 'edit-title';
    $form['title'] = array(
      '#title' => t('Title & description'),
      '#type' => 'details',
      '#group' => 'course_tabs',
      '#weight' => 0,
    );
    $form['outline'] = array(
      '#type' => 'details',
      '#title' => t('Settings'),
      '#group' => 'course_tabs',
      '#weight' => 1,
    );
    $form['plugins']['access'] = array(
      '#type' => 'details',
      '#title' => 'Access',
      '#group' => 'course_tabs',
      '#weight' => 4,
    );
    $form['delete'] = array(
      '#type' => 'details',
      '#title' => 'Delete',
      '#group' => 'course_tabs',
      '#weight' => 5,
    );
    $form['title']['title'] = array(
      '#title' => t('Title'),
      '#type' => 'textfield',
      '#description' => t('The title of this course object as it will appear to users in the course outline.'),
      '#size' => 100,
      '#default_value' => $config['title'],
      '#group' => 'description',
      '#required' => TRUE,
    );
    $form['outline']['enabled'] = array(
      '#title' => t('Enabled'),
      '#type' => 'checkbox',
      '#description' => t('Enabled course objects will become part of the course. Uncheck this box if you are not using this course object.'),
      '#default_value' => $config['enabled'],
    );
    $form['outline']['hidden'] = array(
      '#title' => t('Visible in outline'),
      '#type' => 'checkbox',
      '#description' => t('Objects that are not visible will not be seen by the learner. Uncheck this box for course objects that you do not want the learner to see.'),
      '#default_value' => !$config['hidden'],
      '#group' => 'course',
    );
    $form['outline']['required'] = array(
      '#title' => t('Completion required'),
      '#type' => 'checkbox',
      '#description' => t('Users must complete required objects. Uncheck this box if this is an optional course object.'),
      '#default_value' => $config['required'],
    );
    $form['outline']['skippable'] = array(
      '#title' => t('Skippable'),
      '#type' => 'checkbox',
      '#default_value' => $config['skippable'],
      '#states' => array(
        'visible' => array(
          '#edit-required' => array(
            'checked' => TRUE,
          ),
        ),
      ),
      '#description' => t('Users can proceed past this object but it will still be required for course completion.'),
    );

    // Delete object
    $form['delete']['delete_button'] = array(
      '#value' => t('Delete'),
      '#weight' => 999,
      '#type' => 'submit',
      '#submit' => array(
        array(
          $this,
          'setDelete',
        ),
        array(
          $this,
          'optionsSubmit',
        ),
      ),
      '#limit_validation_errors' => array(),
    );

    // Only allow deletion of existing instances.
    if (!empty($config['instance'])) {
      $form['delete']['delete_instance'] = array(
        '#title' => t('Also delete the referenced content.'),
        '#type' => 'checkbox',
        '#default_value' => $config['delete_instance'],
        '#stats' => array(
          'visible' => array(
            '#edit-delete' => array(
              'checked' => TRUE,
            ),
          ),
        ),
        '#group' => 'delete',
      );

      // Check for multiple instances.
      if (Drupal::database()
        ->query("SELECT count(coid) FROM {course_object} WHERE object_type = :object_type AND instance = :instance", array(
        ':object_type' => $config['object_type'],
        ':instance' => $config['instance'],
      ))
        ->fetchField() > 1) {
        $form['delete']['delete_instance']['#description'] = t('<span class="error"><strong>WARNING</strong></span>: multiple course objects link to this instance. Deleting the instance might break the other course objects that use it.');
      }
    }
    if ($this
      ->isGraded()) {
      $form['grading'] = array(
        '#title' => t('Grading'),
        '#type' => 'details',
        '#description' => t('Settings for graded objects.'),
        '#group' => 'course_tabs',
        '#weight' => 2,
      );
      $form['grading']['grade_include'] = array(
        '#title' => t('Include in final course grade'),
        '#description' => t('Include this grade result for calculation of the final course grade.<br/>Currently, only the last grade result per Course will be used.'),
        '#default_value' => $config['grade_include'],
        '#type' => 'checkbox',
      );
    }

    // Bring in access plugin configuration.
    $form['plugins']['#tree'] = TRUE;
    $form['plugins']['access']['#title'] = t('Access');
    $form['plugins']['access']['#description'] = t('By default, all required objects appearing before this object in the course outline must be completed before the user may access this object. Conditional access allows for additional conditions to be applied.');
    $form['plugins']['access']['#type'] = 'details';
    $pluginManager = Drupal::service('plugin.manager.course.object.access');
    $plugins = $pluginManager
      ->getDefinitions();
    foreach ($plugins as $key => $plugin) {
      $form['plugins']['access']['#tree'] = TRUE;
      $form['plugins']['access'][$key] = array(
        '#title' => $plugin['label'],
        '#type' => 'details',
        '#tree' => TRUE,
        '#open' => FALSE,
      );

      // Initialize access class.

      /* @var $courseAccess CourseObjectAccessPluginBase */
      $courseAccess = $pluginManager
        ->createInstance($key);
      $courseAccess
        ->setCourseObject($this);
      $courseAccess
        ->setType($key);

      // Add access plugin form to our form.
      $access_form = $access_form_state = array();
      $form['plugins']['access'][$key] += $courseAccess
        ->optionsForm($access_form, $access_form_state);
    }

    // Update settings
    $form['actions']['update'] = array(
      '#value' => t('Update'),
      '#weight' => 999,
      '#type' => 'submit',
      '#validate' => array(
        array(
          $this,
          'optionsValidate',
        ),
      ),
      '#submit' => array(
        array(
          $this,
          'optionsSubmit',
        ),
      ),
    );
  }

  /**
   * Mark this object for deletion.
   */
  public function setDelete(&$form, FormStateInterface $form_state) {
    $form_state
      ->setValue('delete', 1);
    if (!empty($form_state
      ->getUserInput()['delete_instance'])) {
      $form_state
        ->setValue('delete_instance', $form_state
        ->getUserInput()['delete_instance']);
    }
  }
  public function optionsValidate(&$form, FormStateInterface $form_state) {

    // Pass validation to plugins.
    //    ctools_include('plugins');
    //    foreach (ctools_get_plugins('course', 'course_object_access') as $key => $plugin) {
    //      $values = & $form_state['values']['plugins']['access'][$key];
    //      $class = ctools_plugin_get_class($plugin, 'handler');
    //      $instance = new $class($values);
    //      $instance->setCourseObject($this);
    //      $instance->setType($key);
    //      $instance->optionsValidate($form['plugins']['access'][$key], $form_state['values']['plugins']['access'][$key]);
    //    }
  }

  /**
   * Save object configs to cache.
   */
  public function optionsSubmit(&$form, FormStateInterface $form_state) {
    $uniqid = $this
      ->getId();
    $course = $form_state
      ->getBuildInfo()['args'][0];
    $cid = $course
      ->id();

    // Start editing session.
    course_editing_start($this
      ->getCourse());

    // Flip 'visible' so it behaves like 'hidden'.
    if ($form_state
      ->getValue('hidden')) {
      $form_state
        ->setValue('hidden', $form_state
        ->getValue('hidden') != 1);
    }

    // Object-specific settings
    $_SESSION['course'][$cid]['editing'][$uniqid] = $form_state
      ->getValues() + $_SESSION['course'][$cid]['editing'][$uniqid];
  }

  /**
   * Get core options summary.
   *
   * @return array
   *   An associative array of summary keys and values.
   */
  public function getOptionsSummary() {
    $summary = parent::getOptionsSummary();

    // Get options.
    $options = $this
      ->getOptions();

    // Enabled.
    if ($options['enabled']) {
      $summary['enabled'] = t('Enabled');
    }
    else {
      $summary['enabled'] = '<span class="warning">' . t('Not enabled') . '</span>';
    }

    // Hidden.
    if (!$options['hidden']) {
      $summary['hidden'] = t('Visible in outline');
    }
    else {
      $summary['hidden'] = '<span class="warning">' . t('Not visible in outline') . '</span>';
    }

    // Required.
    if ($options['required']) {
      $summary['required'] = t('Completion required');
      if ($options['skippable']) {
        $summary['skippable'] = '<span class="warning">' . t('Skippable') . '</span>';
      }
    }
    else {
      $summary['required'] = '<span class="warning">' . t('Completion not required') . '</span>';
    }

    // Instance edit link.
    $editUrl = $this
      ->getEditUrl();
    if (!empty($editUrl)) {
      $text = t('Edit instance');
      $summary['instance'] = Link::fromTextAndUrl($text, $editUrl)
        ->toString();
    }
    elseif ($this
      ->isTemporary()) {
      $summary['instance'] = '<span class="warning">' . t('Save course to edit object') . '</span>';
    }

    // Instance view link.
    $viewUrl = $this
      ->getViewUrl();
    if (!empty($viewUrl)) {
      $text = t('View instance');
      $summary['instance_view'] = Link::fromTextAndUrl($text, $viewUrl)
        ->toString();
    }

    // Required.
    if (!empty($options['delete'])) {
      $dest = Url::fromRoute('course.object.restore', [
        'course' => $options['cid'],
        'course_object' => $this
          ->getId(),
      ], [
        'attributes' => [
          'class' => 'use-ajax',
        ],
      ]);
      $text = t('Object will be removed from outline');
      $restore_text = t('Restore this object to the course outline.');
      if ($options['delete_instance']) {
        $text = t('Object will be removed from outline, and related instance(s) will be deleted');
        $restore_text = t('Restore this object and related instance(s) to the course outline.');
      }
      $restore = Link::fromTextAndUrl(t('Restore'), $dest, $restore_text)
        ->toString();
      $summary['delete'] = '<span class="error">' . $text . '</span>';
      $summary['restore'] = $restore;
    }
    return $summary;
  }

  /**
   * Get all course object implementations of getOptionsSummary().
   *
   * @todo need plugins
   */
  public function renderOptionsSummary() {
    $summary = $this
      ->getOptionsSummary();
    $out = [];
    foreach ($summary as $key => $item) {
      $out[$key]['#markup'] = $item;
    }
    return $out;
  }

  /**
   * Get options, with session options, except weight, having precedence.
   */
  public function getOptions() {
    $options = parent::getOptions();
    $sessionDefaults = array();
    if (isset($options['cid']) && isset($options['coid']) && isset($_SESSION['course'][$options['cid']]['editing'][$options['coid']])) {
      $sessionDefaults += array_filter((array) $_SESSION['course'][$options['cid']]['editing'][$options['coid']], array(
        $this,
        'optionFilter',
      ));
      unset($sessionDefaults['weight']);
    }
    return array_merge($options, (array) $sessionDefaults);
  }
  private function optionFilter($a) {
    return !is_null($a);
  }

  /**
   * Take a course object.
   *
   * - Set the session of this course object being taken. This allows for
   *   non-node objects to be tracked.
   * - Delegate the course object take functionality
   *
   * @return mixed
   *   HTML content or a redirect.
   */
  public final function takeObject() {
    $account = Drupal::currentUser();
    $_SESSION['course']['active'] = $this
      ->getCourse()
      ->id();
    $_SESSION['course'][$this
      ->getCourse()
      ->id()]['taking']['active'] = $this
      ->getId();

    // Run access checks.
    if ($this
      ->access('take')) {

      // Grant access to external course object.
      $this
        ->getFulfillment($account)
        ->grant();

      // Record start date.
      $this
        ->getFulfillment($account)
        ->save();
    }
    else {

      // User can't access this object, revoke access.
      $this
        ->getFulfillment($account)
        ->revoke();
      return FALSE;
    }

    // If we're not displaying any content but we want to fire take() anyway, to
    // let the course object know we sent the user.
    $out = $this
      ->take();
    $url = $this
      ->getTakeUrl();
    switch ($this
      ->getTakeType()) {
      case 'iframe':
        return course_iframe($url);
      case 'popup':
        return "will popup {$url}";
      case 'content':
        return $out;
      case 'redirect':
      default:

        // This URL should have already been url()'d (it might be external).
        return new RedirectResponse($url
          ->toString());
    }
  }

  /**
   * How should this course object be executed?
   *
   * - iframe: display an iframe with getTakeUrl() in it
   * - popup: launch getTakeUrl() in a popup
   * - modal: launch getTakeUrl() in a modal
   * - content: print the value from take() (or do whatever the module wants to
   *   do)
   */
  public function getTakeType() {
    return 'content';
  }

  /**
   * Course object entry point for taking. This method should return a value
   * corresponding to the type set in getTakeType().
   */
  public function take() {
    return t('This should be overridden by the module to return course content.');
  }

  /**
   * Return the URL to the course object router.
   *
   * @return Url
   */
  public function getUrl() {
    return Url::fromRoute('course.object', [
      'course' => $this
        ->getCourse()
        ->id(),
      'course_object' => $this
        ->id(),
    ]);
  }

  /**
   * Get the URL to take this course object, if any.
   *
   * Outline handlers or subclasses should use getUrl().
   *
   * @return Url
   */
  protected function getTakeUrl() {
  }

  /**
   * Get the URL to edit this course object, if any.
   *
   * @return Url
   */
  public function getEditUrl() {
  }

  /**
   * Get the URL to view this course object, if any.
   *
   * @return Url
   */
  public function getViewUrl() {
  }

  /**
   * Is this course object required for course completion?
   *
   * @return bool
   */
  public function isRequired() {
    return (bool) $this
      ->getOption('required');
  }

  /**
   * If this course object is required, can be it skipped?
   *
   * @return bool
   */
  public function isSkippable() {
    return (bool) $this
      ->getOption('skippable');
  }

  /**
   * Is this object graded?
   *
   * Returning TRUE here will cause some more configurations to show on the
   * object's form.
   *
   * @return bool
   */
  function isGraded() {
    return FALSE;
  }

  /**
   * Get the user's status in this course object.
   *
   * This is how an object would notify the user why they cannot proceed to the
   * next step from the course outline. For example, if this was a quiz and
   * they failed, this should let them know.
   */
  public function getStatus() {
  }

  /**
   * Get a user's fulfillment for this course object. If the user has not
   * started this course object, a new, unsaved fulfillment will be return.
   *
   * @param stdClass $account
   *   User account to get fulfillment for.
   *
   * @return CourseObjectFulfillment
   */
  public function getFulfillment(AccountInterface $account) {
    $entities = \Drupal::entityTypeManager()
      ->getStorage('course_object_fulfillment')
      ->loadByProperties([
      'coid' => $this
        ->id(),
      'uid' => $account
        ->id(),
    ]);
    if ($entities) {
      return reset($entities);
    }
    else {
      return CourseObjectFulfillment::create(array(
        'coid' => $this
          ->id(),
        'uid' => $account
          ->id(),
        'object_type' => $this
          ->get('object_type')
          ->getString(),
      ));
    }
  }

  /**
   * Get the instance ID. This could be the external component ID, a Node ID...
   *
   * @return string
   */
  function getInstanceId() {
    return $this
      ->getOption('instance');
  }

  /**
   * Set this object's instance ID.
   *
   * @param string $id The external ID of this course object.
   */
  function setInstanceId($id) {
    return $this
      ->setOption('instance', $id);
  }

  /**
   * Set the Course for this CourseObject.
   *
   * @param Course|int $course
   *   A Course or node ID.
   *
   * @return CourseObject
   */
  public function setCourse($course) {
    if (is_numeric($course)) {
      $this
        ->setOption('cid', $course);
    }
    else {
      $this
        ->setOption('cid', $course
        ->id());
    }
    return $this;
  }

  /**
   * Get the Course that contains this CourseObject.
   *
   * @return Course
   */
  function getCourse() {
    return Course::load($this
      ->get('cid')
      ->getString());
  }

  /**
   * Get the object component title for this course object.
   *
   * @return string
   */
  function getComponentName() {
    $handlers = course_get_handlers('object');
    return $handlers[$this
      ->getComponent()]['label'];
  }

  /**
   * Get the object component for this course object.
   *
   * @return string
   */
  function getComponent() {
    return $this
      ->getOption('object_type');
  }

  /**
   * Set the object component for this course object.
   *
   * @param string $component
   *   The object's component.
   *
   * @return CourseObject
   */
  function setComponent($component) {
    return $this
      ->setOption('object_type', $component);
  }

  /**
   * Set the internal course object ID.
   *
   * @param int $coid
   *   ID of the course object.
   */
  function setId($coid) {
    return $this
      ->setOption('coid', $coid);
  }

  /**
   * Creates a course object.
   *
   * For example, this would create the new node and return the node ID if this
   * was a CourseObjectNode.
   *
   * Do not confuse this with save(), which saves the course outline record for
   * tracking.
   *
   * Course objects should call setInstanceId() if this is a course object
   * that creates external resources.
   */
  public function createInstance() {

    //$this->setInstanceId($id);
  }

  /**
   * Objects should implement this method if they need to delete their own
   * content.
   */
  public function deleteInstance() {

    //thing_delete();
  }
  function getTitle() {
    $object_info = course_get_handlers('object');

    // If title is not specified, set title from component.
    if (!$this
      ->getOption('title')) {

      // Get the component name from object info.
      $title = $object_info[$this
        ->getOption('object_type')]['label'];
      $this
        ->setOption('title', $title);
    }
    return $this
      ->getOption('title');
  }

  /**
   * Give the course object a chance do asynchronous polling and set completion
   * on demand.
   *
   * Useful for external objects.
   */
  function poll() {
  }

  /**
   * Let the course object provide its own reports.
   *
   * @return array
   *   An array indexed by report key, containing 'title' which is the menu link
   *   in the course object reports.
   */
  function getReports() {
    return array(
      'default' => array(
        'title' => 'Overview',
      ),
    );
  }

  /**
   * Let the course object provide its own reports.
   *
   * @return array
   *   An array containing:
   *     - title: The title of this report as show on the page
   *     - content: Content to be displayed.
   *     - url: URL to be loaded in an iframe.
   *   Reports should return either 'content' or 'url'.
   */
  function getReport($key) {
    if ($key == 'default') {
      return array(
        'title' => 'Overview',
        'content' => views_embed_view('course_object_report', 'default', $this
          ->getCourse()
          ->id(), $this
          ->getId()),
      );
    }
  }
  function freeze() {
  }
  function thaw($ice) {
  }

  /**
   * Returns an translated error message if this object has issues with cloning.
   *
   * @return mixed
   *   TRUE if cloning is supported, FALSE if cloning is not supported. A string
   *   if the object can clone but with caveats.
   */
  function getCloneAbility() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   *
   * Apply configuration from session and let objects create their instances
   * before saving the course object.
   */
  public function save() {

    // If there is no title, set it.
    $this
      ->getTitle();
    if ($ice = $this
      ->getOption('freeze')) {

      // Found frozen data. Restore it to this object.
      $this
        ->setInstanceId($this
        ->thaw($ice));
      $this
        ->setOption('freeze', NULL);
    }

    // Pull temporary configuration from session.
    foreach ($this
      ->optionsDefinition() as $key => $default) {
      $value = $this
        ->getOption($key);
      $this
        ->set($key, $value);
    }

    // If there is no instance ID, tell the object to create external content.
    if (!$this
      ->getInstanceId()) {
      $this
        ->createInstance();
    }

    // Set the ID to NULL because this is a temporary course object being
    // created for the first time.
    if (strpos($this
      ->getId(), 'course_object_') !== FALSE) {
      $this
        ->setId(NULL);
    }
    $data = $this
      ->get('data')
      ->getValue();

    // Delegate to parent entity save.
    return parent::save();
  }

  /**
   * Checks the temporary status of a course object.
   */
  function isTemporary() {
    return strpos($this
      ->getId(), 'course_object_') === 0;
  }

  /**
   * Return the number of occurances that can be in a course at the same time.
   * For example, the design of the Certificate module can only have 1 set of
   * mappings per node. The same goes for Course Credit. We may also want a
   * course object that can only be added twice (for example, a before/after
   * comparison).
   *
   * This method is static because we might have to call it without an object
   * being instantiated.
   */
  public static function getMaxOccurences() {
    return FALSE;
  }

  /**
   * Set the context of which course this course object belongs to.
   *
   * The return parameters should be compliant with course_determine_context().
   */
  public static function context() {
  }

  /**
   * Generate URI from course object.
   */
  public function uri() {
    return array(
      'path' => 'node/' . $this->nid . '/object/' . $this
        ->identifier(),
    );
  }

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    $fields = parent::baseFieldDefinitions($entity_type);
    $fields['cid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Course'));
    $fields['object_type'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Object'));
    $fields['title'] = BaseFieldDefinition::create('string')
      ->setDisplayConfigurable('view', TRUE)
      ->setLabel(t('Object title'));
    $fields['enabled'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Course ID'));
    $fields['instance'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Instance identifier'))
      ->setDescription('An ID used to identify a remote activity.');
    $fields['required'] = BaseFieldDefinition::create('boolean')
      ->setDisplayConfigurable('view', TRUE)
      ->setLabel(t('Required'));
    $fields['weight'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Weight'));
    $fields['hidden'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Hidden'));
    $fields['duration'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Duration'));
    $fields['data'] = BaseFieldDefinition::create('map')
      ->setLabel(t('Data'));
    $fields['created'] = BaseFieldDefinition::create('created')
      ->setRevisionable(TRUE)
      ->setLabel('Created');
    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setRevisionable(TRUE)
      ->setLabel('Changed');
    return $fields;
  }

  /**
   * Set field in extra data if needed.
   *
   * {@inheritdoc}
   */
  function set($name, $value, $notify = TRUE) {
    if (!in_array($name, array_keys($this
      ->getFieldDefinitions()))) {
      $extra = parent::get('data')
        ->getValue() ?? [];
      $extra[0][$name] = $value;
      return parent::set('data', $extra[0]);
    }
    else {
      return parent::set($name, $value, $notify);
    }
  }

  /**
   * Map this object base to the base entity class.
   */
  public function getEntityType() {
    $entityType = parent::getEntityType();
    $class = get_class($this);
    $entityType
      ->set('originalClass', $class);
    return $entityType;
  }
  public static function postDelete(Drupal\Core\Entity\EntityStorageInterface $storage, array $entities) {
    $fs = \Drupal::entityTypeManager()
      ->getStorage('course_object_fulfillment');
    $coids = array_keys($entities);
    $fulfillments = $fs
      ->loadByProperties([
      'coid' => $coids,
    ]);
    $fs
      ->delete($fulfillments);
    parent::postDelete($storage, $entities);
  }

  /**
   * Clear static access cache on save.
   *
   * {@inheritdoc}
   */
  public function postSave(Drupal\Core\Entity\EntityStorageInterface $storage, $update = TRUE) {
    parent::postSave($storage, $update);
    \Drupal::entityTypeManager()
      ->getAccessControlHandler('course_object')
      ->resetCache();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CacheableDependencyTrait::$cacheContexts protected property Cache contexts.
CacheableDependencyTrait::$cacheMaxAge protected property Cache max-age.
CacheableDependencyTrait::$cacheTags protected property Cache tags.
CacheableDependencyTrait::setCacheability protected function Sets cacheability; useful for value object constructors.
ContentEntityBase::$activeLangcode protected property Language code identifying the entity active language.
ContentEntityBase::$defaultLangcode protected property Local cache for the default language code.
ContentEntityBase::$defaultLangcodeKey protected property The default langcode entity key.
ContentEntityBase::$enforceRevisionTranslationAffected protected property Whether the revision translation affected flag has been enforced.
ContentEntityBase::$entityKeys protected property Holds untranslatable entity keys such as the ID, bundle, and revision ID.
ContentEntityBase::$fieldDefinitions protected property Local cache for field definitions.
ContentEntityBase::$fields protected property The array of fields, each being an instance of FieldItemListInterface.
ContentEntityBase::$fieldsToSkipFromTranslationChangesCheck protected static property Local cache for fields to skip from the checking for translation changes.
ContentEntityBase::$isDefaultRevision protected property Indicates whether this is the default revision.
ContentEntityBase::$langcodeKey protected property The language entity key.
ContentEntityBase::$languages protected property Local cache for the available language objects.
ContentEntityBase::$loadedRevisionId protected property The loaded revision ID before the new revision was set.
ContentEntityBase::$newRevision protected property Boolean indicating whether a new revision should be created on save.
ContentEntityBase::$revisionTranslationAffectedKey protected property The revision translation affected entity key.
ContentEntityBase::$translatableEntityKeys protected property Holds translatable entity keys such as the label.
ContentEntityBase::$translationInitialize protected property A flag indicating whether a translation object is being initialized.
ContentEntityBase::$translations protected property An array of entity translation metadata.
ContentEntityBase::$validated protected property Whether entity validation was performed.
ContentEntityBase::$validationRequired protected property Whether entity validation is required before saving the entity.
ContentEntityBase::$values protected property The plain data values of the contained fields.
ContentEntityBase::access public function Checks data value access. Overrides EntityBase::access 1
ContentEntityBase::addTranslation public function Adds a new translation to the translatable object. Overrides TranslatableInterface::addTranslation
ContentEntityBase::bundle public function Gets the bundle of the entity. Overrides EntityBase::bundle
ContentEntityBase::bundleFieldDefinitions public static function Provides field definitions for a specific bundle. Overrides FieldableEntityInterface::bundleFieldDefinitions 4
ContentEntityBase::clearTranslationCache protected function Clear entity translation object cache to remove stale references.
ContentEntityBase::createDuplicate public function Creates a duplicate of the entity. Overrides EntityBase::createDuplicate 1
ContentEntityBase::get public function Gets a field item list. Overrides FieldableEntityInterface::get
ContentEntityBase::getEntityKey protected function Gets the value of the given entity key, if defined. 1
ContentEntityBase::getFieldDefinition public function Gets the definition of a contained field. Overrides FieldableEntityInterface::getFieldDefinition
ContentEntityBase::getFieldDefinitions public function Gets an array of field definitions of all contained fields. Overrides FieldableEntityInterface::getFieldDefinitions
ContentEntityBase::getFields public function Gets an array of all field item lists. Overrides FieldableEntityInterface::getFields
ContentEntityBase::getFieldsToSkipFromTranslationChangesCheck protected function Returns an array of field names to skip in ::hasTranslationChanges. 1
ContentEntityBase::getIterator public function
ContentEntityBase::getLanguages protected function
ContentEntityBase::getLoadedRevisionId public function Gets the loaded Revision ID of the entity. Overrides RevisionableInterface::getLoadedRevisionId
ContentEntityBase::getRevisionId public function Gets the revision identifier of the entity. Overrides RevisionableInterface::getRevisionId
ContentEntityBase::getTranslatableFields public function Gets an array of field item lists for translatable fields. Overrides FieldableEntityInterface::getTranslatableFields
ContentEntityBase::getTranslatedField protected function Gets a translated field.
ContentEntityBase::getTranslation public function Gets a translation of the data. Overrides TranslatableInterface::getTranslation
ContentEntityBase::getTranslationLanguages public function Returns the languages the data is translated to. Overrides TranslatableInterface::getTranslationLanguages
ContentEntityBase::getTranslationStatus public function Returns the translation status. Overrides TranslationStatusInterface::getTranslationStatus
ContentEntityBase::getUntranslated public function Returns the translatable object referring to the original language. Overrides TranslatableInterface::getUntranslated
ContentEntityBase::hasField public function Determines whether the entity has a field with the given name. Overrides FieldableEntityInterface::hasField
ContentEntityBase::hasTranslation public function Checks there is a translation for the given language code. Overrides TranslatableInterface::hasTranslation
ContentEntityBase::hasTranslationChanges public function Determines if the current translation of the entity has unsaved changes. Overrides TranslatableInterface::hasTranslationChanges
ContentEntityBase::id public function Gets the identifier. Overrides EntityBase::id
ContentEntityBase::initializeTranslation protected function Instantiates a translation object for an existing translation.
ContentEntityBase::isDefaultRevision public function Checks if this entity is the default revision. Overrides RevisionableInterface::isDefaultRevision
ContentEntityBase::isDefaultTranslation public function Checks whether the translation is the default one. Overrides TranslatableInterface::isDefaultTranslation
ContentEntityBase::isDefaultTranslationAffectedOnly public function Checks if untranslatable fields should affect only the default translation. Overrides TranslatableRevisionableInterface::isDefaultTranslationAffectedOnly
ContentEntityBase::isLatestRevision public function Checks if this entity is the latest revision. Overrides RevisionableInterface::isLatestRevision
ContentEntityBase::isLatestTranslationAffectedRevision public function Checks whether this is the latest revision affecting this translation. Overrides TranslatableRevisionableInterface::isLatestTranslationAffectedRevision
ContentEntityBase::isNewRevision public function Determines whether a new revision should be created on save. Overrides RevisionableInterface::isNewRevision
ContentEntityBase::isNewTranslation public function Checks whether the translation is new. Overrides TranslatableInterface::isNewTranslation
ContentEntityBase::isRevisionTranslationAffected public function Checks whether the current translation is affected by the current revision. Overrides TranslatableRevisionableInterface::isRevisionTranslationAffected
ContentEntityBase::isRevisionTranslationAffectedEnforced public function Checks if the revision translation affected flag value has been enforced. Overrides TranslatableRevisionableInterface::isRevisionTranslationAffectedEnforced
ContentEntityBase::isTranslatable public function Returns the translation support status. Overrides TranslatableInterface::isTranslatable
ContentEntityBase::isValidationRequired public function Checks whether entity validation is required before saving the entity. Overrides FieldableEntityInterface::isValidationRequired
ContentEntityBase::label public function Gets the label of the entity. Overrides EntityBase::label 2
ContentEntityBase::language public function Gets the language of the entity. Overrides EntityBase::language
ContentEntityBase::onChange public function Reacts to changes to a field. Overrides FieldableEntityInterface::onChange
ContentEntityBase::postCreate public function Acts on a created entity before hooks are invoked. Overrides EntityBase::postCreate
ContentEntityBase::preSave public function Acts on an entity before the presave hook is invoked. Overrides EntityBase::preSave 5
ContentEntityBase::preSaveRevision public function Acts on a revision before it gets saved. Overrides RevisionableInterface::preSaveRevision 2
ContentEntityBase::referencedEntities public function Gets a list of entities referenced by this entity. Overrides EntityBase::referencedEntities 1
ContentEntityBase::removeTranslation public function Removes the translation identified by the given language code. Overrides TranslatableInterface::removeTranslation
ContentEntityBase::setDefaultLangcode protected function Populates the local cache for the default language code.
ContentEntityBase::setNewRevision public function Enforces an entity to be saved as a new revision. Overrides RevisionableInterface::setNewRevision
ContentEntityBase::setRevisionTranslationAffected public function Marks the current revision translation as affected. Overrides TranslatableRevisionableInterface::setRevisionTranslationAffected
ContentEntityBase::setRevisionTranslationAffectedEnforced public function Enforces the revision translation affected flag value. Overrides TranslatableRevisionableInterface::setRevisionTranslationAffectedEnforced
ContentEntityBase::setValidationRequired public function Sets whether entity validation is required before saving the entity. Overrides FieldableEntityInterface::setValidationRequired
ContentEntityBase::toArray public function Gets an array of all property values. Overrides EntityBase::toArray
ContentEntityBase::updateFieldLangcodes protected function Updates language for already instantiated fields.
ContentEntityBase::updateLoadedRevisionId public function Updates the loaded Revision ID with the revision ID. Overrides RevisionableInterface::updateLoadedRevisionId
ContentEntityBase::updateOriginalValues public function Updates the original values with the interim changes.
ContentEntityBase::uuid public function Gets the entity UUID (Universally Unique Identifier). Overrides EntityBase::uuid
ContentEntityBase::validate public function Validates the currently set values. Overrides FieldableEntityInterface::validate
ContentEntityBase::wasDefaultRevision public function Checks whether the entity object was a default revision when it was saved. Overrides RevisionableInterface::wasDefaultRevision
ContentEntityBase::__clone public function Magic method: Implements a deep clone.
ContentEntityBase::__construct public function Constructs an Entity object. Overrides EntityBase::__construct
ContentEntityBase::__get public function Implements the magic method for getting object properties.
ContentEntityBase::__isset public function Implements the magic method for isset().
ContentEntityBase::__set public function Implements the magic method for setting object properties.
ContentEntityBase::__sleep public function Overrides EntityBase::__sleep
ContentEntityBase::__unset public function Implements the magic method for unset().
CourseHandler::addOptions final public function Merge an array of options onto the existing options.
CourseHandler::getAccessMessages public function Get an array of access messages.
CourseHandler::getId function Handlers must have an ID.
CourseHandler::getOption public function Get an handler option's value.
CourseHandler::getWarnings public function Return a list of warning strings about this handler. 3
CourseHandler::optionsMerge private function Merge arrays with replace, not append.
CourseHandler::setAccessMessage public function Set an access message to be displayed along with the course object when it is in the outline. For example, "This activity will open on XYZ" or "Please complete Step 1 to take this activity."
CourseHandler::setOption public function Set an option for this handler.
CourseHandler::setOptions final public function Set this entire handler's options.
CourseObject::$accessMessages protected property
CourseObject::baseFieldDefinitions public static function Provides base field definitions for an entity type. Overrides ContentEntityBase::baseFieldDefinitions
CourseObject::context public static function Set the context of which course this course object belongs to. 3
CourseObject::createInstance public function Creates a course object. 3
CourseObject::deleteInstance public function Objects should implement this method if they need to delete their own content. 1
CourseObject::freeze function 1
CourseObject::getCloneAbility function Returns an translated error message if this object has issues with cloning. 4
CourseObject::getComponent function Get the object component for this course object.
CourseObject::getComponentName function Get the object component title for this course object.
CourseObject::getCourse function Get the Course that contains this CourseObject.
CourseObject::getEditUrl public function Get the URL to edit this course object, if any. 1
CourseObject::getEntityType public function Map this object base to the base entity class. Overrides EntityBase::getEntityType
CourseObject::getFulfillment public function Get a user's fulfillment for this course object. If the user has not started this course object, a new, unsaved fulfillment will be return.
CourseObject::getInstanceId function Get the instance ID. This could be the external component ID, a Node ID...
CourseObject::getMaxOccurences public static function Return the number of occurances that can be in a course at the same time. For example, the design of the Certificate module can only have 1 set of mappings per node. The same goes for Course Credit. We may also want a course object that can only be… 2
CourseObject::getOptions public function Get options, with session options, except weight, having precedence. Overrides CourseHandler::getOptions
CourseObject::getOptionsSummary public function Get core options summary. Overrides CourseHandler::getOptionsSummary 3
CourseObject::getReport function Let the course object provide its own reports. 2
CourseObject::getReports function Let the course object provide its own reports. 2
CourseObject::getStatus public function Get the user's status in this course object. 2
CourseObject::getTakeType public function How should this course object be executed? 4
CourseObject::getTakeUrl protected function Get the URL to take this course object, if any. 3
CourseObject::getTitle function 1
CourseObject::getUrl public function Return the URL to the course object router.
CourseObject::getViewUrl public function Get the URL to view this course object, if any. 1
CourseObject::hasPolling public function Specify whether fulfillment uses asynchronous polling.
CourseObject::isActive public function
CourseObject::isGraded function Is this object graded? 2
CourseObject::isRequired public function Is this course object required for course completion?
CourseObject::isSkippable public function If this course object is required, can be it skipped?
CourseObject::isTemporary function Checks the temporary status of a course object.
CourseObject::optionFilter private function
CourseObject::optionsDefinition public function Define configuration elements and their defaults. Overrides CourseHandler::optionsDefinition 5
CourseObject::optionsForm public function Default options form for all course objects. Overrides CourseHandler::optionsForm 5
CourseObject::optionsSubmit public function Save object configs to cache. Overrides CourseHandler::optionsSubmit 1
CourseObject::optionsValidate public function Validate? Overrides CourseHandler::optionsValidate 1
CourseObject::overrideNavigation public function Override navigation links. 1
CourseObject::overrideOutlineListItem public function Overrides a course outline list item. 1
CourseObject::poll function Give the course object a chance do asynchronous polling and set completion on demand.
CourseObject::postDelete public static function Acts on deleted entities before the delete hook is invoked. Overrides EntityBase::postDelete
CourseObject::postSave public function Clear static access cache on save. Overrides ContentEntityBase::postSave 1
CourseObject::renderOptionsSummary public function Get all course object implementations of getOptionsSummary().
CourseObject::save public function Apply configuration from session and let objects create their instances before saving the course object. Overrides EntityBase::save 1
CourseObject::set function Set field in extra data if needed. Overrides ContentEntityBase::set
CourseObject::setComponent function Set the object component for this course object.
CourseObject::setCourse public function Set the Course for this CourseObject.
CourseObject::setDelete public function Mark this object for deletion.
CourseObject::setId function Set the internal course object ID.
CourseObject::setInstanceId function Set this object's instance ID.
CourseObject::take public function Course object entry point for taking. This method should return a value corresponding to the type set in getTakeType(). 4
CourseObject::takeObject final public function Take a course object.
CourseObject::thaw function 1
CourseObject::uri public function Generate URI from course object.
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 Aliased as: traitSleep 1
DependencySerializationTrait::__wakeup public function 2
EntityBase::$enforceIsNew protected property Boolean indicating whether the entity should be forced to be new.
EntityBase::$entityTypeId protected property The entity type.
EntityBase::$typedData protected property A typed data object wrapping this entity.
EntityBase::create public static function Constructs a new entity object, without permanently saving it. Overrides EntityInterface::create
EntityBase::delete public function Deletes an entity permanently. Overrides EntityInterface::delete 2
EntityBase::enforceIsNew public function Enforces an entity to be new. Overrides EntityInterface::enforceIsNew
EntityBase::entityManager Deprecated protected function Gets the entity manager.
EntityBase::entityTypeBundleInfo protected function Gets the entity type bundle info service.
EntityBase::entityTypeManager protected function Gets the entity type manager.
EntityBase::getCacheContexts public function The cache contexts associated with this object. Overrides CacheableDependencyTrait::getCacheContexts
EntityBase::getCacheMaxAge public function The maximum age for which this object may be cached. Overrides CacheableDependencyTrait::getCacheMaxAge
EntityBase::getCacheTags public function The cache tags associated with this object. Overrides CacheableDependencyTrait::getCacheTags
EntityBase::getCacheTagsToInvalidate public function Returns the cache tags that should be used to invalidate caches. Overrides EntityInterface::getCacheTagsToInvalidate 2
EntityBase::getConfigDependencyKey public function Gets the key that is used to store configuration dependencies. Overrides EntityInterface::getConfigDependencyKey
EntityBase::getConfigDependencyName public function Gets the configuration dependency name. Overrides EntityInterface::getConfigDependencyName 1
EntityBase::getConfigTarget public function Gets the configuration target identifier for the entity. Overrides EntityInterface::getConfigTarget 1
EntityBase::getEntityTypeId public function Gets the ID of the type of the entity. Overrides EntityInterface::getEntityTypeId
EntityBase::getListCacheTagsToInvalidate protected function The list cache tags to invalidate for this entity.
EntityBase::getOriginalId public function Gets the original ID. Overrides EntityInterface::getOriginalId 1
EntityBase::getTypedData public function Gets a typed data object for this entity object. Overrides EntityInterface::getTypedData
EntityBase::hasLinkTemplate public function Indicates if a link template exists for a given key. Overrides EntityInterface::hasLinkTemplate
EntityBase::invalidateTagsOnDelete protected static function Invalidates an entity's cache tags upon delete. 1
EntityBase::invalidateTagsOnSave protected function Invalidates an entity's cache tags upon save. 1
EntityBase::isNew public function Determines whether the entity is new. Overrides EntityInterface::isNew 2
EntityBase::languageManager protected function Gets the language manager.
EntityBase::link public function Deprecated way of generating a link to the entity. See toLink(). Overrides EntityInterface::link 1
EntityBase::linkTemplates protected function Gets an array link templates. 1
EntityBase::load public static function Loads an entity. Overrides EntityInterface::load
EntityBase::loadMultiple public static function Loads one or more entities. Overrides EntityInterface::loadMultiple
EntityBase::postLoad public static function Acts on loaded entities. Overrides EntityInterface::postLoad 2
EntityBase::preCreate public static function Changes the values of an entity before it is created. Overrides EntityInterface::preCreate 5
EntityBase::preDelete public static function Acts on entities before they are deleted and before hooks are invoked. Overrides EntityInterface::preDelete 4
EntityBase::setOriginalId public function Sets the original ID. Overrides EntityInterface::setOriginalId 1
EntityBase::toLink public function Generates the HTML for a link to this entity. Overrides EntityInterface::toLink
EntityBase::toUrl public function Gets the URL object for the entity. Overrides EntityInterface::toUrl 2
EntityBase::uriRelationships public function Gets a list of URI relationships supported by this entity. Overrides EntityInterface::uriRelationships
EntityBase::url public function Gets the public URL for this entity. Overrides EntityInterface::url 2
EntityBase::urlInfo public function Gets the URL object for the entity. Overrides EntityInterface::urlInfo 1
EntityBase::urlRouteParameters protected function Gets an array of placeholders for this entity. 2
EntityBase::uuidGenerator protected function Gets the UUID generator.
EntityChangesDetectionTrait::getFieldsToSkipFromTranslationChangesCheck protected function Returns an array of field names to skip when checking for changes. Aliased as: traitGetFieldsToSkipFromTranslationChangesCheck
RefinableCacheableDependencyTrait::addCacheableDependency public function 1
RefinableCacheableDependencyTrait::addCacheContexts public function
RefinableCacheableDependencyTrait::addCacheTags public function
RefinableCacheableDependencyTrait::mergeCacheMaxAge public function
SynchronizableEntityTrait::$isSyncing protected property Whether this entity is being created, updated or deleted through a synchronization process.
SynchronizableEntityTrait::isSyncing public function
SynchronizableEntityTrait::setSyncing public function
TranslationStatusInterface::TRANSLATION_CREATED constant Status code identifying a newly created translation.
TranslationStatusInterface::TRANSLATION_EXISTING constant Status code identifying an existing translation.
TranslationStatusInterface::TRANSLATION_REMOVED constant Status code identifying a removed translation.