You are here

abstract class CourseObjectNode in Course 7

Same name and namespace in other branches
  1. 6 includes/course_object.core.inc \CourseObjectNode
  2. 7.2 includes/CourseObjectNode.inc \CourseObjectNode

A course object that uses a node as a base.

Hierarchy

Expanded class hierarchy of CourseObjectNode

1 string reference to 'CourseObjectNode'
course_cron in ./course.module
Implements hook_cron().

File

includes/CourseObjectNode.inc, line 6

View source
abstract class CourseObjectNode extends CourseObject {
  protected $node;
  public function __construct($values, $entityType) {
    parent::__construct($values, $entityType);
    if ($this
      ->getInstanceId()) {
      if ($node = node_load($this
        ->getInstanceId())) {
        $this
          ->setNode($node);
      }
    }
  }

  /**
   * Course context handler callback.
   */
  public static function context() {

    // Get node from URL.
    if (arg(0) == 'node' && ($node = node_load(arg(1)))) {

      // This node might not be in a course, so let's check for related nodes.
      $instances = static::getNodeInstances($node);
      if (!empty($instances)) {
        $node = node_load($instances[0]);
      }
      if (isset($node->nid)) {
        return array(
          'object_type' => $node->type,
          'instance' => $node->nid,
        );
      }
    }
  }

  /**
   * When passed a node, this method should return the "parent" nodes that are
   * contained in a course outline.
   *
   * For example, if the passed node was a question in a quiz, all the quiz node
   * IDs should be returned.
   */
  public static function getNodeInstances($node) {
    return array();
  }
  public function hasNodePrivacySupport() {
    return module_exists('content_access') && module_exists('acl');
  }

  /**
   * Return a list of valid node types.
   *
   * @return array
   *   An array with node type machine names.
   */
  public abstract function getNodeTypes();

  /**
   * Simple node course object behavior is to just redirect to the node.
   */
  public function getTakeType() {
    return 'redirect';
  }
  public function getTakeUrl() {
    if ($this
      ->getNode()) {
      return url("node/{$this->getNode()->nid}");
    }
  }
  public function getEditUrl() {
    if ($this
      ->getNode()) {
      return url("node/{$this->getNode()->nid}/edit");
    }
  }
  public function getViewUrl() {
    if ($this
      ->getNode()) {
      return url("node/{$this->getNode()->nid}");
    }
  }

  /**
   * Create a node and set it as this course object's node.
   *
   * @param stdClass $node
   *   A node to be processed for creation, or none to create a generic node. If
   *   a node is provided, it must have at least a type.
   */
  public function create($node = NULL) {
    global $user;
    if (!$node) {
      $node = new stdClass();
    }
    if (!isset($node->type)) {
      $node->type = $this
        ->getOption('node_type');
    }
    $node->title = $this
      ->getTitle();
    $node->uid = $user->uid;
    $language = language_default();
    $node->language = $language->language;
    node_object_prepare($node);
    node_save($node);
    $this
      ->setNode($node);
  }

  /**
   * Set the node and instance ID (node ID) of this CourseObjectNode.
   *
   * @param mixed $node
   *   A node or node ID.
   */
  public function setNode($node) {
    $this->node = clone $node;
    $this
      ->setInstanceId($this->node->nid);
  }

