View source
<?php
abstract class CourseObject extends CourseHandler {
private $course = NULL;
private $courseObjectFulfillment = NULL;
protected $user = NULL;
function __construct($config = array(), $account = NULL, Course $course = NULL) {
$config = (array) $config;
$this->serializedField = 'data';
$this->handlerType = 'course_object';
$this->primaryKey = 'coid';
$this->table = 'course_outline';
parent::__construct($config);
if (!$account) {
global $user;
$account = $user;
}
$this->user = $account;
if ($course) {
$this->course = $course;
}
$this->courseObjectFulfillment = new CourseObjectFulfillment($this, $account);
}
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 (!$account) {
global $user;
$account = $user;
}
switch ($op) {
case 'see':
$access = !$this
->getOption('hidden') && $this
->getOption('enabled');
break;
case 'take':
if (!$account->uid) {
return FALSE;
}
case 'view':
$course = clone $this
->getCourse();
$course
->setActive($this
->getId());
$courseObjects = $course
->getObjects();
if ($courseObjects && $courseObjects[0]
->getId() == $this
->getId()) {
$access = course_enrolment_check($this
->getCourseNid(), $account->uid);
}
if ($course
->getPrev() && !$course
->getPrev()
->isRequired()) {
$access = TRUE;
$objects = array_reverse($course
->getObjects());
$check = FALSE;
foreach ($objects as $object) {
if ($check) {
if ($object
->isRequired()) {
if (!$object
->getFulfillment()
->isComplete()) {
$access = FALSE;
break;
}
else {
$access = TRUE;
break;
}
}
}
if ($object
->getId() == $this
->getId()) {
$check = 1;
}
}
}
if ($course
->getPrev() && $course
->getPrev()
->getFulfillment()
->isComplete()) {
$access = TRUE;
}
}
foreach (ctools_get_plugins('course', 'access') as $key => $plugin) {
$class = ctools_plugin_get_class($plugin, 'handler');
$accessPluginDefaults = array();
if (isset($this->config['plugins']['access'][$key])) {
$accessPluginDefaults = (array) $this->config['plugins']['access'][$key];
}
$accessPlugin = new $class();
if ($accessPluginDefaults) {
$accessPlugin
->setOptions($accessPluginDefaults);
}
$accessPlugin
->setCourseObject($this);
$ret = $accessPlugin
->{$op}();
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,
'delete' => 0,
'delete_instance' => 0,
'grade_include' => 0,
'instance' => NULL,
);
return $defaults;
}
public function optionsForm(&$form, &$form_state) {
ctools_include('dependent');
ctools_include('plugins');
parent::optionsForm($form, $form_state);
$config = $this
->getOptions();
$form['header']['#value'] = t("<h2>Settings for %t</h2>", array(
'%t' => $this
->getTitle(),
));
$form['uniqid'] = array(
'#type' => 'hidden',
'#value' => arg(4),
);
$form['nid'] = array(
'#type' => 'hidden',
'#value' => arg(1),
);
$form['title'] = array(
'#title' => 'Title',
'#type' => 'textfield',
'#size' => 100,
'#default_value' => check_plain($config['title']),
);
$form['enabled'] = array(
'#title' => 'Enabled',
'#type' => 'checkbox',
'#default_value' => $config['enabled'],
);
$form['hidden'] = array(
'#title' => t('Visible in outline'),
'#type' => 'checkbox',
'#default_value' => !$config['hidden'],
);
$form['required'] = array(
'#title' => t('Completion required'),
'#type' => 'checkbox',
'#default_value' => $config['required'],
);
$form['delete'] = array(
'#title' => t('Delete'),
'#type' => 'checkbox',
'#default_value' => $config['delete'],
);
if (!empty($config['instance'])) {
$form['delete_instance'] = array(
'#title' => t('Also delete related object instance(s)'),
'#type' => 'checkbox',
'#default_value' => $config['delete_instance'],
'#process' => array(
'ctools_dependent_process',
),
'#dependency' => array(
'edit-delete' => array(
1,
),
),
);
$sql = "SELECT count(coid) FROM {course_outline} WHERE module = '%s' AND object_type = '%s' AND instance = '%s'";
if (db_result(db_query($sql, $config['module'], $config['object_type'], $config['instance'])) > 1) {
$form['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' => 'Grading',
'#type' => 'fieldset',
'#description' => t('Settings for graded objects.'),
);
$form['grading']['grade_include'] = array(
'#title' => 'Include in final course grade',
'#description' => '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',
);
}
$form['plugins']['#tree'] = TRUE;
$form['plugins']['#weight'] = 998;
$form['plugins']['access']['#title'] = 'Access plugins';
$form['plugins']['access']['#type'] = 'fieldset';
foreach (ctools_get_plugins('course', 'access') as $key => $plugin) {
$form['plugins']['access']['#tree'] = TRUE;
$form['plugins']['access'][$key] = array(
'#title' => $plugin['title'],
'#type' => 'fieldset',
'#tree' => TRUE,
'#collapsible' => TRUE,
);
$class = ctools_plugin_get_class($plugin, 'handler');
$courseAccess = new $class();
if (!empty($config['plugins']['access'][$key])) {
$courseAccess
->setOptions($config['plugins']['access'][$key]);
}
$courseAccess
->setCourseObject($this);
$form['plugins']['access'][$key] += $courseAccess
->optionsForm();
}
$form['update'] = array(
'#value' => t('Update'),
'#weight' => 999,
'#type' => 'submit',
);
}
public function optionsValidate(&$form, &$form_state) {
ctools_include('plugins');
foreach (ctools_get_plugins('course', 'access') as $key => $plugin) {
$values =& $form_state['values']['plugins']['access'][$key];
$class = ctools_plugin_get_class($plugin, 'handler');
$instance = new $class($values);
$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();
$form_state['values']['hidden'] = $form_state['values']['hidden'] != 1;
foreach (array_keys($this
->getOptions()) as $key) {
if (!is_null($form_state['values'][$key])) {
$_SESSION['course'][$nid]['editing'][$uniqid][$key] = $form_state['values'][$key];
}
}
foreach (ctools_get_plugins('course', '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]);
if ($form_state['ajax']) {
$commands = array();
$title = '<div id="title-' . $uniqid . '">' . $form_state['values']['title'] . '</div>';
$commands[] = ctools_ajax_command_replace("#title-{$uniqid}", $title);
$html = '<div id="summary-' . $uniqid . '">' . $this
->renderOptionsSummary() . '</div>';
$commands[] = ctools_ajax_command_replace("#summary-{$uniqid}", $html);
if ($form_state['values']['delete']) {
$commands[] = ctools_ajax_command_attr("#row-{$uniqid}", 'class', 'deleted');
}
$commands[] = ctools_modal_command_dismiss();
ctools_ajax_render($commands);
}
}
public function getOptionsSummary() {
$summary = parent::getOptionsSummary();
$options = $this
->getOptions();
$form = array();
$this
->optionsForm($form, $form_state);
$uniqid = $options['uniqid'];
if ($options['enabled']) {
$summary['enabled'] = $form['enabled']['#title'];
}
else {
$summary['enabled'] = '<span class="warning">' . t('Not enabled') . '</span>';
}
if (!$options['hidden']) {
$summary['hidden'] = $form['hidden']['#title'];
}
else {
$summary['hidden'] = '<span class="warning">' . t('Not visible in outline') . '</span>';
}
if ($options['required']) {
$summary['required'] = $form['required']['#title'];
}
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>';
}
if (!empty($options['delete'])) {
$dest = "node/{$options['nid']}/course-object/nojs/{$uniqid}/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', 'access') as $key => $plugin) {
}
$rendered_summary = '';
if (!empty($summary)) {
$rendered_summary = $html = '<div class="description">' . theme('item_list', $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',
));
}
return array_merge($options, (array) $sessionDefaults);
}
private function optionFilter($a) {
return !is_null($a);
}
public final function takeCourseObject() {
$_SESSION['course']['active'] = $this
->getCourseNid();
$_SESSION['course'][$this
->getCourseNid()]['taking']['active'] = $this
->getId();
if ($this
->access('take')) {
$this
->grant();
if (!$this
->getFulfillment()
->getOption('date_started')) {
$this
->getFulfillment()
->setOption('date_started', time());
}
}
else {
$this
->revoke();
return FALSE;
}
$this
->getFulfillment()
->save();
$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:
session_write_close();
header("Location: {$url}");
exit;
}
}
public function getTakeType() {
return 'content';
}
public function take() {
return '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 isRequired() {
return (bool) $this
->getOption('required');
}
function isGraded() {
return FALSE;
}
public function getStatus() {
}
public function getFulfillment() {
return $this->courseObjectFulfillment;
}
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);
$courseNode = node_load($course);
$this->course = course_get_course($courseNode);
}
else {
$this->course = $course;
$this
->setOption('nid', $course
->getNode()->nid);
}
return $this;
}
function getCourse() {
if (!$this->course) {
$nid = $this
->getCourseNid();
if ($nid) {
$this->course = new Course(node_load($nid), $this->user);
}
}
return $this->course;
}
function getModule() {
return $this
->getOption('module');
}
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);
}
function setUser($user) {
$this->user = $user;
}
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 grant() {
}
function revoke() {
}
function getReports() {
return array();
}
function getReport($key) {
return array();
}
function freeze() {
}
function thaw() {
}
function getCloneAbility() {
return t('The course object %object cannot be cloned. A new instance will be created.', array(
'%object' => $this
->getTitle(),
));
}
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);
}
return parent::save();
}
function unEnroll() {
}
function isTemporary() {
return strpos($this
->getId(), 'course_object_') === 0;
}
public static function getMaxOccurences() {
return FALSE;
}
}
class CourseObjectFulfillment extends CourseHandler {
private $courseObject;
function __construct(CourseObject $courseObject, $user) {
$this->handlerType = 'course_object_fulfillment';
$this->table = 'course_outline_fulfillment';
$this->primaryKey = 'cofid';
$this->serializedField = 'info';
$this->courseObject = $courseObject;
$sql = "SELECT * FROM {course_outline_fulfillment} WHERE coid = %d AND uid = %d";
$fulfillment = db_fetch_array(db_query($sql, $this->courseObject
->getId(), $user->uid));
$this
->setOptions($fulfillment);
$this
->setOption('coid', $this->courseObject
->getId());
$this
->setOption('uid', $user->uid);
}
function isComplete() {
return (bool) $this
->getOption('complete');
}
function setComplete($complete = 1) {
if (!$this
->getOption('date_completed')) {
$this
->setOption('date_completed', time());
}
return $this
->setOption('complete', $complete);
}
function setGrade($grade) {
return $this
->setOption('grade_result', $grade);
}
function getGrade() {
return $this
->getOption('grade_result');
}
function getCourseObject() {
return $this->courseObject;
}
public function delete() {
$sql = "DELETE FROM {course_outline_fulfillment} WHERE cofid = %d";
db_query($sql, $this
->getId());
}
public function save() {
parent::save();
$this
->getCourseObject()
->getCourse()
->track();
}
}
abstract class CourseObjectNode extends CourseObject {
protected $node;
public function __construct($object, $user = NULL, $course = NULL) {
parent::__construct($object, $user, $course);
$this->node = node_load($this
->getInstanceId());
}
public function hasNodePrivacySupport() {
return module_exists('content_access') && module_exists('acl');
}
public abstract function getNodeTypes();
public function getTakeType() {
return 'redirect';
}
public function getTakeUrl() {
if ($this
->getInstanceId()) {
return url("node/{$this->node->nid}");
}
}
public function getEditUrl() {
if ($this
->getInstanceId()) {
return url("node/{$this->node->nid}/edit");
}
}
public function create() {
$node = new stdClass();
$node->type = $this
->getOption('node_type');
$node->title = $this
->getTitle();
$node->uid = $this->user->uid;
node_save($node);
$this
->setNode($node);
}
public function setNode($node) {
$this->node = $node;
$this
->setInstanceId($this->node->nid);
}
public function delete() {
node_delete($this
->getInstanceId());
}
public function optionsDefinition() {
$defaults = parent::optionsDefinition();
$defaults['private'] = 0;
$options = array_intersect_key(node_get_types('names'), $this
->getNodeTypes());
$defaults['node_type'] = key($options);
return $defaults;
}
public function optionsForm(&$form, &$form_state) {
parent::optionsForm($form, $form_state);
$config = $this
->getOptions();
if (!$this
->getInstanceId()) {
$types = drupal_map_assoc($this
->getNodeTypes());
$options = array_intersect_key(node_get_types('names'), $types);
if (count($options) > 1) {
array_unshift($options, '- select -');
}
$form['node_type'] = array(
'#title' => 'Create node',
'#type' => 'select',
'#options' => $options,
'#description' => 'Selecting a node type will automatically create this node and link it to this course object.',
'#default_value' => $config['node_type'],
);
if (count($options) > 1) {
$form['node_type']['#required'] = TRUE;
}
}
$form['node'] = array(
'#type' => 'fieldset',
'#title' => 'Node',
'#description' => 'Settings for node-based objects.',
);
$form['node']['instance'] = array(
'#title' => 'Existing node',
'#autocomplete_path' => 'course/autocomplete/node/' . implode(',', $this
->getNodeTypes()),
'#type' => 'textfield',
'#description' => t('Use an existing node instead of creating a new one.'),
);
$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(),
);
$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,
));
}
}
public function optionsValidate(&$form, &$form_state) {
if (isset($form_state['values']['node_type']) && empty($form_state['values']['node_type'])) {
form_set_error('node_type', 'Please select a node type.');
}
}
public function optionsSubmit(&$form, &$form_state) {
$nid = $form_state['values']['instance'];
if (!is_numeric($nid)) {
preg_match('/^(?:\\s*|(.*) )?\\[\\s*nid\\s*:\\s*(\\d+)\\s*\\]$/', $nid, $matches);
$nid = $matches[2];
}
if ($nid) {
$form_state['values']['instance'] = $nid;
}
else {
unset($form_state['values']['instance']);
}
parent::optionsSubmit($form, $form_state);
}
public function getWarnings() {
$warnings = parent::getWarnings();
if ($this
->getOption('private')) {
$settings = variable_get('content_access_settings', array());
if (!$settings['per_node'][$this
->getComponent()]) {
$warnings[] = t('%t is set to Private, but the content type %c does not have access control lists enabled. Users will not be able to acces this content. Please visit !l to set up content access settings.', array(
'%t' => $this
->getTitle(),
'%c' => $this
->getComponent(),
'!l' => l('Access control', "admin/content/node-type/{$this->getComponent()}/access"),
));
}
}
return $warnings;
}
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_save($this->node);
}
}
}
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_save($this->node);
}
}
}
function save() {
parent::save();
if ($this
->hasNodePrivacySupport() && $this
->getOption('private')) {
$global_settings = content_access_get_settings();
$global_settings['per_node'][$this->node->type] = 1;
content_access_set_settings($global_settings);
$settings = content_access_get_per_node_settings($this->node);
$settings['view'] = array();
content_access_save_per_node_settings($this->node, $settings);
node_save($this->node);
}
}
function freeze() {
if ($this->node->nid != $this
->getCourse()
->getNode()->nid) {
$ice = new stdClass();
$ice->node = $this->node;
return $ice;
}
}
function thaw($ice) {
$this->node = $ice->node;
unset($this->node->nid);
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(),
));
}
}