course.module in Course 6
Same filename and directory in other branches
course.module Core functionality for Courses.
File
course.moduleView source
<?php
/**
* @file course.module
* Core functionality for Courses.
*/
// Course outline functions
require_once drupal_get_path('module', 'course') . '/includes/course.outline.inc';
// Course exporting functions
require_once drupal_get_path('module', 'course') . '/includes/course.exporting.inc';
/**
* Implements hook_menu().
*/
function course_menu() {
$items = array();
// Base configuration.
$items['admin/settings/course'] = array(
'title' => 'Course',
'description' => 'Configure courses.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'course_settings_overview',
),
'access arguments' => array(
'administer course',
),
'file' => 'includes/course.settings.inc',
);
// Default tab for settings.
$items['admin/settings/course/overview'] = array(
'title' => 'Overview',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['course/autocomplete/node/%'] = array(
'page callback' => 'course_object_autocomplete_node',
'access arguments' => array(
'access content',
),
'page arguments' => array(
3,
4,
),
);
// Course settings handler forms. This gives organization and consistency to
// form placement for each module that defines settings handlers through
// hook_course_handlers().
$modules = course_get_handlers('settings');
$default_set = array();
$handlers = array();
$packages = array();
// Flatten each module's settings handlers into one array, so we can get info
// about any other handler-package while looping over each handler below.
foreach ($modules as $module_key => $settings) {
if (is_array($settings)) {
// Define
foreach ($settings as $handler_key => $handler_info) {
// Manually set the implementing module key. It would be unnecessary to
// force implementing modules to set this, since we can get it here.
$handler_info['module'] = $module_key;
// Manually set which package the handler belongs in. If one is not
// defined, assume the handler is it's own package.
$handler_info['package'] = isset($handler_info['package']) ? $handler_info['package'] : $handler_key;
// Build the array of handlers. Add this handler with a combined key,
// so module defined settings handlers can avoid namespace conflicts.
$module_handler_key = "{$module_key}_{$handler_key}";
$handlers[$module_handler_key] = $handler_info;
// Build a reverse array of handler keys - keyed by package - so we can
// get package info below when we need it. If there are duplicate
// handler/package keys, use the first one for grouping the others.
$package_key = $handler_info['package'] ? $handler_info['package'] : $handler_key;
if (!isset($packages[$package_key])) {
$packages[$package_key] = $module_handler_key;
}
}
}
}
// Loop over each handler, and set tabs accordingly.
foreach ($handlers as $module_handler_key => $handler_info) {
// Get package info for this handler.
$package_key = $handler_info['package'];
$package_info = $handlers[$packages[$package_key]];
// Define a path for the handler's specified package.
$package_router = "admin/settings/course/{$package_key}";
// Define a path for the handler.
$handler_router = "admin/settings/course/{$package_key}/{$module_handler_key}";
// Add the handler item, either as the default page content
// (MENU_NORMAL_ITEM will work with the MENU_DEFAULT_LOCAL_TASK below)
// or as one of the other MENU_LOCAL_TASK tabs). If this is the deafult
// page content, the router path and title will be taken from the
// handler which defined the package.
$item_router = !isset($default_set[$package_key]) ? $package_router : $handler_router;
$item_title = !isset($default_set[$package_key]) ? $package_info['name'] : $handler_info['name'];
$item_type = !isset($default_set[$package_key]) ? MENU_NORMAL_ITEM : MENU_LOCAL_TASK;
$items[$item_router] = array(
'title' => $item_title,
'description' => $handler_info['description'],
'access arguments' => array(
'administer course',
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
$handler_info['callback'],
),
'type' => $item_type,
);
// Append file info to $items, if specified.
$file_info = array();
if (isset($handler_info['file'])) {
// Define the item 'file' key.
$items[$item_router]['file'] = $handler_info['file'];
// Define the item 'file path' key.
if (isset($handler_info['file path'])) {
// Use the path if provided. If not provided, we need to specify the
// handler provider module path, otherwise hook_menu() assumes
// 'file path' is the path to it's implementing module (Course).
$items[$item_router]['file path'] = $handler_info['file path'] ? $handler_info['file path'] : drupal_get_path('module', $handler_info['module']);
}
}
// Check if a default tab has already been set for this module.
if (!isset($default_set[$package_key])) {
// Add the default tab with the handler router item and name. We do
// this here so the first handler settings form always displays as the
// default page content at the module router item path.
$items[$handler_router] = array(
'title' => $handler_info['name'],
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
// Flag MENU_DEFAULT_LOCAL_TASK as set for this module.
$default_set[$package_key] = TRUE;
}
}
// Per course user type selection.
$items['node/%course/course-user-type'] = array(
'title' => 'Choose user type',
'description' => 'Allow the learner to choose their user type.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'course_user_type_form',
1,
),
'access callback' => 'user_is_logged_in',
'type' => MENU_CALLBACK,
);
// Landing page for course completion.
$items['node/%course/course-outline'] = array(
'title' => 'Course outline',
'access arguments' => array(
'edit courses',
),
'page arguments' => array(
'course_outline_overview_form',
),
'page callback' => 'drupal_get_form',
'type' => MENU_LOCAL_TASK,
'file' => 'includes/course.outline.inc',
);
// Landing page for course completion.
$items['node/%course/course-complete'] = array(
'title' => 'Course completion',
'access callback' => TRUE,
'page arguments' => array(
1,
),
'page callback' => 'course_outline_show_complete_links',
'type' => MENU_CALLBACK,
'file' => 'includes/course.outline.inc',
);
// Display the 'Take course' menu item as a tab or link, depending.
$items['node/%course/takecourse'] = array(
'title' => 'Take course',
'title callback' => 'course_takecourse_title',
'title arguments' => array(
1,
),
'description' => 'Take course.',
'page callback' => 'course_take_course',
'page arguments' => array(
1,
),
'access callback' => 'course_take_course_menu_access',
'access arguments' => array(
1,
),
'type' => variable_get('course_takecourse_tab_display', 1) ? MENU_LOCAL_TASK : MENU_CALLBACK,
);
// Display the 'Course settings' menu item as a tab or link, depending.
$items['node/%course/coursesettings'] = array(
'title' => 'Course settings',
'description' => 'Course settings.',
'page callback' => 'course_edit_course',
'page arguments' => array(
1,
),
'access callback' => 'course_settings_menu_access',
'access arguments' => array(
1,
),
'type' => MENU_CALLBACK,
);
// Reports page listing each course object.
$items['node/%course/course-reports/objects'] = array(
'title' => 'Course objects',
'type' => MENU_LOCAL_TASK,
'page callback' => 'course_object_reports_page',
'page arguments' => array(
1,
),
'access arguments' => array(
'access course reports',
),
'file' => 'includes/course.reports.inc',
);
// Global report area
$items['admin/reports/course'] = array(
'title' => 'Course reports',
'description' => 'View and download course information.',
'access arguments' => array(
'access all course reports',
),
'page callback' => 'system_admin_menu_block_page',
'file path' => drupal_get_path('module', 'system'),
'file' => 'system.admin.inc',
);
// Course object
$items['node/%course/course-object/%course_object'] = array(
'title' => 'Course object router',
'page callback' => 'course_object_take',
'page arguments' => array(
3,
),
'access callback' => 'course_access_object',
'access arguments' => array(
1,
3,
),
);
// Course object edit
$items['node/%course/course-object/%ctools_js/%course_object/options'] = array(
'title' => 'Course object settings',
'page callback' => 'course_object_options',
'page arguments' => array(
1,
3,
4,
),
'access callback' => 'node_access',
'access arguments' => array(
'update',
1,
),
);
// Course object edit
$items['node/%course/course-object/%ctools_js/%course_object/restore'] = array(
'page callback' => 'course_object_restore',
'page arguments' => array(
1,
3,
4,
),
'access callback' => 'node_access',
'access arguments' => array(
'update',
1,
),
'type' => MENU_CALLBACK,
);
// AHAH handler.
$items['node/%course/course-outline/%ctools_js/more/%'] = array(
'page callback' => 'course_outline_overview_js_more',
'access callback' => 'node_access',
'access arguments' => array(
'update',
1,
),
'type' => MENU_CALLBACK,
'page arguments' => array(
1,
3,
5,
),
);
$items['node/%course/course-object/%course_object/%ctools_js/nav'] = array(
'page callback' => 'course_ajaj_fulfullment_check',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
'page arguments' => array(
1,
3,
5,
),
);
return $items;
}
/**
* Implements hook_course_handlers().
*
* @see course_menu()
* @see course_settings_overview()
*/
function course_course_handlers() {
$outline = 'includes/course.outline.inc';
$settings = 'includes/course.settings.inc';
return array(
'outline' => array(
'course' => array(
'name' => t('Course'),
'description' => t('Stock outline display.'),
'callback' => 'course_outline_list',
'file' => $outline,
),
'none' => array(
'name' => t('None'),
'description' => t('No outline provided (placeholder course).'),
),
),
'context' => array(
'node' => array(
'callback' => 'course_context',
),
),
'settings' => array(
'appearance' => array(
'name' => t('Appearance'),
'description' => t('Configure the course appearance, including outline style, disabling regions, and <em>enroll</em> and <em>take course</em> links.'),
'callback' => 'course_settings_appearance_form',
'file' => $settings,
),
'objects' => array(
'name' => t('Course objects'),
'description' => t('Course object settings.'),
'callback' => 'course_settings_objects_form',
'file' => $settings,
),
'user_types' => array(
'name' => t('User types'),
'description' => t('Configure course user types.'),
'callback' => 'course_user_type_settings_form',
'file' => $settings,
),
),
);
}
/**
* Get course handlers.
*
* @param string $type
* (optional) The course handler type to return.
* If no type is specified, all types are returned.
*
* @return array
* A merged, structured array of course handlers, optionally limited by type.
*
* @return array
* An array of hook implementations keyed by module name, containing:
* - A single handler type definition, if the $type parameter is passed.
* - Or an associative array of all course handler definitions keyed by type.
*/
function course_get_handlers($type = NULL, $flush = FALSE) {
static $all = array();
if (!$all || $flush) {
// Allow modules to define handlers that extend Course functionality.
// Do not use module_invoke_all() here because we need to know which module
// is providing the 'object' handler type. This is to avoid namespace
// conflicts between multiple modules providing a 'quiz' object for example.
$hook = 'course_handlers';
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
$handlers = $function();
// Allow modules to alter each other's list of handlers.
drupal_alter($hook, $handlers, $module);
if (isset($handlers) && is_array($handlers)) {
$all[$module] = $handlers;
}
}
}
if (isset($type)) {
// Loop through each module's result again, and rebuild the array including
// only the specified handler type. We do this again so we can static cache
// the hook invocation and function calls above.
$filtered = array();
foreach ($all as $module => $handlers) {
if (isset($handlers[$type])) {
$filtered[$module] = $handlers[$type];
}
}
// Return the keyed array of implementations, each filtered to include only
// the specified handler type definition.
return $filtered;
}
else {
// Return the keyed array of all implementations.
return $all;
}
}
/**
* Menu access for course object router.
*/
function course_access_object($node, $courseObject) {
global $user;
$course = course_get_course($node, $user);
$courseObject
->setCourse($course);
$course
->setActive($courseObject
->getId());
return $courseObject
->access('take');
}
/**
* Fulfillment check callback.
*
* This function is polled from nav.js to check remote fulfillments for external
* learning objects.
*/
function course_ajaj_fulfullment_check($node, $courseObject, $js = FALSE) {
$courseObject
->poll();
if (course_node_is_course($node)) {
course_set_context($node);
}
module_load_include('inc', 'course', 'includes/course.block');
// Bust cache.
course_get_course($node, $courseObject
->getCourse()
->getUser(), TRUE);
$block = _course_block_navigation_view();
drupal_json(array(
'content' => $block['content'],
'complete' => $courseObject
->getFulfillment()
->isComplete(),
));
}
/**
* Start an editing session for this course. Populate the session from
* persistent storage.
*
* @param Course $course
* A Course.
*/
function course_editing_start($course) {
if (empty($_SESSION['course'][$course
->getNode()->nid]['editing'])) {
// Start editing cache from what we have in DB.
foreach ($course
->getObjects() as $courseObject) {
$_SESSION['course'][$course
->getNode()->nid]['editing'][$courseObject
->getId()] = $courseObject
->getOptions();
}
}
}
/**
* Callback to restore a course object temporarily removed from outline overview
* form.
*/
function course_object_restore($node, $js, CourseObject $courseObject) {
$course = course_get_course($node);
$courseObject
->setCourse($course);
$uniqid = $courseObject
->getId();
$nid = $node->nid;
// Set the session value.
$_SESSION['course'][$nid]['editing'][$uniqid]['delete'] = 0;
$_SESSION['course'][$nid]['editing'][$uniqid]['delete_instance'] = 0;
if ($js) {
ctools_include('ajax');
// Perform ajax operations on the overview form, after restore.
$commands = array();
// Reset summary.
// @todo reload just this row. How?
//$commands[] = ctools_ajax_command_replace("#row-{$uniqid}", $html);
$commands[] = ctools_ajax_command_reload();
ctools_ajax_render($commands);
}
else {
drupal_goto("node/{$nid}/course-outline");
}
}
/**
* Page callback: Handles object options form for both ctools modal and nojs.
*
* @param stdClass $node
* A course node object loaded from course_load().
* @param boolean $js
* Detects if ajax is enabled, loaded from ctools_js_load().
* @param courseObject $courseObject
* A courseObject object, loaded from course_object_load().
*
*/
function course_object_options($node, $js, $courseObject) {
$course = course_get_course($node);
$courseObject
->setCourse($course);
if ($js) {
ctools_include('ajax');
ctools_include('modal');
$form_state = array(
'ajax' => TRUE,
'title' => t("Settings for %t", array(
'%t' => $courseObject
->getTitle(),
)),
);
$form_state['args'][] = $courseObject;
$output = ctools_modal_form_wrapper('course_object_options_form', $form_state);
if (empty($output)) {
$output[] = ctools_modal_command_loading();
$output[] = ctools_modal_command_dismiss();
}
ctools_ajax_render($output);
}
else {
return drupal_get_form('course_object_options_form', $courseObject);
}
}
/**
* Form API builder for course object options.
*
* @param array $form_state
* Form state.
* @param courseObject $courseObject
* An initialized courseObject object.
*
* @see course_object_options_form_validate()
* @see course_object_options_form_submit()
* @see course_object_options()
* @ingroup forms
*
* @return array
* The FAPI array.
*/
function course_object_options_form(&$form_state, $courseObject) {
$form = array();
$courseObject
->optionsForm($form, $form_state);
return $form;
}
/**
* Form validation handler for course_object_options_form().
*
* @see course_object_options_form_submit()
*/
function course_object_options_form_validate(&$form, &$form_state) {
if ($form_state['values']['uniqid']) {
$nid = $form_state['values']['nid'];
// Get course object from session/database.
$courseObject = course_object_load($form_state['values']['uniqid']);
$courseObject
->setId($form_state['values']['uniqid']);
$courseObject
->optionsValidate($form, $form_state);
}
}
/**
* Form submission handler for course_object_options_form().
*
* @see course_object_options_form_validate()
*/
function course_object_options_form_submit(&$form, &$form_state) {
if ($form_state['values']['uniqid']) {
$nid = $form_state['values']['nid'];
// Get course object from session/database.
$courseObject = course_object_load($form_state['values']['uniqid']);
$course = $courseObject
->getCourse();
// Start editing session.
course_editing_start($course);
$courseObject
->setId($form_state['values']['uniqid']);
$courseObject
->optionsSubmit($form, $form_state);
}
}
/**
* Menu loader for course objects, in the context of a course.
*/
function course_object_load($coid) {
global $user;
$nid = arg(0) == 'node' && is_numeric(arg(1)) ? arg(1) : 0;
// Stored course object.
$courseObject = course_get_course_object_by_id($coid, $user);
if ($courseObject && $nid) {
// If we're loading this from a menu loader, set the course.
$courseObject
->setCourse($nid);
}
return $courseObject;
}
/**
* Take the course object.
*
* @return string
* Themed output.
*/
function course_object_take($courseObject) {
drupal_set_title($courseObject
->getTitle());
// Preserve course tabs
$course = $courseObject
->getCourse();
$item = menu_get_item($course
->getUrl());
menu_set_item(NULL, $item);
return $courseObject
->takeCourseObject();
}
/**
* Implements hook_menu_alter().
*
* Add a default reports tab if views isn't enabled.
*/
function course_menu_alter(&$items) {
if (!module_exists('views')) {
$default = $items['node/%course/course-reports/objects'];
$items['node/%course/course-reports'] = $default;
$items['node/%course/course-reports']['title'] = 'Course reports';
$items['node/%course/course-reports']['type'] = MENU_LOCAL_TASK;
$items['node/%course/course-reports/objects']['type'] = MENU_DEFAULT_LOCAL_TASK;
}
}
/**
* Implements hook_block().
*/
function course_block($op = 'list', $delta = 0) {
switch ($op) {
case 'list':
$info = array(
'outline' => array(
'info' => t('Course: Outline'),
'cache' => BLOCK_NO_CACHE,
),
'navigation' => array(
'info' => t('Course: Navigation'),
'cache' => BLOCK_NO_CACHE,
),
);
return $info;
case 'configure':
case 'view':
case 'save':
module_load_include('inc', 'course', 'includes/course.block');
$function = "_course_block_{$delta}_{$op}";
if (function_exists($function)) {
return $function();
}
break;
}
}
/**
* Menu title handler for the Take course tab.
*
* @return string
* "Review course" or "Take course", depending on the current user's
* completion status.
*/
function course_takecourse_title($node) {
global $user;
$report = course_report_load($node, $user);
return $user->uid > 1 && isset($report->complete) && $report->complete ? t('Review course') : t('Take course');
}
/**
* Menu loader: check if node is a Course.
*/
function course_load($arg) {
$node = node_load($arg);
return course_node_is_course($node) ? $node : FALSE;
}
/**
* Implements hook_perm().
*
* Define permissions to take courses and edit course settings.
*/
function course_perm() {
return array(
// Manage course settings
'administer course',
// Take courses
'access course',
// Edit course objects (not the node itself)
'edit courses',
// Can user get to the course reports area
'access course reports',
// Can use view all course reports
'access all course reports',
);
}
/**
* Menu access callback to determins if the take course button should display
* on the course node.
*
* This differs from course_take_course_access() as it only returns a boolean.
*
* @param object $node
* The course node.
*
* @see course_uc_token_values()
*/
function course_take_course_menu_access($node) {
global $user;
static $courses = array();
if (!isset($courses[$node->nid])) {
// Allow modules to restrict menu access to the take course tab.
$hooks = module_invoke_all('course_has_takecourse', $node, $user);
$courses[$node->nid] = !in_array(FALSE, $hooks);
}
return $courses[$node->nid];
}
/**
* Menu access callback to determine if the course settings should tab should
* display on the course node.
*
* This differs from course_settings_access() as it only returns a boolean.
*/
function course_settings_menu_access($node) {
global $user;
static $courses = array();
if (!isset($courses[$node->nid])) {
// Allow modules to restrict menu access to the course setting tab.
$hooks = module_invoke_all('course_has_settings', $node, $user);
$courses[$node->nid] = !in_array(FALSE, $hooks) && course_settings_access($node);
}
return $courses[$node->nid];
}
/**
* Determine if taking this course should be restricted.
*
* @param object $node
* By reference. The course node.
*
* @return boolean|array
* Either FALSE, or an array containing:
* - success: Boolean. Indicates whether or not the user has permission to
* take this course.
* - message: String. If success is FALSE, a message to display to the user.
*/
function course_take_course_access($node, $account = NULL, $flush = FALSE) {
if (!$account) {
global $user;
$account = $user;
}
static $courses = array();
// Don't let anonymous users see /takecourse. Also keeps from being indexed.
if (arg(2) == 'takecourse' && !user_is_logged_in() && $_SERVER['SCRIPT_NAME'] != '/cron.php') {
drupal_set_message(t('You must login or register before taking this course.'));
drupal_goto('user/login', drupal_get_destination());
}
if (!isset($courses[$node->nid]) || $flush) {
$courses[$node->nid]['success'] = TRUE;
// Allow modules to determine if this course should be restricted.
$hooks = module_invoke_all('can_take_course', $node, $account);
foreach ($hooks as $key => $hook) {
if (!$hook) {
// Ok. Old style blocker. But look for messages.
$courses[$node->nid] = FALSE;
}
if (is_array($hook) && !$hook['success']) {
// New style blocker, return immediately.
$courses[$node->nid] = $hook;
return $hook;
}
}
}
if (is_array($courses[$node->nid])) {
return $courses[$node->nid];
}
else {
return array(
'success' => $courses[$node->nid],
'message' => "Old style blocker",
);
}
}
/**
* Callback for checking course settings permission.
*/
function course_settings_access($node) {
global $user;
return (user_access('edit own course products') || user_access('edit own course content')) && $node->uid == $user->uid || user_access('edit courses');
}
/**
* Implements hook_can_take_course().
*
* Check for built-in access restrictions (enrollment, release/expiration).
*
* @param object $node The course node.
* @param object $user The user to check.
*/
function course_can_take_course($node, $user) {
if (!node_access('view', $node)) {
return array(
array(
'success' => FALSE,
'header' => t('Access denied'),
'message' => t('You do not have permission to take this course.'),
),
);
}
$sql = "SELECT * FROM {course_enrolment} WHERE nid = %d AND uid = %d";
if ($row = db_fetch_object(db_query($sql, $node->nid, $user->uid))) {
if ($row->enrol_end > 0 && time() > $row->enrol_end) {
return array(
array(
'success' => FALSE,
'message' => 'Sorry, your enrollment has expired for this course.',
),
);
}
}
if (isset($node->course['close']) && $node->course['close']) {
if (time() > $node->course['close']) {
return array(
array(
'success' => FALSE,
'message' => 'Sorry, this course is expired.',
),
);
}
}
}
/**
* Implements hook_nodeapi().
*
* When a course is saved, handles changes to the course outline and the
* creation of external courses.
*
* Renders the "take course" button on view.
*/
function course_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
switch ($op) {
case 'view':
if (course_node_is_course($node)) {
// Render take course button.
$show = variable_get('course_take_course_button_show', array());
if ($teaser && !empty($show['teaser']) || $page && !empty($show['page'])) {
$node->content['course']['#value'] = course_render_button($node);
}
}
break;
case 'insert':
if (course_node_is_course($node)) {
if (!isset($node->course)) {
$node->course = array();
}
}
case 'update':
if (course_node_is_course($node)) {
$record = $node->course;
$record['nid'] = $node->nid;
// Support built-in non-cck date fields.
$builtin_dates = array(
'open',
'close',
);
// Check whether each element has a submitted value, and convert to a
// Unix timestamp before saving to the database.
foreach ($builtin_dates as $element) {
if (!empty($node->course[$element])) {
$record[$element] = !empty($node->validated) ? strtotime($node->course[$element]) : $node->course[$element];
}
}
// Add configurable dates to the node object for easy retrieval.
// Support both configurable cck and built-in non-cck date fields.
$cck_dates = array(
'open' => 'course_start_date_' . $node->type,
'close' => 'course_expiration_date_' . $node->type,
'live_from_date' => 'course_live_from_date_' . $node->type,
'live_to_date' => 'course_live_to_date_' . $node->type,
);
// Check whether each variable is set and the field exists on the
// content type. If so, load that field's value to the course object,
// overriding the coresponding column from the database.
foreach ($cck_dates as $key => $variable) {
$settings = @unserialize(variable_get($variable, array()));
$field_exists = module_exists('content') && isset($settings['field']) && content_fields($settings['field'], $node->type) != FALSE;
if ($field_exists) {
$value = $node->{$settings['field']}[0][$settings['value']];
if (!empty($value)) {
$date = new DateTime("{$value} UTC");
$value = $date
->format('U');
}
$record[$key] = $value;
}
}
$existing = db_result(db_query('SELECT 1 FROM {course_node} WHERE nid = %d', $node->nid));
$update = $existing ? array(
'nid',
) : array();
drupal_write_record('course_node', $record, $update);
// Support cloning.
course_handle_clone($node);
// Save the course objects - necessary for programmatic course creation.
if (isset($node->course['objects'])) {
$course = course_get_course($node);
course_save_objects($node->course['objects'], $course);
}
}
break;
case 'load':
if (course_node_is_course($node)) {
if ($course = db_fetch_array(db_query('SELECT * FROM {course_node} WHERE nid = %d', $node->nid))) {
// Attach additional module provided info to $node->course.
$hooks = module_invoke_all('course_nodeapi_extra', $node, 'load');
$load = array_merge($course, $hooks);
foreach ($load as $key => $data) {
$node->course[$key] = $data;
}
}
// Load the course outline to the node.
$sql = "SELECT * FROM {course_outline}\n WHERE nid = %d\n ORDER BY weight ASC";
$result = db_query($sql, $node->nid);
$objects = array();
while ($object = db_fetch_object($result)) {
foreach ($object as $key => $value) {
$objects[$object->coid]->{$key} = $value;
}
}
$node->course['objects'] = $objects;
}
break;
case 'delete':
if (course_node_is_course($node)) {
// Clean up course specific settings and enrollments when a course is
// deleted.
db_query("DELETE FROM {course_node} WHERE nid = %d", $node->nid);
db_query("DELETE FROM {course_enrolment} WHERE nid = %d", $node->nid);
}
break;
}
}
/**
* Saves course objects.
*
* @param array $objects
* An array of course object definitions.
* @param Course $course
* (optional) An instantiated Course, from course_get_course().
*/
function course_save_objects(array $objects, Course $course = NULL) {
foreach ($objects as $object) {
// Check if this course object already exists in the database.
if (isset($object->coid)) {
// Check if this object does not belong to the current node.
if ($object->nid != $course
->getNode()->nid) {
// We are importing or cloning. Ensure the necessary keys are empty,
// in order to prepare a new object using this object's definitions.
$unset = array(
'coid',
'nid',
'uuid',
);
foreach ($unset as $key) {
if (isset($object->{$key})) {
unset($object->{$key});
}
}
// Replace the nid key, to properly associate the current course node
// with this course object.
$object->nid = $course
->getNode()->nid;
// Clean out serialized data field.
$unset_data_keys = array(
'uniqid',
'uuid',
);
if (isset($object->data) && ($data = unserialize($object->data))) {
foreach ($unset_data_keys as $key) {
if (isset($data[$key])) {
unset($data[$key]);
}
}
$object->data = serialize($data);
}
}
}
// Set options for this object.
if ($prepareObject = course_get_course_object($object, NULL, NULL, NULL, $course)) {
$available_options = $prepareObject
->getOptions();
$options = array();
foreach ($object as $key => $value) {
// Check if this key is a valid option.
if (isset($available_options[$key])) {
$options[$key] = $value;
}
}
// Set the options.
$prepareObject
->setOptions($options);
// Save the object, creating new instances, if applicable.
$prepareObject
->save();
}
}
}
/**
* Check the permissions of showing the take course button, and return the HTML.
*/
function course_render_button($node) {
global $user;
$can_enrol = course_enrol_access($node, $user);
if ($can_enrol['success']) {
// User can self-enrol and take the course. Show the button.
return course_take_course_button_html($node);
}
}
/**
* Generate a button for taking the course.
*/
function course_take_course_button_html(&$node) {
// Allow modules to provide the course button.
$course_button = module_invoke_all('course_button', $node);
if (isset($course_button[0])) {
return $course_button[0];
}
else {
$link = l(t('Take Course'), "node/{$node->nid}/takecourse");
return '<div class="action-link">' . $link . '</div>';
}
}
/**
* Enrols a user in a course.
*
* Timestamp is by design 0, so a user may purchase a course but start taking
* it later.
*
* @param object $node
* By reference. The course node.
* @param object $user
* By reference. The enrolling user.
* @param string $from
* The type of enrollment, if applicable. {course_enrolment}.enrollmenttype.
* @param string $code
* The access code used to enroll. {course_enrolment}.code.
* @param integer $status
* The enrolment status. {course_enrolment}.status.
*/
function course_enrol($node, $account = NULL, $from = NULL, $code = NULL, $status = 1) {
if (!$account) {
global $user;
$account = $user;
}
if (course_node_is_course($node)) {
$enroll = array(
'nid' => $node->nid,
'uid' => $account->uid,
'enrollmenttype' => $from,
'status' => $status,
'code' => $code,
);
if (isset($node->course['duration']) && $node->course['duration'] > 0) {
// Set enrolment end to now + the duration of the course.
$enroll['enrol_end'] = time() + $node->course['duration'] * 86400;
}
$enroll = (object) $enroll;
$watchdog_variables = array(
'!uid' => $account->uid,
'!nid' => $node->nid,
);
if (!course_enrolment_check($node->nid, $account->uid)) {
// User is not enrolled yet.
watchdog('course_enrol', 'Enrolling user !uid into !nid', $watchdog_variables);
course_enrolment_save($enroll);
$op = 'insert';
}
else {
watchdog('course_enrol', 'Re-enrolling user !uid into !nid', $watchdog_variables);
// return drupal_write_record('course_enrolment', $enroll, array('nid', 'uid'));
$op = 'update';
}
// @todo figure $op out.
// Notify modules about a course enrollment.
module_invoke_all('course_enrol', $node, $account, $from, $code, $status);
return $enroll;
}
else {
return FALSE;
}
}
/**
* Un-enroll the user.
*
* Deletes course report entries, course enrollments, and object fulfillment
* records.
*
* @param object $node
* A course node.
* @param object $user
* A user.
* @return bool
* TRUE if user is un-enrolled, FALSE if node is not a course.
*/
function course_unenrol(&$node, &$user) {
if (course_node_is_course($node)) {
$course = course_get_course($node, $user);
$course
->unenroll();
$sql = "DELETE FROM {course_report} WHERE nid = %d AND uid = %d";
db_query($sql, $node->nid, $user->uid);
$sql = "DELETE FROM {course_enrolment} WHERE nid = %d AND uid = %d";
db_query($sql, $node->nid, $user->uid);
// Find all course objects in this course and delete the fulfillment.
$sql = "SELECT coid FROM {course_outline} WHERE nid = %d";
$values = array();
$result = db_query($sql, $node->nid);
while ($row = db_fetch_object($result)) {
$values[] = $row->coid;
}
if (count($values)) {
$values[] = $user->uid;
$placeholders = db_placeholders($values);
$sql = "DELETE FROM {course_outline_fulfillment} WHERE coid IN ({$placeholders}) AND uid = %d";
db_query($sql, $values);
}
// Notify other modules after course unenrollment.
module_invoke_all('course_unenrol', $node, $user);
$watchdog_variables = array(
'!uid' => $user->uid,
'!nid' => $node->nid,
);
watchdog('course_enrol', 'Removed user !uid from !nid', $watchdog_variables);
return TRUE;
}
else {
return FALSE;
}
}
/**
* Check if the user has enrolled in a course.
*
* @param mixed $nid
* A course node ID.
* @param mixed $uid
* A user ID.
*
* @return bool
* TRUE if the user is enrolled, FALSE otherwise.
*/
function course_enrolment_check($nid, $uid) {
$sql_check = "SELECT 1 FROM {course_enrolment} WHERE nid = %d AND uid = %d AND status = %d";
$query = db_query($sql_check, $nid, $uid, 1);
return db_result($query) > 0;
}
/**
* Load an enrollment from a node ID and user ID.
*
* @param int $nid
* Enrollment ID, or node ID.
* @param int $uid
* User ID.
*
* @return mixed
* Enrollment object or FALSE
*/
function course_enrolment_load($nid, $uid = NULL) {
if (is_object($nid)) {
$nid = $nid->nid;
}
if (!$uid) {
$sql = "SELECT * FROM {course_enrolment} WHERE eid = %d";
return db_fetch_object(db_query($sql, $nid));
}
if (is_object($uid)) {
$uid = $uid->uid;
}
$sql = "SELECT * FROM {course_enrolment} WHERE nid = %d AND uid = %d";
return db_fetch_object(db_query($sql, $nid, $uid));
}
/**
* Implements hook_enable().
*
* Insert course as product and add admin theme to course settings.
*/
function course_enable() {
// Add course settings to admin theme.
$paths = variable_get('admin_theme_path', '');
if (strpos($paths, 'coursesettings') === FALSE) {
$paths .= "\n*/coursesettings";
variable_set('admin_theme_path', $paths);
}
// Flush autoload caches.
autoload_flush_caches();
}
/**
* Implements hook_form_alter().
*
* Course node settings form.
*
* @todo move this course node settings form to a secondary local task, under
* the course settings tab.
*/
function course_form_alter(&$form, &$form_state, $form_id) {
$node = isset($form['#node']) ? $form['#node'] : NULL;
// Course node settings form.
if (course_node_is_course($node) && strpos($form_id, '_node_form') !== FALSE) {
$form['course']['#tree'] = TRUE;
$form['course']['#type'] = 'fieldset';
$form['course']['#title'] = t('Course settings');
$form['course']['#group'] = TRUE;
// Course outline display handler.
$outlines = array();
$handlers = course_get_handlers('outline');
foreach ($handlers as $outline_handlers) {
if ($outline_handlers) {
foreach ($outline_handlers as $key => $outline_handler) {
$outlines[$key] = $outline_handler['name'];
}
}
}
$form['course']['outline'] = array(
'#title' => t('Available outline displays'),
'#type' => 'select',
'#options' => $outlines,
);
// This is a fake field. It stores the aggregate credit from course_credit.
// @todo...something
$form['course']['credits'] = array(
'#title' => t('Credit hours'),
'#type' => 'textfield',
'#size' => 4,
'#access' => FALSE,
);
if (module_exists('date')) {
$open = variable_get('course_start_date_' . $node->type, array());
$close = variable_get('course_expiration_date_' . $node->type, array());
$form['course']['open'] = array(
'#title' => t('Release date'),
'#type' => module_exists('date_popup') ? 'date_popup' : 'date_text',
'#access' => empty($open['field']),
);
$form['course']['close'] = array(
'#title' => t('Expiration date'),
'#type' => module_exists('date_popup') ? 'date_popup' : 'date_text',
'#access' => empty($close['field']),
);
}
$form['course']['duration'] = array(
'#title' => t('Duration'),
'#type' => 'textfield',
'#size' => 4,
'#description' => t('Length in days a user can remain in the course. Enter 0 for unlimited.'),
);
$form['course']['cid'] = array(
'#title' => t('External learning application course ID'),
'#description' => t('If using an external learning application, the ID of the external course.'),
'#type' => 'textfield',
'#size' => 4,
'#access' => FALSE,
);
$form['course']['external_id'] = array(
'#title' => t('External course ID'),
'#description' => t('Course ID used to relate to an outside system.'),
'#type' => 'textfield',
'#size' => 16,
);
foreach (element_children($form['course']) as $key) {
$form['course'][$key]['#default_value'] = isset($node->course[$key]) ? $node->course[$key] : NULL;
}
if (arg(2) == 'clone') {
$form['course']['clone_type'] = array(
'#title' => t('Course object cloning'),
'#description' => t('"New" will create new instances of all course objects.<br/>"Reference" will link supported content in the old course to the new course.<br/>"Clone" will copy supported course objects, otherwise create new ones.'),
'#type' => 'radios',
'#options' => array(
'clone' => 'Clone',
'reference' => 'Reference',
'new' => 'New',
),
'#default_value' => 'clone',
);
}
// After creating a new course, redirect the user to the course outline
// overview form.
if (empty($node->nid)) {
$form['buttons']['submit']['#submit'][] = 'course_form_submit';
}
}
if (strpos($form_id, 'views_bulk_operations_form') === 0 && strpos($_GET['q'], 'admin/reports/course/overview/select') === 0) {
$form['url']['#default_value'] = url('admin/reports/course/overview/view', array(
'absolute' => TRUE,
));
$form['url']['#type'] = 'hidden';
}
}
/**
* Submit handler for the course node form.
*
* Redirect the user to the outline overview form on new node inserts. Note that
* this fires after the hook_submit() function above.
*/
function course_form_submit($form, &$form_state) {
drupal_set_message(t('Add new items to your course outline using the form below.'));
$form_state['redirect'] = 'node/' . $form_state['nid'] . '/course-outline';
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function course_form_node_type_form_alter(&$form, &$form_state) {
// Alter the node type's configuration form to add our setting.
$form['course'] = array(
'#type' => 'fieldset',
'#title' => t('Course settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#access' => user_access('administer course'),
'#group' => TRUE,
);
$form['course']['course_use'] = array(
'#title' => t('Use as course type'),
'#type' => 'checkbox',
'#default_value' => variable_get("course_use_{$form['#node_type']->type}", 0),
);
// Ctools dependency.
ctools_include('dependent');
$dependent = array(
'#process' => array(
'ctools_dependent_process',
),
'#dependency' => array(
'edit-course-use' => array(
1,
),
),
);
// Configurable date fields.
if (module_exists('date')) {
$options = array();
$options[0] = t('<Not specified>');
$fields = content_fields();
foreach ($fields as $field) {
if ($field['module'] == 'date') {
foreach ($field['columns'] as $column => $value) {
if (in_array($column, array(
'value',
'value2',
))) {
$position = $column == 'value' ? 'From' : 'To';
// Use the same label pattern as date_api_fields() for consistency
// with Views, and in case we support other date options than
// content date fields.
$label = t('Content: !label (!name) - @position date', array(
'!label' => $field['widget']['label'],
'!name' => $field['field_name'],
'@position' => $position,
));
#$key = "{$field['field_name']}[0]['{$column}']";
$key = serialize(array(
'field' => $field['field_name'],
'value' => $column,
));
$options[$key] = $label;
}
}
}
}
// Enduring course dates.
$form['course']['course_start_date'] = array(
'#title' => t('Field to use for enduring-course start date'),
'#description' => t('Select the field to use for enduring-course start date.'),
'#type' => 'select',
'#options' => $options,
'#default_value' => variable_get("course_start_date_{$form['#node_type']->type}", 0),
'#prefix' => '<h3>' . t('Enduring course dates') . '</h3>',
) + $dependent;
$form['course']['course_expiration_date'] = array(
'#title' => t('Field to use for enduring-course expiration date'),
'#description' => t('Select the field to use for enduring-course expiration date.'),
'#type' => 'select',
'#options' => $options,
'#default_value' => variable_get("course_expiration_date_{$form['#node_type']->type}", 0),
) + $dependent;
// Live course dates.
$form['course']['course_live_from_date'] = array(
'#title' => t('Field to use for live-course start date'),
'#description' => t('Select the field to use for live-course start date.'),
'#type' => 'select',
'#options' => $options,
'#default_value' => variable_get("course_live_from_date_{$form['#node_type']->type}", 0),
'#prefix' => '<h3>' . t('Live course dates') . '</h3>',
) + $dependent;
$form['course']['course_live_to_date'] = array(
'#title' => t('Field to use for live-course end date'),
'#description' => t('Select the field to use for live-course end date.'),
'#type' => 'select',
'#options' => $options,
'#default_value' => variable_get("course_live_to_date_{$form['#node_type']->type}", 0),
) + $dependent;
}
}
/**
* Generic Course IFrame function.
*
* @param string $url
* An iframe HTML element src attribute.
* @param string $height
* A string representing an iframe height.
* @param string $class
* A HTML class attribute for the iframe.
*/
function course_iframe($url = NULL, $height = '600px', $class = NULL) {
$style = 'border:none; margin:0; width:100%; height:' . $height . ';';
$iframe = '<iframe name="course-viewer" src="' . $url . '" style="' . $style . '" class="' . $class . '" scrolling="no" frameborder="0" onload="resizeFrame(this);"></iframe>';
// Add JS to resize parent frame. This assumes additional JS on the targeted iframe content.
drupal_add_js(drupal_get_path('module', 'course') . '/js/resizeframe.js');
return $iframe;
}
/**
* Take a course.
*
* - Enroll the user, if allowed.
* - Block the user if not allowed.
* - Fire the outline handler.
*/
function course_take_course($node) {
global $user;
drupal_set_title($node->title);
$result = course_enrol_access($node);
$enrolment = course_enrolment_load($node, $user);
// Check enroll access or if user is already enrolled.
if ($result['success'] || $enrolment->status) {
$result = course_take_course_access($node);
}
if ($result['success'] === TRUE) {
if (empty($enrolment->eid)) {
// User has access to take this course, but they haven't been enrolled. So
// enroll them now.
$enrolment = course_enrol($node, $user);
}
if (empty($enrolment->timestamp)) {
// If user hasn't started course, mark start of enrolment.
$enrolment->timestamp = time();
course_enrolment_save($enrolment);
drupal_set_message(t('Your enrollment in this course has been recorded.'));
}
// Display the configured outline handler output.
$key = isset($node->course['outline']) ? $node->course['outline'] : 'course';
$handlers = course_get_handlers('outline');
foreach ($handlers as $module => $outline_handlers) {
if ($outline_handlers) {
foreach ($outline_handlers as $key2 => $outline_handler) {
if ($key == $key2) {
$callback = $outline_handler['callback'];
}
}
}
}
if (function_exists($callback)) {
$outline = $callback($node, $user);
}
else {
$outline = t('Outline not provided.');
}
if (!$outline) {
$outline = t('No learning objects are available this time.');
}
return $outline;
}
else {
drupal_set_header('HTTP/1.1 403 Forbidden');
drupal_set_title(t('Access denied'));
if (empty($result['message'])) {
return t('Sorry, you do not have access to take this course. (No message provided by module).');
}
return "<h2>" . $result['header'] . "</h2>" . $result['message'];
}
}
/**
* Create or update an enrolment.
*/
function course_enrolment_save($enrolment) {
if ($enrolment->nid && $enrolment->uid) {
if (db_result(db_query('SELECT 1 FROM {course_enrolment} WHERE nid = %d AND uid = %d', $enrolment->nid, $enrolment->uid))) {
drupal_write_record('course_enrolment', $enrolment, array(
'nid',
'uid',
));
}
else {
if (!isset($enrolment->created) || $enrolment->created == 0) {
$enrolment->created = time();
}
drupal_write_record('course_enrolment', $enrolment);
}
}
else {
return FALSE;
}
}
/**
* Get a course object by its unique identifier (sessioned course object).
*
* @param string $uniqid
* Unique identifier.
* @param stdClass $account
* Account to instantiate this course object.
* @param Course $course
* Course to instantiate this course object.
*
* @return CourseObject|FALSE
*/
function _course_get_course_object_by_uniqid($uniqid, $account = NULL, $course = NULL) {
if (!empty($_SESSION['course'])) {
foreach ($_SESSION['course'] as $nid => $session) {
if (isset($session['editing']) && is_array($session['editing'])) {
foreach ($session['editing'] as $coid => $object) {
if ($coid == $uniqid) {
$courseObject = course_get_course_object($object, NULL, NULL, $account, $course);
if (!$course) {
$course = course_get_course(node_load($nid));
$courseObject
->setCourse($course);
return $courseObject;
}
}
}
}
}
}
return FALSE;
}
/**
* Get a course object by its identifier.
*
* @param int $coid
* The numeric ID of the course object.
* @param stdClass $account
* If specified the CourseObject will be loaded with this user (for access and
* fulfillment tracking).
*
* @return CourseObject|FALSE
* A loaded CourseObject or FALSE if no object found.
*/
function course_get_course_object_by_id($coid, $account = NULL, $course = NULL) {
if (!$account) {
global $user;
$account = $user;
}
$available = course_get_handlers('object');
if (!is_numeric($coid)) {
return _course_get_course_object_by_uniqid($coid, $account, $course);
}
$result = db_query('SELECT * FROM {course_outline} WHERE coid = %d', $coid);
if ($row = db_fetch_object($result)) {
$ret = $available[$row->module][$row->object_type];
if (!class_exists($ret['class'])) {
drupal_set_message(t("Could not find class for %m-%c!", array(
'%m' => $row->module,
'%c' => $row->object_type,
)), 'error');
return FALSE;
}
else {
if (!$course) {
$course = new Course(node_load($row->nid), $account);
}
return new $ret['class']($row, $account, $course);
}
}
else {
return FALSE;
}
}
/**
* CourseObject factory. Get a loaded course object from database or build one
* from arguments.
*
* @param mixed $module
* The module name of this course object, or an array resembling a row in the
* {course_outline} table.
* @param string $object_type
* The object type belonging to the module.
* @param string $instance
* The course object instance ID, FROM {course_outline}.instance.
* @param stdClass $account
* The user object. This will instantiate a fulfillment record on the returned
* CourseObject.
* @param Course $course
* The Course to pass to the CourseObject instantiation.
*
* @return CourseObject|FALSE
*/
function course_get_course_object($module, $object_type = NULL, $instance = NULL, $account = NULL, $course = NULL) {
$available = course_get_handlers('object');
$fulfillment = FALSE;
if ($account) {
// Account was passed. We are preparing for fulfillment.
$fulfillment = TRUE;
}
if (!$account) {
global $user;
$account = $user;
}
if (is_array($module)) {
// Cast array passed to an object.
$module = (object) $module;
}
if (is_object($module) && !empty($module->coid)) {
// Passed options with the course object ID set.
$coid = $module->coid;
if (strpos($coid, 'course_object_') === FALSE) {
return course_get_course_object_by_id($coid, $account, $course);
}
}
if (is_numeric($module)) {
$coid = $module;
}
elseif (is_object($module)) {
// This is an already loaded (but not saved) course object.
$outline_entry = $module;
}
elseif (!is_null($instance)) {
// Get the course context.
if (!$course) {
if ($courseNode = course_determine_context($module, $object_type, $instance, TRUE, FALSE)) {
$course = new Course($courseNode, $account);
}
}
// Search for context.
$outline_entries = array();
$result = db_query("SELECT * FROM {course_outline} WHERE module = '%s' AND object_type = '%s' AND instance = '%s'", $module, $object_type, $instance);
while ($row = db_fetch_object($result)) {
$outline_entries[$row->nid] = $row;
}
if ($outline_entries) {
// Found some course objects.
//
// Either the active course is in the courses this instance is in, or, the
// active course wasn't a parent of any course object found, so use the
// first object found.
$coid = $courseNode && $outline_entries[$courseNode->nid] ? $outline_entries[$courseNode->nid]->coid : reset($outline_entries)->coid;
return course_get_course_object_by_id($coid, $account);
}
}
if (!isset($outline_entry)) {
if ($fulfillment) {
// Doing fulfillment, we need a persistent CourseObject.
return FALSE;
}
else {
// Couldn't find context, and not checking for fulfillment. We can safely
// construct a new CourseObject.
$outline_entry = new stdClass();
$outline_entry->module = $module;
$outline_entry->object_type = $object_type;
$outline_entry->instance = $instance;
}
}
$ret = $available[$outline_entry->module][$outline_entry->object_type];
if ($ret['class']) {
$class = $ret['class'];
}
else {
return FALSE;
}
$courseObject = new $class($outline_entry, $account, $course);
if ($courseObject) {
return $courseObject;
}
else {
return FALSE;
}
}
/**
* Get a loaded Course.
*
* @param stdClass $node
* The course node object.
* @param stdClass $account
* The user with which to instantiate course objects and fulfillment.
*
* @return Course
*/
function course_get_course($node, $account = NULL, $flush = FALSE) {
if (!$node) {
return FALSE;
}
if (!$account) {
global $user;
$account = $user;
}
static $courses = array();
if ($flush || !isset($courses[$node->nid]) || !isset($courses[$node->nid][$account->uid])) {
$course = new Course($node, $account);
$courses[$node->nid][$account->uid] = $course;
}
return $courses[$node->nid][$account->uid];
}
/**
* Check if node is a Course.
*
* @param stdClass $node
* A node object or string that indicates the node type to check.
*
* @return bool
*/
function course_node_is_course($node) {
$type = is_object($node) ? $node->type : $node;
return variable_get("course_use_{$type}", 0);
}
/**
* Implements hook_views_plugins().
*/
function course_views_plugins() {
return array(
'argument validator' => array(
'course' => array(
'title' => t('Course'),
'handler' => 'views_plugin_argument_validate_course',
'path' => drupal_get_path('module', 'course') . '/views/plugins',
),
),
);
}
/**
* Implements hook_views_api().
*/
function course_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'course') . '/views',
);
}
/**
* Implements hook_content_extra_fields().
*/
function course_content_extra_fields($type) {
$extras = array();
if (course_node_is_course($type)) {
$extras['course'] = array(
'label' => t('Course settings'),
'description' => t('Course settings and button.'),
'weight' => 0,
);
}
return $extras;
}
/**
* Implements hook_preprocess_page().
*/
function course_preprocess_page(&$variables) {
if (arg(2) == 'takecourse') {
$regions = variable_get('course_disable_regions', array());
foreach ($regions as $key => $region) {
if ($region) {
unset($variables[$region]);
}
}
}
if ($course = course_get_context()) {
// Back/next buttons?
//$variables['content'] .= 'sdfsdfsd';
}
}
/**
* Get a list of course types.
*
* @return array
*/
function course_get_types() {
$types = array();
foreach (node_get_types() as $type => $info) {
if (variable_get("course_use_{$type}", 0)) {
$types[] = $type;
}
}
return $types;
}
/**
* Implements hook_token_list().
*/
function course_token_list($type = 'all') {
$tokens = array();
if ($type == 'node') {
module_load_install('course');
$schema = course_schema();
foreach ($schema['course_node']['fields'] as $key => $value) {
$tokens['course']["course-{$key}"] = $value['description'];
}
}
return $tokens;
}
/**
* Implements hook_token_values().
*/
function course_token_values($type, $object = NULL, $options = array()) {
$values = array();
if ($type == 'node') {
$sql = 'SELECT * FROM {course_node} WHERE nid = %d';
if ($course = db_fetch_object(db_query($sql, $object->nid))) {
foreach ($course as $key => $value) {
$values["course-{$key}"] = $value;
}
}
}
return $values;
}
/**
* Implements hook_action_info().
*/
function course_action_info() {
$actions = array();
$actions['course_add_enrollment_action'] = array(
'type' => 'user',
'description' => t('Enroll user in current course'),
'configurable' => FALSE,
);
$actions['course_edit_enrollment_action'] = array(
'type' => 'course_enrolment',
'description' => t('Edit enrollment'),
'configurable' => TRUE,
);
$actions['course_remove_enrollment_action'] = array(
'type' => 'course_enrolment',
'description' => t('Remove a user from current course'),
'configurable' => FALSE,
);
return $actions;
}
/**
* Action to enrol a user in current course.
*/
function course_add_enrollment_action($user, $context) {
if ($node = node_load(arg(1))) {
course_enrol($node, $user);
drupal_set_message(t("Enrolled %name in %title.", array(
'%name' => $user->name,
'%title' => $node->title,
)));
}
}
/**
* Action to unenrol a user.
*/
function course_remove_enrollment_action(&$enrollment, $context) {
$node = node_load(arg(1));
$user = user_load($enrollment->uid);
course_unenrol($node, $user);
}
/**
* Edit enrolment action
*
* @param object $object
* An object containing nid and uid properties.
* @param array $context
* Values from user input.
*/
function course_edit_enrollment_action($object, $context) {
$enrollment = course_enrolment_load($object->nid, $object->uid);
$node = node_load($enrollment->nid);
$account = user_load($enrollment->uid);
if (!($course_report = course_report_load($node, $account))) {
$course_report->nid = $node->nid;
$course_report->uid = $account->uid;
}
// Update enrollment status.
if ($context['status'] != '') {
$enrollment->status = $context['status'];
}
// Update enrollment duration.
if ($context['enrol_end']) {
// Parse date from popup/plain text.
if ($unixtime = strtotime($context['enrol_end'])) {
$enrollment->enrol_end = $unixtime + 86399;
}
}
// Update completion.
if ($context['complete'] != '') {
$course_report->complete = $context['complete'];
}
// Update date completed.
if ($context['date_completed'] != '') {
if ($unixtime = strtotime($context['date_completed'])) {
$course_report->date_completed = $unixtime;
}
}
course_enrolment_save($enrollment);
course_report_save($course_report);
$course = course_get_course($node, $account);
foreach ($course
->getObjects() as $key => $courseObject) {
$coid = $courseObject
->getId();
$fulfillment = $courseObject
->getFulfillment();
if ($context['course_objects'][$coid] != '') {
// There was a change
if ($context['course_objects'][$coid] == 1) {
// Completed
$fulfillment
->setOption('message', "Fulfillment completed via bulk action.");
$fulfillment
->setComplete($context['course_objects'][$coid]);
}
if ($context['course_objects'][$coid] == -1) {
// Delete attempt
$fulfillment
->delete();
}
if ($context['course_objects'][$coid] == 0) {
// Fail user
$fulfillment
->setOption('message', "Fulfillment failed via bulk action.");
$fulfillment
->setComplete(FALSE);
$fulfillment
->setOption('failed', TRUE);
}
$fulfillment
->save();
}
}
drupal_set_message(t('Updated enrollment for %user', array(
'%user' => $account->name,
)));
}
/**
* Edit enrollment action form.
*/
function course_edit_enrollment_action_form($context) {
$form = array();
$node = node_load(arg(1));
if (!$node) {
return array();
}
$num_users = count($context['selection']);
$form['header'] = array(
'#value' => format_plural($num_users, 'Use this form to edit course enrollment and completion data for 1 user', 'Use this form to edit course enrollment and completion data for @count users'),
);
// Check if this action is being performed on a single user, and set the
// account accordingly.
$account = NULL;
$enroll = NULL;
$course_report = NULL;
if ($num_users == 1) {
// Only one user, so let's prefill values.
$selection = reset($context['selection']);
$enroll = course_enrolment_load($selection->eid);
$account = user_load($enroll->uid);
$course_report = course_report_load($node, $account);
}
// Get course objects, with or without a single user account information.
$course = course_get_course($node, $account);
$objects = $course
->getObjects();
// Build a list of a single user's fulfillments.
$fulfillments = NULL;
if ($account) {
$fulfillments = array();
foreach ($objects as $courseObject) {
// Find required course objects the user has not yet completed.
//if ($courseObject->getOption('required') && !$courseObject->getOption('complete')) {
$fulfillments[$courseObject
->getId()] = $courseObject
->getFulfillment();
//}
}
}
$form['status'] = array(
'#title' => t('Set enrollment status to'),
'#type' => 'select',
'#options' => array(
'' => '',
1 => 'Active',
0 => 'Inactive',
),
'#default_value' => $enroll->status,
'#description' => t('Setting an enrollment to "inactive" will prevent a user from accessing the course.'),
);
$form['enrol_end'] = array(
'#title' => t('Extend course enrollment until'),
'#type' => module_exists('date_popup') ? 'date_popup' : 'date_text',
'#date_format' => 'm/d/Y H:i',
'#description' => t('The date when the user will not be able to access the course.'),
'#default_value' => $enroll->enrol_end ? date('Y-m-d H:i:s', $enroll->enrol_end) : '',
);
$form['complete'] = array(
'#title' => t('Set completion status to'),
'#type' => 'select',
'#options' => array(
'' => '',
1 => t('Complete'),
0 => t('Incomplete'),
),
'#description' => t("This will change a user's course completion. Set to incomplete this to re-evaluate all requirements. Course will never be automatically un-completed once they have been marked completed."),
'#default_value' => $course_report->complete,
);
$form['date_completed'] = array(
'#title' => t('Set completion date to'),
'#type' => module_exists('date_popup') ? 'date_popup' : 'date_text',
'#date_format' => 'm/d/Y H:i',
'#description' => t('The date of completion.'),
'#default_value' => !empty($course_report->date_completed) ? date('Y-m-d H:i:s', $course_report->date_completed) : NULL,
);
$form['course_objects'] = array(
'#title' => t('Set completion status'),
'#description' => t('Set the status of a course object to be applied to selected users.'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#tree' => TRUE,
'#prefix' => '<span id="course-objects-wrapper">',
'#suffix' => '</span>',
);
foreach ($objects as $courseObject) {
$form['course_objects'][$courseObject
->getId()] = array(
'#type' => 'select',
'#title' => check_plain($courseObject
->getTitle()),
'#options' => array(
'' => '- no change - ',
1 => t('Complete'),
-1 => t('Incomplete'),
0 => t('Failed'),
),
'#default_value' => $fulfillments ? $fulfillments[$courseObject
->getId()]
->isComplete() : NULL,
);
}
return $form;
}
/**
* Submit handler for course_edit_enrollment_action_form().
*/
function course_edit_enrollment_action_submit($form, $form_state) {
return array(
'enrol_end' => $form_state['values']['enrol_end'],
'status' => $form_state['values']['status'],
'complete' => $form_state['values']['complete'],
'date_completed' => $form_state['values']['date_completed'],
'course_objects' => $form_state['values']['course_objects'],
);
}
/**
* Validates the edit enrollment action.
*/
function course_edit_enrollment_action_validate($form, $form_state) {
}
/**
* Implements hook_init().
*/
function course_init() {
course_context();
if (!($courseNode = course_get_context())) {
// Set course context for all modules that define course context handlers.
// @see hook_course_handlers().
$modules = course_get_handlers('context');
foreach ($modules as $module => $handlers) {
if (is_array($handlers)) {
foreach ($handlers as $handler) {
$callback = $handler['callback'];
// Calculate and include the file for each callback, if specified.
if (isset($handler['file'])) {
$file_path = isset($handler['file path']) ? $handler['file path'] : drupal_get_path('module', $module);
$include_file = $file_path . '/' . $handler['file'];
include_once $include_file;
}
// We expect query parameters suitable for course_determine_context().
if (function_exists($callback)) {
$params = $callback();
if (is_array($params) && isset($params['object_type']) && isset($params['instance'])) {
if ($courseNode = course_determine_context($module, $params['object_type'], $params['instance'])) {
course_set_context($courseNode);
}
}
}
}
}
}
}
if (class_exists('Course')) {
// Check that Course exists for a special use case where Autoload hasn't yet
// cached the Course class.
$course = course_get_course($courseNode);
if ($course && ($active = $course
->getActive())) {
if ($active
->hasPolling()) {
drupal_add_js(array(
'courseAjaxNavPath' => url('node/' . $courseNode->nid . '/course-object/' . $course
->getActive()
->getId() . '/ajax/nav'),
), 'setting');
}
}
}
}
/**
* Course context handler callback.
*/
function course_context() {
if (arg(0) == 'node') {
// If we are on the course node, set the context immediately.
$node = node_load(arg(1));
if (course_node_is_course($node)) {
course_set_context($node);
}
}
}
/**
* Implements hook_ctools_plugin_directory().
*/
function course_ctools_plugin_directory($owner, $plugin_type) {
if ($owner == 'ctools' && $plugin_type == 'content_types') {
return 'plugins/content_types';
}
if ($owner == 'course') {
return "plugins/course/{$plugin_type}";
}
}
/**
* Implements course_credit_check_completion().
*
* Require the user to choose a user type before they claim credit.
*/
function course_course_credit_check_completion($course_node) {
global $user;
// Check if course user types enabled, has user types, and there is actually valid credit for this course.
if (variable_get('course_user_types_enabled', 0) && count(course_user_type_get_options())) {
// Check for active credit types. No user checking at this point.
// @todo break out into function to get active credit types on a course.
$active = FALSE;
foreach ($course_node->course_credit as $type) {
if ($type->active) {
$active = TRUE;
}
}
if ($active) {
$enrolment = course_enrolment_load($course_node, $user);
if (!$enrolment->user_type && arg(2) == 'course-credit-app') {
drupal_goto("node/{$course_node->nid}/course-user-type", drupal_get_destination());
}
}
}
}
/**
* Allow the user to set their per-course user type.
*/
function course_user_type_form($form_state, $node) {
$form = array();
$form['#node'] = $node;
$form['course_user_type'] = array(
'#title' => t('Please select your user type'),
'#description' => t('Please select your user type. This will affect the credit and certificate you will receive.'),
'#options' => array_merge(array(
'',
), course_user_type_get_options()),
'#type' => 'select',
'#required' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Next'),
);
return $form;
}
/**
* Save the user's type in the course.
*/
function course_user_type_form_submit(&$form, $form_state) {
global $user;
$enrolment = course_enrolment_load($form['#node'], $user);
$enrolment->user_type = $form_state['values']['course_user_type'];
course_enrolment_save($enrolment);
}
/**
* Check whether or not a user can self-enroll in a course.
*
* This function should be called by any module providing means
* of self-enrollment (e.g., course_uc, course_signup) then act accordingly by
* blocking that ability.
*
* @param object $node
* A node.
* @param object $user
* A user.
* @param bool $all
* Return all values from implementations.
*
* @return array
* An array with values 'success', to indicate whether or not the user
* has permission to self-enroll in this course, and 'message', a
* module-provided message that should be displayed to the user.
*/
function course_enrol_access($node, $user = NULL, $flush = FALSE, $all = FALSE) {
static $courses = array();
if (!$user) {
global $user;
}
if (!isset($courses[$node->nid]) || $flush || $all) {
if ($flush) {
$courses = array();
}
// Allow modules to set self-enrollment access for a user.
$hooks = module_invoke_all('course_can_enrol', $node, $user);
if ($all) {
return $hooks;
}
$courses[$node->nid]['success'] = TRUE;
foreach ($hooks as $key => $hook) {
if (is_array($hook) && !$hook['success']) {
$courses[$node->nid] = $hook;
return $hook;
}
}
}
return $courses[$node->nid];
}
/**
* Implements hook_course_can_enrol().
*
* Block enrollments when a course has either not yet started or is expired.
*/
function course_course_can_enrol($node, $user) {
if (!node_access('view', $node)) {
return array(
array(
'success' => FALSE,
'header' => t('Access denied'),
'message' => t('You do not have permission to enroll into this course'),
),
);
}
if (!empty($node->course['open']) && time() < $node->course['open']) {
return array(
array(
'success' => FALSE,
'message' => t('This course opens on %date.', array(
'%date' => format_date($node->course['open'], 'custom', 'F, jS Y'),
)),
),
);
}
if (!empty($node->course['close']) && time() > $node->course['close']) {
return array(
array(
'success' => FALSE,
'message' => t('This course closed on %date and is no longer available for enrollments.', array(
'%date' => format_date($node->course['close'], 'custom', 'F, jS Y'),
)),
),
);
}
if (!empty($node->course['live_from_date']) && REQUEST_TIME > $node->course['live_from_date']) {
return array(
'course_live_started' => array(
'success' => FALSE,
'message' => t('This live activity started on %date and is no longer available for enrollments.', array(
'%date' => date('F, jS Y', $node->course['live_from_date']),
)),
),
);
}
}
/**
* Inserts or updates a course report record.
*
* @param object $entry
* The report entry to be saved into {course_report}, containing:
* - nid: Required. the node id.
* - uid: Required. the user id.
* - data: An array containing:
* - user: The serialized user object at the time of entry.
* - profile: The serialized user profile at the time of entry.
* - updated: Timestamp. The entry time.
*
* @todo Check for missing fields.
*/
function course_report_save($entry) {
// No shenanigans.
if (!$entry->nid > 0 || !$entry->uid > 0) {
$message = t('Report not entered because entry must have nid and uid.');
watchdog('course_report', $message, WATCHDOG_ERROR);
drupal_set_message(check_plain($message), 'error');
return FALSE;
}
// Load user so we can serialize it.
$account = user_load($entry->uid);
$sql = "SELECT * FROM {course_report} WHERE nid = %d AND uid = %d";
$result = db_query($sql, $entry->nid, $entry->uid);
$old = db_fetch_object($result);
$entry->updated = time();
if ($entry->complete && empty($entry->date_completed)) {
$entry->date_completed = time();
}
if ($old && $old->complete && !$entry->complete) {
// Do not un-complete existing completed records.
$entry->complete = 1;
}
// Allow modules to alter course reports before it goes in.
drupal_alter('course_report', $entry, $account, $old);
// Hello CE credit!
if ($old) {
drupal_write_record('course_report', $entry, array(
'nid',
'uid',
));
}
else {
drupal_write_record('course_report', $entry);
}
// Notify modules that a course report has been saved.
module_invoke_all('course_report_saved', $entry, $account, $old);
}
/**
* Implements hook_user().
*
* Delete the user's course records, fulfillments, and enrollments upon
* deletion.
*/
function course_user($op, &$edit, &$account, $category = NULL) {
if ($op == 'delete') {
$sql = "DELETE FROM {course_report} WHERE uid = %d";
db_query($sql, $account->uid);
$sql = "DELETE FROM {course_outline_fulfillment} WHERE uid = %d";
db_query($sql, $account->uid);
$sql = "SELECT * FROM {course_enrolment} WHERE uid = %d";
$result = db_query($sql, $account->uid);
while ($enrollment = db_fetch_object($result)) {
$node = node_load($enrollment->nid);
course_unenrol($node, $account);
}
}
}
/**
* Implements hook_views_bulk_operations_object_info().
*
* Expose information about the course report object to VBO.
*/
function course_views_bulk_operations_object_info() {
return array(
'course_report' => array(
'type' => 'course_report',
'base_table' => 'course_report',
'load' => 'course_report_load',
'title' => 'name',
),
'course_enrolment' => array(
'type' => 'course_enrolment',
'base_table' => 'course_enrolment',
'load' => 'course_enrolment_load',
'title' => 'eid',
),
);
}
/**
* Load a course report entry, by report entry ID or node/user object.
*
* @return object
* An object representation of a course report.
*/
function course_report_load($mixed, $user = NULL) {
if (is_object($mixed)) {
$result = db_query('SELECT cr.* FROM {course_report} cr WHERE nid = %d AND uid = %d', $mixed->nid, $user->uid);
return db_fetch_object($result);
}
elseif (is_numeric($user)) {
$result = db_query('SELECT cr.* FROM {course_report} cr WHERE nid = %d AND uid = %d', $mixed, $user);
return db_fetch_object($result);
}
else {
$result = db_query('SELECT cr.* FROM {course_report} cr WHERE crid = %d', $mixed);
return db_fetch_object($result);
}
}
/**
* Implements hook_theme().
*/
function course_theme() {
return array(
'course_outline_overview_form' => array(
'arguments' => array(
'form' => NULL,
),
),
'course_report' => array(
'file' => 'includes/course.reports.inc',
'arguments' => array(
'nav' => NULL,
'header' => NULL,
'body' => NULL,
),
),
);
}
/**
* Delete a course object.
*
* @param array $mixed
* An array representing a course object (containing 'coid').
*/
function course_outline_delete_object($mixed) {
if (is_object($mixed)) {
$mixed = (array) $mixed;
}
$sql = "DELETE FROM {course_outline} WHERE coid = %d";
db_query($sql, $mixed['coid']);
$sql = "DELETE FROM {course_outline_fulfillment} WHERE coid = %d";
db_query($sql, $mixed['coid']);
}
/**
* Gets the course context.
*
* @todo support Context.
*/
function course_get_context() {
return course_set_context();
}
/**
* Sets a course context.
*
* @todo support Context.
*/
function course_set_context($node = NULL, $clear = FALSE) {
static $stored_course_node;
if ($clear) {
$stored_course_node = NULL;
}
if (!empty($node)) {
$stored_course_node = $node;
}
return !empty($stored_course_node) ? $stored_course_node : NULL;
}
/**
* Get the course node automatically, or from optional query parameters.
*
* @param string $module
* The implementing course object provider module name.
* @param string $object_type
* The course object key as defined by hook_course_handlers().
* @param string $instance
* A key used internally by the implementing course object provider module,
* to identify an instance of *something* used by this course object type.
* @param bool $no_set
* Do not set the context (active course), just return it.
* @param bool $flush
* Flush the static cache. By default, course_determine_context will stop
* processing once a course is found, and continue to return it.
*
* @return mixed
* A course node or NULL if course context not found.
*/
function course_determine_context($module = NULL, $object_type = NULL, $instance = NULL, $no_set = FALSE, $flush = FALSE) {
static $cache = NULL;
$context = NULL;
if (!$context || $flush || $no_set) {
// Determine the course node based on passed query parameters.
$result = db_query("SELECT nid FROM {course_outline} WHERE instance = '%s' AND module = '%s' AND object_type = '%s'", $instance, $module, $object_type);
$nids = array();
while ($course_outline = db_fetch_object($result)) {
$nids[] = $course_outline->nid;
}
if (count($nids) > 1) {
if (in_array($_SESSION['course']['active'], $nids)) {
// The active course in the session is one of the courses this object
// belongs to.
$context = node_load($_SESSION['course']['active']);
}
else {
// No active course, or no match. We have to guess since we're accessing
// this course material outside of the course.
$context = node_load($nids[0]);
}
}
elseif ($nids) {
// We don't have an active session (or, the course in the active session
// didn't contain this course object). So we just guess the first one.
$context = node_load($nids[0]);
}
if ($no_set) {
// Callee just wants context.
return $context;
}
elseif ($context) {
// Set the active course and static cache it.
$_SESSION['course']['active'] = $context->nid;
$cache = $context;
}
}
return $cache;
}
/**
* Implements hook_date_api_fields().
*
* Expose the course date columns to date API.
*/
function course_date_api_fields($field) {
$values = array(
'sql_type' => DATE_UNIX,
'granularity' => array(
'year',
'month',
'day',
),
);
switch ($field) {
case 'course_report.date_completed':
case 'course_report.updated':
case 'course_enrolment.timestamp':
case 'course_enrolment.enrol_end':
case 'course_node.open':
case 'course_node.close':
return $values;
}
}
/**
* Implements hook_date_api_tables().
*/
function course_date_api_tables() {
return array(
'course_report',
'course_enrolment',
'course_node',
);
}
/**
* Implements hook_services_resources().
*/
function course_services_resources() {
require_once 'services/course_report_resource.inc';
require_once 'services/course_enrollment_resource.inc';
$resources = array();
$resources += _course_report_resource();
$resources += _course_credit_resource();
$resources += _course_enrollment_resource();
return $resources;
}
/**
* Get all the options for a user type selection.
*/
function course_user_type_get_options() {
$field_name = variable_get('course_user_types_field', '');
$options = array();
if (!empty($field_name)) {
$options = content_allowed_values(content_fields($field_name));
}
$lines = explode("\n", variable_get('course_user_types', ''));
$additional = array();
foreach ($lines as $line) {
$line = explode('|', $line);
if ($line[0]) {
$additional[$line[0]] = $line[1];
}
}
return array_merge($options, $additional);
}
/**
* Helper function for autocompletion of node titles.
*/
function course_object_autocomplete_node($types, $string) {
$values = explode(',', $types);
$placeholders = db_placeholders($values, 'varchar');
$values[] = $string;
$values[] = $string;
$result = db_query_range(db_rewrite_sql("SELECT n.nid, n.title, u.name FROM {node} n\n INNER JOIN {users} u ON u.uid = n.uid\n WHERE n.type IN ({$placeholders}) AND (title LIKE '%%%s%%' OR n.nid = %d)"), $values, 0, 10);
$matches = array();
while ($node = db_fetch_object($result)) {
$matches[$node->title . " [nid: {$node->nid}]"] = '<span class="autocomplete_title">' . check_plain($node->title) . '</span>';
}
drupal_json($matches);
}
/**
* Implements hook_cron().
*
* Revoke access to inaccessible objects.
*/
function course_cron() {
$handlers = course_get_handlers('object');
$modules = array();
foreach ($handlers as $module => $object) {
foreach ($object as $key => $info) {
if (is_subclass_of($info['class'], 'CourseObjectNode')) {
// This module provides an object of type CourseObjectNode.
$modules[] = $module;
}
}
}
if ($modules) {
// Get a list of fulfillments for CourseObjectNodes.
$placeholders = db_placeholders($modules, 'varchar');
$sql = "SELECT * FROM {course_outline}\n INNER JOIN {course_outline_fulfillment} USING (coid)\n WHERE module in ({$placeholders})";
$result = db_query($sql, $modules);
while ($row = db_fetch_object($result)) {
$extra = unserialize($row->data);
if ($extra['private']) {
// This fulfillment used private content.
$user = user_load($row->uid);
$courseObject = course_get_course_object_by_id($row->coid, $user);
if (!$courseObject
->access('take')) {
// User has no access to take this course object. Revoke access.
$courseObject
->revoke();
}
}
}
}
}
Functions
Name | Description |
---|---|
course_access_object | Menu access for course object router. |
course_action_info | Implements hook_action_info(). |
course_add_enrollment_action | Action to enrol a user in current course. |
course_ajaj_fulfullment_check | Fulfillment check callback. |
course_block | Implements hook_block(). |
course_can_take_course | Implements hook_can_take_course(). |
course_content_extra_fields | Implements hook_content_extra_fields(). |
course_context | Course context handler callback. |
course_course_can_enrol | Implements hook_course_can_enrol(). |
course_course_credit_check_completion | Implements course_credit_check_completion(). |
course_course_handlers | Implements hook_course_handlers(). |
course_cron | Implements hook_cron(). |
course_ctools_plugin_directory | Implements hook_ctools_plugin_directory(). |
course_date_api_fields | Implements hook_date_api_fields(). |
course_date_api_tables | Implements hook_date_api_tables(). |
course_determine_context | Get the course node automatically, or from optional query parameters. |
course_editing_start | Start an editing session for this course. Populate the session from persistent storage. |
course_edit_enrollment_action | Edit enrolment action |
course_edit_enrollment_action_form | Edit enrollment action form. |
course_edit_enrollment_action_submit | Submit handler for course_edit_enrollment_action_form(). |
course_edit_enrollment_action_validate | Validates the edit enrollment action. |
course_enable | Implements hook_enable(). |
course_enrol | Enrols a user in a course. |
course_enrolment_check | Check if the user has enrolled in a course. |
course_enrolment_load | Load an enrollment from a node ID and user ID. |
course_enrolment_save | Create or update an enrolment. |
course_enrol_access | Check whether or not a user can self-enroll in a course. |
course_form_alter | Implements hook_form_alter(). |
course_form_node_type_form_alter | Implements hook_form_FORM_ID_alter(). |
course_form_submit | Submit handler for the course node form. |
course_get_context | Gets the course context. |
course_get_course | Get a loaded Course. |
course_get_course_object | CourseObject factory. Get a loaded course object from database or build one from arguments. |
course_get_course_object_by_id | Get a course object by its identifier. |
course_get_handlers | Get course handlers. |
course_get_types | Get a list of course types. |
course_iframe | Generic Course IFrame function. |
course_init | Implements hook_init(). |
course_load | Menu loader: check if node is a Course. |
course_menu | Implements hook_menu(). |
course_menu_alter | Implements hook_menu_alter(). |
course_nodeapi | Implements hook_nodeapi(). |
course_node_is_course | Check if node is a Course. |
course_object_autocomplete_node | Helper function for autocompletion of node titles. |
course_object_load | Menu loader for course objects, in the context of a course. |
course_object_options | Page callback: Handles object options form for both ctools modal and nojs. |
course_object_options_form | Form API builder for course object options. |
course_object_options_form_submit | Form submission handler for course_object_options_form(). |
course_object_options_form_validate | Form validation handler for course_object_options_form(). |
course_object_restore | Callback to restore a course object temporarily removed from outline overview form. |
course_object_take | Take the course object. |
course_outline_delete_object | Delete a course object. |
course_perm | Implements hook_perm(). |
course_preprocess_page | Implements hook_preprocess_page(). |
course_remove_enrollment_action | Action to unenrol a user. |
course_render_button | Check the permissions of showing the take course button, and return the HTML. |
course_report_load | Load a course report entry, by report entry ID or node/user object. |
course_report_save | Inserts or updates a course report record. |
course_save_objects | Saves course objects. |
course_services_resources | Implements hook_services_resources(). |
course_settings_access | Callback for checking course settings permission. |
course_settings_menu_access | Menu access callback to determine if the course settings should tab should display on the course node. |
course_set_context | Sets a course context. |
course_takecourse_title | Menu title handler for the Take course tab. |
course_take_course | Take a course. |
course_take_course_access | Determine if taking this course should be restricted. |
course_take_course_button_html | Generate a button for taking the course. |
course_take_course_menu_access | Menu access callback to determins if the take course button should display on the course node. |
course_theme | Implements hook_theme(). |
course_token_list | Implements hook_token_list(). |
course_token_values | Implements hook_token_values(). |
course_unenrol | Un-enroll the user. |
course_user | Implements hook_user(). |
course_user_type_form | Allow the user to set their per-course user type. |
course_user_type_form_submit | Save the user's type in the course. |
course_user_type_get_options | Get all the options for a user type selection. |
course_views_api | Implements hook_views_api(). |
course_views_bulk_operations_object_info | Implements hook_views_bulk_operations_object_info(). |
course_views_plugins | Implements hook_views_plugins(). |
_course_get_course_object_by_uniqid | Get a course object by its unique identifier (sessioned course object). |