  /**
   * Destroy the node instance.
   */
  public function delete() {
    node_delete($this
      ->getInstanceId());
  }
  public function optionsDefinition() {
    $defaults = parent::optionsDefinition();
    $defaults['private'] = !isset($this->node) && $this
      ->hasNodePrivacySupport();
    $options = array_intersect_key(node_type_get_names(), drupal_map_assoc($this
      ->getNodeTypes()));
    $defaults['node_type'] = key($options);
    $defaults['use_node_title'] = 0;
    $defaults['clone_and_reference'] = 0;
    $defaults['use_existing_node'] = 0;
    return $defaults;
  }
  public function optionsForm(&$form, &$form_state) {
    parent::optionsForm($form, $form_state);
    $form['node'] = array(
      '#type' => 'fieldset',
      '#title' => t('Content'),
      '#description' => 'Settings for course object content.',
      '#group' => 'course_tabs',
      '#weight' => 2,
    );
    $config = $this
      ->getOptions();
    $types = drupal_map_assoc($this
      ->getNodeTypes());
    $options = array_intersect_key(node_type_get_names(), $types);
    $form['node']['use_existing_node'] = array(
      '#type' => 'checkbox',
      '#title' => t('Use existing content'),
      '#default_value' => $this
        ->getOption('use_existing_node'),
      '#weight' => 1,
      '#access' => $this
        ->isTemporary(),
    );
    $form['node']['node_type'] = array(
      '#title' => t('Create node'),
      '#type' => 'select',
      '#options' => $options,
      '#description' => t('Selecting a node type will automatically create this node and link it to this course object.'),
      '#default_value' => $config['node_type'],
      '#states' => array(
        'visible' => array(
          ':input[name="use_existing_node"]' => array(
            'checked' => FALSE,
          ),
        ),
      ),
      '#weight' => 2,
      '#access' => $this
        ->isTemporary(),
    );
    if (count($options) > 1) {
      $form['node']['node_type']['#required'] = TRUE;
    }
    $form['node']['instance'] = array(
      '#title' => t('Existing content'),
      '#autocomplete_path' => 'course/autocomplete/node/' . implode(',', $this
        ->getNodeTypes()),
      '#type' => 'textfield',
      '#description' => t('Use existing content instead of creating a new one.'),
      '#default_value' => !empty($this->node->nid) ? check_plain($this->node->title) . " [nid: {$this->node->nid}]" : NULL,
      '#maxlength' => 255,
      '#states' => array(
        'visible' => array(
          ':input[name="use_existing_node"]' => array(
            'checked' => TRUE,
          ),
        ),
      ),
      '#weight' => 3,
    );
    if (module_exists('clone') && !$this
      ->getInstanceId()) {
      $form['node']['clone_and_reference'] = array(
        '#title' => t('Clone and reference'),
        '#type' => 'checkbox',
        '#description' => t('This will clone the selected content first.'),
        '#default_value' => $config['clone_and_reference'],
        '#weight' => 4,
        '#states' => array(
          'visible' => array(
            ':input[name="use_existing_node"]' => array(
              'checked' => TRUE,
            ),
          ),
        ),
      );
    }
    $form['node']['use_node_title'] = array(
      '#type' => 'checkbox',
      '#title' => t('Use existing title'),
      '#description' => t("Use the referenced content's title as this course object's title."),
      '#default_value' => $config['use_node_title'],
      '#weight' => 5,
    );
    $form['node']['private'] = array(
      '#title' => t('Private'),
      '#description' => $this
        ->hasNodePrivacySupport() ? t('This content will not be available to users who are not enrolled in this course.') : t('You must enable content_access and acl in order to restrict course content to users who are enrolled in this course.'),
      '#type' => 'checkbox',
      '#default_value' => $config['private'],
      '#disabled' => !$this
        ->hasNodePrivacySupport(),
      '#weight' => 6,
    );
    $nid = $this
      ->getInstanceId();
    if ($nid) {
      $node = node_load($nid);
      $link = l(t("'%title' [node id %nid]", array(
        '%title' => $node->title,
        '%nid' => $node->nid,
      )), "node/{$node->nid}", array(
        'attributes' => array(
          'target' => '_blank',
          'title' => t('Open in new window'),
        ),
        'html' => TRUE,
      ));
      $form['node']['instance']['#description'] = t('Currently set to !link', array(
        '!link' => $link,
      ));
    }

    // Block deletion of self-referencing node instances.
    if (!empty($config['instance']) && $config['instance'] == $this
      ->getCourseNid()) {
      $form['delete']['delete_instance']['#disabled'] = TRUE;
      $form['delete']['delete_instance']['#description'] = t('You cannot delete this instance, as it references this Course.');
    }
  }

