View source
<?php
abstract class CourseObject extends CourseHandler {
protected $accessMessages = array();
protected $readOnlyOptionsCache = array();
public function overrideNavigation() {
return array();
}
public function hasPolling() {
return FALSE;
}
public function overrideOutlineListItem(&$item) {
}
public function access($op = 'view', $account = NULL) {
ctools_include('plugins');
$access = FALSE;
if (!in_array($op, array(
'see',
'take',
'view',
), TRUE)) {
return FALSE;
}
if (empty($account)) {
$account = $GLOBALS['user'];
}
if (!$this
->isEnabled() || !$this
->isVisible()) {
return FALSE;
}
switch ($op) {
case 'see':
$access = TRUE;
break;
case 'take':
case 'view':
if (!$account->uid) {
return FALSE;
}
$course = clone $this
->getCourse();
$course
->setActive($this
->getId());
$courseObjects = $course
->getObjects();
$result = course_access_course('take', $this
->getCourse()
->getNode(), $account);
if (!course_enrollment_check($this
->getCourseNid(), $account->uid) || !$result['success']) {
return FALSE;
}
else {
if (reset($courseObjects)
->getId() == $this
->getId()) {
$access = TRUE;
}
}
if (!$course
->getPrev()) {
$access = TRUE;
}
elseif (!$course
->getPrev()
->isRequired() || $course
->getPrev()
->isSkippable()) {
$access = TRUE;
$objects = array_reverse($course
->getObjects());
$check = FALSE;
foreach ($objects as $object) {
if (!$object
->isEnabled()) {
continue;
}
if ($check) {
if ($object
->isRequired() && !$object
->isSkippable()) {
if (!$object
->getFulfillment($account)
->isComplete()) {
$access = FALSE;
break;
}
else {
$access = TRUE;
break;
}
}
}
if ($object
->getId() == $this
->getId()) {
$check = 1;
}
}
}
elseif ($course
->getPrev()
->getFulfillment($account)
->isComplete()) {
$access = TRUE;
}
}
foreach (ctools_get_plugins('course', 'course_object_access') as $key => $plugin) {
$class = ctools_plugin_get_class($plugin, 'handler');
$accessPlugin = new $class();
$accessPlugin
->setCourseObject($this);
$accessPlugin
->setType($key);
$ret = $accessPlugin
->{$op}($account);
if ($ret === FALSE) {
$access = $ret;
}
}
return $access;
}
public function isActive() {
return $this
->getCourse()
->current()
->getId() == $this
->getId();
}
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,
'data' => '',
'plugins' => array(),
);
return $defaults;
}
public function optionsForm(&$form, &$form_state) {
ctools_include('plugins');
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['nid'] = array(
'#type' => 'value',
'#value' => $this
->getCourseNid(),
);
$form['course_tabs']['#type'] = 'vertical_tabs';
$form['title'] = array(
'#title' => t('Title & description'),
'#type' => 'fieldset',
'#group' => 'course_tabs',
'#weight' => 0,
);
$form['outline'] = array(
'#type' => 'fieldset',
'#title' => t('Settings'),
'#group' => 'course_tabs',
'#weight' => 1,
);
$form['plugins']['access'] = array(
'#type' => 'fieldset',
'#title' => 'Access',
'#group' => 'course_tabs',
'#weight' => 4,
);
$form['delete'] = array(
'#type' => 'fieldset',
'#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.'),
);
$form['delete']['delete_button'] = array(
'#value' => t('Delete'),
'#weight' => 999,
'#type' => 'submit',
'#submit' => array(
array(
$this,
'setDelete',
),
array(
$this,
'optionsSubmit',
),
),
'#limit_validation_errors' => array(),
);
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',
);
if (db_query("SELECT count(coid) FROM {course_outline} WHERE module = :module AND object_type = :object_type AND instance = :instance", array(
':module' => $config['module'],
':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' => 'fieldset',
'#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 the learner\'s result as an input into the final course grade. The final course grade is an average of all the learner\'s included results.'),
'#default_value' => $config['grade_include'],
'#type' => 'checkbox',
);
}
$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'] = 'fieldset';
foreach (ctools_get_plugins('course', 'course_object_access') as $key => $plugin) {
$form['plugins']['access']['#tree'] = TRUE;
$form['plugins']['access'][$key] = array(
'#title' => $plugin['title'],
'#type' => 'fieldset',
'#tree' => TRUE,
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$class = ctools_plugin_get_class($plugin, 'handler');
$courseAccess = new $class();
$courseAccess
->setCourseObject($this);
$courseAccess
->setType($key);
$access_form = $access_form_state = array();
$form['plugins']['access'][$key] += $courseAccess
->optionsForm($access_form, $access_form_state);
}
$form['actions']['update'] = array(
'#value' => t('Update'),
'#weight' => 999,
'#type' => 'submit',
'#validate' => array(
array(
$this,
'optionsValidate',
),
),
'#submit' => array(
array(
$this,
'optionsSubmit',
),
),
);
}
public function setDelete(&$form, &$form_state) {
$form_state['values']['delete'] = 1;
$form_state['values']['delete_instance'] = !empty($form_state['input']['delete_instance']);
}
public function optionsValidate(&$form, &$form_state) {
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]);
}
}
public function optionsSubmit(&$form, &$form_state) {
ctools_include('plugins');
$uniqid = $this
->getId();
$nid = $this
->getCourseNid();
course_editing_start($this
->getCourse());
if (isset($form_state['values']['hidden'])) {
$form_state['values']['hidden'] = $form_state['values']['hidden'] != 1;
}
foreach ($form_state['values'] as $key => $value) {
if (isset($form_state['values'][$key]) && !is_null($form_state['values'][$key])) {
$_SESSION['course'][$nid]['editing'][$uniqid][$key] = $form_state['values'][$key];
}
}
if (isset($form_state['values']['plugins'])) {
foreach (ctools_get_plugins('course', 'course_object_access') as $key => $plugin) {
$_SESSION['course'][$nid]['editing'][$uniqid]['plugins']['access'][$key] = $form_state['values']['plugins']['access'][$key];
}
}
$this
->setOptions($_SESSION['course'][$nid]['editing'][$uniqid]);
}
public function getOptionsSummary() {
$summary = parent::getOptionsSummary();
$options = $this
->getOptions();
$uniqid = $options['uniqid'];
if ($options['enabled']) {
$summary['enabled'] = t('Enabled');
}
else {
$summary['enabled'] = '<span class="warning">' . t('Not enabled') . '</span>';
}
if (!$options['hidden']) {
$summary['hidden'] = t('Visible in outline');
}
else {
$summary['hidden'] = '<span class="warning">' . t('Not visible in outline') . '</span>';
}
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>';
}
$editUrl = $this
->getEditUrl();
if (!empty($editUrl)) {
$text = t('Edit instance');
$summary['instance'] = l($text, $editUrl, array(
'external' => TRUE,
'query' => drupal_get_destination(),
));
}
elseif ($this
->isTemporary()) {
$summary['instance'] = '<span class="warning">' . t('Save course to edit object') . '</span>';
}
$viewUrl = $this
->getViewUrl();
if (!empty($viewUrl)) {
$text = t('View instance');
$summary['instance_view'] = l($text, $viewUrl, array(
'external' => TRUE,
'query' => drupal_get_destination(),
));
}
if (!empty($options['delete'])) {
$dest = "node/{$options['nid']}/course-object/nojs/{$this->getId()}/restore";
$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 = ctools_ajax_text_button(t('Restore'), $dest, $restore_text);
$summary['delete'] = '<span class="error">' . $text . '</span>' . ' ' . $restore;
}
return $summary;
}
public function renderOptionsSummary() {
ctools_include('plugins');
$summary = $this
->getOptionsSummary();
foreach (ctools_get_plugins('course', 'course_object_access') as $key => $plugin) {
}
$rendered_summary = '';
if (!empty($summary)) {
$rendered_summary = $html = '<div class="description">' . theme('item_list', array(
'title' => '',
'items' => $summary,
)) . '</div>';
}
return $rendered_summary;
}
public function getOptions() {
$options = parent::getOptions();
$sessionDefaults = array();
if (isset($options['nid']) && isset($options['coid']) && isset($_SESSION['course'][$options['nid']]['editing'][$options['coid']])) {
$sessionDefaults += array_filter((array) $_SESSION['course'][$options['nid']]['editing'][$options['coid']], array(
$this,
'optionFilter',
));
unset($sessionDefaults['weight']);
}
return array_merge($options, (array) $sessionDefaults);
}
public function getReadOnlyOptions() {
global $user;
$account = $user;
if (!isset($this->readOnlyOptionsCache[$account->uid])) {
$options = $this
->getOptions();
ctools_include('plugins');
foreach (ctools_get_plugins('course', 'course_object_access') as $key => $plugin) {
$class = ctools_plugin_get_class($plugin, 'handler');
$accessPlugin = new $class();
$accessPlugin
->setCourseObject($this);
$accessPlugin
->setType($key);
$accessPlugin
->alterOptions($options, $account);
}
$this->readOnlyOptionsCache[$account->uid] = $options;
}
return $this->readOnlyOptionsCache[$account->uid];
}
public function getReadOnlyOption($key) {
return $this
->getReadOnlyOptions()[$key];
}
private function optionFilter($a) {
return !is_null($a);
}
public final function takeCourseObject() {
global $user;
$_SESSION['course']['active'] = $this
->getCourseNid();
$_SESSION['course'][$this
->getCourseNid()]['taking']['active'] = $this
->getId();
if ($this
->access('take')) {
$this
->getFulfillment($user)
->grant();
if (!$this
->getFulfillment($user)
->getOption('date_started')) {
$this
->getFulfillment($user)
->setOption('date_started', REQUEST_TIME)
->save();
}
}
else {
$this
->getFulfillment($user)
->revoke();
return FALSE;
}
$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:
drupal_goto($url, array(
'external' => TRUE,
));
}
}
public function getTakeType() {
return 'content';
}
public function take() {
return t('This should be overridden by the module to return course content.');
}
public function getUrl() {
return 'node/' . $this
->getCourseNid() . '/course-object/' . $this
->getId();
}
protected function getTakeUrl() {
}
public function getEditUrl() {
}
public function getViewUrl() {
}
public function isRequired() {
return (bool) $this
->getReadOnlyOption('required');
}
public function isSkippable() {
return (bool) $this
->getReadOnlyOption('skippable');
}
public function isEnabled() {
return (bool) $this
->getReadOnlyOption('enabled');
}
public function isVisible() {
return (bool) (!$this
->getReadOnlyOption('hidden'));
}
function isGraded() {
return FALSE;
}
public function getStatus() {
}
public function getFulfillment($account) {
if ($entities = entity_load('course_object_fulfillment', FALSE, array(
'coid' => $this
->identifier(),
'uid' => $account->uid,
))) {
return reset($entities);
}
else {
return entity_create('course_object_fulfillment', array(
'coid' => $this
->identifier(),
'uid' => $account->uid,
'module' => $this->module,
'object_type' => $this->object_type,
));
}
}
function getInstanceId() {
return $this
->getOption('instance');
}
function setInstanceId($id) {
return $this
->setOption('instance', $id);
}
function getCourseNid() {
return intval($this
->getOption('nid'));
}
public function setCourse($course) {
if (is_numeric($course)) {
$this
->setOption('nid', $course);
}
else {
$this
->setOption('nid', $course
->getNode()->nid);
}
return $this;
}
function getCourse() {
$course = entity_load_single('course', $this->nid);
return $course;
}
function getModule() {
return $this
->getOption('module');
}
function getComponentName() {
$handlers = course_get_handlers('object');
return $handlers[$this
->getModule()][$this
->getComponent()]['name'];
}
function getComponent() {
return $this
->getOption('object_type');
}
function setModule($module) {
return $this
->setOption('module', $module);
}
function setComponent($component) {
return $this
->setOption('object_type', $component);
}
function setId($coid) {
return $this
->setOption('coid', $coid);
}
public function create() {
}
public function delete() {
}
function getTitle() {
$object_info = course_get_handlers('object');
if (!$this
->getOption('title')) {
$title = $object_info[$this
->getOption('module')][$this
->getOption('object_type')]['name'];
$this
->setOption('title', $title);
}
return $this
->getOption('title');
}
function poll() {
}
function getReports() {
return array(
'default' => array(
'title' => 'Overview',
),
);
}
function getReport($key) {
if ($key == 'default') {
return array(
'title' => 'Overview',
'content' => views_embed_view('course_object_report', 'default', $this
->getCourseNid(), $this
->getId()),
);
}
}
function freeze() {
}
function thaw($ice) {
}
function getCloneAbility() {
return FALSE;
}
public function save() {
$this
->getTitle();
if ($ice = $this
->getOption('freeze')) {
$this
->setInstanceId($this
->thaw($ice));
$this
->setOption('freeze', NULL);
}
if (!$this
->getInstanceId()) {
$this
->create();
}
if (strpos($this
->getId(), 'course_object_') !== FALSE) {
$this
->setId(NULL);
}
if (is_object($this
->getCourse())) {
$this
->getCourse()
->resetCache();
}
return parent::save();
}
function isTemporary() {
return strpos($this
->getId(), 'course_object_') === 0;
}
public static function getMaxOccurences() {
return FALSE;
}
public static function context() {
}
function buildContent($view_mode = 'full', $langcode = NULL) {
global $user;
$content = parent::buildContent($view_mode, $langcode);
$step = array();
$step['id'] = 'course-object-' . $this
->getId();
$step['image'] = '';
$step['status'] = '';
if ($this
->access('see', $user)) {
if ($this
->access('take', $user)) {
$step['link'] = $this
->getUrl();
$step['class'][] = 'accessible';
$step['status'] = t('Not started');
if ($this
->getFulfillment($user)
->isComplete()) {
$step['class'][] = 'completed';
$step['status'] = t('Complete');
$step['image'] = 'misc/watchdog-ok.png';
}
elseif ($this
->getFulfillment($user)
->getId()) {
$step['status'] = t('In progress');
$step['class'][] = 'in-progress';
$step['image'] = '';
}
if ($this
->getCourse()
->getActive() === $this) {
$step['class'][] = 'active';
}
}
else {
$step['class'] = array(
'not-accessible',
);
$step['status'] = implode('<br/>', $this
->getAccessMessages());
}
if ($this
->isRequired()) {
$step['class'][] = 'required';
}
$step['class'][] = drupal_html_class($this
->getComponent() . '_' . $this
->getModule());
$img = !empty($step['image']) ? theme('image', array(
'path' => $step['image'],
'alt' => strip_tags($step['status']),
)) : '';
$content['course_outline_image'] = array(
'#markup' => $img,
);
$content['course_outline_link'] = array(
'#markup' => $this
->access('take', $user) ? l($this
->getTitle(), $this
->getUrl()) : $this
->getTitle(),
);
$content['course_outline_status'] = array(
'#markup' => $step['status'],
'#prefix' => '<div>',
'#suffix' => '</div>',
);
}
return $content;
}
public function uri() {
return array(
'path' => 'node/' . $this->nid . '/course-object/' . $this
->identifier(),
);
}
public function setOption($option, $value) {
$this->readOnlyOptionsCache = [];
return parent::setOption($option, $value);
}
}