moodle/course/format/lib.php

466 lines
16 KiB
PHP

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Base class for course format plugins
*
* @package core_course
* @copyright 2012 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
/**
* Returns an instance of format class (extending format_base) for given course
*
* @param int|stdClass $courseorid either course id or
* an object that has the property 'format' and may contain property 'id'
* @return format_base
*/
function course_get_format($courseorid) {
return format_base::instance($courseorid);
}
/**
* Base class for course formats
*
* Each course format must declare class
* class format_FORMATNAME extends format_base {}
* in file lib.php
*
* For each course just one instance of this class is created and it will always be returned by
* course_get_format($courseorid). Format may store it's specific course-dependent options in
* variables of this class.
*
* In rare cases instance of child class may be created just for format without course id
* i.e. to check if format supports AJAX.
*
* Also course formats may extend class section_info and overwrite
* format_base::build_section_cache() to return more information about sections.
*
* If you are upgrading from Moodle 2.3 start with copying the class format_legacy and renaming
* it to format_FORMATNAME, then move the code from your callback functions into
* appropriate functions of the class.
*
* @package core_course
* @copyright 2012 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class format_base {
/** @var int Id of the course in this instance (maybe 0) */
protected $courseid;
/** @var string format used for this course. Please note that it can be different from
* course.format field if course referes to non-existing of disabled format */
protected $format;
/** @var stdClass data for course object, please use {@link format_base::get_course()} */
protected $course = false;
/** @var array cached instances */
private static $instances = array();
/**
* Creates a new instance of class
*
* Please use {@link course_get_format($courseorid)} to get an instance of the format class
*
* @param string $format
* @param int $courseid
* @return format_base
*/
protected function __construct($format, $courseid) {
$this->format = $format;
$this->courseid = $courseid;
}
/**
* Validates that course format exists and enabled and returns either itself or default format
*
* @param string $format
* @return string
*/
protected static final function get_format_or_default($format) {
if ($format === 'site') {
return $format;
}
$plugins = get_plugin_list('format'); // TODO MDL-35260 filter only enabled
if (isset($plugins[$format])) {
return $format;
}
// Else return default format
$defaultformat = reset($plugins); // TODO MDL-35260 get default format from config
debugging('Format plugin format_'.$format.' is not found or is not enabled. Using default format_'.$defaultformat, DEBUG_DEVELOPER);
return $defaultformat;
}
/**
* Get class name for the format
*
* If course format xxx does not declare class format_xxx, format_legacy will be returned.
* This function also includes lib.php file from corresponding format plugin
*
* @param string $format
* @return string
*/
protected static final function get_class_name($format) {
global $CFG;
static $classnames = array('site' => 'format_site');
if (!isset($classnames[$format])) {
$plugins = get_plugin_list('format');
$usedformat = self::get_format_or_default($format);
if (file_exists($plugins[$usedformat].'/lib.php')) {
require_once($plugins[$usedformat].'/lib.php');
}
$classnames[$format] = 'format_'. $usedformat;
if (!class_exists($classnames[$format])) {
require_once($CFG->dirroot.'/course/format/formatlegacy.php');
$classnames[$format] = 'format_legacy';
}
}
return $classnames[$format];
}
/**
* Returns an instance of the class
*
* @todo MDL-35727 use MUC for caching of instances, limit the number of cached instances
*
* @param int|stdClass $courseorid either course id or
* an object that has the property 'format' and may contain property 'id'
* @return format_base
*/
public static final function instance($courseorid) {
global $DB;
if (!is_object($courseorid)) {
$courseid = (int)$courseorid;
if ($courseid && isset(self::$instances[$courseid]) && count(self::$instances[$courseid]) == 1) {
$formats = array_keys(self::$instances[$courseid]);
$format = reset($formats);
} else {
$format = $DB->get_field('course', 'format', array('id' => $courseid), MUST_EXIST);
}
} else {
$format = $courseorid->format;
if (isset($courseorid->id)) {
$courseid = clean_param($courseorid->id, PARAM_INT);
} else {
$courseid = 0;
}
}
// validate that format exists and enabled, use default otherwise
$format = self::get_format_or_default($format);
if (!isset(self::$instances[$courseid][$format])) {
$classname = self::get_class_name($format);
self::$instances[$courseid][$format] = new $classname($format, $courseid);
}
return self::$instances[$courseid][$format];
}
/**
* Resets cache for the course (or all caches)
* To be called from {@link rebuild_course_cache()}
*
* @param int $courseid
*/
public static final function reset_course_cache($courseid = 0) {
if ($courseid) {
if (isset(self::$instances[$courseid])) {
foreach (self::$instances[$courseid] as $format => $object) {
// in case somebody keeps the reference to course format object
self::$instances[$courseid][$format]->course = false;
}
unset(self::$instances[$courseid]);
}
} else {
self::$instances = array();
}
}
/**
* Returns the format name used by this course
*
* @return string
*/
public final function get_format() {
return $this->format;
}
/**
* Returns id of the course (0 if course is not specified)
*
* @return int
*/
public final function get_courseid() {
return $this->courseid;
}
/**
* Returns a record from course database table plus additional fields
* that course format defines
*
* @return stdClass
*/
public function get_course() {
global $DB;
if (!$this->courseid) {
return null;
}
if ($this->course === false) {
$this->course = $DB->get_record('course', array('id' => $this->courseid));
}
return $this->course;
}
/**
* Returns true if this course format uses sections
*
* This function may be called without specifying the course id
* i.e. in {@link course_format_uses_sections()}
*
* Developers, note that if course format does use sections there should be defined a language
* string with the name 'sectionname' defining what the section relates to in the format, i.e.
* $string['sectionname'] = 'Topic';
* or
* $string['sectionname'] = 'Week';
*
* @return bool
*/
public function uses_sections() {
return false;
}
/**
* Returns a list of sections used in the course
*
* This is a shortcut to get_fast_modinfo()->get_section_info_all()
* @see get_fast_modinfo()
* @see course_modinfo::get_section_info_all()
*
* @return array of section_info objects
*/
public final function get_sections() {
if ($course = $this->get_course()) {
$modinfo = get_fast_modinfo($course);
return $modinfo->get_section_info_all();
}
return array();
}
/**
* Returns information about section used in course
*
* @param int|stdClass $section either section number (field course_section.section) or row from course_section table
* @param int $strictness
* @return section_info
*/
public final function get_section($section, $strictness = IGNORE_MISSING) {
if (is_object($section)) {
$sectionnum = $section->section;
} else {
$sectionnum = $section;
}
$sections = $this->get_sections();
if (array_key_exists($sectionnum, $sections)) {
return $sections[$sectionnum];
}
if ($strictness == MUST_EXIST) {
throw new moodle_exception('sectionnotexist');
}
return null;
}
/**
* Returns the display name of the given section that the course prefers.
*
* @param int|stdClass $section Section object from database or just field course_sections.section
* @return Display name that the course format prefers, e.g. "Topic 2"
*/
public function get_section_name($section) {
if (is_object($section)) {
$sectionnum = $section->section;
} else {
$sectionnum = $section;
}
return get_string('sectionname', 'format_'.$this->format) . ' ' . $sectionnum;
}
/**
* Returns the information about the ajax support in the given source format
*
* The returned object's property (boolean)capable indicates that
* the course format supports Moodle course ajax features.
* The property (array)testedbrowsers can be used as a parameter for {@link ajaxenabled()}.
*
* @return stdClass
*/
public function supports_ajax() {
// no support by default
$ajaxsupport = new stdClass();
$ajaxsupport->capable = false;
$ajaxsupport->testedbrowsers = array();
return $ajaxsupport;
}
/**
* Custom action after section has been moved in AJAX mode
*
* Used in course/rest.php
*
* @return array This will be passed in ajax respose
*/
public function ajax_section_move() {
return null;
}
/**
* The URL to use for the specified course (with section)
*
* Please note that course view page /course/view.php?id=COURSEID is hardcoded in many
* places in core and contributed modules. If course format wants to change the location
* of the view script, it is not enough to change just this function. Do not forget
* to add proper redirection.
*
* @param int|stdClass $section Section object from database or just field course_sections.section
* if null the course view page is returned
* @param array $options options for view URL. At the moment core uses:
* 'navigation' (bool) if true and section has no separate page, the function returns null
* 'sr' (int) used by multipage formats to specify to which section to return
* @return null|moodle_url
*/
public function get_view_url($section, $options = array()) {
$course = $this->get_course();
$url = new moodle_url('/course/view.php', array('id' => $course->id));
$sr = null;
if (array_key_exists('sr', $options)) {
$sr = $options['sr'];
}
if (is_object($section)) {
$sectionno = $section->section;
} else {
$sectionno = $section;
}
if ($sectionno !== null) {
if ($sr !== null) {
if ($sr) {
$usercoursedisplay = COURSE_DISPLAY_MULTIPAGE;
$sectionno = $sr;
} else {
$usercoursedisplay = COURSE_DISPLAY_SINGLEPAGE;
}
} else {
$usercoursedisplay = $course->coursedisplay;
}
if ($sectionno != 0 && $usercoursedisplay == COURSE_DISPLAY_MULTIPAGE) {
$url->param('section', $sectionno);
} else {
if (!empty($options['navigation'])) {
return null;
}
$url->set_anchor('section-'.$sectionno);
}
}
return $url;
}
/**
* Loads all of the course sections into the navigation
*
* By default the method {@link global_navigation::load_generic_course_sections()} is called
*
* @param global_navigation $navigation
* @param navigation_node $node The course node within the navigation
* @return array Array of sections where each element also contains the element 'sectionnode'
* referring to the corresponding section node
*/
public function extend_course_navigation(&$navigation, navigation_node $node) {
if ($course = $this->get_course()) {
return $navigation->load_generic_course_sections($course, $node);
}
return array();
}
/**
* Returns the list of blocks to be automatically added for the newly created course
*
* @see blocks_add_default_course_blocks()
*
* @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
* each of values is an array of block names (for left and right side columns)
*/
public function get_default_blocks() {
global $CFG;
if (!empty($CFG->defaultblocks)){
return blocks_parse_default_blocks_list($CFG->defaultblocks);
}
$blocknames = array(
BLOCK_POS_LEFT => array(),
BLOCK_POS_RIGHT => array('search_forums', 'news_items', 'calendar_upcoming', 'recent_activity')
);
return $blocknames;
}
/**
* Allows course format to execute code on moodle_page::set_course()
*
* @param moodle_page $page instance of page calling set_course
*/
public function page_set_course(moodle_page $page) {
}
/**
* Allows course format to execute code on moodle_page::set_cm()
*
* Current module can be accessed as $page->cm (returns instance of cm_info)
*
* @param moodle_page $page instance of page calling set_cm
*/
public function page_set_cm(moodle_page $page) {
}
}
/**
* Pseudo course format used for the site main page
*
* @package core_course
* @copyright 2012 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class format_site extends format_base {
/**
* Returns the display name of the given section that the course prefers.
*
* @param int|stdClass $section Section object from database or just field section.section
* @return Display name that the course format prefers, e.g. "Topic 2"
*/
function get_section_name($section) {
return get_string('site');
}
/**
* For this fake course referring to the whole site, the site homepage is always returned
* regardless of arguments
*
* @param int|stdClass $section
* @param array $options
* @return null|moodle_url
*/
public function get_view_url($section, $options = array()) {
return new moodle_url('/');
}
}