  /**
   * Validate the options form. Check the node type.
   */
  public function optionsValidate(&$form, &$form_state) {
    parent::optionsValidate($form, $form_state);
    $nid = $form_state['values']['instance'];
    if (empty($nid) && isset($form_state['values']['node_type']) && empty($form_state['values']['node_type'])) {
      form_set_error('node_type', t('Please select a node type.'));
    }
    $missing_node = !preg_match('/^(?:\\s*|(.*) )?\\[\\s*nid\\s*:\\s*(\\d+)\\s*\\]$/', $nid);
    if (($form_state['values']['use_existing_node'] || !$this
      ->isTemporary()) && $missing_node) {
      form_set_error('instance', t('Please select a node.'));
    }
  }
  public function optionsSubmit(&$form, &$form_state) {
    if (isset($form_state['values']['instance'])) {
      $nid = $form_state['values']['instance'];
      if (!is_numeric($nid)) {
        if (preg_match('/^(?:\\s*|(.*) )?\\[\\s*nid\\s*:\\s*(\\d+)\\s*\\]$/', $nid, $matches)) {
          $nid = $matches[2];
        }
      }
      if ($nid) {
        $form_state['values']['instance'] = $nid;
      }
      else {

        // Unset it, or we'll erase the relationship (since the textfield is
        // actually blank).
        unset($form_state['values']['instance']);
      }
    }
    parent::optionsSubmit($form, $form_state);
  }

  /**
   * Grant access to course content before going to it.
   */
  function grant() {
    if ($this
      ->hasNodePrivacySupport()) {
      if ($this
        ->getOption('private')) {
        $uid = $this->user->uid;
        module_load_include('inc', 'content_access', 'content_access.admin');
        $acl_id = content_access_get_acl_id($this->node, 'view');
        acl_add_user($acl_id, $uid);
        acl_node_add_acl($this->node->nid, $acl_id, 1, 0, 0, content_access_get_settings('priority', $this->node->type));
        node_access_acquire_grants($this->node);
      }
    }
  }

  /**
   * Duration expired (or something) - CourseObject is telling us so.
   *
   * @todo this needs to move to a fulfillment class because getUser() in this
   * context is deprecated
   */
  function revoke() {
    if ($this
      ->hasNodePrivacySupport()) {
      if ($this
        ->getOption('private')) {
        $uid = $this->user->uid;
        module_load_include('inc', 'content_access', 'content_access.admin');
        $acl_id = content_access_get_acl_id($this->node, 'view');
        acl_remove_user($acl_id, $uid);
        node_access_acquire_grants($this->node);
      }
    }
  }

  /**
   * On object write, set privacy on this node.
   */
  function save() {
    if ($this
      ->getOption('clone_and_reference')) {
      module_load_include('inc', 'clone', 'clone.pages');
      $new_nid = clone_node_save($this
        ->getOption('instance'));
      $this
        ->setInstanceId($new_nid);
      $this
        ->setOption('clone_and_reference', 0);
    }
    parent::save();
    $privacy_enabled = $this
      ->hasNodePrivacySupport() && $this
      ->getOption('private');
    $external_node = $this
      ->getInstanceId() > 0 && $this
      ->getInstanceId() != $this
      ->getCourseNid();
    if ($privacy_enabled && $external_node) {

      // Ensure that per-node access is enabled.
      $type_settings = content_access_get_settings('all', $this->node->type);
      $type_settings['per_node'] = 1;
      content_access_set_settings($type_settings, $this->node->type);

      // Remove "view" permissions to everyone on this node.
      $settings = content_access_get_per_node_settings($this->node);
      $grant_roles = array_filter(variable_get('course_object_private_roles', array()));
      $settings['view'] = $grant_roles;
      content_access_save_per_node_settings($this->node, $settings);

      // Resave node to update access.
      node_access_acquire_grants($this->node);
    }
  }

  /**
   * Freeze data to persist over cloning/exporting.
   * @return array
   *   An array of data to be frozen.
   */
  function freeze() {
    if ($this->node->nid != $this
      ->getCourse()
      ->getNode()->nid) {

      // Don't freeze the course, if this course is part of the objects.
      $ice = new stdClass();
      $ice->node = $this->node;
      return $ice;
    }
  }

  /**
   * Thaw data frozen from an earlier export/clone.
   *
   * @param array $data
   *   Unfrozen data.
   *
   * @return int
   *   The new instance ID.
   */
  function thaw($ice) {
    $this->node = $ice->node;
    unset($this->node->nid);
    unset($this->node->vid);
    node_save($this->node);
    return $this->node->nid;
  }
  function getCloneAbility() {
    return t('%object will be cloned as a node. Results may vary.', array(
      '%object' => $this
        ->getTitle(),
    ));
  }

  /**
   * Get the object title, or return this object's node's title if the option
   * is set.
   */
  function getTitle() {
    if ($this
      ->getOption('use_node_title') && $this->node) {
      return $this->node->title;
    }
    else {
      return parent::getTitle();
    }
  }
  function getNode() {
    return node_load($this->instance);
  }
  function getOptionsSummary() {
    $summary = parent::getOptionsSummary();
    if ($this
      ->getOption('instance') == $this
      ->getCourseNid()) {

      // Don't show the edit instance link when the instance is
      // self-referencing.
      unset($summary['instance']);
    }
    return $summary;
  }

  /**
   * When setting our instance ID, also set the node.
   */
  function setInstanceId($id) {
    parent::setInstanceId($id);

    // Cloning breaks without a reset.
    $this->node = node_load($id, NULL, TRUE);
  }

  /**
   * Show a warning if this object has an instance, but the node does not exist.
   */
  function getWarnings() {
    $warnings = parent::getWarnings();
    if ($this
      ->getInstanceId() && !$this
      ->getNode()) {
      $warnings[] = t('The content associated with this object has been deleted.<br/>Saving the course will create new content from the object settings.');
    }
    return $warnings;
  }

  /**
   * Deny access to objects without content.
   */
  function access($op = 'view', $account = NULL) {
    if ($op == 'take' && !$this
      ->getNode()) {
      return FALSE;
    }
    return parent::access($op, $account);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
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::getDatabaseFields protected function Return an array of database fields. This determines what fields should be serialized instead of stored.
CourseHandler::getId function
CourseHandler::getOption public function Get an handler option's value.
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::buildContent function Builds a structured array representing the entity's content. Overrides Entity::buildContent
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::getCourseNid function Get the course node ID this CourseObject belongs to.
CourseObject::getFulfillment public function Get the user's fulfillment for this course object.
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::getModule function Get the module that provides this course object.
CourseObject::getOptions public function Get options, with session options, except weight, having precedence. Overrides CourseHandler::getOptions
CourseObject::getReport function Let the course object provide its own reports. 4
CourseObject::getReports function Let the course object provide its own reports. 4
CourseObject::getStatus public function Get the user's status in this course object. 2
CourseObject::getUrl public function Return the URL to the course object router.
CourseObject::getUser public function
CourseObject::hasPolling public function Specify whether fulfillment uses asynchronous polling. 2
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::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::renderOptionsSummary public function Get all course object implementations of getOptionsSummary().
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::setModule function Set the module that provides this course object.
CourseObject::setUser public function
CourseObject::take public function 6
CourseObject::takeCourseObject final public function Take a course object.
CourseObject::unEnroll function Remove any records associated with this course object for the user. 2
CourseObject::uri public function Generate URI from course object. Overrides Entity::uri
CourseObjectNode::$node protected property
CourseObjectNode::access function Deny access to objects without content. Overrides CourseObject::access
CourseObjectNode::context public static function Course context handler callback. Overrides CourseObject::context
CourseObjectNode::create public function Create a node and set it as this course object's node. Overrides CourseObject::create 5
CourseObjectNode::delete public function Destroy the node instance. Overrides CourseObject::delete
CourseObjectNode::freeze function Freeze data to persist over cloning/exporting. Overrides CourseObject::freeze 3
CourseObjectNode::getCloneAbility function Returns an translated error message if this object has issues with cloning. Overrides CourseObject::getCloneAbility 6
CourseObjectNode::getEditUrl public function Get the URL to edit this course object, if any. Overrides CourseObject::getEditUrl
CourseObjectNode::getNode function
CourseObjectNode::getNodeInstances public static function When passed a node, this method should return the "parent" nodes that are contained in a course outline. 2
CourseObjectNode::getNodeTypes abstract public function Return a list of valid node types. 7
CourseObjectNode::getOptionsSummary function Get core options summary. Overrides CourseObject::getOptionsSummary 2
CourseObjectNode::getTakeType public function Simple node course object behavior is to just redirect to the node. Overrides CourseObject::getTakeType 3
CourseObjectNode::getTakeUrl public function Get the URL to take this course object, if any. Overrides CourseObject::getTakeUrl 1
CourseObjectNode::getTitle function Get the object title, or return this object's node's title if the option is set. Overrides CourseObject::getTitle
CourseObjectNode::getViewUrl public function Get the URL to view this course object, if any. Overrides CourseObject::getViewUrl
CourseObjectNode::getWarnings function Show a warning if this object has an instance, but the node does not exist. Overrides CourseHandler::getWarnings 2
CourseObjectNode::grant function Grant access to course content before going to it. Overrides CourseObject::grant
CourseObjectNode::hasNodePrivacySupport public function
CourseObjectNode::optionsDefinition public function Define configuration elements and their defaults. Overrides CourseObject::optionsDefinition 4
CourseObjectNode::optionsForm public function Default options form for all course objects. Overrides CourseObject::optionsForm 5
CourseObjectNode::optionsSubmit public function Save object configs to cache. Overrides CourseObject::optionsSubmit 2
CourseObjectNode::optionsValidate public function Validate the options form. Check the node type. Overrides CourseObject::optionsValidate
CourseObjectNode::revoke function Duration expired (or something) - CourseObject is telling us so. Overrides CourseObject::revoke
CourseObjectNode::save function On object write, set privacy on this node. Overrides CourseObject::save 1
CourseObjectNode::setInstanceId function When setting our instance ID, also set the node. Overrides CourseObject::setInstanceId
CourseObjectNode::setNode public function Set the node and instance ID (node ID) of this CourseObjectNode.
CourseObjectNode::thaw function Thaw data frozen from an earlier export/clone. Overrides CourseObject::thaw 3
CourseObjectNode::__construct public function Overrides CourseHandler::__construct
Entity::$defaultLabel protected property 1
Entity::$entityInfo protected property
Entity::$entityType protected property
Entity::$idKey protected property
Entity::$wrapper protected property
Entity::bundle public function Returns the bundle of the entity. Overrides EntityInterface::bundle
Entity::defaultLabel protected function Defines the entity label if the 'entity_class_label' callback is used. 1
Entity::defaultUri protected function Override this in order to implement a custom default URI and specify 'entity_class_uri' as 'uri callback' hook_entity_info().
Entity::entityInfo public function Returns the info of the type of the entity. Overrides EntityInterface::entityInfo
Entity::entityType public function Returns the type of the entity. Overrides EntityInterface::entityType
Entity::export public function Exports the entity. Overrides EntityInterface::export
Entity::getTranslation public function Gets the raw, translated value of a property or field. Overrides EntityInterface::getTranslation
Entity::hasStatus public function Checks if the entity has a certain exportable status. Overrides EntityInterface::hasStatus
Entity::identifier public function Returns the entity identifier, i.e. the entities name or numeric id. Overrides EntityInterface::identifier
Entity::internalIdentifier public function Returns the internal, numeric identifier. Overrides EntityInterface::internalIdentifier
Entity::isDefaultRevision public function Checks whether the entity is the default revision. Overrides EntityInterface::isDefaultRevision
Entity::label public function Returns the label of the entity. Overrides EntityInterface::label
Entity::setUp protected function Set up the object instance on construction or unserializiation.
Entity::view public function Generate an array for rendering the entity. Overrides EntityInterface::view
Entity::wrapper public function Returns the EntityMetadataWrapper of the entity. Overrides EntityInterface::wrapper
Entity::__sleep public function Magic method to only serialize what's necessary.
Entity::__wakeup public function Magic method to invoke setUp() on unserialization.